summaryrefslogtreecommitdiffstats
path: root/src/manfile.c
diff options
context:
space:
mode:
authorKaz Kylheku <kaz@kylheku.com>2014-09-13 09:43:21 -0700
committerKaz Kylheku <kaz@kylheku.com>2014-09-13 09:43:21 -0700
commit5280f9a0cd1f9ba200422ebba65d1e0133410995 (patch)
treebf85ce4e320a769d7e0903ff52ccfde13a422666 /src/manfile.c
downloadman-5280f9a0cd1f9ba200422ebba65d1e0133410995.tar.gz
man-5280f9a0cd1f9ba200422ebba65d1e0133410995.tar.bz2
man-5280f9a0cd1f9ba200422ebba65d1e0133410995.zip
Initial.man-1.6g
Diffstat (limited to 'src/manfile.c')
-rw-r--r--src/manfile.c337
1 files changed, 337 insertions, 0 deletions
diff --git a/src/manfile.c b/src/manfile.c
new file mode 100644
index 0000000..0df62ea
--- /dev/null
+++ b/src/manfile.c
@@ -0,0 +1,337 @@
+/*
+ * manfile.c - aeb, 971231
+ *
+ * Used both by man and man2html - be careful with printing!
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "glob.h"
+#include "util.h"
+#include "manfile.h"
+#include "gripes.h"
+#include "man.h" /* for debug */
+
+static int standards;
+static const char *((*to_cat_filename)(const char *man_filename,
+ const char *ext, int flags));
+
+/*
+ * Append the struct or chain A to the chain HEAD.
+ */
+static void
+append(struct manpage **head, struct manpage *a) {
+ struct manpage *p;
+
+ if (a) {
+ if (*head) {
+ p = *head;
+ while(p->next)
+ p = p->next;
+ p->next = a;
+ } else
+ *head = a;
+ }
+}
+
+
+static int
+my_lth(const char *s) {
+ return s ? strlen(s) : 0;
+}
+
+/*
+ * Find the files of the form DIR/manSEC/NAME.EXT etc.
+ * Use "man" for TYPE_MAN, "cat" for TYPE_SCAT, and
+ * apply convert_to_cat() to the man version for TYPE_CAT.
+ *
+ * Some HP systems use /usr/man/man1.Z/name.1, where name.1 is
+ * compressed - yuk. We can handle this by using section 1.Z
+ * instead of 1 and assuming that the man page is compressed
+ * if the directory name ends in .Z.
+ *
+ * Some Sun systems use /usr/share/man/sman1/man.1 and
+ * /usr/share/man/sman1m/mkfs.1m.
+ *
+ * We support HTML filenames of the following form:
+ * /usr/share/man/sman1m/mkfs.1m.html, optionally followed
+ * by a compression suffix.
+ *
+ * Returns an array with pathnames, or 0 if out-of-memory or error.
+ */
+static char **
+glob_for_file_ext_glob (const char *dir, const char *sec,
+ const char *name, const char *ext, char *hpx,
+ int glob, int type) {
+ char *pathname;
+ const char *p;
+ char **names;
+ int len;
+#define MANFORM "%s/%s%s%s/%s.%s"
+#define GLOB "*"
+#define LENGTHOF(s) (sizeof(s)-1)
+/* This must be long enough to hold the format-directory name.
+ * The basic type-directory names are 'cat' and 'man'; this needs to
+ * allocate space for those or any others such as html or sman.
+ */
+#define TYPELEN 8
+
+ len = my_lth(dir) + my_lth(sec) + my_lth(hpx) + my_lth(name) + my_lth(ext)
+ + TYPELEN
+ + LENGTHOF(".html") + LENGTHOF(MANFORM) + LENGTHOF(GLOB);
+
+ if (debug >= 2)
+ gripe(CALLTRACE3, dir, sec, name, ext, hpx, glob, type);
+
+ pathname = (char *) malloc(len);
+ if (!pathname)
+ return 0;
+
+ sprintf (pathname, MANFORM,
+ dir,
+ (type==TYPE_HTML) ? "html" : (type==TYPE_XML) ? "sman" : (type==TYPE_SCAT) ? "cat" : "man",
+ sec, hpx, name, ext);
+ if (type == TYPE_HTML)
+ strcat(pathname, ".html");
+ if (glob)
+ strcat(pathname, GLOB);
+
+ if (type == TYPE_CAT) {
+ p = to_cat_filename(pathname, 0, standards);
+ if (p) {
+ free(pathname);
+ } else {
+ sprintf (pathname, "%s/cat%s%s/%s.%s%s",
+ dir, sec, hpx, name, ext, glob ? GLOB : "");
+ p = pathname;
+ }
+ } else
+ p = pathname;
+
+ if (debug >=2)
+ gripe(ABOUT_TO_GLOB, p);
+ names = glob_filename (p);
+ if (names == (char **) -1) /* file system error; print msg? */
+ names = 0;
+ return names;
+}
+
+static char **
+glob_for_file_ext (const char *dir, const char *sec,
+ const char *name, const char *ext, int type) {
+ char **names, **namesglob;
+ char *hpx = ((standards & DO_HP) ? ".Z" : "");
+
+ namesglob = glob_for_file_ext_glob(dir,sec,name,ext,hpx,1,type);
+ if (!namesglob && *hpx) {
+ hpx = "";
+ namesglob = glob_for_file_ext_glob(dir,sec,name,ext,hpx,1,type);
+ }
+ if (!namesglob)
+ return 0;
+ if (*namesglob) {
+ /* we found something - try to get a more precise match */
+ names = glob_for_file_ext_glob(dir,sec,name,ext,hpx,0,type);
+ if (names && *names)
+ namesglob = names;
+ }
+ return namesglob;
+}
+
+/*
+ * Find the files of the form DIR/manSEC/NAME.SEC etc.
+ */
+static char **
+glob_for_file (const char *dir, const char *sec, const char *name, int type) {
+ char **names;
+
+ if (debug >= 2)
+ gripe(CALLTRACE2, dir, sec, name, type);
+
+ if (standards & DO_IRIX) {
+ /* try first without `sec' extension */
+ /* maybe this should be done only for cat pages? */
+ return glob_for_file_ext (dir, sec, name, "", type);
+ }
+
+ /* try /usr/X11R6/man/man3x/XSetFont.3x */
+ names = glob_for_file_ext (dir, sec, name, sec, type);
+
+ if (!names)
+ return 0; /* out-of-memory or error */
+
+ /* sometimes the extension is only a single digit */
+ if (!*names && isdigit(sec[0]) && sec[1] != 0) {
+ char ext[2];
+ ext[0] = sec[0];
+ ext[1] = 0;
+ names = glob_for_file_ext (dir, sec, name, ext, type);
+ }
+
+ if (!names)
+ return 0; /* out-of-memory or error */
+
+ /* or the extension could be .man */
+ if (!*names)
+ names = glob_for_file_ext (dir, sec, name, "man", type);
+
+ if (debug >= 2) {
+ if (!names[0])
+ gripe(NO_MATCH);
+ else {
+ char **np;
+ for (np = names; *np; np++)
+ gripe(GLOB_FOR_FILE, *np);
+ }
+ }
+
+ return names;
+}
+
+/*
+ * Find a man page of the given NAME under the directory DIR,
+ * in section SEC. Only types (man, cat, scat, html) permitted in FLAGS
+ * are allowed, and priorities are in this order.
+ */
+static struct manpage *
+manfile_from_sec_and_dir(const char *dir,
+ const char *sec, const char *name, int flags) {
+ struct manpage *res = 0;
+ struct manpage *p;
+ char **names, **np;
+ int i, type;
+ int types[] = {TYPE_HTML, TYPE_MAN, TYPE_CAT, TYPE_SCAT};
+
+ if (debug >= 2)
+ gripe(CALLTRACE1, dir, sec, name, flags);
+
+ for (i=0; i<(sizeof(types)/sizeof(types[0])); i++) {
+ type = types[i];
+
+ /* If convert_to_cat() is trivial, TYPE_CAT and TYPE_SCAT
+ are the same thing. */
+ if ((type == TYPE_CAT) && (flags & TYPE_SCAT) && !standards)
+ continue;
+
+ if (flags & type) {
+ names = glob_for_file (dir, sec, name, type);
+ if (names) {
+ for (np = names; *np; np++) {
+#if 1
+ /* Keep looking if we encounter a file
+ we can't access */
+ if (access(*np, R_OK))
+ continue;
+
+ if (debug >= 2)
+ gripe(FOUND_FILE, *np);
+ /* disadvantage: no error message when permissions
+ are wrong, the page just silently becomes
+ invisible */
+#endif
+ p = (struct manpage *) malloc(sizeof(*p));
+ if (!p)
+ break; /* %% perhaps print msg, free names */
+ p->filename = *np;
+ p->type = type;
+ p->next = 0;
+ append(&res, p);
+ if (res && (flags & ONLY_ONE_PERSEC))
+ break;
+ }
+ free(names);
+ }
+ }
+
+ if (res)
+ return res;
+ }
+
+ return res;
+}
+
+/*
+ * Find a man page of the given NAME, searching in the specified SECTION.
+ * Searching is done in all directories of MANPATH.
+ */
+static struct manpage *
+manfile_from_section(const char *name, const char *section,
+ int flags, char **manpath) {
+ char **mp;
+ struct manpage *res = 0;
+
+ for (mp = manpath; *mp; mp++) {
+ append(&res, manfile_from_sec_and_dir(*mp, section, name, flags));
+ if (res && (flags & ONLY_ONE_PERSEC))
+ break;
+ }
+#if 0
+ /* Someone wants section 1p - better not to give 1 */
+ if (res == NULL && isdigit(section[0]) && section[1]) {
+ char sec[2];
+
+ sec[0] = section[0];
+ sec[1] = 0;
+ for (mp = manpath; *mp; mp++) {
+ append(&res, manfile_from_sec_and_dir(*mp, sec, name, flags));
+ if (res && (flags & ONLY_ONE_PERSEC))
+ break;
+ }
+ }
+#endif
+ return res;
+}
+
+/*
+ * Find a man page of the given NAME, searching in the specified
+ * SECTION, or, if that is 0, in all sections in SECTIONLIST.
+ * Searching is done in all directories of MANPATH.
+ * If FLAGS contains the ONLY_ONE bits, only the first matching
+ * page is returned; otherwise all matching pages are found.
+ * Only types (man, cat, scat) permitted in FLAGS are allowed.
+ */
+struct manpage *
+manfile(const char *name, const char *section, int flags,
+ char **sectionlist, char **manpath,
+ const char *((*tocat)(const char *man_filename, const char *ext,
+ int flags))) {
+ char **sl;
+ struct manpage *res;
+
+ standards = (flags & (FHS | FSSTND | DO_HP | DO_IRIX));
+ to_cat_filename = tocat;
+
+ if (name && (flags & DO_WIN32)) { /* Convert : sequences to a ? */
+ char *n = my_malloc(strlen(name) + 1);
+ const char *p = name;
+ char *q = n;
+
+ while (*p) {
+ if (*p == ':') {
+ *q++ = '?';
+ while (*p == ':')
+ p++;
+ } else
+ *q++ = *p++;
+ }
+ *q = 0;
+ name = n;
+ }
+
+ if (!name || !manpath) /* error msg? */
+ res = 0;
+ else if (section)
+ res = manfile_from_section(name, section, flags, manpath);
+ else if (sectionlist) {
+ res = 0;
+ for (sl = sectionlist; *sl; sl++) {
+ append(&res, manfile_from_section(name, *sl, flags, manpath));
+ if (res && (flags & ONLY_ONE))
+ break;
+ }
+ }
+ return res;
+}