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