1 /* 2 * tw.comp.c: File completion builtin 3 */ 4 /*- 5 * Copyright (c) 1980, 1991 The Regents of the University of California. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 #include "sh.h" 33 #include "tw.h" 34 #include "ed.h" 35 #include "tc.h" 36 37 /* #define TDEBUG */ 38 struct varent completions; 39 40 static int tw_result (const Char *, Char **); 41 static Char **tw_find (Char *, struct varent *, int); 42 static Char *tw_tok (Char *); 43 static int tw_pos (Char *, int); 44 static void tw_pr (Char **); 45 static int tw_match (const Char *, const Char *, int); 46 static void tw_prlist (struct varent *); 47 static const Char *tw_dollar (const Char *,Char **, size_t, Char **, 48 Char, const char *); 49 50 /* docomplete(): 51 * Add or list completions in the completion list 52 */ 53 /*ARGSUSED*/ 54 void 55 docomplete(Char **v, struct command *t) 56 { 57 struct varent *vp; 58 Char *p; 59 Char **pp; 60 61 USE(t); 62 v++; 63 p = *v++; 64 if (p == 0) 65 tw_prlist(&completions); 66 else if (*v == 0) { 67 vp = adrof1(strip(p), &completions); 68 if (vp && vp->vec) 69 tw_pr(vp->vec), xputchar('\n'); 70 else 71 { 72 #ifdef TDEBUG 73 xprintf("tw_find(%s) \n", short2str(strip(p))); 74 #endif /* TDEBUG */ 75 pp = tw_find(strip(p), &completions, FALSE); 76 if (pp) 77 tw_pr(pp), xputchar('\n'); 78 } 79 } 80 else 81 set1(strip(p), saveblk(v), &completions, VAR_READWRITE); 82 } /* end docomplete */ 83 84 85 /* douncomplete(): 86 * Remove completions from the completion list 87 */ 88 /*ARGSUSED*/ 89 void 90 douncomplete(Char **v, struct command *t) 91 { 92 USE(t); 93 unset1(v, &completions); 94 } /* end douncomplete */ 95 96 97 /* tw_prlist(): 98 * Pretty print a list of variables 99 */ 100 static void 101 tw_prlist(struct varent *p) 102 { 103 struct varent *c; 104 105 for (;;) { 106 while (p->v_left) 107 p = p->v_left; 108 x: 109 if (p->v_parent == 0) /* is it the header? */ 110 break; 111 if (setintr) { 112 int old_pintr_disabled; 113 114 pintr_push_enable(&old_pintr_disabled); 115 cleanup_until(&old_pintr_disabled); 116 } 117 xprintf("%s\t", short2str(p->v_name)); 118 if (p->vec) 119 tw_pr(p->vec); 120 xputchar('\n'); 121 if (p->v_right) { 122 p = p->v_right; 123 continue; 124 } 125 do { 126 c = p; 127 p = p->v_parent; 128 } while (p->v_right == c); 129 goto x; 130 } 131 } /* end tw_prlist */ 132 133 134 /* tw_pr(): 135 * Pretty print a completion, adding single quotes around 136 * a completion argument and collapsing multiple spaces to one. 137 */ 138 static void 139 tw_pr(Char **cmp) 140 { 141 int sp, osp; 142 Char *ptr; 143 144 for (; *cmp; cmp++) { 145 xputchar('\''); 146 for (osp = 0, ptr = *cmp; *ptr; ptr++) { 147 sp = Isspace(*ptr); 148 if (sp && osp) 149 continue; 150 xputwchar(*ptr); 151 osp = sp; 152 } 153 xputchar('\''); 154 if (cmp[1]) 155 xputchar(' '); 156 } 157 } /* end tw_pr */ 158 159 160 /* tw_find(): 161 * Find the first matching completion. 162 * For commands we only look at names that start with - 163 */ 164 static Char ** 165 tw_find(Char *nam, struct varent *vp, int cmd) 166 { 167 Char **rv; 168 169 for (vp = vp->v_left; vp; vp = vp->v_right) { 170 if (vp->v_left && (rv = tw_find(nam, vp, cmd)) != NULL) 171 return rv; 172 if (cmd) { 173 if (vp->v_name[0] != '-') 174 continue; 175 if (Gmatch(nam, &vp->v_name[1]) && vp->vec != NULL) 176 return vp->vec; 177 } 178 else 179 if (Gmatch(nam, vp->v_name) && vp->vec != NULL) 180 return vp->vec; 181 } 182 return NULL; 183 } /* end tw_find */ 184 185 186 /* tw_pos(): 187 * Return true if the position is within the specified range 188 */ 189 static int 190 tw_pos(Char *ran, int wno) 191 { 192 Char *p; 193 194 if (ran[0] == '*' && ran[1] == '\0') 195 return 1; 196 197 for (p = ran; *p && *p != '-'; p++) 198 continue; 199 200 if (*p == '\0') /* range == <number> */ 201 return wno == getn(ran); 202 203 if (ran == p) /* range = - <number> */ 204 return wno <= getn(&ran[1]); 205 *p++ = '\0'; 206 207 if (*p == '\0') /* range = <number> - */ 208 return getn(ran) <= wno; 209 else /* range = <number> - <number> */ 210 return (getn(ran) <= wno) && (wno <= getn(p)); 211 } /* end tw_pos */ 212 213 214 /* tw_tok(): 215 * Return the next word from string, unquoteing it. 216 */ 217 static Char * 218 tw_tok(Char *str) 219 { 220 static Char *bf = NULL; 221 222 if (str != NULL) 223 bf = str; 224 225 /* skip leading spaces */ 226 for (; *bf && Isspace(*bf); bf++) 227 continue; 228 229 for (str = bf; *bf && !Isspace(*bf); bf++) { 230 if (ismetahash(*bf)) 231 return INVPTR; 232 *bf = *bf & ~QUOTE; 233 } 234 if (*bf != '\0') 235 *bf++ = '\0'; 236 237 return *str ? str : NULL; 238 } /* end tw_tok */ 239 240 241 /* tw_match(): 242 * Match a string against the pattern given. 243 * and return the number of matched characters 244 * in a prefix of the string. 245 */ 246 static int 247 tw_match(const Char *str, const Char *pat, int exact) 248 { 249 const Char *estr; 250 int rv = exact ? Gmatch(estr = str, pat) : Gnmatch(str, pat, &estr); 251 #ifdef TDEBUG 252 xprintf("G%smatch(%s, ", exact ? "" : "n", short2str(str)); 253 xprintf("%s, ", short2str(pat)); 254 xprintf("%s) = %d [%" TCSH_PTRDIFF_T_FMT "d]\n", short2str(estr), rv, 255 estr - str); 256 #endif /* TDEBUG */ 257 return (int) (rv ? estr - str : -1); 258 } 259 260 261 /* tw_result(): 262 * Return what the completion action should be depending on the 263 * string 264 */ 265 static int 266 tw_result(const Char *act, Char **pat) 267 { 268 int looking; 269 static Char* res = NULL; 270 Char *p; 271 272 if (res != NULL) 273 xfree(res), res = NULL; 274 275 switch (act[0] & ~QUOTE) { 276 case 'X': 277 looking = TW_COMPLETION; 278 break; 279 case 'S': 280 looking = TW_SIGNAL; 281 break; 282 case 'a': 283 looking = TW_ALIAS; 284 break; 285 case 'b': 286 looking = TW_BINDING; 287 break; 288 case 'c': 289 looking = TW_COMMAND; 290 break; 291 case 'C': 292 looking = TW_PATH | TW_COMMAND; 293 break; 294 case 'd': 295 looking = TW_DIRECTORY; 296 break; 297 case 'D': 298 looking = TW_PATH | TW_DIRECTORY; 299 break; 300 case 'e': 301 looking = TW_ENVVAR; 302 break; 303 case 'f': 304 looking = TW_FILE; 305 break; 306 #ifdef COMPAT 307 case 'p': 308 #endif /* COMPAT */ 309 case 'F': 310 looking = TW_PATH | TW_FILE; 311 break; 312 case 'g': 313 looking = TW_GRPNAME; 314 break; 315 case 'j': 316 looking = TW_JOB; 317 break; 318 case 'l': 319 looking = TW_LIMIT; 320 break; 321 case 'n': 322 looking = TW_NONE; 323 break; 324 case 's': 325 looking = TW_SHELLVAR; 326 break; 327 case 't': 328 looking = TW_TEXT; 329 break; 330 case 'T': 331 looking = TW_PATH | TW_TEXT; 332 break; 333 case 'v': 334 looking = TW_VARIABLE; 335 break; 336 case 'u': 337 looking = TW_USER; 338 break; 339 case 'x': 340 looking = TW_EXPLAIN; 341 break; 342 343 case '$': 344 *pat = res = Strsave(&act[1]); 345 (void) strip(res); 346 return(TW_VARLIST); 347 348 case '(': 349 *pat = res = Strsave(&act[1]); 350 if ((p = Strchr(res, ')')) != NULL) 351 *p = '\0'; 352 (void) strip(res); 353 return TW_WORDLIST; 354 355 case '`': 356 res = Strsave(act); 357 if ((p = Strchr(&res[1], '`')) != NULL) 358 *++p = '\0'; 359 360 if (didfds == 0) { 361 /* 362 * Make sure that we have some file descriptors to 363 * play with, so that the processes have at least 0, 1, 2 364 * open 365 */ 366 (void) dcopy(SHIN, 0); 367 (void) dcopy(SHOUT, 1); 368 (void) dcopy(SHDIAG, 2); 369 } 370 if ((p = globone(res, G_APPEND)) != NULL) { 371 xfree(res), res = NULL; 372 *pat = res = Strsave(p); 373 xfree(p); 374 return TW_WORDLIST; 375 } 376 return TW_ZERO; 377 378 default: 379 stderror(ERR_COMPCOM, short2str(act)); 380 return TW_ZERO; 381 } 382 383 switch (act[1] & ~QUOTE) { 384 case '\0': 385 return looking; 386 387 case ':': 388 *pat = res = Strsave(&act[2]); 389 (void) strip(res); 390 return looking; 391 392 default: 393 stderror(ERR_COMPCOM, short2str(act)); 394 return TW_ZERO; 395 } 396 } /* end tw_result */ 397 398 399 /* tw_dollar(): 400 * Expand $<n> args in buffer 401 */ 402 static const Char * 403 tw_dollar(const Char *str, Char **wl, size_t nwl, Char **result, Char sep, 404 const char *msg) 405 { 406 struct Strbuf buf = Strbuf_INIT; 407 Char *res; 408 const Char *sp; 409 410 for (sp = str; *sp && *sp != sep;) 411 if (sp[0] == '$' && sp[1] == ':' && Isdigit(sp[sp[2] == '-' ? 3 : 2])) { 412 int num, neg = 0; 413 sp += 2; 414 if (*sp == '-') { 415 neg = 1; 416 sp++; 417 } 418 for (num = *sp++ - '0'; Isdigit(*sp); num += 10 * num + *sp++ - '0') 419 continue; 420 if (neg) 421 num = nwl - num - 1; 422 if (num >= 0 && (size_t)num < nwl) 423 Strbuf_append(&buf, wl[num]); 424 } 425 else 426 Strbuf_append1(&buf, *sp++); 427 428 res = Strbuf_finish(&buf); 429 430 if (*sp++ == sep) { 431 *result = res; 432 return sp; 433 } 434 435 xfree(res); 436 /* Truncates data if WIDE_STRINGS */ 437 stderror(ERR_COMPMIS, (int)sep, msg, short2str(str)); 438 return --sp; 439 } /* end tw_dollar */ 440 441 442 /* tw_complete(): 443 * Return the appropriate completion for the command 444 * 445 * valid completion strings are: 446 * p/<range>/<completion>/[<suffix>/] positional 447 * c/<pattern>/<completion>/[<suffix>/] current word ignore pattern 448 * C/<pattern>/<completion>/[<suffix>/] current word with pattern 449 * n/<pattern>/<completion>/[<suffix>/] next word 450 * N/<pattern>/<completion>/[<suffix>/] next-next word 451 */ 452 int 453 tw_complete(const Char *line, Char **word, Char **pat, int looking, eChar *suf) 454 { 455 Char *buf, **vec, **wl; 456 static Char nomatch[2] = { (Char) ~0, 0x00 }; 457 const Char *ptr; 458 size_t wordno; 459 int n; 460 461 buf = Strsave(line); 462 cleanup_push(buf, xfree); 463 /* Single-character words, empty current word, terminating NULL */ 464 wl = xmalloc(((Strlen(line) + 1) / 2 + 2) * sizeof (*wl)); 465 cleanup_push(wl, xfree); 466 467 /* find the command */ 468 if ((wl[0] = tw_tok(buf)) == NULL || wl[0] == INVPTR) { 469 cleanup_until(buf); 470 return TW_ZERO; 471 } 472 473 /* 474 * look for hardwired command completions using a globbing 475 * search and for arguments using a normal search. 476 */ 477 if ((vec = tw_find(wl[0], &completions, (looking == TW_COMMAND))) 478 == NULL) { 479 cleanup_until(buf); 480 return looking; 481 } 482 483 /* tokenize the line one more time :-( */ 484 for (wordno = 1; (wl[wordno] = tw_tok(NULL)) != NULL && 485 wl[wordno] != INVPTR; wordno++) 486 continue; 487 488 if (wl[wordno] == INVPTR) { /* Found a meta character */ 489 cleanup_until(buf); 490 return TW_ZERO; /* de-activate completions */ 491 } 492 #ifdef TDEBUG 493 { 494 size_t i; 495 for (i = 0; i < wordno; i++) 496 xprintf("'%s' ", short2str(wl[i])); 497 xprintf("\n"); 498 } 499 #endif /* TDEBUG */ 500 501 /* if the current word is empty move the last word to the next */ 502 if (**word == '\0') { 503 wl[wordno] = *word; 504 wordno++; 505 } 506 wl[wordno] = NULL; 507 508 509 #ifdef TDEBUG 510 xprintf("\r\n"); 511 xprintf(" w#: %lu\n", (unsigned long)wordno); 512 xprintf("line: %s\n", short2str(line)); 513 xprintf(" cmd: %s\n", short2str(wl[0])); 514 xprintf("word: %s\n", short2str(*word)); 515 xprintf("last: %s\n", wordno >= 2 ? short2str(wl[wordno-2]) : "n/a"); 516 xprintf("this: %s\n", wordno >= 1 ? short2str(wl[wordno-1]) : "n/a"); 517 #endif /* TDEBUG */ 518 519 for (;vec != NULL && (ptr = vec[0]) != NULL; vec++) { 520 Char *ran, /* The pattern or range X/<range>/XXXX/ */ 521 *com, /* The completion X/XXXXX/<completion>/ */ 522 *pos = NULL; /* scratch pointer */ 523 int cmd, res; 524 Char sep; /* the command and separator characters */ 525 int exact; 526 527 if (ptr[0] == '\0') 528 continue; 529 530 #ifdef TDEBUG 531 xprintf("match %s\n", short2str(ptr)); 532 #endif /* TDEBUG */ 533 534 switch (cmd = ptr[0]) { 535 case 'N': 536 pos = (wordno < 3) ? nomatch : wl[wordno - 3]; 537 break; 538 case 'n': 539 pos = (wordno < 2) ? nomatch : wl[wordno - 2]; 540 break; 541 case 'c': 542 case 'C': 543 pos = (wordno < 1) ? nomatch : wl[wordno - 1]; 544 break; 545 case 'p': 546 break; 547 default: 548 stderror(ERR_COMPINV, CGETS(27, 1, "command"), cmd); 549 return TW_ZERO; 550 } 551 552 sep = ptr[1]; 553 if (!Ispunct(sep)) { 554 /* Truncates data if WIDE_STRINGS */ 555 stderror(ERR_COMPINV, CGETS(27, 2, "separator"), (int)sep); 556 return TW_ZERO; 557 } 558 559 ptr = tw_dollar(&ptr[2], wl, wordno, &ran, sep, 560 CGETS(27, 3, "pattern")); 561 cleanup_push(ran, xfree); 562 if (ran[0] == '\0') /* check for empty pattern (disallowed) */ 563 { 564 stderror(ERR_COMPINC, cmd == 'p' ? CGETS(27, 4, "range") : 565 CGETS(27, 3, "pattern"), ""); 566 return TW_ZERO; 567 } 568 569 ptr = tw_dollar(ptr, wl, wordno, &com, sep, 570 CGETS(27, 5, "completion")); 571 cleanup_push(com, xfree); 572 573 if (*ptr != '\0') { 574 if (*ptr == sep) 575 *suf = CHAR_ERR; 576 else 577 *suf = *ptr; 578 } 579 else 580 *suf = '\0'; 581 582 #ifdef TDEBUG 583 xprintf("command: %c\nseparator: %c\n", cmd, (int)sep); 584 xprintf("pattern: %s\n", short2str(ran)); 585 xprintf("completion: %s\n", short2str(com)); 586 xprintf("suffix: "); 587 switch (*suf) { 588 case 0: 589 xprintf("*auto suffix*\n"); 590 break; 591 case CHAR_ERR: 592 xprintf("*no suffix*\n"); 593 break; 594 default: 595 xprintf("%c\n", (int)*suf); 596 break; 597 } 598 #endif /* TDEBUG */ 599 600 exact = 0; 601 switch (cmd) { 602 case 'p': /* positional completion */ 603 #ifdef TDEBUG 604 xprintf("p: tw_pos(%s, %lu) = ", short2str(ran), 605 (unsigned long)wordno - 1); 606 xprintf("%d\n", tw_pos(ran, wordno - 1)); 607 #endif /* TDEBUG */ 608 if (!tw_pos(ran, wordno - 1)) { 609 cleanup_until(ran); 610 continue; 611 } 612 break; 613 614 case 'N': /* match with the next-next word */ 615 case 'n': /* match with the next word */ 616 exact = 1; 617 /*FALLTHROUGH*/ 618 case 'c': /* match with the current word */ 619 case 'C': 620 #ifdef TDEBUG 621 xprintf("%c: ", cmd); 622 #endif /* TDEBUG */ 623 if ((n = tw_match(pos, ran, exact)) < 0) { 624 cleanup_until(ran); 625 continue; 626 } 627 if (cmd == 'c') 628 *word += n; 629 break; 630 631 default: 632 abort(); /* Cannot happen */ 633 } 634 tsetenv(STRCOMMAND_LINE, line); 635 res = tw_result(com, pat); 636 Unsetenv(STRCOMMAND_LINE); 637 cleanup_until(buf); 638 return res; 639 } 640 cleanup_until(buf); 641 *suf = '\0'; 642 return TW_ZERO; 643 } /* end tw_complete */ 644