[net.sources] vsh.ar

pn (01/16/83)

!<arch>
README          409019026   70    5     100644  448       `
Vsh is designed for version seven Unix.  It should work on version
six proveded special versions of gtty, stty and getenv are provided.

The builtin parameters in cmdini.c must be correct before compiling.
First, the "argv" variables must reference the proper programs
and files.  Then the parmtab must be modified to reference
other appropriate programs and files.

When finished, execute make to create the vsh program.
Then type "make install".
account.c       409019026   70    5     100644  778       `
#include "hd.h"

/* This records each usage of Vsh in the file LOGFILE */
/* Each entry is in the form "User Time(minutes) Date" */

long time (), ent_time [1];
char *ctime ();

comein () {
time (ent_time);
}

goout () {

/* this proceedure was used upon exit to see who was using
   vsh, for the moment comment out the procedure.
	long lv_time [1];
	char *namep;
	register char *cp;
	FILE *logfile;

	time (lv_time);
	for (namep = cp = envhome; *cp; cp++)
		if (*cp == '/') namep = cp;
	namep++;
	if (compe (namep, "scheibel")) return;
	logfile = fopen (LOGFILE, "a");
	fprintf (logfile, "%8.8s %3.3d %10.10s\n",
		namep, (*lv_time - *ent_time)/60, ctime (ent_time));
	fclose (logfile); */
}

leave () {			/* exit after resetting tty */
	tty_cooked ();
	goout ();
	exit (0);
}
ascii.c         409019026   70    5     100644  803       `
#include "strings.h"	/* See strings.h for comments */

char charclass  [129] = {
/* -1    */	EF,
/* 00-07 */	EF, UD, UD, EF, EF, UD, UD, UD,
/* 08-0f */	UD, WS, EL, UD, UD, EL, UD, UD,
/* 10-17 */	UD, UD, UD, UD, UD, UD, UD, UD,
/* 18-1f */	UD, UD, UD, EL, WS, UD, UD, UD,
/* 20-27 */	WS, SC, SC, SC, SC, SC, SC, SC,
/* 28-2f */	SC, SC, SC, SC, SC, SC, SC, SC,
/* 30-37 */	NU, NU, NU, NU, NU, NU, NU, NU,
/* 38-3f */	NU, NU, SC, SC, SC, SC, SC, SC,
/* 40-47 */	SC, UA, UA, UA, UA, UA, UA, UA,
/* 48-4f */	UA, UA, UA, UA, UA, UA, UA, UA,
/* 50-57 */	UA, UA, UA, UA, UA, UA, UA, UA,
/* 58-5f */	UA, UA, UA, SC, SC, SC, SC, SC,
/* 60-67 */	SC, LA, LA, LA, LA, LA, LA, LA,
/* 68-6f */	LA, LA, LA, LA, LA, LA, LA, LA,
/* 70-77 */	LA, LA, LA, LA, LA, LA, LA, LA,
/* 78-7f */	LA, LA, LA, SC, SC, SC, SC, EF
};

at.c            409019026   70    5     100644  2209      `
#
#include "hd.h"
/*  Display and cursor control routines */

#define BELL	07

int putch ();
extern char *CM, *CL, *UP, *tgoto ();		/* Joy's cursor stuff */

/*  Position cursor at the given (x, y) location.  Origin is
    lower left hand side.  */

atxy (x, y)  int x, y;  {

tputs (tgoto (CM, x, 23 - y), 0, putch);

}

/*  This subroutine takes one parameter.
    Position cursor at line ab from top at character cd.
    At (101) positions cursor at top left;
    At (2480) positions cursor at lower right.  */

at (abcd)  int abcd;  {

	int ab, cd;
	ab = abcd / 100;  cd = abcd - (ab * 100);
	atxy (cd - 1, 24 - ab);
}

erase () {
	tputs (CL, 0, putch);
	putch (CR);
}

/* erase chars on current line */
clearline () {
bufout ();
printf ("%c%80s%s", CR, "", UP);
unbufout ();
}

/* Position cursor at specified file */
atfile (file, col) int file, col; {

atxy (col, 21 - file);
}

/* buffering subroutines */

char outbuf [BUFSIZ];		/* the buffer */
int bcount = 0;			/* can buffer only once */
				/* additional attempts ignored */

bufout () {

if (bcount++ == 0) setbuf (stdout, outbuf);
}

unbufout () {

fflush (stdout);
if (--bcount == 0) setbuf (stdout, CNULL);
}

/* Clear message lines (23-24) for a pcount line message */
/* Lastcount keeps track of the lines with characters on them */
/* Dispdir calls with parameter -1 to reset lastcount */

clearmsg (pcount) int pcount; {

static int lastcount;

at (2301);
if (pcount == -1) lastcount = 0;
else {
	if (lastcount == 0);
	else if (lastcount == 1) {
		clearline ();
	} else {
		bufout ();
		printf ("%159s", "");  unbufout ();
	}
	lastcount = pcount;
}
at (2301);
}

/* Putmsg counts the number of lines in its parameter,
   calls clearmsg with that count, and then displays the message.  */

putmsg (msg) char * msg; {

	extern char * index ();

	clearmsg (index (msg, LF) == NULL ? 1 : 2);
	printf ("   %s", msg);
}

/* Beep bell on terminal */
beep () {
	putch (BELL);
}

/* Print error message about a file */
myperror (parm) char * parm; {

	extern int errno, sys_nerr;
	extern char *sys_errlist[];
	register char *c;

	c = "Unknown error";
	if(errno < sys_nerr)
		c = sys_errlist[errno];
	clearmsg (1);
	printf ("  %s: %s", parm, c);
}

author          409019026   70    5     100644  191       `
.AU
David M. Scheibelhut\s-2\v'-0.4m'1\v'0.4m'\s+2
.AI
Computer Science Division
Electrical Engineering and Computer Sciences Department
University of California
Berkeley, California
and
.IH

classify.c      409019026   70    5     100644  1091      `
#include "hd.h"
#include "classify.h"
#include "mydir.h"

#define aout1	0407
#define aout2	0410
#define aout3	0411
#define aout4	0405
#define	cpio	070707
#define ar	0177545

/* Classify return the file type of parameter fname */

classify (fname) char * fname; {

	int fdesc, rdlen, word;
	unsigned mode;

	char *lastfn();

	if (stat (fname, &scr_stb)) return CL_NULL;

	mode = scr_stb.st_mode & S_IFMT;
	if (mode == S_IFDIR) return CL_DIR;
	if (mode != S_IFREG) return CL_SPCL;

	fdesc = open (fname, 0);
	if (fdesc < 0) return CL_PROTPLN;

	rdlen = read (fdesc, &word, sizeof word);
	close (fdesc);
	if (rdlen < sizeof word) return CL_TEXT;

	if (word == aout1 || word == aout2 || word == aout3 ||
		word == aout4) return CL_AOUT;
	
	if (word == ar) return CL_AR;
	if (word == cpio) return CL_CPIO;

	if (compe (lastfn (fname), "core")) return CL_CORE;

	return CL_TEXT;
}

/* Lastfn returns a pointer to the last file name in path.  */

char *
lastfn (path) register char *path; {
	register char *cp;

	for (cp=path; *cp++;);

	cp--;
	while (*--cp != '/' && cp >= path);

	return ++cp;
}

classify.h      409019027   70    5     100644  507       `
/* Vsh will classify a file as one of the following:  */

#define	CL_NULL		0	/* No such file or protected path */
#define	CL_DIR		1	/* Directory, May NOT be accessable */
#define CL_SPCL		2	/* Special device file */
#define CL_PROTPLN	3	/* Plane file protected unknown fmt */
#define CL_UNKPLN	4	/* Plane file unknown format */
#define CL_AOUT		5	/* A.out format */
#define CL_AR		6	/* Ar format */
#define CL_CPIO		7	/* Cpio format */
#define CL_CORE		8	/* Core dump */
#define CL_TEXT		9	/* Ascii text */

cmdini.c        409019027   70    5     100644  2186      `
#include "hd.h"
#include "command.h"

extern date(), showerror(), showgrep(), file(), home(), grep(),
	wmake(), fmake(), callshell(), longlist(), remove(),
	create(), exec(), display(), options();

struct classstruct classtab[] = {
	"date",		date,		0, 1,
	"showerror",	showerror,	0, 1,
	"showgrep",	showgrep,	0, 1,
	"file",		file,		-1, 1,
	"home",		home,		0, 1,
	"grep",		grep,		0, 1,
	"wmake",	wmake,		0, 1,
	"fmake",	fmake,		0, 1,
	"exec",		exec,		-2, 1,
	"shell",	callshell,	-1, 1,
	"display",	display,	-1, 1,
	"create",	create,		0, 1,
	"longlist",	longlist,	0, 0,
	"remove",	remove,		0, 0,
	"options",	options,	-1, 1,
	"null",		0,		0, 0,
	"",		0,		0, 0
};

static char *whoargv[]  = {"/usr/ucb/w", 0};
static char *yankargv[] = {"/usr/local/yank", 0};
static char *rootargv[] = {"/", 0};
static char *helpargv[] = {"/usr/local/lib/vsh/genhelp", 0};
static char *psargv[] = {"/bin/ps", 0};
static char *bshargv[] = {"/bin/sh", 0};

char *cnull = 0;

struct cmdstruct cmdtab[] = {
	'C',	create,		&cnull, 1,
	'D',	date,		&cnull, 1,
	'E',	showerror,	&cnull, 1,
	'F',	file,		&cnull, 1,
	'G',	grep,		&cnull, 1,
	'I',	display,	&cnull, 1,
	'L',	longlist,	&cnull, 0,
	'M',	wmake,		&cnull, 1,
	'N',	fmake,		&cnull, 1,
	'O',	options,	&cnull, 1,
	'P',	exec,		psargv, 1,
	'R',	remove,		&cnull, 0,
	'S',	showgrep,	&cnull, 1,
	'W',	exec,		whoargv, 1,
	'Y',	exec,		yankargv, 1,
	'^',	home,		&cnull, 1,
	':',	home,		&cnull, 1,
	'!',	callshell,	&cnull, 1,
	'%',	callshell,	&cnull, 1,
	'$',	exec,		bshargv, 1,
	'/',	file,		rootargv, 1,
	'?',	display,	helpargv, 1,

	'A',0,0,0,'B',0,0,0,
	'H',0,0,0,'J',0,0,0,'K',0,0,0,
	'O',0,0,0,'Q',0,0,0,
	'T',0,0,0,'U',0,0,0,'V',0,0,0,'X',0,0,0,
	'Z',0,0,0,'"',0,0,0,'#',0,0,0,
	'&',0,0,0,'(',0,0,0,')',0,0,0,'\'',0,0,0,
	'*',0,0,0,'=',0,0,0,'{',0,0,0,'}',0,0,0,'[',0,0,0,']',0,0,0,
	'~',0,0,0,',',0,0,0,'.',0,0,0,

	CMD_DATE, date,		&cnull, 1,
	CMD_SE,	showerror,	&cnull, 1,
	CMD_SG,	showgrep,	&cnull, 1,
	0,0,0,0
};

