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