xref: /csrg-svn/bin/csh/file.c (revision 69283)
147823Sbostic /*-
260765Sbostic  * Copyright (c) 1980, 1991, 1993
360765Sbostic  *	The Regents of the University of California.  All rights reserved.
447823Sbostic  *
547823Sbostic  * %sccs.include.redist.c%
621932Sdist  */
721932Sdist 
817147Ssam #ifndef lint
9*69283Schristos static char sccsid[] = "@(#)file.c	8.4 (Berkeley) 05/06/95";
1047823Sbostic #endif /* not lint */
1115372Slayer 
1217521Sedward #ifdef FILEC
1349992Sbostic 
1450028Sbostic #include <sys/param.h>
1550028Sbostic #include <sys/ioctl.h>
1650028Sbostic #include <sys/stat.h>
1750028Sbostic #include <termios.h>
1850028Sbostic #include <dirent.h>
1950028Sbostic #include <pwd.h>
2050028Sbostic #include <stdlib.h>
2150028Sbostic #include <unistd.h>
2250463Schristos #ifndef SHORT_STRINGS
2350463Schristos #include <string.h>
2450463Schristos #endif /* SHORT_STRINGS */
2550033Schristos #if __STDC__
2650033Schristos # include <stdarg.h>
2750033Schristos #else
2850033Schristos # include <varargs.h>
2950033Schristos #endif
3050033Schristos 
3150023Sbostic #include "csh.h"
3250023Sbostic #include "extern.h"
3349992Sbostic 
3415372Slayer /*
3515372Slayer  * Tenex style file name recognition, .. and more.
3615372Slayer  * History:
3715372Slayer  *	Author: Ken Greer, Sept. 1975, CMU.
3815372Slayer  *	Finally got around to adding to the Cshell., Ken Greer, Dec. 1981.
3915372Slayer  */
4015372Slayer 
4115372Slayer #define ON	1
4215372Slayer #define OFF	0
4349992Sbostic #ifndef TRUE
4449992Sbostic #define TRUE 1
4549992Sbostic #endif
4649992Sbostic #ifndef FALSE
4749992Sbostic #define FALSE 0
4849992Sbostic #endif
4915372Slayer 
5015372Slayer #define ESC	'\033'
5115372Slayer 
5249992Sbostic typedef enum {
5349992Sbostic     LIST, RECOGNIZE
5449992Sbostic }       COMMAND;
5515372Slayer 
5650024Schristos static void	 setup_tty __P((int));
5750024Schristos static void	 back_to_col_1 __P((void));
5850024Schristos static void	 pushback __P((Char *));
5950024Schristos static void	 catn __P((Char *, Char *, int));
6050024Schristos static void	 copyn __P((Char *, Char *, int));
6150024Schristos static Char	 filetype __P((Char *, Char *));
6250024Schristos static void	 print_by_column __P((Char *, Char *[], int));
6351437Sleres static Char	*tilde __P((Char *, Char *));
6450024Schristos static void	 retype __P((void));
6550024Schristos static void	 beep __P((void));
6651437Sleres static void	 print_recognized_stuff __P((Char *));
6750024Schristos static void	 extract_dir_and_name __P((Char *, Char *, Char *));
6850024Schristos static Char	*getentry __P((DIR *, int));
6950024Schristos static void	 free_items __P((Char **));
7050027Schristos static int	 tsearch __P((Char *, COMMAND, int));
7150024Schristos static int	 recognize __P((Char *, Char *, int, int));
7250024Schristos static int	 is_prefix __P((Char *, Char *));
7350024Schristos static int	 is_suffix __P((Char *, Char *));
7450024Schristos static int	 ignored __P((Char *));
7528048Slepreau 
7617521Sedward /*
7717521Sedward  * Put this here so the binary can be patched with adb to enable file
7826144Sedward  * completion by default.  Filec controls completion, nobeep controls
7926144Sedward  * ringing the terminal bell on incomplete expansions.
8017521Sedward  */
8149992Sbostic bool    filec = 0;
8217521Sedward 
8349992Sbostic static void
setup_tty(on)8417147Ssam setup_tty(on)
8549992Sbostic     int     on;
8615372Slayer {
8749992Sbostic     static struct termios tchars;
8817139Sralph 
8966402Schristos     (void) tcgetattr(SHIN, &tchars);
9066402Schristos 
9149992Sbostic     if (on) {
9249992Sbostic 	tchars.c_cc[VEOL] = ESC;
9349992Sbostic 	if (tchars.c_lflag & ICANON)
9449992Sbostic 	    on = TCSANOW;
9549992Sbostic 	else {
9649992Sbostic 	    on = TCSAFLUSH;
9749992Sbostic 	    tchars.c_lflag |= ICANON;
9815372Slayer 	}
9949992Sbostic     }
10049992Sbostic     else {
10149992Sbostic 	tchars.c_cc[VEOL] = _POSIX_VDISABLE;
10266402Schristos 	on = TCSANOW;
10349992Sbostic     }
10466402Schristos 
10566402Schristos     (void) tcsetattr(SHIN, TCSANOW, &tchars);
10615372Slayer }
10715372Slayer 
10815372Slayer /*
10915372Slayer  * Move back to beginning of current line
11015372Slayer  */
11149992Sbostic static void
back_to_col_1()11217147Ssam back_to_col_1()
11315372Slayer {
11449992Sbostic     struct termios tty, tty_normal;
11568576Schristos     sigset_t sigset, osigset;
11617139Sralph 
11768576Schristos     sigemptyset(&sigset);
11868576Schristos     sigaddset(&sigset, SIGINT);
11968576Schristos     sigprocmask(SIG_BLOCK, &sigset, &osigset);
12049992Sbostic     (void) tcgetattr(SHOUT, &tty);
12149992Sbostic     tty_normal = tty;
12249992Sbostic     tty.c_iflag &= ~INLCR;
12349992Sbostic     tty.c_oflag &= ~ONLCR;
12449992Sbostic     (void) tcsetattr(SHOUT, TCSANOW, &tty);
12549992Sbostic     (void) write(SHOUT, "\r", 1);
12649992Sbostic     (void) tcsetattr(SHOUT, TCSANOW, &tty_normal);
12768576Schristos     sigprocmask(SIG_SETMASK, &osigset, NULL);
12815372Slayer }
12915372Slayer 
13015372Slayer /*
13115372Slayer  * Push string contents back into tty queue
13215372Slayer  */
13349992Sbostic static void
pushback(string)13417147Ssam pushback(string)
13549992Sbostic     Char   *string;
13615372Slayer {
13749992Sbostic     register Char *p;
13849992Sbostic     struct termios tty, tty_normal;
13968576Schristos     sigset_t sigset, osigset;
14049992Sbostic     char    c;
14115372Slayer 
14268576Schristos     sigemptyset(&sigset);
14368576Schristos     sigaddset(&sigset, SIGINT);
14468576Schristos     sigprocmask(SIG_BLOCK, &sigset, &osigset);
14549992Sbostic     (void) tcgetattr(SHOUT, &tty);
14649992Sbostic     tty_normal = tty;
14749992Sbostic     tty.c_lflag &= ~(ECHOKE | ECHO | ECHOE | ECHOK | ECHONL | ECHOPRT | ECHOCTL);
14849992Sbostic     (void) tcsetattr(SHOUT, TCSANOW, &tty);
14915372Slayer 
15060237Schristos     for (p = string; (c = *p) != '\0'; p++)
15149992Sbostic 	(void) ioctl(SHOUT, TIOCSTI, (ioctl_t) & c);
15249992Sbostic     (void) tcsetattr(SHOUT, TCSANOW, &tty_normal);
15368576Schristos     sigprocmask(SIG_SETMASK, &osigset, NULL);
15415372Slayer }
15515372Slayer 
15615372Slayer /*
15717147Ssam  * Concatenate src onto tail of des.
15815372Slayer  * Des is a string whose maximum length is count.
15915372Slayer  * Always null terminate.
16015372Slayer  */
16149992Sbostic static void
catn(des,src,count)16217147Ssam catn(des, src, count)
16349992Sbostic     register Char *des, *src;
16450024Schristos     register int count;
16515372Slayer {
16649992Sbostic     while (--count >= 0 && *des)
16749992Sbostic 	des++;
16849992Sbostic     while (--count >= 0)
16949992Sbostic 	if ((*des++ = *src++) == 0)
17049992Sbostic 	    return;
17149992Sbostic     *des = '\0';
17215372Slayer }
17315372Slayer 
17415372Slayer /*
17517147Ssam  * Like strncpy but always leave room for trailing \0
17615372Slayer  * and always null terminate.
17715372Slayer  */
17849992Sbostic static void
copyn(des,src,count)17917147Ssam copyn(des, src, count)
18049992Sbostic     register Char *des, *src;
18150024Schristos     register int count;
18215372Slayer {
18349992Sbostic     while (--count >= 0)
18449992Sbostic 	if ((*des++ = *src++) == 0)
18549992Sbostic 	    return;
18649992Sbostic     *des = '\0';
18715372Slayer }
18815372Slayer 
18949992Sbostic static  Char
filetype(dir,file)19017147Ssam filetype(dir, file)
19149992Sbostic     Char   *dir, *file;
19215372Slayer {
19349992Sbostic     Char    path[MAXPATHLEN];
19449992Sbostic     struct stat statb;
19517147Ssam 
19649992Sbostic     catn(Strcpy(path, dir), file, sizeof(path) / sizeof(Char));
19749992Sbostic     if (lstat(short2str(path), &statb) == 0) {
19849992Sbostic 	switch (statb.st_mode & S_IFMT) {
19949992Sbostic 	case S_IFDIR:
20049992Sbostic 	    return ('/');
20128048Slepreau 
20249992Sbostic 	case S_IFLNK:
20349992Sbostic 	    if (stat(short2str(path), &statb) == 0 &&	/* follow it out */
20449992Sbostic 		S_ISDIR(statb.st_mode))
20549992Sbostic 		return ('>');
20649992Sbostic 	    else
20749992Sbostic 		return ('@');
20828048Slepreau 
20949992Sbostic 	case S_IFSOCK:
21049992Sbostic 	    return ('=');
21128048Slepreau 
21249992Sbostic 	default:
21349992Sbostic 	    if (statb.st_mode & 0111)
21449992Sbostic 		return ('*');
21515372Slayer 	}
21649992Sbostic     }
21749992Sbostic     return (' ');
21815372Slayer }
21915372Slayer 
22028048Slepreau static struct winsize win;
22128048Slepreau 
22215372Slayer /*
22315372Slayer  * Print sorted down columns
22415372Slayer  */
22549992Sbostic static void
print_by_column(dir,items,count)22617147Ssam print_by_column(dir, items, count)
22749992Sbostic     Char   *dir, *items[];
22849992Sbostic     int     count;
22915372Slayer {
23049992Sbostic     register int i, rows, r, c, maxwidth = 0, columns;
23117147Ssam 
23249992Sbostic     if (ioctl(SHOUT, TIOCGWINSZ, (ioctl_t) & win) < 0 || win.ws_col == 0)
23349992Sbostic 	win.ws_col = 80;
23449992Sbostic     for (i = 0; i < count; i++)
23549992Sbostic 	maxwidth = maxwidth > (r = Strlen(items[i])) ? maxwidth : r;
23649992Sbostic     maxwidth += 2;		/* for the file tag and space */
23749992Sbostic     columns = win.ws_col / maxwidth;
23849992Sbostic     if (columns == 0)
23949992Sbostic 	columns = 1;
24049992Sbostic     rows = (count + (columns - 1)) / columns;
24149992Sbostic     for (r = 0; r < rows; r++) {
24249992Sbostic 	for (c = 0; c < columns; c++) {
24349992Sbostic 	    i = c * rows + r;
24449992Sbostic 	    if (i < count) {
24549992Sbostic 		register int w;
24617147Ssam 
24751589Schristos 		(void) fprintf(cshout, "%s", vis_str(items[i]));
24850439Schristos 		(void) fputc(dir ? filetype(dir, items[i]) : ' ', cshout);
24949992Sbostic 		if (c < columns - 1) {	/* last column? */
25049992Sbostic 		    w = Strlen(items[i]) + 1;
25149992Sbostic 		    for (; w < maxwidth; w++)
25250439Schristos 			(void) fputc(' ', cshout);
25317147Ssam 		}
25449992Sbostic 	    }
25515372Slayer 	}
25650439Schristos 	(void) fputc('\r', cshout);
25750439Schristos 	(void) fputc('\n', cshout);
25849992Sbostic     }
25915372Slayer }
26015372Slayer 
26115372Slayer /*
26217147Ssam  * Expand file name with possible tilde usage
26317147Ssam  *	~person/mumble
26415372Slayer  * expands to
26517147Ssam  *	home_directory_of_person/mumble
26615372Slayer  */
26749992Sbostic static Char *
tilde(new,old)26817147Ssam tilde(new, old)
26949992Sbostic     Char   *new, *old;
27015372Slayer {
27149992Sbostic     register Char *o, *p;
27249992Sbostic     register struct passwd *pw;
27349992Sbostic     static Char person[40];
27415372Slayer 
27549992Sbostic     if (old[0] != '~')
27649992Sbostic 	return (Strcpy(new, old));
27715372Slayer 
27851437Sleres     for (p = person, o = &old[1]; *o && *o != '/'; *p++ = *o++)
27951437Sleres 	continue;
28049992Sbostic     *p = '\0';
28149992Sbostic     if (person[0] == '\0')
28249992Sbostic 	(void) Strcpy(new, value(STRhome));
28349992Sbostic     else {
28449992Sbostic 	pw = getpwnam(short2str(person));
28549992Sbostic 	if (pw == NULL)
28649992Sbostic 	    return (NULL);
28749992Sbostic 	(void) Strcpy(new, str2short(pw->pw_dir));
28849992Sbostic     }
28949992Sbostic     (void) Strcat(new, o);
29049992Sbostic     return (new);
29115372Slayer }
29215372Slayer 
29315372Slayer /*
29415372Slayer  * Cause pending line to be printed
29515372Slayer  */
29649992Sbostic static void
retype()29717147Ssam retype()
29815372Slayer {
29949992Sbostic     struct termios tty;
30017147Ssam 
30149992Sbostic     (void) tcgetattr(SHOUT, &tty);
30249992Sbostic     tty.c_lflag |= PENDIN;
30349992Sbostic     (void) tcsetattr(SHOUT, TCSANOW, &tty);
30415372Slayer }
30515372Slayer 
30649992Sbostic static void
beep()30717147Ssam beep()
30815372Slayer {
30949992Sbostic     if (adrof(STRnobeep) == 0)
31049992Sbostic 	(void) write(SHOUT, "\007", 1);
31115372Slayer }
31215372Slayer 
31315372Slayer /*
31415372Slayer  * Erase that silly ^[ and
31515372Slayer  * print the recognized part of the string
31615372Slayer  */
31749992Sbostic static void
print_recognized_stuff(recognized_part)31817147Ssam print_recognized_stuff(recognized_part)
31949992Sbostic     Char   *recognized_part;
32015372Slayer {
32149992Sbostic     /* An optimized erasing of that silly ^[ */
32250439Schristos     (void) fputc('\b', cshout);
32350439Schristos     (void) fputc('\b', cshout);
32449992Sbostic     switch (Strlen(recognized_part)) {
32517147Ssam 
32649992Sbostic     case 0:			/* erase two Characters: ^[ */
32750439Schristos 	(void) fputc(' ', cshout);
32850439Schristos 	(void) fputc(' ', cshout);
32950439Schristos 	(void) fputc('\b', cshout);
33050439Schristos 	(void) fputc('\b', cshout);
33149992Sbostic 	break;
33217147Ssam 
33349992Sbostic     case 1:			/* overstrike the ^, erase the [ */
33451589Schristos 	(void) fprintf(cshout, "%s", vis_str(recognized_part));
33550439Schristos 	(void) fputc(' ', cshout);
33650439Schristos 	(void) fputc('\b', cshout);
33749992Sbostic 	break;
33817147Ssam 
33949992Sbostic     default:			/* overstrike both Characters ^[ */
34051589Schristos 	(void) fprintf(cshout, "%s", vis_str(recognized_part));
34149992Sbostic 	break;
34249992Sbostic     }
34350439Schristos     (void) fflush(cshout);
34415372Slayer }
34515372Slayer 
34615372Slayer /*
34717147Ssam  * Parse full path in file into 2 parts: directory and file names
34815372Slayer  * Should leave final slash (/) at end of dir.
34915372Slayer  */
35049992Sbostic static void
extract_dir_and_name(path,dir,name)35117147Ssam extract_dir_and_name(path, dir, name)
35249992Sbostic     Char   *path, *dir, *name;
35315372Slayer {
35449992Sbostic     register Char *p;
35517147Ssam 
35649992Sbostic     p = Strrchr(path, '/');
35749992Sbostic     if (p == NULL) {
35849992Sbostic 	copyn(name, path, MAXNAMLEN);
35949992Sbostic 	dir[0] = '\0';
36049992Sbostic     }
36149992Sbostic     else {
36249992Sbostic 	copyn(name, ++p, MAXNAMLEN);
36349992Sbostic 	copyn(dir, path, p - path);
36449992Sbostic     }
36515372Slayer }
36615372Slayer 
36749992Sbostic static Char *
getentry(dir_fd,looking_for_lognames)36817147Ssam getentry(dir_fd, looking_for_lognames)
36949992Sbostic     DIR    *dir_fd;
37049992Sbostic     int     looking_for_lognames;
37115372Slayer {
37249992Sbostic     register struct passwd *pw;
37349992Sbostic     register struct dirent *dirp;
37417147Ssam 
37549992Sbostic     if (looking_for_lognames) {
37649992Sbostic 	if ((pw = getpwent()) == NULL)
37749992Sbostic 	    return (NULL);
37849992Sbostic 	return (str2short(pw->pw_name));
37949992Sbostic     }
38060237Schristos     if ((dirp = readdir(dir_fd)) != NULL)
38149992Sbostic 	return (str2short(dirp->d_name));
38249992Sbostic     return (NULL);
38315372Slayer }
38415372Slayer 
38549992Sbostic static void
free_items(items)38617147Ssam free_items(items)
38749992Sbostic     register Char **items;
38815372Slayer {
38949992Sbostic     register int i;
39017147Ssam 
39149992Sbostic     for (i = 0; items[i]; i++)
39249992Sbostic 	xfree((ptr_t) items[i]);
39349992Sbostic     xfree((ptr_t) items);
39415372Slayer }
39515372Slayer 
39617147Ssam #define FREE_ITEMS(items) { \
39768576Schristos 	sigset_t sigset, osigset;\
39817430Sbloom \
39968576Schristos 	sigemptyset(&sigset);\
40068576Schristos 	sigaddset(&sigset, SIGINT);\
40168576Schristos 	sigprocmask(SIG_BLOCK, &sigset, &osigset);\
40217147Ssam 	free_items(items);\
40317147Ssam 	items = NULL;\
40468576Schristos 	sigprocmask(SIG_SETMASK, &osigset, NULL);\
40515372Slayer }
40615372Slayer 
40715372Slayer /*
40815372Slayer  * Perform a RECOGNIZE or LIST command on string "word".
40915372Slayer  */
41049992Sbostic static int
tsearch(word,command,max_word_length)41149992Sbostic tsearch(word, command, max_word_length)
41249992Sbostic     Char   *word;
41350027Schristos     COMMAND command;
41449992Sbostic     int     max_word_length;
41515372Slayer {
41649992Sbostic     static Char **items = NULL;
41749992Sbostic     register DIR *dir_fd;
41849992Sbostic     register numitems = 0, ignoring = TRUE, nignored = 0;
41949992Sbostic     register name_length, looking_for_lognames;
42049992Sbostic     Char    tilded_dir[MAXPATHLEN + 1], dir[MAXPATHLEN + 1];
42149992Sbostic     Char    name[MAXNAMLEN + 1], extended_name[MAXNAMLEN + 1];
42249992Sbostic     Char   *entry;
42349992Sbostic 
42417147Ssam #define MAXITEMS 1024
42515372Slayer 
42649992Sbostic     if (items != NULL)
42749992Sbostic 	FREE_ITEMS(items);
42815372Slayer 
42949992Sbostic     looking_for_lognames = (*word == '~') && (Strchr(word, '/') == NULL);
43049992Sbostic     if (looking_for_lognames) {
43149992Sbostic 	(void) setpwent();
43249992Sbostic 	copyn(name, &word[1], MAXNAMLEN);	/* name sans ~ */
43349992Sbostic 	dir_fd = NULL;
43449992Sbostic     }
43549992Sbostic     else {
43649992Sbostic 	extract_dir_and_name(word, dir, name);
43749992Sbostic 	if (tilde(tilded_dir, dir) == 0)
43849992Sbostic 	    return (0);
43949992Sbostic 	dir_fd = opendir(*tilded_dir ? short2str(tilded_dir) : ".");
44049992Sbostic 	if (dir_fd == NULL)
44149992Sbostic 	    return (0);
44249992Sbostic     }
44326144Sedward 
44449992Sbostic again:				/* search for matches */
44549992Sbostic     name_length = Strlen(name);
44660237Schristos     for (numitems = 0; (entry = getentry(dir_fd, looking_for_lognames)) != NULL;) {
44749992Sbostic 	if (!is_prefix(name, entry))
44849992Sbostic 	    continue;
44949992Sbostic 	/* Don't match . files on null prefix match */
45049992Sbostic 	if (name_length == 0 && entry[0] == '.' &&
45149992Sbostic 	    !looking_for_lognames)
45249992Sbostic 	    continue;
45349992Sbostic 	if (command == LIST) {
45449992Sbostic 	    if (numitems >= MAXITEMS) {
45550439Schristos 		(void) fprintf(csherr, "\nYikes!! Too many %s!!\n",
45650439Schristos 			       looking_for_lognames ?
45750439Schristos 			       "names in password file" : "files");
45849992Sbostic 		break;
45949992Sbostic 	    }
46049992Sbostic 	    if (items == NULL)
46149992Sbostic 		items = (Char **) xcalloc(sizeof(items[0]), MAXITEMS);
46249992Sbostic 	    items[numitems] = (Char *) xmalloc((size_t) (Strlen(entry) + 1) *
46349992Sbostic 					       sizeof(Char));
46449992Sbostic 	    copyn(items[numitems], entry, MAXNAMLEN);
46549992Sbostic 	    numitems++;
46615372Slayer 	}
46749992Sbostic 	else {			/* RECOGNIZE command */
46849992Sbostic 	    if (ignoring && ignored(entry))
46949992Sbostic 		nignored++;
47049992Sbostic 	    else if (recognize(extended_name,
47149992Sbostic 			       entry, name_length, ++numitems))
47249992Sbostic 		break;
47326144Sedward 	}
47449992Sbostic     }
47549992Sbostic     if (ignoring && numitems == 0 && nignored > 0) {
47649992Sbostic 	ignoring = FALSE;
47749992Sbostic 	nignored = 0;
47849992Sbostic 	if (looking_for_lognames)
47949992Sbostic 	    (void) setpwent();
48049992Sbostic 	else
48149992Sbostic 	    rewinddir(dir_fd);
48249992Sbostic 	goto again;
48349992Sbostic     }
48426144Sedward 
48549992Sbostic     if (looking_for_lognames)
48649992Sbostic 	(void) endpwent();
48749992Sbostic     else
48849992Sbostic 	(void) closedir(dir_fd);
48949992Sbostic     if (numitems == 0)
49049992Sbostic 	return (0);
49149992Sbostic     if (command == RECOGNIZE) {
49215372Slayer 	if (looking_for_lognames)
49349992Sbostic 	    copyn(word, STRtilde, 1);
49415372Slayer 	else
49549992Sbostic 	    /* put back dir part */
49649992Sbostic 	    copyn(word, dir, max_word_length);
49749992Sbostic 	/* add extended name */
49849992Sbostic 	catn(word, extended_name, max_word_length);
49949992Sbostic 	return (numitems);
50049992Sbostic     }
50149992Sbostic     else {			/* LIST */
50252369Schristos 	qsort((ptr_t) items, numitems, sizeof(items[0]),
50352369Schristos 		(int (*) __P((const void *, const void *))) sortscmp);
50449992Sbostic 	print_by_column(looking_for_lognames ? NULL : tilded_dir,
50549992Sbostic 			items, numitems);
50649992Sbostic 	if (items != NULL)
50749992Sbostic 	    FREE_ITEMS(items);
50849992Sbostic     }
50949992Sbostic     return (0);
51015372Slayer }
51115372Slayer 
51215372Slayer /*
51315372Slayer  * Object: extend what user typed up to an ambiguity.
51415372Slayer  * Algorithm:
51549992Sbostic  * On first match, copy full entry (assume it'll be the only match)
51615372Slayer  * On subsequent matches, shorten extended_name to the first
51749992Sbostic  * Character mismatch between extended_name and entry.
51815372Slayer  * If we shorten it back to the prefix length, stop searching.
51915372Slayer  */
52049992Sbostic static int
recognize(extended_name,entry,name_length,numitems)52117147Ssam recognize(extended_name, entry, name_length, numitems)
52249992Sbostic     Char   *extended_name, *entry;
52349992Sbostic     int     name_length, numitems;
52415372Slayer {
52549992Sbostic     if (numitems == 1)		/* 1st match */
52649992Sbostic 	copyn(extended_name, entry, MAXNAMLEN);
52749992Sbostic     else {			/* 2nd & subsequent matches */
52849992Sbostic 	register Char *x, *ent;
52949992Sbostic 	register int len = 0;
53017147Ssam 
53149992Sbostic 	x = extended_name;
53251437Sleres 	for (ent = entry; *x && *x == *ent++; x++, len++)
53351437Sleres 	    continue;
53449992Sbostic 	*x = '\0';		/* Shorten at 1st Char diff */
53549992Sbostic 	if (len == name_length)	/* Ambiguous to prefix? */
53649992Sbostic 	    return (-1);	/* So stop now and save time */
53749992Sbostic     }
53849992Sbostic     return (0);
53915372Slayer }
54015372Slayer 
54115372Slayer /*
54249992Sbostic  * Return true if check matches initial Chars in template.
54315372Slayer  * This differs from PWB imatch in that if check is null
54426969Slepreau  * it matches anything.
54515372Slayer  */
54649992Sbostic static int
is_prefix(check,template)54717147Ssam is_prefix(check, template)
54849992Sbostic     register Char *check, *template;
54915372Slayer {
55049992Sbostic     do
55149992Sbostic 	if (*check == 0)
55249992Sbostic 	    return (TRUE);
55349992Sbostic     while (*check++ == *template++);
55449992Sbostic     return (FALSE);
55515372Slayer }
55615372Slayer 
55726144Sedward /*
55849992Sbostic  *  Return true if the Chars in template appear at the
55926144Sedward  *  end of check, I.e., are it's suffix.
56026144Sedward  */
56149992Sbostic static int
is_suffix(check,template)56226144Sedward is_suffix(check, template)
56349992Sbostic     Char   *check, *template;
56426144Sedward {
56549992Sbostic     register Char *c, *t;
56626144Sedward 
56751437Sleres     for (c = check; *c++;)
56851437Sleres 	continue;
56951437Sleres     for (t = template; *t++;)
57051437Sleres 	continue;
57149992Sbostic     for (;;) {
57249992Sbostic 	if (t == template)
57349992Sbostic 	    return 1;
57449992Sbostic 	if (c == check || *--t != *--c)
57549992Sbostic 	    return 0;
57649992Sbostic     }
57726144Sedward }
57826144Sedward 
57949992Sbostic int
tenex(inputline,inputline_size)58017147Ssam tenex(inputline, inputline_size)
58149992Sbostic     Char   *inputline;
58249992Sbostic     int     inputline_size;
58315372Slayer {
58449992Sbostic     register int numitems, num_read;
58549992Sbostic     char    tinputline[BUFSIZ];
58615372Slayer 
58715372Slayer 
58849992Sbostic     setup_tty(ON);
58915372Slayer 
59049992Sbostic     while ((num_read = read(SHIN, tinputline, BUFSIZ)) > 0) {
59149992Sbostic 	int     i;
59249992Sbostic 	static Char delims[] = {' ', '\'', '"', '\t', ';', '&', '<',
59349992Sbostic 	'>', '(', ')', '|', '^', '%', '\0'};
59449992Sbostic 	register Char *str_end, *word_start, last_Char, should_retype;
59549992Sbostic 	register int space_left;
59649992Sbostic 	COMMAND command;
59715372Slayer 
59849992Sbostic 	for (i = 0; i < num_read; i++)
59949992Sbostic 	    inputline[i] = (unsigned char) tinputline[i];
60049992Sbostic 	last_Char = inputline[num_read - 1] & ASCII;
60115372Slayer 
60249992Sbostic 	if (last_Char == '\n' || num_read == inputline_size)
60349992Sbostic 	    break;
60449992Sbostic 	command = (last_Char == ESC) ? RECOGNIZE : LIST;
60549992Sbostic 	if (command == LIST)
60650439Schristos 	    (void) fputc('\n', cshout);
60749992Sbostic 	str_end = &inputline[num_read];
60849992Sbostic 	if (last_Char == ESC)
60949992Sbostic 	    --str_end;		/* wipeout trailing cmd Char */
61049992Sbostic 	*str_end = '\0';
61149992Sbostic 	/*
61249992Sbostic 	 * Find LAST occurence of a delimiter in the inputline. The word start
61349992Sbostic 	 * is one Character past it.
61449992Sbostic 	 */
61549992Sbostic 	for (word_start = str_end; word_start > inputline; --word_start)
61649992Sbostic 	    if (Strchr(delims, word_start[-1]))
61749992Sbostic 		break;
61849992Sbostic 	space_left = inputline_size - (word_start - inputline) - 1;
61949992Sbostic 	numitems = tsearch(word_start, command, space_left);
62049992Sbostic 
62149992Sbostic 	if (command == RECOGNIZE) {
62249992Sbostic 	    /* print from str_end on */
62349992Sbostic 	    print_recognized_stuff(str_end);
62449992Sbostic 	    if (numitems != 1)	/* Beep = No match/ambiguous */
62549992Sbostic 		beep();
62615372Slayer 	}
62749992Sbostic 
62849992Sbostic 	/*
62949992Sbostic 	 * Tabs in the input line cause trouble after a pushback. tty driver
63049992Sbostic 	 * won't backspace over them because column positions are now
63149992Sbostic 	 * incorrect. This is solved by retyping over current line.
63249992Sbostic 	 */
63349992Sbostic 	should_retype = FALSE;
63449992Sbostic 	if (Strchr(inputline, '\t')) {	/* tab Char in input line? */
63549992Sbostic 	    back_to_col_1();
63649992Sbostic 	    should_retype = TRUE;
63749992Sbostic 	}
63849992Sbostic 	if (command == LIST)	/* Always retype after a LIST */
63949992Sbostic 	    should_retype = TRUE;
64049992Sbostic 	if (should_retype)
64149992Sbostic 	    printprompt();
64249992Sbostic 	pushback(inputline);
64349992Sbostic 	if (should_retype)
64449992Sbostic 	    retype();
64549992Sbostic     }
64649992Sbostic     setup_tty(OFF);
64749992Sbostic     return (num_read);
64815372Slayer }
64926144Sedward 
65049992Sbostic static int
ignored(entry)65126144Sedward ignored(entry)
65249992Sbostic     register Char *entry;
65326144Sedward {
65449992Sbostic     struct varent *vp;
65549992Sbostic     register Char **cp;
65626144Sedward 
65749992Sbostic     if ((vp = adrof(STRfignore)) == NULL || (cp = vp->vec) == NULL)
65826144Sedward 	return (FALSE);
65949992Sbostic     for (; *cp != NULL; cp++)
66049992Sbostic 	if (is_suffix(entry, *cp))
66149992Sbostic 	    return (TRUE);
66249992Sbostic     return (FALSE);
66326144Sedward }
66449992Sbostic #endif				/* FILEC */
665