1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
  24  * Copyright 2012, Josef 'Jeff' Sipek <jeffpc@31bits.net>. All rights reserved.
  25  * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
  26  * Copyright 2014 Garrett D'Amore <garrett@damore.org>
  27  */
  28 
  29 /*      Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989  AT&T.       */
  30 /*              All rights reserved.                                    */
  31 
  32 /*
  33  * University Copyright- Copyright (c) 1982, 1986, 1988
  34  * The Regents of the University of California
  35  * All Rights Reserved
  36  *
  37  * University Acknowledgment- Portions of this document are derived from
  38  * software developed by the University of California, Berkeley, and its
  39  * contributors.
  40  */
  41 
  42 /*
  43  * Find and display reference manual pages. This version includes makewhatis
  44  * functionality as well.
  45  */
  46 
  47 #include <sys/param.h>
  48 #include <sys/stat.h>
  49 #include <sys/termios.h>
  50 #include <sys/types.h>
  51 
  52 #include <ctype.h>
  53 #include <dirent.h>
  54 #include <err.h>
  55 #include <errno.h>
  56 #include <fcntl.h>
  57 #include <fnmatch.h>
  58 #include <limits.h>
  59 #include <locale.h>
  60 #include <malloc.h>
  61 #include <memory.h>
  62 #include <regex.h>
  63 #include <stdio.h>
  64 #include <stdlib.h>
  65 #include <string.h>
  66 #include <unistd.h>
  67 
  68 #include "man.h"
  69 
  70 
  71 /* Mapping of old directories to new directories */
  72 static const struct map_entry {
  73         char    *old_name;
  74         char    *new_name;
  75 } map[] = {
  76         { "3b",         "3ucb"          },
  77         { "3e",         "3elf"          },
  78         { "3g",         "3gen"          },
  79         { "3k",         "3kstat"        },
  80         { "3n",         "3socket"       },
  81         { "3r",         "3rt"           },
  82         { "3s",         "3c"            },
  83         { "3t",         "3thr"          },
  84         { "3x",         "3curses"       },
  85         { "3xc",        "3xcurses"      },
  86         { "3xn",        "3xnet"         },
  87         { NULL,         NULL            }
  88 };
  89 
  90 struct suffix {
  91         char *ds;
  92         char *fs;
  93 };
  94 
  95 /*
  96  * Flags that control behavior of build_manpath()
  97  *
  98  *   BMP_ISPATH                 pathv is a vector constructed from PATH.
  99  *                              Perform appropriate path translations for
 100  *                              manpath.
 101  *   BMP_APPEND_DEFMANDIR       Add DEFMANDIR to the end if it hasn't
 102  *                              already appeared earlier.
 103  *   BMP_FALLBACK_DEFMANDIR     Append /usr/share/man only if no other
 104  *                              manpath (including derived from PATH)
 105  *                              elements are valid.
 106  */
 107 #define BMP_ISPATH              1
 108 #define BMP_APPEND_DEFMANDIR    2
 109 #define BMP_FALLBACK_DEFMANDIR  4
 110 
 111 /*
 112  * When doing equality comparisons of directories, device and inode
 113  * comparisons are done.  The secnode and dupnode structures are used
 114  * to form a list of lists for this processing.
 115  */
 116 struct secnode {
 117         char            *secp;
 118         struct secnode  *next;
 119 };
 120 struct dupnode {
 121         dev_t           dev;    /* from struct stat st_dev */
 122         ino_t           ino;    /* from struct stat st_ino */
 123         struct secnode  *secl;  /* sections already considered */
 124         struct dupnode  *next;
 125 };
 126 
 127 /*
 128  * Map directories that may appear in PATH to the corresponding
 129  * man directory.
 130  */
 131 static struct pathmap {
 132         char    *bindir;
 133         char    *mandir;
 134         dev_t   dev;
 135         ino_t   ino;
 136 } bintoman[] = {
 137         { "/sbin",              "/usr/share/man,1m",            0, 0 },
 138         { "/usr/sbin",          "/usr/share/man,1m",            0, 0 },
 139         { "/usr/ucb",           "/usr/share/man,1b",            0, 0 },
 140         { "/usr/bin",           "/usr/share/man,1,1m,1s,1t,1c", 0, 0 },
 141         { "/usr/xpg4/bin",      "/usr/share/man,1",             0, 0 },
 142         { "/usr/xpg6/bin",      "/usr/share/man,1",             0, 0 },
 143         { NULL,                 NULL,                           0, 0 }
 144 };
 145 
 146 struct man_node {
 147         char            *path;          /* mandir path */
 148         char            **secv;         /* submandir suffices */
 149         int             defsrch;        /* hint for man -p */
 150         int             frompath;       /* hint for man -d */
 151         struct man_node *next;
 152 };
 153 
 154 static int      all = 0;
 155 static int      apropos = 0;
 156 static int      debug = 0;
 157 static int      found = 0;
 158 static int      list = 0;
 159 static int      makewhatis = 0;
 160 static int      printmp = 0;
 161 static int      sargs = 0;
 162 static int      psoutput = 0;
 163 static int      lintout = 0;
 164 static int      whatis = 0;
 165 static int      makewhatishere = 0;
 166 
 167 static char     *mansec;
 168 static char     *pager = NULL;
 169 
 170 static char     *addlocale(char *);
 171 static struct man_node *build_manpath(char **, int);
 172 static void     do_makewhatis(struct man_node *);
 173 static char     *check_config(char *);
 174 static int      cmp(const void *, const void *);
 175 static int      dupcheck(struct man_node *, struct dupnode **);
 176 static int      format(char *, char *, char *, char *);
 177 static void     free_dupnode(struct dupnode *);
 178 static void     free_manp(struct man_node *manp);
 179 static void     freev(char **);
 180 static void     fullpaths(struct man_node **);
 181 static void     get_all_sect(struct man_node *);
 182 static int      getdirs(char *, char ***, int);
 183 static void     getpath(struct man_node *, char **);
 184 static void     getsect(struct man_node *, char **);
 185 static void     init_bintoman(void);
 186 static void     lower(char *);
 187 static void     mandir(char **, char *, char *, int);
 188 static int      manual(struct man_node *, char *);
 189 static char     *map_section(char *, char *);
 190 static char     *path_to_manpath(char *);
 191 static void     print_manpath(struct man_node *);
 192 static void     search_whatis(char *, char *);
 193 static int      searchdir(char *, char *, char *);
 194 static void     sortdir(DIR *, char ***);
 195 static char     **split(char *, char);
 196 static void     usage_man(void);
 197 static void     usage_whatapro(void);
 198 static void     usage_catman(void);
 199 static void     usage_makewhatis(void);
 200 static void     whatapro(struct man_node *, char *);
 201 
 202 static char     language[MAXPATHLEN];   /* LC_MESSAGES */
 203 static char     localedir[MAXPATHLEN];  /* locale specific path component */
 204 
 205 static char     *newsection = NULL;
 206 
 207 static int      manwidth = 0;
 208 
 209 extern const char       *__progname;
 210 
 211 int
 212 main(int argc, char **argv)
 213 {
 214         int             c, i;
 215         char            **pathv;
 216         char            *manpath = NULL;
 217         static struct man_node *mandirs = NULL;
 218         int             bmp_flags = 0;
 219         int             ret = 0;
 220         char            *opts;
 221         char            *mwstr;
 222         int             catman = 0;
 223 
 224         (void) setlocale(LC_ALL, "");
 225         (void) strcpy(language, setlocale(LC_MESSAGES, (char *)NULL));
 226         if (strcmp("C", language) != 0)
 227                 (void) strlcpy(localedir, language, MAXPATHLEN);
 228 
 229 #if !defined(TEXT_DOMAIN)
 230 #define TEXT_DOMAIN "SYS_TEST"
 231 #endif
 232         (void) textdomain(TEXT_DOMAIN);
 233 
 234         if (strcmp(__progname, "apropos") == 0) {
 235                 apropos++;
 236                 opts = "M:ds:";
 237         } else if (strcmp(__progname, "whatis") == 0) {
 238                 apropos++;
 239                 whatis++;
 240                 opts = "M:ds:";
 241         } else if (strcmp(__progname, "catman") == 0) {
 242                 catman++;
 243                 makewhatis++;
 244                 opts = "P:M:w";
 245         } else if (strcmp(__progname, "makewhatis") == 0) {
 246                 makewhatis++;
 247                 makewhatishere++;
 248                 manpath = ".";
 249                 opts = "";
 250         } else {
 251                 opts = "FM:P:T:adfklprs:tw";
 252                 if (argc > 1 && strcmp(argv[1], "-") == 0) {
 253                         pager = "cat";
 254                         optind++;
 255                 }
 256         }
 257 
 258         opterr = 0;
 259         while ((c = getopt(argc, argv, opts)) != -1) {
 260                 switch (c) {
 261                 case 'M':       /* Respecify path for man pages */
 262                         manpath = optarg;
 263                         break;
 264                 case 'a':
 265                         all++;
 266                         break;
 267                 case 'd':
 268                         debug++;
 269                         break;
 270                 case 'f':
 271                         whatis++;
 272                         /*FALLTHROUGH*/
 273                 case 'k':
 274                         apropos++;
 275                         break;
 276                 case 'l':
 277                         list++;
 278                         all++;
 279                         break;
 280                 case 'p':
 281                         printmp++;
 282                         break;
 283                 case 's':
 284                         mansec = optarg;
 285                         sargs++;
 286                         break;
 287                 case 'r':
 288                         lintout++;
 289                         break;
 290                 case 't':
 291                         psoutput++;
 292                         break;
 293                 case 'T':
 294                 case 'P':
 295                 case 'F':
 296                         /* legacy options, compatibility only and ignored */
 297                         break;
 298                 case 'w':
 299                         makewhatis++;
 300                         break;
 301                 case '?':
 302                 default:
 303                         if (apropos)
 304                                 usage_whatapro();
 305                         else if (catman)
 306                                 usage_catman();
 307                         else if (makewhatishere)
 308                                 usage_makewhatis();
 309                         else
 310                                 usage_man();
 311                 }
 312         }
 313         argc -= optind;
 314         argv += optind;
 315 
 316         if (argc == 0) {
 317                 if (apropos) {
 318                         (void) fprintf(stderr, gettext("%s what?\n"),
 319                             __progname);
 320                         exit(1);
 321                 } else if (!printmp && !makewhatis) {
 322                         (void) fprintf(stderr,
 323                             gettext("What manual page do you want?\n"));
 324                         exit(1);
 325                 }
 326         }
 327 
 328         init_bintoman();
 329         if (manpath == NULL && (manpath = getenv("MANPATH")) == NULL) {
 330                 if ((manpath = getenv("PATH")) != NULL)
 331                         bmp_flags = BMP_ISPATH | BMP_APPEND_DEFMANDIR;
 332                 else
 333                         manpath = DEFMANDIR;
 334         }
 335         pathv = split(manpath, ':');
 336         mandirs = build_manpath(pathv, bmp_flags);
 337         freev(pathv);
 338         fullpaths(&mandirs);
 339 
 340         if (makewhatis) {
 341                 do_makewhatis(mandirs);
 342                 exit(0);
 343         }
 344 
 345         if (printmp) {
 346                 print_manpath(mandirs);
 347                 exit(0);
 348         }
 349 
 350         /* Collect environment information */
 351         if (isatty(STDOUT_FILENO) && (mwstr = getenv("MANWIDTH")) != NULL &&
 352             *mwstr != '\0') {
 353                 if (strcasecmp(mwstr, "tty") == 0) {
 354                         struct winsize  ws;
 355 
 356                         if (ioctl(0, TIOCGWINSZ, &ws) != 0)
 357                                 warn("TIOCGWINSZ");
 358                         else
 359                                 manwidth = ws.ws_col;
 360                 } else {
 361                         manwidth = (int)strtol(mwstr, (char **)NULL, 10);
 362                         if (manwidth < 0)
 363                                 manwidth = 0;
 364                 }
 365         }
 366         if (manwidth != 0) {
 367                 DPRINTF("-- Using non-standard page width: %d\n", manwidth);
 368         }
 369 
 370         if (pager == NULL) {
 371                 if ((pager = getenv("PAGER")) == NULL || *pager == '\0')
 372                         pager = PAGER;
 373         }
 374         DPRINTF("-- Using pager: %s\n", pager);
 375 
 376         for (i = 0; i < argc; i++) {
 377                 char            *cmd;
 378                 static struct man_node *mp;
 379                 char            *pv[2];
 380 
 381                 /*
 382                  * If full path to command specified, customize
 383                  * the manpath accordingly.
 384                  */
 385                 if ((cmd = strrchr(argv[i], '/')) != NULL) {
 386                         *cmd = '\0';
 387                         if ((pv[0] = strdup(argv[i])) == NULL)
 388                                 err(1, "strdup");
 389                         pv[1] = NULL;
 390                         *cmd = '/';
 391                         mp = build_manpath(pv,
 392                             BMP_ISPATH | BMP_FALLBACK_DEFMANDIR);
 393                 } else {
 394                         mp = mandirs;
 395                 }
 396 
 397                 if (apropos)
 398                         whatapro(mp, argv[i]);
 399                 else
 400                         ret += manual(mp, argv[i]);
 401 
 402                 if (mp != NULL && mp != mandirs) {
 403                         free(pv[0]);
 404                         free_manp(mp);
 405                 }
 406         }
 407 
 408         return (ret == 0 ? 0 : 1);
 409 }
 410 
 411 /*
 412  * This routine builds the manpage structure from MANPATH or PATH,
 413  * depending on flags.  See BMP_* definitions above for valid
 414  * flags.
 415  */
 416 static struct man_node *
 417 build_manpath(char **pathv, int flags)
 418 {
 419         struct man_node *manpage = NULL;
 420         struct man_node *currp = NULL;
 421         struct man_node *lastp = NULL;
 422         char            **p;
 423         char            **q;
 424         char            *mand = NULL;
 425         char            *mandir = DEFMANDIR;
 426         int             s;
 427         struct dupnode  *didup = NULL;
 428         struct stat     sb;
 429 
 430         s = sizeof (struct man_node);
 431         for (p = pathv; *p != NULL; ) {
 432                 if (flags & BMP_ISPATH) {
 433                         if ((mand = path_to_manpath(*p)) == NULL)
 434                                 goto next;
 435                         free(*p);
 436                         *p = mand;
 437                 }
 438                 q = split(*p, ',');
 439                 if (stat(q[0], &sb) != 0 || (sb.st_mode & S_IFDIR) == 0) {
 440                         freev(q);
 441                         goto next;
 442                 }
 443 
 444                 if (access(q[0], R_OK | X_OK) == 0) {
 445                         /*
 446                          * Some element exists.  Do not append DEFMANDIR as a
 447                          * fallback.
 448                          */
 449                         flags &= ~BMP_FALLBACK_DEFMANDIR;
 450 
 451                         if ((currp = (struct man_node *)calloc(1, s)) == NULL)
 452                                 err(1, "calloc");
 453 
 454                         currp->frompath = (flags & BMP_ISPATH);
 455 
 456                         if (manpage == NULL)
 457                                 lastp = manpage = currp;
 458 
 459                         getpath(currp, p);
 460                         getsect(currp, p);
 461 
 462                         /*
 463                          * If there are no new elements in this path,
 464                          * do not add it to the manpage list.
 465                          */
 466                         if (dupcheck(currp, &didup) != 0) {
 467                                 freev(currp->secv);
 468                                 free(currp);
 469                         } else {
 470                                 currp->next = NULL;
 471                                 if (currp != manpage)
 472                                         lastp->next = currp;
 473                                 lastp = currp;
 474                         }
 475                 }
 476                 freev(q);
 477 next:
 478                 /*
 479                  * Special handling of appending DEFMANDIR. After all pathv
 480                  * elements have been processed, append DEFMANDIR if needed.
 481                  */
 482                 if (p == &mandir)
 483                         break;
 484                 p++;
 485                 if (*p != NULL)
 486                         continue;
 487                 if (flags & (BMP_APPEND_DEFMANDIR | BMP_FALLBACK_DEFMANDIR)) {
 488                         p = &mandir;
 489                         flags &= ~BMP_ISPATH;
 490                 }
 491         }
 492 
 493         free_dupnode(didup);
 494 
 495         return (manpage);
 496 }
 497 
 498 /*
 499  * Store the mandir path into the manp structure.
 500  */
 501 static void
 502 getpath(struct man_node *manp, char **pv)
 503 {
 504         char    *s = *pv;
 505         int     i = 0;
 506 
 507         while (*s != '\0' && *s != ',')
 508                 i++, s++;
 509 
 510         if ((manp->path = (char *)malloc(i + 1)) == NULL)
 511                 err(1, "malloc");
 512         (void) strlcpy(manp->path, *pv, i + 1);
 513 }
 514 
 515 /*
 516  * Store the mandir's corresponding sections (submandir
 517  * directories) into the manp structure.
 518  */
 519 static void
 520 getsect(struct man_node *manp, char **pv)
 521 {
 522         char    *sections;
 523         char    **sectp;
 524 
 525         /* Just store all sections when doing makewhatis or apropos/whatis */
 526         if (makewhatis || apropos) {
 527                 manp->defsrch = 1;
 528                 DPRINTF("-- Adding %s\n", manp->path);
 529                 manp->secv = NULL;
 530                 get_all_sect(manp);
 531         } else if (sargs) {
 532                 manp->secv = split(mansec, ',');
 533                 for (sectp = manp->secv; *sectp; sectp++)
 534                         lower(*sectp);
 535         } else if ((sections = strchr(*pv, ',')) != NULL) {
 536                 DPRINTF("-- Adding %s: MANSECTS=%s\n", manp->path, sections);
 537                 manp->secv = split(++sections, ',');
 538                 for (sectp = manp->secv; *sectp; sectp++)
 539                         lower(*sectp);
 540                 if (*manp->secv == NULL)
 541                         get_all_sect(manp);
 542         } else if ((sections = check_config(*pv)) != NULL) {
 543                 manp->defsrch = 1;
 544                 DPRINTF("-- Adding %s: from %s, MANSECTS=%s\n", manp->path,
 545                     CONFIG, sections);
 546                 manp->secv = split(sections, ',');
 547                 for (sectp = manp->secv; *sectp; sectp++)
 548                         lower(*sectp);
 549                 if (*manp->secv == NULL)
 550                         get_all_sect(manp);
 551         } else {
 552                 manp->defsrch = 1;
 553                 DPRINTF("-- Adding %s: default sort order\n", manp->path);
 554                 manp->secv = NULL;
 555                 get_all_sect(manp);
 556         }
 557 }
 558 
 559 /*
 560  * Get suffices of all sub-mandir directories in a mandir.
 561  */
 562 static void
 563 get_all_sect(struct man_node *manp)
 564 {
 565         DIR     *dp;
 566         char    **dirv;
 567         char    **dv;
 568         char    **p;
 569         char    *prev = NULL;
 570         char    *tmp = NULL;
 571         int     maxentries = MAXTOKENS;
 572         int     entries = 0;
 573 
 574         if ((dp = opendir(manp->path)) == 0)
 575                 return;
 576 
 577         sortdir(dp, &dirv);
 578 
 579         (void) closedir(dp);
 580 
 581         if (manp->secv == NULL) {
 582                 if ((manp->secv = malloc(maxentries * sizeof (char *))) == NULL)
 583                         err(1, "malloc");
 584         }
 585 
 586         for (dv = dirv, p = manp->secv; *dv; dv++) {
 587                 if (strcmp(*dv, CONFIG) == 0) {
 588                         free(*dv);
 589                         continue;
 590                 }
 591 
 592                 free(tmp);
 593                 if ((tmp = strdup(*dv + 3)) == NULL)
 594                         err(1, "strdup");
 595 
 596                 if (prev != NULL && strcmp(prev, tmp) == 0) {
 597                         free(*dv);
 598                         continue;
 599                 }
 600 
 601                 free(prev);
 602                 if ((prev = strdup(*dv + 3)) == NULL)
 603                         err(1, "strdup");
 604 
 605                 if ((*p = strdup(*dv + 3)) == NULL)
 606                         err(1, "strdup");
 607 
 608                 p++; entries++;
 609 
 610                 if (entries == maxentries) {
 611                         maxentries += MAXTOKENS;
 612                         if ((manp->secv = realloc(manp->secv,
 613                             sizeof (char *) * maxentries)) == NULL)
 614                                 err(1, "realloc");
 615                         p = manp->secv + entries;
 616                 }
 617                 free(*dv);
 618         }
 619         free(tmp);
 620         free(prev);
 621         *p = NULL;
 622         free(dirv);
 623 }
 624 
 625 /*
 626  * Build whatis databases.
 627  */
 628 static void
 629 do_makewhatis(struct man_node *manp)
 630 {
 631         struct man_node *p;
 632         char            *ldir;
 633 
 634         for (p = manp; p != NULL; p = p->next) {
 635                 ldir = addlocale(p->path);
 636                 if (*localedir != '\0' && getdirs(ldir, NULL, 0) > 0)
 637                         mwpath(ldir);
 638                 free(ldir);
 639                 mwpath(p->path);
 640         }
 641 }
 642 
 643 /*
 644  * Count mandirs under the given manpath
 645  */
 646 static int
 647 getdirs(char *path, char ***dirv, int flag)
 648 {
 649         DIR             *dp;
 650         struct dirent   *d;
 651         int             n = 0;
 652         int             maxentries = MAXDIRS;
 653         char            **dv = NULL;
 654 
 655         if ((dp = opendir(path)) == NULL)
 656                 return (0);
 657 
 658         if (flag) {
 659                 if ((*dirv = malloc(sizeof (char *) *
 660                     maxentries)) == NULL)
 661                         err(1, "malloc");
 662                 dv = *dirv;
 663         }
 664         while ((d = readdir(dp))) {
 665                 if (strncmp(d->d_name, "man", 3) != 0)
 666                         continue;
 667                 n++;
 668 
 669                 if (flag) {
 670                         if ((*dv = strdup(d->d_name + 3)) == NULL)
 671                                 err(1, "strdup");
 672                         dv++;
 673                         if ((dv - *dirv) == maxentries) {
 674                                 int     entries = maxentries;
 675 
 676                                 maxentries += MAXTOKENS;
 677                                 if ((*dirv = realloc(*dirv,
 678                                     sizeof (char *) * maxentries)) == NULL)
 679                                         err(1, "realloc");
 680                                 dv = *dirv + entries;
 681                         }
 682                 }
 683         }
 684 
 685         (void) closedir(dp);
 686         return (n);
 687 }
 688 
 689 
 690 /*
 691  * Find matching whatis or apropos entries.
 692  */
 693 static void
 694 whatapro(struct man_node *manp, char *word)
 695 {
 696         char            whatpath[MAXPATHLEN];
 697         struct man_node *b;
 698         char            *ldir;
 699 
 700         for (b = manp; b != NULL; b = b->next) {
 701                 if (*localedir != '\0') {
 702                         ldir = addlocale(b->path);
 703                         if (getdirs(ldir, NULL, 0) != 0) {
 704                                 (void) snprintf(whatpath, sizeof (whatpath),
 705                                     "%s/%s", ldir, WHATIS);
 706                                 search_whatis(whatpath, word);
 707                         }
 708                         free(ldir);
 709                 }
 710                 (void) snprintf(whatpath, sizeof (whatpath), "%s/%s", b->path,
 711                     WHATIS);
 712                 search_whatis(whatpath, word);
 713         }
 714 }
 715 
 716 static void
 717 search_whatis(char *whatpath, char *word)
 718 {
 719         FILE            *fp;
 720         char            *line = NULL;
 721         size_t          linecap = 0;
 722         char            *pkwd;
 723         regex_t         preg;
 724         char            **ss = NULL;
 725         char            s[MAXNAMELEN];
 726         int             i;
 727 
 728         if ((fp = fopen(whatpath, "r")) == NULL) {
 729                 perror(whatpath);
 730                 return;
 731         }
 732 
 733         DPRINTF("-- Found %s: %s\n", WHATIS, whatpath);
 734 
 735         /* Build keyword regex */
 736         if (asprintf(&pkwd, "%s%s%s", (whatis) ? "\\<" : "",
 737             word, (whatis) ? "\\>" : "") == -1)
 738                 err(1, "asprintf");
 739 
 740         if (regcomp(&preg, pkwd, REG_BASIC | REG_ICASE | REG_NOSUB) != 0)
 741                 err(1, "regcomp");
 742 
 743         if (sargs)
 744                 ss = split(mansec, ',');
 745 
 746         while (getline(&line, &linecap, fp) > 0) {
 747                 if (regexec(&preg, line, 0, NULL, 0) == 0) {
 748                         if (sargs) {
 749                                 /* Section-restricted search */
 750                                 for (i = 0; ss[i] != NULL; i++) {
 751                                         (void) snprintf(s, sizeof (s), "(%s)",
 752                                             ss[i]);
 753                                         if (strstr(line, s) != NULL) {
 754                                                 (void) printf("%s", line);
 755                                                 break;
 756                                         }
 757                                 }
 758                         } else {
 759                                 (void) printf("%s", line);
 760                         }
 761                 }
 762         }
 763 
 764         if (ss != NULL)
 765                 freev(ss);
 766         free(pkwd);
 767         (void) fclose(fp);
 768 }
 769 
 770 
 771 /*
 772  * Split a string by specified separator.
 773  */
 774 static char **
 775 split(char *s1, char sep)
 776 {
 777         char    **tokv, **vp;
 778         char    *mp = s1, *tp;
 779         int     maxentries = MAXTOKENS;
 780         int     entries = 0;
 781 
 782         if ((tokv = vp = malloc(maxentries * sizeof (char *))) == NULL)
 783                 err(1, "malloc");
 784 
 785         for (; mp && *mp; mp = tp) {
 786                 tp = strchr(mp, sep);
 787                 if (mp == tp) {
 788                         tp++;
 789                         continue;
 790                 }
 791                 if (tp) {
 792                         size_t  len;
 793 
 794                         len = tp - mp;
 795                         if ((*vp = (char *)malloc(sizeof (char) *
 796                             len + 1)) == NULL)
 797                                 err(1, "malloc");
 798                         (void) strncpy(*vp, mp, len);
 799                         *(*vp + len) = '\0';
 800                         tp++;
 801                         vp++;
 802                 } else {
 803                         if ((*vp = strdup(mp)) == NULL)
 804                                 err(1, "strdup");
 805                         vp++;
 806                 }
 807                 entries++;
 808                 if (entries == maxentries) {
 809                         maxentries += MAXTOKENS;
 810                         if ((tokv = realloc(tokv,
 811                             maxentries * sizeof (char *))) == NULL)
 812                                 err(1, "realloc");
 813                         vp = tokv + entries;
 814                 }
 815         }
 816         *vp = 0;
 817 
 818         return (tokv);
 819 }
 820 
 821 /*
 822  * Free a vector allocated by split()
 823  */
 824 static void
 825 freev(char **v)
 826 {
 827         int i;
 828         if (v != NULL) {
 829                 for (i = 0; v[i] != NULL; i++) {
 830                         free(v[i]);
 831                 }
 832                 free(v);
 833         }
 834 }
 835 
 836 /*
 837  * Convert paths to full paths if necessary
 838  */
 839 static void
 840 fullpaths(struct man_node **manp_head)
 841 {
 842         char            *cwd = NULL;
 843         char            *p;
 844         int             cwd_gotten = 0;
 845         struct man_node *manp = *manp_head;
 846         struct man_node *b;
 847         struct man_node *prev = NULL;
 848 
 849         for (b = manp; b != NULL; b = b->next) {
 850                 if (*(b->path) == '/') {
 851                         prev = b;
 852                         continue;
 853                 }
 854 
 855                 if (!cwd_gotten) {
 856                         cwd = getcwd(NULL, MAXPATHLEN);
 857                         cwd_gotten = 1;
 858                 }
 859 
 860                 if (cwd) {
 861                         /* Relative manpath with cwd: make absolute */
 862                         if (asprintf(&p, "%s/%s", cwd, b->path) == -1)
 863                                 err(1, "asprintf");
 864                         free(b->path);
 865                         b->path = p;
 866                 } else {
 867                         /* Relative manpath but no cwd: omit path entry */
 868                         if (prev)
 869                                 prev->next = b->next;
 870                         else
 871                                 *manp_head = b->next;
 872 
 873                         free_manp(b);
 874                 }
 875         }
 876         free(cwd);
 877 }
 878 
 879 /*
 880  * Free a man_node structure and its contents
 881  */
 882 static void
 883 free_manp(struct man_node *manp)
 884 {
 885         char    **p;
 886 
 887         free(manp->path);
 888         p = manp->secv;
 889         while ((p != NULL) && (*p != NULL)) {
 890                 free(*p);
 891                 p++;
 892         }
 893         free(manp->secv);
 894         free(manp);
 895 }
 896 
 897 
 898 /*
 899  * Map (in place) to lower case.
 900  */
 901 static void
 902 lower(char *s)
 903 {
 904 
 905         if (s == 0)
 906                 return;
 907         while (*s) {
 908                 if (isupper(*s))
 909                         *s = tolower(*s);
 910                 s++;
 911         }
 912 }
 913 
 914 
 915 /*
 916  * Compare function for qsort().
 917  * Sort first by section, then by prefix.
 918  */
 919 static int
 920 cmp(const void *arg1, const void *arg2)
 921 {
 922         int     n;
 923         char    **p1 = (char **)arg1;
 924         char    **p2 = (char **)arg2;
 925 
 926         /* By section */
 927         if ((n = strcmp(*p1 + 3, *p2 + 3)) != 0)
 928                 return (n);
 929 
 930         /* By prefix reversed */
 931         return (strncmp(*p2, *p1, 3));
 932 }
 933 
 934 
 935 /*
 936  * Find a manpage.
 937  */
 938 static int
 939 manual(struct man_node *manp, char *name)
 940 {
 941         struct man_node *p;
 942         struct man_node *local;
 943         int             ndirs = 0;
 944         char            *ldir;
 945         char            *ldirs[2];
 946         char            *fullname = name;
 947         char            *slash;
 948 
 949         if ((slash = strrchr(name, '/')) != NULL)
 950                 name = slash + 1;
 951 
 952         /* For each path in MANPATH */
 953         found = 0;
 954 
 955         for (p = manp; p != NULL; p = p->next) {
 956                 DPRINTF("-- Searching mandir: %s\n", p->path);
 957 
 958                 if (*localedir != '\0') {
 959                         ldir = addlocale(p->path);
 960                         ndirs = getdirs(ldir, NULL, 0);
 961                         if (ndirs != 0) {
 962                                 ldirs[0] = ldir;
 963                                 ldirs[1] = NULL;
 964                                 local = build_manpath(ldirs, 0);
 965                                 DPRINTF("-- Locale specific subdir: %s\n",
 966                                     ldir);
 967                                 mandir(local->secv, ldir, name, 1);
 968                                 free_manp(local);
 969                         }
 970                         free(ldir);
 971                 }
 972 
 973                 /*
 974                  * Locale mandir not valid, man page in locale
 975                  * mandir not found, or -a option present
 976                  */
 977                 if (ndirs == 0 || !found || all)
 978                         mandir(p->secv, p->path, name, 0);
 979 
 980                 if (found && !all)
 981                         break;
 982         }
 983 
 984         if (!found) {
 985                 if (sargs) {
 986                         (void) fprintf(stderr, gettext(
 987                             "No manual entry for %s in section(s) %s\n"),
 988                             fullname, mansec);
 989                 } else {
 990                         (void) fprintf(stderr,
 991                             gettext("No manual entry for %s\n"), fullname);
 992                 }
 993 
 994         }
 995 
 996         return (!found);
 997 }
 998 
 999 
