1 1.25 andvar /* $NetBSD: complete.c,v 1.25 2022/08/06 18:26:43 andvar Exp $ */ 2 1.1 christos 3 1.1 christos /*- 4 1.9 christos * Copyright (c) 1997-2000,2005,2006 The NetBSD Foundation, Inc. 5 1.1 christos * All rights reserved. 6 1.1 christos * 7 1.1 christos * This code is derived from software contributed to The NetBSD Foundation 8 1.1 christos * by Luke Mewburn. 9 1.1 christos * 10 1.1 christos * Redistribution and use in source and binary forms, with or without 11 1.1 christos * modification, are permitted provided that the following conditions 12 1.1 christos * are met: 13 1.1 christos * 1. Redistributions of source code must retain the above copyright 14 1.1 christos * notice, this list of conditions and the following disclaimer. 15 1.1 christos * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 christos * notice, this list of conditions and the following disclaimer in the 17 1.1 christos * documentation and/or other materials provided with the distribution. 18 1.1 christos * 19 1.1 christos * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 1.1 christos * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 1.1 christos * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 1.1 christos * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 1.1 christos * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 1.1 christos * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 1.1 christos * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 1.1 christos * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 1.1 christos * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 1.1 christos * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 1.1 christos * POSSIBILITY OF SUCH DAMAGE. 30 1.1 christos */ 31 1.1 christos 32 1.11 christos /* 33 1.11 christos * Most of this is derived or copied from src/usr.bin/ftp/complete.c (1.41). 34 1.11 christos */ 35 1.1 christos 36 1.9 christos #ifdef USE_EDITLINE 37 1.1 christos 38 1.1 christos #include <sys/cdefs.h> 39 1.1 christos #ifndef lint 40 1.25 andvar __RCSID("$NetBSD: complete.c,v 1.25 2022/08/06 18:26:43 andvar Exp $"); 41 1.1 christos #endif /* not lint */ 42 1.1 christos 43 1.1 christos /* 44 1.1 christos * FTP user program - command and file completion routines 45 1.1 christos */ 46 1.1 christos 47 1.11 christos #include <assert.h> 48 1.1 christos #include <ctype.h> 49 1.1 christos #include <err.h> 50 1.1 christos #include <dirent.h> 51 1.1 christos #include <glob.h> 52 1.1 christos #include <stdio.h> 53 1.1 christos #include <stdlib.h> 54 1.1 christos #include <string.h> 55 1.1 christos #include <stringlist.h> 56 1.17 christos #include <termcap.h> 57 1.9 christos #include <util.h> 58 1.1 christos 59 1.11 christos #include <sys/param.h> 60 1.11 christos #include <sys/stat.h> 61 1.11 christos 62 1.1 christos #include "rcv.h" /* includes "glob.h" */ 63 1.1 christos #include "extern.h" 64 1.1 christos #include "complete.h" 65 1.9 christos #ifdef MIME_SUPPORT 66 1.9 christos #include "mime.h" 67 1.9 christos #endif 68 1.19 christos #include "sig.h" 69 1.11 christos #ifdef THREAD_SUPPORT 70 1.11 christos #include "thread.h" 71 1.11 christos #endif 72 1.11 christos 73 1.11 christos #define BELL 0x7 74 1.1 christos 75 1.1 christos /* 76 1.1 christos * Global variables 77 1.1 christos */ 78 1.1 christos static int doglob = 1; /* glob local file names */ 79 1.1 christos 80 1.1 christos #define ttyout stdout 81 1.1 christos #define ttywidth screenwidth /* in "glob.h" */ 82 1.1 christos #define ttyheight screenheight /* in "glob.h" */ 83 1.1 christos 84 1.1 christos /************************************************************************/ 85 1.9 christos /* from src/usr.bin/ftp/utils.h (1.135) - begin */ 86 1.1 christos 87 1.1 christos /* 88 1.1 christos * List words in stringlist, vertically arranged 89 1.1 christos */ 90 1.1 christos static void 91 1.1 christos list_vertical(StringList *sl) 92 1.1 christos { 93 1.16 lukem int k; 94 1.16 lukem size_t i, j, columns, lines; 95 1.1 christos char *p; 96 1.1 christos size_t w, width; 97 1.1 christos 98 1.1 christos width = 0; 99 1.1 christos 100 1.9 christos for (i = 0; i < sl->sl_cur; i++) { 101 1.1 christos w = strlen(sl->sl_str[i]); 102 1.1 christos if (w > width) 103 1.1 christos width = w; 104 1.1 christos } 105 1.1 christos width = (width + 8) &~ 7; 106 1.1 christos 107 1.1 christos columns = ttywidth / width; 108 1.1 christos if (columns == 0) 109 1.1 christos columns = 1; 110 1.1 christos lines = (sl->sl_cur + columns - 1) / columns; 111 1.11 christos k = 0; 112 1.1 christos for (i = 0; i < lines; i++) { 113 1.1 christos for (j = 0; j < columns; j++) { 114 1.1 christos p = sl->sl_str[j * lines + i]; 115 1.1 christos if (p) 116 1.9 christos (void)fputs(p, ttyout); 117 1.1 christos if (j * lines + i + lines >= sl->sl_cur) { 118 1.9 christos (void)putc('\n', ttyout); 119 1.1 christos break; 120 1.1 christos } 121 1.1 christos if (p) { 122 1.1 christos w = strlen(p); 123 1.1 christos while (w < width) { 124 1.1 christos w = (w + 8) &~ 7; 125 1.1 christos (void)putc('\t', ttyout); 126 1.1 christos } 127 1.1 christos } 128 1.1 christos } 129 1.11 christos if (ttyheight > 2 && ++k == ttyheight - 2) { 130 1.11 christos int ch; 131 1.11 christos k = 0; 132 1.11 christos (void)fputs("--more--", ttyout); 133 1.11 christos while ((ch = getchar()) != EOF && ch != ' ' && ch != 'q') 134 1.11 christos (void)putc(BELL, ttyout); 135 1.11 christos (void)fputs("\r \r", ttyout); 136 1.11 christos if (ch == 'q') 137 1.11 christos break; 138 1.11 christos } 139 1.1 christos } 140 1.1 christos } 141 1.1 christos 142 1.1 christos /* 143 1.1 christos * Copy characters from src into dst, \ quoting characters that require it 144 1.1 christos */ 145 1.1 christos static void 146 1.1 christos ftpvis(char *dst, size_t dstlen, const char *src, size_t srclen) 147 1.1 christos { 148 1.16 lukem size_t di, si; 149 1.1 christos 150 1.1 christos for (di = si = 0; 151 1.1 christos src[si] != '\0' && di < dstlen && si < srclen; 152 1.1 christos di++, si++) { 153 1.1 christos switch (src[si]) { 154 1.1 christos case '\\': 155 1.1 christos case ' ': 156 1.1 christos case '\t': 157 1.1 christos case '\r': 158 1.1 christos case '\n': 159 1.1 christos case '"': 160 1.1 christos dst[di++] = '\\'; 161 1.1 christos if (di >= dstlen) 162 1.1 christos break; 163 1.1 christos /* FALLTHROUGH */ 164 1.1 christos default: 165 1.1 christos dst[di] = src[si]; 166 1.1 christos } 167 1.1 christos } 168 1.1 christos dst[di] = '\0'; 169 1.1 christos } 170 1.1 christos 171 1.1 christos /* 172 1.1 christos * sl_init() with inbuilt error checking 173 1.1 christos */ 174 1.1 christos static StringList * 175 1.9 christos mail_sl_init(void) 176 1.1 christos { 177 1.1 christos StringList *p; 178 1.1 christos 179 1.1 christos p = sl_init(); 180 1.1 christos if (p == NULL) 181 1.20 christos err(EXIT_FAILURE, "Unable to allocate memory for stringlist"); 182 1.11 christos return p; 183 1.1 christos } 184 1.1 christos 185 1.1 christos 186 1.1 christos /* 187 1.1 christos * sl_add() with inbuilt error checking 188 1.1 christos */ 189 1.1 christos static void 190 1.9 christos mail_sl_add(StringList *sl, char *i) 191 1.1 christos { 192 1.1 christos 193 1.1 christos if (sl_add(sl, i) == -1) 194 1.20 christos err(EXIT_FAILURE, "Unable to add `%s' to stringlist", i); 195 1.1 christos } 196 1.1 christos 197 1.1 christos 198 1.1 christos /* 199 1.1 christos * Glob a local file name specification with the expectation of a single 200 1.1 christos * return value. Can't control multiple values being expanded from the 201 1.1 christos * expression, we return only the first. 202 1.1 christos * Returns NULL on error, or a pointer to a buffer containing the filename 203 1.25 andvar * that's the caller's responsibility to free(3) when finished with. 204 1.1 christos */ 205 1.1 christos static char * 206 1.1 christos globulize(const char *pattern) 207 1.1 christos { 208 1.1 christos glob_t gl; 209 1.1 christos int flags; 210 1.1 christos char *p; 211 1.1 christos 212 1.1 christos if (!doglob) 213 1.9 christos return estrdup(pattern); 214 1.1 christos 215 1.1 christos flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE; 216 1.9 christos (void)memset(&gl, 0, sizeof(gl)); 217 1.1 christos if (glob(pattern, flags, NULL, &gl) || gl.gl_pathc == 0) { 218 1.1 christos warnx("%s: not found", pattern); 219 1.1 christos globfree(&gl); 220 1.11 christos return NULL; 221 1.1 christos } 222 1.9 christos p = estrdup(gl.gl_pathv[0]); 223 1.1 christos globfree(&gl); 224 1.11 christos return p; 225 1.1 christos } 226 1.1 christos 227 1.9 christos /* from src/usr.bin/ftp/utils.h (1.135) - end */ 228 1.1 christos /************************************************************************/ 229 1.1 christos 230 1.1 christos static int 231 1.1 christos comparstr(const void *a, const void *b) 232 1.1 christos { 233 1.11 christos return strcmp(*(const char * const *)a, *(const char * const *)b); 234 1.1 christos } 235 1.1 christos 236 1.1 christos /* 237 1.1 christos * Determine if complete is ambiguous. If unique, insert. 238 1.1 christos * If no choices, error. If unambiguous prefix, insert that. 239 1.1 christos * Otherwise, list choices. words is assumed to be filtered 240 1.1 christos * to only contain possible choices. 241 1.1 christos * Args: 242 1.1 christos * word word which started the match 243 1.9 christos * dolist list by default 244 1.1 christos * words stringlist containing possible matches 245 1.1 christos * Returns a result as per el_set(EL_ADDFN, ...) 246 1.1 christos */ 247 1.1 christos static unsigned char 248 1.9 christos complete_ambiguous(EditLine *el, char *word, int dolist, StringList *words) 249 1.1 christos { 250 1.1 christos char insertstr[MAXPATHLEN]; 251 1.1 christos char *lastmatch, *p; 252 1.16 lukem size_t i, j, matchlen, wordlen; 253 1.1 christos 254 1.1 christos wordlen = strlen(word); 255 1.1 christos if (words->sl_cur == 0) 256 1.11 christos return CC_ERROR; /* no choices available */ 257 1.1 christos 258 1.1 christos if (words->sl_cur == 1) { /* only once choice available */ 259 1.1 christos p = words->sl_str[0] + wordlen; 260 1.1 christos if (*p == '\0') /* at end of word? */ 261 1.11 christos return CC_REFRESH; 262 1.1 christos ftpvis(insertstr, sizeof(insertstr), p, strlen(p)); 263 1.1 christos if (el_insertstr(el, insertstr) == -1) 264 1.11 christos return CC_ERROR; 265 1.1 christos else 266 1.11 christos return CC_REFRESH; 267 1.1 christos } 268 1.1 christos 269 1.9 christos if (!dolist) { 270 1.1 christos matchlen = 0; 271 1.1 christos lastmatch = words->sl_str[0]; 272 1.1 christos matchlen = strlen(lastmatch); 273 1.9 christos for (i = 1; i < words->sl_cur; i++) { 274 1.9 christos for (j = wordlen; j < strlen(words->sl_str[i]); j++) 275 1.1 christos if (lastmatch[j] != words->sl_str[i][j]) 276 1.1 christos break; 277 1.1 christos if (j < matchlen) 278 1.1 christos matchlen = j; 279 1.1 christos } 280 1.11 christos if (matchlen >= wordlen) { 281 1.1 christos ftpvis(insertstr, sizeof(insertstr), 282 1.1 christos lastmatch + wordlen, matchlen - wordlen); 283 1.1 christos if (el_insertstr(el, insertstr) == -1) 284 1.11 christos return CC_ERROR; 285 1.1 christos else 286 1.11 christos return CC_REFRESH_BEEP; 287 1.1 christos } 288 1.1 christos } 289 1.1 christos 290 1.9 christos (void)putc('\n', ttyout); 291 1.1 christos qsort(words->sl_str, words->sl_cur, sizeof(char *), comparstr); 292 1.11 christos 293 1.1 christos list_vertical(words); 294 1.11 christos return CC_REDISPLAY; 295 1.1 christos } 296 1.1 christos 297 1.1 christos /* 298 1.1 christos * Complete a mail command. 299 1.1 christos */ 300 1.1 christos static unsigned char 301 1.9 christos complete_command(EditLine *el, char *word, int dolist) 302 1.1 christos { 303 1.1 christos const struct cmd *c; 304 1.1 christos StringList *words; 305 1.1 christos size_t wordlen; 306 1.1 christos unsigned char rv; 307 1.1 christos 308 1.9 christos words = mail_sl_init(); 309 1.1 christos wordlen = strlen(word); 310 1.1 christos 311 1.1 christos for (c = cmdtab; c->c_name != NULL; c++) { 312 1.1 christos if (wordlen > strlen(c->c_name)) 313 1.1 christos continue; 314 1.1 christos if (strncmp(word, c->c_name, wordlen) == 0) 315 1.9 christos mail_sl_add(words, __UNCONST(c->c_name)); 316 1.1 christos } 317 1.1 christos 318 1.9 christos rv = complete_ambiguous(el, word, dolist, words); 319 1.1 christos if (rv == CC_REFRESH) { 320 1.1 christos if (el_insertstr(el, " ") == -1) 321 1.1 christos rv = CC_ERROR; 322 1.1 christos } 323 1.1 christos sl_free(words, 0); 324 1.11 christos return rv; 325 1.1 christos } 326 1.1 christos 327 1.1 christos /* 328 1.1 christos * Complete a local filename. 329 1.1 christos */ 330 1.1 christos static unsigned char 331 1.9 christos complete_filename(EditLine *el, char *word, int dolist) 332 1.1 christos { 333 1.1 christos StringList *words; 334 1.1 christos char dir[MAXPATHLEN]; 335 1.21 christos char *fname, *mf; 336 1.1 christos DIR *dd; 337 1.1 christos struct dirent *dp; 338 1.1 christos unsigned char rv; 339 1.1 christos size_t len; 340 1.1 christos 341 1.1 christos if ((fname = strrchr(word, '/')) == NULL) { 342 1.23 christos if (word[0] == '+' && (mf = value(ENAME_FOLDER)) != NULL) { 343 1.21 christos if (mf[0] == '/') { 344 1.21 christos (void)estrlcpy(dir, mf, sizeof(dir)); 345 1.21 christos } else { 346 1.21 christos dir[0] = '~'; 347 1.21 christos dir[1] = '/'; 348 1.21 christos (void)estrlcpy(dir + 2, mf, sizeof(dir) - 2); 349 1.21 christos } 350 1.21 christos fname = word + 1; 351 1.21 christos } else { 352 1.21 christos dir[0] = '.'; 353 1.21 christos dir[1] = '\0'; 354 1.21 christos fname = word; 355 1.21 christos } 356 1.1 christos } else { 357 1.1 christos if (fname == word) { 358 1.1 christos dir[0] = '/'; 359 1.1 christos dir[1] = '\0'; 360 1.9 christos } else { 361 1.9 christos len = fname - word + 1; 362 1.9 christos (void)estrlcpy(dir, word, sizeof(dir)); 363 1.9 christos dir[len] = '\0'; 364 1.9 christos } 365 1.1 christos fname++; 366 1.1 christos } 367 1.1 christos if (dir[0] == '~') { 368 1.1 christos char *p; 369 1.1 christos 370 1.1 christos if ((p = globulize(dir)) == NULL) 371 1.11 christos return CC_ERROR; 372 1.9 christos (void)estrlcpy(dir, p, sizeof(dir)); 373 1.1 christos free(p); 374 1.1 christos } 375 1.1 christos 376 1.1 christos if ((dd = opendir(dir)) == NULL) 377 1.11 christos return CC_ERROR; 378 1.1 christos 379 1.9 christos words = mail_sl_init(); 380 1.1 christos len = strlen(fname); 381 1.1 christos 382 1.1 christos for (dp = readdir(dd); dp != NULL; dp = readdir(dd)) { 383 1.1 christos if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 384 1.1 christos continue; 385 1.1 christos 386 1.1 christos #if defined(DIRENT_MISSING_D_NAMLEN) 387 1.1 christos if (len > strlen(dp->d_name)) 388 1.1 christos continue; 389 1.1 christos #else 390 1.1 christos if (len > dp->d_namlen) 391 1.1 christos continue; 392 1.1 christos #endif 393 1.1 christos if (strncmp(fname, dp->d_name, len) == 0) { 394 1.1 christos char *tcp; 395 1.1 christos 396 1.9 christos tcp = estrdup(dp->d_name); 397 1.9 christos mail_sl_add(words, tcp); 398 1.1 christos } 399 1.1 christos } 400 1.9 christos (void)closedir(dd); 401 1.1 christos 402 1.9 christos rv = complete_ambiguous(el, fname, dolist, words); 403 1.1 christos if (rv == CC_REFRESH) { 404 1.1 christos struct stat sb; 405 1.1 christos char path[MAXPATHLEN]; 406 1.1 christos 407 1.9 christos (void)estrlcpy(path, dir, sizeof(path)); 408 1.9 christos (void)estrlcat(path, "/", sizeof(path)); 409 1.9 christos (void)estrlcat(path, words->sl_str[0], sizeof(path)); 410 1.1 christos 411 1.1 christos if (stat(path, &sb) >= 0) { 412 1.1 christos char suffix[2] = " "; 413 1.1 christos 414 1.1 christos if (S_ISDIR(sb.st_mode)) 415 1.1 christos suffix[0] = '/'; 416 1.1 christos if (el_insertstr(el, suffix) == -1) 417 1.1 christos rv = CC_ERROR; 418 1.1 christos } 419 1.1 christos } 420 1.1 christos sl_free(words, 1); 421 1.11 christos return rv; 422 1.1 christos } 423 1.1 christos 424 1.1 christos static int 425 1.1 christos find_execs(char *word, char *path, StringList *list) 426 1.1 christos { 427 1.1 christos char *sep; 428 1.1 christos char *dir=path; 429 1.1 christos DIR *dd; 430 1.1 christos struct dirent *dp; 431 1.9 christos size_t len = strlen(word); 432 1.1 christos uid_t uid = getuid(); 433 1.1 christos gid_t gid = getgid(); 434 1.1 christos 435 1.9 christos for (sep = dir; sep; dir = sep + 1) { 436 1.1 christos if ((sep=strchr(dir, ':')) != NULL) { 437 1.1 christos *sep=0; 438 1.1 christos } 439 1.13 christos 440 1.1 christos if ((dd = opendir(dir)) == NULL) { 441 1.1 christos perror("dir"); 442 1.1 christos return -1; 443 1.1 christos } 444 1.13 christos 445 1.1 christos for (dp = readdir(dd); dp != NULL; dp = readdir(dd)) { 446 1.1 christos 447 1.1 christos if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 448 1.1 christos continue; 449 1.13 christos 450 1.1 christos #if defined(DIRENT_MISSING_D_NAMLEN) 451 1.1 christos if (len > strlen(dp->d_name)) 452 1.1 christos continue; 453 1.1 christos #else 454 1.1 christos if (len > dp->d_namlen) 455 1.1 christos continue; 456 1.1 christos #endif 457 1.1 christos 458 1.1 christos if (strncmp(word, dp->d_name, len) == 0) { 459 1.1 christos struct stat sb; 460 1.1 christos char pathname[ MAXPATHLEN ]; 461 1.1 christos unsigned mask; 462 1.1 christos 463 1.9 christos (void)snprintf(pathname, sizeof(pathname), 464 1.9 christos "%s/%s", dir, dp->d_name); 465 1.1 christos if (stat(pathname, &sb) != 0) { 466 1.1 christos perror(pathname); 467 1.1 christos continue; 468 1.1 christos } 469 1.1 christos 470 1.1 christos mask = 0001; 471 1.1 christos if (sb.st_uid == uid) mask |= 0100; 472 1.1 christos if (sb.st_gid == gid) mask |= 0010; 473 1.1 christos 474 1.1 christos if ((sb.st_mode & mask) != 0) { 475 1.1 christos char *tcp; 476 1.9 christos tcp = estrdup(dp->d_name); 477 1.9 christos mail_sl_add(list, tcp); 478 1.1 christos } 479 1.1 christos } 480 1.13 christos 481 1.1 christos } 482 1.1 christos 483 1.9 christos (void)closedir(dd); 484 1.1 christos } 485 1.1 christos 486 1.1 christos return 0; 487 1.1 christos } 488 1.1 christos 489 1.1 christos 490 1.1 christos /* 491 1.1 christos * Complete a local executable 492 1.1 christos */ 493 1.1 christos static unsigned char 494 1.9 christos complete_executable(EditLine *el, char *word, int dolist) 495 1.1 christos { 496 1.1 christos StringList *words; 497 1.1 christos char dir[ MAXPATHLEN ]; 498 1.1 christos char *fname; 499 1.1 christos unsigned char rv; 500 1.9 christos size_t len; 501 1.1 christos int error; 502 1.1 christos 503 1.1 christos if ((fname = strrchr(word, '/')) == NULL) { 504 1.1 christos dir[0] = '\0'; /* walk the path */ 505 1.1 christos fname = word; 506 1.1 christos } else { 507 1.1 christos if (fname == word) { 508 1.1 christos dir[0] = '/'; 509 1.1 christos dir[1] = '\0'; 510 1.1 christos } else { 511 1.9 christos len = fname - word; 512 1.9 christos (void)strncpy(dir, word, len); 513 1.1 christos dir[fname - word] = '\0'; 514 1.1 christos } 515 1.1 christos fname++; 516 1.1 christos } 517 1.1 christos 518 1.1 christos words = sl_init(); 519 1.1 christos 520 1.1 christos if (*dir == '\0') { /* walk path */ 521 1.1 christos char *env; 522 1.1 christos char *path; 523 1.1 christos env = getenv("PATH"); 524 1.1 christos len = strlen(env); 525 1.1 christos path = salloc(len + 1); 526 1.9 christos (void)strcpy(path, env); 527 1.1 christos error = find_execs(word, path, words); 528 1.1 christos } 529 1.1 christos else { /* check specified dir only */ 530 1.1 christos error = find_execs(word, dir, words); 531 1.1 christos } 532 1.1 christos if (error != 0) 533 1.1 christos return CC_ERROR; 534 1.1 christos 535 1.9 christos rv = complete_ambiguous(el, fname, dolist, words); 536 1.11 christos if (rv == CC_REFRESH) { 537 1.1 christos if (el_insertstr(el, " ") == -1) 538 1.1 christos rv = CC_ERROR; 539 1.11 christos } 540 1.1 christos sl_free(words, 1); 541 1.1 christos 542 1.11 christos return rv; 543 1.1 christos } 544 1.1 christos 545 1.1 christos 546 1.11 christos static unsigned char 547 1.11 christos complete_set(EditLine *el, char *word, int dolist) 548 1.1 christos { 549 1.1 christos struct var *vp; 550 1.11 christos const char **ap; 551 1.11 christos const char **p; 552 1.1 christos int h; 553 1.1 christos int s; 554 1.9 christos size_t len = strlen(word); 555 1.1 christos StringList *words; 556 1.1 christos unsigned char rv; 557 1.1 christos 558 1.1 christos words = sl_init(); 559 1.1 christos 560 1.1 christos /* allocate space for variables table */ 561 1.10 christos s = 1; 562 1.10 christos for (h = 0; h < HSHSIZE; h++) 563 1.1 christos for (vp = variables[h]; vp != NULL; vp = vp->v_link) 564 1.1 christos s++; 565 1.14 christos ap = salloc(s * sizeof(*ap)); 566 1.1 christos 567 1.10 christos /* save the pointers */ 568 1.1 christos for (h = 0, p = ap; h < HSHSIZE; h++) 569 1.1 christos for (vp = variables[h]; vp != NULL; vp = vp->v_link) 570 1.1 christos *p++ = vp->v_name; 571 1.1 christos *p = NULL; 572 1.1 christos sort(ap); 573 1.10 christos for (p = ap; *p != NULL; p++) 574 1.10 christos if (len == 0 || strncmp(*p, word, len) == 0) 575 1.10 christos mail_sl_add(words, estrdup(*p)); 576 1.1 christos 577 1.9 christos rv = complete_ambiguous(el, word, dolist, words); 578 1.1 christos 579 1.1 christos sl_free(words, 1); 580 1.1 christos 581 1.11 christos return rv; 582 1.1 christos } 583 1.1 christos 584 1.1 christos 585 1.1 christos static unsigned char 586 1.9 christos complete_alias(EditLine *el, char *word, int dolist) 587 1.1 christos { 588 1.1 christos struct grouphead *gh; 589 1.11 christos const char **ap; 590 1.11 christos const char **p; 591 1.1 christos int h; 592 1.1 christos int s; 593 1.9 christos size_t len = strlen(word); 594 1.1 christos StringList *words; 595 1.1 christos unsigned char rv; 596 1.1 christos 597 1.1 christos words = sl_init(); 598 1.1 christos 599 1.1 christos /* allocate space for alias table */ 600 1.10 christos s = 1; 601 1.10 christos for (h = 0; h < HSHSIZE; h++) 602 1.1 christos for (gh = groups[h]; gh != NULL; gh = gh->g_link) 603 1.1 christos s++; 604 1.11 christos ap = salloc(s * sizeof(*ap)); 605 1.13 christos 606 1.1 christos /* save pointers */ 607 1.10 christos p = ap; 608 1.10 christos for (h = 0; h < HSHSIZE; h++) 609 1.1 christos for (gh = groups[h]; gh != NULL; gh = gh->g_link) 610 1.1 christos *p++ = gh->g_name; 611 1.11 christos 612 1.1 christos *p = NULL; 613 1.11 christos 614 1.10 christos sort(ap); 615 1.10 christos for (p = ap; *p != NULL; p++) 616 1.10 christos if (len == 0 || strncmp(*p, word, len) == 0) 617 1.10 christos mail_sl_add(words, estrdup(*p)); 618 1.13 christos 619 1.10 christos rv = complete_ambiguous(el, word, dolist, words); 620 1.11 christos if (rv == CC_REFRESH) { 621 1.10 christos if (el_insertstr(el, " ") == -1) 622 1.10 christos rv = CC_ERROR; 623 1.11 christos } 624 1.10 christos sl_free(words, 1); 625 1.11 christos return rv; 626 1.10 christos } 627 1.10 christos 628 1.10 christos 629 1.11 christos static unsigned char 630 1.11 christos complete_smopts(EditLine *el, char *word, int dolist) 631 1.10 christos { 632 1.10 christos struct grouphead *gh; 633 1.10 christos struct smopts_s *sp; 634 1.11 christos const char **ap; 635 1.11 christos const char **p; 636 1.10 christos int h; 637 1.10 christos int s1; 638 1.10 christos int s2; 639 1.10 christos size_t len; 640 1.10 christos StringList *words; 641 1.10 christos unsigned char rv; 642 1.1 christos 643 1.10 christos len = strlen(word); 644 1.10 christos words = sl_init(); 645 1.1 christos 646 1.10 christos /* count the entries in the smoptstbl and groups (alias) tables */ 647 1.10 christos s1 = 1; 648 1.10 christos s2 = 1; 649 1.10 christos for (h = 0; h < HSHSIZE; h++) { 650 1.10 christos for (sp = smoptstbl[h]; sp != NULL; sp = sp->s_link) 651 1.10 christos s1++; 652 1.10 christos for (gh = groups[h]; gh != NULL; gh = gh->g_link) 653 1.10 christos s2++; 654 1.1 christos } 655 1.1 christos 656 1.10 christos /* allocate sufficient space for the pointers */ 657 1.14 christos ap = salloc(MAX(s1, s2) * sizeof(*ap)); 658 1.10 christos 659 1.10 christos /* 660 1.10 christos * First do the smoptstbl pointers. (case _insensitive_) 661 1.10 christos */ 662 1.10 christos p = ap; 663 1.10 christos for (h = 0; h < HSHSIZE; h++) 664 1.10 christos for (sp = smoptstbl[h]; sp != NULL; sp = sp->s_link) 665 1.10 christos *p++ = sp->s_name; 666 1.10 christos *p = NULL; 667 1.10 christos sort(ap); 668 1.10 christos for (p = ap; *p != NULL; p++) 669 1.10 christos if (len == 0 || strncasecmp(*p, word, len) == 0) 670 1.10 christos mail_sl_add(words, estrdup(*p)); 671 1.10 christos 672 1.10 christos /* 673 1.10 christos * Now do the groups (alias) pointers. (case sensitive) 674 1.10 christos */ 675 1.10 christos p = ap; 676 1.10 christos for (h = 0; h < HSHSIZE; h++) 677 1.10 christos for (gh = groups[h]; gh != NULL; gh = gh->g_link) 678 1.10 christos *p++ = gh->g_name; 679 1.10 christos *p = NULL; 680 1.10 christos sort(ap); 681 1.10 christos for (p = ap; *p != NULL; p++) 682 1.10 christos if (len == 0 || strncmp(*p, word, len) == 0) 683 1.10 christos mail_sl_add(words, estrdup(*p)); 684 1.10 christos 685 1.9 christos rv = complete_ambiguous(el, word, dolist, words); 686 1.1 christos 687 1.1 christos sl_free(words, 1); 688 1.1 christos 689 1.11 christos return rv; 690 1.1 christos } 691 1.1 christos 692 1.1 christos 693 1.11 christos #ifdef THREAD_SUPPORT 694 1.11 christos static unsigned char 695 1.11 christos complete_thread_key(EditLine *el, char *word, int dolist) 696 1.1 christos { 697 1.11 christos const char **ap; 698 1.11 christos const char **p; 699 1.11 christos const char *name; 700 1.11 christos size_t len; 701 1.11 christos StringList *words; 702 1.11 christos unsigned char rv; 703 1.11 christos int cnt; 704 1.11 christos const void *cookie; 705 1.1 christos 706 1.11 christos len = strlen(word); 707 1.11 christos words = sl_init(); 708 1.1 christos 709 1.11 christos /* count the entries in the table */ 710 1.11 christos /* XXX - have a function return this rather than counting? */ 711 1.11 christos cnt = 1; /* count the NULL terminator */ 712 1.11 christos cookie = NULL; 713 1.11 christos while (thread_next_key_name(&cookie) != NULL) 714 1.11 christos cnt++; 715 1.1 christos 716 1.11 christos /* allocate sufficient space for the pointers */ 717 1.14 christos ap = salloc(cnt * sizeof(*ap)); 718 1.1 christos 719 1.11 christos /* load the array */ 720 1.11 christos p = ap; 721 1.11 christos cookie = NULL; 722 1.11 christos while ((name = thread_next_key_name(&cookie)) != NULL) 723 1.11 christos *p++ = name; 724 1.11 christos *p = NULL; 725 1.11 christos sort(ap); 726 1.11 christos for (p = ap; *p != NULL; p++) 727 1.11 christos if (len == 0 || strncmp(*p, word, len) == 0) 728 1.11 christos mail_sl_add(words, estrdup(*p)); 729 1.1 christos 730 1.11 christos rv = complete_ambiguous(el, word, dolist, words); 731 1.1 christos 732 1.11 christos sl_free(words, 1); 733 1.1 christos 734 1.11 christos return rv; 735 1.1 christos } 736 1.11 christos #endif /* THREAD_SUPPORT */ 737 1.1 christos 738 1.9 christos /* from /usr/src/usr.bin/ftp/main.c(1.101) - end */ 739 1.1 christos /************************************************************************/ 740 1.1 christos 741 1.9 christos /* Some people like to bind file completion to CTRL-D. In emacs mode, 742 1.9 christos * CTRL-D is also used to delete the current character, we have to 743 1.9 christos * special case this situation. 744 1.9 christos */ 745 1.9 christos #define EMACS_CTRL_D_BINDING_HACK 746 1.9 christos 747 1.9 christos #ifdef EMACS_CTRL_D_BINDING_HACK 748 1.9 christos static int 749 1.9 christos is_emacs_mode(EditLine *el) 750 1.9 christos { 751 1.9 christos char *mode; 752 1.19 christos 753 1.9 christos if (el_get(el, EL_EDITOR, &mode) == -1) 754 1.9 christos return 0; 755 1.9 christos return equal(mode, "emacs"); 756 1.9 christos } 757 1.9 christos 758 1.9 christos static int 759 1.11 christos emacs_ctrl_d(EditLine *el, const LineInfo *lf, int ch) 760 1.9 christos { 761 1.9 christos static char delunder[3] = { CTRL('f'), CTRL('h'), '\0' }; 762 1.19 christos 763 1.9 christos if (ch == CTRL('d') && is_emacs_mode(el)) { /* CTRL-D is special */ 764 1.13 christos if (lf->buffer == lf->lastchar) 765 1.11 christos return CC_EOF; 766 1.11 christos if (lf->cursor != lf->lastchar) { /* delete without using ^D */ 767 1.9 christos el_push(el, delunder); /* ^F^H */ 768 1.11 christos return CC_NORM; 769 1.9 christos } 770 1.9 christos } 771 1.9 christos return -1; 772 1.9 christos } 773 1.9 christos #endif /* EMACS_CTRL_D_BINDING_HACK */ 774 1.1 christos 775 1.1 christos /* 776 1.11 christos * Check if this is the second request made for this line indicating 777 1.11 christos * the need to list all the completion possibilities. 778 1.11 christos */ 779 1.11 christos static int 780 1.11 christos get_dolist(const LineInfo *lf) 781 1.11 christos { 782 1.11 christos static char last_line[LINESIZE]; 783 1.11 christos static char *last_cursor_pos; 784 1.11 christos char *cursor_pos; 785 1.11 christos int dolist; 786 1.11 christos size_t len; 787 1.11 christos 788 1.11 christos len = lf->lastchar - lf->buffer; 789 1.11 christos if (len >= sizeof(last_line) - 1) 790 1.11 christos return -1; 791 1.11 christos 792 1.11 christos cursor_pos = last_line + (lf->cursor - lf->buffer); 793 1.11 christos dolist = 794 1.11 christos cursor_pos == last_cursor_pos && 795 1.11 christos strncmp(last_line, lf->buffer, len) == 0; 796 1.11 christos 797 1.11 christos (void)strlcpy(last_line, lf->buffer, len + 1); 798 1.11 christos last_cursor_pos = cursor_pos; 799 1.11 christos 800 1.11 christos return dolist; 801 1.11 christos } 802 1.11 christos 803 1.11 christos /* 804 1.11 christos * Take the full line (lf) including the command and split it into a 805 1.11 christos * sub-line (returned) and a completion context (cmplarray). 806 1.11 christos */ 807 1.11 christos static LineInfo * 808 1.11 christos split_line(const char **cmplarray, const LineInfo *lf) 809 1.11 christos { 810 1.11 christos static LineInfo li; 811 1.11 christos const struct cmd *c; 812 1.11 christos char *cmdname; 813 1.11 christos char line[LINESIZE]; 814 1.11 christos char *cp; 815 1.11 christos size_t len; 816 1.11 christos 817 1.11 christos len = lf->cursor - lf->buffer; 818 1.11 christos if (len + 1 > sizeof(line)) 819 1.11 christos return NULL; 820 1.11 christos 821 1.11 christos (void)strlcpy(line, lf->buffer, len + 1); 822 1.11 christos 823 1.11 christos li.cursor = line + len; 824 1.11 christos li.lastchar = line + len; 825 1.13 christos 826 1.13 christos cp = skip_WSP(line); 827 1.11 christos cmdname = get_cmdname(cp); 828 1.11 christos cp += strlen(cmdname); 829 1.11 christos 830 1.11 christos if (cp == li.cursor) { 831 1.11 christos *cmplarray = "c"; 832 1.11 christos li.buffer = cmdname; 833 1.11 christos return &li; 834 1.11 christos } 835 1.11 christos 836 1.11 christos c = lex(cmdname); 837 1.11 christos if (c == NULL) 838 1.11 christos return NULL; 839 1.13 christos 840 1.11 christos *cmplarray = c->c_complete; 841 1.11 christos if (c->c_pipe) { 842 1.11 christos char *cp2; 843 1.11 christos if ((cp2 = shellpr(cp)) != NULL) { 844 1.11 christos cp = cp2; 845 1.13 christos # define XX(a) ((a) + ((a)[1] == '>' ? 2 : 1)) 846 1.11 christos while ((cp2 = shellpr(XX(cp))) != NULL) 847 1.11 christos cp = cp2; 848 1.13 christos 849 1.11 christos if (*cp == '|') { 850 1.11 christos *cmplarray = "xF"; 851 1.13 christos cp = skip_WSP(cp + 1); 852 1.11 christos } 853 1.11 christos else { 854 1.11 christos assert(*cp == '>'); 855 1.13 christos cp = skip_WSP(XX(cp)); 856 1.11 christos *cmplarray = "f"; 857 1.11 christos } 858 1.11 christos # undef XX 859 1.11 christos } 860 1.11 christos } 861 1.11 christos li.buffer = cp; 862 1.11 christos return &li; 863 1.11 christos } 864 1.11 christos 865 1.11 christos /* 866 1.11 christos * Split a sub-line and a completion context into a word and a 867 1.11 christos * completion type. Use the editline tokenizer to handle the quoting 868 1.11 christos * and splitting. 869 1.11 christos */ 870 1.11 christos static char * 871 1.11 christos split_word(int *cmpltype, const char *cmplarray, LineInfo *li) 872 1.11 christos { 873 1.11 christos static Tokenizer *t = NULL; 874 1.11 christos const char **argv; 875 1.11 christos char *word; 876 1.11 christos int argc; 877 1.11 christos int cursorc; 878 1.11 christos int cursoro; 879 1.11 christos int arraylen; 880 1.11 christos 881 1.11 christos if (t != NULL) 882 1.11 christos tok_reset(t); 883 1.11 christos else { 884 1.11 christos if ((t = tok_init(NULL)) == NULL) 885 1.11 christos err(EXIT_FAILURE, "tok_init"); 886 1.11 christos } 887 1.11 christos if (tok_line(t, li, &argc, &argv, &cursorc, &cursoro) == -1) 888 1.11 christos err(EXIT_FAILURE, "tok_line"); 889 1.13 christos 890 1.11 christos if (cursorc >= argc) 891 1.11 christos word = __UNCONST(""); 892 1.11 christos else { 893 1.11 christos word = salloc((size_t)cursoro + 1); 894 1.11 christos (void)strlcpy(word, argv[cursorc], (size_t)cursoro + 1); 895 1.11 christos } 896 1.11 christos 897 1.11 christos /* check for 'continuation' completes (which are uppercase) */ 898 1.19 christos arraylen = (int)strlen(cmplarray); 899 1.11 christos if (cursorc >= arraylen && 900 1.11 christos arraylen > 0 && 901 1.11 christos isupper((unsigned char)cmplarray[arraylen - 1])) 902 1.11 christos cursorc = arraylen - 1; 903 1.13 christos 904 1.11 christos if (cursorc >= arraylen) 905 1.11 christos return NULL; 906 1.13 christos 907 1.11 christos *cmpltype = cmplarray[cursorc]; 908 1.11 christos return word; 909 1.11 christos } 910 1.11 christos 911 1.11 christos /* 912 1.11 christos * A generic complete routine for the mail command line. 913 1.1 christos */ 914 1.1 christos static unsigned char 915 1.9 christos mail_complete(EditLine *el, int ch) 916 1.1 christos { 917 1.11 christos LineInfo *li; 918 1.1 christos const LineInfo *lf; 919 1.11 christos const char *cmplarray; 920 1.11 christos int dolist; 921 1.11 christos int cmpltype; 922 1.11 christos char *word; 923 1.1 christos 924 1.1 christos lf = el_line(el); 925 1.9 christos 926 1.9 christos #ifdef EMACS_CTRL_D_BINDING_HACK 927 1.9 christos { 928 1.9 christos int cc_ret; 929 1.11 christos if ((cc_ret = emacs_ctrl_d(el, lf, ch)) != -1) 930 1.9 christos return cc_ret; 931 1.1 christos } 932 1.9 christos #endif /* EMACS_CTRL_D_BINDING_HACK */ 933 1.9 christos 934 1.11 christos if ((dolist = get_dolist(lf)) == -1) 935 1.11 christos return CC_ERROR; 936 1.9 christos 937 1.11 christos if ((li = split_line(&cmplarray, lf)) == NULL) 938 1.11 christos return CC_ERROR; 939 1.1 christos 940 1.11 christos if ((word = split_word(&cmpltype, cmplarray, li)) == NULL) 941 1.11 christos return CC_ERROR; 942 1.1 christos 943 1.1 christos switch (cmpltype) { 944 1.11 christos case 'a': /* alias complete */ 945 1.11 christos case 'A': 946 1.11 christos return complete_alias(el, word, dolist); 947 1.13 christos 948 1.11 christos case 'c': /* command complete */ 949 1.11 christos case 'C': 950 1.11 christos return complete_command(el, word, dolist); 951 1.13 christos 952 1.11 christos case 'f': /* filename complete */ 953 1.11 christos case 'F': 954 1.11 christos return complete_filename(el, word, dolist); 955 1.13 christos 956 1.11 christos case 'm': 957 1.11 christos case 'M': 958 1.11 christos return complete_smopts(el, word, dolist); 959 1.13 christos 960 1.11 christos case 'n': /* no complete */ 961 1.11 christos case 'N': /* no complete */ 962 1.11 christos return CC_ERROR; 963 1.13 christos 964 1.11 christos case 's': 965 1.11 christos case 'S': 966 1.11 christos return complete_set(el, word, dolist); 967 1.11 christos #ifdef THREAD_SUPPORT 968 1.11 christos case 't': 969 1.11 christos case 'T': 970 1.11 christos return complete_thread_key(el, word, dolist); 971 1.10 christos #endif 972 1.1 christos case 'x': /* executable complete */ 973 1.11 christos case 'X': 974 1.11 christos return complete_executable(el, word, dolist); 975 1.13 christos 976 1.11 christos default: 977 1.11 christos warnx("unknown complete type `%c'", cmpltype); 978 1.11 christos #if 0 979 1.11 christos assert(/*CONSTCOND*/0); 980 1.11 christos #endif 981 1.11 christos return CC_ERROR; 982 1.1 christos } 983 1.1 christos /* NOTREACHED */ 984 1.1 christos } 985 1.1 christos 986 1.1 christos 987 1.9 christos /* 988 1.11 christos * A generic file completion routine. 989 1.9 christos */ 990 1.9 christos static unsigned char 991 1.9 christos file_complete(EditLine *el, int ch) 992 1.9 christos { 993 1.9 christos static char word[LINESIZE]; 994 1.9 christos const LineInfo *lf; 995 1.11 christos size_t word_len; 996 1.11 christos int dolist; 997 1.9 christos 998 1.9 christos lf = el_line(el); 999 1.9 christos 1000 1.9 christos #ifdef EMACS_CTRL_D_BINDING_HACK 1001 1.9 christos { 1002 1.9 christos int cc_ret; 1003 1.11 christos if ((cc_ret = emacs_ctrl_d(el, lf, ch)) != -1) 1004 1.9 christos return cc_ret; 1005 1.9 christos } 1006 1.9 christos #endif /* EMACS_CTRL_D_BINDING_HACK */ 1007 1.9 christos 1008 1.11 christos word_len = lf->cursor - lf->buffer; 1009 1.11 christos if (word_len + 1 > sizeof(word)) 1010 1.11 christos return CC_ERROR; 1011 1.9 christos 1012 1.9 christos (void)strlcpy(word, lf->buffer, word_len + 1); /* do not use estrlcpy here! */ 1013 1.11 christos 1014 1.11 christos if ((dolist = get_dolist(lf)) == -1) 1015 1.11 christos return CC_ERROR; 1016 1.11 christos 1017 1.11 christos return complete_filename(el, word, dolist); 1018 1.9 christos } 1019 1.1 christos 1020 1.1 christos 1021 1.9 christos #ifdef MIME_SUPPORT 1022 1.9 christos /* 1023 1.11 christos * Complete mime_transfer_encoding type. 1024 1.9 christos */ 1025 1.9 christos static unsigned char 1026 1.9 christos mime_enc_complete(EditLine *el, int ch) 1027 1.1 christos { 1028 1.9 christos static char word[LINESIZE]; 1029 1.9 christos StringList *words; 1030 1.9 christos unsigned char rv; 1031 1.9 christos const LineInfo *lf; 1032 1.11 christos size_t word_len; 1033 1.11 christos int dolist; 1034 1.9 christos 1035 1.9 christos lf = el_line(el); 1036 1.1 christos 1037 1.9 christos #ifdef EMACS_CTRL_D_BINDING_HACK 1038 1.9 christos { 1039 1.9 christos int cc_ret; 1040 1.11 christos if ((cc_ret = emacs_ctrl_d(el, lf, ch)) != -1) 1041 1.9 christos return cc_ret; 1042 1.9 christos } 1043 1.9 christos #endif /* EMACS_CTRL_D_BINDING_HACK */ 1044 1.1 christos 1045 1.11 christos word_len = lf->cursor - lf->buffer; 1046 1.11 christos if (word_len >= sizeof(word) - 1) 1047 1.11 christos return CC_ERROR; 1048 1.1 christos 1049 1.9 christos words = mail_sl_init(); 1050 1.9 christos { 1051 1.9 christos const char *ename; 1052 1.9 christos const void *cookie; 1053 1.9 christos cookie = NULL; 1054 1.9 christos for (ename = mime_next_encoding_name(&cookie); 1055 1.9 christos ename; 1056 1.9 christos ename = mime_next_encoding_name(&cookie)) 1057 1.9 christos if (word_len == 0 || 1058 1.9 christos strncmp(lf->buffer, ename, word_len) == 0) { 1059 1.9 christos char *cp; 1060 1.9 christos cp = estrdup(ename); 1061 1.9 christos mail_sl_add(words, cp); 1062 1.9 christos } 1063 1.9 christos } 1064 1.11 christos (void)strlcpy(word, lf->buffer, word_len + 1); 1065 1.1 christos 1066 1.11 christos if ((dolist = get_dolist(lf)) == -1) 1067 1.11 christos return CC_ERROR; 1068 1.11 christos 1069 1.11 christos rv = complete_ambiguous(el, word, dolist, words); 1070 1.1 christos 1071 1.9 christos sl_free(words, 1); 1072 1.11 christos return rv; 1073 1.1 christos } 1074 1.9 christos #endif /* MIME_SUPPORT */ 1075 1.9 christos 1076 1.11 christos 1077 1.9 christos /************************************************************************* 1078 1.9 christos * Our public interface to el_gets(): 1079 1.9 christos * 1080 1.9 christos * init_editline() 1081 1.24 msaitoh * Initializes of all editline and completion data structures. 1082 1.9 christos * 1083 1.9 christos * my_gets() 1084 1.19 christos * Displays prompt, calls el_gets() and deals with history. 1085 1.19 christos * Returns the next line of input as a NULL termnated string 1086 1.19 christos * without the trailing newline, or NULL if el_gets() sees is an 1087 1.19 christos * error or signal. 1088 1.9 christos */ 1089 1.1 christos 1090 1.9 christos static const char *el_prompt; 1091 1.9 christos 1092 1.13 christos /*ARGSUSED*/ 1093 1.9 christos static const char * 1094 1.9 christos show_prompt(EditLine *e __unused) 1095 1.9 christos { 1096 1.9 christos return el_prompt; 1097 1.9 christos } 1098 1.1 christos 1099 1.19 christos /* 1100 1.19 christos * Write the current INTR character to fp in a friendly form. 1101 1.19 christos */ 1102 1.19 christos static void 1103 1.19 christos echo_INTR(void *p) 1104 1.19 christos { 1105 1.19 christos struct termios ttybuf; 1106 1.19 christos char buf[5]; 1107 1.19 christos FILE *fp; 1108 1.19 christos 1109 1.19 christos fp = p; 1110 1.19 christos if (tcgetattr(fileno(stdin), &ttybuf) == -1) 1111 1.19 christos warn("tcgetattr"); 1112 1.19 christos else { 1113 1.19 christos (void)vis(buf, ttybuf.c_cc[VINTR], VIS_SAFE | VIS_NOSLASH, 0); 1114 1.19 christos (void)fprintf(fp, "%s", buf); 1115 1.19 christos (void)fflush(fp); 1116 1.19 christos } 1117 1.19 christos } 1118 1.19 christos 1119 1.19 christos static sig_t old_sigint; 1120 1.19 christos static void 1121 1.19 christos comp_intr(int signo) 1122 1.19 christos { 1123 1.19 christos 1124 1.19 christos echo_INTR(stdout); 1125 1.19 christos old_sigint(signo); 1126 1.19 christos } 1127 1.19 christos 1128 1.11 christos PUBLIC char * 1129 1.9 christos my_gets(el_mode_t *em, const char *prompt, char *string) 1130 1.1 christos { 1131 1.19 christos static char line[LINE_MAX]; 1132 1.19 christos size_t len; 1133 1.9 christos int cnt; 1134 1.9 christos const char *buf; 1135 1.9 christos HistEvent ev; 1136 1.19 christos 1137 1.19 christos sig_check(); 1138 1.1 christos 1139 1.9 christos el_prompt = prompt; 1140 1.1 christos if (string) 1141 1.9 christos el_push(em->el, string); 1142 1.9 christos 1143 1.19 christos /* 1144 1.19 christos * Let el_gets() deal with flow control. Also, make sure we 1145 1.19 christos * output a ^C when we get a SIGINT as el_gets() doesn't echo 1146 1.19 christos * one. 1147 1.19 christos */ 1148 1.19 christos old_sigint = sig_signal(SIGINT, comp_intr); 1149 1.9 christos buf = el_gets(em->el, &cnt); 1150 1.19 christos (void)sig_signal(SIGINT, old_sigint); 1151 1.1 christos 1152 1.19 christos if (buf == NULL) { 1153 1.19 christos sig_check(); 1154 1.9 christos return NULL; 1155 1.3 christos } 1156 1.1 christos 1157 1.20 christos if (cnt > 0) { 1158 1.20 christos if (buf[cnt - 1] == '\n') 1159 1.20 christos cnt--; /* trash the trailing LF */ 1160 1.19 christos 1161 1.20 christos len = MIN(sizeof(line) - 1, (size_t)cnt); 1162 1.20 christos (void)memcpy(line, buf, len); 1163 1.20 christos } 1164 1.1 christos line[cnt] = '\0'; 1165 1.1 christos 1166 1.9 christos /* enter non-empty lines into history */ 1167 1.9 christos if (em->hist) { 1168 1.9 christos const char *p; 1169 1.19 christos 1170 1.13 christos p = skip_WSP(line); 1171 1.9 christos if (*p && history(em->hist, &ev, H_ENTER, line) == 0) 1172 1.9 christos (void)printf("Failed history entry: %s", line); 1173 1.9 christos } 1174 1.19 christos sig_check(); 1175 1.1 christos return line; 1176 1.1 christos } 1177 1.1 christos 1178 1.9 christos static el_mode_t 1179 1.9 christos init_el_mode( 1180 1.9 christos const char *el_editor, 1181 1.9 christos unsigned char (*completer)(EditLine *, int), 1182 1.9 christos struct name *keys, 1183 1.9 christos int history_size) 1184 1.9 christos { 1185 1.17 christos FILE *nullfp; 1186 1.9 christos el_mode_t em; 1187 1.17 christos 1188 1.9 christos (void)memset(&em, 0, sizeof(em)); 1189 1.9 christos 1190 1.17 christos if ((nullfp = fopen(_PATH_DEVNULL, "w")) == NULL) 1191 1.18 christos err(EXIT_FAILURE, "Cannot open `%s'", _PATH_DEVNULL); 1192 1.17 christos 1193 1.17 christos if ((em.el = el_init(getprogname(), stdin, stdout, nullfp)) == NULL) { 1194 1.12 christos warn("el_init"); 1195 1.12 christos return em; 1196 1.12 christos } 1197 1.17 christos (void)fflush(nullfp); 1198 1.17 christos (void)dup2(STDERR_FILENO, fileno(nullfp)); 1199 1.1 christos 1200 1.9 christos (void)el_set(em.el, EL_PROMPT, show_prompt); 1201 1.12 christos (void)el_set(em.el, EL_SIGNAL, 1); /* editline handles the signals. */ 1202 1.13 christos 1203 1.9 christos if (el_editor) 1204 1.9 christos (void)el_set(em.el, EL_EDITOR, el_editor); 1205 1.9 christos 1206 1.9 christos if (completer) { 1207 1.9 christos struct name *np; 1208 1.9 christos (void)el_set(em.el, EL_ADDFN, "mail-complete", 1209 1.9 christos "Context sensitive argument completion", completer); 1210 1.9 christos for (np = keys; np; np = np->n_flink) 1211 1.9 christos (void)el_set(em.el, EL_BIND, np->n_name, 1212 1.9 christos "mail-complete", NULL); 1213 1.9 christos } 1214 1.9 christos 1215 1.9 christos if (history_size) { 1216 1.9 christos HistEvent ev; 1217 1.12 christos if ((em.hist = history_init()) == NULL) { 1218 1.12 christos warn("history_init"); 1219 1.12 christos return em; 1220 1.12 christos } 1221 1.12 christos if (history(em.hist, &ev, H_SETSIZE, history_size) == -1) 1222 1.9 christos (void)printf("history: %s\n", ev.str); 1223 1.9 christos (void)el_set(em.el, EL_HIST, history, em.hist); 1224 1.9 christos } 1225 1.9 christos 1226 1.9 christos (void)el_source(em.el, NULL); /* read ~/.editrc */ 1227 1.9 christos 1228 1.9 christos return em; 1229 1.1 christos } 1230 1.1 christos 1231 1.9 christos 1232 1.9 christos struct el_modes_s elm = { 1233 1.9 christos .command = { .el = NULL, .hist = NULL, }, 1234 1.9 christos .string = { .el = NULL, .hist = NULL, }, 1235 1.9 christos .filec = { .el = NULL, .hist = NULL, }, 1236 1.9 christos #ifdef MIME_SUPPORT 1237 1.9 christos .mime_enc = { .el = NULL, .hist = NULL, }, 1238 1.9 christos #endif 1239 1.9 christos }; 1240 1.9 christos 1241 1.11 christos PUBLIC void 1242 1.9 christos init_editline(void) 1243 1.1 christos { 1244 1.9 christos const char *mode; 1245 1.9 christos int hist_size; 1246 1.9 christos struct name *keys; 1247 1.9 christos char *cp; 1248 1.9 christos 1249 1.9 christos mode = value(ENAME_EL_EDITOR); 1250 1.13 christos 1251 1.9 christos cp = value(ENAME_EL_HISTORY_SIZE); 1252 1.9 christos hist_size = cp ? atoi(cp) : 0; 1253 1.1 christos 1254 1.9 christos cp = value(ENAME_EL_COMPLETION_KEYS); 1255 1.9 christos keys = cp && *cp ? lexpand(cp, 0) : NULL; 1256 1.1 christos 1257 1.9 christos elm.command = init_el_mode(mode, mail_complete, keys, hist_size); 1258 1.9 christos elm.filec = init_el_mode(mode, file_complete, keys, 0); 1259 1.9 christos elm.string = init_el_mode(mode, NULL, NULL, 0); 1260 1.9 christos #ifdef MIME_SUPPORT 1261 1.9 christos elm.mime_enc = init_el_mode(mode, mime_enc_complete, keys, 0); 1262 1.9 christos #endif 1263 1.1 christos return; 1264 1.1 christos } 1265 1.1 christos 1266 1.9 christos #endif /* USE_EDITLINE */ 1267