manfile.c   [plain text]


/*
 * 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 "glob.h"
#include "util.h"
#include "manfile.h"

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.
 *
 * 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;

     len = my_lth(dir) + my_lth(sec) + my_lth(hpx) + my_lth(name)
	  + my_lth(ext) + 8;
     pathname = (char *) malloc(len);
     if (!pathname)
	  return 0;

     sprintf (pathname, "%s/%s%s%s/%s.%s%s",
	      dir,
	      (type == TYPE_XML) ? "sman" :
	      (type == TYPE_SCAT) ? "cat" : "man", sec, hpx,
	      name, ext, 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 ? "*" : "");
	       p = pathname;
	  }
     } else
	  p = pathname;

     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 (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);

     return names;
}

/*
 * Find a man page of the given NAME under the directory DIR,
 * in section SEC. Only types (man, cat, scat) 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[3] = { TYPE_MAN, TYPE_CAT, TYPE_SCAT };
	  /* cannot handle TYPE_XML yet */

     for (i=0; i<3; 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 0			 /* 1 requires <unistd.h> */
			 /* Keep looking if we encounter a file
			    we can't access */
			 if (access(*np, R_OK))
			      continue;

			 /* 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;
}