1 1.51 christos /* $NetBSD: makewhatis.c,v 1.51 2017/10/02 22:14:32 christos Exp $ */ 2 1.1 tron 3 1.1 tron /*- 4 1.1 tron * Copyright (c) 1999 The NetBSD Foundation, Inc. 5 1.1 tron * All rights reserved. 6 1.1 tron * 7 1.1 tron * This code is derived from software contributed to The NetBSD Foundation 8 1.1 tron * by Matthias Scheler. 9 1.1 tron * 10 1.1 tron * Redistribution and use in source and binary forms, with or without 11 1.1 tron * modification, are permitted provided that the following conditions 12 1.1 tron * are met: 13 1.1 tron * 1. Redistributions of source code must retain the above copyright 14 1.1 tron * notice, this list of conditions and the following disclaimer. 15 1.1 tron * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 tron * notice, this list of conditions and the following disclaimer in the 17 1.1 tron * documentation and/or other materials provided with the distribution. 18 1.1 tron * 19 1.1 tron * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 1.1 tron * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 1.1 tron * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 1.18 christos * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 1.1 tron * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 1.1 tron * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 1.1 tron * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 1.1 tron * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 1.1 tron * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 1.1 tron * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 1.1 tron * POSSIBILITY OF SUCH DAMAGE. 30 1.1 tron */ 31 1.1 tron 32 1.30 lukem #if HAVE_NBTOOL_CONFIG_H 33 1.30 lukem #include "nbtool_config.h" 34 1.30 lukem #endif 35 1.30 lukem 36 1.1 tron #include <sys/cdefs.h> 37 1.30 lukem #if !defined(lint) 38 1.44 lukem __COPYRIGHT("@(#) Copyright (c) 1999\ 39 1.44 lukem The NetBSD Foundation, Inc. All rights reserved."); 40 1.51 christos __RCSID("$NetBSD: makewhatis.c,v 1.51 2017/10/02 22:14:32 christos Exp $"); 41 1.1 tron #endif /* not lint */ 42 1.21 tv 43 1.1 tron #include <sys/types.h> 44 1.8 tron #include <sys/param.h> 45 1.23 jdolecek #include <sys/queue.h> 46 1.1 tron #include <sys/stat.h> 47 1.8 tron #include <sys/wait.h> 48 1.1 tron 49 1.1 tron #include <ctype.h> 50 1.21 tv #include <err.h> 51 1.1 tron #include <errno.h> 52 1.10 tron #include <fcntl.h> 53 1.21 tv #include <fts.h> 54 1.23 jdolecek #include <glob.h> 55 1.1 tron #include <locale.h> 56 1.8 tron #include <paths.h> 57 1.11 tron #include <signal.h> 58 1.1 tron #include <stdio.h> 59 1.1 tron #include <stdlib.h> 60 1.1 tron #include <string.h> 61 1.50 joerg #include <time.h> 62 1.1 tron #include <unistd.h> 63 1.1 tron #include <zlib.h> 64 1.40 christos #include <util.h> 65 1.1 tron 66 1.26 thorpej #include <man/manconf.h> 67 1.23 jdolecek #include <man/pathnames.h> 68 1.23 jdolecek 69 1.28 wiz #ifndef NROFF 70 1.28 wiz #define NROFF "nroff" 71 1.28 wiz #endif 72 1.28 wiz 73 1.1 tron typedef struct manpagestruct manpage; 74 1.1 tron struct manpagestruct { 75 1.46 dholland manpage *mp_left, *mp_right; 76 1.1 tron ino_t mp_inode; 77 1.22 jdolecek size_t mp_sdoff; 78 1.22 jdolecek size_t mp_sdlen; 79 1.18 christos char mp_name[1]; 80 1.1 tron }; 81 1.1 tron 82 1.1 tron typedef struct whatisstruct whatis; 83 1.1 tron struct whatisstruct { 84 1.46 dholland whatis *wi_left, *wi_right; 85 1.1 tron char *wi_data; 86 1.22 jdolecek char wi_prefix[1]; 87 1.1 tron }; 88 1.1 tron 89 1.37 tron int main(int, char * const *); 90 1.37 tron static char *findwhitespace(char *); 91 1.46 dholland static char *strmove(char *, char *); 92 1.37 tron static char *GetS(gzFile, char *, size_t); 93 1.37 tron static int pathnamesection(const char *, const char *); 94 1.37 tron static int manpagesection(char *); 95 1.37 tron static char *createsectionstring(char *); 96 1.37 tron static void addmanpage(manpage **, ino_t, char *, size_t, size_t); 97 1.37 tron static void addwhatis(whatis **, char *, char *); 98 1.37 tron static char *makesection(int); 99 1.37 tron static char *makewhatisline(const char *, const char *, const char *); 100 1.37 tron static void catpreprocess(char *); 101 1.51 christos static char *parsecatpage(const char *, gzFile); 102 1.37 tron static int manpreprocess(char *); 103 1.51 christos static char *nroff(const char *, gzFile); 104 1.51 christos static char *parsemanpage(const char *, gzFile, int); 105 1.37 tron static char *getwhatisdata(char *); 106 1.46 dholland static void processmanpages(manpage **, whatis **); 107 1.37 tron static void dumpwhatis(FILE *, whatis *); 108 1.37 tron static int makewhatis(char * const *manpath); 109 1.1 tron 110 1.37 tron static char * const default_manpath[] = { 111 1.1 tron "/usr/share/man", 112 1.1 tron NULL 113 1.1 tron }; 114 1.1 tron 115 1.37 tron static const char *sectionext = "0123456789ln"; 116 1.37 tron static const char *whatisdb = _PATH_WHATIS; 117 1.42 dholland static const char *whatisdb_new = _PATH_WHATIS ".new"; 118 1.37 tron static int dowarn = 0; 119 1.37 tron 120 1.37 tron #define ISALPHA(c) isalpha((unsigned char)(c)) 121 1.37 tron #define ISDIGIT(c) isdigit((unsigned char)(c)) 122 1.37 tron #define ISSPACE(c) isspace((unsigned char)(c)) 123 1.1 tron 124 1.1 tron int 125 1.22 jdolecek main(int argc, char *const *argv) 126 1.1 tron { 127 1.37 tron char * const *manpath; 128 1.37 tron int c, dofork; 129 1.37 tron const char *conffile; 130 1.37 tron ENTRY *ep; 131 1.37 tron TAG *tp; 132 1.37 tron int rv, jobs, status; 133 1.37 tron glob_t pg; 134 1.37 tron char *paths[2], **p, *sl; 135 1.37 tron int retval; 136 1.37 tron 137 1.37 tron dofork = 1; 138 1.37 tron conffile = NULL; 139 1.37 tron jobs = 0; 140 1.37 tron retval = EXIT_SUCCESS; 141 1.23 jdolecek 142 1.23 jdolecek (void)setlocale(LC_ALL, ""); 143 1.23 jdolecek 144 1.37 tron while ((c = getopt(argc, argv, "C:fw")) != -1) { 145 1.37 tron switch (c) { 146 1.23 jdolecek case 'C': 147 1.23 jdolecek conffile = optarg; 148 1.23 jdolecek break; 149 1.23 jdolecek case 'f': 150 1.23 jdolecek /* run all processing on foreground */ 151 1.23 jdolecek dofork = 0; 152 1.23 jdolecek break; 153 1.34 christos case 'w': 154 1.34 christos dowarn++; 155 1.34 christos break; 156 1.23 jdolecek default: 157 1.35 wiz fprintf(stderr, "Usage: %s [-fw] [-C file] [manpath ...]\n", 158 1.23 jdolecek getprogname()); 159 1.23 jdolecek exit(EXIT_FAILURE); 160 1.23 jdolecek } 161 1.23 jdolecek } 162 1.23 jdolecek argc -= optind; 163 1.23 jdolecek argv += optind; 164 1.46 dholland 165 1.24 jdolecek if (argc >= 1) { 166 1.23 jdolecek manpath = &argv[0]; 167 1.46 dholland 168 1.23 jdolecek mkwhatis: 169 1.37 tron return makewhatis(manpath); 170 1.23 jdolecek } 171 1.23 jdolecek 172 1.23 jdolecek /* 173 1.23 jdolecek * Try read config file, fallback to default_manpath[] 174 1.23 jdolecek * if man.conf not available. 175 1.23 jdolecek */ 176 1.23 jdolecek config(conffile); 177 1.39 chuck if ((tp = gettag("_whatdb", 0)) == NULL) { 178 1.23 jdolecek manpath = default_manpath; 179 1.23 jdolecek goto mkwhatis; 180 1.23 jdolecek } 181 1.23 jdolecek 182 1.23 jdolecek /* Build individual databases */ 183 1.23 jdolecek paths[1] = NULL; 184 1.39 chuck TAILQ_FOREACH(ep, &tp->entrylist, q) { 185 1.23 jdolecek if ((rv = glob(ep->s, 186 1.23 jdolecek GLOB_BRACE | GLOB_NOSORT | GLOB_ERR | GLOB_NOCHECK, 187 1.23 jdolecek NULL, &pg)) != 0) 188 1.23 jdolecek err(EXIT_FAILURE, "glob('%s')", ep->s); 189 1.23 jdolecek 190 1.23 jdolecek /* We always have something to work with here */ 191 1.23 jdolecek for (p = pg.gl_pathv; *p; p++) { 192 1.23 jdolecek sl = strrchr(*p, '/'); 193 1.37 tron if (sl == NULL) { 194 1.37 tron err(EXIT_FAILURE, "glob: _whatdb entry '%s' " 195 1.37 tron "doesn't contain slash", ep->s); 196 1.37 tron } 197 1.23 jdolecek 198 1.23 jdolecek /* 199 1.23 jdolecek * Cut the last component of path, leaving just 200 1.23 jdolecek * the directory. We will use the result as root 201 1.23 jdolecek * for manpage search. 202 1.23 jdolecek * glob malloc()s space for the paths, so it's 203 1.23 jdolecek * okay to change it in-place. 204 1.23 jdolecek */ 205 1.23 jdolecek *sl = '\0'; 206 1.23 jdolecek paths[0] = *p; 207 1.23 jdolecek 208 1.23 jdolecek if (!dofork) { 209 1.23 jdolecek /* Do not fork child */ 210 1.23 jdolecek makewhatis(paths); 211 1.23 jdolecek continue; 212 1.23 jdolecek } 213 1.23 jdolecek 214 1.23 jdolecek switch (fork()) { 215 1.23 jdolecek case 0: 216 1.23 jdolecek exit(makewhatis(paths)); 217 1.23 jdolecek break; 218 1.23 jdolecek case -1: 219 1.23 jdolecek warn("fork"); 220 1.23 jdolecek makewhatis(paths); 221 1.23 jdolecek break; 222 1.23 jdolecek default: 223 1.23 jdolecek jobs++; 224 1.23 jdolecek break; 225 1.23 jdolecek } 226 1.46 dholland 227 1.23 jdolecek } 228 1.23 jdolecek 229 1.23 jdolecek globfree(&pg); 230 1.23 jdolecek } 231 1.23 jdolecek 232 1.23 jdolecek /* Wait for the childern to finish */ 233 1.37 tron while (jobs > 0) { 234 1.37 tron (void)wait(&status); 235 1.23 jdolecek if (!WIFEXITED(status) || WEXITSTATUS(status) != EXIT_SUCCESS) 236 1.23 jdolecek retval = EXIT_FAILURE; 237 1.23 jdolecek jobs--; 238 1.23 jdolecek } 239 1.46 dholland 240 1.37 tron return retval; 241 1.23 jdolecek } 242 1.23 jdolecek 243 1.23 jdolecek static int 244 1.23 jdolecek makewhatis(char * const * manpath) 245 1.23 jdolecek { 246 1.1 tron FTS *fts; 247 1.1 tron FTSENT *fe; 248 1.18 christos manpage *source; 249 1.1 tron whatis *dest; 250 1.1 tron FILE *out; 251 1.22 jdolecek size_t sdoff, sdlen; 252 1.45 dholland int outfd; 253 1.45 dholland struct stat st_before, st_after; 254 1.1 tron 255 1.18 christos if ((fts = fts_open(manpath, FTS_LOGICAL, NULL)) == NULL) 256 1.18 christos err(EXIT_FAILURE, "Cannot open `%s'", *manpath); 257 1.1 tron 258 1.1 tron source = NULL; 259 1.1 tron while ((fe = fts_read(fts)) != NULL) { 260 1.1 tron switch (fe->fts_info) { 261 1.1 tron case FTS_F: 262 1.22 jdolecek if (manpagesection(fe->fts_path) >= 0) { 263 1.22 jdolecek /* 264 1.22 jdolecek * Get manpage subdirectory prefix. Most 265 1.22 jdolecek * commonly, this is arch-specific subdirectory. 266 1.22 jdolecek */ 267 1.22 jdolecek if (fe->fts_level >= 3) { 268 1.37 tron int sl; 269 1.37 tron const char *s, *lsl; 270 1.22 jdolecek 271 1.37 tron lsl = NULL; 272 1.37 tron s = &fe->fts_path[fe->fts_pathlen - 1]; 273 1.37 tron for(sl = fe->fts_level - 1; sl > 0; 274 1.37 tron sl--) { 275 1.22 jdolecek s--; 276 1.37 tron while (s[0] != '/') 277 1.37 tron s--; 278 1.37 tron if (lsl == NULL) 279 1.22 jdolecek lsl = s; 280 1.22 jdolecek } 281 1.46 dholland 282 1.46 dholland /* 283 1.46 dholland * Include trailing '/', so we get 284 1.46 dholland * 'arch/'. 285 1.46 dholland */ 286 1.22 jdolecek sdoff = s + 1 - fe->fts_path; 287 1.22 jdolecek sdlen = lsl - s + 1; 288 1.22 jdolecek } else { 289 1.22 jdolecek sdoff = 0; 290 1.22 jdolecek sdlen = 0; 291 1.22 jdolecek } 292 1.22 jdolecek 293 1.18 christos addmanpage(&source, fe->fts_statp->st_ino, 294 1.22 jdolecek fe->fts_path, sdoff, sdlen); 295 1.22 jdolecek } 296 1.18 christos /*FALLTHROUGH*/ 297 1.1 tron case FTS_D: 298 1.4 tron case FTS_DC: 299 1.4 tron case FTS_DEFAULT: 300 1.1 tron case FTS_DP: 301 1.38 christos case FTS_SL: 302 1.38 christos case FTS_DOT: 303 1.38 christos case FTS_W: 304 1.38 christos case FTS_NSOK: 305 1.38 christos case FTS_INIT: 306 1.38 christos break; 307 1.4 tron case FTS_SLNONE: 308 1.38 christos warnx("Symbolic link with no target: `%s'", 309 1.38 christos fe->fts_path); 310 1.38 christos break; 311 1.38 christos case FTS_DNR: 312 1.38 christos warnx("Unreadable directory: `%s'", fe->fts_path); 313 1.38 christos break; 314 1.38 christos case FTS_NS: 315 1.38 christos errno = fe->fts_errno; 316 1.38 christos warn("Cannot stat `%s'", fe->fts_path); 317 1.38 christos break; 318 1.38 christos case FTS_ERR: 319 1.38 christos errno = fe->fts_errno; 320 1.38 christos warn("Error reading `%s'", fe->fts_path); 321 1.1 tron break; 322 1.1 tron default: 323 1.38 christos errx(EXIT_FAILURE, "Unknown info %d returned from fts " 324 1.38 christos " for path: `%s'", fe->fts_info, fe->fts_path); 325 1.1 tron } 326 1.1 tron } 327 1.1 tron 328 1.1 tron (void)fts_close(fts); 329 1.1 tron 330 1.1 tron dest = NULL; 331 1.1 tron processmanpages(&source, &dest); 332 1.1 tron 333 1.18 christos if (chdir(manpath[0]) == -1) 334 1.18 christos err(EXIT_FAILURE, "Cannot change dir to `%s'", manpath[0]); 335 1.1 tron 336 1.45 dholland /* 337 1.45 dholland * makewhatis runs unattended, so it needs to be able to 338 1.45 dholland * recover if the last run crashed out. Therefore, if 339 1.45 dholland * whatisdb_new exists and is more than (arbitrarily) sixteen 340 1.45 dholland * hours old, nuke it. If it exists but is not so old, refuse 341 1.45 dholland * to run until it's cleaned up, in case another makewhatis is 342 1.45 dholland * already running. Also, open the output with O_EXCL to make 343 1.45 dholland * sure we get our own, in case two copies start exactly at 344 1.45 dholland * once. (Unlikely? Maybe, maybe not, if two copies of cron 345 1.45 dholland * end up running.) 346 1.45 dholland * 347 1.45 dholland * Similarly, before renaming the file after we finish writing 348 1.45 dholland * to it, make sure it's still the same file we opened. This 349 1.45 dholland * can't be completely race-free, but getting caught by it 350 1.45 dholland * would require an unexplained sixteen-hour-or-more lag 351 1.45 dholland * between the last mtime update when we wrote to it and when 352 1.45 dholland * we get to the stat call *and* another makewhatis starting 353 1.45 dholland * out to write at exactly the wrong moment. Not impossible, 354 1.45 dholland * but not likely enough to worry about. 355 1.45 dholland * 356 1.45 dholland * This is maybe unnecessarily elaborate, but generating 357 1.45 dholland * corrupted output isn't so good either. 358 1.45 dholland */ 359 1.45 dholland 360 1.45 dholland if (stat(whatisdb_new, &st_before) == 0) { 361 1.45 dholland if (st_before.st_mtime - time(NULL) > 16*60*60) { 362 1.45 dholland /* Don't complain if someone else just removed it. */ 363 1.45 dholland if (unlink(whatisdb_new) == -1 && errno != ENOENT) { 364 1.45 dholland err(EXIT_FAILURE, "Could not remove `%s'", 365 1.45 dholland whatisdb_new); 366 1.45 dholland } else { 367 1.45 dholland warnx("Removed stale `%s'", whatisdb_new); 368 1.45 dholland } 369 1.45 dholland } else { 370 1.45 dholland errx(EXIT_FAILURE, "The file `%s' already exists " 371 1.45 dholland "-- am I already running?", whatisdb_new); 372 1.45 dholland } 373 1.45 dholland } else if (errno != ENOENT) { 374 1.45 dholland /* Something unexpected happened. */ 375 1.45 dholland err(EXIT_FAILURE, "Cannot stat `%s'", whatisdb_new); 376 1.45 dholland } 377 1.45 dholland 378 1.45 dholland outfd = open(whatisdb_new, O_WRONLY|O_CREAT|O_EXCL, 379 1.45 dholland S_IRUSR|S_IRGRP|S_IROTH); 380 1.45 dholland if (outfd < 0) 381 1.43 dholland err(EXIT_FAILURE, "Cannot open `%s'", whatisdb_new); 382 1.1 tron 383 1.45 dholland if (fstat(outfd, &st_before) == -1) 384 1.45 dholland err(EXIT_FAILURE, "Cannot fstat `%s'", whatisdb_new); 385 1.45 dholland 386 1.45 dholland if ((out = fdopen(outfd, "w")) == NULL) 387 1.45 dholland err(EXIT_FAILURE, "Cannot fdopen `%s'", whatisdb_new); 388 1.45 dholland 389 1.18 christos dumpwhatis(out, dest); 390 1.18 christos if (fchmod(fileno(out), S_IRUSR|S_IRGRP|S_IROTH) == -1) 391 1.42 dholland err(EXIT_FAILURE, "Cannot chmod `%s'", whatisdb_new); 392 1.18 christos if (fclose(out) != 0) 393 1.42 dholland err(EXIT_FAILURE, "Cannot close `%s'", whatisdb_new); 394 1.42 dholland 395 1.45 dholland if (stat(whatisdb_new, &st_after) == -1) 396 1.45 dholland err(EXIT_FAILURE, "Cannot stat `%s' (after writing)", 397 1.45 dholland whatisdb_new); 398 1.45 dholland 399 1.45 dholland if (st_before.st_dev != st_after.st_dev || 400 1.45 dholland st_before.st_ino != st_after.st_ino) { 401 1.45 dholland errx(EXIT_FAILURE, "The file `%s' changed under me; giving up", 402 1.45 dholland whatisdb_new); 403 1.45 dholland } 404 1.45 dholland 405 1.42 dholland if (rename(whatisdb_new, whatisdb) == -1) 406 1.42 dholland err(EXIT_FAILURE, "Could not rename `%s' to `%s'", 407 1.42 dholland whatisdb_new, whatisdb); 408 1.1 tron 409 1.1 tron return EXIT_SUCCESS; 410 1.1 tron } 411 1.1 tron 412 1.37 tron static char * 413 1.18 christos findwhitespace(char *str) 414 1.6 tron { 415 1.37 tron while (!ISSPACE(*str)) 416 1.6 tron if (*str++ == '\0') { 417 1.6 tron str = NULL; 418 1.6 tron break; 419 1.6 tron } 420 1.6 tron 421 1.6 tron return str; 422 1.6 tron } 423 1.6 tron 424 1.46 dholland static char * 425 1.46 dholland strmove(char *dest, char *src) 426 1.14 tron { 427 1.14 tron return memmove(dest, src, strlen(src) + 1); 428 1.14 tron } 429 1.14 tron 430 1.37 tron static char * 431 1.18 christos GetS(gzFile in, char *buffer, size_t length) 432 1.5 tron { 433 1.5 tron char *ptr; 434 1.5 tron 435 1.18 christos if (((ptr = gzgets(in, buffer, (int)length)) != NULL) && (*ptr == '\0')) 436 1.5 tron ptr = NULL; 437 1.5 tron 438 1.5 tron return ptr; 439 1.5 tron } 440 1.5 tron 441 1.37 tron static char * 442 1.34 christos makesection(int s) 443 1.34 christos { 444 1.34 christos char sectionbuffer[24]; 445 1.34 christos if (s == -1) 446 1.34 christos return NULL; 447 1.34 christos (void)snprintf(sectionbuffer, sizeof(sectionbuffer), 448 1.34 christos " (%c) - ", sectionext[s]); 449 1.34 christos return estrdup(sectionbuffer); 450 1.34 christos } 451 1.34 christos 452 1.37 tron static int 453 1.34 christos pathnamesection(const char *pat, const char *name) 454 1.34 christos { 455 1.34 christos char *ptr, *ext; 456 1.34 christos size_t len = strlen(pat); 457 1.34 christos 458 1.34 christos 459 1.34 christos while ((ptr = strstr(name, pat)) != NULL) { 460 1.34 christos if ((ext = strchr(sectionext, ptr[len])) != NULL) { 461 1.34 christos return ext - sectionext; 462 1.34 christos } 463 1.34 christos name = ptr + 1; 464 1.34 christos } 465 1.34 christos return -1; 466 1.34 christos } 467 1.34 christos 468 1.34 christos 469 1.37 tron static int 470 1.1 tron manpagesection(char *name) 471 1.1 tron { 472 1.1 tron char *ptr; 473 1.1 tron 474 1.1 tron if ((ptr = strrchr(name, '/')) != NULL) 475 1.1 tron ptr++; 476 1.1 tron else 477 1.1 tron ptr = name; 478 1.1 tron 479 1.4 tron while ((ptr = strchr(ptr, '.')) != NULL) { 480 1.4 tron int section; 481 1.4 tron 482 1.4 tron ptr++; 483 1.34 christos section = 0; 484 1.4 tron while (sectionext[section] != '\0') 485 1.4 tron if (sectionext[section] == *ptr) 486 1.4 tron return section; 487 1.4 tron else 488 1.4 tron section++; 489 1.4 tron } 490 1.1 tron return -1; 491 1.1 tron } 492 1.1 tron 493 1.37 tron static char * 494 1.18 christos createsectionstring(char *section_id) 495 1.14 tron { 496 1.29 itojun char *section; 497 1.29 itojun 498 1.29 itojun if (asprintf(§ion, " (%s) - ", section_id) < 0) 499 1.29 itojun err(EXIT_FAILURE, "malloc failed"); 500 1.14 tron return section; 501 1.14 tron } 502 1.14 tron 503 1.37 tron static void 504 1.46 dholland addmanpage(manpage **tree, ino_t inode, char *name, size_t sdoff, size_t sdlen) 505 1.1 tron { 506 1.18 christos manpage *mp; 507 1.1 tron 508 1.1 tron while ((mp = *tree) != NULL) { 509 1.1 tron if (mp->mp_inode == inode) 510 1.18 christos return; 511 1.18 christos tree = inode < mp->mp_inode ? &mp->mp_left : &mp->mp_right; 512 1.1 tron } 513 1.1 tron 514 1.18 christos mp = emalloc(sizeof(manpage) + strlen(name)); 515 1.1 tron mp->mp_left = NULL; 516 1.1 tron mp->mp_right = NULL; 517 1.1 tron mp->mp_inode = inode; 518 1.22 jdolecek mp->mp_sdoff = sdoff; 519 1.22 jdolecek mp->mp_sdlen = sdlen; 520 1.18 christos (void)strcpy(mp->mp_name, name); 521 1.1 tron *tree = mp; 522 1.1 tron } 523 1.1 tron 524 1.37 tron static void 525 1.22 jdolecek addwhatis(whatis **tree, char *data, char *prefix) 526 1.1 tron { 527 1.1 tron whatis *wi; 528 1.1 tron int result; 529 1.1 tron 530 1.37 tron while (ISSPACE(*data)) 531 1.7 tron data++; 532 1.7 tron 533 1.7 tron if (*data == '/') { 534 1.7 tron char *ptr; 535 1.7 tron 536 1.7 tron ptr = ++data; 537 1.37 tron while ((*ptr != '\0') && !ISSPACE(*ptr)) 538 1.7 tron if (*ptr++ == '/') 539 1.7 tron data = ptr; 540 1.7 tron } 541 1.7 tron 542 1.1 tron while ((wi = *tree) != NULL) { 543 1.18 christos result = strcmp(data, wi->wi_data); 544 1.47 apb if (result == 0) result = strcmp(prefix, wi->wi_prefix); 545 1.18 christos if (result == 0) return; 546 1.18 christos tree = result < 0 ? &wi->wi_left : &wi->wi_right; 547 1.1 tron } 548 1.1 tron 549 1.22 jdolecek wi = emalloc(sizeof(whatis) + strlen(prefix)); 550 1.1 tron 551 1.1 tron wi->wi_left = NULL; 552 1.1 tron wi->wi_right = NULL; 553 1.1 tron wi->wi_data = data; 554 1.22 jdolecek if (prefix[0] != '\0') 555 1.22 jdolecek (void) strcpy(wi->wi_prefix, prefix); 556 1.22 jdolecek else 557 1.22 jdolecek wi->wi_prefix[0] = '\0'; 558 1.1 tron *tree = wi; 559 1.1 tron } 560 1.1 tron 561 1.37 tron static void 562 1.1 tron catpreprocess(char *from) 563 1.1 tron { 564 1.1 tron char *to; 565 1.1 tron 566 1.1 tron to = from; 567 1.37 tron while (ISSPACE(*from)) from++; 568 1.1 tron 569 1.1 tron while (*from != '\0') 570 1.37 tron if (ISSPACE(*from)) { 571 1.37 tron while (ISSPACE(*++from)); 572 1.1 tron if (*from != '\0') 573 1.1 tron *to++ = ' '; 574 1.1 tron } 575 1.32 christos else if (*(from + 1) == '\b') 576 1.1 tron from += 2; 577 1.1 tron else 578 1.1 tron *to++ = *from++; 579 1.1 tron 580 1.1 tron *to = '\0'; 581 1.1 tron } 582 1.1 tron 583 1.37 tron static char * 584 1.34 christos makewhatisline(const char *file, const char *line, const char *section) 585 1.1 tron { 586 1.34 christos static const char *del[] = { 587 1.34 christos " - ", 588 1.34 christos " -- ", 589 1.34 christos "- ", 590 1.34 christos " -", 591 1.34 christos NULL 592 1.34 christos }; 593 1.34 christos size_t i, pos; 594 1.34 christos size_t llen, slen, dlen; 595 1.34 christos char *result, *ptr; 596 1.1 tron 597 1.36 lukem ptr = NULL; 598 1.34 christos if (section == NULL) { 599 1.34 christos if (dowarn) 600 1.34 christos warnx("%s: No section provided for `%s'", file, line); 601 1.34 christos return estrdup(line); 602 1.34 christos } 603 1.1 tron 604 1.34 christos for (i = 0; del[i]; i++) 605 1.34 christos if ((ptr = strstr(line, del[i])) != NULL) 606 1.34 christos break; 607 1.1 tron 608 1.34 christos if (del[i] == NULL) { 609 1.34 christos if (dowarn) 610 1.34 christos warnx("%s: Bad format line `%s'", file, line); 611 1.34 christos return estrdup(line); 612 1.34 christos } 613 1.34 christos 614 1.34 christos slen = strlen(section); 615 1.34 christos llen = strlen(line); 616 1.34 christos dlen = strlen(del[i]); 617 1.34 christos 618 1.34 christos result = emalloc(llen - dlen + slen + 1); 619 1.34 christos pos = ptr - line; 620 1.34 christos 621 1.34 christos (void)memcpy(result, line, pos); 622 1.34 christos (void)memcpy(&result[pos], section, slen); 623 1.34 christos (void)strcpy(&result[pos + slen], &line[pos + dlen]); 624 1.1 tron return result; 625 1.1 tron } 626 1.1 tron 627 1.37 tron static char * 628 1.51 christos parsecatpage(const char *name, gzFile in) 629 1.1 tron { 630 1.18 christos char buffer[8192]; 631 1.1 tron char *section, *ptr, *last; 632 1.18 christos size_t size; 633 1.1 tron 634 1.1 tron do { 635 1.5 tron if (GetS(in, buffer, sizeof(buffer)) == NULL) 636 1.1 tron return NULL; 637 1.1 tron } 638 1.1 tron while (buffer[0] == '\n'); 639 1.1 tron 640 1.1 tron section = NULL; 641 1.1 tron if ((ptr = strchr(buffer, '(')) != NULL) { 642 1.1 tron if ((last = strchr(ptr + 1, ')')) !=NULL) { 643 1.18 christos size_t length; 644 1.1 tron 645 1.1 tron length = last - ptr + 1; 646 1.18 christos section = emalloc(length + 5); 647 1.1 tron *section = ' '; 648 1.1 tron (void) memcpy(section + 1, ptr, length); 649 1.1 tron (void) strcpy(section + 1 + length, " - "); 650 1.1 tron } 651 1.1 tron } 652 1.1 tron 653 1.1 tron for (;;) { 654 1.5 tron if (GetS(in, buffer, sizeof(buffer)) == NULL) { 655 1.1 tron free(section); 656 1.1 tron return NULL; 657 1.1 tron } 658 1.16 tron catpreprocess(buffer); 659 1.16 tron if (strncmp(buffer, "NAME", 4) == 0) 660 1.1 tron break; 661 1.1 tron } 662 1.34 christos if (section == NULL) 663 1.34 christos section = makesection(pathnamesection("/cat", name)); 664 1.1 tron 665 1.1 tron ptr = last = buffer; 666 1.1 tron size = sizeof(buffer) - 1; 667 1.5 tron while ((size > 0) && (GetS(in, ptr, size) != NULL)) { 668 1.1 tron int length; 669 1.1 tron 670 1.1 tron catpreprocess(ptr); 671 1.1 tron 672 1.1 tron length = strlen(ptr); 673 1.1 tron if (length == 0) { 674 1.1 tron *last = '\0'; 675 1.1 tron 676 1.34 christos ptr = makewhatisline(name, buffer, section); 677 1.1 tron free(section); 678 1.1 tron return ptr; 679 1.1 tron } 680 1.1 tron if ((length > 1) && (ptr[length - 1] == '-') && 681 1.37 tron ISALPHA(ptr[length - 2])) 682 1.1 tron last = &ptr[--length]; 683 1.1 tron else { 684 1.1 tron last = &ptr[length++]; 685 1.1 tron *last = ' '; 686 1.1 tron } 687 1.1 tron 688 1.1 tron ptr += length; 689 1.1 tron size -= length; 690 1.1 tron } 691 1.1 tron 692 1.1 tron free(section); 693 1.1 tron 694 1.1 tron return NULL; 695 1.1 tron } 696 1.1 tron 697 1.37 tron static int 698 1.1 tron manpreprocess(char *line) 699 1.1 tron { 700 1.1 tron char *from, *to; 701 1.1 tron 702 1.1 tron to = from = line; 703 1.37 tron while (ISSPACE(*from)) 704 1.37 tron from++; 705 1.1 tron if (strncmp(from, ".\\\"", 3) == 0) 706 1.1 tron return 1; 707 1.1 tron 708 1.1 tron while (*from != '\0') 709 1.37 tron if (ISSPACE(*from)) { 710 1.37 tron while (ISSPACE(*++from)); 711 1.1 tron if ((*from != '\0') && (*from != ',')) 712 1.1 tron *to++ = ' '; 713 1.37 tron } else if (*from == '\\') { 714 1.1 tron switch (*++from) { 715 1.1 tron case '\0': 716 1.1 tron case '-': 717 1.1 tron break; 718 1.14 tron case 'f': 719 1.7 tron case 's': 720 1.14 tron from++; 721 1.7 tron if ((*from=='+') || (*from=='-')) 722 1.7 tron from++; 723 1.37 tron while (ISDIGIT(*from)) 724 1.7 tron from++; 725 1.7 tron break; 726 1.1 tron default: 727 1.1 tron from++; 728 1.1 tron } 729 1.37 tron } else { 730 1.1 tron if (*from == '"') 731 1.1 tron from++; 732 1.1 tron else 733 1.1 tron *to++ = *from++; 734 1.37 tron } 735 1.1 tron 736 1.1 tron *to = '\0'; 737 1.1 tron 738 1.1 tron if (strncasecmp(line, ".Xr", 3) == 0) { 739 1.1 tron char *sect; 740 1.1 tron 741 1.1 tron from = line + 3; 742 1.37 tron if (ISSPACE(*from)) 743 1.1 tron from++; 744 1.1 tron 745 1.6 tron if ((sect = findwhitespace(from)) != NULL) { 746 1.19 tron size_t length; 747 1.19 tron char *trail; 748 1.1 tron 749 1.1 tron *sect++ = '\0'; 750 1.19 tron if ((trail = findwhitespace(sect)) != NULL) 751 1.19 tron *trail++ = '\0'; 752 1.1 tron length = strlen(from); 753 1.1 tron (void) memmove(line, from, length); 754 1.1 tron line[length++] = '('; 755 1.1 tron to = &line[length]; 756 1.1 tron length = strlen(sect); 757 1.1 tron (void) memmove(to, sect, length); 758 1.19 tron if (trail == NULL) { 759 1.19 tron (void) strcpy(&to[length], ")"); 760 1.19 tron } else { 761 1.19 tron to += length; 762 1.19 tron *to++ = ')'; 763 1.19 tron length = strlen(trail); 764 1.19 tron (void) memmove(to, trail, length + 1); 765 1.19 tron } 766 1.1 tron } 767 1.1 tron } 768 1.1 tron 769 1.1 tron return 0; 770 1.1 tron } 771 1.1 tron 772 1.37 tron static char * 773 1.51 christos nroff(const char *inname, gzFile in) 774 1.8 tron { 775 1.9 tron char tempname[MAXPATHLEN], buffer[65536], *data; 776 1.9 tron int tempfd, bytes, pipefd[2], status; 777 1.10 tron static int devnull = -1; 778 1.8 tron pid_t child; 779 1.8 tron 780 1.18 christos if (gzrewind(in) < 0) 781 1.18 christos err(EXIT_FAILURE, "Cannot rewind pipe"); 782 1.8 tron 783 1.10 tron if ((devnull < 0) && 784 1.18 christos ((devnull = open(_PATH_DEVNULL, O_WRONLY, 0)) < 0)) 785 1.18 christos err(EXIT_FAILURE, "Cannot open `/dev/null'"); 786 1.10 tron 787 1.29 itojun (void)strlcpy(tempname, _PATH_TMP "makewhatis.XXXXXX", 788 1.29 itojun sizeof(tempname)); 789 1.18 christos if ((tempfd = mkstemp(tempname)) == -1) 790 1.18 christos err(EXIT_FAILURE, "Cannot create temp file"); 791 1.8 tron 792 1.8 tron while ((bytes = gzread(in, buffer, sizeof(buffer))) > 0) 793 1.18 christos if (write(tempfd, buffer, (size_t)bytes) != bytes) { 794 1.8 tron bytes = -1; 795 1.8 tron break; 796 1.8 tron } 797 1.8 tron 798 1.18 christos if (bytes < 0) { 799 1.18 christos (void)close(tempfd); 800 1.18 christos (void)unlink(tempname); 801 1.18 christos err(EXIT_FAILURE, "Read from pipe failed"); 802 1.18 christos } 803 1.18 christos if (lseek(tempfd, (off_t)0, SEEK_SET) == (off_t)-1) { 804 1.18 christos (void)close(tempfd); 805 1.18 christos (void)unlink(tempname); 806 1.18 christos err(EXIT_FAILURE, "Cannot rewind temp file"); 807 1.18 christos } 808 1.18 christos if (pipe(pipefd) == -1) { 809 1.8 tron (void)close(tempfd); 810 1.8 tron (void)unlink(tempname); 811 1.18 christos err(EXIT_FAILURE, "Cannot create pipe"); 812 1.8 tron } 813 1.8 tron 814 1.8 tron switch (child = vfork()) { 815 1.8 tron case -1: 816 1.8 tron (void)close(pipefd[1]); 817 1.8 tron (void)close(pipefd[0]); 818 1.8 tron (void)close(tempfd); 819 1.8 tron (void)unlink(tempname); 820 1.18 christos err(EXIT_FAILURE, "Fork failed"); 821 1.8 tron /* NOTREACHED */ 822 1.8 tron case 0: 823 1.8 tron (void)close(pipefd[0]); 824 1.10 tron if (tempfd != STDIN_FILENO) { 825 1.10 tron (void)dup2(tempfd, STDIN_FILENO); 826 1.10 tron (void)close(tempfd); 827 1.10 tron } 828 1.8 tron if (pipefd[1] != STDOUT_FILENO) { 829 1.8 tron (void)dup2(pipefd[1], STDOUT_FILENO); 830 1.8 tron (void)close(pipefd[1]); 831 1.8 tron } 832 1.10 tron if (devnull != STDERR_FILENO) { 833 1.10 tron (void)dup2(devnull, STDERR_FILENO); 834 1.10 tron (void)close(devnull); 835 1.10 tron } 836 1.28 wiz (void)execlp(NROFF, NROFF, "-S", "-man", NULL); 837 1.8 tron _exit(EXIT_FAILURE); 838 1.18 christos /*NOTREACHED*/ 839 1.8 tron default: 840 1.8 tron (void)close(pipefd[1]); 841 1.8 tron (void)close(tempfd); 842 1.18 christos break; 843 1.8 tron } 844 1.8 tron 845 1.8 tron if ((in = gzdopen(pipefd[0], "r")) == NULL) { 846 1.8 tron if (errno == 0) 847 1.8 tron errno = ENOMEM; 848 1.11 tron (void)close(pipefd[0]); 849 1.11 tron (void)kill(child, SIGTERM); 850 1.11 tron while (waitpid(child, NULL, 0) != child); 851 1.8 tron (void)unlink(tempname); 852 1.18 christos err(EXIT_FAILURE, "Cannot read from pipe"); 853 1.8 tron } 854 1.8 tron 855 1.34 christos data = parsecatpage(inname, in); 856 1.9 tron while (gzread(in, buffer, sizeof(buffer)) > 0); 857 1.9 tron (void)gzclose(in); 858 1.9 tron 859 1.9 tron while (waitpid(child, &status, 0) != child); 860 1.9 tron if ((data != NULL) && 861 1.9 tron !(WIFEXITED(status) && (WEXITSTATUS(status) == 0))) { 862 1.9 tron free(data); 863 1.28 wiz errx(EXIT_FAILURE, NROFF " on `%s' exited with %d status", 864 1.27 wiz inname, WEXITSTATUS(status)); 865 1.9 tron } 866 1.8 tron 867 1.8 tron (void)unlink(tempname); 868 1.8 tron return data; 869 1.8 tron } 870 1.8 tron 871 1.37 tron static char * 872 1.51 christos parsemanpage(const char *name, gzFile in, int defaultsection) 873 1.1 tron { 874 1.1 tron char *section, buffer[8192], *ptr; 875 1.48 christos static const char POD[] = ".\\\" Automatically generated by Pod"; 876 1.48 christos static const char IX[] = ".IX TITLE"; 877 1.1 tron 878 1.1 tron section = NULL; 879 1.1 tron do { 880 1.5 tron if (GetS(in, buffer, sizeof(buffer) - 1) == NULL) { 881 1.1 tron free(section); 882 1.1 tron return NULL; 883 1.1 tron } 884 1.48 christos 885 1.48 christos /* 886 1.48 christos * Skip over lines in man pages that have been generated 887 1.49 christos * by Pod, until we find the TITLE. 888 1.48 christos */ 889 1.48 christos if (strncasecmp(buffer, POD, sizeof(POD) - 1) == 0) { 890 1.48 christos do { 891 1.48 christos if (GetS(in, buffer, sizeof(buffer) - 1) 892 1.48 christos == NULL) { 893 1.48 christos free(section); 894 1.48 christos return NULL; 895 1.48 christos } 896 1.48 christos } while (strncasecmp(buffer, IX, sizeof(IX) - 1) != 0); 897 1.48 christos } 898 1.48 christos 899 1.1 tron if (manpreprocess(buffer)) 900 1.1 tron continue; 901 1.1 tron if (strncasecmp(buffer, ".Dt", 3) == 0) { 902 1.1 tron char *end; 903 1.1 tron 904 1.1 tron ptr = &buffer[3]; 905 1.37 tron if (ISSPACE(*ptr)) 906 1.1 tron ptr++; 907 1.6 tron if ((ptr = findwhitespace(ptr)) == NULL) 908 1.1 tron continue; 909 1.1 tron 910 1.6 tron if ((end = findwhitespace(++ptr)) != NULL) 911 1.1 tron *end = '\0'; 912 1.1 tron 913 1.1 tron free(section); 914 1.14 tron section = createsectionstring(ptr); 915 1.14 tron } 916 1.14 tron else if (strncasecmp(buffer, ".TH", 3) == 0) { 917 1.14 tron ptr = &buffer[3]; 918 1.37 tron while (ISSPACE(*ptr)) 919 1.14 tron ptr++; 920 1.14 tron if ((ptr = findwhitespace(ptr)) != NULL) { 921 1.14 tron char *next; 922 1.14 tron 923 1.37 tron while (ISSPACE(*ptr)) 924 1.14 tron ptr++; 925 1.14 tron if ((next = findwhitespace(ptr)) != NULL) 926 1.14 tron *next = '\0'; 927 1.14 tron free(section); 928 1.14 tron section = createsectionstring(ptr); 929 1.1 tron } 930 1.1 tron } 931 1.14 tron else if (strncasecmp(buffer, ".Ds", 3) == 0) { 932 1.14 tron free(section); 933 1.14 tron return NULL; 934 1.14 tron } 935 1.14 tron } while (strncasecmp(buffer, ".Sh NAME", 8) != 0); 936 1.1 tron 937 1.1 tron do { 938 1.5 tron if (GetS(in, buffer, sizeof(buffer) - 1) == NULL) { 939 1.1 tron free(section); 940 1.1 tron return NULL; 941 1.1 tron } 942 1.1 tron } while (manpreprocess(buffer)); 943 1.1 tron 944 1.1 tron if (strncasecmp(buffer, ".Nm", 3) == 0) { 945 1.18 christos size_t length, offset; 946 1.1 tron 947 1.1 tron ptr = &buffer[3]; 948 1.37 tron while (ISSPACE(*ptr)) 949 1.1 tron ptr++; 950 1.1 tron 951 1.1 tron length = strlen(ptr); 952 1.1 tron if ((length > 1) && (ptr[length - 1] == ',') && 953 1.37 tron ISSPACE(ptr[length - 2])) { 954 1.1 tron ptr[--length] = '\0'; 955 1.1 tron ptr[length - 1] = ','; 956 1.1 tron } 957 1.1 tron (void) memmove(buffer, ptr, length + 1); 958 1.1 tron 959 1.1 tron offset = length + 3; 960 1.1 tron ptr = &buffer[offset]; 961 1.1 tron for (;;) { 962 1.18 christos size_t more; 963 1.1 tron 964 1.1 tron if ((sizeof(buffer) == offset) || 965 1.18 christos (GetS(in, ptr, sizeof(buffer) - offset) 966 1.1 tron == NULL)) { 967 1.1 tron free(section); 968 1.1 tron return NULL; 969 1.1 tron } 970 1.1 tron if (manpreprocess(ptr)) 971 1.1 tron continue; 972 1.1 tron 973 1.1 tron if (strncasecmp(ptr, ".Nm", 3) != 0) break; 974 1.1 tron 975 1.1 tron ptr += 3; 976 1.37 tron if (ISSPACE(*ptr)) 977 1.1 tron ptr++; 978 1.1 tron 979 1.1 tron buffer[length++] = ' '; 980 1.1 tron more = strlen(ptr); 981 1.1 tron if ((more > 1) && (ptr[more - 1] == ',') && 982 1.37 tron ISSPACE(ptr[more - 2])) { 983 1.1 tron ptr[--more] = '\0'; 984 1.1 tron ptr[more - 1] = ','; 985 1.1 tron } 986 1.1 tron 987 1.1 tron (void) memmove(&buffer[length], ptr, more + 1); 988 1.1 tron length += more; 989 1.1 tron offset = length + 3; 990 1.1 tron 991 1.1 tron ptr = &buffer[offset]; 992 1.1 tron } 993 1.1 tron 994 1.1 tron if (strncasecmp(ptr, ".Nd", 3) == 0) { 995 1.29 itojun (void) strlcpy(&buffer[length], " -", 996 1.29 itojun sizeof(buffer) - length); 997 1.1 tron 998 1.1 tron while (strncasecmp(ptr, ".Sh", 3) != 0) { 999 1.1 tron int more; 1000 1.1 tron 1001 1.1 tron if (*ptr == '.') { 1002 1.1 tron char *space; 1003 1.1 tron 1004 1.37 tron if (strncasecmp(ptr, ".Nd", 3) != 0 || 1005 1.37 tron strchr(ptr, '[') != NULL) { 1006 1.15 tron free(section); 1007 1.15 tron return NULL; 1008 1.15 tron } 1009 1.14 tron space = findwhitespace(ptr); 1010 1.37 tron if (space == NULL) { 1011 1.1 tron ptr = ""; 1012 1.37 tron } else { 1013 1.1 tron space++; 1014 1.14 tron (void) strmove(ptr, space); 1015 1.1 tron } 1016 1.1 tron } 1017 1.1 tron 1018 1.1 tron if (*ptr != '\0') { 1019 1.1 tron buffer[offset - 1] = ' '; 1020 1.1 tron more = strlen(ptr) + 1; 1021 1.1 tron offset += more; 1022 1.1 tron } 1023 1.1 tron ptr = &buffer[offset]; 1024 1.1 tron if ((sizeof(buffer) == offset) || 1025 1.18 christos (GetS(in, ptr, sizeof(buffer) - offset) 1026 1.1 tron == NULL)) { 1027 1.1 tron free(section); 1028 1.1 tron return NULL; 1029 1.1 tron } 1030 1.1 tron if (manpreprocess(ptr)) 1031 1.1 tron *ptr = '\0'; 1032 1.1 tron } 1033 1.1 tron } 1034 1.1 tron } 1035 1.1 tron else { 1036 1.1 tron int offset; 1037 1.1 tron 1038 1.1 tron if (*buffer == '.') { 1039 1.1 tron char *space; 1040 1.1 tron 1041 1.14 tron if ((space = findwhitespace(&buffer[1])) == NULL) { 1042 1.1 tron free(section); 1043 1.1 tron return NULL; 1044 1.1 tron } 1045 1.1 tron space++; 1046 1.14 tron (void) strmove(buffer, space); 1047 1.1 tron } 1048 1.1 tron 1049 1.1 tron offset = strlen(buffer) + 1; 1050 1.1 tron for (;;) { 1051 1.1 tron int more; 1052 1.1 tron 1053 1.1 tron ptr = &buffer[offset]; 1054 1.1 tron if ((sizeof(buffer) == offset) || 1055 1.18 christos (GetS(in, ptr, sizeof(buffer) - offset) 1056 1.1 tron == NULL)) { 1057 1.1 tron free(section); 1058 1.1 tron return NULL; 1059 1.1 tron } 1060 1.1 tron if (manpreprocess(ptr) || (*ptr == '\0')) 1061 1.1 tron continue; 1062 1.1 tron 1063 1.5 tron if ((strncasecmp(ptr, ".Sh", 3) == 0) || 1064 1.5 tron (strncasecmp(ptr, ".Ss", 3) == 0)) 1065 1.1 tron break; 1066 1.1 tron 1067 1.1 tron if (*ptr == '.') { 1068 1.1 tron char *space; 1069 1.1 tron 1070 1.6 tron if ((space = findwhitespace(ptr)) == NULL) { 1071 1.1 tron continue; 1072 1.5 tron } 1073 1.5 tron 1074 1.1 tron space++; 1075 1.5 tron (void) memmove(ptr, space, strlen(space) + 1); 1076 1.1 tron } 1077 1.1 tron 1078 1.1 tron buffer[offset - 1] = ' '; 1079 1.1 tron more = strlen(ptr); 1080 1.1 tron if ((more > 1) && (ptr[more - 1] == ',') && 1081 1.37 tron ISSPACE(ptr[more - 2])) { 1082 1.1 tron ptr[more - 1] = '\0'; 1083 1.1 tron ptr[more - 2] = ','; 1084 1.1 tron } 1085 1.1 tron else more++; 1086 1.1 tron offset += more; 1087 1.1 tron } 1088 1.1 tron } 1089 1.1 tron 1090 1.34 christos if (section == NULL) 1091 1.34 christos section = makesection(defaultsection); 1092 1.1 tron 1093 1.34 christos ptr = makewhatisline(name, buffer, section); 1094 1.34 christos free(section); 1095 1.1 tron return ptr; 1096 1.1 tron } 1097 1.1 tron 1098 1.37 tron static char * 1099 1.1 tron getwhatisdata(char *name) 1100 1.1 tron { 1101 1.51 christos gzFile in; 1102 1.1 tron char *data; 1103 1.1 tron int section; 1104 1.1 tron 1105 1.1 tron if ((in = gzopen(name, "r")) == NULL) { 1106 1.18 christos if (errno == 0) 1107 1.18 christos errno = ENOMEM; 1108 1.18 christos err(EXIT_FAILURE, "Cannot open `%s'", name); 1109 1.1 tron /* NOTREACHED */ 1110 1.1 tron } 1111 1.1 tron 1112 1.1 tron section = manpagesection(name); 1113 1.34 christos if (section == 0) { 1114 1.34 christos data = parsecatpage(name, in); 1115 1.34 christos } else { 1116 1.34 christos data = parsemanpage(name, in, section); 1117 1.14 tron if (data == NULL) 1118 1.34 christos data = nroff(name, in); 1119 1.14 tron } 1120 1.1 tron 1121 1.1 tron (void) gzclose(in); 1122 1.1 tron return data; 1123 1.1 tron } 1124 1.1 tron 1125 1.37 tron static void 1126 1.1 tron processmanpages(manpage **source, whatis **dest) 1127 1.1 tron { 1128 1.18 christos manpage *mp; 1129 1.22 jdolecek char sd[128]; 1130 1.1 tron 1131 1.1 tron mp = *source; 1132 1.1 tron *source = NULL; 1133 1.1 tron 1134 1.1 tron while (mp != NULL) { 1135 1.1 tron manpage *obsolete; 1136 1.1 tron char *data; 1137 1.1 tron 1138 1.1 tron if (mp->mp_left != NULL) 1139 1.46 dholland processmanpages(&mp->mp_left, dest); 1140 1.1 tron 1141 1.22 jdolecek if ((data = getwhatisdata(mp->mp_name)) != NULL) { 1142 1.22 jdolecek /* Pass eventual directory prefix to addwhatis() */ 1143 1.22 jdolecek if (mp->mp_sdlen > 0 && mp->mp_sdlen < sizeof(sd)-1) 1144 1.22 jdolecek strlcpy(sd, &mp->mp_name[mp->mp_sdoff], 1145 1.22 jdolecek mp->mp_sdlen); 1146 1.22 jdolecek else 1147 1.22 jdolecek sd[0] = '\0'; 1148 1.22 jdolecek 1149 1.22 jdolecek addwhatis(dest, data, sd); 1150 1.22 jdolecek } 1151 1.1 tron 1152 1.1 tron obsolete = mp; 1153 1.1 tron mp = mp->mp_right; 1154 1.1 tron free(obsolete); 1155 1.1 tron } 1156 1.1 tron } 1157 1.1 tron 1158 1.37 tron static void 1159 1.18 christos dumpwhatis(FILE *out, whatis *tree) 1160 1.1 tron { 1161 1.1 tron while (tree != NULL) { 1162 1.1 tron if (tree->wi_left) 1163 1.18 christos dumpwhatis(out, tree->wi_left); 1164 1.1 tron 1165 1.22 jdolecek if ((tree->wi_data[0] && fputs(tree->wi_prefix, out) == EOF) || 1166 1.22 jdolecek (fputs(tree->wi_data, out) == EOF) || 1167 1.1 tron (fputc('\n', out) == EOF)) 1168 1.18 christos err(EXIT_FAILURE, "Write failed"); 1169 1.1 tron 1170 1.1 tron tree = tree->wi_right; 1171 1.1 tron } 1172 1.18 christos } 1173