d09fe2c467
- avoid double slashes when displaying man pages (got tired of '/usr/share/man//cat1/man.0'). - got rid of __P() while working on it. - incorporate some of my old notes explaining how manpath works into the comments of the code itself. - renamed some of the vars so that the code is consistent throughout (and hopefully clearer and easier to understand) - fixed relative man paths for multiple man pages (man did a chdir() on the first man page it had to format --- this broke any remaining relative path man pages left to process). save old directory and fchdir() back to it after formatting. - improved doc on "man -h" which does more than just whatis(1) [e.g. "man -h fopen" prints the required include files and the prototypes rather than just the one-liner you get with whatis(1)] - manconf.c now fills in the "len" length field in the TAG/ENTRY structures (man now uses len). revise man.conf file reading stuff to return error on failure in addentry/gettag (fka getlist) rather than just err()ing out. this allows man(1) to call cleanup and delete its tmp files rather than just leave them floating. revise other apps using this code (makewhatis, apropos, catman, whatis) to expect this. also remove __P on updated files.
256 lines
6.4 KiB
C
256 lines
6.4 KiB
C
/* $NetBSD: manconf.c,v 1.5 2006/04/10 14:39:06 chuck Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 1989, 1993, 1995
|
|
* The Regents of the University of California. All rights reserved.
|
|
*
|
|
* 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. Neither the name of the University 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 REGENTS 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 REGENTS 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.
|
|
*/
|
|
|
|
/*
|
|
* manconf.c: provides interface for reading man.conf files
|
|
*
|
|
* note that this code is shared across all programs that read man.conf.
|
|
* (currently: apropos, catman, makewhatis, man, and whatis...)
|
|
*/
|
|
|
|
#if HAVE_NBTOOL_CONFIG_H
|
|
#include "nbtool_config.h"
|
|
#endif
|
|
|
|
#include <sys/cdefs.h>
|
|
#ifndef lint
|
|
#if 0
|
|
static char sccsid[] = "@(#)config.c 8.8 (Berkeley) 1/31/95";
|
|
#else
|
|
__RCSID("$NetBSD: manconf.c,v 1.5 2006/04/10 14:39:06 chuck Exp $");
|
|
#endif
|
|
#endif /* not lint */
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/queue.h>
|
|
|
|
#include <ctype.h>
|
|
#include <err.h>
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "manconf.h"
|
|
#include "pathnames.h"
|
|
|
|
TAILQ_HEAD(_head, _tag);
|
|
static struct _head head; /* 'head' -- top level data structure */
|
|
|
|
/*
|
|
* xstrdup: like strdup, but also returns length of string in lenp
|
|
*/
|
|
static char *
|
|
xstrdup(const char *str, size_t *lenp)
|
|
{
|
|
size_t len;
|
|
char *copy;
|
|
|
|
len = strlen(str) + 1;
|
|
copy = malloc(len);
|
|
if (!copy)
|
|
return(NULL);
|
|
memcpy(copy, str, len);
|
|
if (lenp)
|
|
*lenp = len - 1; /* subtract out the null */
|
|
return(copy);
|
|
}
|
|
|
|
/*
|
|
* config --
|
|
*
|
|
* Read the configuration file and build a doubly linked
|
|
* list off of "head" that looks like:
|
|
*
|
|
* tag1 <-> entry <-> entry <-> entry
|
|
* |
|
|
* tag2 <-> entry <-> entry <-> entry
|
|
*
|
|
* note: will err/errx out on error (fopen or malloc failure)
|
|
*/
|
|
void
|
|
config(const char *fname)
|
|
{
|
|
TAG *tp;
|
|
FILE *cfp;
|
|
size_t len;
|
|
int lcnt;
|
|
char *p, *t, type;
|
|
|
|
if (fname == NULL)
|
|
fname = _PATH_MANCONF;
|
|
if ((cfp = fopen(fname, "r")) == NULL)
|
|
err(1, "%s", fname);
|
|
TAILQ_INIT(&head);
|
|
for (lcnt = 1; (p = fgetln(cfp, &len)) != NULL; ++lcnt) {
|
|
if (len == 1) /* Skip empty lines. */
|
|
continue;
|
|
if (p[len - 1] != '\n') { /* Skip corrupted lines. */
|
|
warnx("%s: line %d corrupted", fname, lcnt);
|
|
continue;
|
|
}
|
|
p[len - 1] = '\0'; /* Terminate the line. */
|
|
|
|
/* Skip leading space. */
|
|
for (; *p != '\0' && isspace((unsigned char)*p); ++p);
|
|
/* Skip empty/comment lines. */
|
|
if (*p == '\0' || *p == '#')
|
|
continue;
|
|
/* Find first token. */
|
|
for (t = p; *t && !isspace((unsigned char)*t); ++t);
|
|
if (*t == '\0') /* Need more than one token.*/
|
|
continue;
|
|
*t = '\0';
|
|
|
|
tp = gettag(p, 1);
|
|
if (!tp)
|
|
errx(1, "gettag: malloc failed");
|
|
|
|
/*
|
|
* Attach new records. Check to see if it is a
|
|
* section record or not.
|
|
*/
|
|
|
|
if (*p == '_') { /* not a section record */
|
|
/*
|
|
* Special cases: _build and _crunch take the
|
|
* rest of the line as a single entry.
|
|
*/
|
|
if (!strcmp(p, "_build") || !strcmp(p, "_crunch")) {
|
|
/*
|
|
* The reason we're not just using
|
|
* strtok(3) for all of the parsing is
|
|
* so we don't get caught if a line
|
|
* has only a single token on it.
|
|
*/
|
|
while (*++t && isspace((unsigned char)*t));
|
|
if (addentry(tp, t, 0) < 0)
|
|
errx(1, "addentry: malloc failed");
|
|
} else {
|
|
for(++t; (p = strtok(t, " \t\n")) != NULL;
|
|
t = NULL) {
|
|
if (addentry(tp, p, 0) < 0)
|
|
errx(1,
|
|
"addentry: malloc failed");
|
|
}
|
|
}
|
|
|
|
} else { /* section record */
|
|
|
|
/*
|
|
* section entries can either be all absolute
|
|
* paths or all relative paths, but not both.
|
|
*/
|
|
type = (TAILQ_FIRST(&tp->entrylist) != NULL) ?
|
|
*(TAILQ_FIRST(&tp->entrylist)->s) : 0;
|
|
|
|
for (++t; (p = strtok(t, " \t\n")) != NULL; t = NULL) {
|
|
|
|
/* ensure an assigned type */
|
|
if (type == 0)
|
|
type = *p;
|
|
|
|
/* check for illegal mix */
|
|
if (*p != type) {
|
|
warnx("section %s: %s: invalid entry, does not match previous types",
|
|
tp->s, p);
|
|
warnx("man.conf cannot mix absolute and relative paths in an entry");
|
|
continue;
|
|
}
|
|
if (addentry(tp, p, 0) < 0)
|
|
errx(1, "addentry: malloc failed");
|
|
}
|
|
}
|
|
}
|
|
|
|
fclose(cfp);
|
|
}
|
|
|
|
/*
|
|
* gettag --
|
|
* if (!create) return tag for given name if it exists, or NULL otherwise
|
|
*
|
|
* if (create) return tag for given name if it exists, try and create
|
|
* a new tag if it does not exist. return NULL if unable to create new
|
|
* tag.
|
|
*/
|
|
TAG *
|
|
gettag(const char *name, int create)
|
|
{
|
|
TAG *tp;
|
|
|
|
TAILQ_FOREACH(tp, &head, q)
|
|
if (!strcmp(name, tp->s))
|
|
return (tp);
|
|
if (!create)
|
|
return(NULL);
|
|
|
|
/* try and add it in */
|
|
tp = malloc(sizeof(*tp));
|
|
if (tp)
|
|
tp->s = xstrdup(name, &tp->len);
|
|
if (!tp || !tp->s) {
|
|
if (tp)
|
|
free(tp);
|
|
return(NULL);
|
|
}
|
|
TAILQ_INIT(&tp->entrylist);
|
|
TAILQ_INSERT_TAIL(&head, tp, q);
|
|
return (tp);
|
|
}
|
|
|
|
/*
|
|
* addentry --
|
|
* add an entry to a list.
|
|
* returns -1 if malloc failed, otherwise 0.
|
|
*/
|
|
int
|
|
addentry(TAG *tp, const char *newent, int head)
|
|
{
|
|
ENTRY *ep;
|
|
|
|
ep = malloc(sizeof(*ep));
|
|
if (ep)
|
|
ep->s = xstrdup(newent, &ep->len);
|
|
if (!ep || !ep->s) {
|
|
if (ep)
|
|
free(ep);
|
|
return(-1);
|
|
}
|
|
|
|
if (head)
|
|
TAILQ_INSERT_HEAD(&tp->entrylist, ep, q);
|
|
else
|
|
TAILQ_INSERT_TAIL(&tp->entrylist, ep, q);
|
|
|
|
return(0);
|
|
}
|