Directory tree peruser - (nf)
mwm at ea.UUCP
mwm at ea.UUCP
Sat Jul 7 06:54:00 AEST 1984
#N:ea:12000002:000:35402
ea!mwm Jul 6 15:54:00 1984
echo README
sed 's/^ //' > README << 'All work and no play makes Jack a dull boy'
Here, finally, is the file peruser I mentioned lo these many months ago.
This creature started life as an on-line help system for v6. It rapidly
added the ability to be a directory browser and a restricted shell. I
recently tacked on the ability to be a menu system. Creeping Featurism
Lives!
Below (in shar format) you will find both the much-hacked source code, and
the user documentation (yes, that dates from v6, too). Though not obvious,
from the docs, the included code can function as:
1) A file system browser, just invoked with a name that starts
with "b".
2) A menu system, if invoked as something that starts with "m"
or "-m".
3) A restricted shell, if invoked as "-" anything but "m".
4) A help system, if invoked as anything else, and you have the
help directory set up correctly.
When running as a menu system, b checks for the file name defined by
MENUFILE in the current directory, and displays it to the user. This
feature may be more mis than otherwise, as I haven't tried using it. It is
punished by the Unix I/O system and b trying to run buffered output and
still be able to stop at the drop of an interrupt key. If it can be made to
work reasonably, b could be the basis for a VMS-like help system (Yeah!),
with the additional nicety of not having to use a librarian to build help
files.
In it's current incarnation, b is 4.2 dependent. It uses the 4.2 directory
routines, and assumes that it can keep the list of file names in memory.
I've included the 4.2 compatibility library, but I haven't tested it. Those
of you with small address spaces (or small memoried VAXen running AT&T
Unix) will have to live with b occasionally exiting rudely, or fix it so
that b will work with the directory on disk instead of in core.
Good Luck,
<mike
All work and no play makes Jack a dull boy
echo b.c
sed 's/^ //' > b.c << 'All work and no play makes Jack a dull boy'
#
/*
*
* help - A program to list documentation and peruse file systems.
*
* copyright (c) 1980 michael w. meyer
*
* 9/79 - mwm
*
* put in code to handle moving around in the file 10/79 - mwm
*
* added n flag, plus ability to turn flags off interactivly 12/79 - mwm
*
* put in the !<number> hack when shelling out, plus made fpipe
* a single routine, as opposed to being in both roff and pcat. 1/80 - mwm
*
* make the mods for the restricted visual shell mode. 2/80 - mwm
*
* made pflg on the default for background, and added info about
* '$' to the help routine - 4/80 mwm
*
* converted to use v7 system calls, added interaction with man,
* and added a default file to look at if invoked w/out args. 8/80 - mwm
*
* changed to read in the directory and use it if we can instead of
* reading it in off disk everytime. this should allow sorting and
* possibly increase efficiency. (hope hope) the sflg added to
* toggle the sorting of directories. ps -- 10/1/80
*
* added the `?' flag to the list of flags. It gives the user info
* regarding the flags. - mwm 10/80
*
* added code to get erase from termcap and the environment.
* dwb -- 3/2/81
*
* added 4.1bsd and 2.8bsd executable magic numbers. -jab 3/82
*
* removed V6 code -- ps 7/83
*
* change to 4.2 directory code, plus assume large address space.
* The directory either goes in memory, or we exit.
* Clean up various things that the creeping featurism has
* added. - mwm 5/84
*
* Added the PAGER environment variable check and the MENUFILE
* magic. - mwm 6/84
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/dir.h>
#include <sys/stat.h>
#include <signal.h>
#include <ctype.h>
/* Things to change, depending on your site. */
#define HELPFILE "help" /* file in ROOT to show the user */
#define MENUFILE ".menu" /* name of menu file in a directory */
#define ROOT "/usr/man/hel1" /* place to start looking */
#define CHAPST "/usr/man/hel" /* prefix for help chapters */
#define DIRHUNK 128 /* # of dir entries to alloc at a time */
#define HELPID 17 /* id of help user, if he exists */
/* Things to leave alone */
#define fdup(file) dup(fileno(file))
#define fdup2(f, o) dup2(fileno(f), fileno(o))
#define dot "."
#define LINEWIDTH 40
#define BUFLEN 160
#define NILL (char *) NULL
/* flags returned by type to doswitch */
#define DIRECT 0
#define EXEC DIRECT + 1
#define ROFF EXEC + 1
#define SPCL ROFF + 1
#define PDS SPCL + 1
#define ARCH PDS + 1
#define APL ARCH + 1
#define DEF APL + 1
#define OOPS DEF + 1
#define PACK OOPS + 1
#define EMPTY PACK + 1
#define COMPACT EMPTY + 1
/* the global control flags */
char tbuf[BUFSIZ]; /* a place to put env type strings */
struct stat Statbuf ;
char *erase = "\033\032\033\014\014",/* default erase string */
*pager = 0 , /* program to page files */
root[BUFLEN] = ROOT , /* the root of the mess */
*ProgName , /* name I was invoked as */
*format = "%3d: %-14.14s\0XXXXX" ; /* Standard 14-char format */
int lines = 23 , /* reasonable defualt */
rows = 80 , /* ditto */
entries = 4, /* default # of entries on a line */
pflg = 1 , /* we want paging */
nflg = 1 , /* nroff files for him?? */
aflg = 0 , /* show "." files? */
sflg = 1 , /* sort directory listings */
mflg = 0 , /* menu explanation? */
debug = 0 , /* true if being debugged */
rflg = 0 , /* the "restricted shell" mode */
sh = 0 ; /* used to tell if we are a shell */
struct direct *gdirp ;
int numen = DIRHUNK ;
/*
* main - interact with the user, after/while invoking various
* routines. It should be noted that cflg is a non-global flag,
* and is used to indicate that the current directory is no
* longer visible to the user, so a listing must be given to him.
*/
main(argc, argv) char **argv; {
register char buffer[BUFLEN] ;
FILE *file ;
int page(), cat(), special(), reset(), cflg ;
int pagelist(), list() ;
char errbuf[BUFSIZ], outbuf[BUFSIZ] ;
if (debug) fprintf(stderr, "entering main\n") ;
ProgName = argv[0] ;
/* Not portable, but we want to realloc it later... */
gdirp = (struct direct *) malloc(DIRHUNK * sizeof(struct direct)) ;
if (!debug) setbuf(stderr, errbuf) ;
setbuf(stdout, outbuf) ;
getenvinfo() ;
startup(buffer, &file, argc, argv) ;
if (debug) fprintf(stderr, "out of startup succesfully\n") ;
if (doswitch(file, buffer, &cflg)) exit(0) ;
signal(SIGINT, SIG_IGN) ;
signal(SIGQUIT, SIG_IGN) ;
for (;;) {
if (file != NULL) fclose(file) ;
if (cflg) {
cflg = 0 ;
call(pflg ? pagelist : list, 0) ;
}
fprintf(stderr, "\n\nSelect topic ('?' for help): ") ;
fflush(stderr) ;
if (gets(buffer) == NULL) finis() ;
if (*buffer == '$') finis() ;
if (!*buffer) continue ;
if (*buffer == '?') {
help() ;
continue ;
}
if (*buffer == '!') {
if (sh) {
fprintf(stderr,
"Sorry Charlie - can't call the shell!\n") ;
continue ;
}
call(special, &buffer[1]) ;
continue ;
}
if (*buffer == '~') {
setflgs(buffer + 1) ;
continue ;
}
if (isdigit(*buffer) && getfile(atoi(buffer), buffer)) {
fprintf(stderr, "file number %s not found.\n", buffer) ;
continue ;
}
if (*buffer == '/' && !buffer[1]) strcpy(buffer, root) ;
if (debug) fprintf(stderr, "buffer = %s\n", buffer) ;
if ((file = fopen(buffer, "r")) != NULL)
(void) doswitch(file, buffer, &cflg) ;
else {
fprintf(stderr, "can't open file %s.\n", buffer) ;
strcpy(buffer, dot) ;
}
}
}
/*
* startup, and the following function (findroot) handle
* arguments, and initialize files and globals based on them.
*/
startup(where, file, argc, argv)
char *where, **argv; FILE **file; {
register int count = argc ;
register char **vec = argv, *tmpv ;
int bomb = 0, sect = 0 ;
char *name = NILL, *psect = "012345678ul" ;
if (debug) fprintf(stderr, "entering startup\nbomb = %d\n", bomb) ;
if (argc == 1) name = HELPFILE ;
if (**vec == '-') {
sh++ ;
if ((*vec)[1] == 'm') mflg++ ;
if (getuid() != HELPID) {
strcpy(root, dot) ;
strcpy(name, dot) ;
/* chroot(dot) ; */
rflg++ ;
sect-- ;
}
}
else if (**vec == 'm') mflg++, rflg++ ;
else if (**vec == 'b') {
getdir(root) ;
name = dot ;
nflg = 0 ;
aflg = 1 ;
sflg = 0 ;
sect-- ;
}
if (debug) fprintf(stderr, "parsing arguments...") ;
while (--count) {
tmpv = *++vec ;
if (*tmpv == '-') {
if (setflgs(++tmpv)) bomb++ ;
}
else if (*tmpv == '.' || *tmpv == '/') {
if ((*file = fopen(tmpv, "r")) != (FILE *) NULL) {
strcpy(where, tmpv) ;
return ;
}
fprintf(stderr, "can't open file %s.\n", tmpv) ;
exit(1) ;
}
else if (!tmpv[1] && index(psect, *tmpv) && sect != -1)
sect = *tmpv ;
else name = tmpv ;
}
if (debug) fprintf(stderr, "completed!\nbomb = %d\n", bomb) ;
if (bomb) exit(1) ;
if (!isatty(fileno(stdout))) pflg = 0 ;
if (sect) {
findroot(where, name, sect) ;
if (debug) fprintf(stderr, "where = %s\n", where) ;
if ((*file = fopen(where, "r")) != (FILE *) NULL) return ;
# ifdef noman
fprintf(stderr, "help not available for %s.\n", name) ;
# else
execlp("man", "man", name, NILL);
fprintf(stderr, "couldn't exec man!\n") ;
# endif
exit(1) ;
}
else if (name != NILL) {
findroot(where, name, -1) ;
if (debug) fprintf(stderr, "where = %s\n", where) ;
if ((*file = fopen(where, "r")) != (FILE *) NULL) return ;
findroot(where, name, (int) '1') ;
if (debug) fprintf(stderr, "where = %s\n", where) ;
if ((*file = fopen(where, "r")) != (FILE *) NULL) return ;
# ifdef noman
fprintf(stderr, "help not available for %s.\n", name) ;
# else
execlp("man", "man", name, NILL);
fprintf(stderr, "couldn't exec man!\n") ;
# endif
exit(1) ;
}
else {
if ((*file = fopen(root, "r")) != (FILE *) NULL) {
strcpy(where, root) ;
return ;
}
fprintf(stderr, "can't open documentation root!\n") ;
exit(1) ;
}
}
findroot(where, what, sect) char *where, *what; {
if (debug) fprintf(stderr, "entering findroot\n") ;
if (sect == -1) {
strcpy(where, root) ;
where[strlen(root)] = '/' ;
strcpy(where + strlen(root) + 1, what) ;
return ;
}
if (what == (char *) NULL) sprintf(where, "%s%c", CHAPST, sect) ;
else sprintf(where, "%s%c/%s.%c", CHAPST, sect, what, sect) ;
if (debug) fprintf(stderr, "Where is %s\n", where) ;
}
/*
* getdir... return current directory. If something is wrong, e.g.
* you are in a an orphan directory or /bin/pwd is missing, returns
* "/" as the current directory...
* The basic idea behind this routine came to me from jab. It was modified
* to follow the conventions of help, and my coding style. - mwm
*/
getdir(p) char *p; {
FILE *infile, *outfile ;
register int pid ;
fpipe(&infile, &outfile) ;
if (pid = fork()) {
while (wait((int *) 0) != pid)
;
if (fgets(p, BUFLEN, infile) == NULL) {
p[0] = '/' ;
p[1] = '\0' ;
}
p[strlen(p) - 1] = '\0' ;
fclose(outfile) ;
fclose(infile) ;
}
else {
fclose(stdout) ;
fdup(outfile) ;
execl("/bin/pwd", NILL) ;
putchar('/') ;
exit(1) ;
}
}
/*
* doswitch - where most of the work actually gets done. Doswitch
* is given a file, and takes appropriate action in each case. It
* lists files, comments when the wrong kind of file is looked at,
* does chdir's, and changes a parameter to let the caller know.
* Doswitch also returns a value of true or false. In the main
* loop, this is ignored. However, on the first call, if
* it returns true (i.e., the user does not want to look at a
* directory,) the program exits. This causes help to behave as
* expected on "help so-and-so."
*/
doswitch(file, buffer, cflg) FILE *file; char *buffer; int *cflg; {
int run(), pcat(), roff() ;
if (debug) fprintf(stderr, "entering doswitch...\n") ;
switch (type(buffer)) {
case DIRECT:
if (debug) fprintf(stderr, "It's a directory...\n") ;
if (chdir(buffer) < 0) {
fprintf(stderr, "can't examine %s.\n", buffer) ;
return(0) ;
}
(*cflg) = 1 ;
fildir() ;
return(0) ;
case SPCL: fprintf(stderr, "%s is a special file.\n", buffer) ;
break ;
case PDS: fprintf(stderr, "%s is a PDS.\n", buffer) ;
break ;
case ARCH: fprintf(stderr, "%s is an archive.\n", buffer) ;
break ;
case APL: fprintf(stderr, "%s is an APL workspace.\n", buffer) ;
break ;
case EXEC:
if (rflg)
call(run, buffer) ;
else
fprintf(stderr, "%s is executable.\n", buffer) ;
break ;
case OOPS: fprintf(stderr, "program error OOPS!\n") ;
break ;
case PACK: call(pcat, buffer) ;
*cflg = pflg ;
break ;
case COMPACT: fprintf(stderr, "%s is a compacted file.\n", buffer) ;
break ;
case ROFF: *cflg = pflg ;
if (nflg) call(roff, file) ;
else call(pflg ? page : cat, file) ;
break ;
case EMPTY: fprintf(stderr, "%s is empty.\n", buffer) ;
break ;
default: *cflg = pflg ;
call(pflg ? page : cat, file) ;
break ;
}
fflush(stdout) ;
return(1) ;
}
/*
* type - determines the type of a file. This routine borrows heavily
* on file (with beauty mods, changes for portability reasons, plus
* simplification).
*/
type(name) char *name; {
register int ch ;
register FILE *file ;
if (debug) fprintf(stderr, "entering type...\n") ;
if (stat(name, &Statbuf) < 0) {
return(OOPS) ;
}
switch(Statbuf . st_mode & S_IFMT) {
case S_IFCHR:
case S_IFBLK:
return(SPCL) ;
case S_IFDIR:
return(DIRECT) ;
}
if (Statbuf . st_size == 0l)
return(EMPTY) ;
if ((file = fopen(name, "r")) == (FILE *) NULL) return OOPS ;
ch = getw(file) ;
rewind(file) ;
switch(ch) {
case 0407: /* pdp11 files.. */
case 0410:
case 0411:
case 0413: /* VAX "-z" format file */
case 0430: /* 2.8bsd overlay */
case 0431: /* 2.8bsd overlay, i/d */
return(EXEC) ;
case 0177545:
case 0177555:
return(ARCH) ;
case 0100554:
case 0100555:
return(APL) ;
case 052525:
return(PDS) ;
case 017437:
return(PACK) ;
case 017777:
return(COMPACT) ;
default:
if ((0377 & ch) == '.') return(ROFF) ;
else return(DEF) ;
}
}
/*
* run - runs a command. This is used in the restricted shell mode to let
* users run commands.
*/
run(buffer) char *buffer; {
execl(buffer, buffer, NILL) ;
fprintf(stderr, "couldn't exec %s.\n", buffer) ;
}
/*
* The following five routines do (almost) all of the IO. They list
* files (cat & page) and directories (list & pagelist).
* There are two for each, one with paging (page & pagelist) and one
* without (cat and list). Each of these is provoked by call, so
* that they can be interupted.
*/
list() {
register int i ;
register struct direct *dirp ;
register FILE *menu ;
FILE *fopen() ;
int cat() ;
if (mflg && (menu = fopen(MENUFILE, "r")) != (FILE *) NULL)
call(cat, menu) ;
if (debug) fprintf(stderr, "In list, numen = %d, top = %D\n", numen,
(long) &gdirp[numen]) ;
fprintf(stderr, "\n\ncurrent topics are:\n\n") ;
for (i = 0, dirp = gdirp; dirp < &gdirp[numen] ; dirp++) {
if (*(dirp -> d_name) == 0) break ;
if (!aflg && dirp->d_name[0] == '.') continue ;
fprintf(stderr, format, ++i, dirp->d_name) ;
if (i && i % entries == 0)
putc('\n', stderr) ;
}
if (i == 0)
fprintf(stderr, "The directory is empty.\n") ;
if (menu != (FILE *) NULL) fclose(menu) ;
fflush(stderr) ;
}
pagelist(file) FILE *file; {
register int i = 0 ;
register struct direct *dirp ;
char buffer[BUFLEN] ;
register FILE *menu ;
FILE *fopen() ;
int page() ;
if (debug) fprintf(stderr, "In pagelist, numen = %d\n", numen) ;
if (mflg && (menu = fopen(MENUFILE, "r")) != (FILE *) NULL) {
fprintf(stderr, "%s", erase) ;
fflush(stderr) ;
call(cat, menu) ;
fprintf(stderr, "\n\nHit enter for menu:") ;
fflush(stderr) ;
gets(buffer) ;
fprintf(stderr, "\n") ;
fflush(stderr) ;
}
else fprintf(stderr, "%s\n\ncurrent topics are:\n\n", erase) ;
for (i = 0, dirp = gdirp; dirp < &gdirp[numen]; dirp++) {
if (*(dirp -> d_name) == 0) break ;
if (!aflg && *(dirp -> d_name) == '.') continue ;
fprintf(stderr, format, ++i, dirp -> d_name) ;
if (i && i % entries == 0)
if (i % (entries * (lines - 3)) != 0)
putc('\n', stderr) ;
else {
putc('\n', stderr) ;
fflush(stderr) ;
if (gets(buffer) == NULL)
finis() ;
fputs(erase, stderr) ;
}
}
if (!i) fprintf(stderr, "The directory is empty.") ;
if (menu != (FILE *) NULL) fclose(menu) ;
fflush(stderr) ;
}
cat(file) FILE *file; {
register int c ;
while ((c = getc(file)) != EOF)
putchar(c) ;
}
page(file) FILE *file; {
register char buffer[BUFLEN] ;
register int i ;
if (pager != (char *) NULL) {
fclose(stdin) ; /* point stdin to the file */
fdup2(file, stdin) ;
execlp(pager, pager, 0) ;
} /* if the execl fails, use default */
for (;;) {
puts(erase) ;
for (i = 0; ++i < lines;)
if (fgets(buffer, BUFLEN, file) != NULL)
fputs(buffer, stdout) ;
else {
i = 0 ;
break ;
}
fflush(stdout) ;
fprintf(stderr, "Press enter to continue") ;
if (gets(buffer) == NULL)
finis() ;
if (!i && command(buffer, file)) return ;
else if (*buffer) command(buffer, file) ;
}
}
/*
* command - run the pointer around in the file for the user
*/
command(buffer, file) char *buffer; FILE *file; {
register int i ;
register char *buf = buffer ;
long j, ftell() ;
char tbuf[BUFLEN] ;
while (*buf == ' ' || *buf == '\t')
buf++ ;
if (!(*buf)) return(1) ;
if (*buf == '0') {
rewind(file) ;
return(0) ;
}
i = atoi(buf) ;
while (isdigit(*buf))
*buf++ ;
if (*buf == '+') i = i ? i : 1 ;
else if (*buf == '-') i = i ? -i - 1 : -2 ;
while (*++buf == '-' || *buf == '+')
i += *buf == '-' ? -1 : 1 ;
j = i * lines * LINEWIDTH ;
if (ftell(file) < -j) rewind(file) ;
else {
fseek(file, j, 1) ;
fgets(tbuf, BUFLEN, file) ;
}
return(1) ;
}
/*
* pcat - unpack a file and show it to the user...
*/
pcat(file) char *file; {
FILE *infile, *outfile ;
register int pid ;
fpipe(&infile, &outfile) ;
if (pid = fork()) {
fclose(outfile) ;
if (pflg) page(infile) ;
else cat(infile) ;
fflush(stdout) ;
while (wait((int *) 0) != pid)
;
}
else {
fclose(stdout) ;
fdup(outfile) ;
execl("/usr/bin/pcat", "pcat", file, NILL) ;
fprintf(stderr, "couldn't exec pcat.\n") ;
}
}
/*
* roff - list a file for the user, through nroff.....
*/
roff(file) FILE *file; {
FILE *infile, *outfile ;
register int pid ;
fpipe(&infile, &outfile) ;
if (pid = fork()) {
fclose(file) ;
fclose(outfile) ;
if (pflg) page(infile) ;
else cat(infile) ;
while (wait((int *) 0) != pid)
;
}
else {
fclose(stdin) ;
fdup(file) ;
fclose(stdout) ;
fdup(outfile) ;
fclose(infile) ;
execl("/usr/bin/nroff", "nroff", "-h", "-man", NILL) ;
fprintf(stderr, "couldn't exec nroff.\n") ;
}
}
/*
* setflgs - set the global flags.
*/
setflgs(in) char *in; {
register int tmp ;
register char *flgs ;
for (flgs = in; *flgs; flgs++)
switch (*flgs) {
case 0: break ; /* ignore null flags */
case 'a': aflg ^= 1 ;
break ;
case 'p': pflg = 1 ;
break ;
case 'c': pflg = 0 ;
break ;
case 'e': pflg = 1 ;
if ((tmp = strlen(++flgs)) < 6)
strcpy(erase, flgs) ;
else {
flgs[5] = '\0' ;
strcpy(erase, flgs) ;
}
return(0) ;
case 'l': pflg = 1 ;
tmp = atoi(flgs + 1) ;
if (tmp > 0) lines = tmp ;
return(0) ;
case 'n': nflg ^= 1 ;
break ;
case 'd': debug ^= 1 ;
break ;
case 's': sflg ^= 1 ;
break ;
case '?': showflgs() ;
break ;
default:
fprintf(stderr, "bad flag %s\n", flgs) ;
return(1) ;
}
return(0) ;
}
/*
* showflgs - print usefull information about the flags...
*/
showflgs() {
fprintf(stderr, "\n\tflag\tstate\thandles\n\n") ;
fprintf(stderr, "\tl\t%d\tnumber of lines per page\n", lines) ;
fprintf(stderr, "\te\t???\tstring to erase the screen\n") ;
fprintf(stderr, "\tp\t%s\tpage through the file\n",
pflg ? "on" : "off") ;
fprintf(stderr, "\tc\t%s\tturn off paging\n", pflg ? "off" : "on") ;
fprintf(stderr, "\ts\t%s\tsort the directory\n", sflg ? "on" : "off") ;
fprintf(stderr, "\ta\t%s\tshow all files\n", aflg ? "on" : "off") ;
fprintf(stderr, "\tn\t%s\tnroff nroffable files\n",
nflg ? "on" : "off") ;
fflush(stderr) ;
}
/*
* getfile - turns numbers into filenames (the inverse of the mapping
* done by list and pagelist)
*/
getfile(num, name) char *name; {
register struct direct *dirp ;
if (!num || num > numen) return (1) ;
for (dirp = gdirp; dirp < &gdirp[numen]; dirp++) {
if (*(dirp->d_name) == 0) return(1) ;
if (!aflg && dirp->d_name[0] == '.') continue ;
if (--num == 0) break ;
}
strcpy(name, dirp -> d_name) ;
return(0) ;
}
/*
* call - the routine that gets provoked the most. call forks a process
* which points interrups someplace obscure, then invokes the wanted routine
* with the given argument. This is the routine that makes interrupts work
* right. Signals get pointed to _exit, as just turning them on leaves process
* hanging around.
*/
/* VARARGS1 */
call(routine, it) int (*routine)(); {
register int fp ;
int (*oldint)(), (*oldquit)() ;
extern _exit() ;
oldint = signal(SIGINT, SIG_IGN) ;
oldquit = signal(SIGQUIT, SIG_IGN) ;
if (fp = fork())
while (wait((int *) 0) != fp)
;
else {
signal(SIGINT, _exit) ;
signal(SIGQUIT, _exit) ;
(*routine)(it) ;
exit(0) ;
}
signal(SIGINT, oldint) ;
signal(SIGQUIT, oldquit) ;
}
/*
* fpipe - makes a "stdio" pipe. This is the only place in help where
* file manipulation is not STRICTLY stdio. If help/b is ever moved to
* another system, this will have to be rewritten.
*/
fpipe(filein, fileout) FILE **filein, **fileout; {
register int fd[2] ;
pipe(fd) ;
*filein = fdopen(fd[0], "r") ;
*fileout = fdopen(fd[1], "w") ;
}
/*
* help - for the user.
*/
help() {
fprintf(stderr,
"\n\nenter a name or a number to see more documentation\n") ;
fprintf(stderr, "type '.' to list current documentation level\n") ;
fprintf(stderr, "type '/' to return to documentation root\n") ;
if (!sh) fprintf(stderr, "type '!' followed by shell command\n") ;
fprintf(stderr, "type $ (or ^D) to exit from help\n") ;
fprintf(stderr, "type '?' for this message\n\n\n") ;
}
/*
* Used to process '!' input. Provoked by call
*/
special(input) char *input; {
register char *ptr, *cmdptr, *tmpptr ;
char command[BUFLEN], file[BUFLEN], changeflg = 0 ;
for (ptr = input, cmdptr = command; *ptr;)
if (*ptr != '!' || !isdigit(ptr[1]))
*cmdptr++ = *ptr++ ;
else {
if (getfile(atoi(++ptr), file)) {
fprintf(stderr, "file number %s not found.\n",
ptr) ;
fflush(stderr) ;
return ;
}
changeflg++ ;
for (tmpptr = file; *tmpptr;)
*cmdptr++ = *tmpptr++ ;
while (isdigit(*ptr)) ptr++ ;
}
*cmdptr = '\0' ;
if (changeflg) fprintf(stderr, "command is: %s\n", command) ;
fflush(stderr) ;
execl("/bin/csh", "sh", "-c", command, NILL) ;
fprintf(stderr, "couldn't exec the shell!\n") ;
}
/*
* used to tidy up before we go away...
*/
finis() {
putc('\n', stderr) ;
fflush(stderr) ;
exit(0) ;
}
/*
* fildir - allocate space for and read the directory into memory
*/
fildir(file) register FILE *file; {
char *calloc() ;
int dircmp(), i ;
struct direct *tmp ;
register struct direct *direct = gdirp ;
register DIR *thisdir ;
if ((thisdir = opendir(dot)) == (DIR *) NULL) {
perror(ProgName) ;
exit(1) ;
}
for (;;) {
if (direct - gdirp == numen) { /* out of directory memory */
gdirp = (struct direct *) realloc(gdirp,
(numen += DIRHUNK) * sizeof(struct direct)) ;
if (gdirp == (struct direct *) NULL) {
perror(ProgName) ;
exit(1) ;
}
}
if ((tmp = readdir(thisdir)) == (struct direct *) NULL)
break ;
if (tmp -> d_namlen > entries) entries = tmp -> d_namlen ;
*direct++ = *tmp ; /* copy it into place */
}
if (debug) fprintf(stderr, "in filldir, have %d entries\n",
direct - gdirp) ;
if (sflg)
qsort((char *)gdirp, direct - gdirp,
sizeof(struct direct), dircmp) ;
/* fill out rest so we know it's empty */
for (i = numen - (direct - gdirp); i >= 0; i--)
*(direct++ -> d_name) = 0 ;
sprintf(format, "%%3d: %%-%d.%ds", entries, entries) ;
entries = rows / (entries + 5) ;
}
dircmp(dp1, dp2) register struct direct *dp1, *dp2; {
return(strcmp(dp1->d_name, dp2->d_name)) ;
}
getenvinfo() {
char buf[1024] ;
char *p ;
char *getenv() ;
int i ;
if ((p = getenv("TERM")) == (char *) NULL) pflg = 0 ;
else
switch (tgetent(buf, p)) {
case -1: fprintf(stderr, "can't open termcap\n") ;
break ;
case 0: fprintf(stderr, "unknown term type: %s.\n", p) ;
break ;
case 1: p = tbuf ;
tgetstr("cl", &p) ;
erase = tbuf ;
if (erase = (char *) NULL) pflg = 0 ;
i = tgetnum("li") ;
if (i > 0) lines = i ;
i = tgetnum("co") ;
if (i > 0) rows = i ;
if (tgetflag("am")) --rows ;
break ;
}
pager = getenv("PAGER") ;
}
All work and no play makes Jack a dull boy
echo b.nr
sed 's/^ //' > b.nr << 'All work and no play makes Jack a dull boy'
.sh NAME
help, b -- peruse system files and documentation
.sh SYNOPSIS
help [-apcn] [-eccc] [-lnnn] [name] [section]
.sh DESCRIPTION
Help and browse are a document perusing system, the command help being
one entry point, and b being the other. The behavior of the system
does not, for the most part, depend on which is invoked, so help will
be described, with the important differences of b being noted.
.s3
To start help, type help, with the a name and/or section number. If
both are omitted, help will show a short document on the help system. If
only a name is given, help will search the documentation root and
then section 1 for name. If name starts with a '.' or a '/', help
will assume that it is a valid file name, and try to open it
without searching the documentation root. If only a section number is
given, help starts on that section. If both a name and section are given,
help searches the section given for name. B acts approximatly the same
way, except that the section is ignored, and the current directory
is searched instead of the documentation root.
.s3
The flags for help toggle on and off various features, and change defaults.
The p flag starts help in paging mode, which is the default. The c
flag causes help to run in continuous mode. The l and e flag are
used only in paging mode, and are, respectively, the number of
lines per page, and the erase string to be used. The a flag causes
help to show all files, even ones that have a dot as the first character.
The n flag causes help to not nroff files. This is useful when
you are examining as programs. B starts in all mode, without nroffing.
.s3
When help first starts, it determines if the file it was asked to
show is a directory or not. If the file is a directory, help
will display a menu, and wait for the user to pick a
topic from it. Otherwise, help allows the user to examine the file,
and then returns to the shell.
.s3
Help has two modes, paging and continuous. When in continuous mode,
help assumes the user is at a hard-copy terminal, and acts accordingly.
It will list files without pause, and shows menu's only when the
current menu changes.
When help displays a menu, it will display a list of names, each
having a number associated with it. It then waits for the user
to pick a topic.
While help is waiting, the following
responses are valid.
.s1
1) a number: examine the file with that number in the
current menu.
.s1
2) a name: examine the file of that name in the current
menu.
.s1
3) .: redisplay the current menu.
.s1
4) ..: go up to the directory (menu) above the current
one.
.s1
5) ~[flags]: toggle the flags back and forth. Use `~~'
to enter a `~' at the beginning of a line.
~? will display the state of the flags
.s1
6) ?: give a short help listing for help.
.s1
7) ![cmd]: execute cmd as a shell cmd. See below
.s1
8) /: go back to the documentation root.
(for b, the working directory when it was
started.)
.s1
9) //: go to they system root.
.s1
10) $ or ^D: end the current session.
.s1
11) finally, /names and ../.. all work as expected.
.s3
When running shell commands from inside help (via the `!' mechanism),
you can reference the file numbers given in the menu. This is done by
putting `!' followed by a number in the command. In this case, help
will echo the modified command.
.s3
For example, assume that there are two file in the current listing,
these being:
.s1
1: glop 2: gort.c
.s1
Then you could find out what glop was by typing `!file !1'.
If glop turned out to be executable, it could be run by typeing `!!1'.
.s3
When help is in paging mode, it assumes the user is at a crt. The most
noticeable thing that this causes is a pause at the bottom of a page
when listing files and directories. Help will wait until the user
types a newline before continuing. If help is listing a file, it will
also watch what the user types on that line, and take special action
based on this.
.s3
If nothing but a blank line is typed, help will keep on listing the file.
If it sees an unsigned number, or a number followed by a '+', help will
skip forward that many pages. A '+' alone acts like a '1+'.
If help sees a '-' or a number followed by a '-', it will go
backwards 1 or number pages. If help sees a '0' (as the first character!)
it will start the file over.
The full syntax of this is
one or more spaces or tabs,
followed by an optional number, or '0'
followed by either a '+', a '-', or nothing.
.s3
If you would prefer a different pager, say more, setting the environment
variable PAGER to the name of the program will cause it to be invoked
instead of the help internal pager described above.
.s3
Note that if asked to skip all the way past the end of the file, help
will act as if it has gone past the end normally, and stop. If asked
to go out the front end of the file, help starts again at the
front.
.s3
If help is showing you a packed or roffed file, help will ignore these things.
.sh DIAGNOSTICS
mostly self-explanatory.
.sh FILES
The documentation root is /usr/man, with /usr/man/helx being the
directories searched.
All work and no play makes Jack a dull boy
echo dir.c
sed 's/^ //' > dir.c << 'All work and no play makes Jack a dull boy'
#include <sys/types.h>
#include <sys/param.h>
#include "dir.h"
/*
* close a directory.
*/
void
closedir(dirp)
register DIR *dirp;
{
close(dirp->dd_fd);
dirp->dd_fd = -1;
dirp->dd_loc = 0;
free(dirp);
}
/*
* open a directory.
*/
DIR *
opendir(name)
char *name;
{
register DIR *dirp;
register int fd;
if ((fd = open(name, 0)) == -1)
return NULL;
if ((dirp = (DIR *)malloc(sizeof(DIR))) == NULL) {
close (fd);
return NULL;
}
dirp->dd_fd = fd;
dirp->dd_loc = 0;
return dirp;
}
/*
* read an old style directory entry and present it as a new one
*/
#define ODIRSIZ 14
struct olddirect {
ino_t od_ino;
char od_name[ODIRSIZ];
};
/*
* get next entry in a directory.
*/
struct direct *
readdir(dirp)
register DIR *dirp;
{
register struct olddirect *dp;
static struct direct dir;
for (;;) {
if (dirp->dd_loc == 0) {
dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf,
DIRBLKSIZ);
if (dirp->dd_size <= 0)
return NULL;
}
if (dirp->dd_loc >= dirp->dd_size) {
dirp->dd_loc = 0;
continue;
}
dp = (struct olddirect *)(dirp->dd_buf + dirp->dd_loc);
dirp->dd_loc += sizeof(struct olddirect);
if (dp->od_ino == 0)
continue;
dir.d_ino = dp->od_ino;
strncpy(dir.d_name, dp->od_name, ODIRSIZ);
dir.d_name[ODIRSIZ] = '\0'; /* insure null termination */
dir.d_namlen = strlen(dir.d_name);
dir.d_reclen = DIRBLKSIZ;
return (&dir);
}
}
All work and no play makes Jack a dull boy
echo dir.h
sed 's/^ //' > dir.h << 'All work and no play makes Jack a dull boy'
/* dir.h 4.4 82/07/25 */
/*
* A directory consists of some number of blocks of DIRBLKSIZ
* bytes, where DIRBLKSIZ is chosen such that it can be transferred
* to disk in a single atomic operation (e.g. 512 bytes on most machines).
*
* Each DIRBLKSIZ byte block contains some number of directory entry
* structures, which are of variable length. Each directory entry has
* a struct direct at the front of it, containing its inode number,
* the length of the entry, and the length of the name contained in
* the entry. These are followed by the name padded to a 4 byte boundary
* with null bytes. All names are guaranteed null terminated.
* The maximum length of a name in a directory is MAXNAMLEN.
*
* The macro DIRSIZ(dp) gives the amount of space required to represent
* a directory entry. Free space in a directory is represented by
* entries which have dp->d_reclen >= DIRSIZ(dp). All DIRBLKSIZ bytes
* in a directory block are claimed by the directory entries. This
* usually results in the last entry in a directory having a large
* dp->d_reclen. When entries are deleted from a directory, the
* space is returned to the previous entry in the same directory
* block by increasing its dp->d_reclen. If the first entry of
* a directory block is free, then its dp->d_ino is set to 0.
* Entries other than the first in a directory do not normally have
* dp->d_ino set to 0.
*/
#define DIRBLKSIZ 512
#define MAXNAMLEN 255
struct direct {
long d_ino; /* inode number of entry */
short d_reclen; /* length of this record */
short d_namlen; /* length of string in d_name */
char d_name[MAXNAMLEN + 1]; /* name must be no longer than this */
};
/*
* The DIRSIZ macro gives the minimum record length which will hold
* the directory entry. This requires the amount of space in struct direct
* without the d_name field, plus enough space for the name with a terminating
* null byte (dp->d_namlen+1), rounded up to a 4 byte boundary.
*/
#undef DIRSIZ
#define DIRSIZ(dp) \
((sizeof (struct direct) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &~ 3))
#ifndef KERNEL
/*
* Definitions for library routines operating on directories.
*/
typedef struct _dirdesc {
int dd_fd;
long dd_loc;
long dd_size;
char dd_buf[DIRBLKSIZ];
} DIR;
#ifndef NULL
#define NULL 0
#endif
extern DIR *opendir();
extern struct direct *readdir();
extern long telldir();
extern void seekdir();
#define rewinddir(dirp) seekdir((dirp), (long)0)
extern void closedir();
#endif KERNEL
All work and no play makes Jack a dull boy
More information about the Comp.sources.unix
mailing list