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