696 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			696 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
/*	man 2.5 - display online manual pages		Author: Kees J. Bot
 | 
						|
 *								17 Mar 1993
 | 
						|
 */
 | 
						|
#define nil NULL
 | 
						|
#include <sys/types.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <dirent.h>
 | 
						|
#include <string.h>
 | 
						|
#include <errno.h>
 | 
						|
#include <unistd.h>
 | 
						|
#include <stdarg.h>
 | 
						|
#include <fcntl.h>
 | 
						|
#include <signal.h>
 | 
						|
#include <sys/stat.h>
 | 
						|
#include <sys/wait.h>
 | 
						|
 | 
						|
/* Defaults: */
 | 
						|
char MANPATH[]=	"/usr/local/man:/usr/man:/usr/gnu/man";
 | 
						|
char PAGER[]=	"more";
 | 
						|
 | 
						|
/* Comment at the start to let tbl(1) be run before n/troff. */
 | 
						|
char TBL_MAGIC[] = ".\\\"t\n";
 | 
						|
 | 
						|
#define arraysize(a)	(sizeof(a) / sizeof((a)[0]))
 | 
						|
#define arraylimit(a)	((a) + arraysize(a))
 | 
						|
#define between(a, c, z) ((unsigned) ((c) - (a)) <= (unsigned) ((z) - (a)))
 | 
						|
 | 
						|
/* Section 1x uses special macros under Minix. */
 | 
						|
#if __minix
 | 
						|
#define SEC1xSPECIAL	1
 | 
						|
#else
 | 
						|
#define SEC1xSPECIAL	0
 | 
						|
#endif
 | 
						|
 | 
						|
int searchwhatis(FILE *wf, char *title, char **ppage, char **psection)
 | 
						|
/* Search a whatis file for the next occurence of "title".  Return the basename
 | 
						|
 * of the page to read and the section it is in.  Return 0 on failure, 1 on
 | 
						|
 * success, -1 on EOF or error.
 | 
						|
 */
 | 
						|
{
 | 
						|
    static char page[256], section[32];
 | 
						|
    char alias[256];
 | 
						|
    int found= 0;
 | 
						|
    int c;
 | 
						|
 | 
						|
    /* Each whatis line should have the format:
 | 
						|
     *	page, title, title (section) - descriptive text
 | 
						|
     */
 | 
						|
 | 
						|
    /* Search the file for a line with the title. */
 | 
						|
    do {
 | 
						|
	int first= 1;
 | 
						|
	char *pc= section;
 | 
						|
 | 
						|
	c= fgetc(wf);
 | 
						|
 | 
						|
	/* Search the line for the title. */
 | 
						|
	do {
 | 
						|
	    char *pa= alias;
 | 
						|
 | 
						|
	    while (c == ' ' || c == '\t' || c == ',') c= fgetc(wf);
 | 
						|
 | 
						|
	    while (c != ' ' && c != '\t' && c != ','
 | 
						|
		    && c != '(' && c != '\n' && c != EOF
 | 
						|
	    ) {
 | 
						|
		if (pa < arraylimit(alias)-1) *pa++= c;
 | 
						|
		c= getc(wf);
 | 
						|
	    }
 | 
						|
	    *pa= 0;
 | 
						|
	    if (first) { strcpy(page, alias); first= 0; }
 | 
						|
 | 
						|
	    if (strcmp(alias, title) == 0) found= 1;
 | 
						|
	} while (c != '(' && c != '\n' && c != EOF);
 | 
						|
 | 
						|
	if (c != '(') {
 | 
						|
	    found= 0;
 | 
						|
	} else {
 | 
						|
	    while ((c= fgetc(wf)) != ')' && c != '\n' && c != EOF) {
 | 
						|
		if ('A' <= c && c <= 'Z') c= c - 'A' + 'a';
 | 
						|
		if (pc < arraylimit(section)-1) *pc++= c;
 | 
						|
	    }
 | 
						|
	    *pc= 0;
 | 
						|
	    if (c != ')' || pc == section) found= 0;
 | 
						|
	}
 | 
						|
	while (c != EOF && c != '\n') c= getc(wf);
 | 
						|
    } while (!found && c != EOF);
 | 
						|
 | 
						|
    if (found) {
 | 
						|
	*ppage= page;
 | 
						|
	*psection= section;
 | 
						|
    }
 | 
						|
    return c == EOF ? -1 : found;
 | 
						|
}
 | 
						|
 | 
						|
