-/* $Id: manpath.c,v 1.3 2011/11/24 10:44:56 kristaps Exp $ */
+/* $Id: manpath.c,v 1.19 2014/11/27 00:30:40 schwarze Exp $ */
/*
- * Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2011, 2014 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
*
* Permission to use, copy, modify, and distribute this software for any
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#ifdef HAVE_CONFIG_H
#include "config.h"
-#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
#include <assert.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
-#include "mandoc.h"
+#include "mandoc_aux.h"
#include "manpath.h"
#define MAN_CONF_FILE "/etc/man.conf"
#define MAN_CONF_KEY "_whatdb"
-static void manpath_add(struct manpaths *, const char *);
+static void manpath_add(struct manpaths *, const char *, int);
+static void manpath_parseline(struct manpaths *, char *, int);
void
-manpath_parse(struct manpaths *dirs, char *defp, char *auxp)
+manpath_parse(struct manpaths *dirs, const char *file,
+ char *defp, char *auxp)
{
+#if HAVE_MANPATH
+ char cmd[(PATH_MAX * 3) + 20];
+ FILE *stream;
+ char *buf;
+ size_t sz, bsz;
+
+ strlcpy(cmd, "manpath", sizeof(cmd));
+ if (file) {
+ strlcat(cmd, " -C ", sizeof(cmd));
+ strlcat(cmd, file, sizeof(cmd));
+ }
+ if (auxp) {
+ strlcat(cmd, " -m ", sizeof(cmd));
+ strlcat(cmd, auxp, sizeof(cmd));
+ }
+ if (defp) {
+ strlcat(cmd, " -M ", sizeof(cmd));
+ strlcat(cmd, defp, sizeof(cmd));
+ }
+
+ /* Open manpath(1). Ignore errors. */
+
+ stream = popen(cmd, "r");
+ if (NULL == stream)
+ return;
+
+ buf = NULL;
+ bsz = 0;
+
+ /* Read in as much output as we can. */
+
+ do {
+ buf = mandoc_realloc(buf, bsz + 1024);
+ sz = fread(buf + bsz, 1, 1024, stream);
+ bsz += sz;
+ } while (sz > 0);
+
+ if ( ! ferror(stream) && feof(stream) &&
+ bsz && '\n' == buf[bsz - 1]) {
+ buf[bsz - 1] = '\0';
+ manpath_parseline(dirs, buf, 1);
+ }
+
+ free(buf);
+ pclose(stream);
+#else
+ char *insert;
+
+ /* Always prepend -m. */
+ manpath_parseline(dirs, auxp, 1);
+
+ /* If -M is given, it overrides everything else. */
+ if (NULL != defp) {
+ manpath_parseline(dirs, defp, 1);
+ return;
+ }
+
+ /* MANPATH and man.conf(5) cooperate. */
+ defp = getenv("MANPATH");
+ if (NULL == file)
+ file = MAN_CONF_FILE;
+
+ /* No MANPATH; use man.conf(5) only. */
+ if (NULL == defp || '\0' == defp[0]) {
+ manpath_manconf(dirs, file);
+ return;
+ }
+
+ /* Prepend man.conf(5) to MANPATH. */
+ if (':' == defp[0]) {
+ manpath_manconf(dirs, file);
+ manpath_parseline(dirs, defp, 0);
+ return;
+ }
- if (NULL != getenv("MANPATH"))
- defp = getenv("MANPATH");
+ /* Append man.conf(5) to MANPATH. */
+ if (':' == defp[strlen(defp) - 1]) {
+ manpath_parseline(dirs, defp, 0);
+ manpath_manconf(dirs, file);
+ return;
+ }
- if (NULL == defp)
- manpath_parseconf(dirs);
- else
- manpath_parseline(dirs, defp);
+ /* Insert man.conf(5) into MANPATH. */
+ insert = strstr(defp, "::");
+ if (NULL != insert) {
+ *insert++ = '\0';
+ manpath_parseline(dirs, defp, 0);
+ manpath_manconf(dirs, file);
+ manpath_parseline(dirs, insert + 1, 0);
+ return;
+ }
- manpath_parseline(dirs, auxp);
+ /* MANPATH overrides man.conf(5) completely. */
+ manpath_parseline(dirs, defp, 0);
+#endif
}
/*
* Parse a FULL pathname from a colon-separated list of arrays.
*/
-void
-manpath_parseline(struct manpaths *dirs, char *path)
+static void
+manpath_parseline(struct manpaths *dirs, char *path, int complain)
{
char *dir;
return;
for (dir = strtok(path, ":"); dir; dir = strtok(NULL, ":"))
- manpath_add(dirs, dir);
+ manpath_add(dirs, dir, complain);
}
/*
* Grow the array one-by-one for simplicity's sake.
*/
static void
-manpath_add(struct manpaths *dirs, const char *dir)
+manpath_add(struct manpaths *dirs, const char *dir, int complain)
{
char buf[PATH_MAX];
+ struct stat sb;
char *cp;
- int i;
+ size_t i;
- if (NULL == (cp = realpath(dir, buf)))
+ if (NULL == (cp = realpath(dir, buf))) {
+ if (complain) {
+ fputs("manpath: ", stderr);
+ perror(dir);
+ }
return;
+ }
for (i = 0; i < dirs->sz; i++)
if (0 == strcmp(dirs->paths[i], dir))
return;
- dirs->paths = mandoc_realloc
- (dirs->paths,
- ((size_t)dirs->sz + 1) * sizeof(char *));
-
- dirs->paths[dirs->sz++] = mandoc_strdup(cp);
-}
-
-void
-manpath_parseconf(struct manpaths *dirs)
-{
-#ifdef USE_MANPATH
- FILE *stream;
- char *buf;
- size_t sz, bsz;
-
- /* Open manpath(1). Ignore errors. */
-
- stream = popen("manpath", "r");
- if (NULL == stream)
+ if (stat(cp, &sb) == -1) {
+ if (complain) {
+ fputs("manpath: ", stderr);
+ perror(dir);
+ }
return;
-
- buf = NULL;
- bsz = 0;
-
- /* Read in as much output as we can. */
-
- do {
- buf = mandoc_realloc(buf, bsz + 1024);
- sz = fread(buf + (int)bsz, 1, 1024, stream);
- bsz += sz;
- } while (sz > 0);
-
- if ( ! ferror(stream) && feof(stream) &&
- bsz && '\n' == buf[bsz - 1]) {
- buf[bsz - 1] = '\0';
- manpath_parseline(dirs, buf);
}
- free(buf);
- pclose(stream);
-#else
- manpath_manconf(MAN_CONF_FILE, dirs);
-#endif
+ dirs->paths = mandoc_reallocarray(dirs->paths,
+ dirs->sz + 1, sizeof(char *));
+
+ dirs->paths[dirs->sz++] = mandoc_strdup(cp);
}
void
manpath_free(struct manpaths *p)
{
- int i;
+ size_t i;
for (i = 0; i < p->sz; i++)
free(p->paths[i]);
}
void
-manpath_manconf(const char *file, struct manpaths *dirs)
+manpath_manconf(struct manpaths *dirs, const char *file)
{
FILE *stream;
char *p, *q;
- size_t len, keysz;
+ size_t len, keysz;
keysz = strlen(MAN_CONF_KEY);
assert(keysz > 0);
if (strncmp(MAN_CONF_KEY, p, keysz))
continue;
p += keysz;
- while (isspace(*p))
+ while (isspace((unsigned char)*p))
p++;
if ('\0' == *p)
continue;
if (NULL == (q = strrchr(p, '/')))
continue;
*q = '\0';
- manpath_add(dirs, p);
+ manpath_add(dirs, p, 0);
}
fclose(stream);