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: }

Defined functions

catn defined in line 1420; used 34 times
dollar defined in line 1152; used 2 times
extct_dir_from_path defined in line 1043; never used
extract_dir_and_name defined in line 1098; used 1 times
fcompare defined in line 1400; used 3 times
filetype defined in line 1233; used 2 times
free_items defined in line 1079; never used
getentry defined in line 1116; used 2 times
ignored defined in line 458; used 1 times
is_prefix defined in line 426; used 2 times
is_suffix defined in line 440; used 1 times
isadirectory defined in line 1295; used 2 times
print_by_column defined in line 1325; used 4 times
quote_meta defined in line 401; used 3 times
rcsid defined in line 42; never used
recognize defined in line 553; used 1 times
starting_a_command defined in line 477; used 2 times
t_glob defined in line 366; used 1 times
t_search defined in line 585; used 7 times
tenematch defined in line 110; never used
tilde defined in line 1204; used 1 times
tw_get_comm_list defined in line 1448; used 1 times

Defined variables

SearchNoDirErr defined in line 72; used 4 times
com_list defined in line 56; used 1 times
dirctr defined in line 67; used 5 times
dirflag defined in line 70; used 9 times
have_sorted defined in line 59; used 4 times
maxitems defined in line 55; used 7 times
non_unique_match defined in line 64; used 4 times
numcommands defined in line 58; used 22 times
relatives_in_path declared in line 79; defined in line 53; used 5 times

Defined macros

EVEN defined in line 475; used 1 times
isaset defined in line 100; used 1 times
Last modified: 1991-08-21
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 7021
Valid CSS Valid XHTML 1.0 Strict