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