00001
00002
00003
00004 #define nil NULL
00005 #include <sys/types.h>
00006 #include <stdio.h>
00007 #include <stdlib.h>
00008 #include <dirent.h>
00009 #include <string.h>
00010 #include <errno.h>
00011 #include <unistd.h>
00012 #include <stdarg.h>
00013 #include <fcntl.h>
00014 #include <signal.h>
00015 #include <sys/stat.h>
00016 #include <sys/wait.h>
00017
00018
00019 char MANPATH[]= "/usr/local/man:/usr/man:/usr/gnu/man";
00020 char PAGER[]= "more";
00021
00022
00023 char TBL_MAGIC[] = ".\\\"t\n";
00024
00025 #define arraysize(a) (sizeof(a) / sizeof((a)[0]))
00026 #define arraylimit(a) ((a) + arraysize(a))
00027 #define between(a, c, z) ((unsigned) ((c) - (a)) <= (unsigned) ((z) - (a)))
00028
00029
00030 #if __minix
00031 #define SEC9SPECIAL 1
00032 #else
00033 #define SEC9SPECIAL 0
00034 #endif
00035
00036 int searchwhatis(FILE *wf, char *title, char **ppage, char **psection)
00037
00038
00039
00040
00041 {
00042 static char page[256], section[32];
00043 char alias[256];
00044 int found= 0;
00045 int c;
00046
00047
00048
00049
00050
00051
00052 do {
00053 int first= 1;
00054 char *pc= section;
00055
00056 c= fgetc(wf);
00057
00058
00059 do {
00060 char *pa= alias;
00061
00062 while (c == ' ' || c == '\t' || c == ',') c= fgetc(wf);
00063
00064 while (c != ' ' && c != '\t' && c != ','
00065 && c != '(' && c != '\n' && c != EOF
00066 ) {
00067 if (pa < arraylimit(alias)-1) *pa++= c;
00068 c= getc(wf);
00069 }
00070 *pa= 0;
00071 if (first) { strcpy(page, alias); first= 0; }
00072
00073 if (strcmp(alias, title) == 0) found= 1;
00074 } while (c != '(' && c != '\n' && c != EOF);
00075
00076 if (c != '(') {
00077 found= 0;
00078 } else {
00079 while ((c= fgetc(wf)) != ')' && c != '\n' && c != EOF) {
00080 if ('A' <= c && c <= 'Z') c= c - 'A' + 'a';
00081 if (pc < arraylimit(section)-1) *pc++= c;
00082 }
00083 *pc= 0;
00084 if (c != ')' || pc == section) found= 0;
00085 }
00086 while (c != EOF && c != '\n') c= getc(wf);
00087 } while (!found && c != EOF);
00088
00089 if (found) {
00090 *ppage= page;
00091 *psection= section;
00092 }
00093 return c == EOF ? -1 : found;
00094 }
00095
00096 int searchwindex(FILE *wf, char *title, char **ppage, char **psection)
00097
00098
00099
00100
00101 {
00102 static char page[256], section[32];
00103 static long low, high;
00104 long mid0, mid1;
00105 int c;
00106 unsigned char *pt;
00107 char *pc;
00108
00109
00110
00111
00112
00113
00114 if (ftell(wf) == 0) {
00115
00116 low= 0;
00117 fseek(wf, (off_t) 0, SEEK_END);
00118 high= ftell(wf);
00119 }
00120
00121
00122 while (low <= high) {
00123 pt= (unsigned char *) title;
00124
00125 mid0= mid1= (low + high) >> 1;
00126 if (mid0 == 0) {
00127 if (fseek(wf, (off_t) 0, SEEK_SET) != 0)
00128 return -1;
00129 } else {
00130 if (fseek(wf, (off_t) mid0 - 1, SEEK_SET) != 0)
00131 return -1;
00132
00133
00134 while ((c= getc(wf)) != EOF && c != '\n')
00135 mid1++;
00136 if (ferror(wf)) return -1;
00137 }
00138
00139
00140 for (;;) {
00141 if ((c= getc(wf)) == ' ' || c == '\t') c= 0;
00142 if (c == 0 || c != *pt) break;
00143 pt++;
00144 }
00145
00146
00147 if (c == EOF || *pt <= c) {
00148 high= mid0 - 1;
00149 } else {
00150 low= mid1 + 1;
00151 }
00152 }
00153
00154
00155 if (fseek(wf, (off_t) low, SEEK_SET) != 0)
00156 return -1;
00157
00158 do {
00159 if (low != 0) {
00160
00161 while ((c= getc(wf)) != EOF && c != '\n')
00162 low++;
00163 if (ferror(wf)) return -1;
00164 }
00165
00166
00167 pt= (unsigned char *) title;
00168
00169 for (;;) {
00170 if ((c= getc(wf)) == EOF) return 0;
00171 low++;
00172 if (c == ' ' || c == '\t') c= 0;
00173 if (c == 0 || c != *pt) break;
00174 pt++;
00175 }
00176 } while (c < *pt);
00177
00178 if (*pt != c) return 0;
00179
00180
00181 while ((c= fgetc(wf)) == ' ' || c == '\t') {}
00182
00183 pc= page;
00184 while (c != ' ' && c != '\t' && c != '(' && c != '\n' && c != EOF) {
00185 if (pc < arraylimit(page)-1) *pc++= c;
00186 c= getc(wf);
00187 }
00188 if (pc == page) return 0;
00189 *pc= 0;
00190
00191 while (c == ' ' || c == '\t') c= fgetc(wf);
00192
00193 if (c != '(') return 0;
00194
00195 pc= section;
00196 while ((c= fgetc(wf)) != ')' && c != '\n' && c != EOF) {
00197 if ('A' <= c && c <= 'Z') c= c - 'A' + 'a';
00198 if (pc < arraylimit(section)-1) *pc++= c;
00199 }
00200 *pc= 0;
00201 if (c != ')' || pc == section) return 0;
00202
00203 while (c != EOF && c != '\n') c= getc(wf);
00204 if (c != '\n') return 0;
00205
00206 *ppage= page;
00207 *psection= section;
00208 return 1;
00209 }
00210
00211 char ALL[]= "";
00212
00213 int all= 0;
00214 int whatis= 0;
00215 int apropos= 0;
00216 int quiet= 0;
00217 enum ROFF { NROFF, TROFF } rofftype= NROFF;
00218 char *roff[] = { "nroff", "troff" };
00219
00220 int shown;
00221 int tty;
00222 char *manpath;
00223 char *pager;
00224
00225 char *pipeline[8][8];
00226 char *(*plast)[8] = pipeline;
00227
00228 void putinline(char *arg1, ...)
00229
00230 {
00231 va_list ap;
00232 char **argv;
00233
00234 argv= *plast++;
00235 *argv++= arg1;
00236
00237 va_start(ap, arg1);
00238 while ((*argv++= va_arg(ap, char *)) != nil) {}
00239 va_end(ap);
00240 }
00241
00242 void execute(int set_mp, char *file)
00243
00244
00245
00246 {
00247 char *(*plp)[8], **argv;
00248 char *mp;
00249 int fd0, pfd[2], err[2];
00250 pid_t pid;
00251 int r, status;
00252 int last;
00253 void (*isav)(int sig), (*qsav)(int sig), (*tsav)(int sig);
00254
00255 if (tty) {
00256
00257 putinline(pager, (char *) nil);
00258 }
00259 if (plast == pipeline) {
00260
00261 putinline("cat", (char *) nil);
00262 }
00263
00264
00265 argv= pipeline[0];
00266 while (*argv != nil) argv++;
00267 *argv++= file;
00268 *argv= nil;
00269
00270
00271 fd0= 0;
00272 for (plp= pipeline; plp < plast; plp++) {
00273 argv= *plp;
00274 last= (plp+1 == plast);
00275
00276
00277 if (pipe(err) < 0 || (!last && pipe(pfd) < 0)) {
00278 fprintf(stderr, "man: can't create a pipe: %s\n", strerror(errno));
00279 exit(1);
00280 }
00281
00282 (void) fcntl(err[1], F_SETFD, fcntl(err[1], F_GETFD) | FD_CLOEXEC);
00283
00284 if ((pid = fork()) < 0) {
00285 fprintf(stderr, "man: cannot fork: %s\n", strerror(errno));
00286 exit(1);
00287 }
00288 if (pid == 0) {
00289
00290 if (set_mp) {
00291 mp= malloc((8 + strlen(manpath) + 1) * sizeof(*mp));
00292 if (mp != nil) {
00293 strcpy(mp, "MANPATH=");
00294 strcat(mp, manpath);
00295 (void) putenv(mp);
00296 }
00297 }
00298
00299 if (fd0 != 0) {
00300 dup2(fd0, 0);
00301 close(fd0);
00302 }
00303 if (!last) {
00304 close(pfd[0]);
00305 if (pfd[1] != 1) {
00306 dup2(pfd[1], 1);
00307 close(pfd[1]);
00308 }
00309 }
00310 close(err[0]);
00311 execvp(argv[0], argv);
00312 (void) write(err[1], &errno, sizeof(errno));
00313 _exit(1);
00314 }
00315
00316 close(err[1]);
00317 if (read(err[0], &errno, sizeof(errno)) != 0) {
00318 fprintf(stderr, "man: %s: %s\n", argv[0],
00319 strerror(errno));
00320 exit(1);
00321 }
00322 close(err[0]);
00323
00324 if (!last) {
00325 close(pfd[1]);
00326 fd0= pfd[0];
00327 }
00328 set_mp= 0;
00329 }
00330
00331
00332 isav= signal(SIGINT, SIG_IGN);
00333 qsav= signal(SIGQUIT, SIG_IGN);
00334 tsav= signal(SIGTERM, SIG_IGN);
00335 while ((r= wait(&status)) != pid) {
00336 if (r < 0) {
00337 fprintf(stderr, "man: wait(): %s\n", strerror(errno));
00338 exit(1);
00339 }
00340 }
00341 (void) signal(SIGINT, isav);
00342 (void) signal(SIGQUIT, qsav);
00343 (void) signal(SIGTERM, tsav);
00344 if (status != 0) exit(1);
00345 plast= pipeline;
00346 }
00347
00348 void keyword(char *keyword)
00349
00350 {
00351 putinline(apropos ? "apropos" : "whatis",
00352 all ? "-a" : (char *) nil,
00353 (char *) nil);
00354
00355 if (tty) {
00356 printf("Looking for keyword '%s'\n", keyword);
00357 fflush(stdout);
00358 }
00359
00360 execute(1, keyword);
00361 }
00362
00363 enum pagetype { CAT, CATZ, MAN, MANZ, SMAN, SMANZ };
00364
00365 int showpage(char *page, enum pagetype ptype, char *macros)
00366
00367
00368
00369 {
00370 struct stat st;
00371
00372
00373 if (stat(page, &st) < 0) return 0;
00374
00375 if (!S_ISREG(st.st_mode)) return 0;
00376 if ((st.st_mode & 0111) && page[0] != '/') return 0;
00377
00378
00379 if (quiet) { shown= 1; return 1; }
00380
00381 if (ptype == CATZ || ptype == MANZ || ptype == SMANZ) {
00382 putinline("zcat", (char *) nil);
00383 }
00384
00385 if (ptype == SMAN || ptype == SMANZ) {
00386
00387 putinline("/usr/lib/sgml/sgml2roff", (char *) nil);
00388 putinline("tbl", (char *) nil);
00389 putinline("eqn", (char *) nil);
00390 }
00391
00392 if (ptype == MAN) {
00393
00394 FILE *fp;
00395 int c;
00396 char *tp = TBL_MAGIC;
00397
00398 if ((fp = fopen(page, "r")) == nil) {
00399 fprintf(stderr, "man: %s: %s\n", page, strerror(errno));
00400 exit(1);
00401 }
00402 c= fgetc(fp);
00403 for (;;) {
00404 if (c == *tp || (c == '\'' && *tp == '.')) {
00405 if (*++tp == 0) {
00406
00407 putinline("tbl", (char *) nil);
00408 break;
00409 }
00410 } else {
00411
00412 break;
00413 }
00414 while ((c = fgetc(fp)) == ' ' || c == '\t') {}
00415 }
00416 fclose(fp);
00417 }
00418
00419 if (ptype == MAN || ptype == MANZ || ptype == SMAN || ptype == SMANZ) {
00420 putinline(roff[rofftype], macros, (char *) nil);
00421 }
00422
00423 if (tty) {
00424 printf("%s %s\n",
00425 ptype == CAT || ptype == CATZ ? "Showing" : "Formatting", page);
00426 fflush(stdout);
00427 }
00428 execute(0, page);
00429
00430 shown= 1;
00431 return 1;
00432 }
00433
00434 int member(char *word, char *list)
00435
00436 {
00437 size_t len= strlen(word);
00438
00439 if (list == ALL) return 1;
00440
00441 while (*list != 0) {
00442 if (strncmp(word, list, len) == 0
00443 && (list[len] == 0 || list[len] == ','))
00444 return 1;
00445 while (*list != 0 && *list != ',') list++;
00446 if (*list == ',') list++;
00447 }
00448 return 0;
00449 }
00450
00451 int trymandir(char *mandir, char *title, char *section)
00452
00453
00454
00455 {
00456 FILE *wf;
00457 char whatis[1024], pagename[1024], *wpage, *wsection;
00458 int rsw, rsp;
00459 int ntries;
00460 int (*searchidx)(FILE *, char *, char **, char **);
00461 struct searchnames {
00462 enum pagetype ptype;
00463 char *pathfmt;
00464 } *sp;
00465 static struct searchnames searchN[] = {
00466 { CAT, "%s/cat%s/%s.%s" },
00467 { CATZ, "%s/cat%s/%s.%s.Z" },
00468 { MAN, "%s/man%s/%s.%s" },
00469 { MANZ, "%s/man%s/%s.%s.Z" },
00470 { SMAN, "%s/sman%s/%s.%s" },
00471 { SMANZ,"%s/sman%s/%s.%s.Z" },
00472 { CAT, "%s/cat%.1s/%s.%s" },
00473 { CATZ, "%s/cat%.1s/%s.%s.Z" },
00474 { MAN, "%s/man%.1s/%s.%s" },
00475 { MANZ, "%s/man%.1s/%s.%s.Z" },
00476 };
00477
00478 if (strlen(mandir) + 1 + 6 + 1 > arraysize(whatis)) return 0;
00479
00480
00481 sprintf(whatis, "%s/windex", mandir);
00482
00483 if ((wf= fopen(whatis, "r")) != nil) {
00484 searchidx= searchwindex;
00485 } else {
00486
00487 sprintf(whatis, "%s/whatis", mandir);
00488
00489 if ((wf= fopen(whatis, "r")) == nil) return 0;
00490 searchidx= searchwhatis;
00491 }
00492
00493 rsp= 0;
00494 while (!rsp && (rsw= (*searchidx)(wf, title, &wpage, &wsection)) == 1) {
00495 if (!member(wsection, section)) continue;
00496
00497
00498
00499
00500
00501
00502
00503
00504
00505
00506
00507
00508
00509
00510 if (strlen(mandir) + 2 * strlen(wsection) + strlen(wpage)
00511 + 10 > arraysize(pagename))
00512 continue;
00513
00514 sp= searchN;
00515 ntries= arraysize(searchN);
00516 do {
00517 if (sp->ptype <= CATZ && rofftype != NROFF)
00518 continue;
00519
00520 sprintf(pagename, sp->pathfmt,
00521 mandir, wsection, wpage, wsection);
00522
00523 rsp= showpage(pagename, sp->ptype,
00524 (SEC9SPECIAL && strcmp(wsection, "9") == 0) ? "-mnx" : "-man");
00525 } while (sp++, !rsp && --ntries != 0);
00526
00527 if (all) rsp= 0;
00528 }
00529 if (rsw < 0 && ferror(wf)) {
00530 fprintf(stderr, "man: %s: %s\n", whatis, strerror(errno));
00531 exit(1);
00532 }
00533 fclose(wf);
00534 return rsp;
00535 }
00536
00537 int trysubmandir(char *mandir, char *title, char *section)
00538
00539
00540
00541 {
00542 char submandir[1024];
00543 DIR *md;
00544 struct dirent *entry;
00545
00546 if ((md= opendir(mandir)) == nil) return 0;
00547
00548 while ((entry= readdir(md)) != nil) {
00549 if (strcmp(entry->d_name, ".") == 0
00550 || strcmp(entry->d_name, "..") == 0) continue;
00551 if ((strncmp(entry->d_name, "man", 3) == 0
00552 || strncmp(entry->d_name, "cat", 3) == 0)
00553 && between('0', entry->d_name[3], '9')) continue;
00554
00555 if (strlen(mandir) + 1 + strlen(entry->d_name) + 1
00556 > arraysize(submandir)) continue;
00557
00558 sprintf(submandir, "%s/%s", mandir, entry->d_name);
00559
00560 if (trymandir(submandir, title, section) && !all) {
00561 closedir(md);
00562 return 1;
00563 }
00564 }
00565 closedir(md);
00566
00567 return 0;
00568 }
00569
00570 void searchmanpath(char *title, char *section)
00571
00572 {
00573 char mandir[1024];
00574 char *pp= manpath, *pd;
00575
00576 for (;;) {
00577 while (*pp != 0 && *pp == ':') pp++;
00578
00579 if (*pp == 0) break;
00580
00581 pd= mandir;
00582 while (*pp != 0 && *pp != ':') {
00583 if (pd < arraylimit(mandir)) *pd++= *pp;
00584 pp++;
00585 }
00586 if (pd == arraylimit(mandir)) continue;
00587
00588 *pd= 0;
00589 if (trysubmandir(mandir, title, section) && !all) break;
00590 if (trymandir(mandir, title, section) && !all) break;
00591 }
00592 }
00593
00594 void usage(void)
00595 {
00596 fprintf(stderr, "Usage: man -[antfkq] [-M path] [-s section] title ...\n");
00597 exit(1);
00598 }
00599
00600 int main(int argc, char **argv)
00601 {
00602 char *title, *section= ALL;
00603 int i;
00604 int nomoreopt= 0;
00605 char *opt;
00606
00607 if ((pager= getenv("PAGER")) == nil) pager= PAGER;
00608 if ((manpath= getenv("MANPATH")) == nil) manpath= MANPATH;
00609 tty= isatty(1);
00610
00611 i= 1;
00612 do {
00613 while (i < argc && argv[i][0] == '-' && !nomoreopt) {
00614 opt= argv[i++]+1;
00615 if (opt[0] == '-' && opt[1] == 0) {
00616 nomoreopt= 1;
00617 break;
00618 }
00619 while (*opt != 0) {
00620 switch (*opt++) {
00621 case 'a':
00622 all= 1;
00623 break;
00624 case 'f':
00625 whatis= 1;
00626 break;
00627 case 'k':
00628 apropos= 1;
00629 break;
00630 case 'q':
00631 quiet= 1;
00632 break;
00633 case 'n':
00634 rofftype= NROFF;
00635 apropos= whatis= 0;
00636 break;
00637 case 't':
00638 rofftype= TROFF;
00639 apropos= whatis= 0;
00640 break;
00641 case 's':
00642 if (*opt == 0) {
00643 if (i == argc) usage();
00644 section= argv[i++];
00645 } else {
00646 section= opt;
00647 opt= "";
00648 }
00649 break;
00650 case 'M':
00651 if (*opt == 0) {
00652 if (i == argc) usage();
00653 manpath= argv[i++];
00654 } else {
00655 manpath= opt;
00656 opt= "";
00657 }
00658 break;
00659 default:
00660 usage();
00661 }
00662 }
00663 }
00664
00665 if (i >= argc) usage();
00666
00667 if (between('0', argv[i][0], '9') && argv[i][1] == 0) {
00668
00669 section= argv[i++];
00670 }
00671 if (i == argc) usage();
00672
00673 title= argv[i++];
00674
00675 if (whatis || apropos) {
00676 keyword(title);
00677 } else {
00678 shown= 0;
00679 searchmanpath(title, section);
00680
00681 if (!shown) (void) showpage(title, MAN, "-man");
00682
00683 if (!shown) {
00684 if (!quiet) {
00685 fprintf(stderr,
00686 "man: no manual on %s\n",
00687 title);
00688 }
00689 exit(1);
00690 }
00691 }
00692 } while (i < argc);
00693
00694 return 0;
00695 }