tcsh with editor (again) (7 of 7)

Paul Placeway paul at osu-dbs.UUCP
Sat Apr 21 04:38:09 AEST 1984


(I'm trying again for everyone who missed some of it)

The following code is my changes to Ken Greer's tcsh.  I have added a visual
mini-editor to the shell, and cleaned up the expansion routines some what.
note that this is the 4.1 version.  When we get 4.2 up I'll repost the new
changes.

Please send any changes back to me so that I can update our version.

Note: this is part 7 of 7, you need all of the parts to make tcsh.

					Paul W. Placeway
					The Ohio State University
					(UUCP: cbosgd!osu-dbs!paul)
					(CSNet: paul at ohio-state)

================ cut here ================
: This is a shar archive.  Extract with sh, not csh.
echo x - tw.parse.c
cat > tw.parse.c << '!Funky!Stuff!'
#define MAKE_TWENEX		/* flag to include definitions */
#include "tw.h"

/* do the expand or list on the command line -- SHOULD BE REPLACED */
int fcompare();

tenematch (inputline, inputline_size, num_read, command)
char   *inputline;		/* match string prefix */
int     inputline_size;		/* max size of string */
int	num_read;		/* # actually in inputline */
COMMAND command;		/* LIST or RECOGNIZE or PRINT_HELP */

{
    static char 
	    *delims = " '\"\t;&<>()|^%";
    static char 
	    *cmd_delims = ";&(|`";
    char word [FILSIZ + 1];
    register char *str_end, *word_start, *cmd_start, *wp;
    char *cmd_st;
    int space_left;
    int is_a_cmd;		/* UNIX command rather than filename */
    int search_ret;		/* what search returned for debugging */

    str_end = &inputline[num_read];

   /*
    * 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 (index (delims, word_start[-1]))
	    break;

				/* space backward looking for the beginning
				   of this command */
    for (cmd_st = str_end; cmd_st > inputline; --cmd_st)
	if (index (cmd_delims, cmd_st[-1]))
	    break;
				/* step forward over leading spaces */
    while (*cmd_st != '\0' && (*cmd_st == ' ' || *cmd_st == '\t'))
	cmd_st++;

    space_left = inputline_size - (word_start - inputline) - 1;
    
    is_a_cmd = starting_a_command (word_start, inputline);

    for (cmd_start = word_start, wp = word; cmd_start < str_end;
    	 *wp++ = *cmd_start++);
    *wp = 0;

/*  printf ("\ncmd_st:%s:\nword_start:%s:\n", cmd_st, word_start); */
				/* for debugging */
    if (command != PRINT_HELP) {
        search_ret = search (word, wp, command, space_left, is_a_cmd);
        copyn (word_start, word, space_left);
				   /* put the stuf INTO the line */
        return (search_ret);
    }
    else {
	do_help (cmd_st);
	return (1);
    }
}



/*
 * return true if check items initial chars in template
 * This differs from PWB imatch in that if check is null
 * it items anything
 */

static
is_prefix (check, template)
char   *check,
       *template;
{
    register char  *check_char,
                   *template_char;

    check_char = check;
    template_char = template;
    do
	if (*check_char == 0)
	    return (TRUE);
    while (*check_char++ == *template_char++);
    return (FALSE);
}

/* return true if the command starting at wordstart is a command */

starting_a_command (wordstart, inputline)
register char *wordstart, *inputline;
{
    static char
	    *cmdstart = ";&(|`",
	    *cmdalive = " \t'\"";
    while (--wordstart >= inputline)
    {
	if (index (cmdstart, *wordstart))
	    break;
	if (!index (cmdalive, *wordstart))
	    return (FALSE);
    }
    if (wordstart > inputline && *wordstart == '&')	/* Look for >& */
    {
	while (wordstart > inputline &&
			(*--wordstart == ' ' || *wordstart == '\t'));
	if (*wordstart == '>')
		return (FALSE);
    }
    return (TRUE);
}


/*
 * 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.
 */
recognize (extended_name, entry, name_length, numitems)
char *extended_name, *entry;
{
    if (numitems == 1)				/* 1st match */
	copyn (extended_name, entry, MAXNAMLEN);
    else					/* 2nd and subsequent matches */
    {
	register char *x, *ent;
	register int len = 0;
	for (x = extended_name, ent = entry; *x && *x == *ent++; x++, len++);
	*x = '\0';				/* Shorten at 1st char diff */
	if (len == name_length)			/* Ambiguous to prefix? */
	    return (-1);		       /* So stop now and save time */
    }
    return (0);
}


/*
 * Perform a RECOGNIZE or LIST command on string "word".
 */
static
search (word, wp, command, max_word_length, looking_for_command)
char   *word,
       *wp;			/* original end-of-word */
COMMAND command;
{
    register numitems,
	    name_length,		/* Length of prefix (file name) */
	    looking_for_lognames;	/* True if looking for login names */
    int	    showpathn;			/* True if we want path number */
    struct stat
	    dot_statb,			/* Stat buffer for "." */
	    curdir_statb;	       /* Stat buffer for current directory */
    int	    dot_scan,			/* True if scanning "." */
	    dot_got;			/* True if have scanned dot already */
    char    tilded_dir[FILSIZ + 1],	/* dir after ~ expansion */
	    dir[FILSIZ + 1],		/* /x/y/z/ part in /x/y/z/f */
            name[MAXNAMLEN + 1],	/* f part in /d/d/d/f */
            extended_name[MAXNAMLEN+1],	/* the recognized (extended) name */
            *entry,		       /* single directory entry or logname */
	    *path;			/* hacked PATH environment variable */
    int     next_command = 0;		/* the next command to take out of */
					/* the list of commands */
    static DIR 
	    *dir_fd = NULL;
    static char
           **items = NULL;		/* file names when doing a LIST */

    if (items != NULL)
	FREE_ITEMS (items);
    numitems = 0;
    if (dir_fd != NULL)
	FREE_DIR (dir_fd);

    looking_for_lognames = (*word == '~') && (index (word, '/') == NULL);
    looking_for_command &= (*word != '~') && (index (word, '/') == NULL);


    dot_got = FALSE;
    stat (".", &dot_statb);

    if (looking_for_lognames)			/* Looking for login names? */
    {
	setpwent ();				/* Open passwd file */
	copyn (name, &word[1], MAXNAMLEN);	/* name sans ~ */
    }
    else if (!looking_for_command)
    {						/* Open directory */
	extract_dir_and_name (word, dir, name);
	if ((tilde (tilded_dir, dir) == 0) ||	/* expand ~user/... stuff */
	   ((dir_fd = opendir (*tilded_dir ? tilded_dir : ".")) == NULL))
	{
/* LOBOTOMIZE	printf ("No directory file discriptor.\n");	 */
		return (0);
	}

	dot_scan = FALSE;
    }

    name_length = strlen (name);
    showpathn = looking_for_command && is_set("listpathnum");

    while (1) {
        if (!looking_for_command) {
	    if ((entry = getentry (dir_fd, looking_for_lognames)) == NULL) {
		break;
	    }
	    
    	    /*
    	     * Don't match . files on null prefix match
    	     */
    	    if (name_length == 0 && entry[0] == '.' && !looking_for_lognames)
    	        continue;

	} else {
	    if (numcommands == 0) {
		dohash ();
	    }
	    if ((entry = command_list[next_command++]) == NULL)
		break;
	    copyn (name, word, MAXNAMLEN);	/* so it can match things */
	}

    	if (!is_prefix (name, entry))
    	    continue;
    
    	if (command == LIST)		/* LIST command */
    	{
    	    extern char *malloc ();
    	    register int length;
    	    if (numitems >= MAXITEMS)
    	    {
    		printf ("\nYikes!! Too many %s!!\n",
    		    looking_for_lognames ? "names in password file":"files");
		break;
    	    }
    	    if (items == NULL)
    	    {
    		items = (char **) calloc (sizeof (items[1]), MAXITEMS + 1);
    		if (items == NULL)
    		    break;
    	    }
    	    length = strlen(entry) + 1;
    	    if (showpathn)
    		length += strlen(dirflag);
    	    if ((items[numitems] = malloc (length)) == NULL)
    	    {
    		printf ("\nYikes!! I ran out of memory!!!\n");
    		break;
    	    }
    	    copyn (items[numitems], entry, MAXNAMLEN);
    	    if (showpathn)
    	        catn (items[numitems], dirflag, MAXNAMLEN);
    	    numitems++;
    	}
    	else {					/* RECOGNIZE command */
	    if (adrof ("recexact")) {
		if (strcmp (name, entry) == 0) {	/* EXACT match */
	            copyn (extended_name, entry, MAXNAMLEN);
		    numitems = 1;		/* fake into expanding */
		    break;
		}
	    }
    	    if (recognize (extended_name, entry, name_length, ++numitems))
    		break;
	}
    }

    if (!looking_for_command) {		
        if (looking_for_lognames)
	    endpwent ();
        else
	    FREE_DIR (dir_fd);
    }

    if (command == RECOGNIZE && numitems > 0)
    {
	if (looking_for_lognames)
	    copyn (word, "~", 1);
	else if (looking_for_command)
	    word[0] = 0;
	else
	    copyn (word, dir, max_word_length);	       /* put back dir part */
	catn (word, extended_name, max_word_length);   /* add extended name */
	if (numitems == 1) {
            if (looking_for_lognames) {			/* add / */
                catn (word, "/", max_word_length);
            }
            else {
                if (looking_for_command) {		/* add space */
                    catn (word, " ", max_word_length);
                }
                else {
                    if (filetype (tilded_dir, extended_name) == '/') {
                        catn (word, "/", max_word_length);
                    }
                    else {
                        catn (word, " ", max_word_length);
                    }
                }
            }
	}
	
	return (numitems);		        /* at the end */
    }

    if (command == LIST) {
	qsort (items, numitems, sizeof (items[1]), fcompare);
	print_by_column (looking_for_lognames ? NULL:tilded_dir, items,
			 numitems, looking_for_command);
	if (items != NULL)
	    FREE_ITEMS (items);
    }
    return (0);
}


/* stuff for general command line hacking */

/*
 * Strip next directory from path; return ptr to next unstripped directory.
 */
 
char *extract_dir_from_path (path, dir)
char *path, dir[];
{
    register char *d = dir;

    while (*path && (*path == ' ' || *path == ':')) path++;
    while (*path && (*path != ' ' && *path != ':')) *(d++) = *(path++);
    while (*path && (*path == ' ' || *path == ':')) path++;

    ++dirctr;
    if (*dir == '.')
        strcpy (dirflag, " .");
    else
    {
        dirflag[0] = ' ';
	if (dirctr <= 9)
	{
		dirflag[1] = '0' + dirctr;
		dirflag[2] = '\0';
	}
	else
	{
		dirflag[1] = '0' + dirctr / 10;
		dirflag[2] = '0' + dirctr % 10;
		dirflag[3] = '\0';
	}
    }
    *(d++) = '/';
    *d = 0;

    return path;
}


static
free_items (items)
register char **items;
{
    register int i;
    for (i = 0; items[i]; i++)
	free (items[i]);
    free (items);
}


/*
 * parse full path in file into 2 parts: directory and file names
 * Should leave final slash (/) at end of dir.
 */
static
extract_dir_and_name (path, dir, name)
char   *path, *dir, *name;
{
    extern char *rindex ();
    register char  *p;
    p = rindex (path, '/');
    if (p == NULL)
    {
	copyn (name, path, MAXNAMLEN);
	dir[0] = '\0';
    }
    else
    {
	p++;
	copyn (name, p, MAXNAMLEN);
	copyn (dir, path, p - path);
    }
}


char *
getentry (dir_fd, looking_for_lognames)
DIR *dir_fd;
{
    if (looking_for_lognames)			/* Is it login names we want? */
    {
	extern struct passwd *getpwent ();
	register struct passwd *pw;
	if ((pw = getpwent ()) == NULL)
	    return (NULL);
	return (pw -> pw_name);
    }
    else					/* It's a dir entry we want */
    {
	register struct direct *dirp;
	if (dirp = readdir (dir_fd))
	    return (dirp -> d_name);
	return (NULL);
    }
}


/*
 * expand "old" file name with possible tilde usage
 *		~person/mumble
 * expands to
 *		home_directory_of_person/mumble
 * into string "new".
 */

char *
tilde (new, old)
char *new, *old;
{
    extern struct passwd *getpwuid (), *getpwnam ();

    register char *o, *p;
    register struct passwd *pw;
    static char person[40] = {0};

    if (old[0] != '~')
    {
	strcpy (new, old);
	return (new);
    }

    for (p = person, o = &old[1]; *o && *o != '/'; *p++ = *o++);
    *p = '\0';

    if (person[0] == '\0')			/* then use current uid */
	pw = getpwuid (getuid ());
    else
	pw = getpwnam (person);

    if (pw == NULL)
	return (NULL);

    strcpy (new, pw -> pw_dir);
    (void) strcat (new, o);
    return (new);
}

char
filetype (dir, file)
char *dir, *file;
{
    if (dir)
    {
	char path[512];
	struct stat statb;
	strcpy (path, dir);
	catn (path, file, sizeof path);
	if (stat (path, &statb) >= 0)
	{
	    if (statb.st_mode & S_IFDIR)
		return ('/');
	    if (statb.st_mode & 0111)
		return ('*');
	}
    }
    return (' ');
}

/*
 * Print sorted down columns
 */
print_by_column (dir, items, count, looking_for_command)
register char *dir, *items[];
{
    register int i, rows, r, c, maxwidth = 0, columns;
    for (i = 0; i < count; i++)
	maxwidth = max (maxwidth, strlen (items[i]));
    maxwidth += looking_for_command ? 1:2;    /* for the file tag and space */
    columns = 80 / maxwidth;
    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;
		printf("%s", items[i]);
		w = strlen (items[i]);
		/* Print filename followed by '/' or '*' or ' ' */
		if (!looking_for_command)
			putchar (filetype (dir, items[i])), w++;
		if (c < (columns - 1))			/* Not last column? */
		    for (; w < maxwidth; w++)
			putchar (' ');
	    }
	}
	printf ("\n");
    }
}

/*
 * For qsort()
 */
fcompare (file1, file2)
char  **file1, **file2;
{
    return (strcmp (*file1, *file2));
}
!Funky!Stuff!



More information about the Comp.sources.unix mailing list