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