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