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