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