/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2012, Josef 'Jeff' Sipek <jeffpc@31bits.net>. All rights reserved.
* Copyright 2014 Garrett D'Amore <garrett@damore.org>
* Copyright 2016 Nexenta Systems, Inc.
*/
/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T. */
/* All rights reserved. */
/*
* University Copyright- Copyright (c) 1982, 1986, 1988
* The Regents of the University of California
* All Rights Reserved
*
* University Acknowledgment- Portions of this document are derived from
* software developed by the University of California, Berkeley, and its
* contributors.
*/
/*
* Find and display reference manual pages. This version includes makewhatis
* functionality as well.
*/
#include <ctype.h>
#include <dirent.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <fnmatch.h>
#include <limits.h>
#include <locale.h>
#include <malloc.h>
#include <memory.h>
#include <regex.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "man.h"
/* Mapping of old directories to new directories */
static const struct map_entry {
char *old_name;
char *new_name;
} map[] = {
{ "3b", "3ucb" },
{ "3e", "3elf" },
{ "3g", "3gen" },
{ "3k", "3kstat" },
{ "3n", "3socket" },
{ "3r", "3rt" },
{ "3s", "3c" },
{ "3t", "3thr" },
{ "3x", "3curses" },
{ "3xc", "3xcurses" },
{ "3xn", "3xnet" },
};
struct suffix {
char *ds;
char *fs;
};
/*
* Flags that control behavior of build_manpath()
*
* BMP_ISPATH pathv is a vector constructed from PATH.
* Perform appropriate path translations for
* manpath.
* BMP_APPEND_DEFMANDIR Add DEFMANDIR to the end if it hasn't
* already appeared earlier.
* manpath (including derived from PATH)
* elements are valid.
*/
/*
* When doing equality comparisons of directories, device and inode
* comparisons are done. The secnode and dupnode structures are used
* to form a list of lists for this processing.
*/
struct secnode {
char *secp;
};
struct dupnode {
};
/*
* Map directories that may appear in PATH to the corresponding
* man directory.
*/
static struct pathmap {
char *bindir;
char *mandir;
} bintoman[] = {
};
struct man_node {
};
static int all = 0;
static int apropos = 0;
static int debug = 0;
static int found = 0;
static int list = 0;
static int makewhatis = 0;
static int printmp = 0;
static int sargs = 0;
static int psoutput = 0;
static int lintout = 0;
static int whatis = 0;
static int makewhatishere = 0;
static char *mansec;
static char *addlocale(char *);
static struct man_node *build_manpath(char **, int);
static void do_makewhatis(struct man_node *);
static char *check_config(char *);
static int cmp(const void *, const void *);
static int format(char *, char *, char *, char *);
static void free_dupnode(struct dupnode *);
static void freev(char **);
static void get_all_sect(struct man_node *);
static int getdirs(char *, char ***, int);
static void init_bintoman(void);
static void lower(char *);
static void mandir(char **, char *, char *, int);
static char *map_section(char *, char *);
static char *path_to_manpath(char *);
static void print_manpath(struct man_node *);
static void search_whatis(char *, char *);
static int searchdir(char *, char *, char *);
static char **split(char *, char);
static void usage_man(void);
static void usage_whatapro(void);
static void usage_catman(void);
static void usage_makewhatis(void);
static int manwidth = 0;
extern const char *__progname;
int
{
int c, i;
char **pathv;
int bmp_flags = 0;
int ret = 0;
char *opts;
char *mwstr;
int catman = 0;
#if !defined(TEXT_DOMAIN)
#endif
(void) textdomain(TEXT_DOMAIN);
apropos++;
opts = "M:ds:";
apropos++;
whatis++;
opts = "M:ds:";
catman++;
makewhatis++;
opts = "P:M:w";
makewhatis++;
manpath = ".";
opts = "";
} else {
opts = "FM:P:T:adfklprs:tw";
pager = "cat";
optind++;
}
}
opterr = 0;
switch (c) {
case 'M': /* Respecify path for man pages */
break;
case 'a':
all++;
break;
case 'd':
debug++;
break;
case 'f':
whatis++;
/*FALLTHROUGH*/
case 'k':
apropos++;
break;
case 'l':
list++;
all++;
break;
case 'p':
printmp++;
break;
case 's':
sargs++;
break;
case 'r':
lintout++;
break;
case 't':
psoutput++;
break;
case 'T':
case 'P':
case 'F':
/* legacy options, compatibility only and ignored */
break;
case 'w':
makewhatis++;
break;
case '?':
default:
if (apropos)
else if (catman)
usage_catman();
else if (makewhatishere)
else
usage_man();
}
}
if (argc == 0) {
if (apropos) {
exit(1);
} else if (!printmp && !makewhatis) {
gettext("What manual page do you want?\n"));
exit(1);
}
}
else
}
if (makewhatis) {
exit(0);
}
if (printmp) {
exit(0);
}
/* Collect environment information */
*mwstr != '\0') {
warn("TIOCGWINSZ");
else
} else {
if (manwidth < 0)
manwidth = 0;
}
}
if (manwidth != 0) {
}
}
for (i = 0; i < argc; i++) {
char *cmd;
/*
* If full path to command specified, customize
* the manpath accordingly.
*/
*cmd = '\0';
*cmd = '/';
} else {
}
if (apropos)
else
}
}
return (ret == 0 ? 0 : 1);
}
/*
* This routine builds the manpage structure from MANPATH or PATH,
* depending on flags. See BMP_* definitions above for valid
* flags.
*/
static struct man_node *
{
char **p;
char **q;
int s;
s = sizeof (struct man_node);
if (flags & BMP_ISPATH) {
goto next;
free(*p);
*p = mand;
}
q = split(*p, ',');
freev(q);
goto next;
}
/*
* Some element exists. Do not append DEFMANDIR as a
* fallback.
*/
/*
* If there are no new elements in this path,
* do not add it to the manpage list.
*/
} else {
}
}
freev(q);
next:
/*
* Special handling of appending DEFMANDIR. After all pathv
* elements have been processed, append DEFMANDIR if needed.
*/
if (p == &mandir)
break;
p++;
if (*p != NULL)
continue;
p = &mandir;
flags &= ~BMP_ISPATH;
}
}
return (manpage);
}
/*
* Store the mandir path into the manp structure.
*/
static void
{
char *s = *pv;
int i = 0;
while (*s != '\0' && *s != ',')
i++, s++;
}
/*
* Store the mandir's corresponding sections (submandir
* directories) into the manp structure.
*/
static void
{
char *sections;
char **sectp;
if (makewhatis || apropos) {
} else if (sargs) {
sections++;
} else {
}
}
/*
* Get suffices of all sub-mandir directories in a mandir.
*/
static void
{
char **dirv;
char **dv;
char **p;
int entries = 0;
return;
}
continue;
}
continue;
}
p++; entries++;
if (entries == maxentries) {
maxentries += MAXTOKENS;
sizeof (char *) * maxentries)) == NULL)
}
}
*p = NULL;
}
/*
* Build whatis databases.
*/
static void
{
struct man_node *p;
char *ldir;
}
}
/*
* Count mandirs under the given manpath
*/
static int
{
struct dirent *d;
int n = 0;
return (0);
if (flag) {
maxentries)) == NULL)
}
continue;
n++;
if (flag) {
dv++;
maxentries += MAXTOKENS;
sizeof (char *) * maxentries)) == NULL)
}
}
}
return (n);
}
/*
* Find matching whatis or apropos entries.
*/
static void
{
struct man_node *b;
char *ldir;
if (*localedir != '\0') {
}
}
WHATIS);
}
}
static void
{
char *pkwd;
char s[MAXNAMELEN];
int i;
return;
}
/* Build keyword regex */
if (sargs)
if (sargs) {
/* Section-restricted search */
(void) snprintf(s, sizeof (s), "(%s)",
ss[i]);
break;
}
}
} else {
}
}
}
}
/*
* Split a string by specified separator.
*/
static char **
{
int entries = 0;
tp++;
continue;
}
if (tp) {
tp++;
vp++;
} else {
vp++;
}
entries++;
if (entries == maxentries) {
maxentries += MAXTOKENS;
maxentries * sizeof (char *))) == NULL)
}
}
*vp = 0;
return (tokv);
}
/*
* Free a vector allocated by split()
*/
static void
freev(char **v)
{
int i;
if (v != NULL) {
for (i = 0; v[i] != NULL; i++) {
free(v[i]);
}
free(v);
}
}
/*
* Convert paths to full paths if necessary
*/
static void
{
char *p;
int cwd_gotten = 0;
struct man_node *b;
if (*(b->path) == '/') {
prev = b;
continue;
}
if (!cwd_gotten) {
cwd_gotten = 1;
}
if (cwd) {
/* Relative manpath with cwd: make absolute */
b->path = p;
} else {
/* Relative manpath but no cwd: omit path entry */
if (prev)
else
free_manp(b);
}
}
}
/*
* Free a man_node structure and its contents
*/
static void
{
char **p;
free(*p);
p++;
}
}
/*
* Map (in place) to lower case.
*/
static void
lower(char *s)
{
if (s == 0)
return;
while (*s) {
if (isupper(*s))
*s = tolower(*s);
s++;
}
}
/*
* Compare function for qsort().
* Sort first by section, then by prefix.
*/
static int
{
int n;
/* By section */
return (n);
/* By prefix reversed */
}
/*
* Find a manpage.
*/
static int
{
struct man_node *p;
int ndirs = 0;
char *ldir;
char *slash;
/* For each path in MANPATH */
found = 0;
if (*localedir != '\0') {
if (ndirs != 0) {
DPRINTF("-- Locale specific subdir: %s\n",
ldir);
}
}
/*
* Locale mandir not valid, man page in locale
* mandir not found, or -a option present
*/
break;
}
if (!found) {
if (sargs) {
"No manual entry for %s in section(s) %s\n"),
} else {
}
}
return (!found);
}
/*
* For a specified manual directory, read, store and sort section subdirs.
* For each section specified, find and search matching subdirs.
*/
static void
{
char **dirv;
return;
if (lspec)
/* Search in the order specified by MANSECTS */
if (**secv == '\\') {
continue;
if (!all &&
== NULL) {
continue;
}
if (newsection == NULL)
newsection = "";
continue;
}
}
continue;
if (!all) {
while (*pdv) {
pdv++;
}
return;
}
dv++;
}
}
pdv++;
}
}
/*
* Sort directories.
*/
static void
{
struct dirent *d;
char **dv;
int entries = 0;
maxentries)) == NULL)
continue;
dv++;
entries++;
if (entries == maxentries) {
maxentries += MAXDIRS;
sizeof (char *) * maxentries)) == NULL)
}
}
}
*dv = 0;
}
/*
* Search a section subdir for a given manpage.
*/
static int
{
char *last;
int nlen;
return (0);
char *pname;
*last = '\0';
return (1);
}
}
return (0);
}
/*
* Check the hash table of old directory names to see if there is a
* new directory name.
*/
static char *
{
int i;
if (list) /* -l option fall through */
return (NULL);
} else {
return (NULL);
}
}
}
return (NULL);
}
/*
* Format the manpage.
*/
static int
{
char *cattool;
found++;
if (list) {
return (-1);
}
/* Can't do PS output if manpage doesn't exist */
return (-1);
/*
* If both manpage and catpage do not exist, manpname is
* broken symlink, most likely.
*/
/* Setup cattool */
cattool = "gzcat";
cattool = "bzcat";
else
cattool = "cat";
if (psoutput) {
"cd %s; %s %s | mandoc -Tps | lp -Tpostscript",
goto cmd;
} else if (lintout) {
"cd %s; %s %s | mandoc -Tlint",
goto cmd;
}
/*
* Output catpage if:
* - manpage doesn't exist
* - output width is standard and catpage is recent enough
*/
goto cmd;
}
if (manwidth > 0)
cmd:
if (!debug)
else
return (0);
}
/*
* Add <localedir> to the path.
*/
static char *
{
char *tmp;
return (tmp);
}
/*
* Get the order of sections from man.cf.
*/
static char *
{
char *nl;
return (NULL);
break;
}
*nl = '\0';
}
return (sect);
}
/*
* Initialize the bintoman array with appropriate device and inode info.
*/
static void
init_bintoman(void)
{
int i;
} else {
}
}
}
/*
* If a duplicate is found, return 1.
* If a duplicate is not found, add it to the dupnode list and return 0.
*/
static int
{
int i;
int dupfound;
/* If the path doesn't exist, treat it as a duplicate */
return (1);
/* If no sections were found in the man dir, treat it as duplicate */
return (1);
/*
* Find the dupnode structure for the previous time this directory
* was looked at. Device and inode numbers are compared so that
*/
break;
}
/*
* First time this directory has been seen. Add a new node to the
* head of the list. Since all entries are guaranteed to be unique
* copy all sections to new node.
*/
== NULL)
}
return (0);
}
/*
* Traverse the section vector in the man_node and the section list
* in dupnode cache to eliminate all duplicates from man_node.
*/
dupfound = 0;
dupfound = 1;
break;
}
}
if (dupfound) {
continue;
}
/*
* Update curdnp and set return value to indicate that this
* was not all duplicates.
*/
rv = 0;
}
return (rv);
}
/*
* Given a bindir, return corresponding mandir.
*/
static char *
{
char *mand, *p;
int i;
/* First look for known translations for specific bin paths */
return (NULL);
}
*p = '\0';
return (NULL);
}
if (p != NULL)
*p = ',';
return (mand);
}
}
/*
* and `dirname $bindir`/man
*/
return (NULL);
}
/*
* Advance to end of buffer, strip trailing /'s then remove last
* directory component.
*/
for (p = mand; *p != '\0'; p++)
;
for (; p > mand && *p == '/'; p--)
;
for (; p > mand && *p != '/'; p--)
;
if (p == mand && *p == '.') {
return (NULL);
}
for (; *p != '\0'; p++)
;
} else {
*p = '\0';
}
return (NULL);
}
return (mand);
}
/*
*/
*p = '\0';
return (NULL);
}
return (mand);
}
/*
*/
return (NULL);
}
/*
* Free a linked list of dupnode structs.
*/
void
{
}
}
}
/*
* Print manp linked list to stdout.
*/
void
{
char **secp;
colon[0] = ':';
/*
* If man.cf or a directory scan was used to create section
* list, do not print section list again. If the output of
* man -p is used to set MANPATH, subsequent runs of man
* required.
*/
continue;
/*
* Section deduplication may have eliminated some
* sections from the vector. Avoid displaying this
* detail which would appear as ",," in output
*/
if ((*secp)[0] != '\0')
}
}
(void) printf("\n");
}
static void
usage_man(void)
{
"usage: man [-alptw] [-M path] [-s section] name ...\n"
" man [-M path] [-s section] -k keyword ...\n"
" man [-M path] [-s section] -f keyword ...\n"));
exit(1);
}
static void
usage_whatapro(void)
{
"usage: %s [-M path] [-s section] keyword ...\n"),
exit(1);
}
static void
usage_catman(void)
{
"usage: catman [-M path] [-w]\n"));
exit(1);
}
static void
usage_makewhatis(void)
{
exit(1);
}