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