tcsh with a command line editor (4 of 5)

Paul Placeway paul at osu-dbs.UUCP
Sat Apr 14 05:29:46 AEST 1984


.
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 4 of 5, you need all of the parts to make tcsh.

					Paul W. Placeway
					The Ohio State University IRCC
					(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.h
cat > tw.h << '!Funky!Stuff!'
#ifdef MAKE_TWENEX

#include <sys/types.h>
#include <sys/stat.h>
#include <sgtty.h>
#include "dir.h"
#include <signal.h>
#include <pwd.h>

/*
 * Input lines are parsed into doubly linked circular
 * lists of words of the following form.
 */
struct	wordent {
	char	*word;
	struct	wordent *prev;
	struct	wordent *next;
};

/*
 * History list
 *
 * Each history list entry contains an embedded wordlist
 * from the scanner, a number for the event, and a reference count
 * to aid in discarding old entries.
 *
 * Essentially "invisible" entries are put on the history list
 * when history substitution includes modifiers, and thrown away
 * at the next discarding since their event numbers are very negative.
 */
struct	Hist {
	struct	wordent Hlex;
	int	Hnum;
	int	Href;
	struct	Hist *Hnext;
} Histlist;

/* Don't include stdio.h!  Csh doesn't like it!! */

extern short SHIN, SHOUT;

#define FREE_ITEMS(items)\
{\
    sighold (SIGINT);\
    free_items (items);\
    items = NULL;\
    sigrelse (SIGINT);\
}

#define FREE_DIR(fd)\
{\
    sighold (SIGINT);\
    closedir (fd);\
    fd = NULL;\
    sigrelse (SIGINT);\
}

#define TRUE		1
#define FALSE		0
#define ON		1
#define OFF		0
#define FILSIZ		512		/* Max reasonable file name length */
#define ESC		'\033'
#define equal(a, b)	(strcmp(a, b) == 0)

#define is_set(var)	adrof(var)

#define BUILTINS	"/usr/local/lib/builtins/" /* fake builtin bin */
#define MAXITEMS 2048
#define SEARCHLIST "HPATH"	/* Env. param for helpfile searchlist */
#define DEFAULTLIST ":/u0/osu/man/cat1:/u0/osu/man/cat8:/u0/osu/man/cat6:/usr/man/cat1:/usr/man/cat8:/usr/man/cat6:/u0/local/man/cat1:/u0/local/man/cat8:/u0/local/man/cat6"    /* if no HPATH */

extern char  PromptBuf[];

extern char *getenv ();

typedef enum {LIST, RECOGNIZE, PRINT_HELP} COMMAND;


#define	beep()	write (SHOUT, BELL, 1)

char *getentry();
char GetAChar();
char *tilde();
char filetype();

#ifndef DONT_EXTERN

extern char *BELL;
extern char *command_list[];  /* the pre-digested list of commands
					 for speed and general usefullness */
extern int numcommands;

extern int commands_file;	/* for a global file discriptor */

extern int  key_bindings[];	/* the currently assigned keys */
extern int  bindings_are_inited;	/* for setup of bindings */
						/* the first time */

extern int  dirctr;		/* -1 0 1 2 ... */
extern char dirflag[5];		/*  ' nn\0' - dir #s -  . 1 2 ... */


#endif
#endif

#define	ERROR			-1
#define	NO_OP			0
#define	UP_HIST			1
#define DOWN_HIST		2
#define	COMPLETE		3
#define	HELPME			4
#define	LIST_CHOICES		5
!Funky!Stuff!
echo x - tw.help.c
cat > tw.help.c << '!Funky!Stuff!'
#define MAKE_TWENEX		/* flag to include definitions */
#include "tw.h"


/* actually look up and print documentation on a file.  Look down the path
   for an approiate file, then print it.  Note that the printing is NOT 
   PAGED.  This is because the function is NOT ment to look at manual pages,
   it only does so if there is no .help file to look in. */

do_help (command)
char *command;
{
    char name[FILSIZ + 1];
    char *cmd_p;
    int f;
    char curdir[128];		/* Current directory being looked at */
    char *getenv();
    register char *hpath;	/* The environment parameter */
    char *skipslist();
    char full[128], buf[512];	/* full path name and buffer for read */
    int len;			/* length of read buffer */

				/* copy the string to a safe place */
    copyn (name, command, sizeof (name));

			/* trim off the garbage that may be at the end */
    for (cmd_p = name; *cmd_p != '\0'; cmd_p++) {
	if (*cmd_p == ' ' || *cmd_p == '\t') *cmd_p = '\0';
    }

				/* if nothing left, return */
    if (*name == '\0') return;

		/* got is, now "cat" the file based on the path $HPATH */

    hpath = getenv(SEARCHLIST);
    if(hpath == (char *)0)
        hpath = DEFAULTLIST;

    while (1)
    {
	if(!*hpath)
	{
	    printf("No help file for %s\n", name);
	    break;
	}
	nextslist(hpath, curdir);
	hpath = skipslist(hpath);

/* now make the full path name - try first /bar/foo.help, then /bar/foo.1,
   /bar/foo.8, then finally /bar/foo.6 .  This is so that you don't spit
   a binary at the tty when $HPATH == $PATH.  I know, I know, gotos are
   BOGUS */

	copyn (full, curdir, sizeof (full));
	catn (full, "/", sizeof (full));
	catn (full, name, sizeof (full));
	catn (full, ".help", sizeof (full));
	f = open (full, 0);	/* try to open for reading */
	if (f != -1) goto cat_it;	/* no file there */

	copyn (full, curdir, sizeof (full));
	catn (full, "/", sizeof (full));
	catn (full, name, sizeof (full));
	catn (full, ".1", sizeof (full));
	f = open (full, 0);	/* try to open for reading */
	if (f != -1) goto cat_it;	/* no file there */

	copyn (full, curdir, sizeof (full));
	catn (full, "/", sizeof (full));
	catn (full, name, sizeof (full));
	catn (full, ".8", sizeof (full));
	f = open (full, 0);	/* try to open for reading */
	if (f != -1) goto cat_it;	/* no file there */

	copyn (full, curdir, sizeof (full));
	catn (full, "/", sizeof (full));
	catn (full, name, sizeof (full));
	catn (full, ".6", sizeof (full));
	f = open (full, 0);	/* try to open for reading */
	if (f == -1) continue;	/* no file there */

cat_it:
	while ((len = read (f, buf, 512)) != 0) {	/* the file is here */
	    write (SHOUT, buf, len);			/* so cat it to the */
	}						/* terminal */
	break;
    }
}

/* these next two are stolen from CMU's man(1) command for looking down 
 * paths.  they are prety straight forward. */

/*
 * nextslist takes a search list and copies the next path in it
 * to np.  A null search list entry is expanded to ".".
 * If there are no entries in the search list, then np will point
 * to a null string.
 */

nextslist(sl, np)
  register char *sl;
  register char *np;
{
    if(!*sl)
	*np = '\000';
    else if(*sl == ':')
    {
	*np++ = '.';
	*np = '\000';
    }
    else
    {
	while(*sl && *sl != ':')
	    *np++ = *sl++;
	*np = '\000';
    }
}

/*
 * skipslist returns the pointer to the next entry in the search list.
 */

char *
skipslist(sl)
  register char *sl;
{
    while(*sl && *sl++ != ':');
    return(sl);
}

!Funky!Stuff!
echo x - tw.init.c
cat > tw.init.c << '!Funky!Stuff!'
#define MAKE_TWENEX		/* flag to include definitions */
#include "tw.h"

/*
 * Build the command name list (and print the results).  This is a HACK until
 * I can get the rehash command to include its results in the command list.
 */

char *extract_dir_from_path();
int fcompare();

static int tw_been_inited = 0;
			/* to avoid double reading of commands file */

extern struct	biltins {
	char	*bname;
	int	(*bfunct)();
	short	minargs, maxargs;
} bfunc[];

extern struct	varent {
	char	**vec;		/* Array of words which is the value */
	char	*name;		/* Name of variable/alias */
	struct	varent *link;
} aliases;

tw_clear_comm_list() {
    register int i;

/*     printf ("Building the completion path...");
    flush(); */

    if (numcommands != 0) {
/* 	printf("Freeing..."); */
        for (i = 0; command_list[i]; i++) {
	    if (command_list[i] == 0) {
		printf ("tried to free a null, i = %d\n", i);
	    } else {
		free (command_list[i]);
	    }
	    command_list[i] = NULL;
	}
	numcommands = 0;
    }
}

tw_sort_comms () {
    register int i,j;

/*     printf ("sorting...");
    flush(); */
    
/* sort the list. */
    qsort (command_list, numcommands, sizeof (command_list[0]), fcompare);

/* get rid of multiple entries */
    for (i = 0; i < numcommands - 1; i++) {
	if (strcmp (command_list[i], command_list[i+1]) == 0) {
	    if (command_list[i] == 0) {
		printf ("tried to free a null, i = %d, previous = %s\n", i,
		    command_list [i-1]);
	    } else {
		free (command_list[i]);
	    }
	    for (j = i; j < numcommands; j++) {
		command_list[j] = command_list[j+1];
	    }
	    numcommands--;	/* keep count right */
	    i--;		/* to catch many versions */
	}
    }

/*     print_by_column (looking_for_lognames ? NULL:tilded_dir, command_list,
  			 numcommands, 1);	 */

/*     printf ("Done.\n"); */
    flush();
}

tw_add_comm_name (name)
char *name;
{
    register int length;

    if (numcommands >= MAXITEMS) {
        printf ("\nYikes!! Too many commands in PATH!!\n");
        return;
    }

    length = strlen(name) + 1;

    if ((command_list[numcommands] = (char *)malloc (length)) == NULL) {
        printf ("\nYikes!! I ran out of memory!!!\n");
        return;
    }

    copyn (command_list[numcommands], name, MAXNAMLEN);
    numcommands++;
}

tw_add_builtins() {
    register char *cp;
    register struct biltins *bptr;

    for (bptr = bfunc; cp = bptr->bname; bptr++) {
	tw_add_comm_name (cp);
    }
}

tw_add_aliases ()
{
    register struct varent *vp;

    vp = &aliases;

    for (vp = vp->link; vp != 0; vp = vp->link) {
	tw_add_comm_name(vp->name);
    }

}
!Funky!Stuff!
echo x - tw.main.c
cat > tw.main.c << '!Funky!Stuff!'
/* tw.c -- input parser main routines.  84/04/02 Paul Placeway */

/* 
 * This is a re-write of the routines by Ken Greer, Mike Ellis, and myself
 * for doing twenex (Tops-20 (tm)) style command and file recognition,
 * expansion, and listing, as well as other nice additions to my (inputl)
 * input line parser for C-shell and other things.  This is styled after the
 * Tops-20 COMND jsys, but somewhat changed to what I think it should be in
 * Unix.
 */

#define MAKE_TWENEX		/* flag to include definitions */
#define DONT_EXTERN		/* don't do the extern ... stuff */
#include "tw.h"


static char
    *BELL = "\07";

char *command_list[2048] = NULL;  /* the pre-digested list of commands
					 for speed and general usefullness */
int numcommands = 0;

int commands_file = -1;	/* for a global file discriptor */

int  key_bindings[256];	/* the currently assigned keys */
int  bindings_are_inited = FALSE;	/* for setup of bindings */

int  dirctr;		/* -1 0 1 2 ... */
char dirflag[5];		/*  ' nn\0' - dir #s -  . 1 2 ... */

/* the main routine -- the inputline and size are the location to place the
 * expanded line into. */

twenex (inputline, inputline_size)
char   *inputline;		/* pointer to a buffer */
int     inputline_size;		/* size of the buffer */
{
    register int numitems, num_read;
    int hist_num = 0;
    int hnumcntr;
    char linebuf[512];
    struct Hist *hp;

    if (bindings_are_inited = FALSE) tw_init_bindings();

    while (1) {
	register char *str_end, last_char, should_retype;
	char inputl();
	COMMAND command;

	last_char = inputl (PromptBuf, inputline, inputline_size);
						/* PWP: use the editor */
	num_read = strlen(inputline);

	if (last_char == -1)		/* PWP: for end-of-file */
	    break;			/* did i hear you screem HACK? */

	last_char &= 0177;	/* keep it to 7 bits */

	if (last_char == '\n' || num_read == inputline_size)
	    break;		/* send the line */

	switch (key_bindings[last_char]) {
	    case UP_HIST:	/* ^P -- previous hist line */
	        if (hist_num == 0)
		    copyn(linebuf, inputline, 511);

	        hp = Histlist.Hnext;
	        if (hp == 0) {
		    beep();
		    ilpushback (inputline);
		    break;
	        }
	        hist_num++;
	        for (hnumcntr = 1; hnumcntr < hist_num; hnumcntr++) {
		    if ((hp->Hnext) == 0) {
		        beep();
		        hist_num--;
		        break;
		    }
		    hp = hp -> Hnext;
	        }

	        sprlex (inputline, &hp->Hlex);
	        if (inputline[strlen(inputline)-1] == '\n')
		    inputline[strlen(inputline)-1] = '\0';

	        printprompt ();
	        ilpushback (inputline);
	        ilredisplay ();
	        break;

	    case DOWN_HIST:	/* ^N -- next hist line */
	        if (hist_num == 0) {
		    ilpushback (inputline);
		    beep();
		    break;
	        }
	        if (hist_num == 1) {
		    copyn(inputline, linebuf, inputline_size);
		    hist_num--;
		    printprompt ();
		    ilpushback (inputline);
		    ilredisplay();
		    break;
	        }

	        hp = Histlist.Hnext;
	        hist_num--;
	        for (hnumcntr = 1; hnumcntr < hist_num; hnumcntr++) {
		    if ((hp->Hnext) == 0) break;
		    hp = hp -> Hnext;
	        }

	        sprlex (inputline, &hp->Hlex);
	        if (inputline[strlen(inputline)-1] == '\n')
		    inputline[strlen(inputline)-1] = '\0';

	        printprompt ();
	        ilpushback (inputline);
	        ilredisplay ();
	        break;

	    case COMPLETE:		/* RECOGNIZE */
		do_recognize (inputline, inputline_size, num_read);
		break;

	    case HELPME:	/* help */
		print_help (inputline, inputline_size, num_read);
		break;

	    case LIST_CHOICES:	/* LIST */
		list_choices (inputline, inputline_size, num_read);
		break;

	    case NO_OP:		/* just push it back */
		ilpushback (inputline);
		break;

	    case ERROR:
	    default:
		beep();		/* complain */
		ilpushback (inputline);
		break;

	}			/* end case */
    }				/* end while (1) */

    return (num_read);
}


/* handle the keybindings stuff like initing them, binding them, and seeing
   what is bound to a given key. */


/* bind function func to key key */

int
tw_bind_to_key (func, key)
int func;
char key;
{
    if (key == -1) {		/* trap unsetings */
	return (-1);
    }

    if ((func < 0) || (func > 10)) {
	return(-1);
    }

    if (bindings_are_inited = FALSE) tw_init_bindings();

    key_bindings[key] = func;
}

/* return what function is bound to key key */

int
tw_get_binding(key)
char key;
{
    return (key_bindings[key]);
}

/* initilize the bindings */

tw_init_bindings()
{
    register int i;

    if (bindings_are_inited) return;

    for (i=0; i<128; i++) {	/* assign all the keys */
	key_bindings[i] = NO_OP;
    }
    key_bindings[020] = NO_OP;		/* ^P */
    key_bindings[016] = NO_OP;		/* ^N */
    key_bindings[033] = COMPLETE;	/* ^[ (ESC) */
    key_bindings[000] = HELPME;		/* ^@ */
    key_bindings[077] = LIST_CHOICES;	/* ? */

    bindings_are_inited = TRUE;
}


/* the top level of the recognize and list procedures.  These are just to
   save a little work on the part of twenex(). */

/* do the recognize: take the inputline, expand the command name, if there is
   a white space character, hand the stuff to the parse expander */

do_recognize (inputline, inputline_size, num_read)
char *inputline;
int inputline_size;
int num_read;
{
/*  CharPtr = &inputline[num_read]; */
				/* do it!! */
    if (tenematch (inputline, inputline_size, num_read, RECOGNIZE) != 1)
	 		/* Beep = No match/ambiguous */
	beep ();

    flush ();
    ilpushback (inputline);		/* pushback for editor */
}

/* run the list choices option:  Same as above, but use a parse list options
   routine rather than an expander.  Nothing additional is pushed back onto
   the line (just what was typed in). */

list_choices (inputline, inputline_size, num_read)
char *inputline;
int inputline_size;
int num_read;
{
    putchar ('\n');
/*  CharPtr = &inputline[num_read]; */

			/* do this early so that you can ^C it */
    printprompt ();
    ilpushback (inputline);		/* pushback for editor */
    ilredisplay ();

    tenematch (inputline, inputline_size, num_read, LIST);
				/* do it!! */
    flush ();
}

/* print help:  Figure out what command is being entered, expand the name,
   then do a lookup on documentation of this command */

print_help (inputline, inputline_size, num_read)
char *inputline;
int inputline_size;
int num_read;
{
    putchar ('\n');
/*  CharPtr = &inputline[num_read]; */

			/* do this early so that you can ^C it */
    printprompt ();
    ilpushback (inputline);		/* pushback for editor */
    ilredisplay ();

    tenematch (inputline, inputline_size, num_read, PRINT_HELP);
				/* do it!! */
    flush ();
}



/*
 * Concatonate src onto tail of des.
 * Des is a string whose maximum length is count.
 * Always null terminate.
 */

catn (des, src, count)
register char *des, *src;
register count;
{
    while (--count >= 0 && *des)
	des++;
    while (--count >= 0)
	if ((*des++ = *src++) == 0)
	    return;
    *des = '\0';
}

max (a, b)
{
    if (a > b)
	return (a);
    return (b);
}

/*
 * like strncpy but always leave room for trailing \0
 * and always null terminate.
 */
copyn (des, src, count)
register char *des, *src;
register count;
{
    while (--count >= 0)
	if ((*des++ = *src++) == 0)
	    return;
    *des = '\0';
}
!Funky!Stuff!
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!
echo x - tw.sort.c
cat > tw.sort.c << '!Funky!Stuff!'
/* @(#)qsort.c	4.1 (Berkeley) 12/21/80 */

static int	(*Qscmp)();
static int	Qses;

Qsort(a, n, es, fc)
char *a;
unsigned n;
int es;
int (*fc)();
{
	Qscmp = fc;
	Qses = es;
	Qs1(a, a+n*es);
}

static Qs1(a, l)
char *a, *l;
{
	register char *i, *j;
	register es;
	char **k;
	char *lp, *hp;
	int c;
	unsigned n;


	es = Qses;

start:
	if((n=l-a) <= es)
		return;
	n = es * (n / (2*es));
	hp = lp = a+n;
	i = a;
	j = l-es;
	for(;;) {
		if(i < lp) {
			if((c = (*Qscmp)(i, lp)) == 0) {
				Qsexc(i, lp -= es);
				continue;
			}
			if(c < 0) {
				i += es;
				continue;
			}
		}

loop:
		if(j > hp) {
			if((c = (*Qscmp)(hp, j)) == 0) {
				Qsexc(hp += es, j);
				goto loop;
			}
			if(c > 0) {
				if(i == lp) {
					Qstexc(i, hp += es, j);
					i = lp += es;
					goto loop;
				}
				Qsexc(i, j);
				j -= es;
				i += es;
				continue;
			}
			j -= es;
			goto loop;
		}


		if(i == lp) {
/* the two are the same, dump the copy */
/* 			**(lp+1) = '*'; */
/* 			for(k=lp+2; k<=hp;) **k++ = '\0'; */

/* 			for(k=lp+1; k<=hp;) **k++ = '\0';	 */

			if(lp-a >= l-hp) {
				Qs1(hp+es, l);
				l = lp;
			} else {
				Qs1(a, lp);
				a = hp+es;
			}
			goto start;
		}


		Qstexc(j, lp -= es, i);
		j = hp -= es;
	}
}

static Qsexc(i, j)
char *i, *j;
{
	register char *ri, *rj, c;
	int n;

	n = Qses;
	ri = i;
	rj = j;
	do {
		c = *ri;
		*ri++ = *rj;
		*rj++ = c;
	} while(--n);
}

static Qstexc(i, j, k)
char *i, *j, *k;
{
	register char *ri, *rj, *rk;
	int c;
	int n;

	n = Qses;
	ri = i;
	rj = j;
	rk = k;
	do {
		c = *ri;
		*ri++ = *rk;
		*rk++ = *rj;
		*rj++ = c;
	} while(--n);
}
!Funky!Stuff!



More information about the Comp.sources.unix mailing list