int searchwindex(FILE *wf, char *title, char **ppage, char **psection)
 | 
						|
/* Search a windex file for the next occurence of "title".  Return the basename
 | 
						|
 * of the page to read and the section it is in.  Return 0 on failure, 1 on
 | 
						|
 * success, -1 on EOF or error.
 | 
						|
 */
 | 
						|
{
 | 
						|
    static char page[256], section[32];
 | 
						|
    static long low, high;
 | 
						|
    long mid0, mid1;
 | 
						|
    int c;
 | 
						|
    unsigned char *pt;
 | 
						|
    char *pc;
 | 
						|
 | 
						|
    /* Each windex line should have the format:
 | 
						|
     *	title page (section) - descriptive text
 | 
						|
     * The file is sorted.
 | 
						|
     */
 | 
						|
 | 
						|
    if (ftell(wf) == 0) {
 | 
						|
	/* First read of this file, initialize. */
 | 
						|
	low= 0;
 | 
						|
	fseek(wf, (off_t) 0, SEEK_END);
 | 
						|
	high= ftell(wf);
 | 
						|
    }
 | 
						|
 | 
						|
    /* Binary search for the title. */
 | 
						|
    while (low <= high) {
 | 
						|
	pt= (unsigned char *) title;
 | 
						|
 | 
						|
	mid0= mid1= (low + high) >> 1;
 | 
						|
	if (mid0 == 0) {
 | 
						|
	    if (fseek(wf, (off_t) 0, SEEK_SET) != 0)
 | 
						|
		return -1;
 | 
						|
	} else {
 | 
						|
	    if (fseek(wf, (off_t) mid0 - 1, SEEK_SET) != 0)
 | 
						|
		return -1;
 | 
						|
 | 
						|
	    /* Find the start of a line. */
 | 
						|
	    while ((c= getc(wf)) != EOF && c != '\n')
 | 
						|
		mid1++;
 | 
						|
	    if (ferror(wf)) return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	/* See if the line has the title we seek. */
 | 
						|
	for (;;) {
 | 
						|
	    if ((c= getc(wf)) == ' ' || c == '\t') c= 0;
 | 
						|
	    if (c == 0 || c != *pt) break;
 | 
						|
	    pt++;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Halve the search range. */
 | 
						|
	if (c == EOF || *pt <= c) {
 | 
						|
	    high= mid0 - 1;
 | 
						|
	} else {
 | 
						|
	    low= mid1 + 1;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
    /* Look for the title from 'low' onwards. */
 | 
						|
    if (fseek(wf, (off_t) low, SEEK_SET) != 0)
 | 
						|
	return -1;
 | 
						|
 | 
						|
    do {
 | 
						|
	if (low != 0) {
 | 
						|
	    /* Find the start of a line. */
 | 
						|
	    while ((c= getc(wf)) != EOF && c != '\n')
 | 
						|
		low++;
 | 
						|
	    if (ferror(wf)) return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	/* See if the line has the title we seek. */
 | 
						|
	pt= (unsigned char *) title;
 | 
						|
 | 
						|
	for (;;) {
 | 
						|
	    if ((c= getc(wf)) == EOF) return 0;
 | 
						|
	    low++;
 | 
						|
	    if (c == ' ' || c == '\t') c= 0;
 | 
						|
	    if (c == 0 || c != *pt) break;
 | 
						|
	    pt++;
 | 
						|
	}
 | 
						|
    } while (c < *pt);
 | 
						|
 | 
						|
    if (*pt != c) return 0;		/* Not found. */
 | 
						|
 | 
						|
    /* Get page and section. */
 | 
						|
    while ((c= fgetc(wf)) == ' ' || c == '\t') {}
 | 
						|
 | 
						|
    pc= page;
 | 
						|
    while (c != ' ' && c != '\t' && c != '(' && c != '\n' && c != EOF) {
 | 
						|
	if (pc < arraylimit(page)-1) *pc++= c;
 | 
						|
	c= getc(wf);
 | 
						|
    }
 | 
						|
    if (pc == page) return 0;
 | 
						|
    *pc= 0;
 | 
						|
 | 
						|
    while (c == ' ' || c == '\t') c= fgetc(wf);
 | 
						|
 | 
						|
    if (c != '(') return 0;
 | 
						|
 | 
						|
    pc= section;
 | 
						|
    while ((c= fgetc(wf)) != ')' && c != '\n' && c != EOF) {
 | 
						|
	if ('A' <= c && c <= 'Z') c= c - 'A' + 'a';
 | 
						|
	if (pc < arraylimit(section)-1) *pc++= c;
 | 
						|
    }
 | 
						|
    *pc= 0;
 | 
						|
    if (c != ')' || pc == section) return 0;
 | 
						|
 | 
						|
    while (c != EOF && c != '\n') c= getc(wf);
 | 
						|
    if (c != '\n') return 0;
 | 
						|
 | 
						|
    *ppage= page;
 | 
						|
    *psection= section;
 | 
						|
    return 1;
 | 
						|
}
 | 
						|
 | 
						|
char ALL[]= "";		/* Magic sequence of all sections. */
 | 
						|
 | 
						|
int all= 0;		/* Show all pages with a given title. */
 | 
						|
int whatis= 0;		/* man -f word == whatis word. */
 | 
						|
int apropos= 0;		/* man -k word == apropos word. */
 | 
						|
int quiet= 0;		/* man -q == quietly check. */
 | 
						|
enum ROFF { NROFF, TROFF } rofftype= NROFF;
 | 
						|
char *roff[] = { "nroff", "troff" };
 | 
						|
 | 
						|
int shown;		/* True if something has been shown. */
 | 
						|
int tty;		/* True if displaying on a terminal. */
 | 
						|
char *manpath;		/* The manual directory path. */
 | 
						|
char *pager;		/* The pager to use. */
 | 
						|
 | 
						|
char *pipeline[8][8];	/* An 8 command pipeline of 7 arguments each. */
 | 
						|
char *(*plast)[8] = pipeline;
 | 
						|
 | 
						|
void putinline(char *arg1, ...)
 | 
						|
/* Add a command to the pipeline. */
 | 
						|
{
 | 
						|
    va_list ap;
 | 
						|
    char **argv;
 | 
						|
 | 
						|
    argv= *plast++;
 | 
						|
    *argv++= arg1;
 | 
						|
 | 
						|
    va_start(ap, arg1);
 | 
						|
    while ((*argv++= va_arg(ap, char *)) != nil) {}
 | 
						|
    va_end(ap);
 | 
						|
}
 | 
						|
 | 
						|
void execute(int set_mp, char *file)
 | 
						|
/* Execute the pipeline build with putinline().  (This is a lot of work to
 | 
						|
 * avoid a call to system(), but it so much fun to do it right!)
 | 
						|
 */
 | 
						|
{
 | 
						|
    char *(*plp)[8], **argv;
 | 
						|
    char *mp;
 | 
						|
    int fd0, pfd[2], err[2];
 | 
						|
    pid_t pid;
 | 
						|
    int r, status;
 | 
						|
    int last;
 | 
						|
    void (*isav)(int sig), (*qsav)(int sig), (*tsav)(int sig);
 | 
						|
 | 
						|
    if (tty) {
 | 
						|
	/* Must run this through a pager. */
 | 
						|
	putinline(pager, (char *) nil);
 | 
						|
    }
 | 
						|
    if (plast == pipeline) {
 | 
						|
	/* No commands at all? */
 | 
						|
	putinline("cat", (char *) nil);
 | 
						|
    }
 | 
						|
 | 
						|
    /* Add the file as argument to the first command. */
 | 
						|
    argv= pipeline[0];
 | 
						|
    while (*argv != nil) argv++;
 | 
						|
    *argv++= file;
 | 
						|
    *argv= nil;
 | 
						|
 | 
						|
    /* Start the commands. */
 | 
						|
    fd0= 0;
 | 
						|
    for (plp= pipeline; plp < plast; plp++) {
 | 
						|
	argv= *plp;
 | 
						|
	last= (plp+1 == plast);
 | 
						|
 | 
						|
	/* Create an error pipe and pipe between this command and the next. */
 | 
						|
	if (pipe(err) < 0 || (!last && pipe(pfd) < 0)) {
 | 
						|
	    fprintf(stderr, "man: can't create a pipe: %s\n", strerror(errno));
 | 
						|
	    exit(1);
 | 
						|
	}
 | 
						|
 | 
						|
	(void) fcntl(err[1], F_SETFD, fcntl(err[1], F_GETFD) | FD_CLOEXEC);
 | 
						|
 | 
						|
	if ((pid = fork()) < 0) {
 | 
						|
	    fprintf(stderr, "man: cannot fork: %s\n", strerror(errno));
 | 
						|
	    exit(1);
 | 
						|
	}
 | 
						|
	if (pid == 0) {
 | 
						|
	    /* Child. */
 | 
						|
	    if (set_mp) {
 | 
						|
		mp= malloc((8 + strlen(manpath) + 1) * sizeof(*mp));
 | 
						|
		if (mp != nil) {
 | 
						|
		    strcpy(mp, "MANPATH=");
 | 
						|
		    strcat(mp, manpath);
 | 
						|
		    (void) putenv(mp);
 | 
						|
		}
 | 
						|
	    }
 | 
						|
 | 
						|
	    if (fd0 != 0) {
 | 
						|
		dup2(fd0, 0);
 | 
						|
		close(fd0);
 | 
						|
	    }
 | 
						|
	    if (!last) {
 | 
						|
		close(pfd[0]);
 | 
						|
		if (pfd[1] != 1) {
 | 
						|
		    dup2(pfd[1], 1);
 | 
						|
		    close(pfd[1]);
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	    close(err[0]);
 | 
						|
	    execvp(argv[0], argv);
 | 
						|
	    (void) write(err[1], &errno, sizeof(errno));
 | 
						|
	    _exit(1);
 | 
						|
	}
 | 
						|
 | 
						|
	close(err[1]);
 | 
						|
	if (read(err[0], &errno, sizeof(errno)) != 0) {
 | 
						|
	    fprintf(stderr, "man: %s: %s\n", argv[0],
 | 
						|
			    strerror(errno));
 | 
						|
	    exit(1);
 | 
						|
	}
 | 
						|
	close(err[0]);
 | 
						|
 | 
						|
	if (!last) {
 | 
						|
	    close(pfd[1]);
 | 
						|
	    fd0= pfd[0];
 | 
						|
	}
 | 
						|
	set_mp= 0;
 | 
						|
    }
 | 
						|
 | 
						|
    /* Wait for the last command to finish. */
 | 
						|
    isav= signal(SIGINT, SIG_IGN);
 | 
						|
    qsav= signal(SIGQUIT, SIG_IGN);
 | 
						|
    tsav= signal(SIGTERM, SIG_IGN);
 | 
						|
    while ((r= wait(&status)) != pid) {
 | 
						|
	if (r < 0) {
 | 
						|
	    fprintf(stderr, "man: wait(): %s\n", strerror(errno));
 | 
						|
	    exit(1);
 | 
						|
	}
 | 
						|
    }
 | 
						|
    (void) signal(SIGINT, isav);
 | 
						|
    (void) signal(SIGQUIT, qsav);
 | 
						|
    (void) signal(SIGTERM, tsav);
 | 
						|
    if (status != 0) exit(1);
 | 
						|
    plast= pipeline;
 | 
						|
}
 | 
						|
 | 
						|
void keyword(char *keyword)
 | 
						|
/* Make an apropos(1) or whatis(1) call. */
 | 
						|
{
 | 
						|
    putinline(apropos ? "apropos" : "whatis",
 | 
						|
		all ? "-a" : (char *) nil,
 | 
						|
		(char *) nil);
 | 
						|
 | 
						|
    if (tty) {
 | 
						|
	printf("Looking for keyword '%s'\n", keyword);
 | 
						|
	fflush(stdout);
 | 
						|
    }
 | 
						|
 | 
						|
    execute(1, keyword);
 | 
						|
}
 | 
						|
 | 
						|
enum pagetype { CAT, CATZ, MAN, MANZ, SMAN, SMANZ };
 | 
						|
 | 
						|
int showpage(char *page, enum pagetype ptype, char *macros)
 | 
						|
/* Show a manual page if it exists using the proper decompression and
 | 
						|
 * formatting tools.
 | 
						|
 */
 | 
						|
{
 | 
						|
    struct stat st;
 | 
						|
 | 
						|
    /* We want a normal file without X bits if not a full path. */
 | 
						|
    if (stat(page, &st) < 0) return 0;
 | 
						|
 | 
						|
    if (!S_ISREG(st.st_mode)) return 0;
 | 
						|
    if ((st.st_mode & 0111) && page[0] != '/') return 0;
 | 
						|
 | 
						|
    /* Do we only care if it exists? */
 | 
						|
    if (quiet) { shown= 1; return 1; }
 | 
						|
 | 
						|
    if (ptype == CATZ || ptype == MANZ || ptype == SMANZ) {
 | 
						|
	putinline("zcat", (char *) nil);
 | 
						|
    }
 | 
						|
 | 
						|
    if (ptype == SMAN || ptype == SMANZ) {
 | 
						|
	/* Change SGML into regular *roff. */
 | 
						|
	putinline("/usr/lib/sgml/sgml2roff", (char *) nil);
 | 
						|
	putinline("tbl", (char *) nil);
 | 
						|
	putinline("eqn", (char *) nil);
 | 
						|
    }
 | 
						|
 | 
						|
    if (ptype == MAN) {
 | 
						|
	/* Do we need tbl? */
 | 
						|
	FILE *fp;
 | 
						|
	int c;
 | 
						|
	char *tp = TBL_MAGIC;
 | 
						|
 | 
						|
	if ((fp = fopen(page, "r")) == nil) {
 | 
						|
	    fprintf(stderr, "man: %s: %s\n", page, strerror(errno));
 | 
						|
	    exit(1);
 | 
						|
	}
 | 
						|
	c= fgetc(fp);
 | 
						|
	for (;;) {
 | 
						|
	    if (c == *tp || (c == '\'' && *tp == '.')) {
 | 
						|
		if (*++tp == 0) {
 | 
						|
		    /* A match, add tbl. */
 | 
						|
		    putinline("tbl", (char *) nil);
 | 
						|
		    break;
 | 
						|
		}
 | 
						|
	    } else {
 | 
						|
		/* No match. */
 | 
						|
		break;
 | 
						|
	    }
 | 
						|
	    while ((c = fgetc(fp)) == ' ' || c == '\t') {}
 | 
						|
	}
 | 
						|
	fclose(fp);
 | 
						|
    }
 | 
						|
 | 
						|
    if (ptype == MAN || ptype == MANZ || ptype == SMAN || ptype == SMANZ) {
 | 
						|
	putinline(roff[rofftype], macros, (char *) nil);
 | 
						|
    }
 | 
						|
 | 
						|
    if (tty) {
 | 
						|
	printf("%s %s\n",
 | 
						|
	    ptype == CAT || ptype == CATZ ? "Showing" : "Formatting", page);
 | 
						|
	fflush(stdout);
 | 
						|
    }
 | 
						|
    execute(0, page);
 | 
						|
 | 
						|
    shown= 1;
 | 
						|
    return 1;
 | 
						|
}
 | 
						|
 | 
						|
int member(char *word, char *list)
 | 
						|
/* True if word is a member of a comma separated list. */
 | 
						|
{
 | 
						|
    size_t len= strlen(word);
 | 
						|
 | 
						|
    if (list == ALL) return 1;
 | 
						|
 | 
						|
    while (*list != 0) {
 | 
						|
	if (strncmp(word, list, len) == 0
 | 
						|
		&& (list[len] == 0 || list[len] == ','))
 | 
						|
	    return 1;
 | 
						|
	while (*list != 0 && *list != ',') list++;
 | 
						|
	if (*list == ',') list++;
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
int trymandir(char *mandir, char *title, char *section)
 | 
						|
/* Search the whatis file of the manual directory for a page of the given
 | 
						|
 * section and display it.
 | 
						|
 */
 | 
						|
{
 | 
						|
    FILE *wf;
 | 
						|
    char whatis[1024], pagename[1024], *wpage, *wsection;
 | 
						|
    int rsw, rsp;
 | 
						|
    int ntries;
 | 
						|
    int (*searchidx)(FILE *, char *, char **, char **);
 | 
						|
    struct searchnames {
 | 
						|
	enum pagetype	ptype;
 | 
						|
	char		*pathfmt;
 | 
						|
    } *sp;
 | 
						|
    static struct searchnames searchN[] = {
 | 
						|
	{ CAT,	"%s/cat%s/%s.%s"	},	/* SysV */
 | 
						|
	{ CATZ,	"%s/cat%s/%s.%s.Z"	},
 | 
						|
	{ MAN,	"%s/man%s/%s.%s"	},
 | 
						|
	{ MANZ,	"%s/man%s/%s.%s.Z"	},
 | 
						|
	{ SMAN,	"%s/sman%s/%s.%s"	},	/* Solaris */
 | 
						|
	{ SMANZ,"%s/sman%s/%s.%s.Z"	},
 | 
						|
	{ CAT,	"%s/cat%.1s/%s.%s"	},	/* BSD */
 | 
						|
	{ CATZ,	"%s/cat%.1s/%s.%s.Z"	},
 | 
						|
	{ MAN,	"%s/man%.1s/%s.%s"	},
 | 
						|
	{ MANZ,	"%s/man%.1s/%s.%s.Z"	},
 | 
						|
    };
 | 
						|
 | 
						|
    if (strlen(mandir) + 1 + 6 + 1 > arraysize(whatis)) return 0;
 | 
						|
 | 
						|
    /* Prefer a fast windex database if available. */
 | 
						|
    sprintf(whatis, "%s/windex", mandir);
 | 
						|
 | 
						|
    if ((wf= fopen(whatis, "r")) != nil) {
 | 
						|
	searchidx= searchwindex;
 | 
						|
    } else {
 | 
						|
	/* Use a classic whatis database. */
 | 
						|
	sprintf(whatis, "%s/whatis", mandir);
 | 
						|
 | 
						|
	if ((wf= fopen(whatis, "r")) == nil) return 0;
 | 
						|
	searchidx= searchwhatis;
 | 
						|
    }
 | 
						|
 | 
						|
    rsp= 0;
 | 
						|
    while (!rsp && (rsw= (*searchidx)(wf, title, &wpage, &wsection)) == 1) {
 | 
						|
	if (!member(wsection, section)) continue;
 | 
						|
 | 
						|
	/* When looking for getc(1S) we try:
 | 
						|
	 *	cat1s/getc.1s
 | 
						|
	 *	cat1s/getc.1s.Z
 | 
						|
	 *	man1s/getc.1s
 | 
						|
	 *	man1s/getc.1s.Z
 | 
						|
	 *	sman1s/getc.1s
 | 
						|
	 *	sman1s/getc.1s.Z
 | 
						|
	 *	cat1/getc.1s
 | 
						|
	 *	cat1/getc.1s.Z
 | 
						|
	 *	man1/getc.1s
 | 
						|
	 *	man1/getc.1s.Z
 | 
						|
	 */
 | 
						|
 | 
						|
	if (strlen(mandir) + 2 * strlen(wsection) + strlen(wpage)
 | 
						|
		    + 10 > arraysize(pagename))
 | 
						|
	    continue;
 | 
						|
 | 
						|
	sp= searchN;
 | 
						|
	ntries= arraysize(searchN);
 | 
						|
	do {
 | 
						|
	    if (sp->ptype <= CATZ && rofftype != NROFF)
 | 
						|
		continue;
 | 
						|
 | 
						|
	    sprintf(pagename, sp->pathfmt,
 | 
						|
		mandir, wsection, wpage, wsection);
 | 
						|
 | 
						|
	    rsp= showpage(pagename, sp->ptype,
 | 
						|
		(SEC1xSPECIAL && strcmp(wsection, "1x") == 0) ? "-mnx" : "-man");
 | 
						|
	} while (sp++, !rsp && --ntries != 0);
 | 
						|
 | 
						|
	if (all) rsp= 0;
 | 
						|
    }
 | 
						|
    if (rsw < 0 && ferror(wf)) {
 | 
						|
	fprintf(stderr, "man: %s: %s\n", whatis, strerror(errno));
 | 
						|
	exit(1);
 | 
						|
    }
 | 
						|
    fclose(wf);
 | 
						|
    return rsp;
 | 
						|
}
 | 
						|
 | 
						|
int trysubmandir(char *mandir, char *title, char *section)
 | 
						|
/* Search the subdirectories of this manual directory for whatis files, they
 | 
						|
 * may have manual pages that override the ones in the major directory.
 | 
						|
 */
 | 
						|
{
 | 
						|
    char submandir[1024];
 | 
						|
    DIR *md;
 | 
						|
    struct dirent *entry;
 | 
						|
 | 
						|
    if ((md= opendir(mandir)) == nil) return 0;
 | 
						|
 | 
						|
    while ((entry= readdir(md)) != nil) {
 | 
						|
	if (strcmp(entry->d_name, ".") == 0
 | 
						|
	    || strcmp(entry->d_name, "..") == 0) continue;
 | 
						|
	if ((strncmp(entry->d_name, "man", 3) == 0
 | 
						|
	    || strncmp(entry->d_name, "cat", 3) == 0)
 | 
						|
	    && between('0', entry->d_name[3], '9')) continue;
 | 
						|
 | 
						|
	if (strlen(mandir) + 1 + strlen(entry->d_name) + 1
 | 
						|
		    > arraysize(submandir)) continue;
 | 
						|
 | 
						|
	sprintf(submandir, "%s/%s", mandir, entry->d_name);
 | 
						|
 | 
						|
	if (trymandir(submandir, title, section) && !all) {
 | 
						|
	    closedir(md);
 | 
						|
	    return 1;
 | 
						|
	}
 | 
						|
    }
 | 
						|
    closedir(md);
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
void searchmanpath(char *title, char *section)
 | 
						|
/* Search the manual path for a manual page describing "title." */
 | 
						|
{
 | 
						|
    char mandir[1024];
 | 
						|
    char *pp= manpath, *pd;
 | 
						|
 | 
						|
    for (;;) {
 | 
						|
	while (*pp != 0 && *pp == ':') pp++;
 | 
						|
 | 
						|
	if (*pp == 0) break;
 | 
						|
 | 
						|
	pd= mandir;
 | 
						|
	while (*pp != 0 && *pp != ':') {
 | 
						|
	    if (pd < arraylimit(mandir)) *pd++= *pp;
 | 
						|
	    pp++;
 | 
						|
	}
 | 
						|
	if (pd == arraylimit(mandir)) continue;		/* forget it */
 | 
						|
 | 
						|
	*pd= 0;
 | 
						|
	if (trysubmandir(mandir, title, section) && !all) break;
 | 
						|
	if (trymandir(mandir, title, section) && !all) break;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void usage(void)
 | 
						|
{
 | 
						|
    fprintf(stderr, "Usage: man -[antfkq] [-M path] [-s section] title ...\n");
 | 
						|
    exit(1);
 | 
						|
}
 | 
						|
 | 
						|
int main(int argc, char **argv)
 | 
						|
{
 | 
						|
    char *title, *section= ALL;
 | 
						|
    int i;
 | 
						|
    int nomoreopt= 0;
 | 
						|
    char *opt;
 | 
						|
 | 
						|
    if ((pager= getenv("PAGER")) == nil) pager= PAGER;
 | 
						|
    if ((manpath= getenv("MANPATH")) == nil) manpath= MANPATH;
 | 
						|
    tty= isatty(1);
 | 
						|
 | 
						|
    i= 1;
 | 
						|
    do {
 | 
						|
	while (i < argc && argv[i][0] == '-' && !nomoreopt) {
 | 
						|
	    opt= argv[i++]+1;
 | 
						|
	    if (opt[0] == '-' && opt[1] == 0) {
 | 
						|
		nomoreopt= 1;
 | 
						|
		break;
 | 
						|
	    }
 | 
						|
	    while (*opt != 0) {
 | 
						|
		switch (*opt++) {
 | 
						|
		case 'a':
 | 
						|
		    all= 1;
 | 
						|
		    break;
 | 
						|
		case 'f':
 | 
						|
		    whatis= 1;
 | 
						|
		    break;
 | 
						|
		case 'k':
 | 
						|
		    apropos= 1;
 | 
						|
		    break;
 | 
						|
		case 'q':
 | 
						|
		    quiet= 1;
 | 
						|
		    break;
 | 
						|
		case 'n':
 | 
						|
		    rofftype= NROFF;
 | 
						|
		    apropos= whatis= 0;
 | 
						|
		    break;
 | 
						|
		case 't':
 | 
						|
		    rofftype= TROFF;
 | 
						|
		    apropos= whatis= 0;
 | 
						|
		    break;
 | 
						|
		case 's':
 | 
						|
		    if (*opt == 0) {
 | 
						|
			if (i == argc) usage();
 | 
						|
			section= argv[i++];
 | 
						|
		    } else {
 | 
						|
			section= opt;
 | 
						|
			opt= "";
 | 
						|
		    }
 | 
						|
		    break;
 | 
						|
		case 'M':
 | 
						|
		    if (*opt == 0) {
 | 
						|
			if (i == argc) usage();
 | 
						|
			manpath= argv[i++];
 | 
						|
		    } else {
 | 
						|
			manpath= opt;
 | 
						|
			opt= "";
 | 
						|
		    }
 | 
						|
		    break;
 | 
						|
		default:
 | 
						|
		    usage();
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	}
 | 
						|
 | 
						|
	if (i >= argc) usage();
 | 
						|
 | 
						|
	if (between('0', argv[i][0], '9') && i+1 < argc) {
 | 
						|
	    /* Old BSD style section designation? */
 | 
						|
	    section= argv[i++];
 | 
						|
	}
 | 
						|
	if (i == argc) usage();
 | 
						|
 | 
						|
	title= argv[i++];
 | 
						|
 | 
						|
	if (whatis || apropos) {
 | 
						|
	    keyword(title);
 | 
						|
	} else {
 | 
						|
	    shown= 0;
 | 
						|
	    searchmanpath(title, section);
 | 
						|
 | 
						|
	    if (!shown) (void) showpage(title, MAN, "-man");
 | 
						|
 | 
						|
	    if (!shown) {
 | 
						|
		if (!quiet) {
 | 
						|
		    fprintf(stderr,
 | 
						|
			"man: no manual on %s\n",
 | 
						|
			title);
 | 
						|
		}
 | 
						|
		exit(1);
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    } while (i < argc);
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 |