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