home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / bsd_srcs / bin / csh / file.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-02-05  |  16.5 KB  |  683 lines

  1. /*-
  2.  * Copyright (c) 1980, 1991 The Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer.
  10.  * 2. Redistributions in binary form must reproduce the above copyright
  11.  *    notice, this list of conditions and the following disclaimer in the
  12.  *    documentation and/or other materials provided with the distribution.
  13.  * 3. All advertising materials mentioning features or use of this software
  14.  *    must display the following acknowledgement:
  15.  *    This product includes software developed by the University of
  16.  *    California, Berkeley and its contributors.
  17.  * 4. Neither the name of the University nor the names of its contributors
  18.  *    may be used to endorse or promote products derived from this software
  19.  *    without specific prior written permission.
  20.  *
  21.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  22.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  23.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  24.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  25.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  26.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  27.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  28.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  29.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  30.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31.  * SUCH DAMAGE.
  32.  */
  33.  
  34. #ifndef lint
  35. static char sccsid[] = "@(#)file.c    5.23 (Berkeley) 2/5/92";
  36. #endif /* not lint */
  37.  
  38. #ifdef FILEC
  39.  
  40. #include <sys/param.h>
  41. #include <sys/ioctl.h>
  42. #include <sys/stat.h>
  43. #include <termios.h>
  44. #include <dirent.h>
  45. #include <pwd.h>
  46. #include <stdlib.h>
  47. #include <unistd.h>
  48. #ifndef SHORT_STRINGS
  49. #include <string.h>
  50. #endif /* SHORT_STRINGS */
  51. #if __STDC__
  52. # include <stdarg.h>
  53. #else
  54. # include <varargs.h>
  55. #endif
  56.  
  57. #include "csh.h"
  58. #include "extern.h"
  59.  
  60. /*
  61.  * Tenex style file name recognition, .. and more.
  62.  * History:
  63.  *    Author: Ken Greer, Sept. 1975, CMU.
  64.  *    Finally got around to adding to the Cshell., Ken Greer, Dec. 1981.
  65.  */
  66.  
  67. #define ON    1
  68. #define OFF    0
  69. #ifndef TRUE
  70. #define TRUE 1
  71. #endif
  72. #ifndef FALSE
  73. #define FALSE 0
  74. #endif
  75.  
  76. #define ESC    '\033'
  77.  
  78. typedef enum {
  79.     LIST, RECOGNIZE
  80. }       COMMAND;
  81.  
  82. static void     setup_tty __P((int));
  83. static void     back_to_col_1 __P((void));
  84. static void     pushback __P((Char *));
  85. static void     catn __P((Char *, Char *, int));
  86. static void     copyn __P((Char *, Char *, int));
  87. static Char     filetype __P((Char *, Char *));
  88. static void     print_by_column __P((Char *, Char *[], int));
  89. static Char    *tilde __P((Char *, Char *));
  90. static void     retype __P((void));
  91. static void     beep __P((void));
  92. static void     print_recognized_stuff __P((Char *));
  93. static void     extract_dir_and_name __P((Char *, Char *, Char *));
  94. static Char    *getentry __P((DIR *, int));
  95. static void     free_items __P((Char **));
  96. static int     tsearch __P((Char *, COMMAND, int));
  97. static int     recognize __P((Char *, Char *, int, int));
  98. static int     is_prefix __P((Char *, Char *));
  99. static int     is_suffix __P((Char *, Char *));
  100. static int     ignored __P((Char *));
  101.  
  102. /*
  103.  * Put this here so the binary can be patched with adb to enable file
  104.  * completion by default.  Filec controls completion, nobeep controls
  105.  * ringing the terminal bell on incomplete expansions.
  106.  */
  107. bool    filec = 0;
  108.  
  109. static void
  110. setup_tty(on)
  111.     int     on;
  112. {
  113.     static struct termios tchars;
  114.  
  115.     if (on) {
  116.     (void) tcgetattr(SHIN, &tchars);
  117.     tchars.c_cc[VEOL] = ESC;
  118.     if (tchars.c_lflag & ICANON)
  119.         on = TCSANOW;
  120.     else {
  121.         on = TCSAFLUSH;
  122.         tchars.c_lflag |= ICANON;
  123.     }
  124.         (void) tcsetattr(SHIN, on, &tchars);
  125.     }
  126.     else {
  127.     tchars.c_cc[VEOL] = _POSIX_VDISABLE;
  128.     (void) tcsetattr(SHIN, TCSANOW, &tchars);
  129.     }
  130. }
  131.  
  132. /*
  133.  * Move back to beginning of current line
  134.  */
  135. static void
  136. back_to_col_1()
  137. {
  138.     struct termios tty, tty_normal;
  139.     int     omask;
  140.  
  141.     omask = sigblock(sigmask(SIGINT));
  142.     (void) tcgetattr(SHOUT, &tty);
  143.     tty_normal = tty;
  144.     tty.c_iflag &= ~INLCR;
  145.     tty.c_oflag &= ~ONLCR;
  146.     (void) tcsetattr(SHOUT, TCSANOW, &tty);
  147.     (void) write(SHOUT, "\r", 1);
  148.     (void) tcsetattr(SHOUT, TCSANOW, &tty_normal);
  149.     (void) sigsetmask(omask);
  150. }
  151.  
  152. /*
  153.  * Push string contents back into tty queue
  154.  */
  155. static void
  156. pushback(string)
  157.     Char   *string;
  158. {
  159.     register Char *p;
  160.     struct termios tty, tty_normal;
  161.     int     omask;
  162.     char    c;
  163.  
  164.     omask = sigblock(sigmask(SIGINT));
  165.     (void) tcgetattr(SHOUT, &tty);
  166.     tty_normal = tty;
  167.     tty.c_lflag &= ~(ECHOKE | ECHO | ECHOE | ECHOK | ECHONL | ECHOPRT | ECHOCTL);
  168.     (void) tcsetattr(SHOUT, TCSANOW, &tty);
  169.  
  170.     for (p = string; c = *p; p++)
  171.     (void) ioctl(SHOUT, TIOCSTI, (ioctl_t) & c);
  172.     (void) tcsetattr(SHOUT, TCSANOW, &tty_normal);
  173.     (void) sigsetmask(omask);
  174. }
  175.  
  176. /*
  177.  * Concatenate src onto tail of des.
  178.  * Des is a string whose maximum length is count.
  179.  * Always null terminate.
  180.  */
  181. static void
  182. catn(des, src, count)
  183.     register Char *des, *src;
  184.     register int count;
  185. {
  186.     while (--count >= 0 && *des)
  187.     des++;
  188.     while (--count >= 0)
  189.     if ((*des++ = *src++) == 0)
  190.         return;
  191.     *des = '\0';
  192. }
  193.  
  194. /*
  195.  * Like strncpy but always leave room for trailing \0
  196.  * and always null terminate.
  197.  */
  198. static void
  199. copyn(des, src, count)
  200.     register Char *des, *src;
  201.     register int count;
  202. {
  203.     while (--count >= 0)
  204.     if ((*des++ = *src++) == 0)
  205.         return;
  206.     *des = '\0';
  207. }
  208.  
  209. static  Char
  210. filetype(dir, file)
  211.     Char   *dir, *file;
  212. {
  213.     Char    path[MAXPATHLEN];
  214.     struct stat statb;
  215.  
  216.     catn(Strcpy(path, dir), file, sizeof(path) / sizeof(Char));
  217.     if (lstat(short2str(path), &statb) == 0) {
  218.     switch (statb.st_mode & S_IFMT) {
  219.     case S_IFDIR:
  220.         return ('/');
  221.  
  222.     case S_IFLNK:
  223.         if (stat(short2str(path), &statb) == 0 &&    /* follow it out */
  224.         S_ISDIR(statb.st_mode))
  225.         return ('>');
  226.         else
  227.         return ('@');
  228.  
  229.     case S_IFSOCK:
  230.         return ('=');
  231.  
  232.     default:
  233.         if (statb.st_mode & 0111)
  234.         return ('*');
  235.     }
  236.     }
  237.     return (' ');
  238. }
  239.  
  240. static struct winsize win;
  241.  
  242. /*
  243.  * Print sorted down columns
  244.  */
  245. static void
  246. print_by_column(dir, items, count)
  247.     Char   *dir, *items[];
  248.     int     count;
  249. {
  250.     register int i, rows, r, c, maxwidth = 0, columns;
  251.  
  252.     if (ioctl(SHOUT, TIOCGWINSZ, (ioctl_t) & win) < 0 || win.ws_col == 0)
  253.     win.ws_col = 80;
  254.     for (i = 0; i < count; i++)
  255.     maxwidth = maxwidth > (r = Strlen(items[i])) ? maxwidth : r;
  256.     maxwidth += 2;        /* for the file tag and space */
  257.     columns = win.ws_col / maxwidth;
  258.     if (columns == 0)
  259.     columns = 1;
  260.     rows = (count + (columns - 1)) / columns;
  261.     for (r = 0; r < rows; r++) {
  262.     for (c = 0; c < columns; c++) {
  263.         i = c * rows + r;
  264.         if (i < count) {
  265.         register int w;
  266.  
  267.         (void) fprintf(cshout, "%s", vis_str(items[i]));
  268.         (void) fputc(dir ? filetype(dir, items[i]) : ' ', cshout);
  269.         if (c < columns - 1) {    /* last column? */
  270.             w = Strlen(items[i]) + 1;
  271.             for (; w < maxwidth; w++)
  272.             (void) fputc(' ', cshout);
  273.         }
  274.         }
  275.     }
  276.     (void) fputc('\r', cshout);
  277.     (void) fputc('\n', cshout);
  278.     }
  279. }
  280.  
  281. /*
  282.  * Expand file name with possible tilde usage
  283.  *    ~person/mumble
  284.  * expands to
  285.  *    home_directory_of_person/mumble
  286.  */
  287. static Char *
  288. tilde(new, old)
  289.     Char   *new, *old;
  290. {
  291.     register Char *o, *p;
  292.     register struct passwd *pw;
  293.     static Char person[40];
  294.  
  295.     if (old[0] != '~')
  296.     return (Strcpy(new, old));
  297.  
  298.     for (p = person, o = &old[1]; *o && *o != '/'; *p++ = *o++)
  299.     continue;
  300.     *p = '\0';
  301.     if (person[0] == '\0')
  302.     (void) Strcpy(new, value(STRhome));
  303.     else {
  304.     pw = getpwnam(short2str(person));
  305.     if (pw == NULL)
  306.         return (NULL);
  307.     (void) Strcpy(new, str2short(pw->pw_dir));
  308.     }
  309.     (void) Strcat(new, o);
  310.     return (new);
  311. }
  312.  
  313. /*
  314.  * Cause pending line to be printed
  315.  */
  316. static void
  317. retype()
  318. {
  319.     struct termios tty;
  320.  
  321.     (void) tcgetattr(SHOUT, &tty);
  322.     tty.c_lflag |= PENDIN;
  323.     (void) tcsetattr(SHOUT, TCSANOW, &tty);
  324. }
  325.  
  326. static void
  327. beep()
  328. {
  329.     if (adrof(STRnobeep) == 0)
  330.     (void) write(SHOUT, "\007", 1);
  331. }
  332.  
  333. /*
  334.  * Erase that silly ^[ and
  335.  * print the recognized part of the string
  336.  */
  337. static void
  338. print_recognized_stuff(recognized_part)
  339.     Char   *recognized_part;
  340. {
  341.     /* An optimized erasing of that silly ^[ */
  342.     (void) fputc('\b', cshout);
  343.     (void) fputc('\b', cshout);
  344.     switch (Strlen(recognized_part)) {
  345.  
  346.     case 0:            /* erase two Characters: ^[ */
  347.     (void) fputc(' ', cshout);
  348.     (void) fputc(' ', cshout);
  349.     (void) fputc('\b', cshout);
  350.     (void) fputc('\b', cshout);
  351.     break;
  352.  
  353.     case 1:            /* overstrike the ^, erase the [ */
  354.     (void) fprintf(cshout, "%s", vis_str(recognized_part));
  355.     (void) fputc(' ', cshout);
  356.     (void) fputc('\b', cshout);
  357.     break;
  358.  
  359.     default:            /* overstrike both Characters ^[ */
  360.     (void) fprintf(cshout, "%s", vis_str(recognized_part));
  361.     break;
  362.     }
  363.     (void) fflush(cshout);
  364. }
  365.  
  366. /*
  367.  * Parse full path in file into 2 parts: directory and file names
  368.  * Should leave final slash (/) at end of dir.
  369.  */
  370. static void
  371. extract_dir_and_name(path, dir, name)
  372.     Char   *path, *dir, *name;
  373. {
  374.     register Char *p;
  375.  
  376.     p = Strrchr(path, '/');
  377.     if (p == NULL) {
  378.     copyn(name, path, MAXNAMLEN);
  379.     dir[0] = '\0';
  380.     }
  381.     else {
  382.     copyn(name, ++p, MAXNAMLEN);
  383.     copyn(dir, path, p - path);
  384.     }
  385. }
  386.  
  387. static Char *
  388. getentry(dir_fd, looking_for_lognames)
  389.     DIR    *dir_fd;
  390.     int     looking_for_lognames;
  391. {
  392.     register struct passwd *pw;
  393.     register struct dirent *dirp;
  394.  
  395.     if (looking_for_lognames) {
  396.     if ((pw = getpwent()) == NULL)
  397.         return (NULL);
  398.     return (str2short(pw->pw_name));
  399.     }
  400.     if (dirp = readdir(dir_fd))
  401.     return (str2short(dirp->d_name));
  402.     return (NULL);
  403. }
  404.  
  405. static void
  406. free_items(items)
  407.     register Char **items;
  408. {
  409.     register int i;
  410.  
  411.     for (i = 0; items[i]; i++)
  412.     xfree((ptr_t) items[i]);
  413.     xfree((ptr_t) items);
  414. }
  415.  
  416. #define FREE_ITEMS(items) { \
  417.     int omask;\
  418. \
  419.     omask = sigblock(sigmask(SIGINT));\
  420.     free_items(items);\
  421.     items = NULL;\
  422.     (void) sigsetmask(omask);\
  423. }
  424.  
  425. /*
  426.  * Perform a RECOGNIZE or LIST command on string "word".
  427.  */
  428. static int
  429. tsearch(word, command, max_word_length)
  430.     Char   *word;
  431.     COMMAND command;
  432.     int     max_word_length;
  433. {
  434.     static Char **items = NULL;
  435.     register DIR *dir_fd;
  436.     register numitems = 0, ignoring = TRUE, nignored = 0;
  437.     register name_length, looking_for_lognames;
  438.     Char    tilded_dir[MAXPATHLEN + 1], dir[MAXPATHLEN + 1];
  439.     Char    name[MAXNAMLEN + 1], extended_name[MAXNAMLEN + 1];
  440.     Char   *entry;
  441.  
  442. #define MAXITEMS 1024
  443.  
  444.     if (items != NULL)
  445.     FREE_ITEMS(items);
  446.  
  447.     looking_for_lognames = (*word == '~') && (Strchr(word, '/') == NULL);
  448.     if (looking_for_lognames) {
  449.     (void) setpwent();
  450.     copyn(name, &word[1], MAXNAMLEN);    /* name sans ~ */
  451.     dir_fd = NULL;
  452.     }
  453.     else {
  454.     extract_dir_and_name(word, dir, name);
  455.     if (tilde(tilded_dir, dir) == 0)
  456.         return (0);
  457.     dir_fd = opendir(*tilded_dir ? short2str(tilded_dir) : ".");
  458.     if (dir_fd == NULL)
  459.         return (0);
  460.     }
  461.  
  462. again:                /* search for matches */
  463.     name_length = Strlen(name);
  464.     for (numitems = 0; entry = getentry(dir_fd, looking_for_lognames);) {
  465.     if (!is_prefix(name, entry))
  466.         continue;
  467.     /* Don't match . files on null prefix match */
  468.     if (name_length == 0 && entry[0] == '.' &&
  469.         !looking_for_lognames)
  470.         continue;
  471.     if (command == LIST) {
  472.         if (numitems >= MAXITEMS) {
  473.         (void) fprintf(csherr, "\nYikes!! Too many %s!!\n",
  474.                    looking_for_lognames ?
  475.                    "names in password file" : "files");
  476.         break;
  477.         }
  478.         if (items == NULL)
  479.         items = (Char **) xcalloc(sizeof(items[0]), MAXITEMS);
  480.         items[numitems] = (Char *) xmalloc((size_t) (Strlen(entry) + 1) *
  481.                            sizeof(Char));
  482.         copyn(items[numitems], entry, MAXNAMLEN);
  483.         numitems++;
  484.     }
  485.     else {            /* RECOGNIZE command */
  486.         if (ignoring && ignored(entry))
  487.         nignored++;
  488.         else if (recognize(extended_name,
  489.                    entry, name_length, ++numitems))
  490.         break;
  491.     }
  492.     }
  493.     if (ignoring && numitems == 0 && nignored > 0) {
  494.     ignoring = FALSE;
  495.     nignored = 0;
  496.     if (looking_for_lognames)
  497.         (void) setpwent();
  498.     else
  499.         rewinddir(dir_fd);
  500.     goto again;
  501.     }
  502.  
  503.     if (looking_for_lognames)
  504.     (void) endpwent();
  505.     else
  506.     (void) closedir(dir_fd);
  507.     if (numitems == 0)
  508.     return (0);
  509.     if (command == RECOGNIZE) {
  510.     if (looking_for_lognames)
  511.         copyn(word, STRtilde, 1);
  512.     else
  513.         /* put back dir part */
  514.         copyn(word, dir, max_word_length);
  515.     /* add extended name */
  516.     catn(word, extended_name, max_word_length);
  517.     return (numitems);
  518.     }
  519.     else {            /* LIST */
  520.     qsort((ptr_t) items, numitems, sizeof(items[0]), 
  521.         (int (*) __P((const void *, const void *))) sortscmp);
  522.     print_by_column(looking_for_lognames ? NULL : tilded_dir,
  523.             items, numitems);
  524.     if (items != NULL)
  525.         FREE_ITEMS(items);
  526.     }
  527.     return (0);
  528. }
  529.  
  530. /*
  531.  * Object: extend what user typed up to an ambiguity.
  532.  * Algorithm:
  533.  * On first match, copy full entry (assume it'll be the only match)
  534.  * On subsequent matches, shorten extended_name to the first
  535.  * Character mismatch between extended_name and entry.
  536.  * If we shorten it back to the prefix length, stop searching.
  537.  */
  538. static int
  539. recognize(extended_name, entry, name_length, numitems)
  540.     Char   *extended_name, *entry;
  541.     int     name_length, numitems;
  542. {
  543.     if (numitems == 1)        /* 1st match */
  544.     copyn(extended_name, entry, MAXNAMLEN);
  545.     else {            /* 2nd & subsequent matches */
  546.     register Char *x, *ent;
  547.     register int len = 0;
  548.  
  549.     x = extended_name;
  550.     for (ent = entry; *x && *x == *ent++; x++, len++)
  551.         continue;
  552.     *x = '\0';        /* Shorten at 1st Char diff */
  553.     if (len == name_length)    /* Ambiguous to prefix? */
  554.         return (-1);    /* So stop now and save time */
  555.     }
  556.     return (0);
  557. }
  558.  
  559. /*
  560.  * Return true if check matches initial Chars in template.
  561.  * This differs from PWB imatch in that if check is null
  562.  * it matches anything.
  563.  */
  564. static int
  565. is_prefix(check, template)
  566.     register Char *check, *template;
  567. {
  568.     do
  569.     if (*check == 0)
  570.         return (TRUE);
  571.     while (*check++ == *template++);
  572.     return (FALSE);
  573. }
  574.  
  575. /*
  576.  *  Return true if the Chars in template appear at the
  577.  *  end of check, I.e., are it's suffix.
  578.  */
  579. static int
  580. is_suffix(check, template)
  581.     Char   *check, *template;
  582. {
  583.     register Char *c, *t;
  584.  
  585.     for (c = check; *c++;)
  586.     continue;
  587.     for (t = template; *t++;)
  588.     continue;
  589.     for (;;) {
  590.     if (t == template)
  591.         return 1;
  592.     if (c == check || *--t != *--c)
  593.         return 0;
  594.     }
  595. }
  596.  
  597. int
  598. tenex(inputline, inputline_size)
  599.     Char   *inputline;
  600.     int     inputline_size;
  601. {
  602.     register int numitems, num_read;
  603.     char    tinputline[BUFSIZ];
  604.  
  605.  
  606.     setup_tty(ON);
  607.  
  608.     while ((num_read = read(SHIN, tinputline, BUFSIZ)) > 0) {
  609.     int     i;
  610.     static Char delims[] = {' ', '\'', '"', '\t', ';', '&', '<',
  611.     '>', '(', ')', '|', '^', '%', '\0'};
  612.     register Char *str_end, *word_start, last_Char, should_retype;
  613.     register int space_left;
  614.     COMMAND command;
  615.  
  616.     for (i = 0; i < num_read; i++)
  617.         inputline[i] = (unsigned char) tinputline[i];
  618.     last_Char = inputline[num_read - 1] & ASCII;
  619.  
  620.     if (last_Char == '\n' || num_read == inputline_size)
  621.         break;
  622.     command = (last_Char == ESC) ? RECOGNIZE : LIST;
  623.     if (command == LIST)
  624.         (void) fputc('\n', cshout);
  625.     str_end = &inputline[num_read];
  626.     if (last_Char == ESC)
  627.         --str_end;        /* wipeout trailing cmd Char */
  628.     *str_end = '\0';
  629.     /*
  630.      * Find LAST occurence of a delimiter in the inputline. The word start
  631.      * is one Character past it.
  632.      */
  633.     for (word_start = str_end; word_start > inputline; --word_start)
  634.         if (Strchr(delims, word_start[-1]))
  635.         break;
  636.     space_left = inputline_size - (word_start - inputline) - 1;
  637.     numitems = tsearch(word_start, command, space_left);
  638.  
  639.     if (command == RECOGNIZE) {
  640.         /* print from str_end on */
  641.         print_recognized_stuff(str_end);
  642.         if (numitems != 1)    /* Beep = No match/ambiguous */
  643.         beep();
  644.     }
  645.  
  646.     /*
  647.      * Tabs in the input line cause trouble after a pushback. tty driver
  648.      * won't backspace over them because column positions are now
  649.      * incorrect. This is solved by retyping over current line.
  650.      */
  651.     should_retype = FALSE;
  652.     if (Strchr(inputline, '\t')) {    /* tab Char in input line? */
  653.         back_to_col_1();
  654.         should_retype = TRUE;
  655.     }
  656.     if (command == LIST)    /* Always retype after a LIST */
  657.         should_retype = TRUE;
  658.     if (should_retype)
  659.         printprompt();
  660.     pushback(inputline);
  661.     if (should_retype)
  662.         retype();
  663.     }
  664.     setup_tty(OFF);
  665.     return (num_read);
  666. }
  667.  
  668. static int
  669. ignored(entry)
  670.     register Char *entry;
  671. {
  672.     struct varent *vp;
  673.     register Char **cp;
  674.  
  675.     if ((vp = adrof(STRfignore)) == NULL || (cp = vp->vec) == NULL)
  676.     return (FALSE);
  677.     for (; *cp != NULL; cp++)
  678.     if (is_suffix(entry, *cp))
  679.         return (TRUE);
  680.     return (FALSE);
  681. }
  682. #endif                /* FILEC */
  683.