struct parmstruct parmtab[] = {
	"editor", "/usr/ucb/vi",
	"make", "/bin/make",
	"grep", "/bin/grep",
	"rmhelp", "/usr/local/lib/vsh/rmhelp",
	"showhelp", "/usr/local/lib/vsh/showhelp",
	"makerror", ".makerror",
	"grepout", ".grepout",
	0, 0
};
cmdload.c       409019028   70    5     100644  2357      `
#include "hd.h"
#include "command.h"

/* This loads the command table from the .vshrc file */

cmdldrc () {
	FILE *rcstream,		/* Stream of .vshrc */
	     *fopen();		/* Stdio open proc */

	char cbuf [STRMAX],	/* Buffer for input parm */
	     *argv [ARGVMAX];	/* Pointers to input parm */
	int argc;		/* Number of parm */

	int line = 1;		/* Current line num of rcstream */

	/* initialize */

	strcpy (cbuf, envhome);
	strcat (cbuf, "/.vshrc");
	rcstream = fopen (cbuf, "r");
	if (rcstream == NULL) return;
	printf ("Loading .vshrc\n");

	while (!feof (rcstream)) {
		if (readarg (rcstream, &line, &argc, argv, cbuf)
			!= FAILURE)
			cmdldarg (line-1, argc, argv);
	}
}

/* This loads command tables as specified in its arguments */
cmdldarg (line, argc, argv)
	int line, argc;  char *argv[]; {

	char *malloc();		/* Standard allocation proc */
	register i;		/* An index */

	register struct cmdstruct *cmdp;	/* Pointers */
	register struct classstruct *classp;
	register struct parmstruct *parmp;

	/* An addressable representation of CNULL (0) */
	extern char *cnull;

	if (argc <= 0) ;
	else if (argc == 1) lderror ("Too few args", line);

	else if (strlen (argv [0]) == 1) {
		cmdp = cmdloc (*argv [0]);
		if (cmdp->cmd_char == 0) {
			lderror ("Not a proper command",
			line);
			return;
		}
		classp = classloc (argv [1]);
		if (*classp->cl_name == 0) {
			lderror ("Illegal keyword", line);
			return;
		}
		if (!(
			argc-2 == classp->cl_count ||
			(classp->cl_count == -1 && argc <= 3) ||
			(classp->cl_count == -2 && argc >= 3))){

			lderror
			("Improper number number of parmeters", line);
			return;
		}
		/* All testing over with--store new command */

		cmdp->cmd_proc = classp->cl_proc;
		cmdp->cmd_xdir = classp->cl_xdir;

		if (argc <= 2) cmdp->cmd_argv = &cnull;
		else cmdp->cmd_argv  =
			(char **) malloc ((argc - 1) * (sizeof *argv));

		for (i=2; i<argc; i++) {
			cmdp->cmd_argv [i-2] =
				malloc (strlen (argv [i]) + 1);
			strcpy (cmdp->cmd_argv[i-2], argv [i]);
			cmdp->cmd_argv [i-1] = CNULL;
		}
	}
	else {
		for (parmp = parmtab;
			parmp->p_name &&
			strcmp (parmp->p_name, argv[0]);
			parmp++);
		if (parmp->p_name) {
			parmp->p_val = malloc
				(strlen (argv[1]) + 1);
			strcpy (parmp->p_val, argv[1]);
		}
		else lderror ("Bad parameter name", line);
		if (argc != 2) lderror
			("Too many args", line);
	}
}

cmdrun.c        411519948   70    5     100644  2368      `
#
#include "hd.h"
#include "command.h"

/* Each command called by command.c must return special information.
   First, the next command to execute is returned in the last eight
   bits (and can be masked out with CMDMASK).  If no more commands
   are to be run, these bits are set to 0.  The next bit is the
   REPLOT bit.  If on, the screen has been altered enough to require
   a replot.  The NOOP bit indicates a command was not found.
   Finally, the ENTERDIR bit indicates a new directory has been
   entered.
*/

command (cmd, ctype) register cmd;  int ctype; {

        register ret;           /* return value */
        int next;               /* Temp variable for next command */
        register struct cmdstruct *cmdp;

        ret = 0;
        while (cmd) {
                if ((ctype == DIRCMD) &&
                    ((next = dircmd (cmd)) != NOOP)
		   )
                        cmd = next;
                else {
                        cmdp = cmdloc (cmd);
                        if (cmdp->cmd_proc && (
                                (cmdp->cmd_xdir)||(ctype == DIRCMD))) {

                                cmd = cmdp->cmd_proc (cmdp->cmd_argv);
                        }
                        else cmd = NOOP;
                }
                ret |= cmd & (REPLOT | ENTERDIR | NOOP);
                cmd &= CMDMASK;
        }
        if (ret & NOOP) {
                putmsg ("Not found.  Press ? for help.  ");
                if (ret & REPLOT) getrtn ();
        }
        return ret;
}

/* Classloc returns the classtab element corresponding the keyword referenced
   by cp */

struct classstruct *
classloc (cp) register char *cp; {

        register struct classstruct *classp;

        for (classp = classtab;
                *classp->cl_name && strcmp (cp, classp->cl_name);
                classp++);
        return classp;
}

/* Cmdloc returns the cmdtab element corresponding to ch */

struct cmdstruct *
cmdloc (ch) register char ch; {

        register struct cmdstruct *cmdp;

        for (cmdp = cmdtab;
                cmdp->cmd_char && cmdp->cmd_char != ch;
                cmdp++);

        return cmdp;
}

/* Cmdproc returns a pointer to the procedure which runs the command
   corresponding to ch.  */

int (*
cmdproc (ch))() char ch; {
        extern struct cmdstruct *cmdloc();

        return ((*cmdloc)(ch)->cmd_proc);
}
command.h       409019028   70    5     100644  1462      `
/* There are two important structures used by the command processor.
   Cmdtab encodes the current commands.  Each command corresponds to
   an element in cmdtab and is type struct cmdstruct.  Four fields
   determine the command.  Cmd_char is the character which, when
   pressed, calls the command.  Cmd_proc is the procedure which
   runs the command.  Cmd_argv is used as a parameter to that proc.
   Cmd_xdir, when on, indicates the command may be run anywhere, and
   not just on the directory page.

   The classtab is used to map statements from the .vshrc file to
   cmdtab elements.  The keyword in a .vshrc statement is looked
   up in classtab, and then the classtab element is used to load
   the cmdtab element.
*/

struct classstruct {		/* Classification structure */
	char * cl_name;		/* Name (key word) */
	int (*cl_proc)();	/* Command procedure */
	short cl_count;		/* Number of args */
	char cl_xdir;		/* Is ok outside dir page */
};

struct cmdstruct {		/* Command structure */
	char cmd_char;		/* Command char */
	int  (*cmd_proc)();	/* Command procedure */
	char ** cmd_argv;	/* Array of arguments for command */
	char cmd_xdir;		/* Is ok outside dir page */
};

struct classstruct *classloc();
struct cmdstruct   *cmdloc  ();
extern (*cmdproc ())();

extern struct classstruct classtab[];
extern struct cmdstruct   cmdtab[];

/* Command characters for important commands */

#define	CMD_DATE	0x80
#define	CMD_SE		0x81
#define	CMD_SG		0x82
curdir.c        409019028   70    5     100644  740       `
#
/*  Curdir finds the name of the working directory and puts in
    wdname.  This is done by calling on the pwd program.
*/
#include "hd.h"
#include "mydir.h"
static char pwd[] = {"/bin/pwd"};

curdir () {

int p, rdlen, pipefile [2];
#define pipein	pipefile [0]
#define pipeout	pipefile [1]

pipe (pipefile);
printf ("%s\n", pwd);
if ((p = myfork ()) == 0) {
	close (outfile);  dup (pipeout);
	close (pipein);  close (pipeout);
	execl (pwd, pwd, 0);
	exit (1);
} else {
	close (pipeout);
	join (p);
	rdlen = read (pipein, wdname, sizeof wdname);
	if (rdlen < 2 || rdlen == sizeof wdname || wdname [0] != '/') {
		printf ("Cannot find name of working directory\n\r");
		return 1;
	}
	wdname [rdlen - 1] = 0;
	close (pipein);
}
return 0;
}
curses.c        409019028   70    5     100644  480       `
#include "hd.h"
char *BC, *UP, *CM, *CL;  short ospeed;
short PC;
char tspace [512], *atspace;
char *tgetstr ();

curs_init () {

char bp [1024];
char * tgoto ();
char * getenv ();
int putch ();

tgetent (bp, envterm);
atspace = tspace;
PC = tgetflag ("pc", &atspace);
CM=tgetstr ("cm", &atspace);  CL=tgetstr ("cl", &atspace);
UP = tgetstr ("up", &atspace);  BC = tgetstr ("bc", &atspace);
if (CM == CNULL) {
	printf ("Must have a cursor addressable terminal\n");
	leave ();
}
}
dir.c           409019029   70    5     100644  4552      `
#include "hd.h"
#include "mydir.h"

#define	curmtime	wd_stb.st_mtime

/* See dir.h for information about these variables */
int tfiles, tpages, cpage, pageend;

struct stat wd_stb;

int wdfile;
char wdname [MPLEN + 1];

struct dir dirbuf [mfiles + 1];

char *d_namep [mfiles];

int dir_status;
struct stat scr_stb;

char *dirmsg [] = {
	"", "System Error", "Too big to Load", 
	"Executable but not Readable"
};

/*  Enterdir -- Enter into new directory.
    The parameter newdir is the file to change to.  Pass a single
    element file name or a full path name. "file", "..", or "."
    are OK.  "../..", "/a/b/..", "a/", or "a/." must be passed
    to the file procedure instead.

    On success, there is a new current directory, and wdname has been
    modified to reflect the new path name.  ENTERDIR | REPLOT is the
    return value.  On failure, FAILURE is returned.  Nothing is changed.
*/

enterdir (newdir)  char * newdir;  {

int entermode;		/* Type of entry */
#define	FOREWARD	0	/* Deaper into dir */
#define	BACKWARD	1	/* Previous dir (..) */
#define	STOP		2	/* Stay in same place (.) */
#define LOAD		3	/* Load new path name */

if (compe (newdir, DOT)) entermode = STOP;
else if (compe (newdir, DOTDOT)) entermode = BACKWARD;
else if (newdir[0] == '/') entermode = LOAD;
else entermode = FOREWARD;

if (entermode == FOREWARD && strlen (wdname) > LPLEN) {
	putmsg ("Cannot chdir -- Pathname too long");
	return FAILURE;
}

if (chdir (newdir)) {
	myperror (newdir);
	return FAILURE;
}

if (entermode == STOP);			/* change to "." */
else if (entermode == BACKWARDS) {	/* change to ".." */
	todotdot (wdname);
	if (ISROOT (wdname)) chdir (SLASH);
}
else if (entermode == LOAD) strcpy (wdname, newdir);

else {
	if (!ISROOT (wdname)) strcat (wdname, SLASH);
	strcat (wdname, newdir);	/* go deaper into dir */
}

close (wdfile);  wdfile = open (DOT, 0);  dir_status = unloaded;
cpage = 1;
return ENTERDIR | REPLOT;
}

/* loaddir loads dir and sets assoc parameters */
loaddir () {

tpages = tfiles = 0;
if (wdfile < 0) {dir_status = protected;  return;}
else dir_status = unloaded;

fstat (wdfile, &wd_stb);

if (wd_stb.st_size > max_dir_size) {
	dir_status = toobig;  return;
}
lseek (wdfile, 0L, 0);		/* read in entire file */
read (wdfile, dirbuf, (int) wd_stb.st_size);

sortdir ();

tpages = tfiles / nfpp + ((tfiles % nfpp) > 0);
dir_status = loaded;
return;
}

/* sortdir sorts the directory entries.  when done, the following is true:
    1)  tfiles contains the number of files available
    2)  the d_namep array will contain pointers to the files.
	these will be sorted assending.
*/
sortdir () {

struct dir *maxent, *dirp;
int dircmp ();

tfiles = 0;
maxent = & dirbuf [wd_stb.st_size / dirsize];
for (dirp = dirbuf; dirp < maxent; dirp++) {
	if (dirp->d_ino) d_namep [tfiles++] = dirp->d_name;
	dirp->d_ino = 0;
}
qsort (d_namep, tfiles, sizeof d_namep [0], dircmp);
}

