diff options
Diffstat (limited to 'bin/csh/file.c')
| -rw-r--r-- | bin/csh/file.c | 684 | 
1 files changed, 684 insertions, 0 deletions
| diff --git a/bin/csh/file.c b/bin/csh/file.c new file mode 100644 index 000000000000..523844d2b3a0 --- /dev/null +++ b/bin/csh/file.c @@ -0,0 +1,684 @@ +/*- + * Copyright (c) 1980, 1991, 1993 + *	The Regents of the University of California.  All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + *    must display the following acknowledgement: + *	This product includes software developed by the University of + *	California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + *    may be used to endorse or promote products derived from this software + *    without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)file.c	8.2 (Berkeley) 3/19/94"; +#endif /* not lint */ + +#ifdef FILEC + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <termios.h> +#include <dirent.h> +#include <pwd.h> +#include <stdlib.h> +#include <unistd.h> +#ifndef SHORT_STRINGS +#include <string.h> +#endif /* SHORT_STRINGS */ +#if __STDC__ +# include <stdarg.h> +#else +# include <varargs.h> +#endif + +#include "csh.h" +#include "extern.h" + +/* + * Tenex style file name recognition, .. and more. + * History: + *	Author: Ken Greer, Sept. 1975, CMU. + *	Finally got around to adding to the Cshell., Ken Greer, Dec. 1981. + */ + +#define ON	1 +#define OFF	0 +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +#define ESC	'\033' + +typedef enum { +    LIST, RECOGNIZE +}       COMMAND; + +static void	 setup_tty __P((int)); +static void	 back_to_col_1 __P((void)); +static void	 pushback __P((Char *)); +static void	 catn __P((Char *, Char *, int)); +static void	 copyn __P((Char *, Char *, int)); +static Char	 filetype __P((Char *, Char *)); +static void	 print_by_column __P((Char *, Char *[], int)); +static Char	*tilde __P((Char *, Char *)); +static void	 retype __P((void)); +static void	 beep __P((void)); +static void	 print_recognized_stuff __P((Char *)); +static void	 extract_dir_and_name __P((Char *, Char *, Char *)); +static Char	*getentry __P((DIR *, int)); +static void	 free_items __P((Char **)); +static int	 tsearch __P((Char *, COMMAND, int)); +static int	 recognize __P((Char *, Char *, int, int)); +static int	 is_prefix __P((Char *, Char *)); +static int	 is_suffix __P((Char *, Char *)); +static int	 ignored __P((Char *)); + +/* + * Put this here so the binary can be patched with adb to enable file + * completion by default.  Filec controls completion, nobeep controls + * ringing the terminal bell on incomplete expansions. + */ +bool    filec = 0; + +static void +setup_tty(on) +    int     on; +{ +    static struct termios tchars; + +    (void) tcgetattr(SHIN, &tchars); + +    if (on) { +	tchars.c_cc[VEOL] = ESC; +	if (tchars.c_lflag & ICANON) +	    on = TCSANOW; +	else { +	    on = TCSAFLUSH; +	    tchars.c_lflag |= ICANON; +	} +    } +    else { +	tchars.c_cc[VEOL] = _POSIX_VDISABLE; +	on = TCSANOW; +    } + +    (void) tcsetattr(SHIN, TCSANOW, &tchars); +} + +/* + * Move back to beginning of current line + */ +static void +back_to_col_1() +{ +    struct termios tty, tty_normal; +    int     omask; + +    omask = sigblock(sigmask(SIGINT)); +    (void) tcgetattr(SHOUT, &tty); +    tty_normal = tty; +    tty.c_iflag &= ~INLCR; +    tty.c_oflag &= ~ONLCR; +    (void) tcsetattr(SHOUT, TCSANOW, &tty); +    (void) write(SHOUT, "\r", 1); +    (void) tcsetattr(SHOUT, TCSANOW, &tty_normal); +    (void) sigsetmask(omask); +} + +/* + * Push string contents back into tty queue + */ +static void +pushback(string) +    Char   *string; +{ +    register Char *p; +    struct termios tty, tty_normal; +    int     omask; +    char    c; + +    omask = sigblock(sigmask(SIGINT)); +    (void) tcgetattr(SHOUT, &tty); +    tty_normal = tty; +    tty.c_lflag &= ~(ECHOKE | ECHO | ECHOE | ECHOK | ECHONL | ECHOPRT | ECHOCTL); +    (void) tcsetattr(SHOUT, TCSANOW, &tty); + +    for (p = string; (c = *p) != '\0'; p++) +	(void) ioctl(SHOUT, TIOCSTI, (ioctl_t) & c); +    (void) tcsetattr(SHOUT, TCSANOW, &tty_normal); +    (void) sigsetmask(omask); +} + +/* + * Concatenate src onto tail of des. + * Des is a string whose maximum length is count. + * Always null terminate. + */ +static void +catn(des, src, count) +    register Char *des, *src; +    register int count; +{ +    while (--count >= 0 && *des) +	des++; +    while (--count >= 0) +	if ((*des++ = *src++) == 0) +	    return; +    *des = '\0'; +} + +/* + * Like strncpy but always leave room for trailing \0 + * and always null terminate. + */ +static void +copyn(des, src, count) +    register Char *des, *src; +    register int count; +{ +    while (--count >= 0) +	if ((*des++ = *src++) == 0) +	    return; +    *des = '\0'; +} + +static  Char +filetype(dir, file) +    Char   *dir, *file; +{ +    Char    path[MAXPATHLEN]; +    struct stat statb; + +    catn(Strcpy(path, dir), file, sizeof(path) / sizeof(Char)); +    if (lstat(short2str(path), &statb) == 0) { +	switch (statb.st_mode & S_IFMT) { +	case S_IFDIR: +	    return ('/'); + +	case S_IFLNK: +	    if (stat(short2str(path), &statb) == 0 &&	/* follow it out */ +		S_ISDIR(statb.st_mode)) +		return ('>'); +	    else +		return ('@'); + +	case S_IFSOCK: +	    return ('='); + +	default: +	    if (statb.st_mode & 0111) +		return ('*'); +	} +    } +    return (' '); +} + +static struct winsize win; + +/* + * Print sorted down columns + */ +static void +print_by_column(dir, items, count) +    Char   *dir, *items[]; +    int     count; +{ +    register int i, rows, r, c, maxwidth = 0, columns; + +    if (ioctl(SHOUT, TIOCGWINSZ, (ioctl_t) & win) < 0 || win.ws_col == 0) +	win.ws_col = 80; +    for (i = 0; i < count; i++) +	maxwidth = maxwidth > (r = Strlen(items[i])) ? maxwidth : r; +    maxwidth += 2;		/* for the file tag and space */ +    columns = win.ws_col / maxwidth; +    if (columns == 0) +	columns = 1; +    rows = (count + (columns - 1)) / columns; +    for (r = 0; r < rows; r++) { +	for (c = 0; c < columns; c++) { +	    i = c * rows + r; +	    if (i < count) { +		register int w; + +		(void) fprintf(cshout, "%s", vis_str(items[i])); +		(void) fputc(dir ? filetype(dir, items[i]) : ' ', cshout); +		if (c < columns - 1) {	/* last column? */ +		    w = Strlen(items[i]) + 1; +		    for (; w < maxwidth; w++) +			(void) fputc(' ', cshout); +		} +	    } +	} +	(void) fputc('\r', cshout); +	(void) fputc('\n', cshout); +    } +} + +/* + * Expand file name with possible tilde usage + *	~person/mumble + * expands to + *	home_directory_of_person/mumble + */ +static Char * +tilde(new, old) +    Char   *new, *old; +{ +    register Char *o, *p; +    register struct passwd *pw; +    static Char person[40]; + +    if (old[0] != '~') +	return (Strcpy(new, old)); + +    for (p = person, o = &old[1]; *o && *o != '/'; *p++ = *o++) +	continue; +    *p = '\0'; +    if (person[0] == '\0') +	(void) Strcpy(new, value(STRhome)); +    else { +	pw = getpwnam(short2str(person)); +	if (pw == NULL) +	    return (NULL); +	(void) Strcpy(new, str2short(pw->pw_dir)); +    } +    (void) Strcat(new, o); +    return (new); +} + +/* + * Cause pending line to be printed + */ +static void +retype() +{ +    struct termios tty; + +    (void) tcgetattr(SHOUT, &tty); +    tty.c_lflag |= PENDIN; +    (void) tcsetattr(SHOUT, TCSANOW, &tty); +} + +static void +beep() +{ +    if (adrof(STRnobeep) == 0) +	(void) write(SHOUT, "\007", 1); +} + +/* + * Erase that silly ^[ and + * print the recognized part of the string + */ +static void +print_recognized_stuff(recognized_part) +    Char   *recognized_part; +{ +    /* An optimized erasing of that silly ^[ */ +    (void) fputc('\b', cshout); +    (void) fputc('\b', cshout); +    switch (Strlen(recognized_part)) { + +    case 0:			/* erase two Characters: ^[ */ +	(void) fputc(' ', cshout); +	(void) fputc(' ', cshout); +	(void) fputc('\b', cshout); +	(void) fputc('\b', cshout); +	break; + +    case 1:			/* overstrike the ^, erase the [ */ +	(void) fprintf(cshout, "%s", vis_str(recognized_part)); +	(void) fputc(' ', cshout); +	(void) fputc('\b', cshout); +	break; + +    default:			/* overstrike both Characters ^[ */ +	(void) fprintf(cshout, "%s", vis_str(recognized_part)); +	break; +    } +    (void) fflush(cshout); +} + +/* + * Parse full path in file into 2 parts: directory and file names + * Should leave final slash (/) at end of dir. + */ +static void +extract_dir_and_name(path, dir, name) +    Char   *path, *dir, *name; +{ +    register Char *p; + +    p = Strrchr(path, '/'); +    if (p == NULL) { +	copyn(name, path, MAXNAMLEN); +	dir[0] = '\0'; +    } +    else { +	copyn(name, ++p, MAXNAMLEN); +	copyn(dir, path, p - path); +    } +} + +static Char * +getentry(dir_fd, looking_for_lognames) +    DIR    *dir_fd; +    int     looking_for_lognames; +{ +    register struct passwd *pw; +    register struct dirent *dirp; + +    if (looking_for_lognames) { +	if ((pw = getpwent()) == NULL) +	    return (NULL); +	return (str2short(pw->pw_name)); +    } +    if ((dirp = readdir(dir_fd)) != NULL) +	return (str2short(dirp->d_name)); +    return (NULL); +} + +static void +free_items(items) +    register Char **items; +{ +    register int i; + +    for (i = 0; items[i]; i++) +	xfree((ptr_t) items[i]); +    xfree((ptr_t) items); +} + +#define FREE_ITEMS(items) { \ +	int omask;\ +\ +	omask = sigblock(sigmask(SIGINT));\ +	free_items(items);\ +	items = NULL;\ +	(void) sigsetmask(omask);\ +} + +/* + * Perform a RECOGNIZE or LIST command on string "word". + */ +static int +tsearch(word, command, max_word_length) +    Char   *word; +    COMMAND command; +    int     max_word_length; +{ +    static Char **items = NULL; +    register DIR *dir_fd; +    register numitems = 0, ignoring = TRUE, nignored = 0; +    register name_length, looking_for_lognames; +    Char    tilded_dir[MAXPATHLEN + 1], dir[MAXPATHLEN + 1]; +    Char    name[MAXNAMLEN + 1], extended_name[MAXNAMLEN + 1]; +    Char   *entry; + +#define MAXITEMS 1024 + +    if (items != NULL) +	FREE_ITEMS(items); + +    looking_for_lognames = (*word == '~') && (Strchr(word, '/') == NULL); +    if (looking_for_lognames) { +	(void) setpwent(); +	copyn(name, &word[1], MAXNAMLEN);	/* name sans ~ */ +	dir_fd = NULL; +    } +    else { +	extract_dir_and_name(word, dir, name); +	if (tilde(tilded_dir, dir) == 0) +	    return (0); +	dir_fd = opendir(*tilded_dir ? short2str(tilded_dir) : "."); +	if (dir_fd == NULL) +	    return (0); +    } + +again:				/* search for matches */ +    name_length = Strlen(name); +    for (numitems = 0; (entry = getentry(dir_fd, looking_for_lognames)) != NULL;) { +	if (!is_prefix(name, entry)) +	    continue; +	/* Don't match . files on null prefix match */ +	if (name_length == 0 && entry[0] == '.' && +	    !looking_for_lognames) +	    continue; +	if (command == LIST) { +	    if (numitems >= MAXITEMS) { +		(void) fprintf(csherr, "\nYikes!! Too many %s!!\n", +			       looking_for_lognames ? +			       "names in password file" : "files"); +		break; +	    } +	    if (items == NULL) +		items = (Char **) xcalloc(sizeof(items[0]), MAXITEMS); +	    items[numitems] = (Char *) xmalloc((size_t) (Strlen(entry) + 1) * +					       sizeof(Char)); +	    copyn(items[numitems], entry, MAXNAMLEN); +	    numitems++; +	} +	else {			/* RECOGNIZE command */ +	    if (ignoring && ignored(entry)) +		nignored++; +	    else if (recognize(extended_name, +			       entry, name_length, ++numitems)) +		break; +	} +    } +    if (ignoring && numitems == 0 && nignored > 0) { +	ignoring = FALSE; +	nignored = 0; +	if (looking_for_lognames) +	    (void) setpwent(); +	else +	    rewinddir(dir_fd); +	goto again; +    } + +    if (looking_for_lognames) +	(void) endpwent(); +    else +	(void) closedir(dir_fd); +    if (numitems == 0) +	return (0); +    if (command == RECOGNIZE) { +	if (looking_for_lognames) +	    copyn(word, STRtilde, 1); +	else +	    /* put back dir part */ +	    copyn(word, dir, max_word_length); +	/* add extended name */ +	catn(word, extended_name, max_word_length); +	return (numitems); +    } +    else {			/* LIST */ +	qsort((ptr_t) items, numitems, sizeof(items[0]),  +		(int (*) __P((const void *, const void *))) sortscmp); +	print_by_column(looking_for_lognames ? NULL : tilded_dir, +			items, numitems); +	if (items != NULL) +	    FREE_ITEMS(items); +    } +    return (0); +} + +/* + * Object: extend what user typed up to an ambiguity. + * Algorithm: + * On first match, copy full entry (assume it'll be the only match) + * On subsequent matches, shorten extended_name to the first + * Character mismatch between extended_name and entry. + * If we shorten it back to the prefix length, stop searching. + */ +static int +recognize(extended_name, entry, name_length, numitems) +    Char   *extended_name, *entry; +    int     name_length, numitems; +{ +    if (numitems == 1)		/* 1st match */ +	copyn(extended_name, entry, MAXNAMLEN); +    else {			/* 2nd & subsequent matches */ +	register Char *x, *ent; +	register int len = 0; + +	x = extended_name; +	for (ent = entry; *x && *x == *ent++; x++, len++) +	    continue; +	*x = '\0';		/* Shorten at 1st Char diff */ +	if (len == name_length)	/* Ambiguous to prefix? */ +	    return (-1);	/* So stop now and save time */ +    } +    return (0); +} + +/* + * Return true if check matches initial Chars in template. + * This differs from PWB imatch in that if check is null + * it matches anything. + */ +static int +is_prefix(check, template) +    register Char *check, *template; +{ +    do +	if (*check == 0) +	    return (TRUE); +    while (*check++ == *template++); +    return (FALSE); +} + +/* + *  Return true if the Chars in template appear at the + *  end of check, I.e., are it's suffix. + */ +static int +is_suffix(check, template) +    Char   *check, *template; +{ +    register Char *c, *t; + +    for (c = check; *c++;) +	continue; +    for (t = template; *t++;) +	continue; +    for (;;) { +	if (t == template) +	    return 1; +	if (c == check || *--t != *--c) +	    return 0; +    } +} + +int +tenex(inputline, inputline_size) +    Char   *inputline; +    int     inputline_size; +{ +    register int numitems, num_read; +    char    tinputline[BUFSIZ]; + + +    setup_tty(ON); + +    while ((num_read = read(SHIN, tinputline, BUFSIZ)) > 0) { +	int     i; +	static Char delims[] = {' ', '\'', '"', '\t', ';', '&', '<', +	'>', '(', ')', '|', '^', '%', '\0'}; +	register Char *str_end, *word_start, last_Char, should_retype; +	register int space_left; +	COMMAND command; + +	for (i = 0; i < num_read; i++) +	    inputline[i] = (unsigned char) tinputline[i]; +	last_Char = inputline[num_read - 1] & ASCII; + +	if (last_Char == '\n' || num_read == inputline_size) +	    break; +	command = (last_Char == ESC) ? RECOGNIZE : LIST; +	if (command == LIST) +	    (void) fputc('\n', cshout); +	str_end = &inputline[num_read]; +	if (last_Char == ESC) +	    --str_end;		/* wipeout trailing cmd Char */ +	*str_end = '\0'; +	/* +	 * Find LAST occurence of a delimiter in the inputline. The word start +	 * is one Character past it. +	 */ +	for (word_start = str_end; word_start > inputline; --word_start) +	    if (Strchr(delims, word_start[-1])) +		break; +	space_left = inputline_size - (word_start - inputline) - 1; +	numitems = tsearch(word_start, command, space_left); + +	if (command == RECOGNIZE) { +	    /* print from str_end on */ +	    print_recognized_stuff(str_end); +	    if (numitems != 1)	/* Beep = No match/ambiguous */ +		beep(); +	} + +	/* +	 * Tabs in the input line cause trouble after a pushback. tty driver +	 * won't backspace over them because column positions are now +	 * incorrect. This is solved by retyping over current line. +	 */ +	should_retype = FALSE; +	if (Strchr(inputline, '\t')) {	/* tab Char in input line? */ +	    back_to_col_1(); +	    should_retype = TRUE; +	} +	if (command == LIST)	/* Always retype after a LIST */ +	    should_retype = TRUE; +	if (should_retype) +	    printprompt(); +	pushback(inputline); +	if (should_retype) +	    retype(); +    } +    setup_tty(OFF); +    return (num_read); +} + +static int +ignored(entry) +    register Char *entry; +{ +    struct varent *vp; +    register Char **cp; + +    if ((vp = adrof(STRfignore)) == NULL || (cp = vp->vec) == NULL) +	return (FALSE); +    for (; *cp != NULL; cp++) +	if (is_suffix(entry, *cp)) +	    return (TRUE); +    return (FALSE); +} +#endif				/* FILEC */ | 
