1/*
2 * manfile.c - aeb, 971231
3 *
4 * Used both by man and man2html - be careful with printing!
5 */
6#include <stdlib.h>
7#include <stdio.h>
8#include <ctype.h>
9#include <string.h>
10#include <unistd.h>
11
12#include "glob.h"
13#include "util.h"
14#include "manfile.h"
15#include "gripes.h"
16#include "man.h"		/* for debug */
17
18static int standards;
19static const char *((*to_cat_filename)(const char *man_filename,
20				       const char *ext, int flags));
21
22/*
23 * Append the struct or chain A to the chain HEAD.
24 */
25static void
26append(struct manpage **head, struct manpage *a) {
27     struct manpage *p;
28
29     if (a) {
30	  if (*head) {
31	       p = *head;
32	       while(p->next)
33		    p = p->next;
34	       p->next = a;
35	  } else
36	       *head = a;
37     }
38}
39
40
41static int
42my_lth(const char *s) {
43     return s ? strlen(s) : 0;
44}
45
46/*
47 * Find the files of the form DIR/manSEC/NAME.EXT etc.
48 * Use "man" for TYPE_MAN, "cat" for TYPE_SCAT, and
49 * apply convert_to_cat() to the man version for TYPE_CAT.
50 *
51 * Some HP systems use /usr/man/man1.Z/name.1, where name.1 is
52 * compressed - yuk.  We can handle this by using section 1.Z
53 * instead of 1 and assuming that the man page is compressed
54 * if the directory name ends in .Z.
55 *
56 * Some Sun systems use /usr/share/man/sman1/man.1 and
57 * /usr/share/man/sman1m/mkfs.1m.
58 *
59 * We support HTML filenames of the following form:
60 * /usr/share/man/sman1m/mkfs.1m.html, optionally followed
61 * by a compression suffix.
62 *
63 * Returns an array with pathnames, or 0 if out-of-memory or error.
64 */
65static char **
66glob_for_file_ext_glob (const char *dir, const char *sec,
67			const char *name, const char *ext, char *hpx,
68			int glob, int type) {
69     char *pathname;
70     const char *p;
71     char **names;
72     int len;
73#define MANFORM	"%s/%s%s%s/%s.%s"
74#define GLOB	"*"
75#define LENGTHOF(s)	(sizeof(s)-1)
76/* This must be long enough to hold the format-directory name.
77 * The basic type-directory names are 'cat' and 'man'; this needs to
78 * allocate space for those or any others such as html or sman.
79 */
80#define TYPELEN	8
81
82     len = my_lth(dir) + my_lth(sec) + my_lth(hpx) + my_lth(name) + my_lth(ext)
83	 + TYPELEN
84	 + LENGTHOF(".html") + LENGTHOF(MANFORM) + LENGTHOF(GLOB);
85
86     if (debug >= 2)
87	 gripe(CALLTRACE3, dir, sec, name, ext, hpx, glob, type);
88
89     pathname = (char *) malloc(len);
90     if (!pathname)
91	  return 0;
92
93     sprintf (pathname, MANFORM,
94	      dir,
95	      (type==TYPE_HTML) ? "html" : (type==TYPE_XML) ? "sman" : (type==TYPE_SCAT) ? "cat" : "man",
96	      sec, hpx, name, ext);
97     if (type == TYPE_HTML)
98	 strcat(pathname, ".html");
99     if (glob)
100	 strcat(pathname, GLOB);
101
102     if (type == TYPE_CAT) {
103          p = to_cat_filename(pathname, 0, standards);
104          if (p) {
105	       free(pathname);
106          } else {
107               sprintf (pathname, "%s/cat%s%s/%s.%s%s",
108                        dir, sec, hpx, name, ext, glob ? GLOB : "");
109	       p = pathname;
110	  }
111     } else
112	  p = pathname;
113
114     if (debug >=2)
115	 gripe(ABOUT_TO_GLOB, p);
116     names = glob_filename (p);
117     if (names == (char **) -1)		/* file system error; print msg? */
118	  names = 0;
119     return names;
120}
121
122static char **
123glob_for_file_ext (const char *dir, const char *sec,
124		   const char *name, const char *ext, int type) {
125     char **names, **namesglob;
126     char *hpx = ((standards & DO_HP) ? ".Z" : "");
127
128     namesglob = glob_for_file_ext_glob(dir,sec,name,ext,hpx,1,type);
129     if (!namesglob && *hpx) {
130	  hpx = "";
131	  namesglob = glob_for_file_ext_glob(dir,sec,name,ext,hpx,1,type);
132     }
133     if (!namesglob)
134	  return 0;
135     if (*namesglob) {
136	  /* we found something - try to get a more precise match */
137	  names = glob_for_file_ext_glob(dir,sec,name,ext,hpx,0,type);
138	  if (names && *names)
139	       namesglob = names;
140     }
141     return namesglob;
142}
143
144/*
145 * Find the files of the form DIR/manSEC/NAME.SEC etc.
146 */
147static char **
148glob_for_file (const char *dir, const char *sec, const char *name, int type) {
149     char **names;
150     char shortsec[2];
151
152     if (debug >= 2)
153	 gripe(CALLTRACE2, dir, sec, name, type);
154
155     shortsec[0] = sec[0];
156     shortsec[1] = '\0';
157
158     if (standards & DO_IRIX) {
159	  /* try first without `sec' extension */
160	  /* maybe this should be done only for cat pages? */
161	  return glob_for_file_ext (dir, sec, name, "", type);
162     }
163
164     /* try /usr/X11R6/man/man3x/XSetFont.3x */
165     names = glob_for_file_ext (dir, shortsec, name, sec, type);
166
167     if (!names)
168	  return 0;		/* out-of-memory or error */
169
170     /* sometimes the extension is only a single digit */
171     if (!*names && isdigit(sec[0]) && sec[1] != 0) {
172	  char ext[2];
173	  ext[0] = sec[0];
174	  ext[1] = 0;
175	  names = glob_for_file_ext (dir, sec, name, ext, type);
176     }
177
178     if (!names)
179	  return 0;		/* out-of-memory or error */
180
181     /* or the extension could be .man */
182     if (!*names)
183	  names = glob_for_file_ext (dir, sec, name, "man", type);
184
185     if (debug >= 2) {
186	 if (!names[0])
187	     gripe(NO_MATCH);
188         else {
189	     char **np;
190	     for (np = names; *np; np++)
191		 gripe(GLOB_FOR_FILE, *np);
192	 }
193     }
194
195     return names;
196}
197
198/*
199 * Find a man page of the given NAME under the directory DIR,
200 * in section SEC. Only types (man, cat, scat, html) permitted in FLAGS
201 * are allowed, and priorities are in this order.
202 */
203static struct manpage *
204manfile_from_sec_and_dir(const char *dir,
205			 const char *sec, const char *name, int flags) {
206     struct manpage *res = 0;
207     struct manpage *p;
208     char **names, **np;
209     int i, type;
210     int types[] = {TYPE_HTML, TYPE_MAN, TYPE_CAT, TYPE_SCAT};
211
212     if (debug >= 2)
213	 gripe(CALLTRACE1, dir, sec, name, flags);
214
215     for (i=0; i<(sizeof(types)/sizeof(types[0])); i++) {
216	  type = types[i];
217
218	  /* If convert_to_cat() is trivial, TYPE_CAT and TYPE_SCAT
219	     are the same thing. */
220	  if ((type == TYPE_CAT) && (flags & TYPE_SCAT) && !standards)
221	       continue;
222
223	  if (flags & type) {
224	       names = glob_for_file (dir, sec, name, type);
225	       if (names) {
226		    for (np = names; *np; np++) {
227#if 1
228			 /* Keep looking if we encounter a file
229			    we can't access */
230			 if (access(*np, R_OK))
231			      continue;
232
233			 if (debug >= 2)
234			     gripe(FOUND_FILE, *np);
235			 /* disadvantage: no error message when permissions
236			    are wrong, the page just silently becomes
237			    invisible */
238#endif
239			 p = (struct manpage *) malloc(sizeof(*p));
240			 if (!p)
241			      break; 	/* %% perhaps print msg, free names */
242			 p->filename = *np;
243			 p->type = type;
244			 p->next = 0;
245			 append(&res, p);
246			 if (res && (flags & ONLY_ONE_PERSEC))
247			      break;
248		    }
249		    free(names);
250	       }
251	  }
252
253	  if (res)
254	       return res;
255     }
256
257     return res;
258}
259
260/*
261 * Find a man page of the given NAME, searching in the specified SECTION.
262 * Searching is done in all directories of MANPATH.
263 */
264static struct manpage *
265manfile_from_section(const char *name, const char *section,
266		     int flags, char **manpath) {
267     char **mp;
268     struct manpage *res = 0;
269
270     for (mp = manpath; *mp; mp++) {
271	  append(&res, manfile_from_sec_and_dir(*mp, section, name, flags));
272	  if (res && (flags & ONLY_ONE_PERSEC))
273	       break;
274     }
275#if 0
276     /* Someone wants section 1p - better not to give 1 */
277     if (res == NULL && isdigit(section[0]) && section[1]) {
278	  char sec[2];
279
280	  sec[0] = section[0];
281	  sec[1] = 0;
282	  for (mp = manpath; *mp; mp++) {
283	       append(&res, manfile_from_sec_and_dir(*mp, sec, name, flags));
284	       if (res && (flags & ONLY_ONE_PERSEC))
285		    break;
286	  }
287     }
288#endif
289     return res;
290}
291
292/*
293 * Find a man page of the given NAME, searching in the specified
294 * SECTION, or, if that is 0, in all sections in SECTIONLIST.
295 * Searching is done in all directories of MANPATH.
296 * If FLAGS contains the ONLY_ONE bits, only the first matching
297 * page is returned; otherwise all matching pages are found.
298 * Only types (man, cat, scat) permitted in FLAGS are allowed.
299 */
300struct manpage *
301manfile(const char *name, const char *section, int flags,
302        char **sectionlist, char **manpath,
303	const char *((*tocat)(const char *man_filename, const char *ext,
304			      int flags))) {
305     char **sl;
306     struct manpage *res;
307
308     standards = (flags & (FHS | FSSTND | DO_HP | DO_IRIX));
309     to_cat_filename = tocat;
310
311     if (name && (flags & DO_WIN32)) { /* Convert : sequences to a ? */
312	  char *n = my_malloc(strlen(name) + 1);
313	  const char *p = name;
314	  char *q = n;
315
316	  while (*p) {
317		  if (*p == ':') {
318			  *q++ = '?';
319			  while (*p == ':')
320				  p++;
321		  } else
322			  *q++ = *p++;
323	  }
324	  *q = 0;
325	  name = n;
326     }
327
328     if (!name || !manpath)	/* error msg? */
329	  res = 0;
330     else if (section)
331	  res = manfile_from_section(name, section, flags, manpath);
332     else if (sectionlist) {
333	  res = 0;
334	  for (sl = sectionlist; *sl; sl++) {
335	       append(&res, manfile_from_section(name, *sl, flags, manpath));
336	       if (res && (flags & ONLY_ONE))
337		    break;
338	  }
339     }
340     return res;
341}
342