1: /* $Header: /home/hyperion/mu/christos/src/sys/tcsh-6.00/RCS/tw.parse.c,v 3.0 1991/07/04 21:49:28 christos Exp $ */ 2: /* 3: * tw.parse.c: Everyone has taken a shot in this futile effort to 4: * lexically analyze a csh line... Well we cannot good 5: * a job as good as sh.lex.c; but we try. Amazing that 6: * it works considering how many hands have touched this code 7: */ 8: /*- 9: * Copyright (c) 1980, 1991 The Regents of the University of California. 10: * All rights reserved. 11: * 12: * Redistribution and use in source and binary forms, with or without 13: * modification, are permitted provided that the following conditions 14: * are met: 15: * 1. Redistributions of source code must retain the above copyright 16: * notice, this list of conditions and the following disclaimer. 17: * 2. Redistributions in binary form must reproduce the above copyright 18: * notice, this list of conditions and the following disclaimer in the 19: * documentation and/or other materials provided with the distribution. 20: * 3. All advertising materials mentioning features or use of this software 21: * must display the following acknowledgement: 22: * This product includes software developed by the University of 23: * California, Berkeley and its contributors. 24: * 4. Neither the name of the University nor the names of its contributors 25: * may be used to endorse or promote products derived from this software 26: * without specific prior written permission. 27: * 28: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 29: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 30: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 31: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 32: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 33: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 34: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 35: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 36: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 37: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 38: * SUCH DAMAGE. 39: */ 40: #include "config.h" 41: #if !defined(lint) && !defined(pdp11) 42: static char *rcsid() 43: { return "$Id: tw.parse.c,v 3.0 1991/07/04 21:49:28 christos Exp $"; } 44: #endif 45: 46: #include "sh.h" 47: #include "tw.h" 48: #include "ed.h" 49: 50: /* #define TENEDEBUG */ 51: 52: /* true if the path has relative elements */ 53: static bool relatives_in_path; 54: 55: static int maxitems = 0; 56: Char **com_list = (Char **) NULL; /* the pre-digested list of commands 57: * for speed and general usefullness */ 58: int numcommands = 0; 59: int have_sorted = 0; 60: 61: /* Set to TRUE if recexact is set and an exact match is found 62: * along with other, longer, matches. 63: */ 64: int non_unique_match = FALSE; 65: 66: #ifdef notdef 67: int dirctr = 0; /* -1 0 1 2 ... */ 68: 69: #endif 70: Char dirflag[5]; /* ' nn\0' - dir #s - . 1 2 ... */ 71: 72: static bool SearchNoDirErr = 0; /* t_search returns -2 if dir is unreadable */ 73: 74: /* do the expand or list on the command line -- SHOULD BE REPLACED */ 75: 76: extern Char NeedsRedraw; /* from ed.h */ 77: extern int TermH; /* from the editor routines */ 78: extern int lbuffed; /* from sh.print.c */ 79: extern bool relatives_in_path; /* set true if PATH has relative elements */ 80: 81: static void free_items __P((Char **, int)); 82: static void extract_dir_and_name __P((Char *, Char *, Char *)); 83: static Char *quote_meta __P((Char *, bool)); 84: static Char *getentry __P((DIR *, int)); 85: static Char *dollar __P((Char *, Char *)); 86: static Char *tilde __P((Char *, Char *)); 87: static Char filetype __P((Char *, Char *)); 88: static int t_glob __P((Char ***)); 89: static int is_prefix __P((Char *, Char *)); 90: static int is_suffix __P((Char *, Char *)); 91: static int recognize __P((Char *, Char *, int, int)); 92: static int ignored __P((Char *)); 93: static void tw_get_comm_list __P((void)); 94: static int isadirectory __P((Char *, Char *)); 95: 96: /* 97: * If we find a set command, then we break a=b to a= and word becomes 98: * b else, we don't break a=b. 99: */ 100: #define isaset(c, w) ((w)[-1] == '=' && \ 101: ((c)[0] == 's' && (c)[1] == 'e' && (c)[2] == 't' && \ 102: ((c[3] == ' ' || (c)[3] == '\t')))) 103: /* 104: * Return value for tenematch(): 105: * > 1: No. of items found 106: * = 1: Exactly one match / spelling corrected 107: * = 0: No match / spelling was correct 108: * < 0: Error (incl spelling correction impossible) 109: */ 110: int 111: tenematch(in_line, in_li_size, num_read, command) 112: Char *in_line; /* match string prefix */ 113: int in_li_size; /* max size of string */ 114: int num_read; /* # actually in in_line */ 115: COMMAND command; /* LIST or RECOGNIZE or PRINT_HELP */ 116: 117: { 118: Char word[FILSIZ + 1]; 119: register Char *str_end, *word_start, *cmd_start, *wp; 120: Char *cmd_st; 121: int space_left; 122: int is_a_cmd; /* UNIX command rather than filename */ 123: int search_ret; /* what search returned for debugging */ 124: int in_single, in_double; /* In single or in_double quotes */ 125: 126: str_end = &in_line[num_read]; 127: 128: /* 129: * space backward looking for the beginning of this command 130: */ 131: for (cmd_st = str_end; cmd_st > in_line; --cmd_st) 132: if (iscmdmeta(cmd_st[-1]) 133: && ((cmd_st - 1 == in_line) || (cmd_st[-2] != '\\'))) 134: break; 135: /* step forward over leading spaces */ 136: while (*cmd_st != '\0' && (*cmd_st == ' ' || *cmd_st == '\t')) 137: cmd_st++; 138: 139: /* 140: * Find LAST occurence of a delimiter in the in_line. The word start is 141: * one character past it. 142: */ 143: for (word_start = str_end; word_start > in_line; --word_start) { 144: if ((ismeta(word_start[-1]) || isaset(cmd_st, word_start)) && 145: (word_start[-1] != '#') && (word_start[-1] != '$') && 146: ((word_start - 1 == in_line) || (word_start[-2] != '\\'))) 147: break; 148: } 149: 150: 151: 152: #ifdef masscomp 153: /* 154: * Avoid a nasty message from the RTU 4.1A & RTU 5.0 comp_r concerning 155: * the "overuse of registers". According to the comp_r release notes, 156: * incorrect code may be produced unless the offending expression is 157: * rewritten. Therefore, we can't just ignore it, DAS DEC-90. 158: */ 159: space_left = in_li_size; 160: space_left -= word_start - in_line + 1; 161: #else 162: space_left = in_li_size - (word_start - in_line) - 1; 163: #endif 164: 165: is_a_cmd = starting_a_command(word_start, in_line); 166: #ifdef TENEDEBUG 167: xprintf("starting_a_command %d\n", is_a_cmd); 168: #endif 169: 170: /* 171: * Quote args 172: */ 173: in_double = 0; 174: in_single = 0; 175: for (cmd_start = word_start, wp = word; cmd_start < str_end && wp <= word + FILSIZ; cmd_start++) 176: switch (*cmd_start) { 177: case '\'': 178: if (!in_double) { 179: if (in_single) 180: in_single = 0; 181: else 182: in_single = QUOTE; 183: /* 184: * Move the word_start further, cause the quotes so far have no 185: * effect. 186: */ 187: if (cmd_start == word_start) 188: word_start++; 189: } 190: else 191: *wp++ = *cmd_start | QUOTE; 192: break; 193: case '"': 194: if (!in_single) { 195: if (in_double) 196: in_double = 0; 197: else 198: in_double = QUOTE; 199: /* 200: * Move the word_start further, cause the quotes so far have no 201: * effect. 202: */ 203: if (cmd_start == word_start) 204: word_start++; 205: } 206: else 207: *wp++ = *cmd_start | QUOTE; 208: break; 209: case '/': 210: /* 211: * This is so that the recognize stuff works easily 212: */ 213: *wp++ = *cmd_start; 214: break; 215: case '\\': 216: if (in_single || in_double) 217: *wp++ = *cmd_start | QUOTE; 218: else 219: *wp++ = *++cmd_start | QUOTE; 220: break; 221: default: 222: *wp++ = *cmd_start | in_single; 223: break; 224: } 225: if (wp > word + FILSIZ) 226: return (-1); 227: *wp = '\0'; 228: 229: 230: #ifdef TENEDEBUG 231: xprintf("\ncmd_st:%s:\n", short2str(cmd_st)); 232: xprintf("word:%s:\n", short2str(word)); 233: xprintf("word:"); 234: for (wp = word; *wp; wp++) 235: xprintf("%c", *wp & QUOTE ? '-' : ' '); 236: xprintf(":\n"); 237: #endif 238: switch ((int) command) { 239: Char buffer[FILSIZ + 1], *bptr; 240: Char *slshp; 241: Char *items[2], **ptr; 242: int i, count; 243: 244: case RECOGNIZE: 245: if (adrof(STRa_correct)) { 246: if ((slshp = Strrchr(word, '/')) != NULL && slshp[1] != NULL) { 247: SearchNoDirErr = 1; 248: for (bptr = word; bptr < slshp; bptr++) { 249: /* 250: * do not try to correct spelling of words containing 251: * globbing characters 252: */ 253: if (isglob(*bptr)) { 254: SearchNoDirErr = 0; 255: break; 256: } 257: } 258: } 259: } 260: else 261: slshp = STRNULL; 262: search_ret = t_search(word, wp, command, space_left, is_a_cmd, 1); 263: SearchNoDirErr = 0; 264: 265: if (search_ret == -2) { 266: Char rword[FILSIZ + 1]; 267: 268: (void) Strcpy(rword, slshp); 269: if (slshp != STRNULL) 270: *slshp = NULL; 271: if ((search_ret = spell_me(word, sizeof(word), is_a_cmd)) == 1) { 272: DeleteBack(str_end - word_start);/* get rid of old word */ 273: (void) Strcat(word, rword); 274: if (InsertStr(word) < 0) /* insert newly spelled word */ 275: return -1; /* error inserting */ 276: wp = word + Strlen(word); 277: search_ret = t_search(word, wp, command, space_left, 278: is_a_cmd, 1); 279: } 280: } 281: 282: /* 283: * Change by Christos Zoulas: if the name has metachars in it, quote 284: * the metachars, but only if we are outside quotes. 285: */ 286: if (*wp && InsertStr((in_single || in_double) ? 287: wp : quote_meta(wp, 288: (bool) is_set(STRaddsuffix))) < 0) 289: /* put it in the input buffer */ 290: return -1; /* error inserting */ 291: return search_ret; 292: 293: case SPELL: 294: for (bptr = word_start; bptr < str_end; bptr++) { 295: /* 296: * do not try to correct spelling of words containing globbing 297: * characters 298: */ 299: if (isglob(*bptr)) 300: return 0; 301: } 302: if ((search_ret = spell_me(word, sizeof(word), is_a_cmd)) == 1) { 303: DeleteBack(str_end - word_start); /* get rid of old word */ 304: if (InsertStr(word) < 0) /* insert newly spelled word */ 305: return -1; /* error inserting */ 306: } 307: return search_ret; 308: 309: case PRINT_HELP: 310: do_help(cmd_st); 311: return 1; 312: 313: case GLOB: 314: case GLOB_EXPAND: 315: (void) Strncpy(buffer, word, FILSIZ + 1); 316: items[0] = buffer; 317: items[1] = NULL; 318: ptr = items; 319: if (is_a_cmd) { 320: xprintf("Sorry no globbing for commands yet..\n"); 321: return 0; 322: } 323: if ((count = t_glob(&ptr)) > 0) { 324: if (command == GLOB) 325: print_by_column(STRNULL, ptr, count, is_a_cmd); 326: else { 327: DeleteBack(str_end - word_start);/* get rid of old word */ 328: for (i = 0; i < count; i++) 329: if (ptr[i] && *ptr[i]) { 330: if (InsertStr((in_single || in_double) ? 331: ptr[i] : quote_meta(ptr[i], 0)) < 0 || 332: InsertStr(STRspace) < 0) { 333: blkfree(ptr); 334: return (-1); 335: } 336: } 337: } 338: blkfree(ptr); 339: } 340: return count; 341: 342: case VARS_EXPAND: 343: if (dollar(buffer, word)) { 344: DeleteBack(str_end - word_start); 345: if (InsertStr((in_single || in_double) ? 346: buffer : quote_meta(buffer, 0)) < 0) 347: return (-1); 348: return (1); 349: } 350: return (0); 351: 352: case LIST: 353: search_ret = t_search(word, wp, command, space_left, is_a_cmd, 1); 354: return search_ret; 355: 356: default: 357: xprintf("tcsh: Internal match error.\n"); 358: return 1; 359: 360: } 361: } 362: 363: 364: 365: 366: static int 367: t_glob(v) 368: register Char ***v; 369: { 370: jmp_buf osetexit; 371: 372: if (**v == 0) 373: return (0); 374: gflag = 0, tglob(*v); 375: if (gflag) { 376: getexit(osetexit); /* make sure to come back here */ 377: if (setexit() == 0) 378: *v = globall(*v); 379: resexit(osetexit); 380: gargv = 0; 381: if (haderr) { 382: haderr = 0; 383: NeedsRedraw = 1; 384: return (-1); 385: } 386: if (*v == 0) 387: return (0); 388: } 389: else 390: return (0); 391: 392: return ((int)gargc); 393: } 394: 395: 396: /* 397: * quote (\) the meta-characters in a word 398: * except trailing space if trail_space is set 399: * return pointer to quoted word in static storage 400: */ 401: static Char * 402: quote_meta(word, trail_space) 403: Char *word; 404: bool trail_space; 405: { 406: static Char buffer[2 * FILSIZ + 1], *bptr, *wptr; 407: 408: for (bptr = buffer, wptr = word; *wptr != '\0';) { 409: if ((cmap(*wptr, _META | _DOL | _Q | _ESC | _GLOB) || *wptr == HIST || 410: *wptr == HISTSUB) && 411: (*wptr != ' ' || !trail_space || *(wptr + 1) != '\0')) 412: *bptr++ = '\\'; 413: *bptr++ = *wptr++; 414: } 415: *bptr = '\0'; 416: return (buffer); 417: } 418: 419: 420: /* 421: * return true if check items initial chars in template 422: * This differs from PWB imatch in that if check is null 423: * it items anything 424: */ 425: 426: static int 427: is_prefix(check, template) 428: register Char *check, *template; 429: { 430: for (; *check; check++, template++) 431: if ((*check & TRIM) != (*template & TRIM)) 432: return (FALSE); 433: return (TRUE); 434: } 435: 436: /* 437: * Return true if the chars in template appear at the 438: * end of check, I.e., are it's suffix. 439: */ 440: static int 441: is_suffix(check, template) 442: register Char *check, *template; 443: { 444: register Char *t, *c; 445: 446: for (t = template; *t++;); 447: for (c = check; *c++;); 448: for (;;) { 449: if (t == template) 450: return 1; 451: --t; 452: --c; 453: if (c == check || (*t & TRIM) != (*c & TRIM)) 454: return 0; 455: } 456: } 457: 458: static int 459: ignored(entry) 460: register Char *entry; 461: { 462: struct varent *vp; 463: register Char **cp; 464: 465: if ((vp = adrof(STRfignore)) == NULL || (cp = vp->vec) == NULL) 466: return (FALSE); 467: for (; *cp != NULL; cp++) 468: if (is_suffix(entry, *cp)) 469: return (TRUE); 470: return (FALSE); 471: } 472: 473: /* return true if the command starting at wordstart is a command */ 474: 475: #define EVEN(x) (((x) & 1) != 1) 476: 477: int 478: starting_a_command(wordstart, in_line) 479: register Char *wordstart, *in_line; 480: { 481: register Char *ptr, *ncmdstart; 482: int count; 483: static Char 484: cmdstart[] = {'`', ';', '&', '(', '|', '\0'}, 485: cmdalive[] = {' ', '\t', '\'', '"', '<', '>', '\0'}; 486: 487: /* 488: * Find if the number of backquotes is odd or even. 489: */ 490: for (ptr = wordstart, count = 0; 491: ptr >= in_line; 492: count += (*ptr-- == '`')); 493: /* 494: * if the number of backquotes is even don't include the backquote char in 495: * the list of command starting delimiters [if it is zero, then it does not 496: * matter] 497: */ 498: ncmdstart = cmdstart + EVEN(count); 499: 500: /* 501: * look for the characters previous to this word if we find a command 502: * starting delimiter we break. if we find whitespace and another previous 503: * word then we are not a command 504: * 505: * count is our state machine: 0 looking for anything 1 found white-space 506: * looking for non-ws 507: */ 508: for (count = 0; wordstart >= in_line; wordstart--) { 509: if (*wordstart == '\0') 510: continue; 511: if (Strchr(ncmdstart, *wordstart)) 512: break; 513: /* 514: * found white space 515: */ 516: if (ptr = Strchr(cmdalive, *wordstart)) 517: count = 1; 518: if (count == 1 && !ptr) 519: return (FALSE); 520: } 521: 522: if (wordstart > in_line) 523: switch (*wordstart) { 524: case '&': /* Look for >& */ 525: while (wordstart > in_line && 526: (*--wordstart == ' ' || *wordstart == '\t')); 527: if (*wordstart == '>') 528: return (FALSE); 529: break; 530: case '(': /* check for foreach, if etc. */ 531: while (wordstart > in_line && 532: (*--wordstart == ' ' || *wordstart == '\t')); 533: if (!iscmdmeta(*wordstart) && 534: (*wordstart != ' ' && *wordstart != '\t')) 535: return (FALSE); 536: break; 537: default: 538: break; 539: } 540: return (TRUE); 541: } 542: 543: 544: 545: /* 546: * Object: extend what user typed up to an ambiguity. 547: * Algorithm: 548: * On first match, copy full entry (assume it'll be the only match) 549: * On subsequent matches, shorten extended_name to the first 550: * character mismatch between extended_name and entry. 551: * If we shorten it back to the prefix length, stop searching. 552: */ 553: static int 554: recognize(extended_name, entry, name_length, numitems) 555: Char *extended_name, *entry; 556: int name_length, numitems; 557: { 558: if (numitems == 1) /* 1st match */ 559: copyn(extended_name, entry, MAXNAMLEN); 560: else { /* 2nd and subsequent matches */ 561: register Char *x, *ent; 562: register int len = 0; 563: 564: for (x = extended_name, ent = entry; 565: *x && (*x & TRIM) == (*ent & TRIM); x++, len++, ent++); 566: *x = '\0'; /* Shorten at 1st char diff */ 567: if (len == name_length) /* Ambiguous to prefix? */ 568: return (-1); /* So stop now and save time */ 569: } 570: return (0); 571: } 572: 573: 574: 575: /* 576: * Perform a RECOGNIZE or LIST command on string "word". 577: * 578: * Return value: 579: * >= 0: SPELL command: "distance" (see spdist()) 580: * other: No. of items found 581: * < 0: Error (message or beep is output) 582: */ 583: 584: /*ARGSUSED*/ 585: int 586: t_search(word, wp, command, max_word_length, look_command, list_max) 587: Char *word, *wp; /* original end-of-word */ 588: COMMAND command; 589: int max_word_length, look_command, list_max; 590: { 591: register ignoring = 1, nignored = 0; 592: register name_length, /* Length of prefix (file name) */ 593: look_lognames; /* True if looking for login names */ 594: int showpathn; /* True if we want path number */ 595: Char tilded_dir[FILSIZ + 1], /* dir after ~ expansion */ 596: dollar_dir[FILSIZ + 1], /* dir after $ expansion */ 597: dir[FILSIZ + 1], /* /x/y/z/ part in /x/y/z/f */ 598: name[MAXNAMLEN + 1],/* f part in /d/d/d/f */ 599: extended_name[MAXNAMLEN + 1], /* the recognized (extended) 600: * name */ 601: *entry = NULL, /* single directory entry or logname */ 602: *target; /* Target to expand/correct/list */ 603: int next_command = 0; /* the next command to take out of */ 604: 605: /* the list of commands */ 606: int look_shellvar, /* true if looking for $foo */ 607: look_file; /* true if looking for a file name */ 608: Char **pathv; /* pointer to PATH elements */ 609: struct varent *v_ptr = NULL;/* current shell variable position */ 610: Char **env_ptr = NULL; /* current env. variable position */ 611: 612: int d = 4, nd; /* distance and new distance to command for 613: * SPELL */ 614: int exec_check = 0, dir_ok = 0; /* need to check 615: * executability/directory */ 616: 617: static Char /* For unset path */ 618: * pv[2] = {STRNULL, NULL}; 619: static DIR 620: * dir_fd = NULL; 621: static Char 622: ** items = NULL; /* file names when doing a LIST */ 623: 624: /* 625: * bugfix by Marty Grossman (grossman@CC5.BBN.COM): directory listing can 626: * dump core when interrupted 627: */ 628: static int numitems; 629: 630: pathv = (v_ptr = adrof(STRPATH)) == NULL ? pv : v_ptr->vec; 631: 632: if (items != NULL) 633: FREE_ITEMS(items, numitems); 634: numitems = 0; 635: if (dir_fd != NULL) 636: FREE_DIR(dir_fd); 637: 638: non_unique_match = FALSE; /* See the recexact code below */ 639: 640: extract_dir_and_name(word, dir, name); 641: look_lognames = (*word == '~') && (Strchr(word, '/') == NULL); 642: look_shellvar = (target = Strrchr(name, '$')) && 643: (Strchr(name, '/') == NULL); 644: look_file = (!look_command && !look_lognames && 645: !look_shellvar) || Strchr(word, '/'); 646: 647: /* PWP: don't even bother when doing ALL of the commands */ 648: if (look_command && (*word == '\0')) { 649: Beep(); 650: return (-1); 651: } 652: tilded_dir[0] = '\0'; 653: dollar_dir[0] = '\0'; 654: 655: if (look_shellvar) { /* Looking for a shell var? */ 656: v_ptr = tw_shell_list_start(); 657: env_ptr = tw_env_list_start(); 658: target++; 659: } 660: else 661: target = name; 662: if (look_shellvar || look_file) { 663: Char *nd = NULL; 664: 665: /* Open the directory */ 666: /* expand ~user/... and variables stuff */ 667: if ((dollar(dollar_dir, dir) == 0) || 668: (tilde(tilded_dir, dollar_dir) == 0) || 669: !(nd = dnormalize(*tilded_dir ? tilded_dir : STRdot)) || 670: ((dir_fd = opendir(short2str(nd))) == NULL)) { 671: xfree((ptr_t) nd); 672: if (SearchNoDirErr) 673: return (-2); 674: xprintf("\n%s unreadable\n", 675: *tilded_dir ? short2str(tilded_dir) : 676: (*dollar_dir ? short2str(dollar_dir) : short2str(dir))); 677: NeedsRedraw = 1; 678: return (-1); 679: } 680: if (nd) { 681: if (*tilded_dir != '\0') { 682: Char *s, *d, *p; 683: 684: /* 685: * Copy and append a / if there was one 686: */ 687: for (p = tilded_dir; *p; p++); 688: if (*--p == '/') { 689: for (p = nd; *p; p++); 690: if (*--p != '/') 691: p = NULL; 692: } 693: for (d = tilded_dir, s = nd; *d++ = *s++;); 694: if (!p) { 695: *d-- = '\0'; 696: *d = '/'; 697: } 698: } 699: xfree((ptr_t) nd); 700: } 701: } 702: else if (look_lognames) { /* Looking for login names? */ 703: (void) setpwent(); /* Open passwd file */ 704: copyn(name, &word[1], MAXNAMLEN); /* name sans ~ */ 705: } 706: else if (look_command) { 707: if (!numcommands) /* if we have no list of commands */ 708: tw_get_comm_list(); 709: if (!have_sorted) { /* if we haven't sorted them yet */ 710: tw_builtins_add(); 711: tw_aliases_add(); 712: tw_sort_comms(); /* re-build the command path for twenex.c */ 713: } 714: copyn(target, word, MAXNAMLEN); /* so it can match things */ 715: } 716: else { 717: xprintf("\ntcsh internal error: I don't know what I'm looking for!\n"); 718: NeedsRedraw = 1; 719: return (-1); 720: } 721: 722: 723: again: 724: name_length = Strlen(target); 725: showpathn = look_command && is_set(STRl_pathnum); 726: 727: while (1) { 728: if (look_shellvar) { 729: if ((entry = tw_n_shell_var(&v_ptr)) == NULL) 730: if ((entry = tw_n_env_var(&env_ptr)) == NULL) 731: break; 732: } 733: else if (look_file || look_lognames) { 734: if ((entry = getentry(dir_fd, look_lognames)) == NULL) { 735: break; 736: } 737: 738: /* 739: * Don't match . files on null prefix match 740: */ 741: if (name_length == 0 && entry[0] == '.' && 742: !look_lognames && !is_set(STRshowdots)) 743: continue; 744: if (look_command && !look_lognames) { 745: exec_check = 1; 746: dir_ok = 1; 747: } 748: } 749: else if (look_command) { 750: #ifdef NOTDEF /* Not possible */ 751: if (numcommands == 0) { 752: dohash(); 753: } 754: #endif 755: /* searching . added by Andreas Luik <luik@isaak.isa.de> */ 756: 757: if ((next_command < numcommands) && 758: (entry = com_list[next_command]) == NULL) 759: next_command = numcommands; 760: if (next_command >= numcommands) { /* search relative elems */ 761: if (!relatives_in_path) 762: break; /* we don't need to do it */ 763: while ((dir_fd == NULL || 764: (entry = getentry(dir_fd, FALSE)) == NULL) && 765: *pathv) { 766: if (dir_fd != NULL) 767: FREE_DIR(dir_fd); 768: entry = NULL; 769: while (*pathv && pathv[0][0] == '/') 770: pathv++; 771: if (*pathv) { 772: if (pathv[0][0] == '\0' || 773: (pathv[0][0] == '.' && pathv[0][1] == '\0')) { 774: *tilded_dir = '\0'; 775: dir_fd = opendir("."); 776: } 777: else { 778: copyn(tilded_dir, *pathv, FILSIZ); 779: catn(tilded_dir, STRslash, FILSIZ); 780: dir_fd = opendir(short2str(*pathv)); 781: } 782: pathv++; 783: } 784: } 785: if (entry == NULL) 786: break; /* end of PATH */ 787: /* 788: * executability check for other than "." should perhaps be 789: * conditional on recognize_only_executables? 790: */ 791: exec_check = 1; 792: dir_ok = 0; 793: } 794: else 795: next_command++; 796: } 797: 798: if (command == SPELL) { /* correct the spelling of the last bit */ 799: if (name_length == 0) {/* zero-length word can't be misspelled */ 800: extended_name[0] = '\0';/* (not trying is important for ~) */ 801: d = 0; 802: break; 803: } 804: nd = spdist(entry, target); /* test the entry against original */ 805: if (nd <= d && nd != 4) { 806: if (exec_check && !executable(tilded_dir, entry, dir_ok)) 807: continue; 808: (void) Strcpy(extended_name, entry); 809: d = nd; 810: if (d == 0) /* if found it exactly */ 811: break; 812: } 813: else if (nd == 4) { 814: if (spdir(extended_name, tilded_dir, entry, target)) { 815: if (exec_check && 816: !executable(tilded_dir, extended_name, dir_ok)) 817: continue; 818: d = 0; 819: break; 820: } 821: } 822: } 823: else if (command == LIST) { /* LIST command */ 824: register int length; 825: register long i; 826: register Char **ni, **p2; 827: 828: if (!is_prefix(target, entry)) 829: continue; 830: if (exec_check && !executable(tilded_dir, entry, dir_ok)) 831: continue; 832: 833: if (items == NULL || maxitems == 0) { 834: items = (Char **) xmalloc((size_t) (sizeof(items[0]) * 835: (ITEMS_START + 1))); 836: maxitems = ITEMS_START; 837: for (i = 0, p2 = items; i < maxitems; i++) 838: *p2++ = NULL; 839: } 840: else if (numitems >= maxitems) { 841: ni = (Char **) xrealloc((ptr_t) items, (size_t) 842: (sizeof(items[0])) * (maxitems + ITEMS_INCR)); 843: items = ni; 844: maxitems += ITEMS_INCR; 845: } 846: 847: 848: length = Strlen(entry) + 1; 849: if (showpathn) 850: length += Strlen(dirflag); 851: if (!look_lognames && !look_shellvar) 852: length++; 853: 854: /* safety check */ 855: items[numitems] = (Char *) xmalloc((size_t) (length * sizeof(Char))); 856: 857: copyn(items[numitems], entry, MAXNAMLEN); 858: 859: if (!look_lognames && !look_shellvar 860: && !(look_command && !look_file)) { 861: Char typestr[2]; 862: 863: typestr[0] = filetype(tilded_dir, entry); 864: typestr[1] = '\0'; 865: catn(items[numitems], typestr, MAXNAMLEN); 866: } 867: 868: if (showpathn) 869: catn(items[numitems], dirflag, MAXNAMLEN); 870: numitems++; 871: } 872: else { /* RECOGNIZE command */ 873: if (!is_prefix(target, entry)) 874: continue; 875: if (exec_check && !executable(tilded_dir, entry, dir_ok)) 876: continue; 877: 878: if (ignoring && ignored(entry)) { 879: nignored++; 880: continue; 881: } 882: if (is_set(STRrecexact)) { 883: if (StrQcmp(target, entry) == 0) { /* EXACT match */ 884: copyn(extended_name, entry, MAXNAMLEN); 885: numitems = 1; /* fake into expanding */ 886: non_unique_match = TRUE; 887: break; 888: } 889: } 890: if (recognize(extended_name, entry, name_length, ++numitems)) 891: break; 892: } 893: } 894: 895: if (ignoring && numitems == 0 && nignored > 0) { 896: ignoring = 0; 897: nignored = 0; 898: if (look_lognames) 899: (void) setpwent(); 900: else 901: rewinddir(dir_fd); 902: goto again; 903: } 904: if (look_lognames) { 905: #ifdef YPBUGS 906: fix_yp_bugs(); 907: #endif /* YPBUGS */ 908: (void) endpwent(); 909: } 910: else if (look_file || look_shellvar || 911: (look_command && relatives_in_path)) { 912: if (dir_fd != NULL) 913: FREE_DIR(dir_fd); 914: } 915: 916: if (command == RECOGNIZE) { 917: if (numitems > 0) { 918: if (look_lognames) 919: copyn(word, STRtilde, 1); 920: else if (look_shellvar) { 921: Char *ptr = Strrchr(word, '$'); 922: 923: *++ptr = '\0'; /* Delete after the dollar */ 924: } 925: else if (look_file) 926: copyn(word, dir, max_word_length); /* put back dir part */ 927: else 928: word[0] = '\0'; 929: catn(word, extended_name, max_word_length); /* add extended name */ 930: if (is_set(STRaddsuffix)) { 931: if (numitems == 1) { 932: if (look_lognames) { /* add / */ 933: catn(word, STRslash, max_word_length); 934: } 935: else if (look_shellvar) { 936: struct varent *vp = adrof(extended_name); 937: Char *stp; 938: 939: /* 940: * Don't consider array variables or empty variables 941: */ 942: if (vp) { 943: if (!(stp = vp->vec[0]) || vp->vec[0][0] == '\0' || 944: vp->vec[1]) { 945: catn(word, STRspace, max_word_length); 946: stp = NULL; 947: } 948: else 949: stp = vp->vec[0]; 950: } 951: else if ((stp = Getenv(extended_name)) == NULL) 952: catn(word, STRspace, max_word_length); 953: if (stp != NULL) { 954: *--target = '\0'; 955: (void) Strcat(tilded_dir, name); 956: if (isadirectory(tilded_dir, stp)) 957: catn(word, STRslash, max_word_length); 958: else 959: catn(word, STRspace, max_word_length); 960: } 961: } 962: else if (look_file) { 963: if (isadirectory(tilded_dir, extended_name)) { 964: catn(word, STRslash, max_word_length); 965: } 966: else { 967: catn(word, STRspace, max_word_length); 968: } 969: } 970: else { /* prob. looking for a command */ 971: catn(word, STRspace, max_word_length); 972: } 973: } 974: } 975: } 976: return (numitems); /* at the end */ 977: } 978: else if (command == LIST) { 979: register int max_items = 0; 980: register Char *cp; 981: 982: if (cp = value(STRl_max)) { 983: while (*cp) { 984: if (!Isdigit(*cp)) { 985: max_items = 0; 986: break; 987: } 988: max_items = max_items * 10 + *cp++ - '0'; 989: } 990: } 991: 992: if ((max_items > 0) && (numitems > max_items) && list_max) { 993: char tc; 994: 995: xprintf("There are %d items, list them anyway? [n/y] ", numitems); 996: flush(); 997: /* We should be in Rawmode here, so no \n to catch */ 998: (void) read(SHIN, &tc, 1); 999: xprintf("%c\r\n", tc); /* echo the char, do a newline */ 1000: if ((tc != 'y') && (tc != 'Y')) 1001: goto done_list; 1002: } 1003: qsort((ptr_t) items, (size_t) numitems, sizeof(items[1]), 1004: (int (*) __P((const void *, const void *))) fcompare); 1005: 1006: print_by_column(STRNULL, items, numitems, TRUE); 1007: 1008: done_list: 1009: if (items != NULL) 1010: FREE_ITEMS(items, numitems); 1011: return (numitems); 1012: } 1013: else if (command == SPELL) { 1014: if (look_lognames) 1015: copyn(word, STRtilde, 1); 1016: else if (look_shellvar) { 1017: Char *ptr = Strrchr(word, '$'); 1018: 1019: *++ptr = '\0'; /* Delete after the dollar */ 1020: } 1021: else if (look_file) 1022: copyn(word, dir, max_word_length); /* put back dir part */ 1023: else 1024: word[0] = '\0'; 1025: catn(word, extended_name, max_word_length); /* add extended name */ 1026: return (d); 1027: } 1028: else { 1029: xprintf("Bad tw_command\n"); 1030: return (0); 1031: } 1032: } 1033: 1034: 1035: 1036: /* stuff for general command line hacking */ 1037: 1038: /* 1039: * Strip next directory from path; return ptr to next unstripped directory. 1040: */ 1041: 1042: #ifdef notdef 1043: Char * extct_dir_from_path(path, dir) 1044: Char *path, dir[]; 1045: { 1046: register Char *d = dir; 1047: 1048: while (*path && (*path == ' ' || *path == ':')) 1049: path++; 1050: while (*path && (*path != ' ' && *path != ':')) 1051: *(d++) = *(path++); 1052: while (*path && (*path == ' ' || *path == ':')) 1053: path++; 1054: 1055: ++dirctr; 1056: if (*dir == '.') 1057: (void) Strcpy(dirflag, STRdotsp); 1058: else { 1059: dirflag[0] = ' '; 1060: if (dirctr <= 9) { 1061: dirflag[1] = '0' + dirctr; 1062: dirflag[2] = '\0'; 1063: } 1064: else { 1065: dirflag[1] = '0' + dirctr / 10; 1066: dirflag[2] = '0' + dirctr % 10; 1067: dirflag[3] = '\0'; 1068: } 1069: } 1070: *(d++) = '/'; 1071: *d = 0; 1072: 1073: return path; 1074: } 1075: 1076: #endif 1077: 1078: 1079: static void 1080: free_items(items, numitems) 1081: register Char **items; 1082: register int numitems; 1083: { 1084: register int i; 1085: 1086: /* for (i = 0; items[i] != (Char *)NULL; i++) */ 1087: for (i = 0; i < numitems; i++) 1088: xfree((ptr_t) items[i]); 1089: xfree((ptr_t) items); 1090: maxitems = 0; 1091: } 1092: 1093: 1094: /* 1095: * parse full path in file into 2 parts: directory and file names 1096: * Should leave final slash (/) at end of dir. 1097: */ 1098: static void 1099: extract_dir_and_name(path, dir, name) 1100: Char *path, *dir, *name; 1101: { 1102: register Char *p; 1103: 1104: p = Strrchr(path, '/'); 1105: if (p == NULL) { 1106: copyn(name, path, MAXNAMLEN); 1107: dir[0] = '\0'; 1108: } 1109: else { 1110: p++; 1111: copyn(name, p, MAXNAMLEN); 1112: copyn(dir, path, p - path); 1113: } 1114: } 1115: 1116: static Char * 1117: getentry(dir_fd, look_lognames) 1118: DIR *dir_fd; 1119: int look_lognames; 1120: { 1121: register struct passwd *pw; 1122: static Char retname[MAXPATHLEN]; 1123: 1124: register struct dirent *dirp; 1125: 1126: if (look_lognames) { /* Is it login names we want? */ 1127: 1128: pw = getpwent(); 1129: 1130: if (pw == NULL) { 1131: #ifdef YPBUGS 1132: fix_yp_bugs(); 1133: #endif 1134: return (NULL); 1135: } 1136: (void) Strcpy(retname, str2short(pw->pw_name)); 1137: return (retname); 1138: } 1139: else { /* It's a dir entry we want */ 1140: if (dirp = readdir(dir_fd)) { 1141: (void) Strcpy(retname, str2short(dirp->d_name)); 1142: return (retname); 1143: } 1144: return (NULL); 1145: } 1146: } 1147: 1148: /* 1149: * expand "/$old1/$old2/old3/" 1150: * to "/value_of_old1/value_of_old2/old3/" 1151: */ 1152: static Char * 1153: dollar(new, old) 1154: Char *new, *old; 1155: { 1156: Char *var, *val, *p, save; 1157: int space; 1158: 1159: for (space = FILSIZ, p = new; *old && space > 0;) 1160: if (*old != '$') { 1161: *p++ = *old++; 1162: space--; 1163: } 1164: else { 1165: struct varent *vp; 1166: 1167: /* found a variable, expand it */ 1168: for (var = ++old; alnum(*old); old++); 1169: save = *old; 1170: *old = '\0'; 1171: vp = adrof(var); 1172: val = (!vp) ? Getenv(var) : NULL; 1173: *old = save; 1174: /* 1175: * Don't expand array variables 1176: */ 1177: if (vp) { 1178: if (!vp->vec[0] || vp->vec[1]) { 1179: *new = '\0'; 1180: return (NULL); 1181: } 1182: else 1183: val = vp->vec[0]; 1184: } 1185: else if (!val) { 1186: *new = '\0'; 1187: return (NULL); 1188: } 1189: for (; space > 0 && *val; space--) 1190: *p++ = *val++; 1191: } 1192: *p = '\0'; 1193: return (new); 1194: } 1195: 1196: /* 1197: * expand "old" file name with possible tilde usage 1198: * ~person/mumble 1199: * expands to 1200: * home_directory_of_person/mumble 1201: * into string "new". 1202: */ 1203: 1204: static Char * 1205: tilde(new, old) 1206: Char *new, *old; 1207: { 1208: register Char *o, *p; 1209: 1210: if ((old[0] != '~') && (old[0] != '=')) { 1211: (void) Strcpy(new, old); 1212: return (new); 1213: } 1214: 1215: new[0] = '\0'; 1216: for (p = new, o = &old[1]; *o && *o != '/'; *p++ = *o++); 1217: *p = '\0'; 1218: 1219: if (old[0] == '~') { 1220: if (gethdir(new)) 1221: return (NULL); 1222: } 1223: else { /* '=' stack expansion */ 1224: if (!Isdigit(old[1]) && old[1] != '-') 1225: return (NULL); 1226: if (!getstakd(new, (old[1] == '-') ? -1 : old[1] - '0')) 1227: return (NULL); 1228: } 1229: (void) Strcat(new, o); 1230: return (new); 1231: } 1232: 1233: static Char 1234: filetype(dir, file) /* symbology from 4.3 ls command */ 1235: Char *dir, *file; 1236: { 1237: if (dir) { 1238: Char path[512]; 1239: char *ptr; 1240: struct stat statb; 1241: 1242: (void) Strcpy(path, dir); 1243: catn(path, file, sizeof(path) / sizeof(Char)); 1244: 1245: if (lstat(ptr = short2str(path), &statb) != -1) 1246: /* see above #define of lstat */ 1247: { 1248: #ifdef S_ISLNK 1249: if (S_ISLNK(statb.st_mode)) { /* Symbolic link */ 1250: if (adrof(STRl_links)) { 1251: if (stat(ptr, &statb) == -1) 1252: return ('&'); 1253: else if (S_ISDIR(statb.st_mode)) 1254: return ('>'); 1255: else 1256: return ('@'); 1257: } 1258: else 1259: return ('@'); 1260: } 1261: #endif 1262: #ifdef S_ISSOCK 1263: if (S_ISSOCK(statb.st_mode)) /* Socket */ 1264: return ('='); 1265: #endif 1266: #ifdef S_ISFIFO 1267: if (S_ISFIFO(statb.st_mode)) /* Named Pipe */ 1268: return ('|'); 1269: #endif 1270: #ifdef S_ISHIDDEN 1271: if (S_ISHIDDEN(statb.st_mode)) /* Hidden Directory [aix] */ 1272: return ('+'); 1273: #endif 1274: #ifdef S_ISCDF 1275: if (S_ISCDF(statb.st_mode)) /* Context Dependent Files [hpux] */ 1276: return ('+'); 1277: #endif 1278: #ifdef S_ISNWK 1279: if (S_ISNWK(statb.st_mode)) /* Network Special [hpux] */ 1280: return (':'); 1281: #endif 1282: if (S_ISCHR(statb.st_mode)) /* char device */ 1283: return ('%'); 1284: if (S_ISBLK(statb.st_mode)) /* block device */ 1285: return ('#'); 1286: if (S_ISDIR(statb.st_mode)) /* normal Directory */ 1287: return ('/'); 1288: if (statb.st_mode & 0111) 1289: return ('*'); 1290: } 1291: } 1292: return (' '); 1293: } 1294: 1295: static int 1296: isadirectory(dir, file) /* return 1 if dir/file is a directory */ 1297: Char *dir, *file; /* uses stat rather than lstat to get dest. */ 1298: { 1299: if (dir) { 1300: Char path[MAXPATHLEN]; 1301: struct stat statb; 1302: 1303: (void) Strcpy(path, dir); 1304: catn(path, file, sizeof(path) / sizeof(Char)); 1305: if (stat(short2str(path), &statb) >= 0) { /* resolve through 1306: * symlink */ 1307: #ifdef S_ISSOCK 1308: if (S_ISSOCK(statb.st_mode)) /* Socket */ 1309: return 0; 1310: #endif 1311: #ifdef S_ISFIFO 1312: if (S_ISFIFO(statb.st_mode)) /* Named Pipe */ 1313: return 0; 1314: #endif 1315: if (S_ISDIR(statb.st_mode)) /* normal Directory */ 1316: return 1; 1317: } 1318: } 1319: return 0; 1320: } 1321: 1322: /* 1323: * Print sorted down columns 1324: */ 1325: void 1326: print_by_column(dir, items, count, no_file_suffix) 1327: register Char *dir, *items[]; 1328: int count, no_file_suffix; 1329: { 1330: register int i, r, c, w, maxwidth = 0, columns, rows; 1331: extern int Tty_raw_mode; 1332: 1333: lbuffed = 0; /* turn off line buffering */ 1334: 1335: for (i = 0; i < count; i++) /* find widest string */ 1336: maxwidth = max(maxwidth, Strlen(items[i])); 1337: 1338: maxwidth += no_file_suffix ? 1 : 2; /* for the file tag and space */ 1339: columns = (TermH + 1) / maxwidth; /* PWP: terminal size change */ 1340: if (!columns) 1341: columns = 1; 1342: rows = (count + (columns - 1)) / columns; 1343: 1344: for (r = 0; r < rows; r++) { 1345: for (c = 0; c < columns; c++) { 1346: i = c * rows + r; 1347: 1348: if (i < count) { 1349: w = Strlen(items[i]); 1350: 1351: if (no_file_suffix) { 1352: /* Print the command name */ 1353: xprintf("%s", short2str(items[i])); 1354: } 1355: else { 1356: /* Print filename followed by '/' or '*' or ' ' */ 1357: xprintf("%s%c", short2str(items[i]), 1358: filetype(dir, items[i])); 1359: w++; 1360: } 1361: 1362: if (c < (columns - 1)) /* Not last column? */ 1363: for (; w < maxwidth; w++) 1364: xputchar(' '); 1365: } 1366: } 1367: if (Tty_raw_mode) 1368: xputchar('\r'); 1369: xputchar('\n'); 1370: } 1371: 1372: lbuffed = 1; /* turn back on line buffering */ 1373: flush(); 1374: } 1375: 1376: 1377: int 1378: StrQcmp(str1, str2) 1379: register Char *str1, *str2; 1380: { 1381: for (; *str1 && (*str1 & TRIM) == (*str2 & TRIM); str1++, str2++); 1382: /* 1383: * The following case analysis is necessary so that characters which look 1384: * negative collate low against normal characters but high against the 1385: * end-of-string NUL. 1386: */ 1387: if (*str1 == '\0' && *str2 == '\0') 1388: return (0); 1389: else if (*str1 == '\0') 1390: return (-1); 1391: else if (*str2 == '\0') 1392: return (1); 1393: else 1394: return ((*str1 & TRIM) - (*str2 & TRIM)); 1395: } 1396: 1397: /* 1398: * For qsort() 1399: */ 1400: int 1401: fcompare(file1, file2) 1402: Char **file1, **file2; 1403: { 1404: #if defined(NLS) && !defined(NOSTRCOLL) 1405: char buf[2048]; 1406: 1407: (void) strcpy(buf, short2str(*file1)); 1408: return ((int) strcoll(buf, short2str(*file2))); 1409: #else 1410: return (StrQcmp(*file1, *file2)); 1411: #endif 1412: } 1413: 1414: /* 1415: * Concatenate src onto tail of des. 1416: * Des is a string whose maximum length is count. 1417: * Always null terminate. 1418: */ 1419: 1420: void 1421: catn(des, src, count) 1422: register Char *des, *src; 1423: register count; 1424: { 1425: while (--count >= 0 && *des) 1426: des++; 1427: while (--count >= 0) 1428: if ((*des++ = *src++) == 0) 1429: return; 1430: *des = '\0'; 1431: } 1432: 1433: /* 1434: * like strncpy but always leave room for trailing \0 1435: * and always null terminate. 1436: */ 1437: void 1438: copyn(des, src, count) 1439: register Char *des, *src; 1440: register count; 1441: { 1442: while (--count >= 0) 1443: if ((*des++ = *src++) == 0) 1444: return; 1445: *des = '\0'; 1446: } 1447: 1448: static void 1449: tw_get_comm_list() 1450: { /* stolen from sh.exec.c dohash() */ 1451: register DIR *dirp; 1452: register struct dirent *dp; 1453: register Char *dir; 1454: register Char **pv; 1455: struct varent *v = adrof(STRpath); 1456: 1457: relatives_in_path = 0; /* set to false until we know better */ 1458: tw_clear_comm_list(); 1459: if (v == 0) /* if no path */ 1460: return; 1461: 1462: if (adrof(STRrecognize_only_executables)) { 1463: for (pv = v->vec; *pv; pv++) { 1464: if (pv[0][0] != '/') { 1465: relatives_in_path = 1; 1466: continue; 1467: } 1468: dirp = opendir(short2str(*pv)); 1469: if (dirp == NULL) 1470: continue; 1471: 1472: dir = Strspl(*pv, STRslash); 1473: while ((dp = readdir(dirp)) != NULL) { 1474: /* the call to executable() may make this a bit slow */ 1475: if (dp->d_ino != 0 && 1476: executable(dir, str2short(dp->d_name), 0)) 1477: tw_comm_name_add(str2short(dp->d_name)); 1478: } 1479: (void) closedir(dirp); 1480: xfree((ptr_t) dir); 1481: } 1482: } 1483: else { 1484: for (pv = v->vec; *pv; pv++) { 1485: if (pv[0][0] != '/') { 1486: relatives_in_path = 1; 1487: continue; 1488: } 1489: dirp = opendir(short2str(*pv)); 1490: if (dirp == NULL) 1491: continue; 1492: 1493: while ((dp = readdir(dirp)) != NULL) { 1494: if (dp->d_ino != 0) 1495: tw_comm_name_add(str2short(dp->d_name)); 1496: } 1497: (void) closedir(dirp); 1498: } 1499: } 1500: }