+++ /dev/null
-/* 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;
-}