dircmp (a, b) char **a, **b; {
	return strcmp (*a, *b);
}

/*  Dispdir displays a page of the directory.
    W A R N I N G.  Dispdir modifies global data.  If the dir is not
    loaded, or is out of date, dispdir will call on loaddir.
    Cpage can be adjusted to conform to the current dir.
    An out of date dir is reloaded only if reload is true.

    In general, the goal of dispdir is to make sure the internal
    representation of the directory is consistent with the real
    directory, and what is displayed is consistent with the internal
    directory.
*/

dispdir (reload) int reload; {

int dirx;		/* index into dirbuf */
int dirchar;		/* char to select file assoc. with dirx */

long lastmtime;		/* last time dir was modified */

bufout ();  clearmsg (-1);
if (reload) {
	lastmtime = curmtime;
	fstat (wdfile, &wd_stb);
	if ((lastmtime != curmtime) || dir_status) loaddir ();
}

cpage = max (1, min (cpage, tpages));
pageend = 0;

erase ();
printf ("Directory = %s  %s", wdname, dirmsg [dir_status]);

if (tfiles == 0) {
	unbufout ();  return;
}

if (tpages > 1) {
	at ((strlen (wdname) > 50) ? 268 : 168);
	printf ("Page %d / %d    ", cpage, tpages);
}

pageend = tfiles % nfpp;
if (cpage != tpages || pageend == 0) pageend = nfpp;

for (dirx = 0, dirchar = 'a'; dirx < pageend;  dirx++, dirchar++) {
	atfile (dirx, 3);
	printf ("%c  %s", dirchar, filename (dirx));
}
unbufout ();
}

/*	Change dir to father of dir  */
todotdot (dir) char *dir; {

	register char *cp;

	for (cp = dir; *cp; cp++);	/* Scan to end of name */
	while (*--cp != '/');		/* Scan back to a slash */
	if (cp == dir) cp++;		/* Must handle root specially */
	*cp = 0;
}
dir.h           409019029   70    5     100644  1267      `
/* Copyright (c) 1982 Regents of the University of California */

/* @(#)ndir.h 4.4 3/30/82 */

/*
 * This sets the "page size" for directories.
 * Requirements are DEV_BSIZE <= DIRBLKSIZ <= MINBSIZE with
 * DIRBLKSIZ a power of two.
 * Dennis Ritchie feels that directory pages should be atomic
 * operations to the disk, so we use DEV_BSIZE.
 */
#define DIRBLKSIZ 512

/*
 * This limits the directory name length. Its main constraint
 * is that it appears twice in the user structure. (u. area)
 */
#define MAXNAMLEN 255

struct  direct {
        u_long  d_ino;
        short   d_reclen;
        short   d_namlen;
        char    d_name[MAXNAMLEN + 1];
        /* typically shorter */
};

struct _dirdesc {
        int     dd_fd;
        long    dd_loc;
        long    dd_size;
        char    dd_buf[DIRBLKSIZ];
};

/*
 * useful macros.
 */
#undef DIRSIZ
#define DIRSIZ(dp) \
    ((sizeof(struct direct) - MAXNAMLEN + (dp)->d_namlen + sizeof(ino_t) - 1) &\
    ~(sizeof(ino_t) - 1))
typedef struct _dirdesc DIR;
#ifndef NULL
#define NULL    0
#endif

/*
 * functions defined on directories
 */
extern DIR *opendir();
extern struct direct *readdir();
extern long telldir();
extern void seekdir();
#define rewinddir(dirp) seekdir((dirp), 0)
extern void closedir();

dircmd.c        409019029   70    5     100644  675       `
#include "hd.h"
#include "mydir.h"

dircmd (cmd) register cmd; {

	register ret;		/* return value */

	ret = REPLOT;

	if (cmd == EOT) leave ();
	else if (cmd == LF)
		ret = enterdir (DOTDOT);
	else if ((cmd == '+') || (cmd == ';') || (cmd == 'u'))
		cpage = ((cpage >= tpages) ? 1 : cpage + 1);
	else if (cmd >= 'a' && cmd <= 'z') {
		cmd -= 'a';
		if (cmd >= pageend) ret = NOREPLOT;
		else if (enterfile (filename (cmd)) == FAILURE) {
			longfile (cmd);  ret = NOREPLOT;
		}

	} else if (cmd > '0' && cmd <= '9')
		cpage = cmd - '0';
	else if (cmd == '0') cpage = 10;
	else if (cmd == '-') {
		cpage--;
		if (cpage < 1) cpage = tpages;
	}
	else ret = NOOP;
	return ret;
}

dirlist.c       409019029   70    5     100644  1457      `
#include "hd.h"
#include "mydir.h"
#include "command.h"

#define SHIFT1	13
#define SHIFT2	6
#define SHIFT3	3
#define MASK	07

pfstatus (fname) char * fname; {

/* long listing of a file */
static char *mchar [2] [8] = {
	"---", "--x", "-w-", "-wx",
	"r--", "r-x", "rw-", "rwx",
	"--s", "--s", "-ws", "-ws",
	"r-s", "r-s", "rws", "rws"
};

static char fformat [8] = {' ', 'c', 'd', 'b', '-', '?', '?', '?'};

register mode;

if (stat (fname, &scr_stb)) {
	printf ("Cannot Access");
	return;
}
mode = scr_stb.st_mode;

printf ("%c%c%s%s%s%s%2d",
	(mode & S_ISVTX) ? 't' : ' ',
	fformat [(mode >> SHIFT1) & MASK],
	mchar [(mode & S_ISUID) != 0] [(mode >> SHIFT2) & MASK],
	mchar [(mode & S_ISGID) != 0] [(mode >> SHIFT3) & MASK],
	mchar [0] 		      [mode & MASK],
	scr_stb.st_nlink > 99 ? "" : " ",
	scr_stb.st_nlink);
if (mode & S_IFCHR)
	printf ("%5d, %3d",
	major (scr_stb.st_rdev), minor (scr_stb.st_rdev));
else
	printf ("%10D", scr_stb.st_size);
printf (" %.24s", ctime (&scr_stb.st_mtime));
}

/* This prints out the protection modes of the files on the current
   page.  It knows the page from the global variable cpage.  The
   value NOREPLOT is always returned, to make globalcmd happy.
*/
longlist () {		/* long listing for entire page */

	register i;
	bufout ();
	for (i=0; i < pageend; i++) longfile (i);
	unbufout ();

	return CMD_DATE;	/* Run date command */
}

longfile (index) int index; {

atfile (index, 22); pfstatus (filename (index));
}

enterf.c        409019029   70    5     100644  1091      `
#include "hd.h"
#include "mydir.h"
#include "classify.h"

/* Enterfile selects its parameter fname;  Fname may either be a single
   element file name (no slashes) or a full path name.  Enterfile
   can handle files which look like ".", "..", "file.c", or "/a/b".
   For files which could look like "/a/b/", "a/b", or "./../../a" use
   the procedure "file" (file expects an argument in argv format).
*/
enterfile (fname) char *fname; {

switch (classify (fname)) {

	case CL_NULL:  case CL_PROTPLN:
	myperror (fname);  return FAILURE;

	case CL_DIR:
	return enterdir (fname);

	case CL_CORE:
	f_exec (DEBUGGER, DEBUGGER, fname, 0); return SUCCESS;

	case CL_AOUT:
	p_exec ("/bin/nm", "nm", fname, 0);  return REPLOT;

	case CL_AR:
	p_exec ("/bin/ar", "ar", "vt", fname, 0);  return REPLOT;

	case CL_CPIO:
	p_exec ("/bin/cpio", "cpio", "-vt", 0);  return REPLOT;

	case CL_TEXT:
	f_exec (EDITOR, EDITOR, fname, 0);  return REPLOT;

	default:
	putmsg (fname);  printf (":  Vsh cannot handle this file");
}
return FAILURE;
}
home () {			/* enter home directory */

	return file (&envhome);
}

file.c          409019030   70    5     100644  3958      `
#include "hd.h"
#include "mydir.h"
#include "strings.h"
#include "classify.h"

/* File gets a file name, converts it into a full path name, and
   selects that file.  */

file (argv) char **argv; {	/* Select specific file */

	char nname [STRMAX];

	if (*argv == CNULL) putmsg ("File:  ");
	if (getfname (*argv, nname) == FAILURE) return FAILURE;

	return (enterfile (nname));
}

/* Create accepts a file name, creats that file, and then enters it.  */
create () {

	char nname [STRMAX], oname [STRMAX];
	register char ch;  char *cp; int class;

	putmsg ("Create:  ");
	if (getfname (CNULL, nname) == FAILURE) return FAILURE;

	if (!access (nname, 0)) {
		putmsg (nname);  printf (":  Already exists");
		return FAILURE;
	}

	todotdot (nname);
	class = classify (nname);

	if (class == CL_NULL || access (nname, 3)) {
		myperror (nname);  return FAILURE;
	}
	else if (class != CL_DIR) {
		putmsg (nname);  printf (":  Not a directory");
		return FAILURE;
	}

	/* Un do effect of todotdot */
	for (cp = nname; *cp++;);
	*--cp = '/';

	erase ();  printf ("Creating %s\n\nSelect:\n\n", nname);

	printf
	("1  Text\n2  Directory\n3  Copy a file\n4  Link a file\n\n");

	ch = getch ();  putch (LF);
	if (ch == '1') {
		f_exec (EDITOR, EDITOR, nname, 0);
	}
	else if (ch == '2') {
		if (f_exec ("/bin/mkdir", "mkdir", nname, 0)) getrtn ();
		return enterdir (nname) | REPLOT;
	}
	else if (ch == '3' || ch == '4') {
		printf ("From:  ");
		if (getfname (CNULL, oname) == FAILURE) return REPLOT;

		switch (classify (oname)) {

			case CL_NULL:  case CL_PROTPLN:
			myperror (oname);  break;

			case CL_DIR:
			printf ("Cannot copy or link to directories");
			break;

			default:
			if (ch == '3') f_exec
				("/bin/cp", "cp", oname, nname, 0);
			if (ch == '4') {
				printf ("Linking\n");
				if (link (oname, nname))
					myperror ("Link failed");
			}
			at (1501);
			if (!access (nname, 0)) {
				printf ("File created.\
  Do you wish to examine it?  ");
				if (getch() == 'y') {
					putch (LF);
					return
					enterfile (nname) | REPLOT;
				}
				return REPLOT;
			}
		}
		at (1501);
		printf ("File not created. ");
		getrtn ();
	}
	return REPLOT;
}

/* Getfname takes two character arrays as parameters.
   Inname is the partial pathname of a file.  If inname == CNULL,
   getfname will instead read the partial pathname from the terminal.
   The full pathname is returned in outname.
   Getfname has a return value of SUCCESS or FAILURE.
*/
getfname (inname, outname) char *inname, *outname; {
	char inword [STRMAX];  int ilen;

	if (inname == CNULL) {
		tty_push (COOKEDMODE);
		ilen = getword (inword);
		tty_pop ();
		if (ilen <= 0) {
			clearmsg (0);
			return FAILURE;
		}
		inname = inword;
	}
	if (pathgen (wdname, inname, outname)) {
		putmsg ("Path name too long");
		return FAILURE;
	}
	return SUCCESS;
}

/* If one is in dir with pathname "old", and does a chdir "change",
   one ends up in directory "new".  Exception: ".." always breaks
   through to the root.
*/
pathgen (old, change, new) char *old, *change, *new; {

char element [DIRSIZ + 1];
char chgbuf  [STRMAX];
register len;

if (change [0] == '/') strcpy (new, SLASH);
else strcpy (new, old);

strcpy (chgbuf, change);	/* So change is not modified */

while (*chgbuf) {

	extract (element, chgbuf);
	if (compe (DOT, element));
	else if (compe (DOTDOT, element)) todotdot (new);

	else {
		len = strlen (new);
		if (len > LPLEN) return 1;
		else if (len > 1) strcat (new, SLASH);
		strcat (new, element);
	}
}
return 0;
}

extract (element, path) char *element, *path; {

register char *cp;
int eltlen;

for (cp = path; *cp != 0 && *cp != '/'; cp++);

eltlen = cp - path;
if (eltlen == 0) {
	strcpy (element, DOT);
}
else {
	strcpyn (element, path, DIRSIZ);
	element [min (eltlen, DIRSIZ)] = 0;
}
if (*cp) shift (path, eltlen + 1);
else path [0] = 0;
}

shift (path, length) char *path;  int length; {

register char *cp;

for (cp = path + length; cp [-1];) *path++ = *cp++;
}
footnote        409019030   70    5     100644  221       `
.FS
1. Work reported herein was supported in part by 
the U. S. Department
of Energy, Contract DE-AT03-76SF00034, Project Agreement
DE-AS03-79ER10358, and the National Science
Foundation under Grant No.  MCS 7807291.
.FE

