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