1000 /*
1001  * For a specified manual directory, read, store and sort section subdirs.
1002  * For each section specified, find and search matching subdirs.
1003  */
1004 static void
1005 mandir(char **secv, char *path, char *name, int lspec)
1006 {
1007         DIR     *dp;
1008         char    **dirv;
1009         char    **dv, **pdv;
1010         int     len, dslen;
1011 
1012         if ((dp = opendir(path)) == NULL)
1013                 return;
1014 
1015         if (lspec)
1016                 DPRINTF("-- Searching mandir: %s\n", path);
1017 
1018         sortdir(dp, &dirv);
1019 
1020         /* Search in the order specified by MANSECTS */
1021         for (; *secv; secv++) {
1022                 len = strlen(*secv);
1023                 for (dv = dirv; *dv; dv++) {
1024                         dslen = strlen(*dv + 3);
1025                         if (dslen > len)
1026                                 len = dslen;
1027                         if (**secv == '\\') {
1028                                 if (strcmp(*secv + 1, *dv + 3) != 0)
1029                                         continue;
1030                         } else if (strncasecmp(*secv, *dv + 3, len) != 0) {
1031                                 if (!all &&
1032                                     (newsection = map_section(*secv, path))
1033                                     == NULL) {
1034                                         continue;
1035                                 }
1036                                 if (newsection == NULL)
1037                                         newsection = "";
1038                                 if (strncmp(newsection, *dv + 3, len) != 0) {
1039                                         continue;
1040                                 }
1041                         }
1042 
1043                         if (searchdir(path, *dv, name) == 0)
1044                                 continue;
1045 
1046                         if (!all) {
1047                                 pdv = dirv;
1048                                 while (*pdv) {
1049                                         free(*pdv);
1050                                         pdv++;
1051                                 }
1052                                 (void) closedir(dp);
1053                                 free(dirv);
1054                                 return;
1055                         }
1056 
1057                         if (all && **dv == 'm' && *(dv + 1) &&
1058                             strcmp(*(dv + 1) + 3, *dv + 3) == 0)
1059                                         dv++;
1060                 }
1061         }
1062         pdv = dirv;
1063         while (*pdv != NULL) {
1064                 free(*pdv);
1065                 pdv++;
1066         }
1067         free(dirv);
1068         (void) closedir(dp);
1069 }
1070 
1071 /*
1072  * Sort directories.
1073  */
1074 static void
1075 sortdir(DIR *dp, char ***dirv)
1076 {
1077         struct dirent   *d;
1078         char            **dv;
1079         int             maxentries = MAXDIRS;
1080         int             entries = 0;
1081 
1082         if ((dv = *dirv = malloc(sizeof (char *) *
1083             maxentries)) == NULL)
1084                 err(1, "malloc");
1085         dv = *dirv;
1086 
1087         while ((d = readdir(dp))) {
1088                 if (strcmp(d->d_name, ".") == 0 ||
1089                     strcmp(d->d_name, "..") == 0)
1090                         continue;
1091 
1092                 if (strncmp(d->d_name, "man", 3) == 0 ||
1093                     strncmp(d->d_name, "cat", 3) == 0) {
1094                         if ((*dv = strdup(d->d_name)) == NULL)
1095                                 err(1, "strdup");
1096                         dv++;
1097                         entries++;
1098                         if (entries == maxentries) {
1099                                 maxentries += MAXDIRS;
1100                                 if ((*dirv = realloc(*dirv,
1101                                     sizeof (char *) * maxentries)) == NULL)
1102                                         err(1, "realloc");
1103                                 dv = *dirv + entries;
1104                         }
1105                 }
1106         }
1107         *dv = 0;
1108 
1109         qsort((void *)*dirv, dv - *dirv, sizeof (char *), cmp);
1110 
1111 }
1112 
1113 
1114 /*
1115  * Search a section subdir for a given manpage.
1116  */
1117 static int
1118 searchdir(char *path, char *dir, char *name)
1119 {
1120         DIR             *sdp;
1121         struct dirent   *sd;
1122         char            sectpath[MAXPATHLEN];
1123         char            file[MAXNAMLEN];
1124         char            dname[MAXPATHLEN];
1125         char            *last;
1126         int             nlen;
1127 
1128         (void) snprintf(sectpath, sizeof (sectpath), "%s/%s", path, dir);
1129         (void) snprintf(file, sizeof (file), "%s.", name);
1130 
1131         if ((sdp = opendir(sectpath)) == NULL)
1132                 return (0);
1133 
1134         while ((sd = readdir(sdp))) {
1135                 char    *pname;
1136 
1137                 if ((pname = strdup(sd->d_name)) == NULL)
1138                         err(1, "strdup");
1139                 if ((last = strrchr(pname, '.')) != NULL &&
1140                     (strcmp(last, ".gz") == 0 || strcmp(last, ".bz2") == 0))
1141                         *last = '\0';
1142                 last = strrchr(pname, '.');
1143                 nlen = last - pname;
1144                 (void) snprintf(dname, sizeof (dname), "%.*s.", nlen, pname);
1145                 if (strcmp(dname, file) == 0 ||
1146                     strcmp(pname, name) == 0) {
1147                         (void) format(path, dir, name, sd->d_name);
1148                         (void) closedir(sdp);
1149                         free(pname);
1150                         return (1);
1151                 }
1152                 free(pname);
1153         }
1154         (void) closedir(sdp);
1155 
1156         return (0);
1157 }
1158 
1159 /*
1160  * Check the hash table of old directory names to see if there is a
1161  * new directory name.
1162  */
1163 static char *
1164 map_section(char *section, char *path)
1165 {
1166         int     i;
1167         char    fullpath[MAXPATHLEN];
1168 
1169         if (list)  /* -l option fall through */
1170                 return (NULL);
1171 
1172         for (i = 0; map[i].new_name != NULL; i++) {
1173                 if (strcmp(section, map[i].old_name) == 0) {
1174                         (void) snprintf(fullpath, sizeof (fullpath),
1175                             "%s/man%s", path, map[i].new_name);
1176                         if (!access(fullpath, R_OK | X_OK)) {
1177                                 return (map[i].new_name);
1178                         } else {
1179                                 return (NULL);
1180                         }
1181                 }
1182         }
1183 
1184         return (NULL);
1185 }
1186 
1187 /*
1188  * Format the manpage.
1189  */
1190 static int
1191 format(char *path, char *dir, char *name, char *pg)
1192 {
1193         char            manpname[MAXPATHLEN], catpname[MAXPATHLEN];
1194         char            cmdbuf[BUFSIZ], tmpbuf[BUFSIZ];
1195         char            *cattool;
1196         int             utf8 = 0;
1197         struct stat     sbman, sbcat;
1198 
1199         found++;
1200 
1201         if (list) {
1202                 (void) printf(gettext("%s(%s)\t-M %s\n"), name, dir + 3, path);
1203                 return (-1);
1204         }
1205 
1206         (void) snprintf(manpname, sizeof (manpname), "%s/man%s/%s", path,
1207             dir + 3, pg);
1208         (void) snprintf(catpname, sizeof (catpname), "%s/cat%s/%s", path,
1209             dir + 3, pg);
1210 
1211         /* Can't do PS output if manpage doesn't exist */
1212         if (stat(manpname, &sbman) != 0 && (psoutput|lintout))
1213                 return (-1);
1214 
1215         /*
1216          * If both manpage and catpage do not exist, manpname is
1217          * broken symlink, most likely.
1218          */
1219         if (stat(catpname, &sbcat) != 0 && stat(manpname, &sbman) != 0)
1220                 err(1, "%s", manpname);
1221 
1222         /* Setup cattool */
1223         if (fnmatch("*.gz", manpname, 0) == 0)
1224                 cattool = "gzcat";
1225         else if (fnmatch("*.bz2", manpname, 0) == 0)
1226                 cattool = "bzcat";
1227         else
1228                 cattool = "cat";
1229 
1230         /* Preprocess UTF-8 input with preconv (could be smarter) */
1231         if (strstr(path, "UTF-8") != NULL)
1232                 utf8 = 1;
1233 
1234         if (psoutput) {
1235                 (void) snprintf(cmdbuf, BUFSIZ,
1236                     "cd %s; %s %s%s | mandoc -Tps | lp -Tpostscript",
1237                     path, cattool, manpname,
1238                     utf8 ? " | " PRECONV " -e UTF-8" : "");
1239                 DPRINTF("-- Using manpage: %s\n", manpname);
1240                 goto cmd;
1241         } else if (lintout) {
1242                 (void) snprintf(cmdbuf, BUFSIZ,
1243                     "cd %s; %s %s%s | mandoc -Tlint",
1244                     path, cattool, manpname,
1245                     utf8 ? " | " PRECONV " -e UTF-8" : "");
1246                 DPRINTF("-- Linting manpage: %s\n", manpname);
1247                 goto cmd;
1248         }
1249 
1250         /*
1251          * Output catpage if:
1252          * - manpage doesn't exist
1253          * - output width is standard and catpage is recent enough
1254          */
1255         if (stat(manpname, &sbman) != 0 || (manwidth == 0 &&
1256             stat(catpname, &sbcat) == 0 && sbcat.st_mtime >= sbman.st_mtime)) {
1257                 DPRINTF("-- Using catpage: %s\n", catpname);
1258                 (void) snprintf(cmdbuf, BUFSIZ, "%s %s", pager, catpname);
1259                 goto cmd;
1260         }
1261 
1262         DPRINTF("-- Using manpage: %s\n", manpname);
1263         if (manwidth > 0)
1264                 (void) snprintf(tmpbuf, BUFSIZ, "-Owidth=%d ", manwidth);
1265         (void) snprintf(cmdbuf, BUFSIZ, "cd %s; %s %s%s | mandoc -T%s %s| %s",
1266             path, cattool, manpname,
1267             utf8 ? " | " PRECONV " -e UTF-8 " : "",
1268             utf8 ? "utf8" : "ascii", (manwidth > 0) ? tmpbuf : "", pager);
1269 
1270 cmd:
1271         DPRINTF("-- Command: %s\n", cmdbuf);
1272 
1273         if (!debug)
1274                 return (system(cmdbuf) == 0);
1275         else
1276                 return (0);
1277 }
1278 
1279 /*
1280  * Add <localedir> to the path.
1281  */
1282 static char *
1283 addlocale(char *path)
1284 {
1285         char    *tmp;
1286 
1287         if (asprintf(&tmp, "%s/%s", path, localedir) == -1)
1288                 err(1, "asprintf");
1289 
1290         return (tmp);
1291 }
1292 
1293 /*
1294  * Get the order of sections from man.cf.
1295  */
1296 static char *
1297 check_config(char *path)
1298 {
1299         FILE            *fp;
1300         char            *rc = NULL;
1301         char            *sect;
1302         char            fname[MAXPATHLEN];
1303         char            *line = NULL;
1304         size_t          linecap = 0;
1305 
1306         (void) snprintf(fname, MAXPATHLEN, "%s/%s", path, CONFIG);
1307 
1308         if ((fp = fopen(fname, "r")) == NULL)
1309                 return (NULL);
1310 
1311         while (getline(&line, &linecap, fp) > 0) {
1312                 if ((rc = strstr(line, "MANSECTS")) != NULL)
1313                         break;
1314         }
1315 
1316         (void) fclose(fp);
1317 
1318         if (rc == NULL || (sect = strchr(line, '=')) == NULL)
1319                 return (NULL);
1320         else
1321                 return (++sect);
1322 }
1323 
1324 
1325 /*
1326  * Initialize the bintoman array with appropriate device and inode info.
1327  */
1328 static void
1329 init_bintoman(void)
1330 {
1331         int i;
1332         struct stat sb;
1333 
1334         for (i = 0; bintoman[i].bindir != NULL; i++) {
1335                 if (stat(bintoman[i].bindir, &sb) == 0) {
1336                         bintoman[i].dev = sb.st_dev;
1337                         bintoman[i].ino = sb.st_ino;
1338                 } else {
1339                         bintoman[i].dev = NODEV;
1340                 }
1341         }
1342 }
1343 
1344 /*
1345  * If a duplicate is found, return 1.
1346  * If a duplicate is not found, add it to the dupnode list and return 0.
1347  */
1348 static int
1349 dupcheck(struct man_node *mnp, struct dupnode **dnp)
1350 {
1351         struct dupnode  *curdnp;
1352         struct secnode  *cursnp;
1353         struct stat     sb;
1354         int             i;
1355         int             rv = 1;
1356         int             dupfound;
1357 
1358         /* If the path doesn't exist, treat it as a duplicate */
1359         if (stat(mnp->path, &sb) != 0)
1360                 return (1);
1361 
1362         /* If no sections were found in the man dir, treat it as duplicate */
1363         if (mnp->secv == NULL)
1364                 return (1);
1365 
1366         /*
1367          * Find the dupnode structure for the previous time this directory
1368          * was looked at.  Device and inode numbers are compared so that
1369          * directories that are reached via different paths (e.g. /usr/man and
1370          * /usr/share/man) are treated as equivalent.
1371          */
1372         for (curdnp = *dnp; curdnp != NULL; curdnp = curdnp->next) {
1373                 if (curdnp->dev == sb.st_dev && curdnp->ino == sb.st_ino)
1374                         break;
1375         }
1376 
1377         /*
1378          * First time this directory has been seen. Add a new node to the
1379          * head of the list. Since all entries are guaranteed to be unique
1380          * copy all sections to new node.
1381          */
1382         if (curdnp == NULL) {
1383                 if ((curdnp = calloc(1, sizeof (struct dupnode))) == NULL)
1384                         err(1, "calloc");
1385                 for (i = 0; mnp->secv[i] != NULL; i++) {
1386                         if ((cursnp = calloc(1, sizeof (struct secnode)))
1387                             == NULL)
1388                                 err(1, "calloc");
1389                         cursnp->next = curdnp->secl;
1390                         curdnp->secl = cursnp;
1391                         if ((cursnp->secp = strdup(mnp->secv[i])) == NULL)
1392                                 err(1, "strdup");
1393                 }
1394                 curdnp->dev = sb.st_dev;
1395                 curdnp->ino = sb.st_ino;
1396                 curdnp->next = *dnp;
1397                 *dnp = curdnp;
1398                 return (0);
1399         }
1400 
1401         /*
1402          * Traverse the section vector in the man_node and the section list
1403          * in dupnode cache to eliminate all duplicates from man_node.
1404          */
1405         for (i = 0; mnp->secv[i] != NULL; i++) {
1406                 dupfound = 0;
1407                 for (cursnp = curdnp->secl; cursnp != NULL;
1408                     cursnp = cursnp->next) {
1409                         if (strcmp(mnp->secv[i], cursnp->secp) == 0) {
1410                                 dupfound = 1;
1411                                 break;
1412                         }
1413                 }
1414                 if (dupfound) {
1415                         mnp->secv[i][0] = '\0';
1416                         continue;
1417                 }
1418 
1419 
1420                 /*
1421                  * Update curdnp and set return value to indicate that this
1422                  * was not all duplicates.
1423                  */
1424                 if ((cursnp = calloc(1, sizeof (struct secnode))) == NULL)
1425                         err(1, "calloc");
1426                 cursnp->next = curdnp->secl;
1427                 curdnp->secl = cursnp;
1428                 if ((cursnp->secp = strdup(mnp->secv[i])) == NULL)
1429                         err(1, "strdup");
1430                 rv = 0;
1431         }
1432 
1433         return (rv);
1434 }
1435 
1436 /*
1437  * Given a bindir, return corresponding mandir.
1438  */
1439 static char *
1440 path_to_manpath(char *bindir)
1441 {
1442         char            *mand, *p;
1443         int             i;
1444         struct stat     sb;
1445 
1446         /* First look for known translations for specific bin paths */
1447         if (stat(bindir, &sb) != 0) {
1448                 return (NULL);
1449         }
1450         for (i = 0; bintoman[i].bindir != NULL; i++) {
1451                 if (sb.st_dev == bintoman[i].dev &&
1452                     sb.st_ino == bintoman[i].ino) {
1453                         if ((mand = strdup(bintoman[i].mandir)) == NULL)
1454                                 err(1, "strdup");
1455                         if ((p = strchr(mand, ',')) != NULL)
1456                                 *p = '\0';
1457                         if (stat(mand, &sb) != 0) {
1458                                 free(mand);
1459                                 return (NULL);
1460                         }
1461                         if (p != NULL)
1462                                 *p = ',';
1463                         return (mand);
1464                 }
1465         }
1466 
1467         /*
1468          * No specific translation found.  Try `dirname $bindir`/share/man
1469          * and `dirname $bindir`/man
1470          */
1471         if ((mand = malloc(MAXPATHLEN)) == NULL)
1472                 err(1, "malloc");
1473         if (strlcpy(mand, bindir, MAXPATHLEN) >= MAXPATHLEN) {
1474                 free(mand);
1475                 return (NULL);
1476         }
1477 
1478         /*
1479          * Advance to end of buffer, strip trailing /'s then remove last
1480          * directory component.
1481          */
1482         for (p = mand; *p != '\0'; p++)
1483                 ;
1484         for (; p > mand && *p == '/'; p--)
1485                 ;
1486         for (; p > mand && *p != '/'; p--)
1487                 ;
1488         if (p == mand && *p == '.') {
1489                 if (realpath("..", mand) == NULL) {
1490                         free(mand);
1491                         return (NULL);
1492                 }
1493                 for (; *p != '\0'; p++)
1494                         ;
1495         } else {
1496                 *p = '\0';
1497         }
1498 
1499         if (strlcat(mand, "/share/man", MAXPATHLEN) >= MAXPATHLEN) {
1500                 free(mand);
1501                 return (NULL);
1502         }
1503 
1504         if ((stat(mand, &sb) == 0) && S_ISDIR(sb.st_mode)) {
1505                 return (mand);
1506         }
1507 
1508         /*
1509          * Strip the /share/man off and try /man
1510          */
1511         *p = '\0';
1512         if (strlcat(mand, "/man", MAXPATHLEN) >= MAXPATHLEN) {
1513                 free(mand);
1514                 return (NULL);
1515         }
1516         if ((stat(mand, &sb) == 0) && S_ISDIR(sb.st_mode)) {
1517                 return (mand);
1518         }
1519 
1520         /*
1521          * No man or share/man directory found
1522          */
1523         free(mand);
1524         return (NULL);
1525 }
1526 
1527 /*
1528  * Free a linked list of dupnode structs.
1529  */
1530 void
1531 free_dupnode(struct dupnode *dnp) {
1532         struct dupnode *dnp2;
1533         struct secnode *snp;
1534 
1535         while (dnp != NULL) {
1536                 dnp2 = dnp;
1537                 dnp = dnp->next;
1538                 while (dnp2->secl != NULL) {
1539                         snp = dnp2->secl;
1540                         dnp2->secl = dnp2->secl->next;
1541                         free(snp->secp);
1542                         free(snp);
1543                 }
1544                 free(dnp2);
1545         }
1546 }
1547 
1548 /*
1549  * Print manp linked list to stdout.
1550  */
1551 void
1552 print_manpath(struct man_node *manp)
1553 {
1554         char    colon[2] = "\0\0";
1555         char    **secp;
1556 
1557         for (; manp != NULL; manp = manp->next) {
1558                 (void) printf("%s%s", colon, manp->path);
1559                 colon[0] = ':';
1560 
1561                 /*
1562                  * If man.cf or a directory scan was used to create section
1563                  * list, do not print section list again.  If the output of
1564                  * man -p is used to set MANPATH, subsequent runs of man
1565                  * will re-read man.cf and/or scan man directories as
1566                  * required.
1567                  */
1568                 if (manp->defsrch != 0)
1569                         continue;
1570 
1571                 for (secp = manp->secv; *secp != NULL; secp++) {
1572                         /*
1573                          * Section deduplication may have eliminated some
1574                          * sections from the vector. Avoid displaying this
1575                          * detail which would appear as ",," in output
1576                          */
1577                         if ((*secp)[0] != '\0')
1578                                 (void) printf(",%s", *secp);
1579                 }
1580         }
1581         (void) printf("\n");
1582 }
1583 
1584 static void
1585 usage_man(void)
1586 {
1587 
1588         (void) fprintf(stderr, gettext(
1589 "usage: man [-alptw] [-M path] [-s section] name ...\n"
1590 "       man [-M path] [-s section] -k keyword ...\n"
1591 "       man [-M path] [-s section] -f keyword ...\n"));
1592 
1593         exit(1);
1594 }
1595 
1596 static void
1597 usage_whatapro(void)
1598 {
1599 
1600         (void) fprintf(stderr, gettext(
1601 "usage: %s [-M path] [-s section] keyword ...\n"),
1602             whatis ? "whatis" : "apropos");
1603 
1604         exit(1);
1605 }
1606 
1607 static void
1608 usage_catman(void)
1609 {
1610         (void) fprintf(stderr, gettext(
1611 "usage: catman [-M path] [-w]\n"));
1612 
1613         exit(1);
1614 }
1615 
1616 static void
1617 usage_makewhatis(void)
1618 {
1619         (void) fprintf(stderr, gettext("usage: makewhatis\n"));
1620 
1621         exit(1);
1622 }