[net.sources] VSH sources 3 of 6

dan@rna.UUCP (Dan Ts'o) (06/28/85)

# Here is part 3 of the sources to VSH, a visual shell.
#
#
#					Cheers,
#					Dan Ts'o
#					Dept. Neurobiology
#					Rockefeller Univ.
#					1230 York Ave.
#					NY, NY 10021
#					212-570-7671
#					...cmcl2!rna!dan
echo src/hd.h
sed 's/^X//' > src/hd.h << 'All work and no play makes Jack a dull boy'
X/* Header file for all procedures in Vsh */
X#include "stdio.h"
X
X/* #define	PWBTTY		1		/* Some local stuff */
X/* #define	USGTTY		1		/* 3.0-5.0 drivers */
X#define	V7TTY		1		/* V7 - ?.BSD drivers */
X/* #define	V6		1		/* Version 6 stuff */
X#define	NDIR		1		/* New 4.2BSD directories */
X#define	SYMLINK		1		/* 4.2BSD symbolic links */
X#define	VFORK		1		/* Has vfork() */
X/* #define	MACMAJOR	1		/* types.h missing major() */
X
X/*
X * Buffer size for backing up more(). This buffer is malloc()'d in page()
X * unless MBUFSTACK is defined. PDP-11's and the like should probably
X * define MBUFSTACK, otherwise less data space will be available for
X * directories. page() will try to malloc() less, if reasonable.
X */
X/*#define	MBUFSTACK	1		/* Put more buffer on stack */
X#define	MBUFSIZE	7000		/* Used only with MBUFSTACK */
X#define	MMAXSIZE	"10000"			/* To init param moresize */
X
X#define VERSION	"4.2"
X
X#define max(arg1,arg2)	((arg1 > arg2) ? arg1 : arg2)
X#define min(arg1,arg2)	((arg1 < arg2) ? arg1 : arg2)
X#define compe(arg1,arg2)  (strcmp (arg1, arg2) == 0)
X
X/* Standard file numbers */
X#define infile		0
X#define outfile		1
X#define errorfile	2
X
X/* The values of special keys */
X#define EOT		4
X#define	RUBOUT		0177
X#define	CR		015
X#define	LF		012
X
X/* Directory commands */
X#define	CTLF	006
X#define	CTLL	014
X#define	CTLU	025
X#define	TABCMD	011			/* Multi-column shift */
X
X/* Emacs select positioning */
X#define	ESUP	020			/* ^P */
X#define	ESDN	016			/* ^N */
X#define	ESLF	002			/* ^B */
X#define	ESRT	006			/* ^F */
X#define	ESBS	001			/* ^A back series */
X#define	ESFS	005			/* ^E forward series */
X#define	ESNP	026			/* ^V next page */
X
X/* Display offsets */
X#define	OFFFILE	5
X#define	OFFARROW 3
X
X/* Standard file names */
X#define LOGFILE		"/usr/adm/vsh.log"
X#define DEBUGGER	"/bin/adb"
X
X/* Other parameters */
X#define	STRMAX	120		/* Length of string buffers */
X#define CNULL	((char *) 0)	/* Null char pointer */
X#define ARGVMAX		20	/* Size of a readarg argv */
X#define	PAGEMIN	5		/* Minimum page window */
X#define	MAILCLK	(1*60)		/* Check for mail interval */
X
X#define	VSHTOP	2		/* Lines needed at top of display */
X#define	VSHBOT	2		/* Lines needed at bottom of display */
X#define	VSHLEFT	4		/* Spaces needed at left of display */
X
X/* Parms loadable through .vshrc are accessed through command.h.
X   Alter their default values in cmdini.c	*/
X
X/* Tty_set parameters */
X#define RAWMODE		0
X#define	COOKEDMODE	1
X
X/* Functions called by command return the next command to execute.  */
X/* In addition, the following bits are returned.  */
X
X#define CMDMASK		0x00ff	/* Bits of next command */
X#define	REPLOT		0x0100	/* Must replot directory */
X#define NOOP		0x0200	/* Return for command not found */
X#define ENTERDIR	0x0400	/* New directory entered */
X
X/* If no special return is necessary, use return REPLOT or NOREPLOT. */
X#define NOREPLOT	0x0000
X
X/* GOOD or BAD returns */
X#define	GOOD	1
X#define	BAD	0
X
X/* When calling command, indicate type of command:  */
X#define DIRCMD	1
X#define SHOWCMD	2
Xextern char dircmds[];
X
X/* Show operates in two modes */
X#define GREPMODE	0
X#define MAKEMODE	1
X
X/* Select or enter mode */
X#define	ENTERMODE	0
X#define	SELECTMODE	's'
Xextern char selectname[STRMAX];
Xextern int selectcmd;
Xextern int selectpage;
Xextern int selecttype;
X
Xextern char username[];
Xextern char *arrow;
Xextern char *helptext;
X
Xextern int window, owindow, ewindow;
Xextern int xoff;			/* Column offset for vary CRT widths */
X
X/*
X * Custom enter text info
X */
X#define	ENTEREDIT	"$EDITOR"	/* Enter $EDITOR for text */
X#define	ENTERDISP	"display"	/* Use internal display() for text */
X
X/* Parameters from .vshrc file. P_name is the parameter name, p_val is
X   the parameter's value.
X*/
X
Xstruct parmstruct {
X	char *p_name,
X	     *p_val;
X};
X
Xextern struct parmstruct  parmtab[];
X
X/* References to the various parameters */
X
X#define	EDITOR		parmtab[0].p_val
X#define	MAKE		parmtab[1].p_val
X#define	GREP		parmtab[2].p_val
X#define	RMHELP		parmtab[3].p_val
X#define	SHOWHELP	parmtab[4].p_val
X#define	MAKERROR	parmtab[5].p_val
X#define	GREPOUT		parmtab[6].p_val
X#define	VSHMODE		(*parmtab[7].p_val)
X#define	QUITCHAR	(*parmtab[8].p_val)
X#define	PAGECHAR	(*parmtab[9].p_val)
X#define	PATH		parmtab[10].p_val
X#define	TERM		parmtab[11].p_val
X#define	HOME		parmtab[12].p_val
X#define	SHELL		parmtab[13].p_val
X#define	ENTERTEXT	parmtab[14].p_val
X#define	WINDOW		parmtab[15].p_val
X#define	HELPFILE	parmtab[16].p_val
X#define	COLUMN		parmtab[17].p_val
X#define	NOARG		(*parmtab[18].p_val)
X#define	VIMOTION	(*parmtab[19].p_val)
X#define	MAIL		parmtab[20].p_val
X#define	MORESIZE	parmtab[21].p_val
X#define	ENTERPATH	parmtab[22].p_val
X
X/* Termcap */
Xextern char *BC, *UP, *CM, *CL, *tgoto();
Xextern char *CE, *SO, *SE;
Xextern short PC;
Xextern int CO, LI;
Xextern char *CS;
X/* A new one, clear to beginning of display */
Xextern char *CB;
Xextern char *BO, *BE;
Xextern char *SR, *CD;
Xextern int AM, XN;
Xextern char *TI, *TE;
All work and no play makes Jack a dull boy
echo src/help.c
sed 's/^X//' > src/help.c << 'All work and no play makes Jack a dull boy'
X#include "hd.h"
X
X/* Help displays a file in paged mode.  The parm is the file name. */
X
Xhelp (parm) char *parm; 
X{
X    FILE *helpfile;
X    int flag;
X
X    flag = 0;
X    helpfile = fopen (parm, "r");
X    if (helpfile == NULL) 
X    {
X	myperror (parm);
X	return NOREPLOT;
X    }
X    else
X	flag = page (helpfile, parm);
X    fclose (helpfile);
X    return flag ? REPLOT : NOREPLOT;
X}
X
X/* Display is the command processor's version of help */
Xdisplay (argv) char **argv; 
X{
X    register char *msg;
X    char name [STRMAX];
X
X    msg = 0;
X    if (*argv == CNULL) {
X	if (VSHMODE == SELECTMODE && selecttype == DIRCMD && *selectname)
X		return help(selectname);
X	msg = "Display: ";
X    }
X    if (getfname (*argv, name, msg) == BAD) return NOREPLOT;
X    else return help (name);
X}
All work and no play makes Jack a dull boy
echo src/main.c
sed 's/^X//' > src/main.c << 'All work and no play makes Jack a dull boy'
X#include "hd.h"
X#include "mydir.h"
X#include <pwd.h>
X#include <signal.h>
X#ifdef	PWBTTY
X#include <sys/sgtty.h>
X#endif
X
X#ifdef	STOPABLE
X#define	sigmask(sig)	(1 << ((sig) - 1))
X#define	sigunblock(mask) sigsetmask(sigblock(0) & ~(mask))
Xint suspend();
X#include <sgtty.h>
X#endif
X
X/* Extract data about environment */
X#define ENV_COUNT	6
X
Xchar *env_name [ENV_COUNT] =			/* Names */
X{
X    "SHELL", "HOME", "TERM", "PATH", "EDITOR", "MAIL"
X} ;
X
Xchar **env_dest [ENV_COUNT] =			/* Place stored */
X{
X    &SHELL, &HOME, &TERM, &PATH, &EDITOR, &MAIL
X} ;
X
X#define	USERSIZE 20
Xchar username[USERSIZE] = { "(who are you ?)" };
Xchar *arrow;
X
Xchar *helptext = 0;
X/* HELPSIZE should be plenty */
X#define	HELPSIZE 1536
X
X/* Screen parameters, see also setwindow() */
X#define	CRTROW	24			/* Standard 24x80 CRT default */
Xint window = CRTROW;
Xint nfpp = (CRTROW-4);			/* Two lines of header and footer
X					 *  not greater than maxnfpp == 26
X					 */
X
Xmain (c, v) 
Xchar **v;
X{
X
X/*  Initialize everything, then run.  */
X
X    register int i, j;
X    register char *s;
X    struct passwd *p, *getpwuid();
X    extern leave ();
X    extern char *shargv[];
X    extern char *getenv();
X
X	int fflag;
X	int (*oi)(), (*oq)();
X#ifdef	PWBTTY
X	int tm[3];
X#endif
X
X    comein ();
X    for (i = 0; i < ENV_COUNT; i++)
X	if ((s = getenv(env_name[i])) != NULL)
X		*env_dest [i] = s;
X
X    *shargv = SHELL;
X#ifdef	PWBTTY
X	HOME = (char *) logdir();
X#endif
X	/* Should we depend on $USER ? */
X	p = getpwuid(i = geteuid());
X	if (p) {
X		strcpy(username, p->pw_name);
X		if (*HOME == 0)
X			HOME = p->pw_dir;
X	}
X	/* Give root a different arrow */
X	arrow = i ? "->" : "#>";
X
X	if (MAIL && *MAIL) {
X		i = strlen(MAIL);
X		if (MAIL[i-1] == '~') {
X			s = (char *) malloc(i+1+USERSIZE);
X			if (s != NULL) {
X				strcpy(s, MAIL);
X				strcpy(&s[i-1], username);
X				MAIL = s;
X			}
X		}
X	}
X
X	if (signal(SIGHUP, leave) == SIG_IGN)
X		signal(SIGHUP, SIG_IGN);
X	if (signal(SIGTERM, leave) == SIG_IGN)
X		signal(SIGTERM, SIG_IGN);
X	oi = signal (SIGINT, SIG_IGN);	/* dyt, was leave */
X	oq = signal (SIGQUIT, SIG_IGN);
X	tty_init ();
X	i = curs_init ();
X	/*
X	 * Check if terminal is a CRT
X	 * if not, check option to chain to /bin/sh
X	 */
X	fflag = 0;
X	j = 1;
X	if (c > 1 && strcmp(v[j], "-f") == 0) {
X		c--;
X		j++;
X		fflag++;
X	}
X#ifdef	PWBTTY
X	if (i == 0)
X		i = gtty(0, tm);
X	if (!fflag && (i || (tm[2]&CRT) == 0)) {
X#else
X	if (!fflag && i) {
X#endif
X		fprintf(stderr, "Not a screen terminal");
X		if ((c > 0 && **v == '-')
X			|| (c > 1 && strcmp(v[j], "-") == 0)) {
X			fprintf(stderr, ", exec'ing %s...\r\n", SHELL);
X			signal(SIGINT, oi);
X			signal(SIGQUIT, oq);
X			execl(SHELL, "sh", 0);
X			fprintf(stderr, "exec failed");
X		}
X		fprintf(stderr, "\r\n");
X		exit(0);
X	}
X
X    erase (); printf ("Vsh %s\r\n", VERSION);
X    cmdldrc ();
X    wdfile = -1;
X    if (curdir () || enterdir (DOT) == NOREPLOT)
X	leave ();
X#ifdef	STOPABLE
X    signal(SIGTSTP, suspend);
X#endif
X    tty_push (RAWMODE);
X
X	/* Read in helpfile once */
X	helptext = "";
X	if (HELPFILE && *(HELPFILE)) {
X		if (stat(HELPFILE, &scr_stb) == 0) {
X#ifdef	V6
X			i = (scr_stb.st_size1 < HELPSIZE) ? scr_stb.st_size1 :
X#else
X			i = (scr_stb.st_size < HELPSIZE) ? scr_stb.st_size :
X#endif
X				HELPSIZE;
X			if ((helptext = (char *) malloc(i+1)) == NULL) {
X				fprintf(stderr, "No memory\n");
X				exit(-1);
X			}
X			j = open(HELPFILE, 0);
X			helptext[0] = 0;
X			if (j > 0 && (i = read(j, helptext, i)) > 0)
X				helptext[i] = 0;
X			close(j);
X		}
X	}
X
X	setwindow();
X	setcolumn();
X    process ();
X}
X
X#ifdef	STOPABLE
Xsuspend(signo)
Xint signo;
X{
X	int putch();
X	
X	tty_push(COOKEDMODE);
X	/*
X	 * This is debatable: should we give back a full screen
X	 *	or just the execute window ?
X	 */
X	if (CS && ewindow) {
X		tputs(tgoto(CS, LI-1, 0), 0, putch);
X		atxy(1, LI);
X	}
X#ifdef	SIGVTALRM			/* Kludge to detect 4.2BSD signal */
X	/* stop ourselves */
X	sigunblock(sigmask(signo));
X	signal(signo, SIG_DFL);
X	kill(getpid(), signo);
X	sigblock(sigmask(signo));
X#else
X	kill(getpid(), signo);
X#endif
X	signal(signo, suspend);
X	printf("Vsh restarted.\r");
X	fflush(stdout);
X	tty_pop();
X	ioctl(0, TIOCSTI, "\014");	/* Fake an input of ^L */
X}
X#endif
All work and no play makes Jack a dull boy
echo src/make.c
sed 's/^X//' > src/make.c << 'All work and no play makes Jack a dull boy'
X#include "hd.h"
X#include "strings.h"
X#include "command.h"
X
X/* Interface to make.  Fmake forks make off and returns.  Wmake waits
X   for completion.  Both cause output to be saved in the file .makerror.
X*/
X
Xfmake () 
X{
X    int p;	/*      Process number */
X    int mfile;	/*      File number of .makerror */
X    FILE *mstream;	/*      Stream of .makerror */
X    FILE *showopen ();	/*      Opener of mstream */
X    char inline[STRMAX];
X
X    if (getcomment(inline))
X	return NOREPLOT;
X
X    mstream = showopen ("w", MAKEMODE);
X    if (mstream == NULL) return NOREPLOT;
X    mfile = fileno (mstream);
X
X    if (myfork () == 0) 
X    {
X	if ((p = myfork ()) == 0) 
X	{
X	    close (outfile); close (errorfile);
X	    dup (mfile); dup (mfile);
X	    close (infile); open ("/dev/null", 0);
X	    hilite("%s", inline);
X	    printf("\r\n");
X	    if(SHELL && *SHELL)
X		myexecl (SHELL, "+", "-c", inline, 0);
X	    else
X		myexecl("/bin/sh", "+", "-c", inline, 0);
X	}
X	else 
X	{
X	    p = join (p);
X	    beep (); sleep (2); beep ();
X	    if (p) {
X		sleep(1);
X		beep();
X	    }
X	    exit (0);
X	}
X    }
X    else 
X    {
X	fclose (mstream);
X	putmsg (MAKE);
X    }
X    return NOREPLOT;
X}
X
Xwmake () 
X{
X    int p;	/*      Process number */
X    FILE *mstream;	/*      Stream of .makerror */
X    FILE *showopen ();	/*      Opener of mstream */
X    register ch;	/*      Work character */
X    char inline[STRMAX];
X
X    int pipefile [2];	/*      Pipe file numbers */
X#	define pipein	pipefile [0]
X#	define pipeout	pipefile [1]
X
X    FILE *pipestrm;	/*      Stream of pipein */
X
X    if (getcomment(inline))
X	return NOREPLOT;
X
X    mstream = showopen ("w", MAKEMODE);
X    if (mstream == NULL) return NOREPLOT;
X
X    tty_push(COOKEDMODE);
X    pipe (pipefile);
X    if ((p = myfork ()) == 0) 
X    {
X	close (outfile); close (errorfile);
X	dup (pipeout); dup (pipeout);
X	hilite("%s", inline);
X	printf("\r\n");
X	if(SHELL && *SHELL)
X		myexecl (SHELL, "+", "-c", inline, 0);
X	else
X		myexecl("/bin/sh", "+", "-c", inline, 0);
X    }
X    else 
X    {
X	close (pipeout);
X	pipestrm = fdopen (pipein, "r");
X	while ((ch = getc (pipestrm)) != EOF) 
X	{
X	    putchar (ch); putc (ch, mstream);
X	}
X	fclose (pipestrm); fclose (mstream);
X	join (p);
X    }
X    tty_pop ();
X    return CMD_SE | REPLOT;
X}
X
Xgetcomment(inline) /* get user make parameters */
Xchar *inline;
X{
X    register char *s, *t;
X
X    /* get user comments for the make command */
X    s = inline;
X    t = MAKE;
X    while (*s++ = *t++);
X    --s;
X    *s++ = ' ';
X    tty_push(COOKEDMODE);
X    clearmsg(2);
X    putmsg("Make options (quit):");
X    printf(" ");
X    xgetline(stdin, s, STRMAX);
X    tty_pop();
X    if (!strcmp("q", s) || *s == (QUITCHAR-'@')
X	|| !strcmp("quit", s)) {
X	hilite("(Aborted)");
X	return 1;
X    }
X    return 0;
X}
All work and no play makes Jack a dull boy
echo src/makefile
sed 's/^X//' > src/makefile << 'All work and no play makes Jack a dull boy'
XCC=cc
XSHELL=/bin/sh
XCFLAGS= -O -DVSH -DSTOPABLE -DVT100ARROW
XLDFLAGS= -g
XLOCALLIB=strlib.a
XLIBS=
XBIN=/usr/local/bin
XDESTDIR=/usr/lib/vsh
XOBJECTS= main.o at.o dir.o xeq.o curdir.o process.o enterf.o \
X	help.o page.o dirlist.o tty.o remove.o file.o show.o \
X	ascii.o make.o grep.o showopen.o strings.o \
X	curses.o account.o cmdrun.o misccmd.o dircmd.o cmdini.o \
X	readarg.o cmdload.o classify.o options.o xecute.o find.o system.o
X
X.c.o:
X	$(CC) $(CFLAGS) -c $<
X
Xvsh:	$(OBJECTS) mydir.h hd.h $(LOCALLIB)
X	cc -n -o nvsh $(LDFLAGS) $(OBJECTS) $(LOCALLIB) -ltermlib $(LIBS)
X	-mv vsh ovsh
X	mv nvsh vsh
X	-rm -f ovsh
X	size vsh
X
Xinstall: vsh
X	mv $(BIN)/vsh /usr/tmp/vsh
X	cp vsh $(BIN)/vsh
X	chmod 755 $(BIN)/vsh
X	-rm -f /usr/tmp/vsh
X	echo 'Do make docs to install documentation'
X
Xdocs:
X	-mkdir $(DESTDIR)
X	cp vshrc.gen ../doc/vshelp ../doc/genhelp* ../doc/rmhelp ../doc/showhelp $(DESTDIR)
X	echo 'Look over ../doc/vshrc* and ../doc/genhelp.dan to see a sample of'
X	echo 'how to configure VSH for a nicer interface. See ../termcap for'
X	echo 'additions to your /etc/termcap for a better display.'
X	echo 'A manual page for VSH can be found in ../doc .'
X
Xstrlib.a:
X	(cd ../strlib; $(CC) $(CFLAGS) -c str*.c)
X	ar rv $(LOCALLIB) ../strlib/str*.o
X	-ranlib $(LOCALLIB)
X
Xlint:
X	lint -bxac *.c
X
Xoldinstall: vsh
X	vshrc.gen
X	cp vsh $(BIN)
X	cp genhelp $(DESTDIR)
X	cp rmhelp $(DESTDIR)
X	cp showhelp $(DESTDIR)
X	cp dflt.vshrc $(DESTDIR)
X
Xclean:
X	rm -f $(OBJECTS) vsh
All work and no play makes Jack a dull boy
echo src/misccmd.c
sed 's/^X//' > src/misccmd.c << 'All work and no play makes Jack a dull boy'
X#include "hd.h"
X
Xdate () 
X{		/* display current date */
X
X    long datetime [1];
X
X    time (datetime); clearmsg (1);
X    printf ("    ");
X    hilite ("%.24s", ctime (datetime));
X    return NOREPLOT;
X}
X
Xcallshell (argv) char **argv; 
X{
X    if (argv[0])  
X    {
X	mysystem (argv[0]);
X	getrtn ();
X    }
X    else f_exec (SHELL, SHELL, 0);
X    return REPLOT;
X}
All work and no play makes Jack a dull boy
echo src/mydir.h
sed 's/^X//' > src/mydir.h << 'All work and no play makes Jack a dull boy'
X/*  Global Defines  */
X
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <sys/dir.h>
X
X/*  This data relates to the working directory and the portion of that
X    directory currently displayed on the screen.
X*/
X
Xextern int tfiles;		/* Number of files in this directory */
X
X/*  The files of a directory are partitioned into pages.  Each page
X    represents a display on the terminal screen.  */
X
X#define	maxnfpp	26		/* Maximum number of files per page, a-z */
Xextern int nfpp;		/* Number of files per page */ 
X
Xextern int tpages,		/* Total pages this directory */
Xcpage;				/* Current page this display */
Xextern int pageoff;		/* Page offset for multicolumn display */
Xextern int column, colfield;
X
X/*  File information about working dir */
X
Xextern struct stat wd_stb;	/* its inode */
X
X/* Path name of working directory */
X#define MPLEN	STRMAX - 1	/* Max path len (chars) */
X#define LPLEN	STRMAX - 20	/* Limit path len */
X
Xextern int wdfile;		/* its file number */
Xextern char wdname [STRMAX];	/* its full path name */
X
X/*#define	max_dir_size	(sizeof dirbuf - sizeof dirbuf [0]) */
X
X/* extern char *d_namep [mfiles];	/* pointers to all the file names */
X
X#ifndef	NDIR
Xextern char **d_namep;
X#endif
X
X/* filename returns a pointer to the file assoc. with arg on cpage */
X#ifndef	NDIR
X#define	filename(arg)	(d_namep [arg + (cpage+pageoff) * nfpp - nfpp])
X#else
Xextern struct direct **d_dirp;
X#define	filename(arg)	(d_dirp[arg + (cpage+pageoff) * nfpp - nfpp]->d_name)
X#endif
X
Xextern int dir_status;			/* its logical status */
X#define	loaded		0
X#define	unloaded	1
X#define	toobig		2
X#define	protected	3
X
X/*  scratch */
Xextern struct stat scr_stb;
X
X/* Useful macros */
X
X#define	DOT	"."
X#define DOTDOT	".."
X#define	SLASH	"/"
X#define dirsize sizeof (struct direct)
X
X#ifndef	NDIR
X#define	dnamesize	DIRSIZ
X#endif
X
X#define	ISROOT(arg)	(arg [1] == 0)
All work and no play makes Jack a dull boy
echo src/options.c
sed 's/^X//' > src/options.c << 'All work and no play makes Jack a dull boy'
X#include "hd.h"
X#include "command.h"
X
X/* Print and modify command tables interactively */
X
Xoptions (parm) char **parm; 
X{
X    FILE *vshout;
X    int line = 0;	/* Current line number */
X    register struct cmdstruct *cmdp;
X    register struct classstruct *classp;
X    register char **argv;
X    struct parmstruct *parmp;
X
X    /* Print command tab in .vshrc format */
X    /* If parm present, dump to file and quit */
X    erase ();
X    if (*parm) 
X    {
X	vshout = fopen(*parm, "w");
X	if (vshout == NULL) {
X		printf(" ");
X		hilite("%s: Cannot create%s\r\n", *parm);
X		return REPLOT;
X	}
X	line= -9999;
X    }
X    else {
X	vshout = stdout;
X	bufout();
X    }
X    for (;;) 
X    {
X	for (cmdp = cmdtab; cmdp->cmd_char >= 0; cmdp++) 
X	{
X	    if (cmdp->cmd_proc) 
X	    {
X		for (classp = classtab;
X		*classp->cl_name &&
X		    classp->cl_proc !=
X		    cmdp->cmd_proc;
X		classp++);
X
X		fprintf (vshout, "%c\t%s", cmdp->cmd_char, 
X		    classp->cl_name);
X
X		for (argv = cmdp->cmd_argv; *argv;)
X		    fprintf (vshout, "  %s", *argv++);
X
X		if (optline (&line, vshout) == BAD)
X		    return REPLOT;
X	    }
X	}
X	for (parmp = parmtab; parmp->p_name; parmp++) 
X	{
X	    fprintf (vshout, "%s\t%s", parmp->p_name, parmp->p_val);
X	    if (optline (&line, vshout) == BAD) return REPLOT;
X	}
X
X	if (*parm) {
X		fclose(vshout);
X		return REPLOT;
X	}
X	if (line != 0 && optcmd () == BAD) break;
X	erase (); line = 0;
X    }
X    unbufout();
X    return REPLOT;
X}
X/* Processing for end of each line includes:
X	1.  Print the newline.
X	2.  If the end of the page, prompt for a command.
X*/
Xoptline (line, fd)
Xint *line; 
XFILE *fd;
X{
X    int ret;	/* return from optcmd */
X
X    if (fd == stdout)
X	putc (CR, fd);
X    putc (LF, fd);
X    if (++*line < (window-6)) return GOOD;
X
X    ret = optcmd (); erase (); *line = 0;
X    return ret;
X}
X
Xoptcmd () 
X{
X    char cbuf [STRMAX],	/* Buffer for input parm */
X	*argv [ARGVMAX];	/* Pointers to input parm */
X    int argc;	/* Number of parm */
X    int ret;	/* Return from readarg */
X
X    int line = 0;	/* Current line num of rcstream */
X
X    printf ("\r\n");
X    hilite ("Type in a new parameter, or\r\n\
XPress ^D to leave.  Press -Return- to display more parameters.\r\n\n");
X    unbufout();
X    tty_push (COOKEDMODE);
X    for (;;) 
X    {
X	ret = readarg (stdin, &line, &argc, argv, cbuf);
X	if (argc == 0) break;
X	if (compe ("quit", argv [0])) 
X	{
X	    ret = BAD; break;
X	}
X	if (ret != BAD) cmdldarg (line, argc, argv);
X	/* Reset screen window */
X	setwindow();
X	/* Reset columns */
X	setcolumn();
X    }
X    tty_pop ();
X    erase();
X    if (ret != BAD)
X	bufout();
X    return ret;
X}
All work and no play makes Jack a dull boy
echo src/page.c
sed 's/^X//' > src/page.c << 'All work and no play makes Jack a dull boy'
X#include "hd.h"
X#include "strings.h"
X#include "mydir.h"
X#include <signal.h>
X
X#define	PROMPTLINE	1		/* Line count used by prompt */
X#define	OVERLAP		2		/* Lines of page overlap */
X#define	MMINSIZE	100		/* Tiny more buffer */
X#define	topwin()	((ewindow && CS) ? window-PROMPTLINE : 1)
X
Xint nointer;
Xchar *mbegin, *mend, *mbufp;
Xunsigned mbsize;
Xlong maxline, topline, botline;
Xlong omaxline;
Xint curwin;
Xint noprint;
Xint isfile;
Xint putch();
Xchar *oldline();
X
Xpage (stream, name)
XFILE *stream; 
Xchar *name;
X{
X#ifdef	MBUFSTACK
X	char mbuf[MBUFSIZE];
X#else
X	char mbuf[MMINSIZE];
X#endif
X	register int linelim;
X	register ch;
X	register int ttych;
X	int replot, fwin;
X	char *ll;
X	char number[20];
X	char **v;
X	int serror;
X	int (*oldsig)(); extern catch();
X
X	fwin = replot = 1;
X	oldsig = signal (SIGINT, catch); nointer = 1;
X	if (name && *name == 0) {
X		isfile = 0;
X		name = 0;
X	}
X	else
X		isfile = 1;
X
X#ifdef	MBUFSTACK
X	mbsize = MBUFSIZE;
X	mbufp = mbuf;
X#else
X	mbsize = atoi(MORESIZE);
X	/* If not a pipe, stat the file */
X	if (name && stat(name, &scr_stb) == 0
X		&& (scr_stb.st_mode&S_IFMT) == S_IFREG
X#ifdef	V6
X		&& scr_stb.st_size0 == 0 && scr_stb.st_size1 < mbsize)
X		mbsize = scr_stb.st_size1+1;
X#else
X		&& scr_stb.st_size < mbsize)
X		mbsize = scr_stb.st_size+1;
X#endif
X	mbsize += mbsize/CO;			/* add extra for long lines */
X	if (mbsize < MMINSIZE) {
X		mbufp = mbuf;
X		mbsize = sizeof mbuf;
X	}
X	else {
X		mbufp = (char *)malloc(mbsize);
X		if (mbufp == NULL) {
X			fprintf(stderr, "No memory for pager\n\r");
X			mbufp = mbuf;
X			mbsize = sizeof mbuf;
X		}
X	}
X#endif
X
X	bufout ();
X	if (ewindow && CS) {
X		curwin = LI-(window-VSHBOT)-PROMPTLINE;
X		fwin = replot = !ewin();
X	}
X	else {
X		curwin = LI-PROMPTLINE;
X		noprint = ewindow;
X		ewindow = 0;
X		erase ();
X		ewindow = noprint;
X	}
X	if (name)
X		hilite("%s\r\n", name);
X
X	noprint = 0;
X	initmore();
X	linelim = curwin-PROMPTLINE;
X	do
X	{
X		ch = more(stream, linelim, &ll);
X		if (topline < 0)		/* Only if file is short */
X			topline = 0;
X		if (ch == EOF && linelim >= 0)
X			hilite("Done:");
X		else {
X			ch = '\n';
X			hilite("More?");
X		}
X		fflush (stdout);
X
X		ttych = getch ();
X		printf("\r       \r");
X		fflush(stdout);
X		/* dyt flush input */
X		tty_push(COOKEDMODE);
X		tty_pop();
X		switch(ttych) {
X		case EOT:
X		case EOF:
X			linelim = curwin / 2;
X			break;
X		case 020:	/* ^P, EMACS style */
X		case 'j':
X		case '\r':
X		case '\n':
X			linelim = 1;
X			break;
X		case 025:	/* ^U, VI style */
X			linelim = - curwin / 2;
X			break;
X		case 016:	/* ^N, EMACS style */
X		case 'k':
X		case '-':
X			linelim = -1;
X			break;
X		case '^':
X		case 02:	/* ^B, VI style */
X		case 033:	/* ESC, EMACS ESC-v */
X			linelim = - (curwin-OVERLAP);
X			break;
X		case 'e':
X		case 'v':
X			linelim = 0;
X			if (name == 0) {
X				putch(07);
X				break;
X			}
X			printf("\r%s %s", EDITOR, name);
X			fflush(stdout);
X			sprintf(number, "+%ld", (botline+topline)/2);
X			replot = nedit(name, number);
X			if (replot == NOREPLOT) {
X				printf(" : Bad file");
X				fflush(stdout);
X				sleep(1);
X				break;
X			}
X		case 'q':
X		case 'n':
X			ttych = 'n';
X			break;	/* No more */
X		case 022:		/* ^R, EMACS style */
X		case 023:		/* ^S, EMACS style */
X		case '?':
X		case '/':
X			ch = '\n';
X			linelim = search(stream, ttych, &serror);
X			break;
X		case 'G':
X		case '>':
X			linelim = prttail(stream);
X			break;
X		case '1':
X		case '<':
X			ch = '\n';
X			linelim = prthead(stream);
X			break;
X		case '!':
X			tty_push(COOKEDMODE);
X			v = &ll;
X			ll = CNULL;
X			callshell(v);
X			replot = REPLOT;
X			tty_pop();
X			ewin();
X		case 014:		/* ^L */
X			topline += curwin;
X			botline += curwin;
X			prtback(-curwin, &ll);
X			linelim = 0;
X			break;
X		default:
X			linelim = curwin-OVERLAP;
X			break;
X		}
X
X	} while ((ch != EOF || linelim <= 0) && ttych != 'n' && nointer);
X
X	unbufout ();
X	signal (SIGINT, oldsig);
X	if (!fwin)
X		vwin();
X#ifndef	MBUFSTACK
X	if (mbufp != mbuf)
X		free(mbufp);
X#endif
X	omaxline = 0;		/* Reset for oldline() */
X	return replot;
X}
X
Xmore(stream, linelim, lastline)
XFILE *stream;
Xregister int linelim;
Xchar **lastline;
X{
X	register char *s;
X	register int newlines;
X	register int col, maxcol;
X	int ch;
X
X	newlines = 0;
X	*lastline = 0;
X	ch = '\n';
X	if (linelim == 0)
X		return ch;
X	/* Looking forwards from current botline */
X	if (linelim > 0) {
X		newlines = botline + linelim - maxline;
X		if (newlines < 0)
X			newlines = 0;
X		/* Number of old lines to fetch forwards */
X		linelim -= newlines;
X	}
X	/* Looking backwards from current topline */
X	else {
X		if (curwin >= maxline) {
X			printf("\r        \r\07");
X			return EOF;
X		}
X		if (SR || noprint) {
X			if (!noprint)
X				atxy(1, topwin());
X			while (linelim < 0) {
X				if (topline <= 1) {
X					putch(07);
X					break;
X				}
X				if ((s = oldline((int)(maxline - topline + 2)))
X					== 0)
X					break;
X				linelim++;
X				*lastline = s;
X				if (!noprint) {
X					tputs(SR, 0, putch);
X					putch ('\r');
X					prtold(s);
X				}
X				--topline;
X				--botline;
X			}
X		}
X		/* No reverse scroll (sigh...) */
X		else
X			linelim = prtback(linelim, lastline);
X		if (linelim < 0)
X			ch = EOF;
X		if (!noprint) {
X			atxy(1, LI);
X			clearline();
X		}
X	}
X
X	while (linelim-- > 0) {
X		if ((s = oldline((int)(maxline - botline))) == 0) {
X			if (newlines <= 0)
X				ch = EOF;
X			break;
X		}
X		*lastline = s;
X		if (!noprint) {
X			prtold(s);
X			printf("\r\n");
X		}
X		topline++;
X		botline++;
X	}
X
X	col = 0;
X	maxcol = CO - (AM && !XN ? 1 : 0);
X	while (newlines-- > 0) {
X		s = mend;
X		while ((ch = getc (stream)) != EOF) {
X			/* Put new character in circular buffer (yuck) */
X			switch (ch) {
X			case '\t':
X				col += 8 - (col&7);
X				break;
X			case '\b':
X				if (col > 0)
X					col--;
X				break;
X			case '\r':
X				col = 0;
X				break;
X			case 0177:
X				break;
X			default:
X				if (ch >= ' ')
X					col++;
X				break;
X			}
X			/*
X			 * Fake long lines
X			 *	a kludge, but I don't have time for better
X			 */
X			if (col > maxcol) {
X				ungetc(ch, stream);
X				ch = '\n';
X			}
X			*mend++ = ch;
X			if (mend >= mbufp+mbsize)
X				mend = mbufp;
X			if (mend == mbegin) {
X				mbegin++;
X				if (mbegin >= mbufp+mbsize)
X					mbegin = mbufp;
X			}
X			if (ch == '\n')
X				break;
X			if (!nointer) {
X				ch = EOF;
X				break;
X			}
X			if (!noprint)
X				putch (ch);
X		}
X		col = 0;
X		if (ch == EOF)
X			break;
X		else {
X			maxline++;
X			botline++;
X			topline++;
X			*lastline = s;
X		}
X		if (!noprint) {
X			putch ('\n');
X			putch ('\r');
X			fflush (stdout);
X		}
X	}
X	return ch;
X}
X
X/*
X * Print tail of file
X */
Xprttail(stream)
XFILE *stream;
X{
X	char *ll;
X
X	/* Skip to end of file */
X	noprint = 1;
X	while (more(stream, curwin, &ll) != EOF);
X	more(stream, -curwin, &ll);
X	noprint = 0;
X	return curwin;
X}
X
X/*
X * Print head of file (as much as saved)
X */
Xprthead(stream)
XFILE *stream;
X{
X	char *ll;
X
X	/* If not a pipe, rewind by seeking */
X	if (isfile && fseek(stream, 0L, 0) == 0) {
X		initmore();
X		return curwin;
X	}
X	else {
X		noprint = 1;
X		while (more(stream, -curwin, &ll) != EOF);
X		more(stream, curwin, &ll);
X		noprint = 0;
X		return -curwin;
X	}
X}
X
X/*
X * Initmore
X */
Xinitmore()
X{
X	mbegin = mend = mbufp;
X	maxline = botline = 0;
X	topline = 1 - curwin;
X}
X
X/*
X * Print -n lines before topline to botline+n
X */
Xprtback(n, lastline)
Xregister int n;
Xchar **lastline;
X{
X	register int i, j;
X	register int k;
X	register char *s;
X	int found;
X
X	if (!noprint) {
X		erasebelow(topwin());
X		atxy(1, topwin());
X	}
X	/*
X	 * i is the number of lines to print
X	 * j is the offset (lines into the old buffer)
X	 * n is the line currently being tried (it may not exist)
X	 * k becomes the lines left undone (didn't exist)
X	 */
X	i = curwin-PROMPTLINE;
X	if (i > maxline-PROMPTLINE)
X		i = maxline-PROMPTLINE;
X	j = maxline - (n+topline) + 2;
X	*lastline = 0;
X	found = 0;
X	k = 0;
X	while (i > 0) {
X		if (found)
X			i--;
X		n++;
X		if ((s = oldline(--j)) == 0) {
X			k--;
X			continue;
X		}
X		/* First line actually existant */
X		if (!found) {
X			found++;
X			topline += n-1;
X			botline += n-1;
X			*lastline = s;
X		}
X		if (n < 0)
X			n++;
X		if (!noprint) {
X			prtold(s);
X			putch('\n');
X			putch('\r');
X		}
X	}
X	return k;
X}
X
X/*
X * Print an old line
X */
Xprtold(t)
Xregister char *t;
X{
X	while (*t != '\n') {
X		putchar (*t);
X		if (t == mend)
X			break;
X		if (++t >= mbufp+mbsize)
X			t = mbufp;
X	}
X}
X
X/*
X * Find the n'th old line
X */
Xchar *oldline(ln)
Xint ln;
X{
X	register int n;
X	register char *s, *t;
X	int i, first;
X	static int on;
X	static char *os;
X
X	n = ln;
X	i = 0;
X	/* Try to start from previous position (an optimization) */
X	if (maxline == omaxline) {
X		i = n - on;
X		if (i == 0)
X			return os;
X		if (i == -1 && on > 1) {
X			s = t = os;
X			for (;;) {
X				t++;
X				if (t >= mbufp+mbsize)
X					t = mbufp;
X				if (t == mend)
X					break;
X				if (*s == '\n')
X					goto ret;
X				s = t;
X			}
X		}
X	}
X	if (i > 0 && os != mbegin) {
X		n = i;
X		s = os;
X	}
X	else
X		s = mend;
X	n++;				/* Newline count is one more */
X	first = 1;
X	while (n-- > 0) {
X		for (;;) {
X			t = s;
X			if (s <= mbufp)
X				s = mbufp+mbsize;
X			s--;
X			if (s == mbegin) {
X				/* Just an approximation */
X				if (mbegin != mbufp)
X					putch(07);
X				if (n > 0)
X					return 0;
X				t = s;
X				break;
X			}
X			if (*s == '\n') {
X				first = 0;
X				break;
X			}
X			/*
X			 * If first char is not a newline,
X			 *	last line in file is without newline
X			 *	adjust n
X			 */
X			if (first) {
X				n--;
X				first = 0;
X			}
X		}
X	}
X	/* Save for future reference */
Xret:
X	omaxline = maxline;
X	on = ln;
X	os = t;
X	return t;
X}
X
X/*
X * search string
X */
X#define	SFOR	-1
X#define	SREV	-2
X
Xsearch(stream, way, failp)
XFILE *stream;
Xint way;
Xint *failp;
X{
X	register int i;
X	register int savec;
X	register char *s;
X	char *ll;
X	int serror;
X	static char sbuf[STRMAX];
X
X	*failp = 0;
X	/* Get string to search for */
X	if (way == 022)
X		way = '?';			/* ^R */
X	else if (way == 023)
X		way = '/';			/* ^S */
X	if (way > 0) {
X		tty_push(COOKEDMODE);
X		putch(way);
X		fflush(stdout);
X		savec = sbuf[0];
X		i = getline(sbuf);
X		/* Adjust for linefeed user just hit */
X		tty_pop();
X		if (UP)
X			tputs(UP, 0, putch);
X		clearline();
X		fflush(stdout);
X		if (i == 0) {
X			if (savec == 0) {
X				if (botline >= curwin) {
X					topline++;
X					botline++;
X				}
X				putch(07);
X				*failp = 1;
X				return -1;
X			}
X			sbuf[0] = savec;
X		}
X	}
X	i = 0;
X	noprint = 1;
X	/* Reverse search, does not wraparound */
X	if (way == '?' || way == SREV) {
X		while (more(stream, -1, &ll) != EOF) {
X			if (ll == 0)
X				break;
X			i++;
X			if (match(sbuf, ll)) {
X				noprint = 0;
X				prtback(-(curwin/2), &ll);
X				return 0;
X			}
X		}
X		/* Not found, try to restore screen */
X		if (way == '?')
X			while (i-- >= 0)
X				more(stream, 1, &ll);
X	}
X	/* Forward search, which wraps around */
X	else {
X		while (more(stream, 1, &ll) != EOF) {
X			if (ll == 0)
X				break;
X			i++;
X			if (match(sbuf, ll)) {
X				more(stream, curwin/2, &ll);
X				noprint = 0;
X				prtback(0, &ll);
X				return 0;
X			}
X		}
X		/* Not found below, try wraparound (slow) */
X		if (way > 0) {
X			/* Rewind to beginning (yuck) */
X			if (isfile && fseek(stream, 0L, 0) == 0) {
X				initmore();
X				more(stream, curwin, &ll);
X			}
X			while (more(stream, -curwin, &ll) != EOF);
X			topline -= curwin;
X			botline -= curwin;
X			savec = search(stream, SFOR, &serror);
X			if (!serror)
X				return savec;
X			/* Not found, try to restore screen, seek to end */
X			noprint = 1;
X			while (more(stream, curwin, &ll) != EOF);
X			if (i >= maxline-(curwin+curwin/2)) {
X				noprint = 0;
X				*failp = 1;
X				return prthead(stream);
X			}
X			else {
X				while (--i > 0)
X					if (more(stream, -1, &ll) == EOF)
X						break;
X				if (i && SR) {
X					noprint = 0;
X					topline += curwin;
X					botline += curwin;
X					prtback(-curwin, &ll);
X				}
X			}
X		}
X	}
X	noprint = 0;
X	if (way > 0) {
X		putch(07);
X		/* Adjust for pattern entry */
X		if (botline == maxline) {
X			topline++;
X			botline++;
X		}
X	}
X	*failp = 1;
X	return -1;
X}
X
X/*
X * Find a match in the oldlines
X *	Limited RE's: ^ . $
X */
Xmatch(a, b)
Xchar *a;
Xregister char *b;
X{
X	register char *s, *t;
X	int carat;
X
X	/* Carat matches beginning of line */
X	if (*a == '^') {
X		a++;
X		if (*a == 0)
X			return 1;
X		carat = 1;
X	}
X	else
X		carat = 0;
X	for (;;) {
X		s = a;
X		t = b;
X		for (;;) {
X			if (*t == '\n') {
X				/* Dollar matches endline of line */
X				if (*s == '$')
X					return 1;
X				break;
X			}
X			/* Dot matches any character */
X			if (*s == '.') {
X				s++;
X				t++;
X			}
X			else if (*s++ != *t++)
X				break;
X			if (*s == 0)
X				return 1;
X			if (t >= mbufp+mbsize)
X				t = mbufp;
X		}
X		if (carat)
X			break;
X		if (*b == '\n')
X			break;
X		b++;
X		if (b >= mbufp+mbsize)
X			b = mbufp;
X	}
X	return 0;
X}
X
Xcatch () 
X{		/* Catch an interrupt */
X	nointer = 0;
X	signal (SIGINT, catch);
X}
All work and no play makes Jack a dull boy
echo src/process.c
sed 's/^X//' > src/process.c << 'All work and no play makes Jack a dull boy'
X#
X/*  This is the main loop of vsh.  Each interation processes a command.
X    Commands are one character long.  They are acquired in raw mode
X    and processed without the need to press return.  The goal of vsh
X    is to minimize keypresses as much as possible.
X*/
X
X#include "hd.h"
X#include "mydir.h"
X
Xprocess () 
X{
X    register cmd, next;	/* single character command */
X
X    next = REPLOT;
X    for (;;) 
X    {			/* loop forever */
X	if (next & REPLOT)
X		dispdir (1);
X	atxy (1, window-1);
X	printf (" %s", BC);
X	if (next & REPLOT)
X		chkmail();
X	cmd = getch ();
X#ifdef	PWBTTY
X	/* dyt flush input */
X	tty_push(COOKEDMODE);
X	tty_pop();
X#endif
X	printf ("%s", BC);
X
X	next = command (cmd, DIRCMD);
X    }
X}
X
Xchkmail()
X{
X	static time_t mailtime = 0;
X	long clock;
X
X	if (*MAIL == 0)
X		return;
X	time(&clock);
X
X	if (clock - mailtime < MAILCLK)
X		return;
X
X	if (stat(MAIL, &scr_stb) == 0
X#ifdef	V6
X		&& scr_stb.st_size1
X#else
X		&& scr_stb.st_size
X#endif
X		&& scr_stb.st_atime <= scr_stb.st_mtime
X		&& (scr_stb.st_atime > mailtime || scr_stb.st_mtime > mailtime)
X		&& mailtime)
X		putmsg("You have new mail");
X
X	mailtime = clock;
X}
All work and no play makes Jack a dull boy
exit