1/*	$OpenBSD: file.c,v 1.40 2020/10/06 01:40:43 deraadt Exp $	*/
2/*	$NetBSD: file.c,v 1.11 1996/11/08 19:34:37 christos Exp $	*/
3
4/*-
5 * Copyright (c) 1980, 1991, 1993
6 *	The Regents of the University of California.  All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#include <sys/ioctl.h>
34#include <sys/stat.h>
35#include <sys/types.h>
36
37#include <dirent.h>
38#include <errno.h>
39#include <limits.h>
40#include <pwd.h>
41#include <stdlib.h>
42#include <string.h>
43#include <termios.h>
44#include <unistd.h>
45
46#include "csh.h"
47#include "extern.h"
48
49/*
50 * Tenex style file name recognition, .. and more.
51 * History:
52 *	Author: Ken Greer, Sept. 1975, CMU.
53 *	Finally got around to adding to the Cshell., Ken Greer, Dec. 1981.
54 */
55
56#ifndef TRUE
57#define TRUE 1
58#endif
59#ifndef FALSE
60#define FALSE 0
61#endif
62
63#define	ESC		'\033'
64#define	TABWIDTH	8
65
66typedef enum {
67	LIST,
68	RECOGNIZE
69} COMMAND;
70
71struct cmdline {
72	int	 fdin;
73	int	 fdout;
74	int	 istty;
75	int	 flags;
76#define	CL_ALTWERASE	0x1
77#define	CL_PROMPT	0x2
78	char	*buf;
79	size_t	 len;
80	size_t	 size;
81	size_t	 cursor;
82};
83
84/* Command line auxiliary functions. */
85static void	 cl_beep(struct cmdline *);
86static void	 cl_flush(struct cmdline *);
87static int	 cl_getc(struct cmdline *);
88static Char	*cl_lastw(struct cmdline *);
89static void	 cl_putc(struct cmdline *, int);
90static void	 cl_visc(struct cmdline *, int);
91
92/* Command line editing functions. */
93static int	cl_abort(struct cmdline *, int);
94static int	cl_erasec(struct cmdline *, int);
95static int	cl_erasew(struct cmdline *, int);
96static int	cl_insert(struct cmdline *, int);
97static int	cl_kill(struct cmdline *, int);
98static int	cl_list(struct cmdline *, int);
99static int	cl_literal(struct cmdline *, int);
100static int	cl_recognize(struct cmdline *, int);
101static int	cl_reprint(struct cmdline *, int);
102static int	cl_status(struct cmdline *, int);
103
104static const struct termios	*setup_tty(int);
105
106static void	 catn(Char *, Char *, int);
107static void	 copyn(Char *, Char *, int);
108static Char	 filetype(Char *, Char *);
109static void	 print_by_column(Char *, Char *[], int);
110static Char	*tilde(Char *, Char *);
111static void	 extract_dir_and_name(Char *, Char *, Char *);
112static Char	*getentry(DIR *, int);
113static void	 free_items(Char **, int);
114static int	 tsearch(Char *, COMMAND, int);
115static int	 recognize(Char *, Char *, int, int);
116static int	 is_prefix(Char *, Char *);
117static int	 is_suffix(Char *, Char *);
118static int	 ignored(Char *);
119
120/*
121 * Put this here so the binary can be patched with adb to enable file
122 * completion by default.  Filec controls completion, nobeep controls
123 * ringing the terminal bell on incomplete expansions.
124 */
125bool    filec = 0;
126
127static void
128cl_flush(struct cmdline *cl)
129{
130	size_t	i, len;
131	int	c;
132
133	if (cl->flags & CL_PROMPT) {
134		cl->flags &= ~CL_PROMPT;
135		printprompt();
136	}
137
138	if (cl->cursor < cl->len) {
139		for (; cl->cursor < cl->len; cl->cursor++)
140			cl_visc(cl, cl->buf[cl->cursor]);
141	} else if (cl->cursor > cl->len) {
142		len = cl->cursor - cl->len;
143		for (i = len; i > 0; i--) {
144			c = cl->buf[--cl->cursor];
145			if (c == '\t')
146				len += TABWIDTH - 1;
147			else if (iscntrl(c))
148				len++;	/* account for leading ^ */
149		}
150		for (i = 0; i < len; i++)
151			cl_putc(cl, '\b');
152		for (i = 0; i < len; i++)
153			cl_putc(cl, ' ');
154		for (i = 0; i < len; i++)
155			cl_putc(cl, '\b');
156		cl->cursor = cl->len;
157	}
158}
159
160static int
161cl_getc(struct cmdline *cl)
162{
163	ssize_t		n;
164	unsigned char	c;
165
166	for (;;) {
167		n = read(cl->fdin, &c, 1);
168		switch (n) {
169		case -1:
170			if (errno == EINTR)
171				continue;
172			/* FALLTHROUGH */
173		case 0:
174			return 0;
175		default:
176			return c & 0x7F;
177		}
178	}
179}
180
181static Char *
182cl_lastw(struct cmdline *cl)
183{
184	static Char		 word[BUFSIZ];
185	const unsigned char	*delimiters = " '\"\t;&<>()|^%";
186	Char			*cp;
187	size_t			 i;
188
189	for (i = cl->len; i > 0; i--)
190		if (strchr(delimiters, cl->buf[i - 1]) != NULL)
191			break;
192
193	cp = word;
194	for (; i < cl->len; i++)
195		*cp++ = cl->buf[i];
196	*cp = '\0';
197
198	return word;
199}
200
201static void
202cl_putc(struct cmdline *cl, int c)
203{
204	unsigned char	cc = c;
205
206	write(cl->fdout, &cc, 1);
207}
208
209static void
210cl_visc(struct cmdline *cl, int c)
211{
212#define	UNCNTRL(x)	((x) == 0x7F ? '?' : ((x) | 0x40))
213	int	i;
214
215	if (c == '\t') {
216		for (i = 0; i < TABWIDTH; i++)
217			cl_putc(cl, ' ');
218	} else if (c != '\n' && iscntrl(c)) {
219		cl_putc(cl, '^');
220		cl_putc(cl, UNCNTRL(c));
221	} else {
222		cl_putc(cl, c);
223	}
224}
225
226static int
227cl_abort(struct cmdline *cl, int c)
228{
229	cl_visc(cl, c);
230
231	/* Abort while/foreach loop prematurely. */
232	if (whyles) {
233		if (cl->istty)
234			setup_tty(0);
235		kill(getpid(), SIGINT);
236	}
237
238	cl_putc(cl, '\n');
239	cl->len = cl->cursor = 0;
240	cl->flags |= CL_PROMPT;
241
242	return 0;
243}
244
245static int
246cl_erasec(struct cmdline *cl, int c)
247{
248	if (cl->len > 0)
249		cl->len--;
250
251	return 0;
252}
253
254static int
255cl_erasew(struct cmdline *cl, int c)
256{
257	const unsigned char	*ws = " \t";
258
259	for (; cl->len > 0; cl->len--)
260		if (strchr(ws, cl->buf[cl->len - 1]) == NULL &&
261		    ((cl->flags & CL_ALTWERASE) == 0 ||
262		     isalpha(cl->buf[cl->len - 1])))
263			break;
264	for (; cl->len > 0; cl->len--)
265		if (strchr(ws, cl->buf[cl->len - 1]) != NULL ||
266		    ((cl->flags & CL_ALTWERASE) &&
267		     !isalpha(cl->buf[cl->len - 1])))
268			break;
269
270	return 0;
271}
272
273static void
274cl_beep(struct cmdline *cl)
275{
276	if (adrof(STRnobeep) == 0)
277		cl_putc(cl, '\007');
278}
279
280static int
281cl_insert(struct cmdline *cl, int c)
282{
283	if (cl->len == cl->size)
284		return 1;
285
286	cl->buf[cl->len++] = c;
287
288	if (c == '\n')
289		return 1;
290
291	return 0;
292}
293
294static int
295cl_kill(struct cmdline *cl, int c)
296{
297	cl->len = 0;
298
299	return 0;
300}
301
302static int
303cl_list(struct cmdline *cl, int c)
304{
305	Char	*word;
306	size_t	 len;
307
308	if (adrof(STRignoreeof) || cl->len > 0)
309		cl_visc(cl, c);
310
311	if (cl->len == 0)
312		return 1;
313
314	cl_putc(cl, '\n');
315	cl->cursor = 0;
316	cl->flags |= CL_PROMPT;
317
318	word = cl_lastw(cl);
319	len = Strlen(word);
320	tsearch(word, LIST, BUFSIZ - len - 1);	/* NUL */
321
322	return 0;
323}
324
325static int
326cl_literal(struct cmdline *cl, int c)
327{
328	int	literal;
329
330	literal = cl_getc(cl);
331	if (literal == '\n')
332		literal = '\r';
333	cl_insert(cl, literal);
334
335	return 0;
336}
337
338static int
339cl_recognize(struct cmdline *cl, int c)
340{
341	Char	*word;
342	size_t	 len;
343	int	 nitems;
344
345	if (cl->len == 0) {
346		cl_beep(cl);
347		return 0;
348	}
349
350	word = cl_lastw(cl);
351	len = Strlen(word);
352	nitems = tsearch(word, RECOGNIZE, BUFSIZ - len - 1);	/* NUL */
353	for (word += len; *word != '\0'; word++)
354		cl_insert(cl, *word);
355	if (nitems != 1)
356		cl_beep(cl);
357
358	return 0;
359}
360
361static int
362cl_reprint(struct cmdline *cl, int c)
363{
364	cl_visc(cl, c);
365	cl_putc(cl, '\n');
366	cl->cursor = 0;
367
368	return 0;
369}
370
371static int
372cl_status(struct cmdline *cl, int c)
373{
374	cl->cursor = 0;
375	if (cl->istty)
376		ioctl(cl->fdin, TIOCSTAT);
377
378	return 0;
379}
380
381const struct termios *
382setup_tty(int on)
383{
384	static struct termios	newtio, oldtio;
385
386	if (on) {
387		tcgetattr(SHIN, &oldtio);
388
389		newtio = oldtio;
390		newtio.c_lflag &= ~(ECHO | ICANON | ISIG);
391		newtio.c_cc[VEOL] = ESC;
392		newtio.c_cc[VLNEXT] = _POSIX_VDISABLE;
393		newtio.c_cc[VMIN] = 1;
394		newtio.c_cc[VTIME] = 0;
395	} else {
396		newtio = oldtio;
397	}
398
399	tcsetattr(SHIN, TCSADRAIN, &newtio);
400
401	/*
402	 * Since VLNEXT is disabled, restore its previous value in order to make
403	 * the key detectable.
404	 */
405	newtio.c_cc[VLNEXT] = oldtio.c_cc[VLNEXT];
406
407	return &newtio;
408}
409
410/*
411 * Concatenate src onto tail of des.
412 * Des is a string whose maximum length is count.
413 * Always null terminate.
414 */
415static void
416catn(Char *des, Char *src, int count)
417{
418    while (--count >= 0 && *des)
419	des++;
420    while (--count >= 0)
421	if ((*des++ = *src++) == 0)
422	    return;
423    *des = '\0';
424}
425
426/*
427 * Places Char's like strlcpy, but no special return value.
428 */
429static void
430copyn(Char *des, Char *src, int count)
431{
432    while (--count >= 0)
433	if ((*des++ = *src++) == 0)
434	    return;
435    *des = '\0';
436}
437
438static  Char
439filetype(Char *dir, Char *file)
440{
441    Char    path[PATH_MAX];
442    struct stat statb;
443
444    Strlcpy(path, dir, sizeof path/sizeof(Char));
445    catn(path, file, sizeof(path) / sizeof(Char));
446    if (lstat(short2str(path), &statb) == 0) {
447	switch (statb.st_mode & S_IFMT) {
448	case S_IFDIR:
449	    return ('/');
450
451	case S_IFLNK:
452	    if (stat(short2str(path), &statb) == 0 &&	/* follow it out */
453		S_ISDIR(statb.st_mode))
454		return ('>');
455	    else
456		return ('@');
457
458	case S_IFSOCK:
459	    return ('=');
460
461	default:
462	    if (statb.st_mode & 0111)
463		return ('*');
464	}
465    }
466    return (' ');
467}
468
469/*
470 * Print sorted down columns
471 */
472static void
473print_by_column(Char *dir, Char *items[], int count)
474{
475    struct winsize win;
476    int i, rows, r, c, maxwidth = 0, columns;
477
478    if (ioctl(SHOUT, TIOCGWINSZ, (ioctl_t) & win) == -1 || win.ws_col == 0)
479	win.ws_col = 80;
480    for (i = 0; i < count; i++)
481	maxwidth = maxwidth > (r = Strlen(items[i])) ? maxwidth : r;
482    maxwidth += 2;		/* for the file tag and space */
483    columns = win.ws_col / maxwidth;
484    if (columns == 0)
485	columns = 1;
486    rows = (count + (columns - 1)) / columns;
487    for (r = 0; r < rows; r++) {
488	for (c = 0; c < columns; c++) {
489	    i = c * rows + r;
490	    if (i < count) {
491		int w;
492
493		(void) fprintf(cshout, "%s", vis_str(items[i]));
494		(void) fputc(dir ? filetype(dir, items[i]) : ' ', cshout);
495		if (c < columns - 1) {	/* last column? */
496		    w = Strlen(items[i]) + 1;
497		    for (; w < maxwidth; w++)
498			(void) fputc(' ', cshout);
499		}
500	    }
501	}
502	(void) fputc('\r', cshout);
503	(void) fputc('\n', cshout);
504    }
505}
506
507/*
508 * Expand file name with possible tilde usage
509 *	~person/mumble
510 * expands to
511 *	home_directory_of_person/mumble
512 */
513static Char *
514tilde(Char *new, Char *old)
515{
516    Char *o, *p;
517    struct passwd *pw;
518    static Char person[40];
519
520    if (old[0] != '~') {
521	Strlcpy(new, old, PATH_MAX);
522	return new;
523    }
524
525    for (p = person, o = &old[1]; *o && *o != '/'; *p++ = *o++)
526	continue;
527    *p = '\0';
528    if (person[0] == '\0')
529	(void) Strlcpy(new, value(STRhome), PATH_MAX);
530    else {
531	pw = getpwnam(short2str(person));
532	if (pw == NULL)
533	    return (NULL);
534	(void) Strlcpy(new, str2short(pw->pw_dir), PATH_MAX);
535    }
536    (void) Strlcat(new, o, PATH_MAX);
537    return (new);
538}
539
540/*
541 * Parse full path in file into 2 parts: directory and file names
542 * Should leave final slash (/) at end of dir.
543 */
544static void
545extract_dir_and_name(Char *path, Char *dir, Char *name)
546{
547    Char *p;
548
549    p = Strrchr(path, '/');
550    if (p == NULL) {
551	copyn(name, path, MAXNAMLEN);
552	dir[0] = '\0';
553    }
554    else {
555	copyn(name, ++p, MAXNAMLEN);
556	copyn(dir, path, p - path);
557    }
558}
559
560static Char *
561getentry(DIR *dir_fd, int looking_for_lognames)
562{
563    struct passwd *pw;
564    struct dirent *dirp;
565
566    if (looking_for_lognames) {
567	if ((pw = getpwent()) == NULL)
568	    return (NULL);
569	return (str2short(pw->pw_name));
570    }
571    if ((dirp = readdir(dir_fd)) != NULL)
572	return (str2short(dirp->d_name));
573    return (NULL);
574}
575
576static void
577free_items(Char **items, int numitems)
578{
579    int i;
580
581    for (i = 0; i < numitems; i++)
582	free(items[i]);
583    free(items);
584}
585
586#define FREE_ITEMS(items) { \
587	sigset_t sigset, osigset;\
588\
589	sigemptyset(&sigset);\
590	sigaddset(&sigset, SIGINT);\
591	sigprocmask(SIG_BLOCK, &sigset, &osigset);\
592	free_items(items, numitems);\
593	sigprocmask(SIG_SETMASK, &osigset, NULL);\
594}
595
596/*
597 * Perform a RECOGNIZE or LIST command on string "word".
598 */
599static int
600tsearch(Char *word, COMMAND command, int max_word_length)
601{
602    DIR *dir_fd;
603    int numitems = 0, ignoring = TRUE, nignored = 0;
604    int name_length, looking_for_lognames;
605    Char    tilded_dir[PATH_MAX], dir[PATH_MAX];
606    Char    name[MAXNAMLEN + 1], extended_name[MAXNAMLEN + 1];
607    Char   *entry;
608    Char   **items = NULL;
609    size_t  maxitems = 0;
610
611    looking_for_lognames = (*word == '~') && (Strchr(word, '/') == NULL);
612    if (looking_for_lognames) {
613	(void) setpwent();
614	copyn(name, &word[1], MAXNAMLEN);	/* name sans ~ */
615	dir_fd = NULL;
616    }
617    else {
618	extract_dir_and_name(word, dir, name);
619	if (tilde(tilded_dir, dir) == 0)
620	    return (0);
621	dir_fd = opendir(*tilded_dir ? short2str(tilded_dir) : ".");
622	if (dir_fd == NULL)
623	    return (0);
624    }
625
626again:				/* search for matches */
627    name_length = Strlen(name);
628    for (numitems = 0; (entry = getentry(dir_fd, looking_for_lognames)) != NULL;) {
629	if (!is_prefix(name, entry))
630	    continue;
631	/* Don't match . files on null prefix match */
632	if (name_length == 0 && entry[0] == '.' &&
633	    !looking_for_lognames)
634	    continue;
635	if (command == LIST) {
636	    if (numitems >= maxitems) {
637		maxitems += 1024;
638		items = xreallocarray(items, maxitems, sizeof(*items));
639	    }
640	    items[numitems] = xreallocarray(NULL, (Strlen(entry) + 1), sizeof(Char));
641	    copyn(items[numitems], entry, MAXNAMLEN);
642	    numitems++;
643	}
644	else {			/* RECOGNIZE command */
645	    if (ignoring && ignored(entry))
646		nignored++;
647	    else if (recognize(extended_name,
648			       entry, name_length, ++numitems))
649		break;
650	}
651    }
652    if (ignoring && numitems == 0 && nignored > 0) {
653	ignoring = FALSE;
654	nignored = 0;
655	if (looking_for_lognames)
656	    (void) setpwent();
657	else
658	    rewinddir(dir_fd);
659	goto again;
660    }
661
662    if (looking_for_lognames)
663	(void) endpwent();
664    else
665	(void) closedir(dir_fd);
666    if (numitems == 0)
667	return (0);
668    if (command == RECOGNIZE) {
669	if (looking_for_lognames)
670	    copyn(word, STRtilde, 1);
671	else
672	    /* put back dir part */
673	    copyn(word, dir, max_word_length);
674	/* add extended name */
675	catn(word, extended_name, max_word_length);
676	return (numitems);
677    }
678    else {			/* LIST */
679	qsort(items, numitems, sizeof(*items), sortscmp);
680	print_by_column(looking_for_lognames ? NULL : tilded_dir,
681			items, numitems);
682	if (items != NULL)
683	    FREE_ITEMS(items);
684    }
685    return (0);
686}
687
688/*
689 * Object: extend what user typed up to an ambiguity.
690 * Algorithm:
691 * On first match, copy full entry (assume it'll be the only match)
692 * On subsequent matches, shorten extended_name to the first
693 * Character mismatch between extended_name and entry.
694 * If we shorten it back to the prefix length, stop searching.
695 */
696static int
697recognize(Char *extended_name, Char *entry, int name_length, int numitems)
698{
699    if (numitems == 1)		/* 1st match */
700	copyn(extended_name, entry, MAXNAMLEN);
701    else {			/* 2nd & subsequent matches */
702	Char *x, *ent;
703	int len = 0;
704
705	x = extended_name;
706	for (ent = entry; *x && *x == *ent++; x++, len++)
707	    continue;
708	*x = '\0';		/* Shorten at 1st Char diff */
709	if (len == name_length)	/* Ambiguous to prefix? */
710	    return (-1);	/* So stop now and save time */
711    }
712    return (0);
713}
714
715/*
716 * Return true if check matches initial Chars in template.
717 * This differs from PWB imatch in that if check is null
718 * it matches anything.
719 */
720static int
721is_prefix(Char *check, Char *template)
722{
723    do
724	if (*check == 0)
725	    return (TRUE);
726    while (*check++ == *template++);
727    return (FALSE);
728}
729
730/*
731 *  Return true if the Chars in template appear at the
732 *  end of check, I.e., are its suffix.
733 */
734static int
735is_suffix(Char *check, Char *template)
736{
737    Char *c, *t;
738
739    for (c = check; *c++;)
740	continue;
741    for (t = template; *t++;)
742	continue;
743    for (;;) {
744	if (t == template)
745	    return 1;
746	if (c == check || *--t != *--c)
747	    return 0;
748    }
749}
750
751int
752tenex(Char *inputline, int inputline_size)
753{
754	static struct {
755		int	(*fn)(struct cmdline *, int);
756		int	idx;
757	}			 keys[] = {
758		{ cl_abort,	VINTR },
759		{ cl_erasec,	VERASE },
760		{ cl_erasew,	VWERASE },
761		{ cl_kill,	VKILL },
762		{ cl_list,	VEOF },
763		{ cl_literal,	VLNEXT },
764		{ cl_recognize,	VEOL },
765		{ cl_reprint,	VREPRINT },
766		{ cl_status,	VSTATUS },
767		{ cl_insert,	-1 }
768	};
769	unsigned char		 buf[BUFSIZ];
770	const struct termios	*tio;
771	struct cmdline		 cl;
772	size_t			 i;
773	int			 c, ret;
774
775	memset(&cl, 0, sizeof(cl));
776	cl.fdin = SHIN;
777	cl.fdout = SHOUT;
778	cl.istty = isatty(SHIN);
779
780	if (cl.istty)
781		tio = setup_tty(1);
782
783	cl.buf = buf;
784	cl.size = sizeof(buf);
785	if (inputline_size < cl.size)
786		cl.size = inputline_size;
787	if (cl.istty && tio->c_lflag & ALTWERASE)
788		cl.flags |= CL_ALTWERASE;
789	if (needprompt) {
790		needprompt = 0;
791		cl.flags |= CL_PROMPT;
792		cl_flush(&cl);
793	}
794
795	for (;;) {
796		if ((c = cl_getc(&cl)) == 0)
797			break;
798
799		for (i = 0; keys[i].idx >= 0; i++)
800			if (cl.istty && CCEQ(tio->c_cc[keys[i].idx], c))
801				break;
802		ret = keys[i].fn(&cl, c);
803		cl_flush(&cl);
804		if (ret)
805			break;
806	}
807
808	if (cl.istty)
809		setup_tty(0);
810
811	for (i = 0; i < cl.len; i++)
812		inputline[i] = cl.buf[i];
813	/*
814	 * NUL-terminating the buffer implies that it contains a complete
815	 * command ready to be executed. Therefore, don't terminate if the
816	 * buffer is full since more characters must be read in order to form a
817	 * complete command.
818	 */
819	if (i < cl.size)
820		inputline[i] = '\0';
821
822	return cl.len;
823}
824
825static int
826ignored(Char *entry)
827{
828    struct varent *vp;
829    Char **cp;
830
831    if ((vp = adrof(STRfignore)) == NULL || (cp = vp->vec) == NULL)
832	return (FALSE);
833    for (; *cp != NULL; cp++)
834	if (is_suffix(entry, *cp))
835	    return (TRUE);
836    return (FALSE);
837}
838