NetBSD/libexec/makewhatis/makewhatis.c
jdolecek 534dd161ea If called without arguments, parse /etc/man.conf and regenerate the whatis
databases specified there. By default, the individual databases
are actually generated by forked children in this case, for
performance reasons. This feature can be switched off by new -f flag.
If the configuration file can't be parsed or doesn't contain any
_whatdb entries, the code falls back to /usr/share/man as before.

Added -C, which allows to specify alternate configuration file. This
  is compatible with apropos(1) and whatis(1) flag of same name.

Update manpage accordingly and document behaviour a bit better.
Also add a HISTORY section, hopefully correct (done using CVS logs).

This solves toolchain/5231 by Tim Rightnour and bin/7696 by Allen Briggs.
2002-03-08 21:59:07 +00:00

1028 lines
21 KiB
C

/* $NetBSD: makewhatis.c,v 1.23 2002/03/08 21:59:07 jdolecek Exp $ */
/*-
* Copyright (c) 1999 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Matthias Scheler.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the NetBSD
* Foundation, Inc. and its contributors.
* 4. Neither the name of The NetBSD Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#if defined(__COPYRIGHT) && !defined(lint)
__COPYRIGHT("@(#) Copyright (c) 1999 The NetBSD Foundation, Inc.\n\
All rights reserved.\n");
#endif /* not lint */
#if defined(__RCSID) && !defined(lint)
__RCSID("$NetBSD: makewhatis.c,v 1.23 2002/03/08 21:59:07 jdolecek Exp $");
#endif /* not lint */
#if HAVE_CONFIG_H
#include "config.h"
#endif
#include <sys/types.h>
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <fts.h>
#include <glob.h>
#include <locale.h>
#include <paths.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <zlib.h>
#include <man/config.h>
#include <man/pathnames.h>
typedef struct manpagestruct manpage;
struct manpagestruct {
manpage *mp_left,*mp_right;
ino_t mp_inode;
size_t mp_sdoff;
size_t mp_sdlen;
char mp_name[1];
};
typedef struct whatisstruct whatis;
struct whatisstruct {
whatis *wi_left,*wi_right;
char *wi_data;
char wi_prefix[1];
};
int main(int, char * const *);
char *findwhitespace(char *);
char *strmove(char *,char *);
char *GetS(gzFile, char *, size_t);
int manpagesection(char *);
char *createsectionstring(char *);
void addmanpage(manpage **, ino_t, char *, size_t, size_t);
void addwhatis(whatis **, char *, char *);
char *replacestring(char *, char *, char *);
void catpreprocess(char *);
char *parsecatpage(gzFile *);
int manpreprocess(char *);
char *nroff(gzFile *);
char *parsemanpage(gzFile *, int);
char *getwhatisdata(char *);
void processmanpages(manpage **,whatis **);
void dumpwhatis(FILE *, whatis *);
void *emalloc(size_t);
char *estrdup(const char *);
static int makewhatis(char * const *manpath);
char * const default_manpath[] = {
"/usr/share/man",
NULL
};
const char *sectionext = "0123456789ln";
const char *whatisdb = _PATH_WHATIS;
int
main(int argc, char *const *argv)
{
char * const *manpath;
int c, dofork=1;
const char *conffile = NULL;
ENTRY *ep;
TAG *tp;
int rv, jobs = 0, status;
glob_t pg;
char *paths[2], **p, *sl;
int retval = EXIT_SUCCESS;
(void)setlocale(LC_ALL, "");
while((c = getopt(argc, argv, "C:f")) != -1) {
switch(c) {
case 'C':
conffile = optarg;
break;
case 'f':
/* run all processing on foreground */
dofork = 0;
break;
default:
fprintf(stderr, "Usage: %s [-C file] [-f] [path ...]\n",
getprogname());
exit(EXIT_FAILURE);
}
}
argc -= optind;
argv += optind;
if (argc >= 2) {
manpath = &argv[0];
mkwhatis:
return (makewhatis(manpath));
}
/*
* Try read config file, fallback to default_manpath[]
* if man.conf not available.
*/
config(conffile);
if ((tp = getlist("_whatdb")) == NULL) {
manpath = default_manpath;
goto mkwhatis;
}
/* Build individual databases */
paths[1] = NULL;
TAILQ_FOREACH(ep, &tp->list, q) {
if ((rv = glob(ep->s,
GLOB_BRACE | GLOB_NOSORT | GLOB_ERR | GLOB_NOCHECK,
NULL, &pg)) != 0)
err(EXIT_FAILURE, "glob('%s')", ep->s);
/* We always have something to work with here */
for (p = pg.gl_pathv; *p; p++) {
sl = strrchr(*p, '/');
if (!sl)
err(EXIT_FAILURE, "glob: _whatdb entry '%s' doesn't contain slash", ep->s);
/*
* Cut the last component of path, leaving just
* the directory. We will use the result as root
* for manpage search.
* glob malloc()s space for the paths, so it's
* okay to change it in-place.
*/
*sl = '\0';
paths[0] = *p;
if (!dofork) {
/* Do not fork child */
makewhatis(paths);
continue;
}
switch (fork()) {
case 0:
exit(makewhatis(paths));
break;
case -1:
warn("fork");
makewhatis(paths);
break;
default:
jobs++;
break;
}
}
globfree(&pg);
}
/* Wait for the childern to finish */
while(jobs > 0) {
wait(&status);
if (!WIFEXITED(status) || WEXITSTATUS(status) != EXIT_SUCCESS)
retval = EXIT_FAILURE;
jobs--;
}
return (retval);
}
static int
makewhatis(char * const * manpath)
{
FTS *fts;
FTSENT *fe;
manpage *source;
whatis *dest;
FILE *out;
size_t sdoff, sdlen;
if ((fts = fts_open(manpath, FTS_LOGICAL, NULL)) == NULL)
err(EXIT_FAILURE, "Cannot open `%s'", *manpath);
source = NULL;
while ((fe = fts_read(fts)) != NULL) {
switch (fe->fts_info) {
case FTS_F:
if (manpagesection(fe->fts_path) >= 0) {
/*
* Get manpage subdirectory prefix. Most
* commonly, this is arch-specific subdirectory.
*/
if (fe->fts_level >= 3) {
int sl = fe->fts_level - 1;
const char *s, *lsl=NULL;
s = &fe->fts_path[fe->fts_pathlen-1];
for(; sl > 0; sl--) {
s--;
while(s[0] != '/') s--;
if (!lsl)
lsl = s;
}
/* Include trailing '/', so we get
* 'arch/'. */
sdoff = s + 1 - fe->fts_path;
sdlen = lsl - s + 1;
} else {
sdoff = 0;
sdlen = 0;
}
addmanpage(&source, fe->fts_statp->st_ino,
fe->fts_path, sdoff, sdlen);
}
/*FALLTHROUGH*/
case FTS_D:
case FTS_DC:
case FTS_DEFAULT:
case FTS_DP:
case FTS_SLNONE:
break;
default:
errno = fe->fts_errno;
err(EXIT_FAILURE, "Error reading `%s'", fe->fts_path);
}
}
(void)fts_close(fts);
dest = NULL;
processmanpages(&source, &dest);
if (chdir(manpath[0]) == -1)
err(EXIT_FAILURE, "Cannot change dir to `%s'", manpath[0]);
(void)unlink(whatisdb);
if ((out = fopen(whatisdb, "w")) == NULL)
err(EXIT_FAILURE, "Cannot open `%s'", whatisdb);
dumpwhatis(out, dest);
if (fchmod(fileno(out), S_IRUSR|S_IRGRP|S_IROTH) == -1)
err(EXIT_FAILURE, "Cannot chmod `%s'", whatisdb);
if (fclose(out) != 0)
err(EXIT_FAILURE, "Cannot close `%s'", whatisdb);
return EXIT_SUCCESS;
}
char *
findwhitespace(char *str)
{
while (!isspace((unsigned char)*str))
if (*str++ == '\0') {
str = NULL;
break;
}
return str;
}
char
*strmove(char *dest,char *src)
{
return memmove(dest, src, strlen(src) + 1);
}
char *
GetS(gzFile in, char *buffer, size_t length)
{
char *ptr;
if (((ptr = gzgets(in, buffer, (int)length)) != NULL) && (*ptr == '\0'))
ptr = NULL;
return ptr;
}
int
manpagesection(char *name)
{
char *ptr;
if ((ptr = strrchr(name, '/')) != NULL)
ptr++;
else
ptr = name;
while ((ptr = strchr(ptr, '.')) != NULL) {
int section;
ptr++;
section=0;
while (sectionext[section] != '\0')
if (sectionext[section] == *ptr)
return section;
else
section++;
}
return -1;
}
char *
createsectionstring(char *section_id)
{
char *section = emalloc(strlen(section_id) + 7);
section[0] = ' ';
section[1] = '(';
(void)strcat(strcpy(&section[2], section_id), ") - ");
return section;
}
void
addmanpage(manpage **tree,ino_t inode,char *name, size_t sdoff, size_t sdlen)
{
manpage *mp;
while ((mp = *tree) != NULL) {
if (mp->mp_inode == inode)
return;
tree = inode < mp->mp_inode ? &mp->mp_left : &mp->mp_right;
}
mp = emalloc(sizeof(manpage) + strlen(name));
mp->mp_left = NULL;
mp->mp_right = NULL;
mp->mp_inode = inode;
mp->mp_sdoff = sdoff;
mp->mp_sdlen = sdlen;
(void)strcpy(mp->mp_name, name);
*tree = mp;
}
void
addwhatis(whatis **tree, char *data, char *prefix)
{
whatis *wi;
int result;
while (isspace((unsigned char)*data))
data++;
if (*data == '/') {
char *ptr;
ptr = ++data;
while ((*ptr != '\0') && !isspace((unsigned char)*ptr))
if (*ptr++ == '/')
data = ptr;
}
while ((wi = *tree) != NULL) {
result = strcmp(data, wi->wi_data);
if (result == 0) return;
tree = result < 0 ? &wi->wi_left : &wi->wi_right;
}
wi = emalloc(sizeof(whatis) + strlen(prefix));
wi->wi_left = NULL;
wi->wi_right = NULL;
wi->wi_data = data;
if (prefix[0] != '\0')
(void) strcpy(wi->wi_prefix, prefix);
else
wi->wi_prefix[0] = '\0';
*tree = wi;
}
void
catpreprocess(char *from)
{
char *to;
to = from;
while (isspace((unsigned char)*from)) from++;
while (*from != '\0')
if (isspace((unsigned char)*from)) {
while (isspace((unsigned char)*++from));
if (*from != '\0')
*to++ = ' ';
}
else if (*(from + 1) == '\10')
from += 2;
else
*to++ = *from++;
*to = '\0';
}
char *
replacestring(char *string, char *old, char *new)
{
char *ptr, *result;
size_t slength, olength, nlength, pos;
if (new == NULL)
return estrdup(string);
ptr = strstr(string, old);
if (ptr == NULL)
return estrdup(string);
slength = strlen(string);
olength = strlen(old);
nlength = strlen(new);
result = emalloc(slength - olength + nlength + 1);
pos = ptr - string;
(void)memcpy(result, string, pos);
(void)memcpy(&result[pos], new, nlength);
(void)strcpy(&result[pos + nlength], &string[pos + olength]);
return result;
}
char *
parsecatpage(gzFile *in)
{
char buffer[8192];
char *section, *ptr, *last;
size_t size;
do {
if (GetS(in, buffer, sizeof(buffer)) == NULL)
return NULL;
}
while (buffer[0] == '\n');
section = NULL;
if ((ptr = strchr(buffer, '(')) != NULL) {
if ((last = strchr(ptr + 1, ')')) !=NULL) {
size_t length;
length = last - ptr + 1;
section = emalloc(length + 5);
*section = ' ';
(void) memcpy(section + 1, ptr, length);
(void) strcpy(section + 1 + length, " - ");
}
}
for (;;) {
if (GetS(in, buffer, sizeof(buffer)) == NULL) {
free(section);
return NULL;
}
catpreprocess(buffer);
if (strncmp(buffer, "NAME", 4) == 0)
break;
}
ptr = last = buffer;
size = sizeof(buffer) - 1;
while ((size > 0) && (GetS(in, ptr, size) != NULL)) {
int length;
catpreprocess(ptr);
length = strlen(ptr);
if (length == 0) {
*last = '\0';
ptr = replacestring(buffer, " - ", section);
free(section);
return ptr;
}
if ((length > 1) && (ptr[length - 1] == '-') &&
isalpha(ptr[length - 2]))
last = &ptr[--length];
else {
last = &ptr[length++];
*last = ' ';
}
ptr += length;
size -= length;
}
free(section);
return NULL;
}
int
manpreprocess(char *line)
{
char *from, *to;
to = from = line;
while (isspace((unsigned char)*from)) from++;
if (strncmp(from, ".\\\"", 3) == 0)
return 1;
while (*from != '\0')
if (isspace((unsigned char)*from)) {
while (isspace((unsigned char)*++from));
if ((*from != '\0') && (*from != ','))
*to++ = ' ';
}
else if (*from == '\\')
switch (*++from) {
case '\0':
case '-':
break;
case 'f':
case 's':
from++;
if ((*from=='+') || (*from=='-'))
from++;
while (isdigit(*from))
from++;
break;
default:
from++;
}
else
if (*from == '"')
from++;
else
*to++ = *from++;
*to = '\0';
if (strncasecmp(line, ".Xr", 3) == 0) {
char *sect;
from = line + 3;
if (isspace((unsigned char)*from))
from++;
if ((sect = findwhitespace(from)) != NULL) {
size_t length;
char *trail;
*sect++ = '\0';
if ((trail = findwhitespace(sect)) != NULL)
*trail++ = '\0';
length = strlen(from);
(void) memmove(line, from, length);
line[length++] = '(';
to = &line[length];
length = strlen(sect);
(void) memmove(to, sect, length);
if (trail == NULL) {
(void) strcpy(&to[length], ")");
} else {
to += length;
*to++ = ')';
length = strlen(trail);
(void) memmove(to, trail, length + 1);
}
}
}
return 0;
}
char *
nroff(gzFile *in)
{
char tempname[MAXPATHLEN], buffer[65536], *data;
int tempfd, bytes, pipefd[2], status;
static int devnull = -1;
pid_t child;
if (gzrewind(in) < 0)
err(EXIT_FAILURE, "Cannot rewind pipe");
if ((devnull < 0) &&
((devnull = open(_PATH_DEVNULL, O_WRONLY, 0)) < 0))
err(EXIT_FAILURE, "Cannot open `/dev/null'");
(void)strcpy(tempname, _PATH_TMP "makewhatis.XXXXXX");
if ((tempfd = mkstemp(tempname)) == -1)
err(EXIT_FAILURE, "Cannot create temp file");
while ((bytes = gzread(in, buffer, sizeof(buffer))) > 0)
if (write(tempfd, buffer, (size_t)bytes) != bytes) {
bytes = -1;
break;
}
if (bytes < 0) {
(void)close(tempfd);
(void)unlink(tempname);
err(EXIT_FAILURE, "Read from pipe failed");
}
if (lseek(tempfd, (off_t)0, SEEK_SET) == (off_t)-1) {
(void)close(tempfd);
(void)unlink(tempname);
err(EXIT_FAILURE, "Cannot rewind temp file");
}
if (pipe(pipefd) == -1) {
(void)close(tempfd);
(void)unlink(tempname);
err(EXIT_FAILURE, "Cannot create pipe");
}
switch (child = vfork()) {
case -1:
(void)close(pipefd[1]);
(void)close(pipefd[0]);
(void)close(tempfd);
(void)unlink(tempname);
err(EXIT_FAILURE, "Fork failed");
/* NOTREACHED */
case 0:
(void)close(pipefd[0]);
if (tempfd != STDIN_FILENO) {
(void)dup2(tempfd, STDIN_FILENO);
(void)close(tempfd);
}
if (pipefd[1] != STDOUT_FILENO) {
(void)dup2(pipefd[1], STDOUT_FILENO);
(void)close(pipefd[1]);
}
if (devnull != STDERR_FILENO) {
(void)dup2(devnull, STDERR_FILENO);
(void)close(devnull);
}
(void)execlp("nroff", "nroff", "-S", "-man", NULL);
_exit(EXIT_FAILURE);
/*NOTREACHED*/
default:
(void)close(pipefd[1]);
(void)close(tempfd);
break;
}
if ((in = gzdopen(pipefd[0], "r")) == NULL) {
if (errno == 0)
errno = ENOMEM;
(void)close(pipefd[0]);
(void)kill(child, SIGTERM);
while (waitpid(child, NULL, 0) != child);
(void)unlink(tempname);
err(EXIT_FAILURE, "Cannot read from pipe");
}
data = parsecatpage(in);
while (gzread(in, buffer, sizeof(buffer)) > 0);
(void)gzclose(in);
while (waitpid(child, &status, 0) != child);
if ((data != NULL) &&
!(WIFEXITED(status) && (WEXITSTATUS(status) == 0))) {
free(data);
errx(EXIT_FAILURE, "nroff exited with %d status",
WEXITSTATUS(status));
}
(void)unlink(tempname);
return data;
}
char *
parsemanpage(gzFile *in, int defaultsection)
{
char *section, buffer[8192], *ptr;
section = NULL;
do {
if (GetS(in, buffer, sizeof(buffer) - 1) == NULL) {
free(section);
return NULL;
}
if (manpreprocess(buffer))
continue;
if (strncasecmp(buffer, ".Dt", 3) == 0) {
char *end;
ptr = &buffer[3];
if (isspace((unsigned char)*ptr))
ptr++;
if ((ptr = findwhitespace(ptr)) == NULL)
continue;
if ((end = findwhitespace(++ptr)) != NULL)
*end = '\0';
free(section);
section = createsectionstring(ptr);
}
else if (strncasecmp(buffer, ".TH", 3) == 0) {
ptr = &buffer[3];
while (isspace((unsigned char)*ptr))
ptr++;
if ((ptr = findwhitespace(ptr)) != NULL) {
char *next;
while (isspace((unsigned char)*ptr))
ptr++;
if ((next = findwhitespace(ptr)) != NULL)
*next = '\0';
free(section);
section = createsectionstring(ptr);
}
}
else if (strncasecmp(buffer, ".Ds", 3) == 0) {
free(section);
return NULL;
}
} while (strncasecmp(buffer, ".Sh NAME", 8) != 0);
do {
if (GetS(in, buffer, sizeof(buffer) - 1) == NULL) {
free(section);
return NULL;
}
} while (manpreprocess(buffer));
if (strncasecmp(buffer, ".Nm", 3) == 0) {
size_t length, offset;
ptr = &buffer[3];
while (isspace((unsigned char)*ptr))
ptr++;
length = strlen(ptr);
if ((length > 1) && (ptr[length - 1] == ',') &&
isspace((unsigned char)ptr[length - 2])) {
ptr[--length] = '\0';
ptr[length - 1] = ',';
}
(void) memmove(buffer, ptr, length + 1);
offset = length + 3;
ptr = &buffer[offset];
for (;;) {
size_t more;
if ((sizeof(buffer) == offset) ||
(GetS(in, ptr, sizeof(buffer) - offset)
== NULL)) {
free(section);
return NULL;
}
if (manpreprocess(ptr))
continue;
if (strncasecmp(ptr, ".Nm", 3) != 0) break;
ptr += 3;
if (isspace((unsigned char)*ptr))
ptr++;
buffer[length++] = ' ';
more = strlen(ptr);
if ((more > 1) && (ptr[more - 1] == ',') &&
isspace((unsigned char)ptr[more - 2])) {
ptr[--more] = '\0';
ptr[more - 1] = ',';
}
(void) memmove(&buffer[length], ptr, more + 1);
length += more;
offset = length + 3;
ptr = &buffer[offset];
}
if (strncasecmp(ptr, ".Nd", 3) == 0) {
(void) strcpy(&buffer[length], " -");
while (strncasecmp(ptr, ".Sh", 3) != 0) {
int more;
if (*ptr == '.') {
char *space;
if (strncasecmp(ptr, ".Nd", 3) != 0) {
free(section);
return NULL;
}
space = findwhitespace(ptr);
if (space == NULL)
ptr = "";
else {
space++;
(void) strmove(ptr, space);
}
}
if (*ptr != '\0') {
buffer[offset - 1] = ' ';
more = strlen(ptr) + 1;
offset += more;
}
ptr = &buffer[offset];
if ((sizeof(buffer) == offset) ||
(GetS(in, ptr, sizeof(buffer) - offset)
== NULL)) {
free(section);
return NULL;
}
if (manpreprocess(ptr))
*ptr = '\0';
}
}
}
else {
int offset;
if (*buffer == '.') {
char *space;
if ((space = findwhitespace(&buffer[1])) == NULL) {
free(section);
return NULL;
}
space++;
(void) strmove(buffer, space);
}
offset = strlen(buffer) + 1;
for (;;) {
int more;
ptr = &buffer[offset];
if ((sizeof(buffer) == offset) ||
(GetS(in, ptr, sizeof(buffer) - offset)
== NULL)) {
free(section);
return NULL;
}
if (manpreprocess(ptr) || (*ptr == '\0'))
continue;
if ((strncasecmp(ptr, ".Sh", 3) == 0) ||
(strncasecmp(ptr, ".Ss", 3) == 0))
break;
if (*ptr == '.') {
char *space;
if ((space = findwhitespace(ptr)) == NULL) {
continue;
}
space++;
(void) memmove(ptr, space, strlen(space) + 1);
}
buffer[offset - 1] = ' ';
more = strlen(ptr);
if ((more > 1) && (ptr[more - 1] == ',') &&
isspace((unsigned char)ptr[more - 2])) {
ptr[more - 1] = '\0';
ptr[more - 2] = ',';
}
else more++;
offset += more;
}
}
if (section == NULL) {
char sectionbuffer[24];
(void) sprintf(sectionbuffer, " (%c) - ",
sectionext[defaultsection]);
ptr = replacestring(buffer, " - ", sectionbuffer);
}
else {
ptr = replacestring(buffer, " - ", section);
free(section);
}
return ptr;
}
char *
getwhatisdata(char *name)
{
gzFile *in;
char *data;
int section;
if ((in = gzopen(name, "r")) == NULL) {
if (errno == 0)
errno = ENOMEM;
err(EXIT_FAILURE, "Cannot open `%s'", name);
/* NOTREACHED */
}
section = manpagesection(name);
if (section == 0)
data = parsecatpage(in);
else {
data = parsemanpage(in, section);
if (data == NULL)
data = nroff(in);
}
(void) gzclose(in);
return data;
}
void
processmanpages(manpage **source, whatis **dest)
{
manpage *mp;
char sd[128];
mp = *source;
*source = NULL;
while (mp != NULL) {
manpage *obsolete;
char *data;
if (mp->mp_left != NULL)
processmanpages(&mp->mp_left,dest);
if ((data = getwhatisdata(mp->mp_name)) != NULL) {
/* Pass eventual directory prefix to addwhatis() */
if (mp->mp_sdlen > 0 && mp->mp_sdlen < sizeof(sd)-1)
strlcpy(sd, &mp->mp_name[mp->mp_sdoff],
mp->mp_sdlen);
else
sd[0] = '\0';
addwhatis(dest, data, sd);
}
obsolete = mp;
mp = mp->mp_right;
free(obsolete);
}
}
void
dumpwhatis(FILE *out, whatis *tree)
{
while (tree != NULL) {
if (tree->wi_left)
dumpwhatis(out, tree->wi_left);
if ((tree->wi_data[0] && fputs(tree->wi_prefix, out) == EOF) ||
(fputs(tree->wi_data, out) == EOF) ||
(fputc('\n', out) == EOF))
err(EXIT_FAILURE, "Write failed");
tree = tree->wi_right;
}
}
void *
emalloc(size_t len)
{
void *ptr;
if ((ptr = malloc(len)) == NULL)
err(EXIT_FAILURE, "malloc %lu failed", (unsigned long)len);
return ptr;
}
char *
estrdup(const char *str)
{
char *ptr;
if ((ptr = strdup(str)) == NULL)
err(EXIT_FAILURE, "strdup failed");
return ptr;
}