1 /* $NetBSD: filecomplete.c,v 1.70 2022/03/12 15:29:17 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 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include "config.h" 33 34 #if !defined(lint) && !defined(SCCSID) 35 __RCSID("$NetBSD: filecomplete.c,v 1.70 2022/03/12 15:29:17 christos Exp $"); 36 #endif /* not lint && not SCCSID */ 37 38 #include <sys/types.h> 39 #include <sys/stat.h> 40 #include <dirent.h> 41 #include <errno.h> 42 #include <fcntl.h> 43 #include <limits.h> 44 #include <pwd.h> 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <string.h> 48 #include <unistd.h> 49 50 #include "el.h" 51 #include "filecomplete.h" 52 53 static const wchar_t break_chars[] = L" \t\n\"\\'`@$><=;|&{("; 54 55 /********************************/ 56 /* completion functions */ 57 58 /* 59 * does tilde expansion of strings of type ``~user/foo'' 60 * if ``user'' isn't valid user name or ``txt'' doesn't start 61 * w/ '~', returns pointer to strdup()ed copy of ``txt'' 62 * 63 * it's the caller's responsibility to free() the returned string 64 */ 65 char * 66 fn_tilde_expand(const char *txt) 67 { 68 #if defined(HAVE_GETPW_R_POSIX) || defined(HAVE_GETPW_R_DRAFT) 69 struct passwd pwres; 70 char pwbuf[1024]; 71 #endif 72 struct passwd *pass; 73 const char *pos; 74 char *temp; 75 size_t len = 0; 76 77 if (txt[0] != '~') 78 return strdup(txt); 79 80 pos = strchr(txt + 1, '/'); 81 if (pos == NULL) { 82 temp = strdup(txt + 1); 83 if (temp == NULL) 84 return NULL; 85 } else { 86 /* text until string after slash */ 87 len = (size_t)(pos - txt + 1); 88 temp = el_calloc(len, sizeof(*temp)); 89 if (temp == NULL) 90 return NULL; 91 (void)strlcpy(temp, txt + 1, len - 1); 92 } 93 if (temp[0] == 0) { 94 #ifdef HAVE_GETPW_R_POSIX 95 if (getpwuid_r(getuid(), &pwres, pwbuf, sizeof(pwbuf), 96 &pass) != 0) 97 pass = NULL; 98 #elif HAVE_GETPW_R_DRAFT 99 pass = getpwuid_r(getuid(), &pwres, pwbuf, sizeof(pwbuf)); 100 #else 101 pass = getpwuid(getuid()); 102 #endif 103 } else { 104 #ifdef HAVE_GETPW_R_POSIX 105 if (getpwnam_r(temp, &pwres, pwbuf, sizeof(pwbuf), &pass) != 0) 106 pass = NULL; 107 #elif HAVE_GETPW_R_DRAFT 108 pass = getpwnam_r(temp, &pwres, pwbuf, sizeof(pwbuf)); 109 #else 110 pass = getpwnam(temp); 111 #endif 112 } 113 el_free(temp); /* value no more needed */ 114 if (pass == NULL) 115 return strdup(txt); 116 117 /* update pointer txt to point at string immedially following */ 118 /* first slash */ 119 txt += len; 120 121 len = strlen(pass->pw_dir) + 1 + strlen(txt) + 1; 122 temp = el_calloc(len, sizeof(*temp)); 123 if (temp == NULL) 124 return NULL; 125 (void)snprintf(temp, len, "%s/%s", pass->pw_dir, txt); 126 127 return temp; 128 } 129 130 static int 131 needs_escaping(wchar_t c) 132 { 133 switch (c) { 134 case '\'': 135 case '"': 136 case '(': 137 case ')': 138 case '\\': 139 case '<': 140 case '>': 141 case '$': 142 case '#': 143 case ' ': 144 case '\n': 145 case '\t': 146 case '?': 147 case ';': 148 case '`': 149 case '@': 150 case '=': 151 case '|': 152 case '{': 153 case '}': 154 case '&': 155 case '*': 156 case '[': 157 return 1; 158 default: 159 return 0; 160 } 161 } 162 163 static int 164 needs_dquote_escaping(char c) 165 { 166 switch (c) { 167 case '"': 168 case '\\': 169 case '`': 170 case '$': 171 return 1; 172 default: 173 return 0; 174 } 175 } 176 177 178 static wchar_t * 179 unescape_string(const wchar_t *string, size_t length) 180 { 181 size_t i; 182 size_t j = 0; 183 wchar_t *unescaped = el_calloc(length + 1, sizeof(*string)); 184 if (unescaped == NULL) 185 return NULL; 186 for (i = 0; i < length ; i++) { 187 if (string[i] == '\\') 188 continue; 189 unescaped[j++] = string[i]; 190 } 191 unescaped[j] = 0; 192 return unescaped; 193 } 194 195 static char * 196 escape_filename(EditLine * el, const char *filename, int single_match, 197 const char *(*app_func)(const char *)) 198 { 199 size_t original_len = 0; 200 size_t escaped_character_count = 0; 201 size_t offset = 0; 202 size_t newlen; 203 const char *s; 204 char c; 205 size_t s_quoted = 0; /* does the input contain a single quote */ 206 size_t d_quoted = 0; /* does the input contain a double quote */ 207 char *escaped_str; 208 wchar_t *temp = el->el_line.buffer; 209 const char *append_char = NULL; 210 211 if (filename == NULL) 212 return NULL; 213 214 while (temp != el->el_line.cursor) { 215 /* 216 * If we see a single quote but have not seen a double quote 217 * so far set/unset s_quote, unless it is already quoted 218 */ 219 if (temp[0] == '\'' && !d_quoted && 220 (temp == el->el_line.buffer || temp[-1] != '\\')) 221 s_quoted = !s_quoted; 222 /* 223 * vice versa to the above condition 224 */ 225 else if (temp[0] == '"' && !s_quoted) 226 d_quoted = !d_quoted; 227 temp++; 228 } 229 230 /* Count number of special characters so that we can calculate 231 * number of extra bytes needed in the new string 232 */ 233 for (s = filename; *s; s++, original_len++) { 234 c = *s; 235 /* Inside a single quote only single quotes need escaping */ 236 if (s_quoted && c == '\'') { 237 escaped_character_count += 3; 238 continue; 239 } 240 /* Inside double quotes only ", \, ` and $ need escaping */ 241 if (d_quoted && needs_dquote_escaping(c)) { 242 escaped_character_count++; 243 continue; 244 } 245 if (!s_quoted && !d_quoted && needs_escaping(c)) 246 escaped_character_count++; 247 } 248 249 newlen = original_len + escaped_character_count + 1; 250 if (s_quoted || d_quoted) 251 newlen++; 252 253 if (single_match && app_func) 254 newlen++; 255 256 if ((escaped_str = el_malloc(newlen)) == NULL) 257 return NULL; 258 259 for (s = filename; *s; s++) { 260 c = *s; 261 if (!needs_escaping(c)) { 262 /* no escaping is required continue as usual */ 263 escaped_str[offset++] = c; 264 continue; 265 } 266 267 /* single quotes inside single quotes require special handling */ 268 if (c == '\'' && s_quoted) { 269 escaped_str[offset++] = '\''; 270 escaped_str[offset++] = '\\'; 271 escaped_str[offset++] = '\''; 272 escaped_str[offset++] = '\''; 273 continue; 274 } 275 276 /* Otherwise no escaping needed inside single quotes */ 277 if (s_quoted) { 278 escaped_str[offset++] = c; 279 continue; 280 } 281 282 /* No escaping needed inside a double quoted string either 283 * unless we see a '$', '\', '`', or '"' (itself) 284 */ 285 if (d_quoted && !needs_dquote_escaping(c)) { 286 escaped_str[offset++] = c; 287 continue; 288 } 289 290 /* If we reach here that means escaping is actually needed */ 291 escaped_str[offset++] = '\\'; 292 escaped_str[offset++] = c; 293 } 294 295 if (single_match && app_func) { 296 escaped_str[offset] = 0; 297 append_char = app_func(filename); 298 /* we want to append space only if we are not inside quotes */ 299 if (append_char[0] == ' ') { 300 if (!s_quoted && !d_quoted) 301 escaped_str[offset++] = append_char[0]; 302 } else 303 escaped_str[offset++] = append_char[0]; 304 } 305 306 /* close the quotes if single match and the match is not a directory */ 307 if (single_match && (append_char && append_char[0] == ' ')) { 308 if (s_quoted) 309 escaped_str[offset++] = '\''; 310 else if (d_quoted) 311 escaped_str[offset++] = '"'; 312 } 313 314 escaped_str[offset] = 0; 315 return escaped_str; 316 } 317 318 /* 319 * return first found file name starting by the ``text'' or NULL if no 320 * such file can be found 321 * value of ``state'' is ignored 322 * 323 * it's the caller's responsibility to free the returned string 324 */ 325 char * 326 fn_filename_completion_function(const char *text, int state) 327 { 328 static DIR *dir = NULL; 329 static char *filename = NULL, *dirname = NULL, *dirpath = NULL; 330 static size_t filename_len = 0; 331 struct dirent *entry; 332 char *temp; 333 const char *pos; 334 size_t len; 335 336 if (state == 0 || dir == NULL) { 337 pos = strrchr(text, '/'); 338 if (pos) { 339 char *nptr; 340 pos++; 341 nptr = el_realloc(filename, (strlen(pos) + 1) * 342 sizeof(*nptr)); 343 if (nptr == NULL) { 344 el_free(filename); 345 filename = NULL; 346 return NULL; 347 } 348 filename = nptr; 349 (void)strcpy(filename, pos); 350 len = (size_t)(pos - text); /* including last slash */ 351 352 nptr = el_realloc(dirname, (len + 1) * 353 sizeof(*nptr)); 354 if (nptr == NULL) { 355 el_free(dirname); 356 dirname = NULL; 357 return NULL; 358 } 359 dirname = nptr; 360 (void)strlcpy(dirname, text, len + 1); 361 } else { 362 el_free(filename); 363 if (*text == 0) 364 filename = NULL; 365 else { 366 filename = strdup(text); 367 if (filename == NULL) 368 return NULL; 369 } 370 el_free(dirname); 371 dirname = NULL; 372 } 373 374 if (dir != NULL) { 375 (void)closedir(dir); 376 dir = NULL; 377 } 378 379 /* support for ``~user'' syntax */ 380 381 el_free(dirpath); 382 dirpath = NULL; 383 if (dirname == NULL) { 384 if ((dirname = strdup("")) == NULL) 385 return NULL; 386 dirpath = strdup("./"); 387 } else if (*dirname == '~') 388 dirpath = fn_tilde_expand(dirname); 389 else 390 dirpath = strdup(dirname); 391 392 if (dirpath == NULL) 393 return NULL; 394 395 dir = opendir(dirpath); 396 if (!dir) 397 return NULL; /* cannot open the directory */ 398 399 /* will be used in cycle */ 400 filename_len = filename ? strlen(filename) : 0; 401 } 402 403 /* find the match */ 404 while ((entry = readdir(dir)) != NULL) { 405 /* skip . and .. */ 406 if (entry->d_name[0] == '.' && (!entry->d_name[1] 407 || (entry->d_name[1] == '.' && !entry->d_name[2]))) 408 continue; 409 if (filename_len == 0) 410 break; 411 /* otherwise, get first entry where first */ 412 /* filename_len characters are equal */ 413 if (entry->d_name[0] == filename[0] 414 /* Some dirents have d_namlen, but it is not portable. */ 415 && strlen(entry->d_name) >= filename_len 416 && strncmp(entry->d_name, filename, 417 filename_len) == 0) 418 break; 419 } 420 421 if (entry) { /* match found */ 422 423 /* Some dirents have d_namlen, but it is not portable. */ 424 len = strlen(entry->d_name); 425 426 len = strlen(dirname) + len + 1; 427 temp = el_calloc(len, sizeof(*temp)); 428 if (temp == NULL) 429 return NULL; 430 (void)snprintf(temp, len, "%s%s", dirname, entry->d_name); 431 } else { 432 (void)closedir(dir); 433 dir = NULL; 434 temp = NULL; 435 } 436 437 return temp; 438 } 439 440 441 static const char * 442 append_char_function(const char *name) 443 { 444 struct stat stbuf; 445 char *expname = *name == '~' ? fn_tilde_expand(name) : NULL; 446 const char *rs = " "; 447 448 if (stat(expname ? expname : name, &stbuf) == -1) 449 goto out; 450 if (S_ISDIR(stbuf.st_mode)) 451 rs = "/"; 452 out: 453 if (expname) 454 el_free(expname); 455 return rs; 456 } 457 /* 458 * returns list of completions for text given 459 * non-static for readline. 460 */ 461 char ** completion_matches(const char *, char *(*)(const char *, int)); 462 char ** 463 completion_matches(const char *text, char *(*genfunc)(const char *, int)) 464 { 465 char **match_list = NULL, *retstr, *prevstr; 466 size_t match_list_len, max_equal, which, i; 467 size_t matches; 468 469 matches = 0; 470 match_list_len = 1; 471 while ((retstr = (*genfunc) (text, (int)matches)) != NULL) { 472 /* allow for list terminator here */ 473 if (matches + 3 >= match_list_len) { 474 char **nmatch_list; 475 while (matches + 3 >= match_list_len) 476 match_list_len <<= 1; 477 nmatch_list = el_realloc(match_list, 478 match_list_len * sizeof(*nmatch_list)); 479 if (nmatch_list == NULL) { 480 el_free(match_list); 481 return NULL; 482 } 483 match_list = nmatch_list; 484 485 } 486 match_list[++matches] = retstr; 487 } 488 489 if (!match_list) 490 return NULL; /* nothing found */ 491 492 /* find least denominator and insert it to match_list[0] */ 493 which = 2; 494 prevstr = match_list[1]; 495 max_equal = strlen(prevstr); 496 for (; which <= matches; which++) { 497 for (i = 0; i < max_equal && 498 prevstr[i] == match_list[which][i]; i++) 499 continue; 500 max_equal = i; 501 } 502 503 retstr = el_calloc(max_equal + 1, sizeof(*retstr)); 504 if (retstr == NULL) { 505 el_free(match_list); 506 return NULL; 507 } 508 (void)strlcpy(retstr, match_list[1], max_equal + 1); 509 match_list[0] = retstr; 510 511 /* add NULL as last pointer to the array */ 512 match_list[matches + 1] = NULL; 513 514 return match_list; 515 } 516 517 /* 518 * Sort function for qsort(). Just wrapper around strcasecmp(). 519 */ 520 static int 521 _fn_qsort_string_compare(const void *i1, const void *i2) 522 { 523 const char *s1 = ((const char * const *)i1)[0]; 524 const char *s2 = ((const char * const *)i2)[0]; 525 526 return strcasecmp(s1, s2); 527 } 528 529 /* 530 * Display list of strings in columnar format on readline's output stream. 531 * 'matches' is list of strings, 'num' is number of strings in 'matches', 532 * 'width' is maximum length of string in 'matches'. 533 * 534 * matches[0] is not one of the match strings, but it is counted in 535 * num, so the strings are matches[1] *through* matches[num-1]. 536 */ 537 void 538 fn_display_match_list(EditLine * el, char **matches, size_t num, size_t width, 539 const char *(*app_func) (const char *)) 540 { 541 size_t line, lines, col, cols, thisguy; 542 int screenwidth = el->el_terminal.t_size.h; 543 if (app_func == NULL) 544 app_func = append_char_function; 545 546 /* Ignore matches[0]. Avoid 1-based array logic below. */ 547 matches++; 548 num--; 549 550 /* 551 * Find out how many entries can be put on one line; count 552 * with one space between strings the same way it's printed. 553 */ 554 cols = (size_t)screenwidth / (width + 2); 555 if (cols == 0) 556 cols = 1; 557 558 /* how many lines of output, rounded up */ 559 lines = (num + cols - 1) / cols; 560 561 /* Sort the items. */ 562 qsort(matches, num, sizeof(char *), _fn_qsort_string_compare); 563 564 /* 565 * On the ith line print elements i, i+lines, i+lines*2, etc. 566 */ 567 for (line = 0; line < lines; line++) { 568 for (col = 0; col < cols; col++) { 569 thisguy = line + col * lines; 570 if (thisguy >= num) 571 break; 572 (void)fprintf(el->el_outfile, "%s%s%s", 573 col == 0 ? "" : " ", matches[thisguy], 574 (*app_func)(matches[thisguy])); 575 (void)fprintf(el->el_outfile, "%-*s", 576 (int) (width - strlen(matches[thisguy])), ""); 577 } 578 (void)fprintf(el->el_outfile, "\n"); 579 } 580 } 581 582 static wchar_t * 583 find_word_to_complete(const wchar_t * cursor, const wchar_t * buffer, 584 const wchar_t * word_break, const wchar_t * special_prefixes, size_t * length, 585 int do_unescape) 586 { 587 /* We now look backwards for the start of a filename/variable word */ 588 const wchar_t *ctemp = cursor; 589 wchar_t *temp; 590 size_t len; 591 592 /* if the cursor is placed at a slash or a quote, we need to find the 593 * word before it 594 */ 595 if (ctemp > buffer) { 596 switch (ctemp[-1]) { 597 case '\\': 598 case '\'': 599 case '"': 600 ctemp--; 601 break; 602 default: 603 break; 604 } 605 } 606 607 for (;;) { 608 if (ctemp <= buffer) 609 break; 610 if (ctemp - buffer >= 2 && ctemp[-2] == '\\' && 611 needs_escaping(ctemp[-1])) { 612 ctemp -= 2; 613 continue; 614 } 615 if (wcschr(word_break, ctemp[-1])) 616 break; 617 if (special_prefixes && wcschr(special_prefixes, ctemp[-1])) 618 break; 619 ctemp--; 620 } 621 622 len = (size_t) (cursor - ctemp); 623 if (len == 1 && (ctemp[0] == '\'' || ctemp[0] == '"')) { 624 len = 0; 625 ctemp++; 626 } 627 *length = len; 628 if (do_unescape) { 629 wchar_t *unescaped_word = unescape_string(ctemp, len); 630 if (unescaped_word == NULL) 631 return NULL; 632 return unescaped_word; 633 } 634 temp = el_malloc((len + 1) * sizeof(*temp)); 635 (void) wcsncpy(temp, ctemp, len); 636 temp[len] = '\0'; 637 return temp; 638 } 639 640 /* 641 * Complete the word at or before point, 642 * 'what_to_do' says what to do with the completion. 643 * \t means do standard completion. 644 * `?' means list the possible completions. 645 * `*' means insert all of the possible completions. 646 * `!' means to do standard completion, and list all possible completions if 647 * there is more than one. 648 * 649 * Note: '*' support is not implemented 650 * '!' could never be invoked 651 */ 652 int 653 fn_complete2(EditLine *el, 654 char *(*complete_func)(const char *, int), 655 char **(*attempted_completion_function)(const char *, int, int), 656 const wchar_t *word_break, const wchar_t *special_prefixes, 657 const char *(*app_func)(const char *), size_t query_items, 658 int *completion_type, int *over, int *point, int *end, 659 unsigned int flags) 660 { 661 const LineInfoW *li; 662 wchar_t *temp; 663 char **matches; 664 char *completion; 665 size_t len; 666 int what_to_do = '\t'; 667 int retval = CC_NORM; 668 int do_unescape = flags & FN_QUOTE_MATCH; 669 670 if (el->el_state.lastcmd == el->el_state.thiscmd) 671 what_to_do = '?'; 672 673 /* readline's rl_complete() has to be told what we did... */ 674 if (completion_type != NULL) 675 *completion_type = what_to_do; 676 677 if (!complete_func) 678 complete_func = fn_filename_completion_function; 679 if (!app_func) 680 app_func = append_char_function; 681 682 li = el_wline(el); 683 temp = find_word_to_complete(li->cursor, 684 li->buffer, word_break, special_prefixes, &len, do_unescape); 685 if (temp == NULL) 686 goto out; 687 688 /* these can be used by function called in completion_matches() */ 689 /* or (*attempted_completion_function)() */ 690 if (point != NULL) 691 *point = (int)(li->cursor - li->buffer); 692 if (end != NULL) 693 *end = (int)(li->lastchar - li->buffer); 694 695 if (attempted_completion_function) { 696 int cur_off = (int)(li->cursor - li->buffer); 697 matches = (*attempted_completion_function)( 698 ct_encode_string(temp, &el->el_scratch), 699 cur_off - (int)len, cur_off); 700 } else 701 matches = NULL; 702 if (!attempted_completion_function || 703 (over != NULL && !*over && !matches)) 704 matches = completion_matches( 705 ct_encode_string(temp, &el->el_scratch), complete_func); 706 707 if (over != NULL) 708 *over = 0; 709 710 if (matches == NULL) { 711 goto out; 712 } 713 int i; 714 size_t matches_num, maxlen, match_len, match_display=1; 715 int single_match = matches[2] == NULL && 716 (matches[1] == NULL || strcmp(matches[0], matches[1]) == 0); 717 718 retval = CC_REFRESH; 719 720 if (matches[0][0] != '\0') { 721 el_deletestr(el, (int)len); 722 if (flags & FN_QUOTE_MATCH) 723 completion = escape_filename(el, matches[0], 724 single_match, app_func); 725 else 726 completion = strdup(matches[0]); 727 if (completion == NULL) 728 goto out2; 729 730 /* 731 * Replace the completed string with the common part of 732 * all possible matches if there is a possible completion. 733 */ 734 el_winsertstr(el, 735 ct_decode_string(completion, &el->el_scratch)); 736 737 if (single_match && attempted_completion_function && 738 !(flags & FN_QUOTE_MATCH)) 739 { 740 /* 741 * We found an exact match. Add a space after 742 * it, unless we do filename completion and the 743 * object is a directory. Also do necessary 744 * escape quoting 745 */ 746 el_winsertstr(el, ct_decode_string( 747 (*app_func)(completion), &el->el_scratch)); 748 } 749 free(completion); 750 } 751 752 753 if (!single_match && (what_to_do == '!' || what_to_do == '?')) { 754 /* 755 * More than one match and requested to list possible 756 * matches. 757 */ 758 759 for(i = 1, maxlen = 0; matches[i]; i++) { 760 match_len = strlen(matches[i]); 761 if (match_len > maxlen) 762 maxlen = match_len; 763 } 764 /* matches[1] through matches[i-1] are available */ 765 matches_num = (size_t)(i - 1); 766 767 /* newline to get on next line from command line */ 768 (void)fprintf(el->el_outfile, "\n"); 769 770 /* 771 * If there are too many items, ask user for display 772 * confirmation. 773 */ 774 if (matches_num > query_items) { 775 (void)fprintf(el->el_outfile, 776 "Display all %zu possibilities? (y or n) ", 777 matches_num); 778 (void)fflush(el->el_outfile); 779 if (getc(stdin) != 'y') 780 match_display = 0; 781 (void)fprintf(el->el_outfile, "\n"); 782 } 783 784 if (match_display) { 785 /* 786 * Interface of this function requires the 787 * strings be matches[1..num-1] for compat. 788 * We have matches_num strings not counting 789 * the prefix in matches[0], so we need to 790 * add 1 to matches_num for the call. 791 */ 792 fn_display_match_list(el, matches, 793 matches_num+1, maxlen, app_func); 794 } 795 retval = CC_REDISPLAY; 796 } else if (matches[0][0]) { 797 /* 798 * There was some common match, but the name was 799 * not complete enough. Next tab will print possible 800 * completions. 801 */ 802 el_beep(el); 803 } else { 804 /* lcd is not a valid object - further specification */ 805 /* is needed */ 806 el_beep(el); 807 retval = CC_NORM; 808 } 809 810 /* free elements of array and the array itself */ 811 out2: 812 for (i = 0; matches[i]; i++) 813 el_free(matches[i]); 814 el_free(matches); 815 matches = NULL; 816 817 out: 818 el_free(temp); 819 return retval; 820 } 821 822 int 823 fn_complete(EditLine *el, 824 char *(*complete_func)(const char *, int), 825 char **(*attempted_completion_function)(const char *, int, int), 826 const wchar_t *word_break, const wchar_t *special_prefixes, 827 const char *(*app_func)(const char *), size_t query_items, 828 int *completion_type, int *over, int *point, int *end) 829 { 830 return fn_complete2(el, complete_func, attempted_completion_function, 831 word_break, special_prefixes, app_func, query_items, 832 completion_type, over, point, end, 833 attempted_completion_function ? 0 : FN_QUOTE_MATCH); 834 } 835 836 /* 837 * el-compatible wrapper around rl_complete; needed for key binding 838 */ 839 /* ARGSUSED */ 840 unsigned char 841 _el_fn_complete(EditLine *el, int ch __attribute__((__unused__))) 842 { 843 return (unsigned char)fn_complete(el, NULL, NULL, 844 break_chars, NULL, NULL, (size_t)100, 845 NULL, NULL, NULL, NULL); 846 } 847