# 2.11BSD#431 - /usr/src/bin/tcsh/tw.parse.c

##### Functions

    StrQcmp           X
    catn              X
    copyn             X
    extct_dir_from_pathX
    fcompare          X
    print_by_column   X
    starting_a_commandX
    t_search          X
    tenematch         X

##### Variables

    com_list          X
    dirctr            X
    dirflag           X
    have_sorted       X
    non_unique_match  X
    numcommands       X

##### Macros

    EVEN              X
    isaset            X

       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

StrQcmp defined in line 1377; used 7 times

- in line 883, 1410
- in /usr/src/bin/tcsh/tw.init.c line 268
- in /usr/src/bin/tcsh/tw.parse.c line 883, 1410
- in /usr/src/bin/tcsh/tw.spell.c line 72, 104

catn defined in line 1420; used 34 times

- in line 779, 865-869(2), 929-933(2), 945, 952-971(6), 1025, 1243, 1304
- in /usr/src/bin/tcsh/tw.help.c line 118-123(3)
- in /usr/src/bin/tcsh/tw.parse.c line 779, 865-869(2), 929-933(2), 945, 952-971(6), 1025, 1243, 1304
- in /usr/src/bin/tcsh/tw.spell.c line 159

copyn defined in line 1437; used 30 times

- in line 559, 704, 714, 778, 857, 884, 919, 926, 1015, 1022, 1106-1112(3)
- in /usr/src/bin/tcsh/tw.help.c line 77, 117
- in /usr/src/bin/tcsh/tw.init.c line 142
- in /usr/src/bin/tcsh/tw.parse.c line 559, 704, 714, 778, 857, 884, 919, 926, 1015, 1022, 1106-1112(3)
- in /usr/src/bin/tcsh/tw.spell.c line 73

dollar defined in line 1152; used 2 times

- in line 343, 667

extct_dir_from_path defined in line 1043; never used
extract_dir_and_name defined in line 1098; used 1 times

- in line 640

fcompare defined in line 1400; used 3 times

- in line 1004
- in /usr/src/bin/tcsh/tw.init.c line 77
- in /usr/src/bin/tcsh/tw.parse.c line 1004

filetype defined in line 1233; used 2 times

- in line 863, 1358

free_items defined in line 1079; never used
getentry defined in line 1116; used 2 times

- in line 734, 764

ignored defined in line 458; used 1 times

- in line 878

is_prefix defined in line 426; used 2 times

- in line 828, 873

is_suffix defined in line 440; used 1 times

- in line 468

isadirectory defined in line 1295; used 2 times

- in line 956, 963

print_by_column defined in line 1325; used 4 times

- in line 325, 1006
- in /usr/src/bin/tcsh/tw.parse.c line 325, 1006

quote_meta defined in line 401; used 3 times

- in line 287, 331, 346

rcsid defined in line 42; never used
recognize defined in line 553; used 1 times

- in line 890

starting_a_command defined in line 477; used 2 times

- in line 165
- in /usr/src/bin/tcsh/tw.parse.c line 165

t_glob defined in line 366; used 1 times

- in line 323

t_search defined in line 585; used 7 times

- in line 262, 277, 353
- in /usr/src/bin/tcsh/tw.parse.c line 262, 277, 353
- in /usr/src/bin/tcsh/tw.spell.c line 92

tenematch defined in line 110; never used
tilde defined in line 1204; used 1 times

- in line 668

tw_get_comm_list defined in line 1448; used 1 times

- in line 708

#### Defined variables

SearchNoDirErr defined in line 72; used 4 times

- in line 247, 254, 263, 672

com_list defined in line 56; used 1 times

- in line 758

dirctr defined in line 67; used 5 times

- in line 1055-1066(5)

dirflag defined in line 70; used 9 times

- in line 850, 869, 1057-1067(7)

have_sorted defined in line 59; used 4 times

- in line 709
- in /usr/src/bin/tcsh/tw.init.c line 59, 95
- in /usr/src/bin/tcsh/tw.parse.c line 709

maxitems defined in line 55; used 7 times

- in line 833-844(6), 1090

non_unique_match defined in line 64; used 4 times

- in line 638, 886
- in /usr/src/bin/tcsh/tw.parse.c line 638, 886

numcommands defined in line 58; used 22 times

- in line 707, 751, 757-760(3)
- in /usr/src/bin/tcsh/tw.init.c line 60-66(3), 76-80(2), 92-93(2), 113-116(2), 139-143(3)
- in /usr/src/bin/tcsh/tw.parse.c line 707, 751, 757-760(3)

relatives_in_path declared in line 79; defined in line 53; used 5 times

- in line 761, 911, 1457, 1465, 1486

#### Defined macros

EVEN defined in line 475; used 1 times

- in line 498

isaset defined in line 100; used 1 times

- in line 144

Last modified: 1991-08-21 Generated: 2016-12-26 | Generated by src2html V0.67 page hit count: 3148 |