xref: /illumos-gate/usr/src/cmd/csh/sh.file.c (revision 6c02b4a4)
1 /*
2  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
7 /*	  All Rights Reserved  	*/
8 
9 /*
10  * Copyright (c) 1980 Regents of the University of California.
11  * All rights reserved.  The Berkeley Software License Agreement
12  * specifies the terms and conditions for redistribution.
13  */
14 
15 #pragma ident	"%Z%%M%	%I%	%E% SMI"
16 
17 #ifdef FILEC
18 /*
19  * Tenex style file name recognition, .. and more.
20  * History:
21  *	Author: Ken Greer, Sept. 1975, CMU.
22  *	Finally got around to adding to the Cshell., Ken Greer, Dec. 1981.
23  */
24 
25 #include "sh.h"
26 #include <sys/types.h>
27 #include <dirent.h>
28 #include <pwd.h>
29 #include "sh.tconst.h"
30 
31 #define TRUE	1
32 #define FALSE	0
33 #define ON	1
34 #define OFF	0
35 
36 #define ESC	'\033'
37 
38 extern DIR *opendir_(tchar *);
39 
40 static char *BELL = "\07";
41 static char *CTRLR = "^R\n";
42 
43 typedef enum {LIST, RECOGNIZE} COMMAND;
44 
45 static jmp_buf osetexit;		/* saved setexit() state */
46 static struct termios  tty_save;	/* saved terminal state */
47 static struct termios  tty_new;		/* new terminal state */
48 
49 static int	is_prefix(tchar *, tchar *);
50 static int	is_suffix(tchar *, tchar *);
51 static int	ignored(tchar *);
52 
53 /*
54  * Put this here so the binary can be patched with adb to enable file
55  * completion by default.  Filec controls completion, nobeep controls
56  * ringing the terminal bell on incomplete expansions.
57  */
58 bool filec = 0;
59 
60 static void
61 setup_tty(int on)
62 {
63 	int omask;
64 #ifdef TRACE
65 	tprintf("TRACE- setup_tty()\n");
66 #endif
67 
68 	omask = sigblock(sigmask(SIGINT));
69 	if (on) {
70 		/*
71 		 * The shell makes sure that the tty is not in some weird state
72 		 * and fixes it if it is.  But it should be noted that the
73 		 * tenex routine will not work correctly in CBREAK or RAW mode
74 		 * so this code below is, therefore, mandatory.
75 		 *
76 		 * Also, in order to recognize the ESC (filename-completion)
77 		 * character, set EOL to ESC.  This way, ESC will terminate
78 		 * the line, but still be in the input stream.
79 		 * EOT (filename list) will also terminate the line,
80 		 * but will not appear in the input stream.
81 		 *
82 		 * The getexit/setexit contortions ensure that the
83 		 * tty state will be restored if the user types ^C.
84 		 */
85 		(void) ioctl(SHIN, TCGETS,  (char *)&tty_save);
86 		getexit(osetexit);
87 		if (setjmp(reslab)) {
88 			(void) ioctl(SHIN, TCSETSW,  (char *)&tty_save);
89 			resexit(osetexit);
90 			reset();
91 		}
92 		tty_new = tty_save;
93 		tty_new.c_cc[VEOL] = ESC;
94 		tty_new.c_iflag |= IMAXBEL | BRKINT | IGNPAR;
95 		tty_new.c_lflag |= ICANON;
96 		tty_new.c_lflag |= ECHOCTL;
97 		tty_new.c_oflag &= ~OCRNL;
98 		(void) ioctl(SHIN, TCSETSW,  (char *)&tty_new);
99 	} else {
100 		/*
101 		 * Reset terminal state to what user had when invoked
102 		 */
103 		(void) ioctl(SHIN, TCSETSW,  (char *)&tty_save);
104 		resexit(osetexit);
105 	}
106 	(void) sigsetmask(omask);
107 }
108 
109 static void
110 termchars(void)
111 {
112 	extern char *tgetstr();
113 	char bp[1024];
114 	static char area[256];
115 	static int been_here = 0;
116 	char *ap = area;
117 	char *s;
118 	char *term;
119 
120 #ifdef TRACE
121 	tprintf("TRACE- termchars()\n");
122 #endif
123 	if (been_here)
124 		return;
125 	been_here = TRUE;
126 
127 	if ((term = getenv("TERM")) == NULL)
128 		return;
129 	if (tgetent(bp, term) != 1)
130 		return;
131 	if (s = tgetstr("vb", &ap))		/* Visible Bell */
132 		BELL = s;
133 }
134 
135 /*
136  * Move back to beginning of current line
137  */
138 static void
139 back_to_col_1(void)
140 {
141 	int omask;
142 
143 #ifdef TRACE
144 	tprintf("TRACE- back_to_col_1()\n");
145 #endif
146 	omask = sigblock(sigmask(SIGINT));
147 	(void) write(SHOUT, "\r", 1);
148 	(void) sigsetmask(omask);
149 }
150 
151 /*
152  * Push string contents back into tty queue
153  */
154 static void
155 pushback(tchar *string, int echoflag)
156 {
157 	tchar *p;
158 	struct termios tty;
159 	int omask;
160 
161 #ifdef TRACE
162 	tprintf("TRACE- pushback()\n");
163 #endif
164 	omask = sigblock(sigmask(SIGINT));
165 	tty = tty_new;
166 	if (!echoflag)
167 		tty.c_lflag &= ~ECHO;
168 	(void) ioctl(SHIN, TCSETSF, (char *)&tty);
169 
170 	for (p = string; *p; p++){
171 		char	mbc[MB_LEN_MAX];
172 		int	i, j = wctomb(mbc, (wchar_t)*p);
173 
174 		if (j < 0) {
175 			/* Error! But else what can we do? */
176 			continue;
177 		}
178 		for (i = 0; i < j; ++i) {
179 			/* XXX: no error recovery provision. */
180 			(void) ioctl(SHIN, TIOCSTI, mbc + i);
181 		}
182 	}
183 
184 	if (tty.c_lflag != tty_new.c_lflag)
185 		(void) ioctl(SHIN, TCSETS,  (char *)&tty_new);
186 	(void) sigsetmask(omask);
187 }
188 
189 /*
190  * Concatenate src onto tail of des.
191  * Des is a string whose maximum length is count.
192  * Always null terminate.
193  */
194 void
195 catn(tchar *des, tchar *src, int count)
196 {
197 #ifdef TRACE
198 	tprintf("TRACE- catn()\n");
199 #endif
200 
201 	while (--count >= 0 && *des)
202 		des++;
203 	while (--count >= 0)
204 		if ((*des++ = *src++) == '\0')
205 			return;
206 	*des = '\0';
207 }
208 
209 static int
210 max(a, b)
211 {
212 
213 	return (a > b ? a : b);
214 }
215 
216 /*
217  * Like strncpy but always leave room for trailing \0
218  * and always null terminate.
219  */
220 void
221 copyn(tchar *des, tchar *src, int count)
222 {
223 
224 #ifdef TRACE
225 	tprintf("TRACE- copyn()\n");
226 #endif
227 	while (--count >= 0)
228 		if ((*des++ = *src++) == '\0')
229 			return;
230 	*des = '\0';
231 }
232 
233 /*
234  * For qsort()
235  */
236 static int
237 fcompare(tchar **file1, tchar **file2)
238 {
239 
240 #ifdef TRACE
241 	tprintf("TRACE- fcompare()\n");
242 #endif
243 	return (strcoll_(*file1, *file2));
244 }
245 
246 static char
247 filetype(tchar *dir, tchar *file, int nosym)
248 {
249 	tchar path[MAXPATHLEN + 1];
250 	struct stat statb;
251 
252 #ifdef TRACE
253 	tprintf("TRACE- filetype()\n");
254 #endif
255 	if (dir) {
256 		catn(strcpy_(path, dir), file, MAXPATHLEN);
257 		if (nosym) {
258 			if (stat_(path, &statb) < 0)
259 				return (' ');
260 		} else {
261 			if (lstat_(path, &statb) < 0)
262 				return (' ');
263 		}
264 		if ((statb.st_mode & S_IFMT) == S_IFLNK)
265 			return ('@');
266 		if ((statb.st_mode & S_IFMT) == S_IFDIR)
267 			return ('/');
268 		if (((statb.st_mode & S_IFMT) == S_IFREG) &&
269 		    (statb.st_mode & 011))
270 			return ('*');
271 	}
272 	return (' ');
273 }
274 
275 /*
276  * Print sorted down columns
277  */
278 static void
279 print_by_column(tchar *dir, tchar *items[], int count, int looking_for_command)
280 {
281 	int i, rows, r, c, maxwidth = 0, columns;
282 
283 #ifdef TRACE
284 	tprintf("TRACE- print_by_column()\n");
285 #endif
286 	for (i = 0; i < count; i++)
287 		maxwidth = max(maxwidth, tswidth(items[i]));
288 
289 	/* for the file tag and space */
290 	maxwidth += looking_for_command ? 1 : 2;
291 	columns = max(78 / maxwidth, 1);
292 	rows = (count + (columns - 1)) / columns;
293 
294 	for (r = 0; r < rows; r++) {
295 		for (c = 0; c < columns; c++) {
296 			i = c * rows + r;
297 			if (i < count) {
298 				int w;
299 
300 				/*
301 				 * Print filename followed by
302 				 * '@' or '/' or '*' or ' '
303 				 */
304 				printf("%t", items[i]);
305 				w = tswidth(items[i]);
306 				if (!looking_for_command) {
307 					printf("%c",
308 					    (tchar) filetype(dir, items[i], 0));
309 					w++;
310 				}
311 				if (c < columns - 1)	/* last column? */
312 					for (; w < maxwidth; w++)
313 						printf(" ");
314 			}
315 		}
316 		printf("\n");
317 	}
318 }
319 
320 /*
321  * Expand file name with possible tilde usage
322  *	~person/mumble
323  * expands to
324  *	home_directory_of_person/mumble
325  */
326 tchar *
327 tilde(tchar *new, tchar *old)
328 {
329 	tchar *o, *p;
330 	struct passwd *pw;
331 	static tchar person[40];
332 	char person_[40];		/* work */
333 	tchar *pw_dir;			/* work */
334 
335 #ifdef TRACE
336 	tprintf("TRACE- tilde()\n");
337 #endif
338 	if (old[0] != '~')
339 		return (strcpy_(new, old));
340 
341 	for (p = person, o = &old[1]; *o && *o != '/'; *p++ = *o++)
342 		;
343 	*p = '\0';
344 	if (person[0] == '\0')
345 		(void) strcpy_(new, value(S_home /*"home"*/));
346 	else {
347 		pw = getpwnam(tstostr(person_,person));
348 		if (pw == NULL)
349 			return (NULL);
350 		pw_dir = strtots((tchar *)NULL, pw->pw_dir);	/* allocate */
351 		(void) strcpy_(new, pw_dir);
352 		xfree(pw_dir);					/* free it */
353 	}
354 	(void) strcat_(new, o);
355 	return (new);
356 }
357 
358 /*
359  * Cause pending line to be printed
360  */
361 static void
362 sim_retype(void)
363 {
364 #ifdef notdef
365 	struct termios tty_pending;
366 
367 #ifdef TRACE
368 	tprintf("TRACE- sim_retypr()\n");
369 #endif
370 	tty_pending = tty_new;
371 	tty_pending.c_lflag |= PENDIN;
372 
373 	(void) ioctl(SHIN, TCSETS,  (char *)&tty_pending);
374 #else
375 #ifdef TRACE
376 	tprintf("TRACE- sim_retype()\n");
377 #endif
378 	(void) write(SHOUT, CTRLR, strlen(CTRLR));
379 	printprompt();
380 #endif
381 }
382 
383 static int
384 beep_outc(int c)
385 {
386 	char	buf[1];
387 
388 	buf[0] = c;
389 
390 	(void) write (SHOUT, buf, 1);
391 
392 	return 0;
393 }
394 
395 static void
396 beep(void)
397 {
398 
399 #ifdef TRACE
400 	tprintf("TRACE- beep()\n");
401 #endif
402 	if (adrof(S_nobeep /*"nobeep" */) == 0)
403 		(void) tputs (BELL, 0, beep_outc);
404 }
405 
406 /*
407  * Erase that silly ^[ and print the recognized part of the string.
408  */
409 static void
410 print_recognized_stuff(tchar *recognized_part)
411 {
412 	int unit =  didfds ? 1 : SHOUT;
413 
414 #ifdef TRACE
415 	tprintf("TRACE- print_recognized_stuff()\n");
416 #endif
417 
418 	/*
419 	 * An optimized erasing of that silly ^[
420 	 *
421 	 * One would think that line speeds have become fast enough that this
422 	 * isn't necessary, but it turns out that the visual difference is
423 	 * quite noticeable.
424 	 */
425 	flush();
426 	switch (tswidth(recognized_part)) {
427 	case 0:
428 		/* erase two characters: ^[ */
429 		write(unit, "\b\b  \b\b", sizeof "\b\b  \b\b" - 1);
430 		break;
431 
432 	case 1:
433 		/* overstrike the ^, erase the [ */
434 		write(unit, "\b\b", 2);
435 		printf("%t", recognized_part);
436 		write(unit, "  \b\b", 4);
437 		break;
438 
439 	default:
440 		/* overstrike both characters ^[ */
441 		write(unit, "\b\b", 2);
442 		printf("%t", recognized_part);
443 		break;
444 	}
445 	flush();
446 }
447 
448 /*
449  * Parse full path in file into 2 parts: directory and file names
450  * Should leave final slash (/) at end of dir.
451  */
452 static void
453 extract_dir_and_name(tchar *path, tchar *dir, tchar *name)
454 {
455 	tchar  *p;
456 
457 #ifdef TRACE
458 	tprintf("TRACE- extract_dir_and_name()\n");
459 #endif
460 	p = rindex_(path, '/');
461 	if (p == NOSTR) {
462 		copyn(name, path, MAXNAMLEN);
463 		dir[0] = '\0';
464 	} else {
465 		copyn(name, ++p, MAXNAMLEN);
466 		copyn(dir, path, p - path);
467 	}
468 }
469 
470 tchar *
471 getentry(DIR *dir_fd, int looking_for_lognames)
472 {
473 	struct passwd *pw;
474 	struct dirent *dirp;
475 	/*
476 	 * For char * -> tchar * Conversion
477 	 */
478 	static tchar strbuf[MAXNAMLEN+1];
479 
480 #ifdef TRACE
481 	tprintf("TRACE- getentry()\n");
482 #endif
483 	if (looking_for_lognames) {
484 		if ((pw = getpwent ()) == NULL)
485 			return (NULL);
486 		return (strtots(strbuf,pw->pw_name));
487 	}
488 	if (dirp = readdir(dir_fd))
489 		return (strtots(strbuf,dirp->d_name));
490 	return (NULL);
491 }
492 
493 static void
494 free_items(tchar **items)
495 {
496 	int i;
497 
498 #ifdef TRACE
499 	tprintf("TRACE- free_items()\n");
500 #endif
501 	for (i = 0; items[i]; i++)
502 		free(items[i]);
503 	free( (char *)items);
504 }
505 
506 #define FREE_ITEMS(items) { \
507 	int omask;\
508 \
509 	omask = sigblock(sigmask(SIGINT));\
510 	free_items(items);\
511 	items = NULL;\
512 	(void) sigsetmask(omask);\
513 }
514 
515 /*
516  * Perform a RECOGNIZE or LIST command on string "word".
517  */
518 static int
519 search2(tchar *word, COMMAND command, int max_word_length)
520 {
521 	static tchar **items = NULL;
522 	DIR *dir_fd;
523 	int numitems = 0, ignoring = TRUE, nignored = 0;
524 	int name_length, looking_for_lognames;
525 	tchar tilded_dir[MAXPATHLEN + 1], dir[MAXPATHLEN + 1];
526 	tchar name[MAXNAMLEN + 1], extended_name[MAXNAMLEN+1];
527 	tchar *entry;
528 #define MAXITEMS 1024
529 #ifdef TRACE
530 	tprintf("TRACE- search2()\n");
531 #endif
532 
533 	if (items != NULL)
534 		FREE_ITEMS(items);
535 
536 	looking_for_lognames = (*word == '~') && (index_(word, '/') == NULL);
537 	if (looking_for_lognames) {
538 		(void) setpwent();
539 		copyn(name, &word[1], MAXNAMLEN);	/* name sans ~ */
540 	} else {
541 		extract_dir_and_name(word, dir, name);
542 		if (tilde(tilded_dir, dir) == 0)
543 			return (0);
544 		dir_fd = opendir_(*tilded_dir ? tilded_dir : S_DOT /*"."*/);
545 		if (dir_fd == NULL)
546 			return (0);
547 	}
548 
549 again:	/* search for matches */
550 	name_length = strlen_(name);
551 	for (numitems = 0; entry = getentry(dir_fd, looking_for_lognames); ) {
552 		if (!is_prefix(name, entry))
553 			continue;
554 		/* Don't match . files on null prefix match */
555 		if (name_length == 0 && entry[0] == '.' &&
556 		    !looking_for_lognames)
557 			continue;
558 		if (command == LIST) {
559 			if (numitems >= MAXITEMS) {
560 				printf ("\nYikes!! Too many %s!!\n",
561 				    looking_for_lognames ?
562 					"names in password file":"files");
563 				break;
564 			}
565 			if (items == NULL)
566 				items =  (tchar **) calloc(sizeof (items[1]),
567 				    MAXITEMS+1);
568 			items[numitems] = (tchar *)xalloc((unsigned)(strlen_(entry) + 1)*sizeof(tchar));
569 			copyn(items[numitems], entry, MAXNAMLEN);
570 			numitems++;
571 		} else {			/* RECOGNIZE command */
572 			if (ignoring && ignored(entry))
573 				nignored++;
574 			else if (recognize(extended_name,
575 			    entry, name_length, ++numitems))
576 				break;
577 		}
578 	}
579 	if (ignoring && numitems == 0 && nignored > 0) {
580 		ignoring = FALSE;
581 		nignored = 0;
582 		if (looking_for_lognames)
583 			(void)setpwent();
584 		else
585 			rewinddir(dir_fd);
586 		goto again;
587 	}
588 
589 	if (looking_for_lognames)
590 		(void) endpwent();
591 	else {
592 		unsetfd(dir_fd->dd_fd);
593 		closedir_(dir_fd);
594 	}
595 	if (command == RECOGNIZE && numitems > 0) {
596 		if (looking_for_lognames)
597 			 copyn(word, S_TIL /*"~" */, 1);
598 		else
599 			/* put back dir part */
600 			copyn(word, dir, max_word_length);
601 		/* add extended name */
602 		catn(word, extended_name, max_word_length);
603 		return (numitems);
604 	}
605 	if (command == LIST) {
606 		qsort( (char *)items, numitems, sizeof(items[1]),
607 		      (int (*)(const void *, const void *))fcompare);
608 		/*
609 		 * Never looking for commands in this version, so final
610 		 * argument forced to 0.  If command name completion is
611 		 * reinstated, this must change.
612 		 */
613 		print_by_column(looking_for_lognames ? NULL : tilded_dir,
614 		    items, numitems, 0);
615 		if (items != NULL)
616 			FREE_ITEMS(items);
617 	}
618 	return (0);
619 }
620 
621 /*
622  * Object: extend what user typed up to an ambiguity.
623  * Algorithm:
624  * On first match, copy full entry (assume it'll be the only match)
625  * On subsequent matches, shorten extended_name to the first
626  * character mismatch between extended_name and entry.
627  * If we shorten it back to the prefix length, stop searching.
628  */
629 int
630 recognize(tchar *extended_name, tchar *entry, int name_length, int numitems)
631 {
632 
633 #ifdef TRACE
634 	tprintf("TRACE- recognize()\n");
635 #endif
636 	if (numitems == 1)				/* 1st match */
637 		copyn(extended_name, entry, MAXNAMLEN);
638 	else {					/* 2nd and subsequent matches */
639 		tchar *x, *ent;
640 		int len = 0;
641 
642 		x = extended_name;
643 		for (ent = entry; *x && *x == *ent++; x++, len++)
644 			;
645 		*x = '\0';			/* Shorten at 1st char diff */
646 		if (len == name_length)		/* Ambiguous to prefix? */
647 			return (-1);		/* So stop now and save time */
648 	}
649 	return (0);
650 }
651 
652 /*
653  * Return true if check items initial chars in template
654  * This differs from PWB imatch in that if check is null
655  * it items anything
656  */
657 static int
658 is_prefix(tchar *check, tchar *template)
659 {
660 #ifdef TRACE
661 	tprintf("TRACE- is_prefix()\n");
662 #endif
663 
664 	do
665 		if (*check == 0)
666 			return (TRUE);
667 	while (*check++ == *template++);
668 	return (FALSE);
669 }
670 
671 /*
672  *  Return true if the chars in template appear at the
673  *  end of check, i.e., are its suffix.
674  */
675 static int
676 is_suffix(tchar *check, tchar *template)
677 {
678 	tchar *c, *t;
679 
680 #ifdef TRACE
681 	tprintf("TRACE- is_suffix()\n");
682 #endif
683 	for (c = check; *c++;)
684 		;
685 	for (t = template; *t++;)
686 		;
687 	for (;;) {
688 		if (t == template)
689 			return (TRUE);
690 		if (c == check || *--t != *--c)
691 			return (FALSE);
692 	}
693 }
694 
695 int
696 tenex(tchar *inputline, int inputline_size)
697 {
698 	int numitems, num_read, should_retype;
699 	int i;
700 
701 #ifdef TRACE
702 	tprintf("TRACE- tenex()\n");
703 #endif
704 	setup_tty(ON);
705 	termchars();
706 	num_read = 0;
707 	should_retype = FALSE;
708 	while ((i = read_(SHIN, inputline+num_read, inputline_size-num_read))
709 	    > 0) {
710 		static tchar *delims = S_DELIM /*" '\"\t;&<>()|`"*/;
711 		tchar *str_end, *word_start, last_char;
712 		int space_left;
713 		struct termios tty;
714 		COMMAND command;
715 
716 		num_read += i;
717 		inputline[num_read] = '\0';
718 		last_char = inputline[num_read - 1] & TRIM;
719 
720 		if ((num_read == inputline_size) || (last_char == '\n'))
721 			break;
722 
723 		str_end = &inputline[num_read];
724 		if (last_char == ESC) {
725 			command = RECOGNIZE;
726 			*--str_end = '\0';	/* wipe out trailing ESC */
727 		} else
728 			command = LIST;
729 
730 		tty = tty_new;
731 		tty.c_lflag &= ~ECHO;
732 		(void) ioctl(SHIN, TCSETSF, (char *)&tty);
733 
734 		if (command == LIST)
735 			printf("\n");
736 		/*
737 		 * Find LAST occurence of a delimiter in the inputline.
738 		 * The word start is one character past it.
739 		 */
740 		for (word_start = str_end; word_start > inputline;
741 		    --word_start) {
742 			if (index_(delims, word_start[-1]) ||
743 			    isauxsp(word_start[-1]))
744 				break;
745 		}
746 		space_left = inputline_size - (word_start - inputline) - 1;
747 		numitems = search2(word_start, command, space_left);
748 
749 		/*
750 		 * Tabs in the input line cause trouble after a pushback.
751 		 * tty driver won't backspace over them because column
752 		 * positions are now incorrect. This is solved by retyping
753 		 * over current line.
754 		 */
755 		if (index_(inputline, '\t')) {	/* tab tchar in input line? */
756 			back_to_col_1();
757 			should_retype = TRUE;
758 		}
759 		if (command == LIST)		/* Always retype after a LIST */
760 			should_retype = TRUE;
761 		if (should_retype)
762 			printprompt();
763 		pushback(inputline, should_retype);
764 		num_read = 0;			/* chars will be reread */
765 		should_retype = FALSE;
766 
767 		/*
768 		 * Avoid a race condition by echoing what we're recognized
769 		 * _after_ pushing back the command line.  This way, if the
770 		 * user waits until seeing this output before typing more
771 		 * stuff, the resulting keystrokes won't race with the STIed
772 		 * input we've pushed back.  (Of course, if the user types
773 		 * ahead, the race still exists and it's quite possible that
774 		 * the pushed back input line will interleave with the
775 		 * keystrokes in unexpected ways.)
776 		 */
777 		if (command == RECOGNIZE) {
778 			/* print from str_end on */
779 			print_recognized_stuff(str_end);
780 			if (numitems != 1)	/* Beep = No match/ambiguous */
781 				beep();
782 		}
783 	}
784 	setup_tty(OFF);
785 	return (num_read);
786 }
787 
788 static int
789 ignored(tchar *entry)
790 {
791 	struct varent *vp;
792 	tchar **cp;
793 
794 #ifdef TRACE
795 	tprintf("TRACE- ignored()\n");
796 #endif
797 	if ((vp = adrof(S_fignore /*"fignore"*/)) == NULL ||
798 	    (cp = vp->vec) == NULL)
799 		return (FALSE);
800 	for (; *cp != NULL; cp++)
801 		if (is_suffix(entry, *cp))
802 			return (TRUE);
803 	return (FALSE);
804 }
805 #endif /* FILEC */
806