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