genhelp         409019030   70    5     100644  787       `
			  Command Summary
Command		Action

^D		Leave Vsh
1-9		Select page 1-9
0		Select page 10
+		Select next page
-		Select previous page
;		Same as +

a-t		Select associated file
^ (HOME)	Change to Home directory
/		Change to root
?		Display this page
!		Execute shell (As specified in SHELL environ parameter)
%		Same as !
$		Execute Bourne shell
line feed	Change to previous (parent) directory.





Command		Action

C		Create file or directory
D		Date
E		Show Errors from previous make
F		File selection by name
G		Grep
I		DIsplay text file
L		Long listing of files on current page
M		Make
N		Make forked off
O		Options
P		Ps (Print processes)
R		Remove files
S		Show output of Grep
W		Who
Y		Yank








Future features:

Macros in .vshrc
Environment stack
Selective display of files

grep.c          409019030   70    5     100644  1738      `
#include "hd.h"
#include "strings.h"
#include "command.h"

/* Interface to grep */

#define GBUFLEN 200
#define LIM1	(gbuf + 100)

#define QUOTE	'\''
#define BSLASH	'\\'

#define GSTR1	" -n '"
#define GSTR2	"' /dev/null "

#define CPSET	{cp = gbuf + strlen (gbuf);}

#define	GREPLEAVE	{tty_pop ();  clearmsg (0);  return NOREPLOT;}

/* User is asked to supply patterns and file names.  Grep is then
   run with its output directed to .grepout.  If the user changes his
   mind, and leaves without running grep, NOREPLOT is returned.
   Else REPLOT | CMD_SG is returned.
*/
grep () {
	register char *cp, *clim;  register ch;
	char gbuf [GBUFLEN];
	char inline [STRMAX];  int inlength;  register char *incp;

	FILE *sstream;
	extern FILE *showopen ();

	int saveout;

	extern char wdname [];

	tty_push (COOKEDMODE);
	strcpy (gbuf, GREP);  strcat (gbuf, GSTR1);
	clearmsg (2);  printf ("Grep pattern: ");
	CPSET;  clim = LIM1;

	inlength = getline (inline);  incp = inline;
	if (inlength == 0) GREPLEAVE;

	while ((ch = *incp++) && cp < clim) {
		if (ch == QUOTE) {
			*cp++ = QUOTE;  *cp++ = BSLASH;
			*cp++ = QUOTE;  *cp++ = QUOTE;
		} else *cp++ = ch;
	}
	*cp=0;
	strcat (gbuf, GSTR2);
	at (2317 + inlength);  printf ("--  Grep files: ");

	CPSET;
	inlength = xgetline (stdin, cp, GBUFLEN - strlen (gbuf));
	if (inlength == 0) GREPLEAVE;

	/* Now run the command in gbuf */

	sstream = showopen ("w", GREPMODE);
	if (sstream == NULL) GREPLEAVE;

	printf ("Searching\n");

	saveout = dup (outfile);	/* Set up files */
	close (outfile); dup (fileno (sstream)); fclose (sstream);

	printf ("%s is search directory\n", wdname);
	mysystem (gbuf);
	close (outfile);  dup (saveout);  close (saveout);


	tty_pop ();  return CMD_SG | REPLOT;
}
hd.h            409019031   70    5     100644  2200      `
/* Header file for all procedures in Vsh */
#include "stdio.h"

#define VERSION	2.5

#define max(arg1,arg2)	((arg1 > arg2) ? arg1 : arg2)
#define min(arg1,arg2)	((arg1 < arg2) ? arg1 : arg2)
#define compe(arg1,arg2)  (strcmp (arg1, arg2) == 0)

/* Standard file numbers */
#define infile		0
#define outfile		1
#define errorfile	2

/* The values of special keys */
#define EOT		4
#define	RUBOUT		0177
#define	CR		015
#define	LF		012

/* Standard file names */
#define LOGFILE		"/usr/grad/scheibel/vsh/log"
#define DEBUGGER	"/bin/adb"

/* Other parameters */
#define	STRMAX	120	/* Length of string buffers */
#define CNULL	((char *) 0)	/* Null char pointer */
#define ARGVMAX		20	/* Size of a readarg argv */

/* Parms loadable through .vshrc are accessed through command.h.
   Alter their default values in cmdini.c	*/

/* Tty_set parameters */
#define RAWMODE		0
#define	COOKEDMODE	1

/* Pointers to data about the environment */
extern char *envshell, *envhome, *envedit, *envterm;

/* Functions called by command return the next command to execute.  */
/* In addition, the following bits are returned.  */

#define CMDMASK		0x00ff	/* Bits of next command */
#define	REPLOT		0x0100	/* Must replot directory */
#define NOOP		0x0200	/* Return for command not found */
#define ENTERDIR	0x0400	/* New directory entered */

/* If no special return is necessary, use return REPLOT or NOREPLOT. */
#define NOREPLOT	0x0000

/* Failure and success may be a double meaning for REPLOT/NOREPLOT.
   Check individule commands.  */

#define SUCCESS		REPLOT
#define	FAILURE		NOREPLOT

/* When calling command, indicate type of command:  */
#define DIRCMD	1
#define SHOWCMD	2

/* Show operates in two modes */
#define GREPMODE	0
#define MAKEMODE	1

/* Parameters from .vshrc file. P_name is the parameter name, p_val is
   the parameter's value.
*/

struct parmstruct {
	char *p_name,
	     *p_val;
};

extern struct parmstruct  parmtab[];

/* References to the various parameters */

#define	EDITOR		parmtab[0].p_val
#define	MAKE		parmtab[1].p_val
#define	GREP		parmtab[2].p_val
#define	RMHELP		parmtab[3].p_val
#define	SHOWHELP	parmtab[4].p_val
#define	MAKERROR	parmtab[5].p_val
#define	GREPOUT		parmtab[6].p_val
help.c          409019031   70    5     100644  521       `
#include "hd.h"

/* Help displays a file in paged mode.  The parm is the file name. */

help (parm) char *parm; {

FILE *helpfile;

helpfile = fopen (parm, "r");
if (helpfile == NULL) {
	myperror (parm);
	return FAILURE;
}
else page (helpfile);
fclose (helpfile);
return SUCCESS;
}

/* Display is the command processor's version of help */
display (argv) char **argv; {
	char name [STRMAX];

	if (*argv == CNULL) putmsg ("Display:  ");

	if (getfname (*argv, name) == FAILURE) return FAILURE;
	else return help (name);
}

intro.ms        409019032   70    5     100644  8568      `
.de VS
.I Vsh
..
.TL
A Visual Command Processor
for the
\s-2UNIX\s0 Operating System
.so author
.AB
.VS
is a visual interactive command processor or shell for the
.UX
[1] operating system.
.VS
is an attempt to provide a command processor for
high-bandwidth video terminals.
This orientation results in the printing of much more information
than provided by traditional command processors.
This orientation also results in closer control
over what is printed so nothing unintentionally
scrolls off the top of the screen.
The following set of papers describe
.I Vsh.
The first,
"A Visual Command Processor for the \s-2UNIX\s0 Operating System,"
describes the goals of
.VS
and how successfully they have been met.
The second,
"Vsh Code Organization,"
describes the implementation.
The third is a user's manual.
Finally, the appendix contains copies of the help files
available to the online user.
.AE
.PP
.so footnote
.ls 2
.SH
Introduction.
.PP
A program tends to complement its input/output devices.
For example,
batch programs interface to high speed printers.
A batch program's output tends to be lengthy and detailed,
since the printing costs little and extra detail
may obviate the need for extra runs.
Existing
.UX
programs have the opposite tendency since
they were usually built for slow, hardcopy terminals.
Because printing takes so much time,
messages tend to be terse,
and information is printed only when specifically requested.
High-bandwidth video terminals benefit from a different orientation:
One does not worry excessively about the time it takes to print
messages.
Instead, one is
concerned about losing information off the
top of the screen before it is used.
.SH
Operation.
.PP
.VS
is an attempt to apply this alternative strategy to a command processor.
.VS
is highly visual.
It automatically prints a considerable amount of information,
and makes sure that information does not
scroll off the top of the screen.
.PP
.VS
is organized around the
.UX
file system.
This file system has a hierarchical structure which can be
represented as a tree.
Each node in the tree is a directory and each leaf a file.
Each node and leaf has a name.
To reference a file one specifies the string of names
which traverses the file system from the root to the desired file.
One may also reference files relative to one's working directory.
This directory may be set
to any accessible directory in the file system.
.PP
.VS
operates by automatically displaying
the members of the working directory.
Each member is labeled with a lower case letter
in the fashion of an indexed menu.
To select a member one presses its corresponding letter.
After selection,
.VS
determines the nature of the file and takes appropriate action.
If the member contains \s-2ASCII\s0 text,
.VS
calls on the editor.
If the member is a directory,
.VS
makes it the working directory and displays its files.
.VS
recognizes many other types of files
as described in the user's manual.
.PP
Directories with a large number of members
cannot be displayed in a single screen.
Such directories are broken down into single-screen pages.
These pages are selected by typing in their corresponding number.
To select a file in such a directory,
one first selects the page, then the file.
.PP
One can do more than select files and directories.
By pressing upper-case letters and special characters
one can select commands.
There is a set of basic commands
which allow one to create and delete files,
invoke compilers, and perform other basic functions.
One may also define new commands in terms of the basic commands
and in terms of other programs,
and in this way create a personalized environment.
.PP
Although the
.VS
program is not particularly large,
it has a large number of features.
The difficult part in implementing
.VS
was connecting these parts together so they would coexist
in a harmonious fashion.
I believe the parts do fit together in a clean manner.
The companion document to this paper,
"Vsh Code Organization,"
covers the details of how
.VS
really works.
.SH
Visual Techniques.
.PP
The hallmark of visual software is careful control of output.
Relevant information appears on the screen when needed,
and does not scroll off the top before use.
Because one can only put so much information on a screen,
it is possible to jump between displays
in an easy, natural manner.
I believe
.VS
does satisfy these criterion.
.PP
The ease of jumping between displays was an important
consideration during the design of
.I Vsh.
This emphasis makes
.VS
an ideal tool for browsing through the
.UX
file system,
for one can move easily between the various directories and files.
This convenience also extends to the operation of compilers.
When one compiles a program,
the compiler error messages are collected in a file and displayed.
One then selects an error message from the display,
and
.VS
determines the line and file the message references.
An editor is then called,
editing the file starting at the offending line.
When the error is fixed,
one can return to the error display to select another.
This is a large improvement compared to
just dumping the error messages on the terminal,
because the risk of losing the errors off the top of the screen
before use is eliminated.
.PP
A visual editor called
.I Vi
[2]
has been well received in Berkeley
where one finds little use of the standard editor.
.I Vi
is similar to
.I Vsh;
it automatically displays information (the file being edited)
and is based on short, often single keypress, commands.
A command processor, however, is not an editor.
One does not find the dramatic improvement of
.I Vi
over the standard editor when one uses
.I Vsh.
Part of the problem is that
.VS
does not have the full power and flexibility of the standard
.UX
command processor.
It seems clear that the use of single-keypress commands was
just too restricting.
A richer command language is necessary.
.PP
Historically,
.VS
was not the first visual command processor, but was inspired by
The University of Illinois \s-2PLATO\s0 [3] system.
With \s-2PLATO\s0, everything is visual.
This orientation is in part a result of \s-2PLATO\s0's
computer assisted instruction emphasis,
and in part a reflection on the nature of the \s-2PLATO\s0 terminal
which cannot scroll.
My goal was to merge the best features of \s-2PLATO\s0 and
.UX
philosophies.
A result of this synthesis is
.I Vsh.
.PP
A major problem with \s-2PLATO\s0 is that there is no alternative
to the visual, interactive way of doing things.
If one wants to edit a block,
one cannot specify the block by name.
One must select it from the display.
The
.VS
.B F
command allows one to select a file by name,
easing the problem somewhat.
A major difficulty remains;
one cannot easily consider writing a script of commands for
.I Vsh.
Such a script with its single-keypress commands would be unreadable.
Clearly,
one needs a traditional command processor for many applications.
The issue is whether it is worthwhile
to learn both modes of interaction.
At Berkeley,
people are willing to use both types of editors.
Perhaps people will embrace two shells.
.SH
Further Work.
.PP
It would be interesting to see
.VS
go beyond its current restrictive status
and implement a universally applicable visual shell.
This shell would be the focus of a visually oriented programming system
as available in Xerox's Interlisp [4] system.
One can imagine countless features.
One would be able to split the screen into sub-displays
and run a separate process in each display.
One would be able to recall previous displays.
These features could support utilities such as split-screen editors.
Communications between users would be facilitated.
Messages from other users would appear in a special display,
rather than just showing up at the bottom of the screen.
One could allow others to monitor one's own displays.
.PP
Many questions need to be addressed in such a system,
including the appearance of the user and operating system interfaces,
and the appearance of the terminal.
The use of multiple screens, cursors, touch panels,
special keysets, and intelligent terminals are areas which
ought to be addressed.
What ever its drawbacks,
.VS
is a step in this direction.
.SH
Conclusion.
.PP
The major flaw of
.VS
is that it cannot act as a complete command processor, although
I believe such a command processor can be built as part of
a visually oriented programming system.
Building such a system would be a major endeavor.
For the time being,
.I Vi
and
.VS
can provide the flavor of such a system
and inspire people to build it.
.bp
main.c          409019032   70    5     100644  1124      `
#include "hd.h"
#include "mydir.h"
#include <signal.h>

