tcsh with editor (again) (4 of 7)
Paul Placeway
paul at osu-dbs.UUCP
Sat Apr 21 04:35:46 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 4 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.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!
More information about the Comp.sources.unix
mailing list