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