/* Extract data about environment */
#define ENV_COUNT	3

char *env_defalt [ENV_COUNT] =			/* Defaults */
	{"/bin/csh", "/", "unknown"};
char *env_name   [ENV_COUNT] =			/* Names */
	{"SHELL", "HOME", "TERM"};

char *envshell, *envhome, *envterm;

char **env_dest [ENV_COUNT] =			/* Place stored */
	{&envshell, &envhome, &envterm};

main () {

/*  Initialize everything, then run.  */

int i;
extern char * mygetenv ();

extern leave ();

comein ();

for (i = 0; i < ENV_COUNT; i++)
	*env_dest [i] = mygetenv (env_name [i], env_default [i]);

signal (SIGINT, leave);
tty_init (); curs_init ();
erase (); printf ("Vsh %0.1f\n", VERSION);
cmdldrc ();
wdfile = -1;
if (curdir () || enterdir (DOT) == FAILURE) leave ();
tty_push (RAWMODE);

process ();
}

/* Return environment value of pstr.  If none found, or the string
   length is too long, the default returned */

char *mygetenv
(pstr, def)
char *pstr, *def; {

char *rstr;	/* return value */
char *getenv ();

rstr = getenv (pstr);
if ((rstr == NULL) || (strlen (rstr) >= STRMAX)) return def;
else return rstr;
}
make.c          409019033   70    5     100644  1605      `
#include "hd.h"
#include "command.h"

/* Interface to make.  Fmake forks make off and returns.  Wmake waits
   for completion.  Both cause output to be saved in the file .makerror.
*/
fmake () {

	int p;			/*	Process number */
	int mfile;		/*	File number of .makerror */
	FILE *mstream;		/*	Stream of .makerror */
	FILE *showopen ();	/*	Opener of mstream */

	mstream = showopen ("w", MAKEMODE);
	if (mstream == NULL) return NOREPLOT;
	mfile = fileno (mstream);

	if (myfork () == 0) {
		if ((p = myfork ()) == 0) {
			close (outfile);  close (errorfile);
			dup (mfile);  dup (mfile);
			close (infile);  open ("/dev/null", 0);

			myexecl (MAKE, MAKE, 0);
		} else {
			join (p);
			beep (); sleep (2); beep ();
			exit (0);
		}
	} else {
		fclose (mstream);
		putmsg (MAKE);
	}
	return NOREPLOT;
} 

wmake () {

	int p;			/*	Process number */
	FILE *mstream;		/*	Stream of .makerror */
	FILE *showopen ();	/*	Opener of mstream */
	register ch;		/*	Work character */

	int pipefile [2];	/*	Pipe file numbers */
#	define pipein	pipefile [0]
#	define pipeout	pipefile [1]

	FILE *pipestrm;		/*	Stream of pipein */

	mstream = showopen ("w", MAKEMODE);
	if (mstream == NULL) return NOREPLOT;

	tty_push (COOKEDMODE);
	pipe (pipefile);
	if ((p = myfork ()) == 0) {
		close (outfile);  close (errorfile);
		dup (pipeout);  dup (pipeout);
		myexecl (MAKE, MAKE, 0);
	} else {
		close (pipeout);
		pipestrm = fdopen (pipein, "r");
		while ((ch = getc (pipestrm)) != EOF) {
			putchar (ch);  putc (ch, mstream);
		}
		fclose (pipestrm);  fclose (mstream);
		join (p);
	}
	tty_pop ();
	return CMD_SE | REPLOT;
}

makefile        411519855   70    5     100644  737       `
CFLAGS=-i
OBJECTS= main.o at.o dir.o xeq.o curdir.o process.o enterf.o \
	help.o page.o dirlist.o tty.o remove.o file.o show.o \
	ascii.o make.o grep.o showopen.o strings.o  \
	curses.o account.o cmdrun.o misccmd.o dircmd.o cmdini.o \
	readarg.o cmdload.o classify.o options.o

vsh:   $(OBJECTS)
	-size vsh;  rm -f vsh;
	cc -o vsh -x $(OBJECTS) -ltermlib;  size vsh;

lint:
	lint -bxac *.c

print:
	/bin/csh /usr/ucb/vprint *.h *.c makefile showhelp genhelp rmhelp

install:
	vshrc.gen
	install -s vsh ${DESTDIR}/usr/local/vsh
	install -c genhelp ${DESTDIR}/usr/local/lib
	install -c rmhelp ${DESTDIR}/usr/local/lib
	install -c showhelp ${DESTDIR}/usr/local/lib
	install dflt.vshrc ${DESTDIR}/usr/local/lib

clean:
	rm -f ${OBJECTS} vsh

misccmd.c       409019033   70    5     100644  313       `
#include "hd.h"

date () {		/* display current date */

	long datetime [1];

	time (datetime); clearmsg (1);
	printf ("  %.24s", ctime (datetime));
	return NOREPLOT;
}

callshell (argv) char **argv; {

	if (argv[0])  {
		mysystem (argv[0]);
		getrtn ();
	}
	else f_exec (envshell, envshell, 0);
	return REPLOT;
}

mydir.h         409019033   70    5     100644  1682      `
/*  Global Defines  */

#include <sys/types.h>
#include <sys/stat.h>
#include <dir.h>

/*  This data relates to the working directory and the portion of that
    directory currently displayed on the screen.
*/

#define mfiles	200		/* Max number of files vsh can handel */
extern int tfiles;		/* Number of files in this directory */

/*  The files of a directory are partitioned into pages.  Each page
    represents a display on the terminal screen.  */

#define	nfpp	20		/* Number of files per page */
#define	mpages	(mfiles/nfpp)	/* Max number of pages */

extern int tpages,		/* Total pages this directory */
cpage,				/* Current page this display */
pageend;			/* Index of last file on this page */

/*  File information about working dir */

extern struct stat wd_stb;		/* its inode */

/* Path name of working directory */
#define MPLEN	STRMAX - 1	/* Max path len (chars) */
#define LPLEN	STRMAX - 20	/* Limit path len */

extern int wdfile;		/* its file number */
extern char wdname [STRMAX];	/* its full path name */

extern struct dir dirbuf [mfiles + 1];	/* its contents */
#define	max_dir_size	(sizeof dirbuf - sizeof dirbuf [0])

extern char *d_namep [mfiles];		/* pointers to all the file names */

/* filename returns a pointer to the file assoc. with arg on cpage */
#define	filename(arg)	(d_namep [arg + cpage * nfpp - nfpp])

extern int dir_status;			/* its logical status */
#define	loaded		0
#define	unloaded	1
#define	toobig		2
#define	protected	3

/*  scratch */
extern struct stat scr_stb;

/* Useful macros */

#define	DOT	"."
#define DOTDOT	".."
#define	SLASH	"/"
#define dirsize sizeof dirbuf[0]
#define	dnamesize	DIRSIZ

#define	ISROOT(arg)	(arg [1] == 0)
nauthor         409019033   70    5     100644  169       `
.AU
David M. Scheibelhut [1]
.AI
Computer Science Division
Electrical Engineering and Computer Sciences Department
University of California
Berkeley, California
and
.IH

options.c       409019033   70    5     100644  2060      `
#include "hd.h"
#include "command.h"

#define	MAXLINE	16

/* Print and modify command tables interactively */

options (parm) char **parm; {
	
	int line = 0;		/* Current line number */
	register struct cmdstruct *cmdp;
	register struct classstruct *classp;
	register char **argv;
	struct parmstruct *parmp;

	/* Print command tab in .vshrc format */
	/* If parm present, dump to file and quit */
	erase ();
	if (*parm) {
		close (1);  creat ("vsh.out", 0644);  line= -9999;
	}
	for (;;) {
		for (cmdp = cmdtab; cmdp->cmd_char >= 0; cmdp++) {
			if (cmdp->cmd_proc) {
				for (classp = classtab;
					*classp->cl_name &&
					classp->cl_proc !=
					cmdp->cmd_proc;
					classp++);

				printf ("%c\t%s", cmdp->cmd_char, 
					classp->cl_name);

				for (argv = cmdp->cmd_argv; *argv;)
					printf ("  %s", *argv++);
				
				if (optline (&line) == FAILURE)
					return REPLOT;
			}
		}
		for (parmp = parmtab; parmp->p_name; parmp++) {
			printf ("%s\t%s", parmp->p_name, parmp->p_val);
			if (optline (&line) == FAILURE) return REPLOT;
		}

		if (*parm) leave ();
		if (line != 0 && optcmd () == FAILURE) break;
		erase ();  line = 0;
	}
	return REPLOT;
}
/* Processing for end of each line includes:
	1.  Print the newline.
	2.  If the end of the page, prompt for a command.
*/
optline (line) int *line; {
	int ret;	/* return from optcmd */

	putch (LF);
	if (++*line < MAXLINE) return SUCCESS;

	ret = optcmd (); erase ();  *line = 0;
	return ret;
}

