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 = ≪ 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