1 /* $NetBSD: readline.c,v 1.3 1997/11/12 21:56:05 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 1997 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Jaromir Dolecek. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 #include <sys/cdefs.h> 40 #if !defined(lint) && !defined(SCCSID) 41 __RCSID("$NetBSD: readline.c,v 1.3 1997/11/12 21:56:05 thorpej Exp $"); 42 #endif /* not lint && not SCCSID */ 43 44 #include <sys/types.h> 45 #include <sys/stat.h> 46 #include <stdio.h> 47 #include <dirent.h> 48 #include <string.h> 49 #include <pwd.h> 50 #include <ctype.h> 51 #include <stdlib.h> 52 #include <unistd.h> 53 #include <limits.h> 54 #include "histedit.h" 55 #include "readline.h" 56 #include "sys.h" 57 #include "el.h" 58 59 /* for rl_complete() */ 60 #define TAB '\r' 61 62 /* see comment at the #ifdef for sense of this */ 63 #define GDB_411_HACK 64 65 /* readline compatibility stuff - look at readline sources/documentation */ 66 /* to see what these variables mean */ 67 const char *rl_library_version = "EditLine wrapper"; 68 char *rl_readline_name = ""; 69 FILE *rl_instream = NULL; 70 FILE *rl_outstream = NULL; 71 int rl_point = 0; 72 int rl_end = 0; 73 char *rl_line_buffer = NULL; 74 75 int history_base = 1; /* probably never subject to change */ 76 int history_length = 0; 77 int max_input_history = 0; 78 char history_expansion_char = '!'; 79 char history_subst_char = '^'; 80 char *history_no_expand_chars = " \t\n=("; 81 Function *history_inhibit_expansion_function = NULL; 82 83 int rl_inhibit_completion = 0; 84 int rl_attempted_completion_over = 0; 85 char *rl_basic_word_break_characters = " \t\n\"\\'`@$><=;|&{("; 86 char *rl_completer_word_break_characters = NULL; 87 char *rl_completer_quote_characters = NULL; 88 CPFunction *rl_completion_entry_function = NULL; 89 CPPFunction *rl_attempted_completion_function = NULL; 90 91 /* used for readline emulation */ 92 static History *h = NULL; 93 static EditLine *e = NULL; 94 95 /* internal functions */ 96 static unsigned char _el_rl_complete __P((EditLine *, int)); 97 static char *_get_prompt __P((EditLine *)); 98 static HIST_ENTRY *_move_history __P((int)); 99 static int _history_search_gen __P((const char *, int, int)); 100 static int _history_expand_command __P((const char *, int, char **)); 101 static char *_rl_compat_sub __P((const char *, const char *, 102 const char *, int)); 103 static int rl_complete_internal __P((int)); 104 105 /* 106 * needed for easy prompt switching 107 */ 108 static char *el_rl_prompt = NULL; 109 static char * 110 _get_prompt(el) 111 EditLine *el; 112 { 113 return el_rl_prompt; 114 } 115 116 /* 117 * generic function for moving around history 118 */ 119 static HIST_ENTRY * 120 _move_history(op) 121 int op; 122 { 123 HistEvent ev; 124 static HIST_ENTRY rl_he; 125 126 if (history(h, &ev, op) != 0) 127 return (HIST_ENTRY *) NULL; 128 129 rl_he.line = ev.str; 130 rl_he.data = ""; 131 132 return &rl_he; 133 } 134 135 136 /* 137 * READLINE compatibility stuff 138 */ 139 140 /* 141 * initialize rl compat stuff 142 */ 143 int 144 rl_initialize() 145 { 146 HistEvent ev; 147 const LineInfo *li; 148 149 if (e != NULL) 150 el_end(e); 151 if (h != NULL) 152 history_end(h); 153 154 if (!rl_instream) 155 rl_instream = stdin; 156 if (!rl_outstream) 157 rl_outstream = stdout; 158 e = el_init(rl_readline_name, rl_instream, rl_outstream); 159 160 h = history_init(); 161 if (!e || !h) 162 return -1; 163 164 history(h, &ev, H_SETMAXSIZE, INT_MAX); /* unlimited */ 165 history_length = 0; 166 max_input_history = INT_MAX; 167 el_set(e, EL_HIST, history, h); 168 169 /* for proper prompt printing in readline() */ 170 el_rl_prompt = strdup(""); 171 el_set(e, EL_PROMPT, _get_prompt); 172 el_set(e, EL_SIGNAL, 1); 173 174 /* set default mode to "emacs"-style and read setting afterwards */ 175 /* so this can be overriden */ 176 el_set(e, EL_EDITOR, "emacs"); 177 178 /* for word completition - this has to go AFTER rebinding keys */ 179 /* to emacs-style */ 180 el_set(e, EL_ADDFN, "rl_complete", 181 "ReadLine compatible completition function", 182 _el_rl_complete); 183 el_set(e, EL_BIND, "^I", "rl_complete", NULL); 184 185 /* read settings from configuration file */ 186 el_source(e, NULL); 187 188 /* some readline apps do use this */ 189 li = el_line(e); 190 rl_line_buffer = (char *) li->buffer; 191 rl_point = rl_end = 0; 192 193 return 0; 194 } 195 196 /* 197 * read one line from input stream and return it, chomping 198 * trailing newline (if there is any) 199 */ 200 char * 201 readline(const char *prompt) 202 { 203 HistEvent ev; 204 int count; 205 const char *ret; 206 207 if (e == NULL || h == NULL) 208 rl_initialize(); 209 210 /* set the prompt */ 211 if (strcmp(el_rl_prompt, prompt) != 0) { 212 free(el_rl_prompt); 213 el_rl_prompt = strdup(prompt); 214 } 215 /* get one line from input stream */ 216 ret = el_gets(e, &count); 217 218 if (ret && count > 0) { 219 char *foo; 220 off_t lastidx; 221 222 foo = strdup(ret); 223 lastidx = count - 1; 224 if (foo[lastidx] == '\n') 225 foo[lastidx] = '\0'; 226 227 ret = foo; 228 } else 229 ret = NULL; 230 231 history(h, &ev, H_GETSIZE); 232 history_length = ev.num; 233 234 return (char *) ret; 235 } 236 237 /* 238 * history functions 239 */ 240 241 /* 242 * is normally called before application starts to use 243 * history expansion functions 244 */ 245 void 246 using_history() 247 { 248 if (h == NULL || e == NULL) 249 rl_initialize(); 250 } 251 252 /* 253 * substitute ``what'' with ``with'', returning resulting string; if 254 * globally == 1, substitutes all occurences of what, otherwise only the 255 * first one 256 */ 257 static char * 258 _rl_compat_sub(str, what, with, globally) 259 const char *str, *what, *with; 260 int globally; 261 { 262 char *result; 263 const char *temp, *new; 264 int size, len, i, with_len, what_len, add; 265 266 result = malloc((size = 16)); 267 temp = str; 268 with_len = strlen(with); 269 what_len = strlen(what); 270 len = 0; 271 do { 272 new = strstr(temp, what); 273 if (new) { 274 i = new - temp; 275 add = i + with_len; 276 if (i + add + 1 >= size) { 277 size += add + 1; 278 result = realloc(result, size); 279 } 280 (void)strncpy(&result[len], temp, i); 281 len += i; 282 (void)strcpy(&result[len], with); /* safe */ 283 len += with_len; 284 temp = new + what_len; 285 } else { 286 add = strlen(temp); 287 if (len + add + 1 >= size) { 288 size += add + 1; 289 result = realloc(result, size); 290 } 291 (void)strcpy(&result[len], temp); /* safe */ 292 len += add; 293 temp = NULL; 294 } 295 } while (temp); 296 result[len] = '\0'; 297 298 return result; 299 } 300 301 /* 302 * the real function doing history expansion - takes as argument command 303 * to do and data upon which the command should be executed 304 * does expansion the way I've understood readline documentation 305 * word designator ``%'' isn't supported (yet ?) 306 * 307 * returns 0 if data was not modified, 1 if it was and 2 if the string 308 * should be only printed and not executed; in case of error, 309 * returns -1 and *result points to NULL 310 * it's callers responsibility to free() string returned in *result 311 */ 312 static int 313 _history_expand_command(command, len, result) 314 const char *command; 315 int len; 316 char **result; 317 { 318 char **arr, *temp, *line, *search = NULL, *cmd; 319 const char *event_data = NULL; 320 static const char *from = NULL, *to = NULL; 321 int start = -1, end = -1, max, i, size, idx; 322 int h_on = 0, t_on = 0, r_on = 0, e_on = 0, p_on = 0, 323 g_on = 0; 324 int event_num = 0, retval; 325 326 *result = NULL; 327 328 cmd = alloca(len + 1); 329 (void)strncpy(cmd, command, len); 330 cmd[len] = 0; 331 332 idx = 1; 333 /* find out which event to take */ 334 if (cmd[idx] == history_expansion_char) { 335 event_num = history_length; 336 idx++; 337 } else { 338 int off, len, num; 339 off = idx; 340 while (cmd[off] && !strchr(":^$*-%", cmd[off])) 341 off++; 342 num = atoi(&cmd[idx]); 343 if (num != 0) { 344 event_num = num; 345 if (num < 0) 346 event_num += history_length + 1; 347 } else { 348 int prefix = 1, curr_num; 349 HistEvent ev; 350 351 len = off - idx; 352 if (cmd[idx] == '?') { 353 idx++, len--; 354 if (cmd[off - 1] == '?') 355 len--; 356 else if (cmd[off] != '\n' && cmd[off] != '\0') 357 return -1; 358 prefix = 0; 359 } 360 search = alloca(len + 1); 361 (void)strncpy(search, &cmd[idx], len); 362 search[len] = '\0'; 363 364 if (history(h, &ev, H_CURR) != 0) 365 return -1; 366 curr_num = ev.num; 367 368 if (prefix) 369 retval = history_search_prefix(search, -1); 370 else 371 retval = history_search(search, -1); 372 373 if (retval == -1) { 374 fprintf(rl_outstream, "%s: Event not found\n", 375 search); 376 return -1; 377 } 378 if (history(h, &ev, H_CURR) != 0) 379 return -1; 380 event_data = ev.str; 381 382 /* roll back to original position */ 383 history(h, &ev, H_NEXT_EVENT, curr_num); 384 } 385 idx = off; 386 } 387 388 if (!event_data && event_num >= 0) { 389 HIST_ENTRY *rl_he; 390 rl_he = history_get(event_num); 391 if (!rl_he) 392 return 0; 393 event_data = rl_he->line; 394 } else 395 return -1; 396 397 if (cmd[idx] != ':') 398 return -1; 399 cmd += idx + 1; 400 401 /* recognize cmd */ 402 if (*cmd == '^') 403 start = end = 1, cmd++; 404 else if (*cmd == '$') 405 start = end = -1, cmd++; 406 else if (*cmd == '*') 407 start = 1, end = -1, cmd++; 408 else if (isdigit(*cmd)) { 409 const char *temp; 410 int shifted = 0; 411 412 start = atoi(cmd); 413 temp = cmd; 414 for (; isdigit(*cmd); cmd++); 415 if (temp != cmd) 416 shifted = 1; 417 if (shifted && *cmd == '-') { 418 if (!isdigit(*(cmd + 1))) 419 end = -2; 420 else { 421 end = atoi(cmd + 1); 422 for (; isdigit(*cmd); cmd++); 423 } 424 } else if (shifted && *cmd == '*') 425 end = -1, cmd++; 426 else if (shifted) 427 end = start; 428 } 429 if (*cmd == ':') 430 cmd++; 431 432 line = strdup(event_data); 433 for (; *cmd; cmd++) { 434 if (*cmd == ':') 435 continue; 436 else if (*cmd == 'h') 437 h_on = 1 | g_on, g_on = 0; 438 else if (*cmd == 't') 439 t_on = 1 | g_on, g_on = 0; 440 else if (*cmd == 'r') 441 r_on = 1 | g_on, g_on = 0; 442 else if (*cmd == 'e') 443 e_on = 1 | g_on, g_on = 0; 444 else if (*cmd == 'p') 445 p_on = 1 | g_on, g_on = 0; 446 else if (*cmd == 'g') 447 g_on = 2; 448 else if (*cmd == 's' || *cmd == '&') { 449 char *what, *with, delim; 450 int len, size, from_len; 451 452 if (*cmd == '&' && (from == NULL || to == NULL)) 453 continue; 454 else if (*cmd == 's') { 455 delim = *(++cmd), cmd++; 456 size = 16; 457 what = realloc((void *) from, size); 458 len = 0; 459 for (; *cmd && *cmd != delim; cmd++) { 460 if (*cmd == '\\' 461 && *(cmd + 1) == delim) 462 cmd++; 463 if (len >= size) 464 what = realloc(what, 465 (size <<= 1)); 466 what[len++] = *cmd; 467 } 468 what[len] = '\0'; 469 from = what; 470 if (*what == '\0') { 471 free(what); 472 if (search) 473 from = strdup(search); 474 else { 475 from = NULL; 476 return -1; 477 } 478 } 479 cmd++; /* shift after delim */ 480 if (!*cmd) 481 continue; 482 483 size = 16; 484 with = realloc((void *) to, size); 485 len = 0; 486 from_len = strlen(from); 487 for (; *cmd && *cmd != delim; cmd++) { 488 if (len + from_len + 1 >= size) { 489 size += from_len + 1; 490 with = realloc(with, size); 491 } 492 if (*cmd == '&') { 493 /* safe */ 494 (void)strcpy(&with[len], from); 495 len += from_len; 496 continue; 497 } 498 if (*cmd == '\\' 499 && (*(cmd + 1) == delim 500 || *(cmd + 1) == '&')) 501 cmd++; 502 with[len++] = *cmd; 503 } 504 with[len] = '\0'; 505 to = with; 506 507 temp = _rl_compat_sub(line, from, to, 508 (g_on) ? 1 : 0); 509 free(line); 510 line = temp; 511 g_on = 0; 512 } 513 } 514 } 515 516 arr = history_tokenize(line); 517 free(line); /* no more needed */ 518 if (arr && *arr == NULL) 519 free(arr), arr = NULL; 520 if (!arr) 521 return -1; 522 523 /* find out max valid idx to array of array */ 524 max = 0; 525 for (i = 0; arr[i]; i++) 526 max++; 527 max--; 528 529 /* set boundaries to something relevant */ 530 if (start < 0) 531 start = 1; 532 if (end < 0) 533 end = max - ((end < -1) ? 1 : 0); 534 535 /* check boundaries ... */ 536 if (start > max || end > max || start > end) 537 return -1; 538 539 for (i = 0; i <= max; i++) { 540 char *temp; 541 if (h_on && (i == 1 || h_on > 1) && 542 (temp = strrchr(arr[i], '/'))) 543 *(temp + 1) = '\0'; 544 if (t_on && (i == 1 || t_on > 1) && 545 (temp = strrchr(arr[i], '/'))) 546 (void)strcpy(arr[i], temp + 1); 547 if (r_on && (i == 1 || r_on > 1) && 548 (temp = strrchr(arr[i], '.'))) 549 *temp = '\0'; 550 if (e_on && (i == 1 || e_on > 1) && 551 (temp = strrchr(arr[i], '.'))) 552 (void)strcpy(arr[i], temp); 553 } 554 555 size = 1, len = 0; 556 temp = malloc(size); 557 for (i = start; start <= i && i <= end; i++) { 558 int arr_len; 559 560 arr_len = strlen(arr[i]); 561 if (len + arr_len + 1 >= size) { 562 size += arr_len + 1; 563 temp = realloc(temp, size); 564 } 565 (void)strcpy(&temp[len], arr[i]); /* safe */ 566 len += arr_len; 567 temp[len++] = ' '; /* add a space */ 568 } 569 while (len > 0 && isspace(temp[len - 1])) 570 len--; 571 temp[len] = '\0'; 572 573 *result = temp; 574 575 for (i = 0; i <= max; i++) 576 free(arr[i]); 577 free(arr), arr = (char **) NULL; 578 return (p_on) ? 2 : 1; 579 } 580 581 /* 582 * csh-style history expansion 583 */ 584 int 585 history_expand(str, output) 586 char *str; 587 char **output; 588 { 589 int i, retval = 0, size, idx; 590 char *temp, *result; 591 592 if (h == NULL || e == NULL) 593 rl_initialize(); 594 595 *output = strdup(str); /* do it early */ 596 597 if (str[0] == history_subst_char) { 598 /* ^foo^foo2^ is equivalent to !!:s^foo^foo2^ */ 599 temp = alloca(4 + strlen(str) + 1); 600 temp[0] = temp[1] = history_expansion_char; 601 temp[2] = ':'; 602 temp[3] = 's'; 603 (void)strcpy(temp + 4, str); 604 str = temp; 605 } 606 #define ADD_STRING(what, len) \ 607 { \ 608 if (idx + len + 1 > size) \ 609 result = realloc(result, (size += len + 1)); \ 610 (void)strncpy(&result[idx], what, len); \ 611 idx += len; \ 612 result[idx] = '\0'; \ 613 } 614 615 result = NULL; 616 size = idx = 0; 617 for (i = 0; str[i];) { 618 int start, j, len, loop_again; 619 620 loop_again = 1; 621 start = j = i; 622 loop: 623 for (; str[j]; j++) { 624 if (str[j] == '\\' && 625 str[j + 1] == history_expansion_char) { 626 (void)strcpy(&str[j], &str[j + 1]); 627 continue; 628 } 629 if (!loop_again) { 630 if (str[j] == '?') { 631 while (str[j] && str[++j] != '?'); 632 if (str[j] == '?') 633 j++; 634 } else if (isspace(str[j])) 635 break; 636 } 637 if (str[j] == history_expansion_char 638 && !strchr(history_no_expand_chars, str[j + 1]) 639 && (!history_inhibit_expansion_function || 640 (*history_inhibit_expansion_function) (str, j) == 0)) 641 break; 642 } 643 644 if (str[j] && str[j + 1] != '#' && loop_again) { 645 i = j; 646 j++; 647 if (str[j] == history_expansion_char) 648 j++; 649 loop_again = 0; 650 goto loop; 651 } 652 len = i - start; 653 temp = &str[start]; 654 ADD_STRING(temp, len); 655 656 if (str[i] == '\0' || str[i] != history_expansion_char 657 || str[i + 1] == '#') { 658 len = j - i; 659 temp = &str[i]; 660 ADD_STRING(temp, len); 661 if (start == 0) 662 retval = 0; 663 else 664 retval = 1; 665 break; 666 } 667 retval = _history_expand_command(&str[i], j - i, &temp); 668 if (retval != -1) { 669 len = strlen(temp); 670 ADD_STRING(temp, len); 671 } 672 i = j; 673 } /* for(i ...) */ 674 675 if (retval == 2) { 676 add_history(temp); 677 #ifdef GDB_411_HACK 678 /* gdb 4.11 has been shipped with readline, where */ 679 /* history_expand() returned -1 when the line */ 680 /* should not be executed; in readline 2.1+ */ 681 /* it should return 2 in such a case */ 682 retval = -1; 683 #endif 684 } 685 free(*output); 686 *output = result; 687 688 return retval; 689 } 690 691 /* 692 * returns array of tokens parsed out of string, much as the shell might 693 */ 694 char ** 695 history_tokenize(str) 696 const char *str; 697 { 698 int size = 1, result_idx = 0, i, start, len; 699 char **result = NULL, *temp, delim = '\0'; 700 701 for (i = 0; str[i]; i++) { 702 while (isspace(str[i])) 703 i++; 704 start = i; 705 for (; str[i]; i++) { 706 if (str[i] == '\\') 707 i++; 708 else if (str[i] == delim) 709 delim = '\0'; 710 else if (!delim && 711 (isspace(str[i]) || strchr("()<>;&|$", str[i]))) 712 break; 713 else if (!delim && strchr("'`\"", str[i])) 714 delim = str[i]; 715 } 716 717 if (result_idx + 2 >= size) { 718 size <<= 1; 719 result = realloc(result, size * sizeof(char *)); 720 } 721 len = i - start; 722 temp = malloc(len + 1); 723 (void)strncpy(temp, &str[start], len); 724 temp[len] = '\0'; 725 result[result_idx++] = temp; 726 result[result_idx] = NULL; 727 } 728 729 return result; 730 } 731 732 /* 733 * limit size of history record to ``max'' events 734 */ 735 void 736 stifle_history(max) 737 int max; 738 { 739 HistEvent ev; 740 741 if (h == NULL || e == NULL) 742 rl_initialize(); 743 744 if (history(h, &ev, H_SETMAXSIZE, max) == 0) 745 max_input_history = max; 746 } 747 748 /* 749 * "unlimit" size of history - set the limit to maximum allowed int value 750 */ 751 int 752 unstifle_history() 753 { 754 HistEvent ev; 755 int omax; 756 757 history(h, &ev, H_SETMAXSIZE, INT_MAX); 758 omax = max_input_history; 759 max_input_history = INT_MAX; 760 return omax; /* some value _must_ be returned */ 761 } 762 763 int 764 history_is_stifled() 765 { 766 /* cannot return true answer */ 767 return (max_input_history != INT_MAX); 768 } 769 770 /* 771 * read history from a file given 772 */ 773 int 774 read_history(filename) 775 const char *filename; 776 { 777 HistEvent ev; 778 779 if (h == NULL || e == NULL) 780 rl_initialize(); 781 return history(h, &ev, H_LOAD, filename); 782 } 783 784 /* 785 * write history to a file given 786 */ 787 int 788 write_history(filename) 789 const char *filename; 790 { 791 HistEvent ev; 792 793 if (h == NULL || e == NULL) 794 rl_initialize(); 795 return history(h, &ev, H_SAVE, filename); 796 } 797 798 /* 799 * returns history ``num''th event 800 * 801 * returned pointer points to static variable 802 */ 803 HIST_ENTRY * 804 history_get(num) 805 int num; 806 { 807 static HIST_ENTRY she; 808 HistEvent ev; 809 int i = 1, curr_num; 810 811 if (h == NULL || e == NULL) 812 rl_initialize(); 813 814 /* rewind to beginning */ 815 if (history(h, &ev, H_CURR) != 0) 816 return NULL; 817 curr_num = ev.num; 818 if (history(h, &ev, H_LAST) != 0) 819 return NULL; /* error */ 820 while (i < num && history(h, &ev, H_PREV) == 0) 821 i++; 822 if (i != num) 823 return NULL; /* not so many entries */ 824 825 she.line = ev.str; 826 she.data = NULL; 827 828 /* rewind history to the same event it was before */ 829 (void) history(h, &ev, H_FIRST); 830 (void) history(h, &ev, H_NEXT_EVENT, curr_num); 831 832 return &she; 833 } 834 835 /* 836 * add the line to history table 837 */ 838 int 839 add_history(line) 840 const char *line; 841 { 842 HistEvent ev; 843 844 if (h == NULL || e == NULL) 845 rl_initialize(); 846 847 (void) history(h, &ev, H_ENTER, line); 848 if (history(h, &ev, H_GETSIZE) == 0) 849 history_length = ev.num; 850 851 return (!(history_length > 0)); /* return 0 if all is okay */ 852 } 853 854 /* 855 * clear the history list - delete all entries 856 */ 857 void 858 clear_history() 859 { 860 HistEvent ev; 861 history(h, &ev, H_CLEAR); 862 } 863 864 /* 865 * returns offset of the current history event 866 */ 867 int 868 where_history() 869 { 870 HistEvent ev; 871 int curr_num, off; 872 873 if (history(h, &ev, H_CURR) != 0) 874 return 0; 875 curr_num = ev.num; 876 877 history(h, &ev, H_FIRST); 878 off = 1; 879 while (ev.num != curr_num && history(h, &ev, H_NEXT) == 0) 880 off++; 881 882 return off; 883 } 884 885 /* 886 * returns current history event or NULL if there is no such event 887 */ 888 HIST_ENTRY * 889 current_history() 890 { 891 return _move_history(H_CURR); 892 } 893 894 /* 895 * returns total number of bytes history events' data are using 896 */ 897 int 898 history_total_bytes() 899 { 900 HistEvent ev; 901 int curr_num, size; 902 903 if (history(h, &ev, H_CURR) != 0) 904 return -1; 905 curr_num = ev.num; 906 907 history(h, &ev, H_FIRST); 908 size = 0; 909 do 910 size += strlen(ev.str); 911 while (history(h, &ev, H_NEXT) == 0); 912 913 /* get to the same position as before */ 914 history(h, &ev, H_PREV_EVENT, curr_num); 915 916 return size; 917 } 918 919 /* 920 * sets the position in the history list to ``pos'' 921 */ 922 int 923 history_set_pos(pos) 924 int pos; 925 { 926 HistEvent ev; 927 int off, curr_num; 928 929 if (pos > history_length || pos < 0) 930 return -1; 931 932 history(h, &ev, H_CURR); 933 curr_num = ev.num; 934 history(h, &ev, H_FIRST); 935 off = 0; 936 while (off < pos && history(h, &ev, H_NEXT) == 0) 937 off++; 938 939 if (off != pos) { /* do a rollback in case of error */ 940 history(h, &ev, H_FIRST); 941 history(h, &ev, H_NEXT_EVENT, curr_num); 942 return -1; 943 } 944 return 0; 945 } 946 947 /* 948 * returns previous event in history and shifts pointer accordingly 949 */ 950 HIST_ENTRY * 951 previous_history() 952 { 953 return _move_history(H_PREV); 954 } 955 956 /* 957 * returns next event in history and shifts pointer accordingly 958 */ 959 HIST_ENTRY * 960 next_history() 961 { 962 return _move_history(H_NEXT); 963 } 964 965 /* 966 * generic history search function 967 */ 968 static int 969 _history_search_gen(str, direction, pos) 970 const char *str; 971 int direction, pos; 972 { 973 HistEvent ev; 974 const char *strp; 975 int curr_num; 976 977 if (history(h, &ev, H_CURR) != 0) 978 return -1; 979 curr_num = ev.num; 980 981 for (;;) { 982 strp = strstr(ev.str, str); 983 if (strp && (pos < 0 || &ev.str[pos] == strp)) 984 return (int) (strp - ev.str); 985 if (history(h, &ev, direction < 0 ? H_PREV : H_NEXT) != 0) 986 break; 987 } 988 989 history(h, &ev, direction < 0 ? H_NEXT_EVENT : H_PREV_EVENT, curr_num); 990 991 return -1; 992 } 993 994 /* 995 * searches for first history event containing the str 996 */ 997 int 998 history_search(str, direction) 999 const char *str; 1000 int direction; 1001 { 1002 return _history_search_gen(str, direction, -1); 1003 } 1004 1005 /* 1006 * searches for first history event beginning with str 1007 */ 1008 int 1009 history_search_prefix(str, direction) 1010 const char *str; 1011 int direction; 1012 { 1013 return _history_search_gen(str, direction, 0); 1014 } 1015 1016 /* 1017 * search for event in history containing str, starting at offset 1018 * abs(pos); continue backward, if pos<0, forward otherwise 1019 */ 1020 int 1021 history_search_pos(str, direction, pos) 1022 const char *str; 1023 int direction, pos; 1024 { 1025 HistEvent ev; 1026 int curr_num, off; 1027 1028 off = (pos > 0) ? pos : -pos; 1029 pos = (pos > 0) ? 1 : -1; 1030 1031 if (history(h, &ev, H_CURR) != 0) 1032 return -1; 1033 curr_num = ev.num; 1034 1035 if (history_set_pos(off) != 0 || history(h, &ev, H_CURR) != 0) 1036 return -1; 1037 1038 1039 for (;;) { 1040 if (strstr(ev.str, str)) 1041 return off; 1042 if (history(h, &ev, (pos < 0) ? H_PREV : H_NEXT) != 0) 1043 break; 1044 } 1045 1046 /* set "current" pointer back to previous state */ 1047 history(h, &ev, (pos < 0) ? H_NEXT_EVENT : H_PREV_EVENT, curr_num); 1048 1049 return -1; 1050 } 1051 1052 1053 /********************************/ 1054 /* completition functions */ 1055 1056 /* 1057 * does tilde expansion of strings of type ``~user/foo'' 1058 * if ``user'' isn't valid user name or ``txt'' doesn't start 1059 * w/ '~', returns pointer to strdup()ed copy of ``txt'' 1060 * 1061 * it's callers's responsibility to free() returned string 1062 */ 1063 char * 1064 tilde_expand(txt) 1065 char *txt; 1066 { 1067 struct passwd *pass; 1068 char *temp; 1069 size_t len = 0; 1070 1071 if (txt[0] != '~') 1072 return strdup(txt); 1073 1074 temp = strchr(txt + 1, '/'); 1075 if (temp == NULL) 1076 temp = strdup(txt + 1); 1077 else { 1078 len = temp - txt + 1; /* text until string after slash */ 1079 temp = malloc(len); 1080 (void)strncpy(temp, txt + 1, len - 2); 1081 temp[len - 2] = '\0'; 1082 } 1083 pass = getpwnam(temp); 1084 free(temp); /* value no more needed */ 1085 if (pass == NULL) 1086 return strdup(txt); 1087 1088 /* update pointer txt to point at string immedially following */ 1089 /* first slash */ 1090 txt += len; 1091 1092 temp = malloc(strlen(pass->pw_dir) + 1 + strlen(txt) + 1); 1093 (void)sprintf(temp, "%s/%s", pass->pw_dir, txt); 1094 1095 return temp; 1096 } 1097 1098 /* 1099 * return first found file name starting by the ``text'' or NULL if no 1100 * such file can be found 1101 * value of ``state'' is ignored 1102 * 1103 * it's caller's responsibility to free returned string 1104 */ 1105 char * 1106 filename_completion_function(text, state) 1107 const char *text; 1108 int state; 1109 { 1110 static DIR *dir = NULL; 1111 static char *filename = NULL, *dirname = NULL; 1112 static size_t filename_len = 0; 1113 struct dirent *entry; 1114 char *temp; 1115 size_t len; 1116 1117 if (state == 0 || dir == NULL) { 1118 if (dir != NULL) { 1119 closedir(dir); 1120 dir = NULL; 1121 } 1122 temp = strrchr(text, '/'); 1123 if (temp) { 1124 temp++; 1125 filename = realloc(filename, strlen(temp) + 1); 1126 (void)strcpy(filename, temp); 1127 len = temp - text; /* including last slash */ 1128 dirname = realloc(dirname, len + 1); 1129 (void)strncpy(dirname, text, len); 1130 dirname[len] = '\0'; 1131 } else { 1132 filename = strdup(text); 1133 dirname = NULL; 1134 } 1135 1136 /* support for ``~user'' syntax */ 1137 if (dirname && *dirname == '~') { 1138 temp = tilde_expand(dirname); 1139 dirname = realloc(dirname, strlen(temp) + 1); 1140 (void)strcpy(dirname, temp); /* safe */ 1141 free(temp); /* no more needed */ 1142 } 1143 /* will be used in cycle */ 1144 filename_len = strlen(filename); 1145 if (filename_len == 0) 1146 return NULL; /* no expansion possible */ 1147 1148 dir = opendir(dirname ? dirname : "."); 1149 if (!dir) 1150 return NULL; /* cannot open the directory */ 1151 } 1152 /* find the match */ 1153 while ((entry = readdir(dir))) { 1154 /* otherwise, get first entry where first */ 1155 /* filename_len characters are equal */ 1156 if (entry->d_name[0] == filename[0] 1157 && entry->d_namlen >= filename_len 1158 && strncmp(entry->d_name, filename, 1159 filename_len) == 0) 1160 break; 1161 } 1162 1163 if (entry) { /* match found */ 1164 1165 struct stat stbuf; 1166 len = entry->d_namlen + 1167 ((dirname) ? strlen(dirname) : 0) + 1 + 1; 1168 temp = malloc(len); 1169 (void)sprintf(temp, "%s%s", 1170 dirname ? dirname : "", entry->d_name); /* safe */ 1171 1172 /* test, if it's directory */ 1173 if (stat(temp, &stbuf) == 0 && S_ISDIR(stbuf.st_mode)) 1174 strcat(temp, "/"); /* safe */ 1175 } else 1176 temp = NULL; 1177 1178 return temp; 1179 } 1180 1181 /* 1182 * a completion generator for usernames; returns _first_ username 1183 * which starts with supplied text 1184 * text contains a partial username preceded by random character 1185 * (usually '~'); state is ignored 1186 * it's callers responsibility to free returned value 1187 */ 1188 char * 1189 username_completion_function(text, state) 1190 const char *text; 1191 int state; 1192 { 1193 struct passwd *pwd; 1194 1195 if (text[0] == '\0') 1196 return NULL; 1197 1198 if (*text == '~') 1199 text++; 1200 1201 if (state == 0) 1202 setpwent(); 1203 1204 while ((pwd = getpwent()) && text[0] == pwd->pw_name[0] 1205 && strcmp(text, pwd->pw_name) == 0); 1206 1207 if (pwd == NULL) { 1208 endpwent(); 1209 return NULL; 1210 } 1211 return strdup(pwd->pw_name); 1212 } 1213 1214 /* 1215 * el-compatible wrapper around rl_complete; needed for key binding 1216 */ 1217 static unsigned char 1218 _el_rl_complete(el, ch) 1219 EditLine *el; 1220 int ch; 1221 { 1222 return (unsigned char) rl_complete(0, ch); 1223 } 1224 1225 /* 1226 * returns list of completitions for text given 1227 */ 1228 char ** 1229 completion_matches(text, genfunc) 1230 const char *text; 1231 CPFunction *genfunc; 1232 { 1233 char **match_list = NULL, *retstr, *prevstr; 1234 size_t matches, math_list_len, max_equal, len, which, 1235 i; 1236 1237 if (h == NULL || e == NULL) 1238 rl_initialize(); 1239 1240 matches = 0; 1241 math_list_len = 1; 1242 while ((retstr = (*genfunc) (text, matches))) { 1243 if (matches + 1 >= math_list_len) { 1244 math_list_len <<= 1; 1245 match_list = realloc(match_list, 1246 math_list_len * sizeof(char *)); 1247 } 1248 match_list[++matches] = retstr; 1249 } 1250 1251 if (!match_list) 1252 return (char **) NULL; /* nothing found */ 1253 1254 /* find least denominator and insert it to match_list[0] */ 1255 which = 2; 1256 prevstr = match_list[1]; 1257 len = max_equal = strlen(prevstr); 1258 for (; which < matches; which++) { 1259 for (i = 0; i < max_equal && 1260 prevstr[i] == match_list[which][i]; i++) 1261 continue; 1262 max_equal = i; 1263 } 1264 1265 retstr = malloc(max_equal + 1); 1266 (void)strncpy(retstr, match_list[1], max_equal); 1267 retstr[max_equal] = '\0'; 1268 match_list[0] = retstr; 1269 1270 /* add NULL as last pointer to the array */ 1271 if (matches + 1 >= math_list_len) 1272 match_list = realloc(match_list, 1273 (math_list_len + 1) * sizeof(char *)); 1274 match_list[matches + 1] = (char *) NULL; 1275 1276 return match_list; 1277 } 1278 1279 /* 1280 * called by rl_complete() 1281 */ 1282 static int 1283 rl_complete_internal(what_to_do) 1284 int what_to_do; 1285 { 1286 CPFunction *complet_func; 1287 const LineInfo *li; 1288 char *temp, *temp2, **arr; 1289 int len; 1290 1291 if (h == NULL || e == NULL) 1292 rl_initialize(); 1293 1294 complet_func = rl_completion_entry_function; 1295 if (!complet_func) 1296 complet_func = filename_completion_function; 1297 1298 li = el_line(e); 1299 temp = (char *) li->cursor; 1300 while (temp > li->buffer && 1301 !strchr(rl_basic_word_break_characters, *(temp - 1))) 1302 temp--; 1303 1304 len = li->cursor - temp; 1305 temp2 = alloca(len + 1); 1306 (void)strncpy(temp2, temp, len); 1307 temp = temp2; 1308 temp[len] = '\0'; 1309 1310 /* these can be used by function called in completion_matches() */ 1311 /* or (*rl_attempted_completion_function)() */ 1312 rl_point = li->cursor - li->buffer; 1313 rl_end = li->lastchar - li->buffer; 1314 1315 if (!rl_attempted_completion_function) 1316 arr = completion_matches(temp, complet_func); 1317 else { 1318 int end = li->cursor - li->buffer; 1319 arr = (*rl_attempted_completion_function) (temp, 1320 end - len, end); 1321 } 1322 free(temp); /* no more needed */ 1323 1324 if (arr) { 1325 int i; 1326 1327 el_deletestr(e, len); 1328 el_insertstr(e, arr[0]); 1329 if (strcmp(arr[0], arr[1]) == 0) { 1330 /* lcd is valid object, so add a space to mark it */ 1331 /* in case of filename completition, add a space */ 1332 /* only if object found is not directory */ 1333 int len = strlen(arr[0]); 1334 if (complet_func != filename_completion_function 1335 || (len > 0 && (arr[0])[len - 1] != '/')) 1336 el_insertstr(e, " "); 1337 } else 1338 /* lcd is not a valid object - further specification */ 1339 /* is needed */ 1340 term_beep(e); 1341 1342 /* free elements of array and the array itself */ 1343 for (i = 0; arr[i]; i++) 1344 free(arr[i]); 1345 free(arr), arr = NULL; 1346 1347 return CC_REFRESH; 1348 } 1349 return CC_NORM; 1350 } 1351 1352 /* 1353 * complete word at current point 1354 */ 1355 int 1356 rl_complete(ignore, invoking_key) 1357 int ignore, invoking_key; 1358 { 1359 if (h == NULL || e == NULL) 1360 rl_initialize(); 1361 1362 if (rl_inhibit_completion) { 1363 rl_insert(ignore, invoking_key); 1364 return CC_REFRESH; 1365 } else 1366 return rl_complete_internal(invoking_key); 1367 1368 return CC_REFRESH; 1369 } 1370 1371 /* 1372 * misc other functions 1373 */ 1374 1375 /* 1376 * bind key c to readline-type function func 1377 */ 1378 int 1379 rl_bind_key(c, func) 1380 int c; 1381 int func __P((int, int)); 1382 { 1383 int retval = -1; 1384 1385 if (h == NULL || e == NULL) 1386 rl_initialize(); 1387 1388 if (func == rl_insert) { 1389 /* XXX notice there is no range checking of ``c'' */ 1390 e->el_map.key[c] = ED_INSERT; 1391 retval = 0; 1392 } 1393 return retval; 1394 } 1395 1396 /* 1397 * read one key from input - handles chars pushed back 1398 * to input stream also 1399 */ 1400 int 1401 rl_read_key() 1402 { 1403 char fooarr[2 * sizeof(int)]; 1404 1405 if (e == NULL || h == NULL) 1406 rl_initialize(); 1407 1408 return el_getc(e, fooarr); 1409 } 1410 1411 /* 1412 * reset the terminal 1413 */ 1414 void 1415 rl_reset_terminal(p) 1416 const char *p; 1417 { 1418 if (h == NULL || e == NULL) 1419 rl_initialize(); 1420 el_reset(e); 1421 } 1422 1423 /* 1424 * insert character ``c'' back into input stream, ``count'' times 1425 */ 1426 int 1427 rl_insert(count, c) 1428 int count, c; 1429 { 1430 char arr[2]; 1431 1432 if (h == NULL || e == NULL) 1433 rl_initialize(); 1434 1435 /* XXX - int -> char conversion can lose on multichars */ 1436 arr[0] = c; 1437 arr[1] = '\0'; 1438 1439 for (; count > 0; count--) 1440 el_push(e, arr); 1441 1442 return 0; 1443 } 1444