xref: /titanic_52/usr/src/cmd/csh/sh.file.c (revision cc4b03b52c062a284439a341c57a418bdfc32c70)
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, retry = 0;
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  
169  again:
170  	(void) ioctl(SHIN, TCSETSF, (char *)&tty);
171  
172  	for (p = string; *p; p++) {
173  		char	mbc[MB_LEN_MAX];
174  		int	i, j = wctomb(mbc, (wchar_t)*p);
175  
176  		if (j < 0) {
177  			/* Error! But else what can we do? */
178  			continue;
179  		}
180  		for (i = 0; i < j; ++i) {
181  			if (ioctl(SHIN, TIOCSTI, mbc + i) != 0 &&
182  			    errno == EAGAIN) {
183  				if (retry++ < 5)
184  					goto again;
185  				/* probably no worth retrying any more */
186  			}
187  		}
188  	}
189  
190  	if (tty.c_lflag != tty_new.c_lflag)
191  		(void) ioctl(SHIN, TCSETS,  (char *)&tty_new);
192  	(void) sigsetmask(omask);
193  }
194  
195  /*
196   * Concatenate src onto tail of des.
197   * Des is a string whose maximum length is count.
198   * Always null terminate.
199   */
200  void
201  catn(tchar *des, tchar *src, int count)
202  {
203  #ifdef TRACE
204  	tprintf("TRACE- catn()\n");
205  #endif
206  
207  	while (--count >= 0 && *des)
208  		des++;
209  	while (--count >= 0)
210  		if ((*des++ = *src++) == '\0')
211  			return;
212  	*des = '\0';
213  }
214  
215  static int
216  max(a, b)
217  {
218  
219  	return (a > b ? a : b);
220  }
221  
222  /*
223   * Like strncpy but always leave room for trailing \0
224   * and always null terminate.
225   */
226  void
227  copyn(tchar *des, tchar *src, int count)
228  {
229  
230  #ifdef TRACE
231  	tprintf("TRACE- copyn()\n");
232  #endif
233  	while (--count >= 0)
234  		if ((*des++ = *src++) == '\0')
235  			return;
236  	*des = '\0';
237  }
238  
239  /*
240   * For qsort()
241   */
242  static int
243  fcompare(tchar **file1, tchar **file2)
244  {
245  
246  #ifdef TRACE
247  	tprintf("TRACE- fcompare()\n");
248  #endif
249  	return (strcoll_(*file1, *file2));
250  }
251  
252  static char
253  filetype(tchar *dir, tchar *file, int nosym)
254  {
255  	tchar path[MAXPATHLEN + 1];
256  	struct stat statb;
257  
258  #ifdef TRACE
259  	tprintf("TRACE- filetype()\n");
260  #endif
261  	if (dir) {
262  		catn(strcpy_(path, dir), file, MAXPATHLEN);
263  		if (nosym) {
264  			if (stat_(path, &statb) < 0)
265  				return (' ');
266  		} else {
267  			if (lstat_(path, &statb) < 0)
268  				return (' ');
269  		}
270  		if ((statb.st_mode & S_IFMT) == S_IFLNK)
271  			return ('@');
272  		if ((statb.st_mode & S_IFMT) == S_IFDIR)
273  			return ('/');
274  		if (((statb.st_mode & S_IFMT) == S_IFREG) &&
275  		    (statb.st_mode & 011))
276  			return ('*');
277  	}
278  	return (' ');
279  }
280  
281  /*
282   * Print sorted down columns
283   */
284  static void
285  print_by_column(tchar *dir, tchar *items[], int count, int looking_for_command)
286  {
287  	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  				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(tchar *new, tchar *old)
334  {
335  	tchar *o, *p;
336  	struct passwd *pw;
337  	static tchar person[40];
338  	char person_[40];		/* work */
339  	tchar *pw_dir;			/* work */
340  
341  #ifdef TRACE
342  	tprintf("TRACE- tilde()\n");
343  #endif
344  	if (old[0] != '~')
345  		return (strcpy_(new, old));
346  
347  	for (p = person, o = &old[1]; *o && *o != '/'; *p++ = *o++)
348  		;
349  	*p = '\0';
350  	if (person[0] == '\0')
351  		(void) strcpy_(new, value(S_home /* "home" */));
352  	else {
353  		pw = getpwnam(tstostr(person_, person));
354  		if (pw == NULL)
355  			return (NULL);
356  		pw_dir = strtots((tchar *)NULL, pw->pw_dir);	/* allocate */
357  		(void) strcpy_(new, pw_dir);
358  		xfree(pw_dir);					/* free it */
359  	}
360  	(void) strcat_(new, o);
361  	return (new);
362  }
363  
364  /*
365   * Cause pending line to be printed
366   */
367  static void
368  sim_retype(void)
369  {
370  #ifdef notdef
371  	struct termios tty_pending;
372  
373  #ifdef TRACE
374  	tprintf("TRACE- sim_retypr()\n");
375  #endif
376  	tty_pending = tty_new;
377  	tty_pending.c_lflag |= PENDIN;
378  
379  	(void) ioctl(SHIN, TCSETS,  (char *)&tty_pending);
380  #else
381  #ifdef TRACE
382  	tprintf("TRACE- sim_retype()\n");
383  #endif
384  	(void) write(SHOUT, CTRLR, strlen(CTRLR));
385  	printprompt();
386  #endif
387  }
388  
389  static int
390  beep_outc(int c)
391  {
392  	char	buf[1];
393  
394  	buf[0] = c;
395  
396  	(void) write(SHOUT, buf, 1);
397  
398  	return 0;
399  }
400  
401  static void
402  beep(void)
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 void
416  print_recognized_stuff(tchar *recognized_part)
417  {
418  	int unit =  didfds ? 1 : SHOUT;
419  
420  #ifdef TRACE
421  	tprintf("TRACE- print_recognized_stuff()\n");
422  #endif
423  
424  	/*
425  	 * An optimized erasing of that silly ^[
426  	 *
427  	 * One would think that line speeds have become fast enough that this
428  	 * isn't necessary, but it turns out that the visual difference is
429  	 * quite noticeable.
430  	 */
431  	flush();
432  	switch (tswidth(recognized_part)) {
433  	case 0:
434  		/* erase two characters: ^[ */
435  		write(unit, "\b\b  \b\b", sizeof "\b\b  \b\b" - 1);
436  		break;
437  
438  	case 1:
439  		/* overstrike the ^, erase the [ */
440  		write(unit, "\b\b", 2);
441  		printf("%t", recognized_part);
442  		write(unit, "  \b\b", 4);
443  		break;
444  
445  	default:
446  		/* overstrike both characters ^[ */
447  		write(unit, "\b\b", 2);
448  		printf("%t", recognized_part);
449  		break;
450  	}
451  	flush();
452  }
453  
454  /*
455   * Parse full path in file into 2 parts: directory and file names
456   * Should leave final slash (/) at end of dir.
457   */
458  static void
459  extract_dir_and_name(tchar *path, tchar *dir, tchar *name)
460  {
461  	tchar  *p;
462  
463  #ifdef TRACE
464  	tprintf("TRACE- extract_dir_and_name()\n");
465  #endif
466  	p = rindex_(path, '/');
467  	if (p == NOSTR) {
468  		copyn(name, path, MAXNAMLEN);
469  		dir[0] = '\0';
470  	} else {
471  		copyn(name, ++p, MAXNAMLEN);
472  		copyn(dir, path, p - path);
473  	}
474  }
475  
476  tchar *
477  getentry(DIR *dir_fd, int looking_for_lognames)
478  {
479  	struct passwd *pw;
480  	struct dirent *dirp;
481  	/*
482  	 * For char * -> tchar * Conversion
483  	 */
484  	static tchar strbuf[MAXNAMLEN+1];
485  
486  #ifdef TRACE
487  	tprintf("TRACE- getentry()\n");
488  #endif
489  	if (looking_for_lognames) {
490  		if ((pw = getpwent()) == NULL)
491  			return (NULL);
492  		return (strtots(strbuf, pw->pw_name));
493  	}
494  	if (dirp = readdir(dir_fd))
495  		return (strtots(strbuf, dirp->d_name));
496  	return (NULL);
497  }
498  
499  static void
500  free_items(tchar **items)
501  {
502  	int i;
503  
504  #ifdef TRACE
505  	tprintf("TRACE- free_items()\n");
506  #endif
507  	for (i = 0; items[i]; i++)
508  		xfree(items[i]);
509  	xfree((char *)items);
510  }
511  
512  #define	FREE_ITEMS(items) { \
513  	int omask;\
514  \
515  	omask = sigblock(sigmask(SIGINT));\
516  	free_items(items);\
517  	items = NULL;\
518  	(void) sigsetmask(omask);\
519  }
520  
521  /*
522   * Perform a RECOGNIZE or LIST command on string "word".
523   */
524  static int
525  search2(tchar *word, COMMAND command, int max_word_length)
526  {
527  	static tchar **items = NULL;
528  	DIR *dir_fd;
529  	int numitems = 0, ignoring = TRUE, nignored = 0;
530  	int name_length, looking_for_lognames;
531  	tchar tilded_dir[MAXPATHLEN + 1], dir[MAXPATHLEN + 1];
532  	tchar name[MAXNAMLEN + 1], extended_name[MAXNAMLEN+1];
533  	tchar *entry;
534  #define	MAXITEMS 1024
535  #ifdef TRACE
536  	tprintf("TRACE- search2()\n");
537  #endif
538  
539  	if (items != NULL)
540  		FREE_ITEMS(items);
541  
542  	looking_for_lognames = (*word == '~') && (index_(word, '/') == NULL);
543  	if (looking_for_lognames) {
544  		(void) setpwent();
545  		copyn(name, &word[1], MAXNAMLEN);	/* name sans ~ */
546  	} else {
547  		extract_dir_and_name(word, dir, name);
548  		if (tilde(tilded_dir, dir) == 0)
549  			return (0);
550  		dir_fd = opendir_(*tilded_dir ? tilded_dir : S_DOT /* "." */);
551  		if (dir_fd == NULL)
552  			return (0);
553  	}
554  
555  again:	/* search for matches */
556  	name_length = strlen_(name);
557  	for (numitems = 0; entry = getentry(dir_fd, looking_for_lognames); ) {
558  		if (!is_prefix(name, entry))
559  			continue;
560  		/* Don't match . files on null prefix match */
561  		if (name_length == 0 && entry[0] == '.' &&
562  		    !looking_for_lognames)
563  			continue;
564  		if (command == LIST) {
565  			if (numitems >= MAXITEMS) {
566  				printf("\nYikes!! Too many %s!!\n",
567  				    looking_for_lognames ?
568  					"names in password file":"files");
569  				break;
570  			}
571  			if (items == NULL)
572  				items =  (tchar **)xcalloc(sizeof (items[1]),
573  				    MAXITEMS+1);
574  			items[numitems] = (tchar *)xalloc((unsigned)(strlen_(entry) + 1) * sizeof (tchar));
575  			copyn(items[numitems], entry, MAXNAMLEN);
576  			numitems++;
577  		} else {			/* RECOGNIZE command */
578  			if (ignoring && ignored(entry))
579  				nignored++;
580  			else if (recognize(extended_name,
581  			    entry, name_length, ++numitems))
582  				break;
583  		}
584  	}
585  	if (ignoring && numitems == 0 && nignored > 0) {
586  		ignoring = FALSE;
587  		nignored = 0;
588  		if (looking_for_lognames)
589  			(void) setpwent();
590  		else
591  			rewinddir(dir_fd);
592  		goto again;
593  	}
594  
595  	if (looking_for_lognames)
596  		(void) endpwent();
597  	else {
598  		unsetfd(dir_fd->dd_fd);
599  		closedir_(dir_fd);
600  	}
601  	if (command == RECOGNIZE && numitems > 0) {
602  		if (looking_for_lognames)
603  			copyn(word, S_TIL /* "~" */, 1);
604  		else
605  			/* put back dir part */
606  			copyn(word, dir, max_word_length);
607  		/* add extended name */
608  		catn(word, extended_name, max_word_length);
609  		return (numitems);
610  	}
611  	if (command == LIST) {
612  		qsort((char *)items, numitems, sizeof (items[1]),
613  		    (int (*)(const void *, const void *))fcompare);
614  		/*
615  		 * Never looking for commands in this version, so final
616  		 * argument forced to 0.  If command name completion is
617  		 * reinstated, this must change.
618  		 */
619  		print_by_column(looking_for_lognames ? NULL : tilded_dir,
620  		    items, numitems, 0);
621  		if (items != NULL)
622  			FREE_ITEMS(items);
623  	}
624  	return (0);
625  }
626  
627  /*
628   * Object: extend what user typed up to an ambiguity.
629   * Algorithm:
630   * On first match, copy full entry (assume it'll be the only match)
631   * On subsequent matches, shorten extended_name to the first
632   * character mismatch between extended_name and entry.
633   * If we shorten it back to the prefix length, stop searching.
634   */
635  int
636  recognize(tchar *extended_name, tchar *entry, int name_length, int numitems)
637  {
638  
639  #ifdef TRACE
640  	tprintf("TRACE- recognize()\n");
641  #endif
642  	if (numitems == 1)				/* 1st match */
643  		copyn(extended_name, entry, MAXNAMLEN);
644  	else {					/* 2nd and subsequent matches */
645  		tchar *x, *ent;
646  		int len = 0;
647  
648  		x = extended_name;
649  		for (ent = entry; *x && *x == *ent++; x++, len++)
650  			;
651  		*x = '\0';			/* Shorten at 1st char diff */
652  		if (len == name_length)		/* Ambiguous to prefix? */
653  			return (-1);		/* So stop now and save time */
654  	}
655  	return (0);
656  }
657  
658  /*
659   * Return true if check items initial chars in template
660   * This differs from PWB imatch in that if check is null
661   * it items anything
662   */
663  static int
664  is_prefix(tchar *check, tchar *template)
665  {
666  #ifdef TRACE
667  	tprintf("TRACE- is_prefix()\n");
668  #endif
669  
670  	do
671  		if (*check == 0)
672  			return (TRUE);
673  	while (*check++ == *template++);
674  	return (FALSE);
675  }
676  
677  /*
678   *  Return true if the chars in template appear at the
679   *  end of check, i.e., are its suffix.
680   */
681  static int
682  is_suffix(tchar *check, tchar *template)
683  {
684  	tchar *c, *t;
685  
686  #ifdef TRACE
687  	tprintf("TRACE- is_suffix()\n");
688  #endif
689  	for (c = check; *c++; )
690  		;
691  	for (t = template; *t++; )
692  		;
693  	for (;;) {
694  		if (t == template)
695  			return (TRUE);
696  		if (c == check || *--t != *--c)
697  			return (FALSE);
698  	}
699  }
700  
701  int
702  tenex(tchar *inputline, int inputline_size)
703  {
704  	int numitems, num_read, should_retype;
705  	int i;
706  
707  #ifdef TRACE
708  	tprintf("TRACE- tenex()\n");
709  #endif
710  	setup_tty(ON);
711  	termchars();
712  	num_read = 0;
713  	should_retype = FALSE;
714  	while ((i = read_(SHIN, inputline+num_read, inputline_size-num_read))
715  	    > 0) {
716  		static tchar *delims = S_DELIM /* " '\"\t;&<>()|`" */;
717  		tchar *str_end, *word_start, last_char;
718  		int space_left;
719  		struct termios tty;
720  		COMMAND command;
721  
722  		num_read += i;
723  		inputline[num_read] = '\0';
724  		last_char = inputline[num_read - 1] & TRIM;
725  
726  		/*
727  		 * read_() can return more than requested size if there
728  		 * is multibyte character at the end.
729  		 */
730  		if ((num_read >= inputline_size) || (last_char == '\n'))
731  			break;
732  
733  		str_end = &inputline[num_read];
734  		if (last_char == ESC) {
735  			command = RECOGNIZE;
736  			*--str_end = '\0';	/* wipe out trailing ESC */
737  		} else
738  			command = LIST;
739  
740  		tty = tty_new;
741  		tty.c_lflag &= ~ECHO;
742  		(void) ioctl(SHIN, TCSETSF, (char *)&tty);
743  
744  		if (command == LIST)
745  			printf("\n");
746  		/*
747  		 * Find LAST occurence of a delimiter in the inputline.
748  		 * The word start is one character past it.
749  		 */
750  		for (word_start = str_end; word_start > inputline;
751  		    --word_start) {
752  			if (index_(delims, word_start[-1]) ||
753  			    isauxsp(word_start[-1]))
754  				break;
755  		}
756  		space_left = inputline_size - (word_start - inputline) - 1;
757  		numitems = search2(word_start, command, space_left);
758  
759  		/*
760  		 * Tabs in the input line cause trouble after a pushback.
761  		 * tty driver won't backspace over them because column
762  		 * positions are now incorrect. This is solved by retyping
763  		 * over current line.
764  		 */
765  		if (index_(inputline, '\t')) {	/* tab tchar in input line? */
766  			back_to_col_1();
767  			should_retype = TRUE;
768  		}
769  		if (command == LIST)		/* Always retype after a LIST */
770  			should_retype = TRUE;
771  		if (should_retype)
772  			printprompt();
773  		pushback(inputline, should_retype);
774  		num_read = 0;			/* chars will be reread */
775  		should_retype = FALSE;
776  
777  		/*
778  		 * Avoid a race condition by echoing what we're recognized
779  		 * _after_ pushing back the command line.  This way, if the
780  		 * user waits until seeing this output before typing more
781  		 * stuff, the resulting keystrokes won't race with the STIed
782  		 * input we've pushed back.  (Of course, if the user types
783  		 * ahead, the race still exists and it's quite possible that
784  		 * the pushed back input line will interleave with the
785  		 * keystrokes in unexpected ways.)
786  		 */
787  		if (command == RECOGNIZE) {
788  			/* print from str_end on */
789  			print_recognized_stuff(str_end);
790  			if (numitems != 1)	/* Beep = No match/ambiguous */
791  				beep();
792  		}
793  	}
794  	setup_tty(OFF);
795  	return (num_read);
796  }
797  
798  static int
799  ignored(tchar *entry)
800  {
801  	struct varent *vp;
802  	tchar **cp;
803  
804  #ifdef TRACE
805  	tprintf("TRACE- ignored()\n");
806  #endif
807  	if ((vp = adrof(S_fignore /* "fignore" */)) == NULL ||
808  	    (cp = vp->vec) == NULL)
809  		return (FALSE);
810  	for (; *cp != NULL; cp++)
811  		if (is_suffix(entry, *cp))
812  			return (TRUE);
813  	return (FALSE);
814  }
815  #endif /* FILEC */
816