optcmd () {
	char cbuf [STRMAX],	/* Buffer for input parm */
	     *argv [ARGVMAX];	/* Pointers to input parm */
	int argc;		/* Number of parm */
	int ret;		/* Return from readarg */

	int line = 0;		/* Current line num of rcstream */

	printf ("\nType in a new parameter, or\n\
Press ^D to leave.  Press -Return- to display more parameters.\n");
	for (;;) {
		tty_push (COOKEDMODE);
		ret = readarg (stdin, &line, &argc, argv, cbuf);
		tty_pop ();
		if (argc == 0) break;
		if (compe ("quit", argv [0])) {
			ret = FAILURE;  break;
		}
		if (ret != FAILURE) cmdldarg (line, argc, argv);
	}
	return ret;
}
page.c          409019112   70    5     100644  1005      `
#include "hd.h"
#include <signal.h>

static int nointer;

page (stream) FILE *stream; {

int line, linelim;  register ch;
register ttych, ttych1;

int (*oldsig)(); extern catch();

#define	window	23
#define	scroll	10

linelim = window;  ch = 1;
tty_push (COOKEDMODE);
oldsig = signal (SIGINT, catch);  nointer = 1;

bufout ();  erase ();
do {
	for (line = 0; line < linelim; line++) {
		while ((ch = getc (stream)) != EOF && ch != '\n')
			putchar (ch);
		if (ch == EOF) break;
		putch ('\n');  fflush (stdout);
	}
	if (!nointer) break;
	printf ((ch == EOF) ? "Done:  " : "More:  ");
	fflush (stdout);

	ttych = ttych1 = getch ();
	while (ttych != EOF && ttych != '\n') ttych = getch ();
	if (ttych == EOF) {
		linelim = scroll;
		printf ("\r          \r");
		fflush (stdout);
	} else {
		linelim = window;
		erase ();
	}

} while (ch != EOF && ttych1 != 'n' && nointer);
unbufout ();  tty_pop ();  signal (SIGINT, oldsig);
}

catch () {		/* Catch an interrupt */
	nointer = 0;
	signal (SIGINT, catch);
}

process.c       409019112   70    5     100644  537       `
#
/*  This is the main loop of vsh.  Each interation processes a command.
    Commands are one character long.  They are acquired in raw mode
    and processed without the need to press return.  The goal of vsh
    is to minimize keypresses as much as possible.
*/

#include "hd.h"

process () {

	register cmd, next;		/* single character command */

	next = REPLOT;
	for (;;) {			/* loop forever */
		if (next & REPLOT) dispdir (1);
		at (2301);
		printf (" \b");
		cmd = getch ();
		putch ('\b');

		next = command (cmd, DIRCMD);
	}
}

readarg.c       409019112   70    5     100644  3152      `
#include "hd.h"
#include "strings.h"

/* Table for scanner machine.  The machine can handle any regular
   expression.  Table elements come in pairs.  The first is
   the match character and the second is the next state assoc with
   that character.  The character '*' matches all character.
*/
static char st00[] = {' ',  0, '\t', 0, '\n', 3, '\\', 1,
		      '\'', 8, '"', 10, '*',  4};
static char st01[] = {'\n', 0, '*',  4};
static char st02[] = {'\n', 3, '*',  0};
static char st03[] = {'*',  0};
static char st04[] = {'*',  5};
static char st05[] = {' ',  2, '\n', 2, '\t', 2, '\\', 6, '\'', 8,
		      '"', 10, '*',  4};
static char st06[] = {'\n', 5, '*',  4};
static char st07[] = {'*',  0};
static char st08[] = {'\'', 5, '*', 9};
static char st09[] = {'*',  8};
static char st10[] = {'"',  5, '\\',12, '*',11};
static char st11[] = {'*', 10};
static char st12[] = {'\n',10, '*', 11};

static char *lextab[] = {st00, st01, st02, st03, st04, st05, st06,
	st07, st08, st09, st10, st11, st12};

/* Readarg reads a line from pstream and converts it into
   argv-argc format.  One may use a format similar to the shell.
*/
readarg (pstream, pline, pargc, pargv, pbuf)
	FILE *pstream;		/* Stream being read */
	int *pline;		/* Current line number */
	int *pargc;		/* Count of words */
	char *pargv [ARGVMAX];	/* Array of pointers to the words */
	char pbuf [STRMAX];	/* Buf to hold the words themselves */
	{
	/* Space is allocated from pargv and pbuf as words are read.
	   These vars keep track of the allocations.
	*/
	char **cargv = pargv;	/* Current allocations */
	char * cbuf  = pbuf;
				/* Limit of allocation */
	char **largv = pargv + ARGVMAX - 1;
	char * lbuf  = pbuf  + STRMAX - 1;

	/* Scanner vars */
	int state = 0;		/* Scanner machine state */
	int ch;			/* Current char */
	char *ltp;		/* Lexical table pointer */

	int prompt;		/* Flag indicates when to prompt */
#define	NOPR	0	/* Don't */
#define	FPR	1	/* First prompt */
#define	CPR	2	/* Continuation prompt */

	/* Initialization */
	*pargc = 0;
	cargv[0] = pbuf;
	prompt = FPR;

	for (;;) {
		switch (state) {

			/* Get a char */
			case 0:  case 1:  case 5:  case 6:  case 8:
			case 10: case 12:
			if (prompt && pstream == stdin)
				printf ((prompt == FPR) ?
				"Parm: " : ": ");
			ch = fgetc (pstream);
			prompt = (ch == LF) ? CPR : NOPR;
			if (ascii [ch] == EF) {
				if (*pargc != 0) lderror
					("Incomplete line", *pline);
				return FAILURE;
			}
			else if (ENDLINE (ch)) ++*pline;
			else if (ascii [ch] == UD) {
				lderror ("Non-Ascii character", *pline);
				continue;
			}
			break;

			/* Store a char */
			case 4:  case 9:  case 11:
			if (cbuf >= lbuf || cargv >= largv) {
				lderror ("Too long", *pline);
				return FAILURE;
			}
			*cbuf++ = ch;
			break;

			/* Store a word */
			case 2:  case 7:
			*cbuf++ = 0;
			*++cargv = cbuf;
			++*pargc;
			break;

			/* Return from readarg */
			case 3:
			*cargv = CNULL;  return SUCCESS;
		}
		for (ltp = lextab [state];
			*ltp != ch && *ltp != '*';  ltp += 2);
		state = *++ltp;
	}
}

/* Load error */
lderror (cp, line) char *cp; int line; {
	printf ("Line %d:  %s\n", line, cp);
	getrtn ();
}
refs            409019112   70    5     100644  518       `
1   Ritchie and Thompson; "The Unix Time-Sharing System,"
    Bell System Technical Journal, 57:6.2, July-August 1978.

2   Joy, Bill;  "An Introduction to Display Editing with Vi,"
    University of California at Berkeley Computer Center, 1978.

3   Sherwood, Bruce;  The Tutor Language,  University of Illinois
    at Urbana-Champaign--Computer-Based Education Research
    Laboratory, 1974.

4   Titelman, Warren;  A Visual Lisp Programming System,
    Talk given at The University of California at Berkeley, 1979.
remove.c        409019113   70    5     100644  3183      `
#include "hd.h"
#include "mydir.h"
#include "command.h"

#define	ISDIR	((scr_stb.st_mode & S_IFMT) == S_IFDIR)
#define	paction(arg)	{ \
	atfile (file, 4);printf (action [cpage][file] ? "//" : "  ");}

/* User first indicates which files to remove by selecting them
   with a-t keypresses.  Selected files are stored in the action
   array.  When ready, the user presses R, and the files are gone.
*/

static char quitcmds[] = {CR, LF, EOT, 0};
static char dircmds [] = {"0123456789;+-uL"};

static char *rmmsg[] = {
	"Select the files you wish to remove, then press -R-",
	"Press -Return- to leave with out removing files. -?- for more help"
};

remove () {			/* remove (unlink) files */

register cmd, file;  register char *cp;

short starmode;
#define	OFF	0
#define	ON	1
#define	UNDEF	2

extern char *index ();

char action [mpages][nfpp];

if (access (DOT, 3)) {
	myperror ("Cannot remove files");
	return FAILURE;
}
for (cp = &action [0][0]; cp < &action [mpages-1][nfpp];) *cp++ = OFF;
dispaction (action);
disprmmsg ();

for (;;) {
	at (2380);
	cmd = getch ();
	file = cmd - 'a';
	if (index (quitcmds, cmd) != CNULL) {
		clearmsg (0);  return NOREPLOT;
	}
	else if (index (dircmds, cmd) != CNULL) {
		if (dircmd (cmd) & REPLOT) rmdispdir (action);
	}
	else if (cmd == '*') {
		starmode = UNDEF;
		for (file = 0; file < pageend; file++) {
			cp = &action [cpage][file];

			if (compe (filename (file), DOT));
			else if (compe (filename (file), DOTDOT));

			else if (starmode == UNDEF)
				*cp = starmode = !*cp;
			else *cp = starmode;
		}
		dispaction (action);
	}

	else if (cmd == 'R') break;
	else if (cmd == '?') {
		if (help (RMHELP) & REPLOT) rmdispdir (action);
	}
	else if (cmd >= 'a' && cmd <= 'z' && file < pageend) {
		if (compe (filename (file), DOT));
		else if (compe (filename (file), DOTDOT));
		else {
			action [cpage][file] ^= 1;
			paction (file);
		}
	}
}
act (action);
return REPLOT;
}

dispaction (action) char action [mpages][nfpp]; {
	register file;

	bufout ();
	for (file = 0; file < pageend; file++)
		paction (file);
	unbufout ();
}

rmdispdir (action) char action [mpages][nfpp]; {
	dispdir (0);
	dispaction (action);
	disprmmsg ();
}

disprmmsg () {
	clearmsg (2);
	bufout ();
	printf ("%s\n%s", rmmsg [0], rmmsg [1]);
	unbufout ();
}

/* This does the actual removing */
act (action) char action [mpages][nfpp]; {
	register page, file;

	clearmsg (1);
	for (page = 1; page <= tpages; page ++)
		for (file = 0; file < nfpp; file++)
		if (action [page][file]) {

		if (page != cpage) {
			cpage = page;  dispdir (0);
			dispaction (action);  clearmsg (1);
		}

		atfile (file, 0);  printf ("->");
		at (2301);
		if (stat (filename (file), &scr_stb)) {
			myperror (filename (file));
			if (rmerror ()) return;
		}
		else if (ISDIR && f_exec
			("/bin/rmdir", "+", filename (file), 0)) {
			if (rmerror ()) return;
		}
		else if (!ISDIR && unlink (filename (file))) {
			myperror (filename (file));
			if (rmerror ()) return;
		}
		else {
			atfile (file, 0);  clearline ();
		}
	};
	return;
}
rmerror () {		/* Error in removing file */
	register ch;

	at (2365);  printf ("Press -Return-");
	ch = getch ();
	clearmsg (0);
	return (ch == EOT);
}

rmhelp          409019113   70    5     100644  426       `
To select a file for removal, press its corresponding letter (a-t).
Selecting a file a second time "undeletes" the file.

