[comp.sources.misc] v09i066: browse: screen-oriented directory browser

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