xref: /netbsd-src/bin/csh/file.c (revision 509d26c5fb01645d5dfb1d910cec099cd0f8d510)
1*509d26c5Snia /* $NetBSD: file.c,v 1.34 2024/04/24 15:49:03 nia Exp $ */
249f0ad86Scgd 
361f28255Scgd /*-
4cee2bad8Smycroft  * Copyright (c) 1980, 1991, 1993
5cee2bad8Smycroft  *	The Regents of the University of California.  All rights reserved.
661f28255Scgd  *
761f28255Scgd  * Redistribution and use in source and binary forms, with or without
861f28255Scgd  * modification, are permitted provided that the following conditions
961f28255Scgd  * are met:
1061f28255Scgd  * 1. Redistributions of source code must retain the above copyright
1161f28255Scgd  *    notice, this list of conditions and the following disclaimer.
1261f28255Scgd  * 2. Redistributions in binary form must reproduce the above copyright
1361f28255Scgd  *    notice, this list of conditions and the following disclaimer in the
1461f28255Scgd  *    documentation and/or other materials provided with the distribution.
15b5b29542Sagc  * 3. Neither the name of the University nor the names of its contributors
1661f28255Scgd  *    may be used to endorse or promote products derived from this software
1761f28255Scgd  *    without specific prior written permission.
1861f28255Scgd  *
1961f28255Scgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2061f28255Scgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2161f28255Scgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2261f28255Scgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2361f28255Scgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2461f28255Scgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2561f28255Scgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2661f28255Scgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2761f28255Scgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2861f28255Scgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2961f28255Scgd  * SUCH DAMAGE.
3061f28255Scgd  */
3161f28255Scgd 
328ea378c6Schristos #include <sys/cdefs.h>
3361f28255Scgd #ifndef lint
3449f0ad86Scgd #if 0
3549f0ad86Scgd static char sccsid[] = "@(#)file.c	8.2 (Berkeley) 3/19/94";
3649f0ad86Scgd #else
37*509d26c5Snia __RCSID("$NetBSD: file.c,v 1.34 2024/04/24 15:49:03 nia Exp $");
3849f0ad86Scgd #endif
3961f28255Scgd #endif /* not lint */
4061f28255Scgd 
4161f28255Scgd #ifdef FILEC
4261f28255Scgd 
4361f28255Scgd #include <sys/ioctl.h>
44b771e65bSwiz #include <sys/param.h>
4561f28255Scgd #include <sys/stat.h>
46f1a39637Sitohy #include <sys/tty.h>
47b771e65bSwiz 
4861f28255Scgd #include <dirent.h>
4961f28255Scgd #include <pwd.h>
50b771e65bSwiz #include <termios.h>
5118158540Swiz #include <stdarg.h>
5261f28255Scgd #include <stdlib.h>
5361f28255Scgd #include <unistd.h>
54b771e65bSwiz 
55cee2bad8Smycroft #ifndef SHORT_STRINGS
56cee2bad8Smycroft #include <string.h>
57cee2bad8Smycroft #endif /* SHORT_STRINGS */
58b771e65bSwiz 
5961f28255Scgd #include "csh.h"
6061f28255Scgd #include "extern.h"
6161f28255Scgd 
6261f28255Scgd /*
6361f28255Scgd  * Tenex style file name recognition, .. and more.
6461f28255Scgd  * History:
6561f28255Scgd  *	Author: Ken Greer, Sept. 1975, CMU.
6661f28255Scgd  *	Finally got around to adding to the Cshell., Ken Greer, Dec. 1981.
6761f28255Scgd  */
6861f28255Scgd 
6961f28255Scgd #define ON	1
7061f28255Scgd #define OFF	0
7161f28255Scgd #ifndef TRUE
7261f28255Scgd #define TRUE 1
7361f28255Scgd #endif
7461f28255Scgd #ifndef FALSE
7561f28255Scgd #define FALSE 0
7661f28255Scgd #endif
7761f28255Scgd 
7861f28255Scgd #define ESC '\033'
7961f28255Scgd 
8061f28255Scgd typedef enum {
8161f28255Scgd     LIST, RECOGNIZE
8261f28255Scgd }       COMMAND;
8361f28255Scgd 
84b771e65bSwiz static void setup_tty(int);
85b771e65bSwiz static void back_to_col_1(void);
86b771e65bSwiz static int pushback(Char *);
8737e39248Schristos static void catn(Char *, Char *, size_t);
8837e39248Schristos static void copyn(Char *, Char *, size_t);
89b771e65bSwiz static Char filetype(Char *, Char *);
9037e39248Schristos static void print_by_column(Char *, Char *[], size_t);
91b771e65bSwiz static Char *tilde(Char *, Char *);
92b771e65bSwiz static void retype(void);
93b771e65bSwiz static void beep(void);
94b771e65bSwiz static void print_recognized_stuff(Char *);
95b771e65bSwiz static void extract_dir_and_name(Char *, Char *, Char *);
96b771e65bSwiz static Char *getentry(DIR *, int);
97a7c23d87Schristos static void free_items(Char **, size_t);
9837e39248Schristos static size_t tsearch(Char *, COMMAND, size_t);
9937e39248Schristos static int recognize(Char *, Char *, size_t, size_t);
100b771e65bSwiz static int is_prefix(Char *, Char *);
101b771e65bSwiz static int is_suffix(Char *, Char *);
102b771e65bSwiz static int ignored(Char *);
10361f28255Scgd 
10461f28255Scgd /*
10561f28255Scgd  * Put this here so the binary can be patched with adb to enable file
10661f28255Scgd  * completion by default.  Filec controls completion, nobeep controls
10761f28255Scgd  * ringing the terminal bell on incomplete expansions.
10861f28255Scgd  */
109b79c2ef2Schristos int filec = 0;
11061f28255Scgd 
11161f28255Scgd static void
setup_tty(int on)112b771e65bSwiz setup_tty(int on)
11361f28255Scgd {
114bec9fc50Scgd     struct termios tchars;
11561f28255Scgd 
11661f28255Scgd     (void)tcgetattr(SHIN, &tchars);
117cee2bad8Smycroft 
118cee2bad8Smycroft     if (on) {
11961f28255Scgd 	tchars.c_cc[VEOL] = ESC;
12061f28255Scgd 	if (tchars.c_lflag & ICANON)
121af75045fSpk 	    on = TCSADRAIN;
12261f28255Scgd 	else {
12361f28255Scgd 	    tchars.c_lflag |= ICANON;
124cee2bad8Smycroft 	    on = TCSAFLUSH;
12561f28255Scgd 	}
12661f28255Scgd     }
12761f28255Scgd     else {
12861f28255Scgd 	tchars.c_cc[VEOL] = _POSIX_VDISABLE;
129cee2bad8Smycroft 	on = TCSADRAIN;
13061f28255Scgd     }
131cee2bad8Smycroft 
132cee2bad8Smycroft     (void)tcsetattr(SHIN, on, &tchars);
13361f28255Scgd }
13461f28255Scgd 
13561f28255Scgd /*
13661f28255Scgd  * Move back to beginning of current line
13761f28255Scgd  */
13861f28255Scgd static void
back_to_col_1(void)139b771e65bSwiz back_to_col_1(void)
14061f28255Scgd {
14161f28255Scgd     struct termios tty, tty_normal;
142b3df6303Skleink     sigset_t nsigset, osigset;
14361f28255Scgd 
144b3df6303Skleink     sigemptyset(&nsigset);
145b3df6303Skleink     (void)sigaddset(&nsigset, SIGINT);
146b3df6303Skleink     (void)sigprocmask(SIG_BLOCK, &nsigset, &osigset);
14761f28255Scgd     (void)tcgetattr(SHOUT, &tty);
14861f28255Scgd     tty_normal = tty;
14961f28255Scgd     tty.c_iflag &= ~INLCR;
15061f28255Scgd     tty.c_oflag &= ~ONLCR;
15108051348Schristos     (void)tcsetattr(SHOUT, TCSADRAIN, &tty);
15261f28255Scgd     (void)write(SHOUT, "\r", 1);
15308051348Schristos     (void)tcsetattr(SHOUT, TCSADRAIN, &tty_normal);
1545924694dSmycroft     (void)sigprocmask(SIG_SETMASK, &osigset, NULL);
15561f28255Scgd }
15661f28255Scgd 
15761f28255Scgd /*
15861f28255Scgd  * Push string contents back into tty queue
15961f28255Scgd  */
160e50ba7eaSitohy static int
pushback(Char * string)161b771e65bSwiz pushback(Char *string)
16261f28255Scgd {
16361f28255Scgd     struct termios tty, tty_normal;
16403561a04Schristos     char buf[64], svchars[sizeof(buf)];
165b3df6303Skleink     sigset_t nsigset, osigset;
166b771e65bSwiz     Char *p;
16703561a04Schristos     size_t bufidx, i, len_str, nbuf, nsv, onsv, retrycnt;
16861f28255Scgd     char c;
16961f28255Scgd 
170b771e65bSwiz     nsv = 0;
171b3df6303Skleink     sigemptyset(&nsigset);
172b3df6303Skleink     (void)sigaddset(&nsigset, SIGINT);
173b3df6303Skleink     (void)sigprocmask(SIG_BLOCK, &nsigset, &osigset);
17461f28255Scgd     (void)tcgetattr(SHOUT, &tty);
17561f28255Scgd     tty_normal = tty;
17661f28255Scgd     tty.c_lflag &= ~(ECHOKE | ECHO | ECHOE | ECHOK | ECHONL | ECHOPRT | ECHOCTL);
177e50ba7eaSitohy     /* FIONREAD works only in noncanonical mode. */
178e50ba7eaSitohy     tty.c_lflag &= ~ICANON;
179e50ba7eaSitohy     tty.c_cc[VMIN] = 0;
18008051348Schristos     (void)tcsetattr(SHOUT, TCSADRAIN, &tty);
18161f28255Scgd 
182e50ba7eaSitohy     for (retrycnt = 5; ; retrycnt--) {
183e50ba7eaSitohy 	/*
184e50ba7eaSitohy 	 * Push back characters.
185e50ba7eaSitohy 	 */
18637e39248Schristos 	for (p = string; (c = (char)*p) != '\0'; p++)
18761f28255Scgd 	    (void)ioctl(SHOUT, TIOCSTI, (ioctl_t) &c);
188e50ba7eaSitohy 	for (i = 0; i < nsv; i++)
189e50ba7eaSitohy 	    (void)ioctl(SHOUT, TIOCSTI, (ioctl_t) &svchars[i]);
190e50ba7eaSitohy 
191e50ba7eaSitohy 	if (retrycnt == 0)
192e50ba7eaSitohy 	    break;		/* give up salvaging characters */
193e50ba7eaSitohy 
19437e39248Schristos 	len_str = (size_t)(p - string);
195e50ba7eaSitohy 
196e50ba7eaSitohy 	if (ioctl(SHOUT, FIONREAD, (ioctl_t) &nbuf) ||
197e50ba7eaSitohy 	    nbuf <= len_str + nsv ||	/* The string fit. */
19803561a04Schristos 	    nbuf > sizeof(buf))		/* For future binary compatibility
199e50ba7eaSitohy 					   (and safety). */
200e50ba7eaSitohy 	    break;
201e50ba7eaSitohy 
202e50ba7eaSitohy 	/*
203e50ba7eaSitohy 	 * User has typed characters before the pushback finished.
204e50ba7eaSitohy 	 * Salvage the characters.
205e50ba7eaSitohy 	 */
206e50ba7eaSitohy 
207e50ba7eaSitohy 	/* This read() should be in noncanonical mode. */
20803561a04Schristos 	if (read(SHOUT, &buf, nbuf) != (ssize_t)nbuf)
209e50ba7eaSitohy 	    continue;		/* hangup? */
210e50ba7eaSitohy 
211e50ba7eaSitohy 	onsv = nsv;
212e50ba7eaSitohy 	for (bufidx = 0, i = 0; bufidx < nbuf; bufidx++, i++) {
213e50ba7eaSitohy 	    c = buf[bufidx];
214e50ba7eaSitohy 	    if ((i < len_str) ? c != (char)string[i] :
215e50ba7eaSitohy 			(i < len_str + onsv) ? c != svchars[i - len_str] : 1) {
216e50ba7eaSitohy 		/* Salvage a character. */
217e50ba7eaSitohy 		if (nsv < (int)(sizeof svchars / sizeof svchars[0])) {
218e50ba7eaSitohy 		    svchars[nsv++] = c;
219e50ba7eaSitohy 		    i--;	/* try this comparison with the next char */
220e50ba7eaSitohy 		} else
221f1a39637Sitohy 		    break;	/* too many */
222e50ba7eaSitohy 	    }
223e50ba7eaSitohy 	}
224e50ba7eaSitohy     }
225e50ba7eaSitohy 
226e50ba7eaSitohy #if 1
227e50ba7eaSitohy     /*
228e50ba7eaSitohy      * XXX  Is this a bug or a feature of kernel tty driver?
229e50ba7eaSitohy      *
230e50ba7eaSitohy      * FIONREAD in canonical mode does not return correct byte count
231e50ba7eaSitohy      * in tty input queue, but this is required to avoid unwanted echo.
232e50ba7eaSitohy      */
233e50ba7eaSitohy     tty.c_lflag |= ICANON;
234e50ba7eaSitohy     (void)tcsetattr(SHOUT, TCSADRAIN, &tty);
235e50ba7eaSitohy     (void)ioctl(SHOUT, FIONREAD, (ioctl_t) &i);
236e50ba7eaSitohy #endif
23708051348Schristos     (void)tcsetattr(SHOUT, TCSADRAIN, &tty_normal);
2385924694dSmycroft     (void)sigprocmask(SIG_SETMASK, &osigset, NULL);
239e50ba7eaSitohy 
24037e39248Schristos     return (int)nsv;
24161f28255Scgd }
24261f28255Scgd 
24361f28255Scgd /*
24461f28255Scgd  * Concatenate src onto tail of des.
24561f28255Scgd  * Des is a string whose maximum length is count.
24661f28255Scgd  * Always null terminate.
24761f28255Scgd  */
24861f28255Scgd static void
catn(Char * des,Char * src,size_t count)24937e39248Schristos catn(Char *des, Char *src, size_t count)
25061f28255Scgd {
25137e39248Schristos     while (count-- > 0 && *des)
25261f28255Scgd 	des++;
25337e39248Schristos     while (count-- > 0)
25461f28255Scgd 	if ((*des++ = *src++) == 0)
25561f28255Scgd 	    return;
25661f28255Scgd     *des = '\0';
25761f28255Scgd }
25861f28255Scgd 
25961f28255Scgd /*
26061f28255Scgd  * Like strncpy but always leave room for trailing \0
26161f28255Scgd  * and always null terminate.
26261f28255Scgd  */
26361f28255Scgd static void
copyn(Char * des,Char * src,size_t count)26437e39248Schristos copyn(Char *des, Char *src, size_t count)
26561f28255Scgd {
26637e39248Schristos     while (count-- > 0)
26761f28255Scgd 	if ((*des++ = *src++) == 0)
26861f28255Scgd 	    return;
26961f28255Scgd     *des = '\0';
27061f28255Scgd }
27161f28255Scgd 
27261f28255Scgd static Char
filetype(Char * dir,Char * file)273b771e65bSwiz filetype(Char *dir, Char *file)
27461f28255Scgd {
27561f28255Scgd     struct stat statb;
276b771e65bSwiz     Char path[MAXPATHLEN];
27761f28255Scgd 
27861f28255Scgd     catn(Strcpy(path, dir), file, sizeof(path) / sizeof(Char));
27961f28255Scgd     if (lstat(short2str(path), &statb) == 0) {
28061f28255Scgd 	switch (statb.st_mode & S_IFMT) {
28161f28255Scgd 	case S_IFDIR:
28261f28255Scgd 	    return ('/');
28361f28255Scgd 	case S_IFLNK:
28461f28255Scgd 	    if (stat(short2str(path), &statb) == 0 &&	/* follow it out */
28561f28255Scgd 		S_ISDIR(statb.st_mode))
28661f28255Scgd 		return ('>');
28761f28255Scgd 	    else
28861f28255Scgd 		return ('@');
28961f28255Scgd 	case S_IFSOCK:
29061f28255Scgd 	    return ('=');
29161f28255Scgd 	default:
29261f28255Scgd 	    if (statb.st_mode & 0111)
29361f28255Scgd 		return ('*');
29461f28255Scgd 	}
29561f28255Scgd     }
29661f28255Scgd     return (' ');
29761f28255Scgd }
29861f28255Scgd 
29961f28255Scgd static struct winsize win;
30061f28255Scgd 
30161f28255Scgd /*
30261f28255Scgd  * Print sorted down columns
30361f28255Scgd  */
30461f28255Scgd static void
print_by_column(Char * dir,Char * items[],size_t count)30537e39248Schristos print_by_column(Char *dir, Char *items[], size_t count)
30661f28255Scgd {
30737e39248Schristos     size_t c, columns, i, maxwidth, r, rows;
308b771e65bSwiz 
309b771e65bSwiz     maxwidth = 0;
31061f28255Scgd 
31161f28255Scgd     if (ioctl(SHOUT, TIOCGWINSZ, (ioctl_t) & win) < 0 || win.ws_col == 0)
31261f28255Scgd 	win.ws_col = 80;
31361f28255Scgd     for (i = 0; i < count; i++)
31461f28255Scgd 	maxwidth = maxwidth > (r = Strlen(items[i])) ? maxwidth : r;
31561f28255Scgd     maxwidth += 2;		/* for the file tag and space */
31661f28255Scgd     columns = win.ws_col / maxwidth;
31761f28255Scgd     if (columns == 0)
31861f28255Scgd 	columns = 1;
31961f28255Scgd     rows = (count + (columns - 1)) / columns;
32061f28255Scgd     for (r = 0; r < rows; r++) {
32161f28255Scgd 	for (c = 0; c < columns; c++) {
32261f28255Scgd 	    i = c * rows + r;
32361f28255Scgd 	    if (i < count) {
32437e39248Schristos 		size_t w;
32561f28255Scgd 
326cee2bad8Smycroft 		(void)fprintf(cshout, "%s", vis_str(items[i]));
327cee2bad8Smycroft 		(void)fputc(dir ? filetype(dir, items[i]) : ' ', cshout);
32861f28255Scgd 		if (c < columns - 1) {	/* last column? */
32961f28255Scgd 		    w = Strlen(items[i]) + 1;
33061f28255Scgd 		    for (; w < maxwidth; w++)
331cee2bad8Smycroft 			(void) fputc(' ', cshout);
33261f28255Scgd 		}
33361f28255Scgd 	    }
33461f28255Scgd 	}
335cee2bad8Smycroft 	(void)fputc('\r', cshout);
336cee2bad8Smycroft 	(void)fputc('\n', cshout);
33761f28255Scgd     }
33861f28255Scgd }
33961f28255Scgd 
34061f28255Scgd /*
34161f28255Scgd  * Expand file name with possible tilde usage
34261f28255Scgd  *	~person/mumble
34361f28255Scgd  * expands to
34461f28255Scgd  *	home_directory_of_person/mumble
34561f28255Scgd  */
34661f28255Scgd static Char *
tilde(Char * new,Char * old)347b771e65bSwiz tilde(Char *new, Char *old)
34861f28255Scgd {
34961f28255Scgd     static Char person[40];
350b771e65bSwiz     struct passwd *pw;
351b771e65bSwiz     Char *o, *p;
35261f28255Scgd 
35361f28255Scgd     if (old[0] != '~')
35461f28255Scgd 	return (Strcpy(new, old));
35561f28255Scgd 
356cee2bad8Smycroft     for (p = person, o = &old[1]; *o && *o != '/'; *p++ = *o++)
357cee2bad8Smycroft 	continue;
35861f28255Scgd     *p = '\0';
35961f28255Scgd     if (person[0] == '\0')
36061f28255Scgd 	(void)Strcpy(new, value(STRhome));
36161f28255Scgd     else {
36261f28255Scgd 	pw = getpwnam(short2str(person));
36361f28255Scgd 	if (pw == NULL)
36461f28255Scgd 	    return (NULL);
36561f28255Scgd 	(void)Strcpy(new, str2short(pw->pw_dir));
36661f28255Scgd     }
36761f28255Scgd     (void)Strcat(new, o);
36861f28255Scgd     return (new);
36961f28255Scgd }
37061f28255Scgd 
37161f28255Scgd /*
37261f28255Scgd  * Cause pending line to be printed
37361f28255Scgd  */
37461f28255Scgd static void
retype(void)375b771e65bSwiz retype(void)
37661f28255Scgd {
37761f28255Scgd     struct termios tty;
37861f28255Scgd 
37961f28255Scgd     (void)tcgetattr(SHOUT, &tty);
38061f28255Scgd     tty.c_lflag |= PENDIN;
38108051348Schristos     (void)tcsetattr(SHOUT, TCSADRAIN, &tty);
38261f28255Scgd }
38361f28255Scgd 
38461f28255Scgd static void
beep(void)385b771e65bSwiz beep(void)
38661f28255Scgd {
38761f28255Scgd     if (adrof(STRnobeep) == 0)
38861f28255Scgd 	(void)write(SHOUT, "\007", 1);
38961f28255Scgd }
39061f28255Scgd 
39161f28255Scgd /*
39261f28255Scgd  * Erase that silly ^[ and
39361f28255Scgd  * print the recognized part of the string
39461f28255Scgd  */
39561f28255Scgd static void
print_recognized_stuff(Char * recognized_part)396b771e65bSwiz print_recognized_stuff(Char *recognized_part)
39761f28255Scgd {
39861f28255Scgd     /* An optimized erasing of that silly ^[ */
399cee2bad8Smycroft     (void)fputc('\b', cshout);
400cee2bad8Smycroft     (void)fputc('\b', cshout);
40161f28255Scgd     switch (Strlen(recognized_part)) {
40261f28255Scgd     case 0:			/* erase two Characters: ^[ */
403cee2bad8Smycroft 	(void)fputc(' ', cshout);
404cee2bad8Smycroft 	(void)fputc(' ', cshout);
405cee2bad8Smycroft 	(void)fputc('\b', cshout);
406cee2bad8Smycroft 	(void)fputc('\b', cshout);
40761f28255Scgd 	break;
40861f28255Scgd     case 1:			/* overstrike the ^, erase the [ */
409cee2bad8Smycroft 	(void)fprintf(cshout, "%s", vis_str(recognized_part));
410cee2bad8Smycroft 	(void)fputc(' ', cshout);
411cee2bad8Smycroft 	(void)fputc('\b', cshout);
41261f28255Scgd 	break;
41361f28255Scgd     default:			/* overstrike both Characters ^[ */
414cee2bad8Smycroft 	(void)fprintf(cshout, "%s", vis_str(recognized_part));
41561f28255Scgd 	break;
41661f28255Scgd     }
417cee2bad8Smycroft     (void)fflush(cshout);
41861f28255Scgd }
41961f28255Scgd 
42061f28255Scgd /*
42161f28255Scgd  * Parse full path in file into 2 parts: directory and file names
42261f28255Scgd  * Should leave final slash (/) at end of dir.
42361f28255Scgd  */
42461f28255Scgd static void
extract_dir_and_name(Char * path,Char * dir,Char * name)425b771e65bSwiz extract_dir_and_name(Char *path, Char *dir, Char *name)
42661f28255Scgd {
42776adbe2bStls     Char *p;
42861f28255Scgd 
42961f28255Scgd     p = Strrchr(path, '/');
43061f28255Scgd     if (p == NULL) {
43161f28255Scgd 	copyn(name, path, MAXNAMLEN);
43261f28255Scgd 	dir[0] = '\0';
43361f28255Scgd     }
43461f28255Scgd     else {
43561f28255Scgd 	copyn(name, ++p, MAXNAMLEN);
43637e39248Schristos 	copyn(dir, path, (size_t)(p - path));
43761f28255Scgd     }
43861f28255Scgd }
43961f28255Scgd 
44061f28255Scgd static Char *
getentry(DIR * dir_fd,int looking_for_lognames)441b771e65bSwiz getentry(DIR *dir_fd, int looking_for_lognames)
44261f28255Scgd {
44376adbe2bStls     struct dirent *dirp;
444b771e65bSwiz     struct passwd *pw;
44561f28255Scgd 
44661f28255Scgd     if (looking_for_lognames) {
44761f28255Scgd 	if ((pw = getpwent()) == NULL)
44861f28255Scgd 	    return (NULL);
44961f28255Scgd 	return (str2short(pw->pw_name));
45061f28255Scgd     }
451cee2bad8Smycroft     if ((dirp = readdir(dir_fd)) != NULL)
45261f28255Scgd 	return (str2short(dirp->d_name));
45361f28255Scgd     return (NULL);
45461f28255Scgd }
45561f28255Scgd 
45661f28255Scgd static void
free_items(Char ** items,size_t numitems)457a7c23d87Schristos free_items(Char **items, size_t numitems)
45861f28255Scgd {
459a7c23d87Schristos     size_t i;
46061f28255Scgd 
461a7c23d87Schristos     for (i = 0; i < numitems; i++)
4621767ce60Schristos 	free(items[i]);
4631767ce60Schristos     free(items);
46461f28255Scgd }
46561f28255Scgd 
466a7c23d87Schristos #define FREE_ITEMS(items, numitems) { \
467b3df6303Skleink 	sigset_t nsigset, osigset;\
46861f28255Scgd \
469b3df6303Skleink 	sigemptyset(&nsigset);\
470b3df6303Skleink 	(void) sigaddset(&nsigset, SIGINT);\
471b3df6303Skleink 	(void) sigprocmask(SIG_BLOCK, &nsigset, &osigset);\
472a7c23d87Schristos 	free_items(items, numitems);\
4735924694dSmycroft 	(void) sigprocmask(SIG_SETMASK, &osigset, NULL);\
47461f28255Scgd }
47561f28255Scgd 
47661f28255Scgd /*
47761f28255Scgd  * Perform a RECOGNIZE or LIST command on string "word".
47861f28255Scgd  */
47937e39248Schristos static size_t
tsearch(Char * word,COMMAND command,size_t max_word_length)48037e39248Schristos tsearch(Char *word, COMMAND command, size_t max_word_length)
48161f28255Scgd {
482b771e65bSwiz     Char dir[MAXPATHLEN + 1], extended_name[MAXNAMLEN + 1];
483b771e65bSwiz     Char name[MAXNAMLEN + 1], tilded_dir[MAXPATHLEN + 1];
48476adbe2bStls     DIR *dir_fd;
48561f28255Scgd     Char *entry;
48637e39248Schristos     int ignoring, looking_for_lognames;
48737e39248Schristos     size_t name_length, nignored, numitems;
488a7c23d87Schristos     Char **items = NULL;
489a7c23d87Schristos     size_t maxitems = 0;
490b771e65bSwiz 
491b771e65bSwiz     numitems = 0;
492b771e65bSwiz     ignoring = TRUE;
493b771e65bSwiz     nignored = 0;
49461f28255Scgd 
49561f28255Scgd     looking_for_lognames = (*word == '~') && (Strchr(word, '/') == NULL);
49661f28255Scgd     if (looking_for_lognames) {
49761f28255Scgd 	(void)setpwent();
49861f28255Scgd 	copyn(name, &word[1], MAXNAMLEN);	/* name sans ~ */
49961f28255Scgd 	dir_fd = NULL;
50061f28255Scgd     }
50161f28255Scgd     else {
50261f28255Scgd 	extract_dir_and_name(word, dir, name);
50361f28255Scgd 	if (tilde(tilded_dir, dir) == 0)
50461f28255Scgd 	    return (0);
50561f28255Scgd 	dir_fd = opendir(*tilded_dir ? short2str(tilded_dir) : ".");
50661f28255Scgd 	if (dir_fd == NULL)
50761f28255Scgd 	    return (0);
50861f28255Scgd     }
50961f28255Scgd 
51061f28255Scgd again:				/* search for matches */
51161f28255Scgd     name_length = Strlen(name);
512cee2bad8Smycroft     for (numitems = 0; (entry = getentry(dir_fd, looking_for_lognames)) != NULL;) {
51361f28255Scgd 	if (!is_prefix(name, entry))
51461f28255Scgd 	    continue;
51561f28255Scgd 	/* Don't match . files on null prefix match */
51661f28255Scgd 	if (name_length == 0 && entry[0] == '.' &&
51761f28255Scgd 	    !looking_for_lognames)
51861f28255Scgd 	    continue;
51961f28255Scgd 	if (command == LIST) {
5209050ab5cSlukem 	    if ((size_t)numitems >= maxitems) {
521a7c23d87Schristos 		maxitems += 1024;
522*509d26c5Snia 		items = xreallocarray(items, sizeof(*items), maxitems);
523a7c23d87Schristos  	    }
524*509d26c5Snia 	    items[numitems] = xreallocarray(NULL,
525*509d26c5Snia 		(size_t) (Strlen(entry) + 1), sizeof(Char));
52661f28255Scgd 	    copyn(items[numitems], entry, MAXNAMLEN);
52761f28255Scgd 	    numitems++;
52861f28255Scgd 	}
52961f28255Scgd 	else {			/* RECOGNIZE command */
53061f28255Scgd 	    if (ignoring && ignored(entry))
53161f28255Scgd 		nignored++;
53261f28255Scgd 	    else if (recognize(extended_name,
53361f28255Scgd 	        entry, name_length, ++numitems))
53461f28255Scgd 		break;
53561f28255Scgd 	}
53661f28255Scgd     }
53761f28255Scgd     if (ignoring && numitems == 0 && nignored > 0) {
53861f28255Scgd 	ignoring = FALSE;
53961f28255Scgd 	nignored = 0;
54061f28255Scgd 	if (looking_for_lognames)
54161f28255Scgd 	    (void)setpwent();
54261f28255Scgd 	else
54361f28255Scgd 	    rewinddir(dir_fd);
54461f28255Scgd 	goto again;
54561f28255Scgd     }
54661f28255Scgd 
54761f28255Scgd     if (looking_for_lognames)
54861f28255Scgd 	(void)endpwent();
54961f28255Scgd     else
55061f28255Scgd 	(void)closedir(dir_fd);
55161f28255Scgd     if (numitems == 0)
55261f28255Scgd 	return (0);
55361f28255Scgd     if (command == RECOGNIZE) {
55461f28255Scgd 	if (looking_for_lognames)
55561f28255Scgd 	    copyn(word, STRtilde, 1);
55661f28255Scgd 	else
55761f28255Scgd 	    /* put back dir part */
55861f28255Scgd 	    copyn(word, dir, max_word_length);
55961f28255Scgd 	/* add extended name */
56061f28255Scgd 	catn(word, extended_name, max_word_length);
56161f28255Scgd 	return (numitems);
56261f28255Scgd     }
56361f28255Scgd     else {			/* LIST */
5641767ce60Schristos 	qsort(items, numitems, sizeof(items[0]),
565b771e65bSwiz 		(int (*) (const void *, const void *)) sortscmp);
56661f28255Scgd 	print_by_column(looking_for_lognames ? NULL : tilded_dir,
56761f28255Scgd 			items, numitems);
56861f28255Scgd 	if (items != NULL)
569a7c23d87Schristos 	    FREE_ITEMS(items, numitems);
57061f28255Scgd     }
57161f28255Scgd     return (0);
57261f28255Scgd }
57361f28255Scgd 
57461f28255Scgd /*
57561f28255Scgd  * Object: extend what user typed up to an ambiguity.
57661f28255Scgd  * Algorithm:
57761f28255Scgd  * On first match, copy full entry (assume it'll be the only match)
57861f28255Scgd  * On subsequent matches, shorten extended_name to the first
57961f28255Scgd  * Character mismatch between extended_name and entry.
58061f28255Scgd  * If we shorten it back to the prefix length, stop searching.
58161f28255Scgd  */
58261f28255Scgd static int
recognize(Char * extended_name,Char * entry,size_t name_length,size_t numitems)58337e39248Schristos recognize(Char *extended_name, Char *entry, size_t name_length, size_t numitems)
58461f28255Scgd {
58561f28255Scgd     if (numitems == 1)		/* 1st match */
58661f28255Scgd 	copyn(extended_name, entry, MAXNAMLEN);
58761f28255Scgd     else {			/* 2nd & subsequent matches */
588b771e65bSwiz 	Char *ent, *x;
58937e39248Schristos 	size_t len = 0;
59061f28255Scgd 
59161f28255Scgd 	x = extended_name;
592cee2bad8Smycroft 	for (ent = entry; *x && *x == *ent++; x++, len++)
593cee2bad8Smycroft 	    continue;
59461f28255Scgd 	*x = '\0';		/* Shorten at 1st Char diff */
59561f28255Scgd 	if (len == name_length)	/* Ambiguous to prefix? */
59661f28255Scgd 	    return (-1);	/* So stop now and save time */
59761f28255Scgd     }
59861f28255Scgd     return (0);
59961f28255Scgd }
60061f28255Scgd 
60161f28255Scgd /*
60261f28255Scgd  * Return true if check matches initial Chars in template.
60361f28255Scgd  * This differs from PWB imatch in that if check is null
60461f28255Scgd  * it matches anything.
60561f28255Scgd  */
60661f28255Scgd static int
is_prefix(Char * check,Char * template)607b771e65bSwiz is_prefix(Char *check, Char *template)
60861f28255Scgd {
60961f28255Scgd     do
61061f28255Scgd 	if (*check == 0)
61161f28255Scgd 	    return (TRUE);
61261f28255Scgd     while (*check++ == *template++);
61361f28255Scgd     return (FALSE);
61461f28255Scgd }
61561f28255Scgd 
61661f28255Scgd /*
61761f28255Scgd  *  Return true if the Chars in template appear at the
618a640fe8cSsnj  *  end of check, I.e., are its suffix.
61961f28255Scgd  */
62061f28255Scgd static int
is_suffix(Char * check,Char * template)621b771e65bSwiz is_suffix(Char *check, Char *template)
62261f28255Scgd {
62376adbe2bStls     Char *c, *t;
62461f28255Scgd 
625cee2bad8Smycroft     for (c = check; *c++;)
626cee2bad8Smycroft 	continue;
627cee2bad8Smycroft     for (t = template; *t++;)
628cee2bad8Smycroft 	continue;
62961f28255Scgd     for (;;) {
63061f28255Scgd 	if (t == template)
63161f28255Scgd 	    return 1;
63261f28255Scgd 	if (c == check || *--t != *--c)
63361f28255Scgd 	    return 0;
63461f28255Scgd     }
63561f28255Scgd }
63661f28255Scgd 
63737e39248Schristos ssize_t
tenex(Char * inputline,size_t inputline_size)63837e39248Schristos tenex(Char *inputline, size_t inputline_size)
63961f28255Scgd {
6400bf6fd0cSchristos     char tinputline[BUFSIZE];
64137e39248Schristos     ssize_t num_read;
64237e39248Schristos     size_t numitems;
64361f28255Scgd 
64461f28255Scgd     setup_tty(ON);
64561f28255Scgd 
6460bf6fd0cSchristos     while ((num_read = read(SHIN, tinputline, BUFSIZE)) > 0) {
64737e39248Schristos 	size_t i, nr = (size_t) num_read;
64837e39248Schristos 
649b771e65bSwiz 
65061f28255Scgd 	static Char delims[] = {' ', '\'', '"', '\t', ';', '&', '<',
65161f28255Scgd 	'>', '(', ')', '|', '^', '%', '\0'};
65276adbe2bStls 	Char *str_end, *word_start, last_Char, should_retype;
65337e39248Schristos 	size_t space_left;
65461f28255Scgd 	COMMAND command;
65561f28255Scgd 
65637e39248Schristos 	for (i = 0; i < nr; i++)
65761f28255Scgd 	    inputline[i] = (unsigned char) tinputline[i];
65837e39248Schristos 	last_Char = inputline[nr - 1] & ASCII;
65961f28255Scgd 
66037e39248Schristos 	if (last_Char == '\n' || nr == inputline_size)
66161f28255Scgd 	    break;
66261f28255Scgd 	command = (last_Char == ESC) ? RECOGNIZE : LIST;
66361f28255Scgd 	if (command == LIST)
664cee2bad8Smycroft 	    (void)fputc('\n', cshout);
66537e39248Schristos 	str_end = &inputline[nr];
66661f28255Scgd 	if (last_Char == ESC)
66761f28255Scgd 	    --str_end;		/* wipeout trailing cmd Char */
66861f28255Scgd 	*str_end = '\0';
66961f28255Scgd 	/*
67057920690Smsaitoh 	 * Find LAST occurrence of a delimiter in the inputline. The word start
67161f28255Scgd 	 * is one Character past it.
67261f28255Scgd 	 */
67361f28255Scgd 	for (word_start = str_end; word_start > inputline; --word_start)
67461f28255Scgd 	    if (Strchr(delims, word_start[-1]))
67561f28255Scgd 		break;
67637e39248Schristos 	space_left = inputline_size - (size_t)(word_start - inputline) - 1;
67761f28255Scgd 	numitems = tsearch(word_start, command, space_left);
67861f28255Scgd 
67961f28255Scgd 	if (command == RECOGNIZE) {
68061f28255Scgd 	    /* print from str_end on */
68161f28255Scgd 	    print_recognized_stuff(str_end);
68261f28255Scgd 	    if (numitems != 1)	/* Beep = No match/ambiguous */
68361f28255Scgd 		beep();
68461f28255Scgd 	}
68561f28255Scgd 
68661f28255Scgd 	/*
68761f28255Scgd 	 * Tabs in the input line cause trouble after a pushback. tty driver
68861f28255Scgd 	 * won't backspace over them because column positions are now
68961f28255Scgd 	 * incorrect. This is solved by retyping over current line.
69061f28255Scgd 	 */
69161f28255Scgd 	should_retype = FALSE;
69261f28255Scgd 	if (Strchr(inputline, '\t')) {	/* tab Char in input line? */
69361f28255Scgd 	    back_to_col_1();
69461f28255Scgd 	    should_retype = TRUE;
69561f28255Scgd 	}
69661f28255Scgd 	if (command == LIST)	/* Always retype after a LIST */
69761f28255Scgd 	    should_retype = TRUE;
698e50ba7eaSitohy 	if (pushback(inputline))
699e50ba7eaSitohy 	    should_retype = TRUE;
700e50ba7eaSitohy 	if (should_retype) {
701e50ba7eaSitohy 	    if (command == RECOGNIZE)
702e50ba7eaSitohy 		(void) fputc('\n', cshout);
70361f28255Scgd 	    printprompt();
704e50ba7eaSitohy 	}
70561f28255Scgd 	if (should_retype)
70661f28255Scgd 	    retype();
70761f28255Scgd     }
70861f28255Scgd     setup_tty(OFF);
70937e39248Schristos     return num_read;
71061f28255Scgd }
71161f28255Scgd 
71261f28255Scgd static int
ignored(Char * entry)713b771e65bSwiz ignored(Char *entry)
71461f28255Scgd {
71561f28255Scgd     struct varent *vp;
71676adbe2bStls     Char **cp;
71761f28255Scgd 
71861f28255Scgd     if ((vp = adrof(STRfignore)) == NULL || (cp = vp->vec) == NULL)
71961f28255Scgd 	return (FALSE);
72061f28255Scgd     for (; *cp != NULL; cp++)
72161f28255Scgd 	if (is_suffix(entry, *cp))
72261f28255Scgd 	    return (TRUE);
72361f28255Scgd     return (FALSE);
72461f28255Scgd }
72561f28255Scgd #endif				/* FILEC */
726