One can select all files on a page by pressing -*-.

A file selected for removal is marked with a "//".


One can move through the various pages with the usual commands
(0123456789+-)

To exit without removing any files, press -Return-.
To exit and cause permanent removal of selected files, press -R-.

roadmap         409019113   70    5     100644  3491      `
This is a roadmap of Vsh files and what they contain.

classify.h
        When a file is selected, Vsh determines what kind of file it
        is.  The classify procedure in classify.c returns one of the
        file types in classify.h.

command.h
        Important definitions for the command processor are found in
        command.h.  This includes definitions of important structures
        and macros and procs for accessing those structures.

hd.h
        This fle is included in all Vsh programs.  It contains an
        assortmant of useful macros and fundimental parameters.

mydir.h
        Include this file if necessary to access the directory.

strings.h
         This file contains useful definitions for character string
         manipulation.  Ascii acts as an array indexed from
         -1 (EOF) to 0x7f (the highest ascii character).
         Ascii [char] returns a bit string which classifies char.

account.c
        Account.c keeps track of the users of Vsh.  On entry procedure
        comein logs the starting time.  On exit, procedure goout
        stores usage statistics in a log file.

ascii.c
        This file contains initialization necessary for the
        macros in ascii.h

at.c
        This file contains routines for manipulating the display.

classify.c
        Classify returns the file type of its argument.  See classify.h
        for possible file types.

cmdini.c
        This file contains initialization for tables used by the
        command processor.

cmdload.c
        Cmdload loads commands and parameters from the .vshrc file.

cmdrun.c
        Procedure command runs its argument as a command.

curdir.c
        Curdir is called during initialization to determine the full
        path name of the current directory.  It calls /bin/pwd to
        do the work.

curses.c
        This loads information from the termcap file necessary
        for addressing the cursor.

dir.c
        Dir.c contains the basic directory procedures.

dircmd.c
        Dircmd processes commands only available on the directory page.

dirlist.c
        This file contains procedures which print status found in a
        file's inode in the manner of Ls(I).

enterf.c
        Enterfile classiffies a file and then takes action to
        appropriately select that file.

file.c
        File is a command procedure which interfaces to enterfile.
        Create is a command procedure which creates new files.

grep.c
        This file contains an interface to grep (I).

help.c
        This contains procedures which mostly display help files,
        although any file may be displayed.

main.c
        Here one finds lots of initialization, and then a call to
        the main loop in process.c

make.c
        This contains fmake and wmake, two interfaces to make.

miscmd.c
        This contains the date and the shell commands.

page.c
        Page displays a file in 23 line pages.

process.c
        This contains the main loop.

readarg.c
        Readarg is a simple scanner used to load .vshrc.

remove.c
        Remove is a command procedure which removes files.

show.c
        This file contains the procedures which implement
        showfile mode.

showopen.c
        Showopen opens the showfile for show, make, and grep commands.

strings.c
        This contains useful character character string oriented procs.

tty.c
        Procedures for minipulating sgtty options.

xeq.c
        This file contains procedures for executing other programs.

show.c          409019115   70    5     100644  5437      `
#include "hd.h"
#include "strings.h"
#include "command.h"

#define	SPAGELEN	12	/* Number of Lines per Page */

static char EONE [] = "+1";	/* Default editor parameter */

/* Global data about file being shown */

FILE *sstream;			/* Stream being shown */

int cline;			/* Current (next) line to be shown */
int tline;			/* Top line of page */

char *sdesc [2] = {"Grep", "Make"};

showerror () {
	return show (MAKEMODE);
}
showgrep () {
	return show (GREPMODE);
}

show (p) int p; {		/* Use parms GREPMODE or MAKEMODE */

	FILE *showopen ();
	int cmd, next, number, spaces;
	int (*cproc)();
	extern grep(), wmake(), showerror(), showgrep();

	sstream = showopen ("r", p);
	if (sstream == NULL) return NOREPLOT;
	
	cline = 1;
	next = REPLOT;

	for (;;) {
		if (next & REPLOT) {
			sdisp (p);
			number = spaces = 0;
		}

		if (number == 0)  {
			at (2301);
			printf ("  \r");
		}
		cmd = getch ();
		next = REPLOT;

					/* This accumulates a number */
		if (NUMERIC (cmd) && spaces == 0) {
			number = number * 10 + TONUM (cmd);
			next = 0;
		}
		else if (cmd == '\b') {
			if (spaces > 0) --spaces;
			else number /= 10;
			printf (" \b");
			next = 0;
		}
		else if (cmd == ' ') {
			if (number > 0) ++spaces;
			next = 0;
		}

					/* These commands use number */
		else if (cmd == LF || cmd == CR || cmd == 'e') {
			putch (CR);
			next = sedit (p, number);
			number = spaces = 0;
		}
		else if (cmd == 'p') {
			putch (CR);
			seekline (number);
		}
		else {
			number = spaces = 0;  putch (CR);
			cproc = cmdproc(cmd);
			if (cproc == showerror || cproc == showgrep) {
				next = cmd | REPLOT;
				break;
			}
			else if (cmd == '?') {
				next = help (SHOWHELP);
			}
			else if (cproc == grep) {
				next = grep ();
				if (next != FAILURE) break;
			}
			else if (cproc == wmake) {
				next = wmake ();
				if (next != FAILURE) break;
			}
			else if (cmd == 'q' || cmd == EOT) {
				break;
			}
			else {
				next = command (cmd, SHOWCMD);
			}
			if (next & ENTERDIR) break;
			if (next & REPLOT) seekline (tline);
		}
	}
	fclose (sstream);
	return next | REPLOT;
}

/* Position show file at specified location */
seekline (line) int line; {

	register ch;

	cline = 1;  rewind (sstream);
	while (cline < line && (ch = getc (sstream)) != EOF)
		if (ch == LF) cline++;

	if (feof (sstream)) {
		if (cline <= 2) {
			rewind (sstream);  cline = 1;
		} else {
			seekline (cline - 1);
		}
	}
}

sdisp (p)  int p; {	/* display sstream */

register ch;		/* work char */
int eline;		/* end line -- line to stop display */
short lflag;		/* True if chars have printed on current line */

extern char wdname [];	/* Name of working dir */

if (feof (sstream)) seekline (1);

tline = cline;
eline = cline + SPAGELEN;

clearmsg (-1);  bufout ();  tty_push (COOKEDMODE);

erase ();  printf ("%s", wdname);
at (161);  printf ("Showfile -- %s     \n",  sdesc [p]);

while (cline < eline) {
	lflag = 0;

	do {
		ch = getc (sstream);
		if (ch == EOF) ch = LF;
		else if (!lflag)  {
			printf ("%4d	", cline);
			lflag = 1;
		}
		putchar (ch);
	}  while (ch != LF);

	cline++;
	if (feof (sstream)) {
		seekline (1);  printf ("** End of File **\n");
		break;
	}
}
printf ("\nType the number of the line you wish to edit.\n");
printf ("Type ^D to Leave.  Type ? for more options.");

at (2301); unbufout ();  tty_pop ();
}

sedit (p, number) int p, number; {
	char inbuf [STRMAX];

	int oldline;

	oldline = cline;
	if (number == 0) return REPLOT;
	seekline (number);

	fgetline (sstream, inbuf);
	if (p == GREPMODE && grepfmt (inbuf));
	else if (p == MAKEMODE &&
		(ccfmt (inbuf) || grepfmt (inbuf) || cmdfmt (inbuf)));
	else {
		putmsg ("Cannot find file name");
		seekline (oldline);
		return NOREPLOT;
	}
	seekline (number);
	return REPLOT;
}

/* Extract file names and line numbers from various formats.  */

ccfmt (inbuf) char inbuf [STRMAX]; {	/*  Error from C compiler */
	char filebuf [STRMAX];		/*  or lint		  */
	char *enumber ();
	register char *cpi, *cpo;

	cpi = inbuf;
	while (*cpi != '"') if (*cpi++ == 0) return 0;

	cpi++;  cpo = filebuf;
	while (*cpi != '"') {
		if (*cpi == 0) return 0;
		*cpo++ = *cpi++;
	}
	*cpo = 0;

	return nedit (filebuf, enumber (cpi));
}

grepfmt (inbuf) char inbuf [STRMAX]; {	/*  Line from grep	  */
	char filebuf [STRMAX];		/*  or C preprocessor	  */
	char *enumber ();
	register char *cpi, *cpo;

	cpi = inbuf;  cpo = filebuf;
	while (*cpi != ':') {
		if (*cpi == 0) return 0;
		*cpo++ = *cpi++;
	}
	if (*cpi == 0) return 0;
	*cpo = 0;

	return nedit (filebuf, enumber (cpi));
}

cmdfmt (inbuf) char inbuf [STRMAX]; {	/*  Command line	  */
	char filebuf [STRMAX];
	register char *cpi, *cpo;

	cpi = inbuf;

	do  {
		while (!WHITESPACE (*cpi)) if (*cpi++ == 0) return 0;
		while ( WHITESPACE (*cpi)) cpi++;
	}  while (*cpi == '-');

	if (*cpi == 0) return 0;

	cpo = filebuf;
	while (!WHITESPACE (*cpi) && *cpi != 0) *cpo++ = *cpi++;
	*cpo = 0;

	return nedit (filebuf, EONE);
}

/* Extract number from line */

char *
enumber (cpi) register char *cpi; {

	static char numbuf [8];
#	define	NUMBUFLIM	(numbuf + sizeof numbuf)

	register char *cpo;
	cpo = numbuf;  *cpo++ = '+';

	while (!NUMERIC (*cpi)) if (*cpi++ == 0) return EONE;

	while (NUMERIC (*cpi)) {
		if (cpo >= NUMBUFLIM) return EONE;
		*cpo++ = *cpi++;
	}
	*cpo = 0;

	return numbuf;
}

nedit (file, number) char *file, *number; {

	if (access (file, 4)) return FAILURE;
	f_exec (EDITOR, EDITOR, number, file, 0);
	return REPLOT;
}

showdoc         409019115   70    5     100644  1634      `
.PP
An important feature of
.VS
is the connection between
.I Make(I),
.I Grep(I),
and the editor.
It is useful to immediately edit a line referenced by a compiler
error message regardless of the use of a visual orientation.
One should at least be able to easily save these messages
in a file so they are not lost off the top of the screen.
This reasoning has not occurred to the
.UX
compiler writers.
Try to save error messages with a command such as
.PP
.B
$ make > errors
.R
.LP
and one will be disappointed.
Error messages are directed to the
.I stderr
stream and not
.I stdout.
The result is that the file
.I errors
collects everything except errors!
A better command is
.PP
.B
$ make >& errors
.R
.LP
which collects errors but nothing else.
The best command turns out to be
.PP
.B
$ make > errors 2>&1
.R
.LP
which collects everything in one place.
Of course,
.VS
does all this redirection properly.
It shouldn't have to.
I do not believe
.I stderr
is the proper place for complier
error messages.
They belong in
.I stdout.
.PP
The same functions provided by the
.I
Vsh make/showfile/editor
.R
connection can be provided in a more traditional
.UX
fashion.
First, compilers should send their errors to the standard output.
This will allow users to save those messages without first having
to first consult a
.UX
guru.
Second, a command should be added to the editor which
scans for a file name and line number in the
same fashion as the
.I showfile
procedure.
This will provide the same function
.I Showfile
provides in
.I Vsh.
Finally, a simple shell file can tie this all together:
.PP
.B
make | tee .makerror;  vi .makerror
.R
showhelp        409019115   70    5     100644  1341      `
		* * S h o w f i l e    H e l p * *

