allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc) (12/15/89)
Posting-number: Volume 9, Issue 66 Submitted-by: peter@ficc.UUCP (Peter Da Silva) Archive-name: browse_pds : #! /bin/sh # This is a shell archive, created at Ferranti International Controls Corp. # by peter (peter da silva,2810T 5180 @xds13) on Fri Nov 10 16:15:15 1989 # Remove anything before the "#! /bin/sh" line, then unpack it by saving # it into a file and typing "sh file". If you do not have sh, you need # unshar, a dearchiving program which is widely available. In the absolute # wost case, you can crack the files out by hand. # If the archive is complete, you will see the message "End of archive." # at the end. # This archive contains the following files... # 'Makefile' # 'browse.doc' # 'browse.c' # To extract them, run the following through /bin/sh echo x - Makefile sed 's/^X//' > Makefile << '//END_OF_FILE' XCFILES=browse.c XOFILES=$(CFILES:.c=.o) XTFILES=Makefile browse.doc $(CFILES) XCFLAGS=-O XLFLAGS=-O XLIBS=-ltermlib X Xbrowse: $(OFILES) X $(CC) $(LFLAGS) $(OFILES) -o browse $(LIBS) X Xbrowse.shar: $(TFILES) X shar $(TFILES) > browse.shar //END_OF_FILE echo x - browse.doc sed 's/^X//' > browse.doc << '//END_OF_FILE' XBrowse is a screen-oriented directory browser, based on the metaphor of using X'VI' to edit an 'ls -l' listing. X XCommands: X SPACE Enter a subdirectory, or display a file. X & Run a program that won't clobber the screen. X ! Run a program. X = Enter a directory name. X ? Peek at the current file. X [ Define a macro. X H Go to the top of the page. X J Go to the bottom of the directory. X K Go to the top of the directory. X L Go to the bottom of the page. X ^L Repaint screen. X M Display macros. X N,^F Go down 20 lines. X P,^B Go up 20 lines. X R Rename a file. X S Save definitions. X dd Delete file(s). (ask for verification) X DD Delete file(s). X h Place cursor at beginning of line. X l Move cursor to end of line. X < Display file names only. X > Display long directory listing. X + Make a file permanent. X ( Make the current file permanent. X ) Make the current file non-permanent. X j,^J Go down a line. X k,^K Go up a line. X n,^D Go down 11 lines. X p,^U Go up 11 lines. X X qq,QQ,ZZ Exit. X X r Re-read directories. X X t Toggle tag on current file. X T Tag all files. X U Untag all files. X XMacros: X [xtext] Define x as text. X XWithin text: X % Current filename. X # Current directory. X ! Previous command. X $x Macro for key x X $$ Current process ID X ~ Home directory. X ^K One character from previous command. X @x Execute macro for key x X \x Enter special character x X \nnn nnn, octal X \^x Control-x X XDefault macros: X SPACE !more %<CR> X % !%<CR> X . =. X / =/ X ~ =~ X v !vi %<CR> X $ !vi /tmp/br.env.$$<CR> X -- this file is loaded as your environment X -- whenever you run a program. X XThe contents of the environment variable BROWSE are executed as a macro Xwhen you start up. //END_OF_FILE echo x - browse.c sed 's/^X//' > browse.c << '//END_OF_FILE' X/* -- Just what the hell am I ??? --- */ X X#include <stdio.h> X#ifdef M_XENIX X#define USG X#define rindex strrchr X#define GETCWD X#else X#ifdef L_ctermid X#define USG X#define rindex strrchr X#define minor(i) ((i)&0xFF) X#define major(i) minor((i)>>8) X#else X#include <whoami.h> X#endif X#endif X X/* -- Miscellaneous include files -- */ X X#include <sys/param.h> /* NCARGS, and others */ X#ifndef M_XENIX X#include <sys/types.h> /* data types for various files */ X#endif X#include <sys/stat.h> /* stat data structure for getdir(), statout() */ X#include <sys/dir.h> /* dir data structure for getdir() */ X#include <pwd.h> /* passwd data structure for u_name() */ X#include <grp.h> /* group data structure for g_name() */ X#ifdef BSD X#include <sys/time.h> /* time data structure for printime() */ X#else X#include <time.h> /* time data structure for printime() */ X#endif X#ifdef USG X#ifdef M_XENIX X#include <sys/ioctl.h> X#endif X#include <termio.h> X#else X#include <sgtty.h> /* terminal modes for tinit(), tend() */ X#endif X#include <signal.h> X X/* -- make information -- XBUILD Xbrowse: browse.c X cc browse.c -O -o browse -ltermlib XEND X*/ X X/* -- Miscellaneous defines -- */ X#define FALSE 0 X#define TRUE 1 X X#define MAXENTS 320 X#define MAXID 16 X#define MAXLINE 81 X#define NCOL 64 X#define MAXNAME 14 X#define FILENAME 256 X#define MAXARGC (NCARGS/16) /* Estimate max ARGC */ X#define CHARSET 256 /* Number of macros == size of byte */ X#define NOMAC (0) /* Null macro (last) */ X#define TERMBUF 1024 /* Size of term buf for termcap */ X#define SMALLBUF 256 X X/* -- Extended directory entry format -- */ Xstruct entry { X char *e_name; X int e_flags; X#define FTAGGED (1<<0) X#define FPERMANENT (1<<1) X struct stat e_stat; /* file status field */ X char e_uname[9]; /* user name */ X char e_gname[9]; /* user's group name */ X} X*xentries[MAXENTS], **entries=xentries; Xint nentries; X X/* -- Look-up cache for user names -- */ Xstruct idtab { X int id_id; /* user (or group) id */ X char id_name[9]; /* name[8] + filler */ X} Xu_list[MAXID], /* Matched user id's. */ Xg_list[MAXID]; /* ditto group */ Xint u_ptr=0, g_ptr=0; /* current entries */ X X/* -- Global variables -- */ XFILE *efp; /* Environment file */ Xchar efname[MAXLINE]; /* " name */ Xchar *tent; /* Pointer to tbuf */ Xchar PC; /* Pad character */ Xchar *UP, *BC; /* Upline, backsapce character */ Xshort ospeed; /* Terminal output speed */ Xchar termbuf[TERMBUF]; /* Place to put term info */ X Xchar *macbuf[CHARSET], ungetbuf[SMALLBUF]; /* Buffers for pushback and macros */ Xchar c_macro=NOMAC; /* current macro */ Xchar *macptr = ""; /* Pointer to currently executing macro */ Xchar *ungetptr = ungetbuf; /* Pointer to pushed-back characters */ X Xchar *errname; /* Name of file error found in */ Xextern int errno; /* system error number */ Xint xerrno; /* extended error number */ Xint ccol=NCOL; /* Width of used display, current column */ Xint quickmode; /* short display mode (files only) */ X X#ifdef USG Xstruct termio rawbuf; Xstruct termio cookedbuf; X#else Xstruct sgttyb sgbuf; /* buffer for terminal mode info */ Xint rawflags, cookflags; /* flags for raw & cooked tty mode */ X#endif X Xchar *cm, /* Cursor motion */ X *cs, /* Change scrolling region */ X *sf, /* - scroll forward */ X *sr, /* - scroll backwards */ X *ce, /* Clear to end of line */ X *cl, /* Clear screen */ X *al, /* Insert line */ X *dl, /* delete ditto */ X *so, /* standout */ X *se, /* standout end */ X *us, /* underline */ X *ue, /* underline end */ X *ti, /* Init terminal */ X *te; /* Reset terminal */ Xint li, /* lines on screen */ X co; /* columns ditto */ Xchar xn; /* Magic cookie kludge */ X X/* -- Global error messages -- */ Xchar *emesg[4]={ X "??", X#define TOO_MANY 1 X "Too many directory entries", X#define NOMATCH 2 X "No match", X 0 X}; X Xint top, curr; /* Positions of screen in directory */ X#define bottom ((top+nlines>nentries)?nentries:(top+nlines)) Xchar *dot; /* name of current directory */ Xint nlines; /* number of lines displayed on page */ Xchar display_up; /* Does the display exist? */ Xint todump=1; /* Do we want to dump data? */ Xint ended; /* Have we quite finished? */ Xint intrup; /* Have we been interrupted? */ Xchar *HOME; /* Where did I start from? */ Xchar *SHELL; /* How do I run programs? */ X X/* -- types of functions !!! */ Xchar *getenv(), *tgetstr(); Xchar *malloc(); X X#define NEW(t) (t *)malloc(sizeof (t)) X#define NIL(t) ((t) 0) X X/* -- Code starts here: dummy main -- */ Xmain(ac, av, ep) Xint ac; Xchar **av; Xchar **ep; X{ X if(ac>1) chdir(av[1]); X X sprintf(efname, "/tmp/br.env.%d", getpid()); X HOME=getenv("HOME"); X SHELL=getenv("SHELL"); X dumpenv(ep); X X intrup=0; X tinit(getenv("TERM")); X clear_all(); X browse(); X tend(); X unlink(efname); X} X Xclear_all() X{ X int i; X X for(i = 0; i < MAXENTS; i++) X entries[i] = 0; X} X Xchar *clone(name) Xchar *name; X{ X char *hold; X X hold = (char *)malloc(strlen(name)+1); X X if(hold==0) X return 0; X strcpy(hold, name); X return hold; X} X Xnewname(e, name) Xstruct entry *e; Xchar *name; X{ X if(e->e_name) X free(e->e_name); X e->e_name = clone(name); X} X X#if BSD Xreadent(dp, db) XDIR *dp; Xstruct direct *db; X{ X struct direct *ptr; X X ptr = readdir(dp); X if(!ptr) return 0; X X *db = *ptr; /* V7 'C' and above... safe, since UCB=V7+ */ X free(ptr); X return 1; X} X#else X#define opendir(n) fopen(n, "r") X#define DIR FILE Xreadent(dp, db) XDIR *dp; Xstruct direct *db; X{ X if(fread(db, sizeof(struct direct), 1, dp)) X return 1; X /* else */ X return 0; X} X#define closedir fclose X#endif X Xgetdir() X{ X char *u_name(), *g_name(); X DIR *dp; X int valid; X struct direct *hold = NEW(struct direct); X int i, p; X X if(!(dp = opendir("."))) { X errname="."; X return FALSE; X } X X p = 0; X for(i = 0; i < nentries; i++) { X if(entries[i]->e_flags & FPERMANENT) { X if(p != i) { X struct entry *hold; X hold = entries[p]; X entries[p] = entries[i]; X entries[i] = hold; X } X p++; X } X } X X for(nentries = p; !intrup && nentries < MAXENTS; nentries += valid) { X X if(!entries[nentries]) { X entries[nentries] = NEW(struct entry); X if(!entries[nentries]) X break; X entries[nentries]->e_name = NIL(char *); X } X X if(!readent(dp, hold)) X break; X X valid = (hold->d_ino != 0); X if(valid) { X if(stat(hold->d_name, &entries[nentries]->e_stat)==-1) { X closedir(dp); X errname=hold->d_name; X free(hold); X return FALSE; X } X X newname(entries[nentries], hold->d_name); X X#ifndef BSD /* truncate name to 14 characters in non-BSD systems */ X if(strlen(entries[nentries]->e_name)>14) X entries[nentries]->e_name[14] = 0; X#endif X X entries[nentries]->e_flags = 0; X X strcpy(entries[nentries]->e_uname, X u_name(entries[nentries]->e_stat.st_uid)); X X strcpy(entries[nentries]->e_gname, X g_name(entries[nentries]->e_stat.st_gid)); X } X } X X closedir(dp); X X free(hold); X X if(intrup) X return FALSE; X X if(nentries>=MAXENTS || entries[nentries]==NIL(struct entry *)) { X errno=0; X xerrno=TOO_MANY; X errname="."; X return FALSE; X } X X sortdir(); X X if(intrup) X return FALSE; X return TRUE; X} X Xat_current() X{ X at_file(curr); X} X Xat_file(file) Xint file; X{ X if(display_up) { X if(file < top || file >= top+nlines) X return 0; X at(0, curr-top+2); X } else { X if(file != curr) X return 0; X cmdline(); X } X return 1; X} X Xdisplay_flags(flags) X{ X outc((flags&FTAGGED)?'+':((flags&FPERMANENT)?'!':' ')); X} X Xrepaint_flags() X{ X if(!display_up) return; X at(NCOL-1, curr-top+2); X display_flags(entries[curr]->e_flags); X} X Xrepaint_current() X{ X if(!display_up) return; X at_current(); X dump(curr, curr+1); X} X Xtag() X{ X entries[curr]->e_flags ^= FTAGGED; X repaint_flags(); X} X Xtag_all(flag) Xint flag; X{ X int i; X X for(i = 0; i < nentries; i++) X if(flag) X entries[i]->e_flags |= FTAGGED; X else X entries[i]->e_flags &= ~FTAGGED; X if(display_up) X todump = TRUE; X} X Xdelete_from_display(file) Xint file; X{ X if(!display_up) return; X X if(file>=top+nlines) return; X X if(file < top) file = top; X X scroll(2+file-top, 2+nlines, 1); X at(0, 2+nlines-1); X if(top+nlines >= nentries) X outc('~'); X else X dump(bottom, bottom+1); X} X Xdelete_file_entry(file) Xint file; X{ X struct entry *hold; X int i; X X delete_from_display(file); X X hold = entries[file]; X for(i=file; i<nentries-1; i++) X entries[i]=entries[i+1]; X entries[nentries-1]=hold; X nentries--; X X if(file < curr || curr >= nentries) { X curr--; X if(top >= nentries) { X top--; X display_up = 0; X todump = 1; X } X } X} X Xremove_one(file, doit) Xint file; Xint doit; X{ X if(!doit) { X cmdline(); X outs("Delete "); X ctlouts(entries[file]->e_name); X outs(" [n]?"); X doit = getchar() == 'y'; X outs(doit?"Yes.":"No."); X } X if(doit) { X if(unlink(entries[file]->e_name) == 0) { X cmdline(); X outs("Deleted "); X ctlouts(entries[file]->e_name); X outs("."); X delete_file_entry(file); X return 1; X } else { X wperror(entries[file]->e_name); X return 0; X } X } X return 0; X} X Xremove(doit) Xint doit; X{ X int i; X int found_tags; X X found_tags = 0; X i = 0; X while(i < nentries) { X if(entries[i]->e_flags & FTAGGED) { X found_tags = 1; X if(!remove_one(i, doit)) /* decrements nentries */ X break; X } else X i++; X } X if(!found_tags) X remove_one(curr, doit); X} X Xinsert_entry_at(ent, i) Xstruct entry *ent; Xint i; X{ X struct entry *hold; X int j; X X /* Allocate slot at end */ X if(!entries[nentries]) { X entries[nentries] = NEW(struct entry); X if(!entries[nentries]) X return 0; X entries[nentries]->e_name = NIL(char *); X } else if(entries[nentries]->e_name) { X free(entries[nentries]->e_name); X entries[nentries]->e_name = NIL(char *); X } X X /* Copy data into slot */ X *entries[nentries] = *ent; X entries[nentries]->e_name = clone(ent->e_name); X if(!entries[nentries]->e_name) X return 0; X X if(i != nentries) { X /* Rotate slot to middle */ X hold = entries[nentries]; X for(j = nentries; j > i; j--) X entries[j] = entries[j-1]; X entries[i] = hold; X } X nentries++; X X if(display_up) { X if(i < top) X i = top; X if(i >= bottom) X return; X scroll(2+i-top, 2+nlines, -1); X at(0, 2+i-top); X dump(i, i+1); X } X} X Xinsert_entry(ent) Xstruct entry *ent; X{ X int i; X X if(nentries >= MAXENTS) X return 0; X X for(i = 0; i < nentries; i++) X if(entcmp(&ent, &entries[i]) < 0) X break; X X return insert_entry_at(ent, i); X} X Xmove() X{ X char scratch[FILENAME]; X struct entry hold; X char inps(); X X hold = *entries[curr]; X X cmdline(); X outs("Rename "); X ctlouts(entries[curr]->e_name); X outc(' '); X if(inps(scratch, entries[curr]->e_name, 0)=='\033') { X killcmd(); X return; X } X if(link(entries[curr]->e_name, scratch)!=0) { X char tmbuf[42]; X sprintf(tmbuf, "(rename %s %s)", entries[curr]->e_name, scratch); X wperror(tmbuf); X return; X } X if(unlink(entries[curr]->e_name)!=0) { X wperror(entries[curr]->e_name); X return; X } X X hold.e_name = scratch; X hold.e_flags &= ~FTAGGED; X X delete_file_entry(curr); X insert_entry(&hold); X} X Xpname(name, mode) Xchar *name; Xint mode; X{ X int i; X char *slash, *rindex(); X int max=quickmode?MAXLINE:(MAXNAME+1); X X if((mode&S_IFMT)==S_IFDIR) X max--; X else if((mode&S_IFMT)==S_IFREG && (mode&0111)) X max--; X else if((mode&S_IFMT)!=S_IFREG) X max--; X X if(!quickmode && (slash=rindex(name, '/'))) { X name=slash; X outc('<'); X max--; X } X for(i=0; i<max && *name; name++) X i += ctlout(*name); X standend(); X if(i==max && *name) X outs("\b>"); X if((mode&S_IFMT)==S_IFDIR) { X outc('/'); X } X else if((mode&S_IFMT)==S_IFREG && (mode&0111)) { X outc('*'); X } X else if((mode&S_IFMT)!=S_IFREG) { X outc('?'); X } X} X Xsortdir() X{ X int entcmp(); X qsort(entries, nentries, sizeof(struct entry *), entcmp); X} X Xaddname(flag) Xchar flag; X{ X char buf[FILENAME], *ptr, *tmp; X char scratch[FILENAME]; X struct entry hold; X char inps(); X X cmdline(); X outs("Add "); X if(inps(scratch, entries[curr]->e_name, 0)=='\033') { X killcmd(); X return; X } X X if(stat(scratch, &hold.e_stat)==-1) { X wperror(scratch); X return; X } X if(flag!='+' && *scratch != '/') { X strcpy(buf, dot); X ptr = scratch; X for(;;) { /* eat '../' and './' */ X if(ptr[0]=='.' && ptr[1]=='.' && X (ptr[2]=='/' || !ptr[2])) { X tmp = rindex(buf, '/'); X if(!tmp) break; X *tmp=0; X ptr += 2+(ptr[2]=='/'); X continue; X } X if(ptr[0]=='.' && X (ptr[1]=='/' || !ptr[1])) { X ptr += 1+(ptr[1]=='/'); X continue; X } X break; X } X if(*ptr) { X strcat(buf, "/"); X strcat(buf, ptr); X } X hold.e_name = buf; X } else X hold.e_name = scratch; X strcpy(hold.e_uname, X u_name(hold.e_stat.st_uid)); X strcpy(hold.e_gname, X g_name(hold.e_stat.st_gid)); X hold.e_flags = 0; X if(flag!='+') X hold.e_flags |= FPERMANENT; X insert_entry(&hold); X} X Xaddperm() X{ X char buf[FILENAME], *ptr, *tmp; X struct entry hold; X int entcmp(), i; X X if(entries[curr]->e_flags & FPERMANENT) X return; X X if(entries[curr]->e_name[0]!='/') { X strcpy(buf, dot); X ptr = entries[curr]->e_name; X for(;;) { /* eat '../' and './' */ X if(ptr[0]=='.' && ptr[1]=='.' && X (ptr[2]=='/' || !ptr[2])) { X tmp = rindex(buf, '/'); X if(!tmp) break; X *tmp=0; X ptr += 2+(ptr[2]=='/'); X continue; X } X if(ptr[0]=='.' && X (ptr[1]=='/' || !ptr[1])) { X ptr += 1+(ptr[1]=='/'); X continue; X } X break; X } X if(*ptr) { X strcat(buf, "/"); X strcat(buf, ptr); X } X hold = *entries[curr]; X X hold.e_name = buf; X hold.e_flags &= ~FTAGGED; X hold.e_flags |= FPERMANENT; X insert_entry(&hold); X } else { X entries[curr]->e_flags |= FPERMANENT; X entries[curr]->e_flags &= ~FTAGGED; X repaint_flags(); X } X} X Xdelperm() X{ X entries[curr]->e_flags &= ~(FPERMANENT|FTAGGED); X repaint_flags(); X} X Xentcmp(e1, e2) Xstruct entry **e1, **e2; X{ X if((*e1)->e_name[0] == '/') { X if((*e2)->e_name[0] != '/') X return 1; X } else if((*e2)->e_name[0] == '/') X return -1; X X return strcmp((*e1)->e_name, (*e2)->e_name); X} X Xdump(start, end) Xint start; Xint end; X{ X int i; X int lo = (start<nentries)?start:nentries; X int hi = (end<nentries)?end:nentries; X X for(i=lo; i<hi; i++) { X statout(entries[i]->e_name, X &entries[i]->e_stat, X entries[i]->e_uname, X entries[i]->e_gname, X entries[i]->e_flags); X nl(); X } X return TRUE; X} X Xstatout(name, sbuf, user, group, flags) Xchar *name; Xstruct stat *sbuf; Xchar *user, *group; Xint flags; X{ X int mode = sbuf->st_mode; X X if(!quickmode) { X printf("%5u ", sbuf->st_ino); X X if((mode&S_IFMT)==S_IFCHR) outc('c'); X else if((mode&S_IFMT)==S_IFBLK) outc('b'); X else if((mode&S_IFMT)==S_IFDIR) outc('d'); X else if((mode&S_IFMT)==S_IFREG) outc('-'); X else outc('?'); X triad((mode>>6)&7, mode&S_ISUID, 's'); X triad((mode>>3)&7, mode&S_ISGID, 's'); X triad(mode&7, mode&S_ISVTX, 't'); X outc(' '); X X printf("%3u ", sbuf->st_nlink); X printf("%-8s ", user); X printf("%-8s ", group); X X if((mode&S_IFMT)==S_IFREG || (mode&S_IFMT)==S_IFDIR) X printf("%7ld ", sbuf->st_size); X else X printf("%3d,%3d ", X major(sbuf->st_rdev), X minor(sbuf->st_rdev)); X X outc(' '); X printime(&sbuf->st_mtime); X } X X display_flags(flags); X pname(name, sbuf->st_mode); X} X Xchar * Xu_name(uid) Xint uid; X{ X int i; X struct passwd *pwptr, *getpwuid(); X X for(i=0; i<u_ptr; i++) X if(u_list[i].id_id==uid) X return u_list[i].id_name; X X if(u_ptr>=MAXID) /* cache full */ X u_ptr = 0; /* simplistic algorithm, wrap to beginning */ X /* with MAXID >> # common id's it's good enough */ X X u_list[u_ptr].id_id=uid; X X if(pwptr=getpwuid(uid)) { /* Copy name */ X for(i=0; pwptr->pw_name[i]>' '; i++) X u_list[u_ptr].id_name[i]=pwptr->pw_name[i]; X u_list[u_ptr].id_name[i]=0; X } X else /* Default to UID */ X sprintf(u_list[u_ptr].id_name, "%d", uid); X X return u_list[u_ptr++].id_name; X} X Xchar * Xg_name(gid) Xint gid; X{ X int i; X struct group *grptr, *getgrgid(); X X for(i=0; i<g_ptr; i++) X if(g_list[i].id_id==gid) X return g_list[i].id_name; X X if(g_ptr>=MAXID) /* cache full */ X g_ptr = 0; /* simplistic algorithm, wrap to beginning */ X /* with MAXID >> # common id's it's good enough */ X X g_list[g_ptr].id_id=gid; X X if(grptr=getgrgid(gid)) { /* Copy name */ X for(i=0; grptr->gr_name[i]>' '; i++) X g_list[g_ptr].id_name[i]=grptr->gr_name[i]; X g_list[g_ptr].id_name[i]=0; X } X else /* Default to UID */ X sprintf(g_list[g_ptr].id_name, "%d", gid); X X return g_list[g_ptr++].id_name; X} X Xprintime(clock) Xlong *clock; X{ X struct tm *tmbuf, *localtime(); X static char *months[12]= { X "Jan","Feb","Mar","Apr","May","Jun", X "Jul","Aug","Sep","Oct","Nov","Dec" X }; X X tmbuf=localtime(clock); X printf("%2d %3s %02d %2d:%02d", X tmbuf->tm_mday, X months[tmbuf->tm_mon], X tmbuf->tm_year, X tmbuf->tm_hour, X tmbuf->tm_min); X} X Xheader() X{ X int i; X if(quickmode) X printf(" File Name"); X else X printf( X"Inode Long mode LNX User Group Size/Dev Modify Time File name" X ); X nl(); X} X Xtriad(bits, special, code) Xint bits, special; Xchar code; X{ X if(bits&4) outc('r'); X else outc('-'); X X if(bits&2) outc('w'); X else outc('-'); X X if(special) outc(code); X else if(bits&1) outc('x'); X else outc('-'); X} X Xouts(s) Xchar *s; X{ X int outc(); X X if(s) X tputs(s, 0, outc); X} X Xoutc(c) Xchar c; X{ X putchar(c); X} X X/* Screen manipulation primitives: dumb set for smart terminals */ Xat(x, y) Xint x, y; X{ X outs(tgoto(cm, x, y)); X} X Xnl() X{ X outs(ce); X outc('\n'); X} X X X/* Scroll lines in window (from:to) n lines */ Xscroll(from, to, n) Xint from, to, n; X{ X if(cs && sf && sr) { X outs(tgoto(cs, from, to-1)); X if(n<0) X while(n++) X outs(sr); X else X while(n--) X outs(sf); X outs(tgoto(cs, 0, li-1)); X } X else if(al && dl) { X if(n<0) { X int i=n; X outs(tgoto(cm, 0, to+n)); X while(i++) X outs(dl); X outs(tgoto(cm, 0, from)); X while(n++) X outs(al); X } X else { X int i=n; X outs(tgoto(cm, 0, from)); X while(i--) X outs(dl); X outs(tgoto(cm, 0, to-n)); X while(n--) X outs(al); X } X } X} X Xwperror(file) Xchar *file; X{ X extern int errno; X extern char *sys_errlist[]; X X cmdline(); X ctlouts(file); X ctlouts(": "); X ctlouts(errno?sys_errlist[errno]:emesg[xerrno]); X if(!display_up) X nl(); X} X Xfperror(prog, file) Xchar *prog, *file; X{ X extern int errno; X X fprintf(stderr, "%s -- ", prog); X if(errno) X perror(file); X else X fprintf(stderr, "%s: %s\n", file, emesg[xerrno]); X} X Xtinit(name) Xchar *name; X{ X char *termptr; X char tbuf[TERMBUF], *tmp; X int intr(); X#ifdef BSD X int stop(); X#endif X X termptr = termbuf; X X tgetent(tbuf, name); X X tmp = tgetstr("pc", &termptr); X if(tmp) PC = *tmp; X UP = tgetstr("up", &termptr); X BC = tgetstr("bc", &termptr); X cm = tgetstr("cm", &termptr); X cs = tgetstr("cs", &termptr); X sf = tgetstr("sf", &termptr); X sr = tgetstr("sr", &termptr); X ce = tgetstr("ce", &termptr); X cl = tgetstr("cl", &termptr); X al = tgetstr("al", &termptr); X dl = tgetstr("dl", &termptr); X us = tgetstr("us", &termptr); X ue = tgetstr("ue", &termptr); X so = tgetstr("so", &termptr); X se = tgetstr("se", &termptr); X ti = tgetstr("ti", &termptr); X te = tgetstr("te", &termptr); X li = tgetnum("li"); X co = tgetnum("co"); X xn = tgetflag("xn"); X X nlines=li-3; X X#ifdef USG X ioctl(1, TCGETA, &rawbuf); X cookedbuf = rawbuf; X rawbuf.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL); X rawbuf.c_cc[VMIN] = 1; X rawbuf.c_cc[VTIME] = 0; X#else X gtty(1, &sgbuf); X ospeed=sgbuf.sg_ospeed; X quickmode=ospeed<10; X cookflags=sgbuf.sg_flags; X sgbuf.sg_flags = (sgbuf.sg_flags&~ECHO)|CBREAK; X rawflags=sgbuf.sg_flags; X#endif X signal(SIGINT, intr); X#ifdef BSD X signal(SIGTSTP, stop); X#endif X rawtty(); X} X Xint tmode=0; X Xentty() X{ X if(!tmode) X outs(ti); X tmode=1; X} X Xextty() X{ X if(tmode) X outs(te); X tmode=0; X} X Xrawtty() X{ X#ifdef USG X ioctl(1, TCSETA, &rawbuf); X#else X sgbuf.sg_flags=rawflags; X stty(1, &sgbuf); X#endif X entty(); X} X Xcooktty() X{ X#ifdef USG X ioctl(1, TCSETA, &cookedbuf); X#else X sgbuf.sg_flags=cookflags; X stty(1, &sgbuf); X#endif X extty(); X} X Xintr() X{ X int intr(); X signal(SIGINT, intr); X intrup=1; X bell(); X} X X#ifdef BSD Xstop() X{ X int stop(); X signal(SIGTSTP, stop); X intrup=1; X tend(); X kill(getpid(), SIGSTOP); X rawtty(); X display_up=0; X} X#endif X Xtend() X{ X cmdline(); X cooktty(); X fflush(stdout); X} X Xchar * Xpwd() X#ifdef GETCWD X{ X static char mydir[FILENAME+1] = ""; X char *getcwd(); X X return getcwd(mydir, FILENAME); X} X#else X{ X static char mydir[FILENAME] = ""; X int status; X int pip[2]; X int n; X int (*sigs[2])(); X X sigs[0] = signal(2, 1); X sigs[1] = signal(3, 1); X X if(pipe(pip)<0) X return 0; X X if(!fork()) { X close(1); X dup(pip[1]); X close(pip[0]); X execl("/bin/pwd", "pwd", 0); X exit(-1); X } X close(pip[1]); X wait(&status); X signal(2, sigs[0]); X signal(3, sigs[1]); X X if(status) { X close(pip[0]); X return "/"; X } X n = read(pip[0], mydir, FILENAME); X close(pip[0]); X if(n<=0) X return "/"; X mydir[n-1] = 0; X return mydir; X} X#endif X Xbrowse() X{ X int c; X char *env; X X if(env=getenv("BROWSE")) X macptr = env; X X defmacs(); X newdir(); X redraw(); X ended=0; X X do { X if(intrup) { X intrup=0; /* clear interrupt */ X cmdline(); /* go to the command line */ X outs("Interrupt"); /* let us know about it */ X endmac(); /* clear macros */ X clearin(); /* and input buffer */ X } X here_i_am(); X fflush(stdout); X c = getch(); X cmd(c); X } X while(!ended); X} X Xclearin() X{ X fseek(stdin, 0L, 1); /* stay here messily */ X} X Xcmd(c) Xchar c; X{ X int i; X X if(intrup) X return; X X switch(c) { X case ' ': X if(isdir(entries[curr])) { X if(!cd(entries[curr]->e_name)) X wperror(entries[curr]->e_name); X } else X if(!macro(c)) X bell(); X else X cmd(getch()); X break; X case '&': X syscom(0); X break; X case '!': X syscom(1); X break; X case '=': X todir(); X break; X case '?': X sample(entries[curr]->e_name); X break; X case '[': X define(); X break; X case 'B'-'@': X prev(nlines); X break; X case 'D'-'@': X next(nlines/2); X break; X case 'F'-'@': X next(nlines); X break; X case 'H': X curr=top; X break; X case 'J': X tail(); X break; X case 'K': X home(); X break; X case 'L'-'@': X redraw(); X break; X case 'L': X curr=bottom-1; X break; X case 'M': X definitions(); X break; X case 'N': X next(nlines); X break; X case 'P': X prev(nlines); X break; X case 'R': X move(); X break; X case 'S': X savedefs(); X break; X case 'U'-'@': X prev(nlines/2); X break; X case 'D': X case 'd': X if(getch()==c && !intrup) X remove(c=='D'); X else X bell(); X break; X case 'h': X ccol=0; X break; X case 'l': X ccol=NCOL; X break; X case '<': X quickmode=1; X if(display_up) { X todump=1; X at(0, 1); X header(); X } X break; X case '+': case '^': X addname(c); X break; X case '(': X addperm(); X break; X case ')': X delperm(); X break; X case '>': X quickmode=0; X if(display_up) { X todump=1; X at(0, 1); X header(); X } X break; X case 'J'-'@': X case 'j': X newline(); X break; X case 'K'-'@': X case 'k': X upline(); X break; X case 'n': X next(nlines/2); X break; X case 'p': X prev(nlines/2); X break; X case 'q': case 'Z': case 'Q': X if(getchar()==c && !intrup) X ended=1; X else X bell(); X break; X case 'r': X reload(); X break; X case 't': X tag(); X break; X case 'T': case 'U': X tag_all(c=='T'); X break; X default: X if(!macro(c)) X bell(); X else X cmd(getch()); /* make sure it does something */ X } /* and thus avoid unneeded redraws */ X} X Xcmdline() X{ X at(0, li-1); X outs(ce); X} X Xnewdir() X{ X if(display_up) X at(0,0); X fflush(stdout); X dot=pwd(); X X if(!getdir()) X wperror(dot); X X curr=0; X top=0; X topline(); X if(display_up) X todump=TRUE; X} X Xreload() X{ X getdir(); X curr=(curr>=bottom)?bottom-1:curr; X if(display_up) { X topline(); X todump=TRUE; X } X} X Xtopline() X{ X if(display_up) X at(0,0); X printf("%s: %d files", dot, nentries); X nl(); X} X Xredraw() X{ X outs(cl); X display_up=0; X topline(); X at(0,1); X header(); X todump=1; X display_up=1; X} X Xdumpdata() X{ X at(0,2); X dump(top,bottom); X if(top+nlines>nentries) { X int i; X at(0, bottom-top+2); X for(i=bottom-top; i<nlines; i++) { X outc('~'); X nl(); X } X } X} X Xupline() X{ X if(curr>0) X curr--; X else bell(); X} X Xhome() X{ X curr=0; X} X Xnext(l) X{ X curr += l; X if(curr>=nentries) curr=nentries-1; X} X Xprev(l) X{ X curr -= l; X if(curr<0) curr=0; X} X Xtail() X{ X curr=nentries-1; X} X Xnewline() X{ X if(curr<nentries-1) X curr++; X else X bell(); X} X Xhere_i_am() X{ X int c; X if(*macptr) X return; X if(todump) X display_up = 1; X if(!display_up) { X cmdline(); X statout(entries[curr]->e_name, X &entries[curr]->e_stat, X entries[curr]->e_uname, X entries[curr]->e_gname, X entries[curr]->e_flags); X at(quickmode?1:ccol, li-1); X fflush(stdout); X if((c=getch())=='\n') { X redraw(); X here_i_am(); X } X else X ungetch(c); X } else if(todump) { X if(!(curr-top > 0 && curr-top < nlines)) { X top = curr-nlines/2; X if(top<0) top=0; X } X dumpdata(); X at(quickmode?1:ccol, curr-top+2); X todump=0; X } else { X int lines_to_scroll = curr-top; X if(lines_to_scroll > 0) X if((lines_to_scroll -= nlines-1) < 0) X lines_to_scroll = 0; X if(lines_to_scroll < 1-nlines) { X top=curr; X at(0,2); X dump(top, bottom); X } else if(lines_to_scroll > nlines-1) { X top=curr-nlines+1; X at(0,2); X dump(top, bottom); X } else if(lines_to_scroll) { X scroll(2, nlines+2, lines_to_scroll); X top += lines_to_scroll; X if(lines_to_scroll < 0) { X at(0, 2); X dump(top, top-lines_to_scroll); X } else { X at(0, 2+nlines-lines_to_scroll); X dump(bottom-lines_to_scroll, bottom); X } X } X at(quickmode?1:ccol, curr-top+2); X } X} X Xbell() X{ X outc(7); X} X Xperform(command) Xchar *command; X{ X char cmdbuf[MAXLINE]; X X cmdline(); X extty(); X outc('!'); X printf(command, entries[curr]->e_name); X sprintf(cmdbuf, command, entries[curr]->e_name); X X system(cmdbuf, 1); X entty(); X} X Xsystem(command, rdflag) Xchar *command; Xint rdflag; /* Should I redraw? */ X{ X char scratch[32]; X int status; X int (*sigint)(), (*sigquit)(); X char c; X X sigint=signal(2, 1); X sigquit=signal(3, 1); X cooktty(); X fflush(stdout); X strcpy(scratch, entries[curr]->e_name); X if(!fork()) { X int i; X static char *envp[MAXARGC]; X static char env[NCARGS]; X char *hold; X char line[MAXLINE]; X char *tmp; X X signal(2,0); X signal(3,0); X X if(!(efp=fopen(efname, "r"))) { X fputc('\n', stderr); X perror(efname); X execl(SHELL, SHELL, "-c", command, 0); X execl("/bin/sh", "sh", "-c", command, 0); X perror("/bin/sh"); X exit(-77); X } X X i=0; X tmp=hold=env; X X while(!feof(efp)) { X fgets(line, MAXLINE, efp); X if(strlen(line)+(tmp-env)>NCARGS) X break; X if(line[0]=='$') X if(tmp>hold) { X envp[i] = hold; X i++; X if(i > MAXARGC-2) break; X tmp[-1]=0; /* eat line-feed at end */ X hold = tmp; X } X strcpy(tmp, line+(line[0]=='$')); X tmp += strlen(tmp); X } X if(tmp>hold) { X envp[i] = hold; X i++; X tmp[-1]=0; /* and eat this one as well */ X } X sprintf(tmp, "FILE=%s", scratch); X envp[i++]=tmp; X envp[i]=0; X X if(rdflag) X putchar('\n'); X else X putchar('\r'); X fflush(stdout); X execle(SHELL, SHELL, "-c", command, 0, envp); X execle("/bin/sh", "sh", "-c", command, 0, envp); X perror("/bin/sh"); X exit(-77); X } X wait(&status); X signal(2, sigint); X signal(3, sigquit); X rawtty(); X X if(rdflag || status!=0) X display_up=0; X} X Xchar Xinps(buf, text, termin) Xchar *buf; Xchar *text; Xchar termin; X{ X int i = 0; X char c, *ptr, *txp; X X txp=text?text:"!"; X X while(!intrup && X (fflush(stdout), c=getch()) != '\033' && X c != '\n' && X c != termin) X switch(c) { X case '\b': X case '\177': X if(i>0) { X printf("\b \b"); X i--; X } X else X bell(); X break; X case 'U'-'@': X txp=text?text:"!"; X case 'X'-'@': X if(i>0) X while(i>0) { X printf("\b \b"); X i--; X } X else X bell(); X break; X case 'K'-'@': X if(*txp) X ctloutc(buf[i++] = *txp++); X break; X case '%': X for(ptr=entries[curr]->e_name; *ptr; ptr++) X ctlout(buf[i++] = *ptr); X standend(); X break; X case '#': X for(ptr=dot; *ptr; ptr++) X ctlout(buf[i++] = *ptr); X standend(); X break; X case '~': X for(ptr=HOME; *ptr; ptr++) X ctlout(buf[i++] = *ptr); X standend(); X break; X case '@': X outc(c); X fflush(stdout); X c = getch(); X if(!macro(c)) { X ungetch(c); X buf[i++]='@'; X } X else X outs("\b \b"); X break; X case '$': X outc(c); X fflush(stdout); X c = getch(); X if(c=='$') { X char tmp[10]; X sprintf(tmp, "%d", getpid()); X outs("\b \b"); X for(ptr=tmp; *ptr; ptr++) X ctlout(buf[i++] = *ptr); X standend(); X } X else if(!macbuf[c]) { X ungetch(c); X buf[i++]='$'; X } X else { X outs("\b \b"); X for(ptr=macbuf[c]; *ptr; ptr++) X ctlout(buf[i++] = *ptr); X standend(); X } X break; X case '!': X while(*txp) X ctlout(buf[i++] = *txp++); X standend(); X break; X case '\\': X outc(c); X fflush(stdout); X c=getch(); X if(c=='~' || c=='%' || c=='#' || X c=='\\' || c=='!' || X c=='K'-'@' || c=='U'-'@' || c=='X'-'@' || X c=='\b' || c=='\177' || X c==termin || c=='\033' || c=='\n' || X c=='@' || c=='$') { X outc('\b'); X ctloutc(buf[i++]=c); X break; X } X else if(c>='0' && c<='7') { X int n, val=0; X for(n=0; n<3 && c>='0' && c<='7'; n++) { X val = val*8 + c-'0'; X outc('\b'); X ctloutc(val); X c=getch(); X } X ungetch(c); X c=buf[i++]=val; X break; X } X else if(c=='^') { X outc('\b'); X outc('^'); X fflush(stdout); X c=getch(); X if(c>='?'&&c<='_') { X outc('\b'); X ctloutc(buf[i++]=(c-'@')&'\177'); X break; X } X else if(c>='`'&&c<='~') { X outc('\b'); X ctloutc(buf[i++]=c-'`'); X break; X } /* otherwise default */ X else buf[i++]='^'; /* after adding caret */ X X } /* otherwise fall through to default */ X else buf[i++]='\\'; /* after adding backslash */ X default: X ctloutc(buf[i++] = c); X break; X } X X buf[i] = 0; X return intrup?'\033':c; X} X Xctlouts(s) Xchar *s; X{ X int cnt = 0; X X while(*s) X cnt += ctlout(*s++); X cnt += standend(); X X return cnt; X} X Xctloutc(c) Xchar c; X{ X int cnt; X X cnt = ctlout(c); X cnt += standend(); X X return cnt; X} X Xint somode = 0; Xint ulmode = 0; X Xstandout() X{ X if(!somode) { X outs(so); X somode = 1; X if(xn) return 1; X } X return 0; X} X Xunderline() X{ X if(!ulmode) { X outs(us); X ulmode = 1; X if(xn) return 1; X } X return 0; X} X Xstandend() X{ X int cnt = 0; X X if(somode) { X outs(se); X somode = 0; X if(xn) cnt++; X } X if(ulmode) { X outs(ue); X ulmode = 0; X if(xn) cnt++; X } X return cnt; X} X Xctlout(c) Xchar c; X{ X int cnt = 0; X if(c&'\200') { X cnt += underline(); X cnt += ctlout(c&'\177'); X return cnt; X } X else if(c<' ' || c=='\177') { X cnt += standout(); X outc((c+'@')&'\177'); X cnt++; X return cnt; X } X else { X cnt += standend(); X outc(c); X cnt++; X return cnt; X } X} X Xtodir() X{ X char cmdbuf[MAXLINE]; X static char lastdir[MAXLINE] = "."; X char *slash, *rindex(); X X cmdline(); X printf("goto "); X if(inps(cmdbuf, lastdir, 0)!='\033' && cmdbuf[0]) { X strcpy(lastdir, cmdbuf); X if(!cd(cmdbuf, 0)) { X if(!fgoto(cmdbuf)) X if(slash=rindex(cmdbuf, '/')) { X *slash++=0; X if(cd(cmdbuf, 1)) { X if(!fgoto(slash)) { X errno=0; X xerrno=NOMATCH; X wperror(slash); X } X } else X wperror(cmdbuf); X } else X wperror(cmdbuf); X } X } X else X killcmd(); X} X Xfgoto(file) Xchar *file; X{ X int i; X X for(i = 0; i<nentries; i++) X if(match(entries[i]->e_name, file)) { X curr=i; X return 1; X } X X return 0; X} X Xmatch(target, sample) Xchar *target, *sample; X{ X while(*sample && *target==*sample) { X target++; X sample++; X } X return !*sample; X} X Xkillcmd() X{ X if(!intrup) { X cmdline(); X printf("Command killed"); X if(!display_up) X nl(); X } X} X Xcd(dir, flag) Xchar *dir; Xint flag; X{ X if(flag) { X cmdline(); X printf("cd %s\r", dir); X } X else X outc('\r'); X fflush(stdout); X if(access(dir, 5)==0 && chdir(dir)==0) { X newdir(); X return 1; X } X return 0; X} X Xsyscom(rd) Xint rd; /* should I redraw? */ X{ X char buf[MAXLINE]; X static char lastcmd[MAXLINE] = ""; X char inps(); X X cmdline(); X extty(); X putchar(rd?'!':'&'); X if(inps(buf, lastcmd, 0)=='\033') { X entty(); X killcmd(); X return; X } X else X strcpy(lastcmd, buf); X system(buf, rd); X entty(); X} X Xisdir(entry) Xstruct entry *entry; X{ X return((entry->e_stat.st_mode&S_IFMT)==S_IFDIR); X} X Xsample(name) Xchar *name; X{ X int col; X int c, i; X FILE *tfp; X X if(!(tfp = fopen(name, "r"))) { X wperror(name); X return; X } X X i=0; X do { X cmdline(); X col = 0; X for(c=fgetc(tfp); col<72 && !feof(tfp); c=fgetc(tfp)) X col+=ctlout(c); X standend(); X if(display_up) X outc('\r'); X else X outc('\n'); X } X while(!feof(tfp) && (i=getch())=='?'); X X fclose(tfp); X X if(i && i!=' ' && i!='q' && i!='?') X ungetch(i); X} X Xmacro(c) Xchar c; X{ X if(c==NOMAC) X return 0; X else if(!macbuf[c]) X return 0; X else if(c==c_macro) { X cmdline(); X printf("Recursive macro."); X endmac(); X return 0; X } X else { X c_macro = c; X macptr = macbuf[c]; X return 1; X } X} X Xgetch() X{ X char c; X X if(ungetptr>ungetbuf) X return *--ungetptr; X if(*macptr) X if(*macptr=='\007') { X macptr++; X if(*macptr!='\007') X return getchar(); X else { X if((c=getchar()) != '\n') X macptr--; X else X macptr++; X return (c=='\n')?getch():c; X } X } X else if(*macptr=='\\') { X if(macptr[1]=='\007') X macptr++; X return *macptr++; X } X else X return *macptr++; X else { X endmac(); X return getchar(); X } X} X Xendmac() { X c_macro=NOMAC; X macptr=""; X} X Xungetch(c) Xchar c; X{ X if(ungetptr-ungetbuf>SMALLBUF) X return; X *ungetptr++=c; X} X Xdefine() X{ X int c; X char buf[SMALLBUF]; X X cmdline(); X X printf("define "); X c = getch(); X X if(intrup) X return; X X ctloutc(c); X printf(" as ["); X if(inps(buf, macbuf[c], ']')=='\033') { X killcmd(); X return; X } X printf("]"); X X defent(c, buf); X} X Xdefent(c, buf) Xchar c; Xchar *buf; X{ X if(macbuf[c]) X free(macbuf[c]); X X if(!buf[0]) { X macbuf[c]=0; X return; X } X X macbuf[c] = (char *)malloc(strlen(buf)+1); X X if(!macbuf[c]) { X cmdline(); X printf("No room"); X return; X } X X strcpy(macbuf[c], buf); X} X Xdefmacs() X{ X defent(' ', "!more %\n"); X defent('%', "!%\n"); X defent('.', "=."); X defent('/', "=/"); X defent('~', "=~"); X defent('v', "!vi %\n"); X defent('$', "!vi /tmp/br.env.$$\n"); X} X Xdefinitions() X{ X char *ptr; X int i; X X cmdline(); X for(i=0; i<CHARSET; i++) X if(macbuf[i]) { X printf("define "); X ctloutc(i); X printf(" as ["); X ctlouts(macbuf[i]); X printf("]\n"); X } X display_up=0; X} X Xsavedefs() X{ X int i; X char filename[MAXLINE]; X static char lastfile[MAXLINE] = "/usr/tmp/macros"; X FILE *fp; X X cmdline(); X outs("Save macros as "); X X if(inps(filename, lastfile, '\033')=='\033') { X killcmd(); X return; X } X X strcpy(lastfile, filename); X X if(!(fp = fopen(filename, "w"))) { X wperror(filename); X return; X } X X for(i=1; i<CHARSET; i++) X if(macbuf[i]) X fprintf(fp, "[%c%s]\n", i, macbuf[i]); X X fclose(fp); X} X Xdumpenv(envp) Xchar **envp; X{ X if(!(efp=fopen(efname, "w"))) { X fperror(efname); X exit(-1); X } X while(*envp) X fprintf(efp, "$%s\n", *envp++); X fflush(efp); X} //END_OF_FILE echo "End of archive." # end of archive. exit 0