1209136Sjilles/*- 2209136Sjilles * Copyright (c) 1997 The NetBSD Foundation, Inc. 3209136Sjilles * All rights reserved. 4209136Sjilles * 5209136Sjilles * This code is derived from software contributed to The NetBSD Foundation 6209136Sjilles * by Jaromir Dolecek. 7209136Sjilles * 8209136Sjilles * Redistribution and use in source and binary forms, with or without 9209136Sjilles * modification, are permitted provided that the following conditions 10209136Sjilles * are met: 11209136Sjilles * 1. Redistributions of source code must retain the above copyright 12209136Sjilles * notice, this list of conditions and the following disclaimer. 13209136Sjilles * 2. Redistributions in binary form must reproduce the above copyright 14209136Sjilles * notice, this list of conditions and the following disclaimer in the 15209136Sjilles * documentation and/or other materials provided with the distribution. 16209136Sjilles * 17209136Sjilles * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 18209136Sjilles * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19209136Sjilles * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20209136Sjilles * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 21209136Sjilles * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22209136Sjilles * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23209136Sjilles * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24209136Sjilles * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25209136Sjilles * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26209136Sjilles * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27209136Sjilles * POSSIBILITY OF SUCH DAMAGE. 28209136Sjilles * 29209136Sjilles * $NetBSD: filecomplete.c,v 1.19 2010/06/01 18:20:26 christos Exp $ 30209136Sjilles */ 31209136Sjilles 32209136Sjilles#include <sys/cdefs.h> 33209136Sjilles__FBSDID("$FreeBSD$"); 34209136Sjilles 35209136Sjilles#include <sys/types.h> 36209136Sjilles#include <sys/stat.h> 37209136Sjilles#include <stdio.h> 38209136Sjilles#include <dirent.h> 39209136Sjilles#include <string.h> 40209136Sjilles#include <pwd.h> 41209136Sjilles#include <ctype.h> 42209136Sjilles#include <stdlib.h> 43209136Sjilles#include <unistd.h> 44209136Sjilles#include <limits.h> 45209136Sjilles#include <errno.h> 46209136Sjilles#include <fcntl.h> 47209136Sjilles#include <vis.h> 48209136Sjilles#include "el.h" 49209136Sjilles#include "fcns.h" /* for EL_NUM_FCNS */ 50209136Sjilles#include "histedit.h" 51209136Sjilles#include "filecomplete.h" 52209136Sjilles 53209224Sjillesstatic char break_chars[] = { ' ', '\t', '\n', '"', '\\', '\'', '`', 54209224Sjilles '>', '<', '=', ';', '|', '&', '{', '(', '\0' }; 55209219Sjilles/* Tilde is deliberately omitted here, we treat it specially. */ 56209224Sjillesstatic char extra_quote_chars[] = { ')', '}', '*', '?', '[', '$', '\0' }; 57209136Sjilles 58209136Sjilles 59209136Sjilles/********************************/ 60209136Sjilles/* completion functions */ 61209136Sjilles 62209136Sjilles/* 63209136Sjilles * does tilde expansion of strings of type ``~user/foo'' 64209136Sjilles * if ``user'' isn't valid user name or ``txt'' doesn't start 65209136Sjilles * w/ '~', returns pointer to strdup()ed copy of ``txt'' 66209136Sjilles * 67209136Sjilles * it's callers's responsibility to free() returned string 68209136Sjilles */ 69209136Sjilleschar * 70209136Sjillesfn_tilde_expand(const char *txt) 71209136Sjilles{ 72209136Sjilles struct passwd pwres, *pass; 73209136Sjilles char *temp; 74209136Sjilles size_t len = 0; 75209136Sjilles char pwbuf[1024]; 76209136Sjilles 77209136Sjilles if (txt[0] != '~') 78209136Sjilles return (strdup(txt)); 79209136Sjilles 80209136Sjilles temp = strchr(txt + 1, '/'); 81209136Sjilles if (temp == NULL) { 82209136Sjilles temp = strdup(txt + 1); 83209136Sjilles if (temp == NULL) 84209136Sjilles return NULL; 85209136Sjilles } else { 86209136Sjilles len = temp - txt + 1; /* text until string after slash */ 87209136Sjilles temp = malloc(len); 88209136Sjilles if (temp == NULL) 89209136Sjilles return NULL; 90209136Sjilles (void)strncpy(temp, txt + 1, len - 2); 91209136Sjilles temp[len - 2] = '\0'; 92209136Sjilles } 93209136Sjilles if (temp[0] == 0) { 94209136Sjilles if (getpwuid_r(getuid(), &pwres, pwbuf, sizeof(pwbuf), &pass) != 0) 95209136Sjilles pass = NULL; 96209136Sjilles } else { 97209136Sjilles if (getpwnam_r(temp, &pwres, pwbuf, sizeof(pwbuf), &pass) != 0) 98209136Sjilles pass = NULL; 99209136Sjilles } 100209136Sjilles free(temp); /* value no more needed */ 101209136Sjilles if (pass == NULL) 102209136Sjilles return (strdup(txt)); 103209136Sjilles 104209136Sjilles /* update pointer txt to point at string immediately following */ 105209136Sjilles /* first slash */ 106209136Sjilles txt += len; 107209136Sjilles 108209136Sjilles temp = malloc(strlen(pass->pw_dir) + 1 + strlen(txt) + 1); 109209136Sjilles if (temp == NULL) 110209136Sjilles return NULL; 111209136Sjilles (void)sprintf(temp, "%s/%s", pass->pw_dir, txt); 112209136Sjilles 113209136Sjilles return (temp); 114209136Sjilles} 115209136Sjilles 116209136Sjilles 117209136Sjilles/* 118209136Sjilles * return first found file name starting by the ``text'' or NULL if no 119209136Sjilles * such file can be found 120209136Sjilles * value of ``state'' is ignored 121209136Sjilles * 122209136Sjilles * it's caller's responsibility to free returned string 123209136Sjilles */ 124209136Sjilleschar * 125209136Sjillesfn_filename_completion_function(const char *text, int state) 126209136Sjilles{ 127209136Sjilles static DIR *dir = NULL; 128209136Sjilles static char *filename = NULL, *dirname = NULL, *dirpath = NULL; 129209136Sjilles static size_t filename_len = 0; 130209136Sjilles struct dirent *entry; 131209136Sjilles char *temp; 132209136Sjilles size_t len; 133209136Sjilles 134209136Sjilles if (state == 0 || dir == NULL) { 135209136Sjilles temp = strrchr(text, '/'); 136209136Sjilles if (temp) { 137209136Sjilles char *nptr; 138209136Sjilles temp++; 139209136Sjilles nptr = realloc(filename, strlen(temp) + 1); 140209136Sjilles if (nptr == NULL) { 141209136Sjilles free(filename); 142209136Sjilles filename = NULL; 143209136Sjilles return NULL; 144209136Sjilles } 145209136Sjilles filename = nptr; 146209136Sjilles (void)strcpy(filename, temp); 147209136Sjilles len = temp - text; /* including last slash */ 148209136Sjilles 149209136Sjilles nptr = realloc(dirname, len + 1); 150209136Sjilles if (nptr == NULL) { 151209136Sjilles free(dirname); 152209136Sjilles dirname = NULL; 153209136Sjilles return NULL; 154209136Sjilles } 155209136Sjilles dirname = nptr; 156209136Sjilles (void)strncpy(dirname, text, len); 157209136Sjilles dirname[len] = '\0'; 158209136Sjilles } else { 159209136Sjilles free(filename); 160209136Sjilles if (*text == 0) 161209136Sjilles filename = NULL; 162209136Sjilles else { 163209136Sjilles filename = strdup(text); 164209136Sjilles if (filename == NULL) 165209136Sjilles return NULL; 166209136Sjilles } 167209136Sjilles free(dirname); 168209136Sjilles dirname = NULL; 169209136Sjilles } 170209136Sjilles 171209136Sjilles if (dir != NULL) { 172209136Sjilles (void)closedir(dir); 173209136Sjilles dir = NULL; 174209136Sjilles } 175209136Sjilles 176209136Sjilles /* support for ``~user'' syntax */ 177209136Sjilles 178209136Sjilles free(dirpath); 179209136Sjilles dirpath = NULL; 180209136Sjilles if (dirname == NULL) { 181209136Sjilles if ((dirname = strdup("")) == NULL) 182209136Sjilles return NULL; 183209136Sjilles dirpath = strdup("./"); 184209136Sjilles } else if (*dirname == '~') 185209136Sjilles dirpath = fn_tilde_expand(dirname); 186209136Sjilles else 187209136Sjilles dirpath = strdup(dirname); 188209136Sjilles 189209136Sjilles if (dirpath == NULL) 190209136Sjilles return NULL; 191209136Sjilles 192209136Sjilles dir = opendir(dirpath); 193209136Sjilles if (!dir) 194209136Sjilles return (NULL); /* cannot open the directory */ 195209136Sjilles 196209136Sjilles /* will be used in cycle */ 197209136Sjilles filename_len = filename ? strlen(filename) : 0; 198209136Sjilles } 199209136Sjilles 200209136Sjilles /* find the match */ 201209136Sjilles while ((entry = readdir(dir)) != NULL) { 202209136Sjilles /* skip . and .. */ 203209136Sjilles if (entry->d_name[0] == '.' && (!entry->d_name[1] 204209136Sjilles || (entry->d_name[1] == '.' && !entry->d_name[2]))) 205209136Sjilles continue; 206209136Sjilles if (filename_len == 0) 207209136Sjilles break; 208209136Sjilles /* otherwise, get first entry where first */ 209209136Sjilles /* filename_len characters are equal */ 210209136Sjilles if (entry->d_name[0] == filename[0] 211209136Sjilles && entry->d_namlen >= filename_len 212209136Sjilles && strncmp(entry->d_name, filename, 213209136Sjilles filename_len) == 0) 214209136Sjilles break; 215209136Sjilles } 216209136Sjilles 217209136Sjilles if (entry) { /* match found */ 218209136Sjilles len = entry->d_namlen; 219209136Sjilles 220209136Sjilles temp = malloc(strlen(dirname) + len + 1); 221209136Sjilles if (temp == NULL) 222209136Sjilles return NULL; 223209136Sjilles (void)sprintf(temp, "%s%s", dirname, entry->d_name); 224209136Sjilles } else { 225209136Sjilles (void)closedir(dir); 226209136Sjilles dir = NULL; 227209136Sjilles temp = NULL; 228209136Sjilles } 229209136Sjilles 230209136Sjilles return (temp); 231209136Sjilles} 232209136Sjilles 233209136Sjilles 234209136Sjillesstatic const char * 235209136Sjillesappend_char_function(const char *name) 236209136Sjilles{ 237209136Sjilles struct stat stbuf; 238209136Sjilles char *expname = *name == '~' ? fn_tilde_expand(name) : NULL; 239209136Sjilles const char *rs = " "; 240209136Sjilles 241209136Sjilles if (stat(expname ? expname : name, &stbuf) == -1) 242209136Sjilles goto out; 243209136Sjilles if (S_ISDIR(stbuf.st_mode)) 244209136Sjilles rs = "/"; 245209136Sjillesout: 246209136Sjilles if (expname) 247209136Sjilles free(expname); 248209136Sjilles return rs; 249209136Sjilles} 250209136Sjilles 251209136Sjilles 252209136Sjilles/* 253209136Sjilles * returns list of completions for text given 254209136Sjilles * non-static for readline. 255209136Sjilles */ 256209136Sjilleschar ** completion_matches(const char *, char *(*)(const char *, int)); 257209136Sjilleschar ** 258209136Sjillescompletion_matches(const char *text, char *(*genfunc)(const char *, int)) 259209136Sjilles{ 260209136Sjilles char **match_list = NULL, *retstr, *prevstr; 261209136Sjilles size_t match_list_len, max_equal, which, i; 262209136Sjilles size_t matches; 263209136Sjilles 264209136Sjilles matches = 0; 265209136Sjilles match_list_len = 1; 266209136Sjilles while ((retstr = (*genfunc) (text, (int)matches)) != NULL) { 267209136Sjilles /* allow for list terminator here */ 268209136Sjilles if (matches + 3 >= match_list_len) { 269209136Sjilles char **nmatch_list; 270209136Sjilles while (matches + 3 >= match_list_len) 271209136Sjilles match_list_len <<= 1; 272209136Sjilles nmatch_list = realloc(match_list, 273209136Sjilles match_list_len * sizeof(char *)); 274209136Sjilles if (nmatch_list == NULL) { 275209136Sjilles free(match_list); 276209136Sjilles return NULL; 277209136Sjilles } 278209136Sjilles match_list = nmatch_list; 279209136Sjilles 280209136Sjilles } 281209136Sjilles match_list[++matches] = retstr; 282209136Sjilles } 283209136Sjilles 284209136Sjilles if (!match_list) 285209136Sjilles return NULL; /* nothing found */ 286209136Sjilles 287209136Sjilles /* find least denominator and insert it to match_list[0] */ 288209136Sjilles which = 2; 289209136Sjilles prevstr = match_list[1]; 290209136Sjilles max_equal = strlen(prevstr); 291209136Sjilles for (; which <= matches; which++) { 292209136Sjilles for (i = 0; i < max_equal && 293209136Sjilles prevstr[i] == match_list[which][i]; i++) 294209136Sjilles continue; 295209136Sjilles max_equal = i; 296209136Sjilles } 297209136Sjilles 298209136Sjilles retstr = malloc(max_equal + 1); 299209136Sjilles if (retstr == NULL) { 300209136Sjilles free(match_list); 301209136Sjilles return NULL; 302209136Sjilles } 303209136Sjilles (void)strncpy(retstr, match_list[1], max_equal); 304209136Sjilles retstr[max_equal] = '\0'; 305209136Sjilles match_list[0] = retstr; 306209136Sjilles 307209136Sjilles /* add NULL as last pointer to the array */ 308209136Sjilles match_list[matches + 1] = (char *) NULL; 309209136Sjilles 310209136Sjilles return (match_list); 311209136Sjilles} 312209136Sjilles 313209136Sjilles 314209136Sjilles/* 315209136Sjilles * Sort function for qsort(). Just wrapper around strcasecmp(). 316209136Sjilles */ 317209136Sjillesstatic int 318209136Sjilles_fn_qsort_string_compare(const void *i1, const void *i2) 319209136Sjilles{ 320209136Sjilles const char *s1 = ((const char * const *)i1)[0]; 321209136Sjilles const char *s2 = ((const char * const *)i2)[0]; 322209136Sjilles 323209136Sjilles return strcasecmp(s1, s2); 324209136Sjilles} 325209136Sjilles 326209136Sjilles 327209136Sjilles/* 328209136Sjilles * Display list of strings in columnar format on readline's output stream. 329209136Sjilles * 'matches' is list of strings, 'len' is number of strings in 'matches', 330209136Sjilles * 'max' is maximum length of string in 'matches'. 331209136Sjilles */ 332209136Sjillesvoid 333209136Sjillesfn_display_match_list(EditLine *el, char **matches, size_t len, size_t max) 334209136Sjilles{ 335209136Sjilles size_t i, idx, limit, count; 336209136Sjilles int screenwidth = el->el_term.t_size.h; 337209136Sjilles 338209136Sjilles /* 339209136Sjilles * Find out how many entries can be put on one line, count 340209136Sjilles * with two spaces between strings. 341209136Sjilles */ 342209136Sjilles limit = screenwidth / (max + 2); 343209136Sjilles if (limit == 0) 344209136Sjilles limit = 1; 345209136Sjilles 346209136Sjilles /* how many lines of output */ 347209136Sjilles count = len / limit; 348209136Sjilles if (count * limit < len) 349209136Sjilles count++; 350209136Sjilles 351209136Sjilles /* Sort the items if they are not already sorted. */ 352209217Sjilles qsort(&matches[1], len, sizeof(char *), _fn_qsort_string_compare); 353209136Sjilles 354209136Sjilles idx = 1; 355209136Sjilles for(; count > 0; count--) { 356209136Sjilles int more = limit > 0 && matches[0]; 357209217Sjilles for(i = 0; more; idx++) { 358209136Sjilles more = ++i < limit && matches[idx + 1]; 359209136Sjilles (void)fprintf(el->el_outfile, "%-*s%s", (int)max, 360209136Sjilles matches[idx], more ? " " : ""); 361209136Sjilles } 362209136Sjilles (void)fprintf(el->el_outfile, "\n"); 363209136Sjilles } 364209136Sjilles} 365209136Sjilles 366209136Sjilles 367209136Sjilles/* 368209136Sjilles * Complete the word at or before point, 369209136Sjilles * 'what_to_do' says what to do with the completion. 370209136Sjilles * \t means do standard completion. 371209136Sjilles * `?' means list the possible completions. 372209136Sjilles * `*' means insert all of the possible completions. 373209136Sjilles * `!' means to do standard completion, and list all possible completions if 374209136Sjilles * there is more than one. 375209136Sjilles * 376209136Sjilles * Note: '*' support is not implemented 377209136Sjilles * '!' could never be invoked 378209136Sjilles */ 379209136Sjillesint 380209136Sjillesfn_complete(EditLine *el, 381209136Sjilles char *(*complet_func)(const char *, int), 382209136Sjilles char **(*attempted_completion_function)(const char *, int, int), 383209136Sjilles const char *word_break, const char *special_prefixes, 384209136Sjilles const char *(*app_func)(const char *), size_t query_items, 385209219Sjilles int *completion_type, int *over, int *point, int *end, 386209219Sjilles const char *(*find_word_start_func)(const char *, const char *), 387209219Sjilles char *(*dequoting_func)(const char *), 388209219Sjilles char *(*quoting_func)(const char *)) 389209136Sjilles{ 390209136Sjilles const LineInfo *li; 391209136Sjilles char *temp; 392209219Sjilles char *dequoted_temp; 393209136Sjilles char **matches; 394209136Sjilles const char *ctemp; 395209136Sjilles size_t len; 396209136Sjilles int what_to_do = '\t'; 397209136Sjilles int retval = CC_NORM; 398209136Sjilles 399209136Sjilles if (el->el_state.lastcmd == el->el_state.thiscmd) 400209136Sjilles what_to_do = '?'; 401209136Sjilles 402209136Sjilles /* readline's rl_complete() has to be told what we did... */ 403209136Sjilles if (completion_type != NULL) 404209136Sjilles *completion_type = what_to_do; 405209136Sjilles 406209136Sjilles if (!complet_func) 407209136Sjilles complet_func = fn_filename_completion_function; 408209136Sjilles if (!app_func) 409209136Sjilles app_func = append_char_function; 410209136Sjilles 411209136Sjilles /* We now look backwards for the start of a filename/variable word */ 412209136Sjilles li = el_line(el); 413209219Sjilles if (find_word_start_func) 414209219Sjilles ctemp = find_word_start_func(li->buffer, li->cursor); 415209219Sjilles else { 416209219Sjilles ctemp = li->cursor; 417209219Sjilles while (ctemp > li->buffer 418209219Sjilles && !strchr(word_break, ctemp[-1]) 419209219Sjilles && (!special_prefixes || !strchr(special_prefixes, ctemp[-1]) ) ) 420209219Sjilles ctemp--; 421209219Sjilles } 422209136Sjilles 423209136Sjilles len = li->cursor - ctemp; 424209136Sjilles#if defined(__SSP__) || defined(__SSP_ALL__) 425209136Sjilles temp = malloc(sizeof(*temp) * (len + 1)); 426209136Sjilles if (temp == NULL) 427209136Sjilles return retval; 428209136Sjilles#else 429209136Sjilles temp = alloca(sizeof(*temp) * (len + 1)); 430209136Sjilles#endif 431209136Sjilles (void)strncpy(temp, ctemp, len); 432209136Sjilles temp[len] = '\0'; 433209136Sjilles 434209219Sjilles if (dequoting_func) { 435209219Sjilles dequoted_temp = dequoting_func(temp); 436209219Sjilles if (dequoted_temp == NULL) 437209219Sjilles return retval; 438209219Sjilles } else 439209219Sjilles dequoted_temp = NULL; 440209219Sjilles 441209136Sjilles /* these can be used by function called in completion_matches() */ 442209136Sjilles /* or (*attempted_completion_function)() */ 443209136Sjilles if (point != 0) 444209136Sjilles *point = (int)(li->cursor - li->buffer); 445209136Sjilles if (end != NULL) 446209136Sjilles *end = (int)(li->lastchar - li->buffer); 447209136Sjilles 448209136Sjilles if (attempted_completion_function) { 449209136Sjilles int cur_off = (int)(li->cursor - li->buffer); 450209219Sjilles matches = (*attempted_completion_function) (dequoted_temp ? dequoted_temp : temp, 451209136Sjilles (int)(cur_off - len), cur_off); 452209136Sjilles } else 453209136Sjilles matches = 0; 454209136Sjilles if (!attempted_completion_function || 455209136Sjilles (over != NULL && !*over && !matches)) 456209219Sjilles matches = completion_matches(dequoted_temp ? dequoted_temp : temp, complet_func); 457209136Sjilles 458209136Sjilles if (over != NULL) 459209136Sjilles *over = 0; 460209136Sjilles 461209136Sjilles if (matches) { 462209136Sjilles int i; 463209136Sjilles size_t matches_num, maxlen, match_len, match_display=1; 464209136Sjilles 465209136Sjilles retval = CC_REFRESH; 466209136Sjilles /* 467209136Sjilles * Only replace the completed string with common part of 468209136Sjilles * possible matches if there is possible completion. 469209136Sjilles */ 470209136Sjilles if (matches[0][0] != '\0') { 471209219Sjilles char *quoted_match; 472209219Sjilles if (quoting_func) { 473209219Sjilles quoted_match = quoting_func(matches[0]); 474209219Sjilles if (quoted_match == NULL) 475209219Sjilles goto free_matches; 476209219Sjilles } else 477209219Sjilles quoted_match = NULL; 478209219Sjilles 479209136Sjilles el_deletestr(el, (int) len); 480209219Sjilles el_insertstr(el, quoted_match ? quoted_match : matches[0]); 481209219Sjilles 482209219Sjilles free(quoted_match); 483209136Sjilles } 484209136Sjilles 485209136Sjilles if (what_to_do == '?') 486209136Sjilles goto display_matches; 487209136Sjilles 488209136Sjilles if (matches[2] == NULL && strcmp(matches[0], matches[1]) == 0) { 489209136Sjilles /* 490209136Sjilles * We found exact match. Add a space after 491209136Sjilles * it, unless we do filename completion and the 492209136Sjilles * object is a directory. 493209136Sjilles */ 494209136Sjilles el_insertstr(el, (*app_func)(matches[0])); 495209136Sjilles } else if (what_to_do == '!') { 496209136Sjilles display_matches: 497209136Sjilles /* 498209136Sjilles * More than one match and requested to list possible 499209136Sjilles * matches. 500209136Sjilles */ 501209136Sjilles 502209136Sjilles for(i = 1, maxlen = 0; matches[i]; i++) { 503209136Sjilles match_len = strlen(matches[i]); 504209136Sjilles if (match_len > maxlen) 505209136Sjilles maxlen = match_len; 506209136Sjilles } 507209136Sjilles matches_num = i - 1; 508209136Sjilles 509209136Sjilles /* newline to get on next line from command line */ 510209136Sjilles (void)fprintf(el->el_outfile, "\n"); 511209136Sjilles 512209136Sjilles /* 513209136Sjilles * If there are too many items, ask user for display 514209136Sjilles * confirmation. 515209136Sjilles */ 516209136Sjilles if (matches_num > query_items) { 517209136Sjilles (void)fprintf(el->el_outfile, 518209136Sjilles "Display all %zu possibilities? (y or n) ", 519209136Sjilles matches_num); 520209136Sjilles (void)fflush(el->el_outfile); 521209136Sjilles if (getc(stdin) != 'y') 522209136Sjilles match_display = 0; 523209136Sjilles (void)fprintf(el->el_outfile, "\n"); 524209136Sjilles } 525209136Sjilles 526209136Sjilles if (match_display) 527209136Sjilles fn_display_match_list(el, matches, matches_num, 528209136Sjilles maxlen); 529209136Sjilles retval = CC_REDISPLAY; 530209136Sjilles } else if (matches[0][0]) { 531209136Sjilles /* 532209136Sjilles * There was some common match, but the name was 533209136Sjilles * not complete enough. Next tab will print possible 534209136Sjilles * completions. 535209136Sjilles */ 536209136Sjilles el_beep(el); 537209136Sjilles } else { 538209136Sjilles /* lcd is not a valid object - further specification */ 539209136Sjilles /* is needed */ 540209136Sjilles el_beep(el); 541209136Sjilles retval = CC_NORM; 542209136Sjilles } 543209136Sjilles 544209219Sjillesfree_matches: 545209136Sjilles /* free elements of array and the array itself */ 546209136Sjilles for (i = 0; matches[i]; i++) 547209136Sjilles free(matches[i]); 548209136Sjilles free(matches); 549209136Sjilles matches = NULL; 550209136Sjilles } 551209219Sjilles free(dequoted_temp); 552209136Sjilles#if defined(__SSP__) || defined(__SSP_ALL__) 553209136Sjilles free(temp); 554209136Sjilles#endif 555209136Sjilles return retval; 556209136Sjilles} 557209136Sjilles 558209136Sjilles 559209136Sjilles/* 560209136Sjilles * el-compatible wrapper around rl_complete; needed for key binding 561209136Sjilles */ 562209136Sjilles/* ARGSUSED */ 563209136Sjillesunsigned char 564209136Sjilles_el_fn_complete(EditLine *el, int ch __attribute__((__unused__))) 565209136Sjilles{ 566209136Sjilles return (unsigned char)fn_complete(el, NULL, NULL, 567209136Sjilles break_chars, NULL, NULL, 100, 568209219Sjilles NULL, NULL, NULL, NULL, 569209219Sjilles NULL, NULL, NULL); 570209136Sjilles} 571209219Sjilles 572209219Sjilles 573209219Sjillesstatic const char * 574209219Sjillessh_find_word_start(const char *buffer, const char *cursor) 575209219Sjilles{ 576209219Sjilles const char *word_start = buffer; 577209219Sjilles 578209219Sjilles while (buffer < cursor) { 579209219Sjilles if (*buffer == '\\') 580209219Sjilles buffer++; 581209219Sjilles else if (strchr(break_chars, *buffer)) 582209219Sjilles word_start = buffer + 1; 583209219Sjilles 584209219Sjilles buffer++; 585209219Sjilles } 586209219Sjilles 587209219Sjilles return word_start; 588209219Sjilles} 589209219Sjilles 590209219Sjilles 591209219Sjillesstatic char * 592209219Sjillessh_quote(const char *str) 593209219Sjilles{ 594209219Sjilles const char *src; 595209219Sjilles int extra_len = 0; 596209219Sjilles char *quoted_str, *dst; 597209219Sjilles 598209224Sjilles if (*str == '-' || *str == '+') 599209224Sjilles extra_len += 2; 600209219Sjilles for (src = str; *src != '\0'; src++) 601209219Sjilles if (strchr(break_chars, *src) || 602209219Sjilles strchr(extra_quote_chars, *src)) 603209219Sjilles extra_len++; 604209219Sjilles 605209219Sjilles quoted_str = malloc(sizeof(*quoted_str) * 606209219Sjilles (strlen(str) + extra_len + 1)); 607209219Sjilles if (quoted_str == NULL) 608209219Sjilles return NULL; 609209219Sjilles 610209219Sjilles dst = quoted_str; 611209224Sjilles if (*str == '-' || *str == '+') 612209224Sjilles *dst++ = '.', *dst++ = '/'; 613209219Sjilles for (src = str; *src != '\0'; src++) { 614209219Sjilles if (strchr(break_chars, *src) || 615209219Sjilles strchr(extra_quote_chars, *src)) 616209219Sjilles *dst++ = '\\'; 617209219Sjilles *dst++ = *src; 618209219Sjilles } 619209219Sjilles *dst = '\0'; 620209219Sjilles 621209219Sjilles return quoted_str; 622209219Sjilles} 623209219Sjilles 624209219Sjilles 625209219Sjillesstatic char * 626209219Sjillessh_dequote(const char *str) 627209219Sjilles{ 628209219Sjilles char *dequoted_str, *dst; 629209219Sjilles 630209219Sjilles /* save extra space to replace \~ with ./~ */ 631209219Sjilles dequoted_str = malloc(sizeof(*dequoted_str) * (strlen(str) + 1 + 1)); 632209219Sjilles if (dequoted_str == NULL) 633209219Sjilles return NULL; 634209219Sjilles 635209219Sjilles dst = dequoted_str; 636209219Sjilles 637209219Sjilles /* dequote \~ at start as ./~ */ 638209219Sjilles if (*str == '\\' && str[1] == '~') { 639209219Sjilles str++; 640209219Sjilles *dst++ = '.'; 641209219Sjilles *dst++ = '/'; 642209219Sjilles } 643209219Sjilles 644209219Sjilles while (*str) { 645209219Sjilles if (*str == '\\') 646209219Sjilles str++; 647209219Sjilles if (*str) 648209219Sjilles *dst++ = *str++; 649209219Sjilles } 650209219Sjilles *dst = '\0'; 651209219Sjilles 652209219Sjilles return dequoted_str; 653209219Sjilles} 654209219Sjilles 655209219Sjilles 656209219Sjilles/* 657209219Sjilles * completion function using sh quoting rules; for key binding 658209219Sjilles */ 659209219Sjilles/* ARGSUSED */ 660209219Sjillesunsigned char 661209219Sjilles_el_fn_sh_complete(EditLine *el, int ch __attribute__((__unused__))) 662209219Sjilles{ 663209219Sjilles return (unsigned char)fn_complete(el, NULL, NULL, 664209219Sjilles break_chars, NULL, NULL, 100, 665209219Sjilles NULL, NULL, NULL, NULL, 666209219Sjilles sh_find_word_start, sh_dequote, sh_quote); 667209219Sjilles} 668