These are the showfile commands.  It is not necessary to press -Return-
after these commands.  When a complete command is sensed, it is run
immediately.

Command		Description

^D or q		Leave Showfile.

?		Print this page.

number p	Print file starting at specified line.

number e	Examine line for a file name and line number.
		if a file name is found, run the editor on this file.

-Return-	Print next twelve lines in the file.

number -Return-	
		Same is "number e."


In addition to the previous commands, most of the commands from the
directory page are available in showfile.  They include ^, /, !, %,
and A-Z except L and R.  Particularly useful are M and G which allow
new iterations of Make and Grep, !, the escape to the shell, and F,
which allows one to edit any file.

To return to showfile display from Vsh, use 'E' to review the
last .makerror file and 'S' to show the last .grepout file.

Vsh always searches for .makerror in the current directory.  Vsh
searches for .grepout first the current directory, then in your
home directory.

When a line is selected with the e command, Vsh tries to extract
a file name and line number from the line.  As the output
from grep is of constant format, a selection will never fail with grep.
Vsh tries its best with the output of compilers and lint.

showopen.c      409019115   70    5     100644  661       `
#include "hd.h"

/* Open a showfile */
/* Open with rwmode of "r" or "w" just like fopen */
/* Smode GREPMODE opens for grep;  Smode MAKEMODE opens for Make;  */

FILE *showopen (rwmode, smode) char *rwmode;  int smode; {

	extern char *envhome;
	char buf[STRMAX + 20];
	FILE *tfile;
	char *fname;

	fname = smode == GREPMODE ? GREPOUT : MAKERROR;
	tfile = fopen (fname, rwmode);
	if (tfile == NULL && smode == GREPMODE) {
		if (access (fname, 0) == 0) {
			myperror (fname);
			return NULL;
		}
		strcpy (buf, envhome);
		strcat (buf, "/");
		strcat (buf, fname);
		tfile = fopen (buf, rwmode);
	}
	if (tfile == NULL) {
		myperror (fname);
	}
	return tfile;
}

strings.c       409019116   70    5     100644  1681      `
#
/* Usefull string handeling routines */
#
#include "hd.h"
#include "strings.h"

/* Scanspace reads in the standard input and returns the first character
   which is not a space.  Note this could be an end of file.  */

scanspace () {
	register ch;

	while (WHITESPACE (ch = getch ()));
	return ch;
}

/* Scanend scans to the end of a line */

scanend () {
	register ch;

	while (!ENDLINE (ch =  getch ()));
	return ch;
}

/* Xgetword inputs a string up to clim - 1 chars long */
/* String is returned in argument;  Length is return value.  */

/* Use getword (char_array) instead.  Clim is filled in for you. */

xgetword (input, clim) char *input; int clim; {

#define LASTCHAR	(input + clim - 1)

register ch;  register char *cp, *lchar;

ch = scanspace ();
cp = input;  lchar = LASTCHAR;

while (cp < lchar &&
	!ENDLINE (ch) &&
	!WHITESPACE (ch)) {

	*cp++ = ch;  ch = getch ();
}

while (!ENDLINE (ch)) ch = getch ();
*cp = 0;
return (cp - input);
}

/* Xgetline inputs a string up to clim - 1 chars long */
/* String is returned in argument;  Length is return value.  */

/* Use getline (char_array) instead.  Clim is filled in for you. */
/* Use fgetline (stream, char_array) to get a line from a file   */
/* other than stdin. */

xgetline (stream, input, clim) FILE *stream; char *input;  int clim; {

	register ch;  register char *cp, *lchar;
	cp = input;  lchar = LASTCHAR;

	do {
		ch = fgetc (stream);
		if (cp > lchar) cp = lchar;
		*cp++ = ch;
	} while (!ENDLINE (ch));

	*--cp = 0;
	return (cp - input);
}

getrtn () {		/* Ask person to press return */
	printf ("  Press -Return-");
	scanend ();
}

putch (ch) int ch; {putchar (ch);}
getch () {return getchar ();}

strings.h       409019116   70    5     100644  1421      `
/*  Ascii control table.  There are 129 entries.  To find the class
    of a character, mask out the parity bit (if raw I/O is being
    used) and use it as an index the the ascii array.  With "cooked"
    I/O, EOF is -1.  EOF with "parity" masked out is 7f, which is also
    coded as an eof.  With "raw" I/O, EOT acts as an eof. */

/*	Each entry in the table has the following bits:
	1	End of line
	2	End of file
	4	Alpha
	8	Upper case
	16	Number
	32	Special character
	64	White space
*/
/*	Codes for loading table	*/

#define UD	0	/* Undefined--assorted control characters */
#define EL	1	/* End of line */
#define EF	3	/* End of File (and line) */
#define LA	4	/* Lower case alpha */ 
#define UA	12	/* Upper case alpha */
#define NU	16	/* Numeric */
#define	SC	32	/* Special character */
#define WS	64	/* White space */


/* Ascii acts like an array indexed from -1 to 128 */

extern char charclass [];
#define ascii	(charclass + 1)

/* Usefull macro functions */

#define WHITESPACE(arg)	(ascii [arg] == WS)
#define	NUMERIC(arg)	(ascii [arg] == NU)
#define ENDLINE(arg)	(ascii [arg] & EL)

#define	TONUM(arg)	(arg - '0')

/* This reads a line from stdin and returns the first word */
#define getword(arg)	xgetword (arg, sizeof arg)

/* Read a line from stdin */
#define getline(arg)	xgetline (stdin, arg, sizeof arg)

/* Read a line from any stream */
#define fgetline(arg1,arg2)	xgetline (arg1, arg2, sizeof arg2)

tauthor         409019116   70    5     100644  191       `
.AU
David M. Scheibelhut\s-2\v'-0.4m'1\v'0.4m'\s+2
.AI
Computer Science Division
Electrical Engineering and Computer Sciences Department
University of California
Berkeley, California
and
.IH

tty.c           409019116   70    5     100644  1422      `
#include "hd.h"
#include <sgtty.h>

/* This module controls tty options.

   Tty states are treated as a stack.  Use tty_push and tty_pop.

   Push COOKEDMODE and RAWMODE onto the stack.  RAWMODE is now actually
   CBREAK mode.
*/

struct sgttyb newstty, oldstty;		/* sgtty structures */
int ttysp;				/* Sp for tty stack */
extern short ospeed;			/* Speed for Joy's routines */

#define	TTYLIM	16
char tty_stack [TTYLIM];		/* The tty stack */

/* Initialize tty structures */

tty_init () {

	gtty (infile, &oldstty); gtty (infile, &newstty);
	oldstty.sg_flags &= ~(CBREAK | RAW);
	newstty.sg_flags |= CBREAK;
	ospeed = oldstty.sg_ospeed;

	ttysp = 0;  tty_set (COOKEDMODE);
}

/* Push a new tty mode.  Use RAWMODE and COOKEDMODE as parameters. */

tty_push (pmode) int pmode; {
	ttysp++;
	if (ttysp >= TTYLIM) {
		putmsg ("Tty stack overflow\n");
		leave ();
	}
	tty_set (pmode);
}

tty_pop () {
	ttysp--;
	if (ttysp < 0) {
		putmsg ("Tty stack underflow\n");
		leave ();
	}
	tty_set (tty_stack [ttysp]);
}

/* Tty_set sets the tty mode indicated by its parameter.  Stty is only
   executed if a change will result.
*/

tty_set (pmode) int pmode; {

	static int curmode = -1;	/* Current tty mode */

	tty_stack [ttysp] = pmode;
	if (pmode == curmode) return;
	curmode = pmode;
	if (pmode == RAWMODE) tty_raw ();
	else tty_cooked ();
}

tty_raw () { stty (infile, &newstty); }

tty_cooked () { stty (infile, &oldstty); }
vshrc.gen       409019116   70    5     100755  136       `
TERM=3a;  HOME=nowhere; export TERM HOME;
vsh > /dev/null << eof
OQ options x
quit
Q
eof
sed -e '/^Q/d' vsh.out > dflt.vshrc
rm vsh.out
xeq.c           409019116   70    5     100644  2874      `
#include "hd.h"
#include <signal.h>

/*  F_exec forks and executes a process.  It then waits for the
    forked process to finish.
    Use f_exec like execl, except f_exec waits for termination of the
    called program and then falls through.
*/

extern leave ();

/*VARARGS1*/
f_exec (a, b) char *a, *b;  {

int retval;
int p;

tty_push (COOKEDMODE);
if ((p = myfork ()) == 0) myexecv (a, &b);
else retval = join (p);
tty_pop ();
return retval;
}

/* Exec takes parameters like a command and interfaces properly
   to the command processor in command.c. */

exec (argv) char **argv; {

	register p;

	if (*argv == CNULL) {
		putmsg ("Execute command must have parameter");
		return NOREPLOT;
	}
	tty_push (COOKEDMODE);

	if ((p = myfork ()) == 0) myexecv (*argv, argv);
	else join (p);

	getrtn ();
	tty_pop ();
	return REPLOT;
}
/* Mysystem is similar to system, except the bugs are fixed.  In
   addition, the tty is set to cooked mode and the command is printed.
*/
mysystem (name)  char *name; {

	int pipefile[2];
#	define pipein	pipefile [0]
#	define pipeout	pipefile [1]

	int p; FILE *stream;

	tty_push (COOKEDMODE);  pipe (pipefile);

	if ((p = myfork()) == 0) {
		close (infile);  dup (pipein);
		myexecl (envshell, "+", 0);
	}
	else {
		stream = fdopen (pipeout, "w");
		close (pipein);
		printf ("%s\n", name);
		fprintf (stream, "%s\n", name);
		fclose (stream);
		join (p);
	}
	tty_pop ();
}

/* p_exec is just like f_exec except output is paged */

/*VARARGS1*/
p_exec (a, b) char *a, *b;  {

int pipefile [2];

int p;
FILE *stream;

pipe (pipefile);
if ((p = myfork ()) == 0) {
	close (outfile);  dup (pipeout);
	close (pipein);   close (pipeout);
	myexecv (a, &b);
}
else {
	stream = fdopen (pipein, "r");
	close (pipeout);
	page (stream);
	fclose (stream);
	join (p);
}
}

/* Special interfaces to exec */
/* Myexecl and myexecv close files numbered > 3 and
   print their arguments. */

/*VARARGS1*/
myexecl (a, b) char *a, *b; {
	myexecv (a, &b);
}

myexecv (a, b) char *a, **b; {

register char **sp;  register i;

if (**b != '+') {
	for (sp = b; *sp; sp++) printf ("%s ", *sp);
	printf ("\r\n");
}
for (i = 3; i <= _NFILE; i++) close (i);
execv (a, b);
myperror (a); getrtn (); exit (1);
}

/* Myfork acts like fork but never fails */
#define	MAXTRIES	10	/* Max tries of a fork */
myfork () {
	int p;			/* process number */
	int tries;		/* number of tries */

	for (tries = 1; tries <= MAXTRIES; tries++) {
		p = fork ();
		if (p > 0)  signal (SIGINT, SIG_IGN);
		if (p == 0) signal (SIGINT, SIG_DFL);
		if (p != -1) return p;
		myperror ("Cannot fork");
		sleep (tries);
		clearmsg (0);
	}
	putmsg ("Fatal error -- cannot fork\n");
	leave ();
return -1;
}

/* Join is the compliment of fork */

join (p) int p; {

	int status [2];  int w;

	do {
		w = wait (status);
	} while (p != -1 && w != p);

	signal (SIGINT, leave);

	return (status [0]);
}