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;
 | |
| }
 | 
