/* * manfile.c - aeb, 971231 * * Used both by man and man2html - be careful with printing! */ #include #include #include #include #include #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; }