1 /* $NetBSD: complete.c,v 1.8 2006/10/02 16:43:31 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 1997-2000,2005 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Luke Mewburn. 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 /* Most of this is derived or copied from src/usr.bin/ftp/complete.c (1.41). */ 40 41 42 #ifdef USE_READLINE 43 44 #include <sys/cdefs.h> 45 #ifndef lint 46 __RCSID("$NetBSD: complete.c,v 1.8 2006/10/02 16:43:31 christos Exp $"); 47 #endif /* not lint */ 48 49 /* 50 * FTP user program - command and file completion routines 51 */ 52 53 #include <sys/stat.h> 54 55 #include <ctype.h> 56 #include <err.h> 57 #include <dirent.h> 58 #include <glob.h> 59 #include <stdio.h> 60 #include <stdlib.h> 61 #include <string.h> 62 #include <histedit.h> 63 #include <sys/param.h> 64 #include <stringlist.h> 65 66 #include "rcv.h" /* includes "glob.h" */ 67 #include "extern.h" 68 #include "complete.h" 69 70 /* 71 * Global variables 72 */ 73 static int doglob = 1; /* glob local file names */ 74 75 #define ttyout stdout 76 #define ttywidth screenwidth /* in "glob.h" */ 77 #define ttyheight screenheight /* in "glob.h" */ 78 79 80 /* This should find the first command that matches the name given or 81 * NULL if none. Use the routine from mail in lex.c. 82 */ 83 #define getcmd(w) lex(w) 84 85 86 /************************************************************************/ 87 /* from src/usr.bin/ftp/utils.h (1.135) */ 88 89 /* 90 * List words in stringlist, vertically arranged 91 */ 92 static void 93 list_vertical(StringList *sl) 94 { 95 int i, j; 96 int columns, lines; 97 char *p; 98 size_t w, width; 99 100 width = 0; 101 102 for (i = 0 ; i < sl->sl_cur ; i++) { 103 w = strlen(sl->sl_str[i]); 104 if (w > width) 105 width = w; 106 } 107 width = (width + 8) &~ 7; 108 109 columns = ttywidth / width; 110 if (columns == 0) 111 columns = 1; 112 lines = (sl->sl_cur + columns - 1) / columns; 113 for (i = 0; i < lines; i++) { 114 for (j = 0; j < columns; j++) { 115 p = sl->sl_str[j * lines + i]; 116 if (p) 117 fputs(p, ttyout); 118 if (j * lines + i + lines >= sl->sl_cur) { 119 putc('\n', ttyout); 120 break; 121 } 122 if (p) { 123 w = strlen(p); 124 while (w < width) { 125 w = (w + 8) &~ 7; 126 (void)putc('\t', ttyout); 127 } 128 } 129 } 130 } 131 } 132 133 /* 134 * Copy characters from src into dst, \ quoting characters that require it 135 */ 136 static void 137 ftpvis(char *dst, size_t dstlen, const char *src, size_t srclen) 138 { 139 int di, si; 140 141 for (di = si = 0; 142 src[si] != '\0' && di < dstlen && si < srclen; 143 di++, si++) { 144 switch (src[si]) { 145 case '\\': 146 case ' ': 147 case '\t': 148 case '\r': 149 case '\n': 150 case '"': 151 dst[di++] = '\\'; 152 if (di >= dstlen) 153 break; 154 /* FALLTHROUGH */ 155 default: 156 dst[di] = src[si]; 157 } 158 } 159 dst[di] = '\0'; 160 } 161 162 /* 163 * sl_init() with inbuilt error checking 164 */ 165 static StringList * 166 ftp_sl_init(void) 167 { 168 StringList *p; 169 170 p = sl_init(); 171 if (p == NULL) 172 err(1, "Unable to allocate memory for stringlist"); 173 return (p); 174 } 175 176 177 /* 178 * sl_add() with inbuilt error checking 179 */ 180 static void 181 ftp_sl_add(StringList *sl, char *i) 182 { 183 184 if (sl_add(sl, i) == -1) 185 err(1, "Unable to add `%s' to stringlist", i); 186 } 187 188 /* 189 * strdup() with inbuilt error checking 190 */ 191 static char * 192 ftp_strdup(const char *str) 193 { 194 char *s; 195 196 if (str == NULL) 197 errx(1, "ftp_strdup() called with NULL argument"); 198 s = strdup(str); 199 if (s == NULL) 200 err(1, "Unable to allocate memory for string copy"); 201 return (s); 202 } 203 204 /* 205 * Glob a local file name specification with the expectation of a single 206 * return value. Can't control multiple values being expanded from the 207 * expression, we return only the first. 208 * Returns NULL on error, or a pointer to a buffer containing the filename 209 * that's the caller's responsiblity to free(3) when finished with. 210 */ 211 static char * 212 globulize(const char *pattern) 213 { 214 glob_t gl; 215 int flags; 216 char *p; 217 218 if (!doglob) 219 return (ftp_strdup(pattern)); 220 221 flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE; 222 memset(&gl, 0, sizeof(gl)); 223 if (glob(pattern, flags, NULL, &gl) || gl.gl_pathc == 0) { 224 warnx("%s: not found", pattern); 225 globfree(&gl); 226 return (NULL); 227 } 228 p = ftp_strdup(gl.gl_pathv[0]); 229 globfree(&gl); 230 return (p); 231 } 232 233 /* from src/usr.bin/ftp/utils.h (1.135) */ 234 /************************************************************************/ 235 236 static int 237 comparstr(const void *a, const void *b) 238 { 239 return (strcmp(*(const char * const *)a, *(const char * const *)b)); 240 } 241 242 /* 243 * Determine if complete is ambiguous. If unique, insert. 244 * If no choices, error. If unambiguous prefix, insert that. 245 * Otherwise, list choices. words is assumed to be filtered 246 * to only contain possible choices. 247 * Args: 248 * word word which started the match 249 * list list by default 250 * words stringlist containing possible matches 251 * Returns a result as per el_set(EL_ADDFN, ...) 252 */ 253 static unsigned char 254 complete_ambiguous(EditLine *el, char *word, int list, StringList *words) 255 { 256 char insertstr[MAXPATHLEN]; 257 char *lastmatch, *p; 258 int i, j; 259 size_t matchlen, wordlen; 260 261 wordlen = strlen(word); 262 if (words->sl_cur == 0) 263 return (CC_ERROR); /* no choices available */ 264 265 if (words->sl_cur == 1) { /* only once choice available */ 266 p = words->sl_str[0] + wordlen; 267 if (*p == '\0') /* at end of word? */ 268 return (CC_REFRESH); 269 ftpvis(insertstr, sizeof(insertstr), p, strlen(p)); 270 if (el_insertstr(el, insertstr) == -1) 271 return (CC_ERROR); 272 else 273 return (CC_REFRESH); 274 } 275 276 if (!list) { 277 matchlen = 0; 278 lastmatch = words->sl_str[0]; 279 matchlen = strlen(lastmatch); 280 for (i = 1 ; i < words->sl_cur ; i++) { 281 for (j = wordlen ; j < strlen(words->sl_str[i]); j++) 282 if (lastmatch[j] != words->sl_str[i][j]) 283 break; 284 if (j < matchlen) 285 matchlen = j; 286 } 287 if (matchlen > wordlen) { 288 ftpvis(insertstr, sizeof(insertstr), 289 lastmatch + wordlen, matchlen - wordlen); 290 if (el_insertstr(el, insertstr) == -1) 291 return (CC_ERROR); 292 else 293 return (CC_REFRESH_BEEP); 294 } 295 } 296 297 putc('\n', ttyout); 298 qsort(words->sl_str, words->sl_cur, sizeof(char *), comparstr); 299 list_vertical(words); 300 return (CC_REDISPLAY); 301 } 302 303 /* 304 * Complete a mail command. 305 */ 306 static unsigned char 307 complete_command(EditLine *el, char *word, int list) 308 { 309 const struct cmd *c; 310 StringList *words; 311 size_t wordlen; 312 unsigned char rv; 313 314 words = ftp_sl_init(); 315 wordlen = strlen(word); 316 317 for (c = cmdtab; c->c_name != NULL; c++) { 318 if (wordlen > strlen(c->c_name)) 319 continue; 320 if (strncmp(word, c->c_name, wordlen) == 0) 321 ftp_sl_add(words, __UNCONST(c->c_name)); 322 } 323 324 rv = complete_ambiguous(el, word, list, words); 325 if (rv == CC_REFRESH) { 326 if (el_insertstr(el, " ") == -1) 327 rv = CC_ERROR; 328 } 329 sl_free(words, 0); 330 return (rv); 331 } 332 333 /* 334 * Complete a local filename. 335 */ 336 static unsigned char 337 complete_filename(EditLine *el, char *word, int list) 338 { 339 StringList *words; 340 char dir[MAXPATHLEN]; 341 char *fname; 342 DIR *dd; 343 struct dirent *dp; 344 unsigned char rv; 345 size_t len; 346 347 if ((fname = strrchr(word, '/')) == NULL) { 348 dir[0] = '.'; 349 dir[1] = '\0'; 350 fname = word; 351 } else { 352 if (fname == word) { 353 dir[0] = '/'; 354 dir[1] = '\0'; 355 } else 356 (void)strlcpy(dir, word, fname - word + 1); 357 fname++; 358 } 359 if (dir[0] == '~') { 360 char *p; 361 362 if ((p = globulize(dir)) == NULL) 363 return (CC_ERROR); 364 (void)strlcpy(dir, p, sizeof(dir)); 365 free(p); 366 } 367 368 if ((dd = opendir(dir)) == NULL) 369 return (CC_ERROR); 370 371 words = ftp_sl_init(); 372 len = strlen(fname); 373 374 for (dp = readdir(dd); dp != NULL; dp = readdir(dd)) { 375 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 376 continue; 377 378 #if defined(DIRENT_MISSING_D_NAMLEN) 379 if (len > strlen(dp->d_name)) 380 continue; 381 #else 382 if (len > dp->d_namlen) 383 continue; 384 #endif 385 if (strncmp(fname, dp->d_name, len) == 0) { 386 char *tcp; 387 388 tcp = ftp_strdup(dp->d_name); 389 ftp_sl_add(words, tcp); 390 } 391 } 392 closedir(dd); 393 394 rv = complete_ambiguous(el, fname, list, words); 395 if (rv == CC_REFRESH) { 396 struct stat sb; 397 char path[MAXPATHLEN]; 398 399 (void)strlcpy(path, dir, sizeof(path)); 400 (void)strlcat(path, "/", sizeof(path)); 401 (void)strlcat(path, words->sl_str[0], sizeof(path)); 402 403 if (stat(path, &sb) >= 0) { 404 char suffix[2] = " "; 405 406 if (S_ISDIR(sb.st_mode)) 407 suffix[0] = '/'; 408 if (el_insertstr(el, suffix) == -1) 409 rv = CC_ERROR; 410 } 411 } 412 sl_free(words, 1); 413 return (rv); 414 } 415 416 static int 417 find_execs(char *word, char *path, StringList *list) 418 { 419 char *sep; 420 char *dir=path; 421 DIR *dd; 422 struct dirent *dp; 423 int len = strlen(word); 424 uid_t uid = getuid(); 425 gid_t gid = getgid(); 426 427 for (sep=dir ; sep ; dir=sep+1) { 428 if ((sep=strchr(dir, ':')) != NULL) { 429 *sep=0; 430 } 431 432 if ((dd = opendir(dir)) == NULL) { 433 perror("dir"); 434 return -1; 435 } 436 437 for (dp = readdir(dd); dp != NULL; dp = readdir(dd)) { 438 439 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 440 continue; 441 442 #if defined(DIRENT_MISSING_D_NAMLEN) 443 if (len > strlen(dp->d_name)) 444 continue; 445 #else 446 if (len > dp->d_namlen) 447 continue; 448 #endif 449 450 if (strncmp(word, dp->d_name, len) == 0) { 451 struct stat sb; 452 char pathname[ MAXPATHLEN ]; 453 unsigned mask; 454 455 snprintf(pathname, sizeof(pathname), "%s/%s", dir, dp->d_name); 456 if (stat(pathname, &sb) != 0) { 457 perror(pathname); 458 continue; 459 } 460 461 mask = 0001; 462 if (sb.st_uid == uid) mask |= 0100; 463 if (sb.st_gid == gid) mask |= 0010; 464 465 if ((sb.st_mode & mask) != 0) { 466 char *tcp; 467 tcp = strdup(dp->d_name); 468 sl_add(list, tcp); 469 } 470 } 471 472 } 473 474 closedir(dd); 475 } 476 477 return 0; 478 } 479 480 481 /* 482 * Complete a local executable 483 */ 484 static unsigned char 485 complete_executable(EditLine *el, char *word, int list) 486 { 487 StringList *words; 488 char dir[ MAXPATHLEN ]; 489 char *fname; 490 unsigned char rv; 491 int error; 492 493 if ((fname = strrchr(word, '/')) == NULL) { 494 dir[0] = '\0'; /* walk the path */ 495 fname = word; 496 } else { 497 if (fname == word) { 498 dir[0] = '/'; 499 dir[1] = '\0'; 500 } else { 501 (void)strncpy(dir, word, fname - word); 502 dir[fname - word] = '\0'; 503 } 504 fname++; 505 } 506 507 words = sl_init(); 508 509 if (*dir == '\0') { /* walk path */ 510 char *env; 511 char *path; 512 int len; 513 env = getenv("PATH"); 514 len = strlen(env); 515 path = salloc(len + 1); 516 strcpy(path, env); 517 error = find_execs(word, path, words); 518 } 519 else { /* check specified dir only */ 520 error = find_execs(word, dir, words); 521 } 522 if (error != 0) 523 return CC_ERROR; 524 525 rv = complete_ambiguous(el, fname, list, words); 526 527 if (rv == CC_REFRESH) 528 if (el_insertstr(el, " ") == -1) 529 rv = CC_ERROR; 530 531 sl_free(words, 1); 532 533 return (rv); 534 } 535 536 537 static unsigned 538 char complete_set(EditLine *el, char *word, int list) 539 { 540 struct var *vp; 541 char **ap; 542 char **p; 543 int h; 544 int s; 545 int len = strlen(word); 546 StringList *words; 547 unsigned char rv; 548 549 words = sl_init(); 550 551 /* allocate space for variables table */ 552 for (h = 0, s = 1; h < HSHSIZE; h++) 553 for (vp = variables[h]; vp != NULL; vp = vp->v_link) 554 s++; 555 ap = (char **) salloc(s * sizeof *ap); 556 557 /* save pointers */ 558 for (h = 0, p = ap; h < HSHSIZE; h++) 559 for (vp = variables[h]; vp != NULL; vp = vp->v_link) 560 *p++ = vp->v_name; 561 *p = NULL; 562 563 /* sort pointers */ 564 sort(ap); 565 566 /* complete on list */ 567 for (p = ap; *p != NULL; p++) { 568 if (len == 0 || strncmp(*p, word, len) == 0) { 569 char *tcp; 570 tcp = strdup(*p); 571 sl_add(words, tcp); 572 } 573 } 574 575 rv = complete_ambiguous(el, word, list, words); 576 577 sl_free(words, 1); 578 579 return(rv); 580 } 581 582 583 584 585 586 static unsigned char 587 complete_alias(EditLine *el, char *word, int list) 588 { 589 struct grouphead *gh; 590 char **ap; 591 char **p; 592 int h; 593 int s; 594 int len = strlen(word); 595 StringList *words; 596 unsigned char rv; 597 598 words = sl_init(); 599 600 /* allocate space for alias table */ 601 for (h = 0, s = 1; h < HSHSIZE; h++) 602 for (gh = groups[h]; gh != NULL; gh = gh->g_link) 603 s++; 604 ap = (char **) salloc(s * sizeof *ap); 605 606 /* save pointers */ 607 for (h = 0, p = ap; h < HSHSIZE; h++) 608 for (gh = groups[h]; gh != NULL; gh = gh->g_link) 609 *p++ = gh->g_name; 610 *p = NULL; 611 612 /* sort pointers */ 613 sort(ap); 614 615 /* complete on list */ 616 for (p = ap; *p != NULL; p++) { 617 if (len == 0 || strncmp(*p, word, len) == 0) { 618 char *tcp; 619 tcp = strdup(*p); 620 sl_add(words, tcp); 621 } 622 } 623 624 rv = complete_ambiguous(el, word, list, words); 625 if (rv == CC_REFRESH) 626 if (el_insertstr(el, " ") == -1) 627 rv = CC_ERROR; 628 629 sl_free(words, 1); 630 631 return(rv); 632 } 633 634 635 636 static char *stringbase; /* current scan point in line buffer */ 637 static char *argbase; /* current storage point in arg buffer */ 638 static StringList *marg_sl; /* stringlist containing margv */ 639 static int margc; /* count of arguments on input line */ 640 #define margv (marg_sl->sl_str) /* args parsed from input line */ 641 642 static char *cursor_pos; 643 static int cursor_argc; 644 static int cursor_argo; 645 646 static void 647 init_complete(void) 648 { 649 marg_sl = sl_init(); 650 return; 651 } 652 653 654 655 /************************************************************************/ 656 /* from /usr/src/usr.bin/ftp/main.c(1.101) */ 657 658 static int slrflag; 659 static char *altarg; /* argv[1] with no shell-like preprocessing */ 660 661 #ifdef NO_EDITCOMPLETE 662 #define INC_CHKCURSOR(x) (x)++ 663 #else /* !NO_EDITCOMPLETE */ 664 #define INC_CHKCURSOR(x) \ 665 do { \ 666 (x)++ ; \ 667 if (x == cursor_pos) { \ 668 cursor_argc = margc; \ 669 cursor_argo = ap - argbase; \ 670 cursor_pos = NULL; \ 671 } \ 672 } while(0) 673 #endif /* !NO_EDITCOMPLETE */ 674 675 /* 676 * Parse string into argbuf; 677 * implemented with FSM to 678 * handle quoting and strings 679 */ 680 static char * 681 slurpstring(void) 682 { 683 int got_one = 0; 684 char *sb = stringbase; 685 char *ap = argbase; 686 char *tmp = argbase; /* will return this if token found */ 687 688 if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */ 689 switch (slrflag) { /* and $ as token for macro invoke */ 690 case 0: 691 slrflag++; 692 INC_CHKCURSOR(stringbase); 693 return __UNCONST((*sb == '!') ? "!" : "$"); 694 /* NOTREACHED */ 695 case 1: 696 slrflag++; 697 altarg = stringbase; 698 break; 699 default: 700 break; 701 } 702 } 703 704 S0: 705 switch (*sb) { 706 707 case '\0': 708 goto OUT; 709 710 case ' ': 711 case '\t': 712 INC_CHKCURSOR(sb); 713 goto S0; 714 715 default: 716 switch (slrflag) { 717 case 0: 718 slrflag++; 719 break; 720 case 1: 721 slrflag++; 722 altarg = sb; 723 break; 724 default: 725 break; 726 } 727 goto S1; 728 } 729 730 S1: 731 switch (*sb) { 732 733 case ' ': 734 case '\t': 735 case '\0': 736 goto OUT; /* end of token */ 737 738 case '\\': 739 INC_CHKCURSOR(sb); 740 goto S2; /* slurp next character */ 741 742 case '"': 743 INC_CHKCURSOR(sb); 744 goto S3; /* slurp quoted string */ 745 746 default: 747 /* the first arg (command) is special - see execute() in lex.c */ 748 if (margc == 0 && got_one && 749 strchr(" \t0123456789$^.:/-+*'\"", *sb)) 750 goto OUT; 751 752 *ap = *sb; /* add character to token */ 753 ap++; 754 INC_CHKCURSOR(sb); 755 got_one = 1; 756 goto S1; 757 } 758 759 S2: 760 switch (*sb) { 761 762 case '\0': 763 goto OUT; 764 765 default: 766 *ap = *sb; 767 ap++; 768 INC_CHKCURSOR(sb); 769 got_one = 1; 770 goto S1; 771 } 772 773 S3: 774 switch (*sb) { 775 776 case '\0': 777 goto OUT; 778 779 case '"': 780 INC_CHKCURSOR(sb); 781 goto S1; 782 783 default: 784 *ap = *sb; 785 ap++; 786 INC_CHKCURSOR(sb); 787 got_one = 1; 788 goto S3; 789 } 790 791 OUT: 792 if (got_one) 793 *ap++ = '\0'; 794 argbase = ap; /* update storage pointer */ 795 stringbase = sb; /* update scan pointer */ 796 if (got_one) { 797 return (tmp); 798 } 799 switch (slrflag) { 800 case 0: 801 slrflag++; 802 break; 803 case 1: 804 slrflag++; 805 altarg = NULL; 806 break; 807 default: 808 break; 809 } 810 return (NULL); 811 } 812 813 814 /* 815 * Slice a string up into argc/argv. 816 */ 817 static void 818 makeargv(char *line) 819 { 820 static char argbuf[ LINESIZE ]; /* argument storage buffer */ 821 char *argp; 822 823 stringbase = line; /* scan from first of buffer */ 824 argbase = argbuf; /* store from first of buffer */ 825 slrflag = 0; 826 marg_sl->sl_cur = 0; /* reset to start of marg_sl */ 827 for (margc = 0; ; margc++) { 828 argp = slurpstring(); 829 ftp_sl_add(marg_sl, argp); 830 if (argp == NULL) 831 break; 832 } 833 #ifndef NO_EDITCOMPLETE 834 if (cursor_pos == line) { 835 cursor_argc = 0; 836 cursor_argo = 0; 837 } else if (cursor_pos != NULL) { 838 cursor_argc = margc; 839 cursor_argo = strlen(margv[margc-1]); 840 } 841 #endif /* !NO_EDITCOMPLETE */ 842 } 843 844 /* from /usr/src/usr.bin/ftp/main.c(1.101) */ 845 /************************************************************************/ 846 847 848 /* 849 * Generic complete routine 850 */ 851 static unsigned char 852 complete(EditLine *el, int ch) 853 { 854 static char line[LINESIZE]; /* input line buffer */ 855 static char word[LINESIZE]; 856 static int lastc_argc, lastc_argo; 857 858 const struct cmd *c; 859 const LineInfo *lf; 860 int celems, dolist, cmpltype; 861 size_t len; 862 863 lf = el_line(el); 864 len = lf->lastchar - lf->buffer; 865 #if 1 866 if (ch == 04) { /* CTRL-D is special */ 867 if (len == 0) 868 return (CC_EOF); 869 if (lf->lastchar != lf->cursor) { 870 el_push(el, __UNCONST("")); /* delete current char without using ^D */ 871 return (CC_NORM); 872 } 873 } 874 #endif 875 if (len >= sizeof(line)) 876 return (CC_ERROR); 877 (void)strlcpy(line, lf->buffer, len + 1); 878 cursor_pos = line + (lf->cursor - lf->buffer); 879 lastc_argc = cursor_argc; /* remember last cursor pos */ 880 lastc_argo = cursor_argo; 881 makeargv(line); /* build argc/argv of current line */ 882 883 if (cursor_argo >= sizeof(word)) 884 return (CC_ERROR); 885 886 dolist = 0; 887 /* if cursor and word are the same, list alternatives */ 888 if (lastc_argc == cursor_argc && lastc_argo == cursor_argo 889 && strncmp(word, margv[cursor_argc] ? margv[cursor_argc] : "", 890 cursor_argo) == 0) 891 dolist = 1; 892 else if (cursor_argc < margc) 893 (void)strlcpy(word, margv[cursor_argc], cursor_argo + 1); 894 word[cursor_argo] = '\0'; 895 896 if (cursor_argc == 0) 897 return (complete_command(el, word, dolist)); 898 899 c = getcmd(margv[0]); 900 if (c == NULL) 901 return (CC_ERROR); 902 celems = strlen(c->c_complete); 903 904 /* check for 'continuation' completes (which are uppercase) */ 905 if ((cursor_argc > celems) && (celems > 0) 906 && isupper((unsigned char) c->c_complete[celems-1])) 907 cursor_argc = celems; 908 909 if (cursor_argc > celems) 910 return (CC_ERROR); 911 912 cmpltype = c->c_complete[cursor_argc - 1]; 913 switch (cmpltype) { 914 case 'a': /* alias complete */ 915 case 'A': 916 return (complete_alias(el, word, dolist)); 917 918 case 'c': /* command complete */ 919 case 'C': 920 return (complete_command(el, word, dolist)); 921 922 case 'f': /* filename complete */ 923 case 'F': 924 return (complete_filename(el, word, dolist)); 925 926 case 'n': /* no complete */ 927 case 'N': /* no complete */ 928 return (CC_ERROR); 929 930 case 's': 931 case 'S': 932 return (complete_set(el, word, dolist)); 933 934 case 'x': /* executable complete */ 935 case 'X': 936 return (complete_executable(el, word, dolist)); 937 938 default: 939 errx(1, "unknown complete type `%c'", cmpltype); 940 return (CC_ERROR); 941 } 942 /* NOTREACHED */ 943 } 944 945 946 /*************************************************************************/ 947 /* Most of this was originally taken directly from the readline manpage. */ 948 949 static struct { 950 EditLine *el; /* editline(3) with completion and history */ 951 EditLine *elo; /* editline(3) editline only, no completion */ 952 History *hist; /* editline(3) history structure */ 953 const char *prompt; /* prompt */ 954 } rl_global = { 955 .el = NULL, 956 .hist = NULL, 957 .prompt = NULL 958 }; 959 960 char * 961 rl_gets(const char *prompt) 962 { 963 int cnt; 964 const char *buf; 965 HistEvent ev; 966 static char line[LINE_MAX]; 967 968 rl_global.prompt = prompt; 969 buf = el_gets(rl_global.el, &cnt); 970 971 if (buf == NULL || cnt <= 0) 972 return NULL; 973 974 /* enter the line into history */ 975 if (history(rl_global.hist, &ev, H_ENTER, buf) == 0) 976 printf("Failed history entry: %s", buf); 977 #ifdef DEBUG 978 else 979 printf("history entry: %s\n", ev.str); 980 #endif 981 982 cnt--; /* trash the trailing LF */ 983 cnt = MIN(sizeof(line) - 1, cnt); 984 (void)memcpy(line, buf, cnt); 985 line[cnt] = '\0'; 986 987 return line; 988 } 989 990 991 /* 992 * Edit a line containing string, with no history or completion. 993 */ 994 char * 995 rl_getline(const char *prompt, char *string) 996 { 997 static char line[LINE_MAX]; 998 const char *buf; 999 int cnt; 1000 1001 rl_global.prompt = prompt; 1002 1003 if (string) 1004 el_push(rl_global.elo, string); 1005 1006 buf = el_gets(rl_global.elo, &cnt); 1007 if (buf == NULL || cnt <= 0) { 1008 if (cnt == 0) 1009 fputc('\n', stdout); 1010 line[0] = '\0'; 1011 return line; 1012 } 1013 1014 cnt--; 1015 cnt = MIN(sizeof(line) - 1, cnt); 1016 (void)memcpy(line, buf, cnt); 1017 line[cnt] = '\0'; 1018 1019 return line; 1020 } 1021 1022 1023 static const char * 1024 show_prompt(EditLine *e __attribute__((unused))) 1025 { 1026 return rl_global.prompt; 1027 } 1028 1029 void 1030 init_readline(void) 1031 { 1032 HistEvent ev; 1033 const char *el_editor; 1034 const char *el_history_size; 1035 char *el_completion_keys; 1036 1037 rl_global.hist = history_init(); /* init the builtin history */ 1038 el_history_size = value("el_history_size"); 1039 if (el_history_size == NULL) 1040 el_history_size = "0"; 1041 if (history(rl_global.hist, &ev, H_SETSIZE, atoi(el_history_size))) 1042 printf("history: %s\n", ev.str); 1043 1044 rl_global.el = el_init(getprogname(), stdin, stdout, stderr); 1045 rl_global.elo = el_init(getprogname(), stdin, stdout, stderr); 1046 1047 el_editor = value("el_editor"); 1048 if (el_editor) { 1049 el_set(rl_global.el, EL_EDITOR, el_editor); 1050 el_set(rl_global.elo, EL_EDITOR, el_editor); 1051 } 1052 1053 el_set(rl_global.el, EL_PROMPT, show_prompt); 1054 el_set(rl_global.elo, EL_PROMPT, show_prompt); 1055 el_set(rl_global.el, EL_HIST, history, rl_global.hist); /* use history */ 1056 el_source(rl_global.el, NULL); /* read ~/.editrc */ 1057 1058 /* add local file completion, bind to TAB */ 1059 el_set(rl_global.el, EL_ADDFN, "mail-complete", 1060 "Context sensitive argument completion", 1061 complete); 1062 1063 el_completion_keys = value("el_completion_keys"); 1064 if (el_completion_keys && *el_completion_keys) { 1065 struct name *np, *nq; 1066 np = lexpand(el_completion_keys, 0); 1067 for (nq = np ; nq ; nq = nq->n_flink) 1068 el_set(rl_global.el, EL_BIND, nq->n_name, "mail-complete", NULL); 1069 } 1070 1071 init_complete(); 1072 1073 el_set(rl_global.el, EL_SIGNAL, 1); 1074 el_set(rl_global.elo, EL_SIGNAL, 1); 1075 1076 return; 1077 } 1078 1079 /************************************************************************/ 1080 #endif /* USE_READLINE */ 1081