Annotation of mandoc/manup.c, Revision 1.1
1.1 ! kristaps 1: /* $Id: mdoc.c,v 1.196 2011/09/30 00:13:28 schwarze Exp $ */
! 2: /*
! 3: * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
! 4: *
! 5: * Permission to use, copy, modify, and distribute this software for any
! 6: * purpose with or without fee is hereby granted, provided that the above
! 7: * copyright notice and this permission notice appear in all copies.
! 8: *
! 9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
! 10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
! 11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
! 12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
! 13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
! 14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
! 15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
! 16: */
! 17: #ifdef HAVE_CONFIG_H
! 18: #include "config.h"
! 19: #endif
! 20:
! 21: #include <sys/param.h>
! 22: #include <sys/stat.h>
! 23: #include <sys/wait.h>
! 24:
! 25: #include <assert.h>
! 26: #include <errno.h>
! 27: #include <fcntl.h>
! 28: #include <getopt.h>
! 29: #include <stdio.h>
! 30: #include <stdlib.h>
! 31: #include <string.h>
! 32: #include <unistd.h>
! 33:
! 34: #ifdef __linux__
! 35: # include <db_185.h>
! 36: #else
! 37: # include <db.h>
! 38: #endif
! 39:
! 40: #include "manpath.h"
! 41:
! 42: #define xstrlcpy(_dst, _src, _sz) \
! 43: do if (strlcpy((_dst), (_src), (_sz)) >= (_sz)) { \
! 44: fprintf(stderr, "%s: Path too long", (_dst)); \
! 45: exit(EXIT_FAILURE); \
! 46: } while (/* CONSTCOND */0)
! 47:
! 48: #define xstrlcat(_dst, _src, _sz) \
! 49: do if (strlcat((_dst), (_src), (_sz)) >= (_sz)) { \
! 50: fprintf(stderr, "%s: Path too long", (_dst)); \
! 51: exit(EXIT_FAILURE); \
! 52: } while (/* CONSTCOND */0)
! 53:
! 54: static int indexhtml(char *);
! 55: static int jobstart(const char *, const char *, pid_t *);
! 56: static int jobwait(pid_t);
! 57: static int manup(const struct manpaths *, const char *);
! 58: static int mkpath(char *, mode_t, mode_t);
! 59: static int treecpy(char *, char *);
! 60: static int update(char *, char *);
! 61: static void usage(void);
! 62:
! 63: static const char *progname;
! 64: static int verbose;
! 65: static int force;
! 66:
! 67: int
! 68: main(int argc, char *argv[])
! 69: {
! 70: int ch;
! 71: char *aux, *base;
! 72: const char *dir;
! 73: struct manpaths dirs;
! 74: extern char *optarg;
! 75: extern int optind;
! 76:
! 77: progname = strrchr(argv[0], '/');
! 78: if (progname == NULL)
! 79: progname = argv[0];
! 80: else
! 81: ++progname;
! 82:
! 83: aux = base = NULL;
! 84: dir = "/var/www/cache/man.cgi";
! 85:
! 86: while (-1 != (ch = getopt(argc, argv, "fm:M:o:v")))
! 87: switch (ch) {
! 88: case ('f'):
! 89: force = 1;
! 90: break;
! 91: case ('m'):
! 92: aux = optarg;
! 93: break;
! 94: case ('M'):
! 95: base = optarg;
! 96: break;
! 97: case ('o'):
! 98: dir = optarg;
! 99: break;
! 100: case ('v'):
! 101: verbose++;
! 102: break;
! 103: default:
! 104: usage();
! 105: return(EXIT_FAILURE);
! 106: }
! 107:
! 108: argc -= optind;
! 109: argv += optind;
! 110:
! 111: if (argc > 0) {
! 112: usage();
! 113: return(EXIT_FAILURE);
! 114: }
! 115:
! 116: memset(&dirs, 0, sizeof(struct manpaths));
! 117: manpath_parse(&dirs, base, aux);
! 118: ch = manup(&dirs, dir);
! 119: manpath_free(&dirs);
! 120: return(ch ? EXIT_SUCCESS : EXIT_FAILURE);
! 121: }
! 122:
! 123: static void
! 124: usage(void)
! 125: {
! 126:
! 127: fprintf(stderr, "usage: %s "
! 128: "[-fv] "
! 129: "[-o path] "
! 130: "[-m manpath] "
! 131: "[-M manpath]\n",
! 132: progname);
! 133: }
! 134:
! 135: /*
! 136: * If "src" file doesn't exist (errors out), return -1. Otherwise,
! 137: * return 1 if "src" is newer (which also happens "dst" doesn't exist)
! 138: * and 0 otherwise.
! 139: */
! 140: static int
! 141: isnewer(const char *dst, const char *src)
! 142: {
! 143: struct stat s1, s2;
! 144:
! 145: if (-1 == stat(src, &s1))
! 146: return(-1);
! 147: if (force)
! 148: return(1);
! 149:
! 150: return(-1 == stat(dst, &s2) ? 1 : s1.st_mtime > s2.st_mtime);
! 151: }
! 152:
! 153: /*
! 154: * Copy the contents of one file into another.
! 155: * Returns 0 on failure, 1 on success.
! 156: */
! 157: static int
! 158: filecpy(const char *dst, const char *src)
! 159: {
! 160: char buf[BUFSIZ];
! 161: int sfd, dfd, rc;
! 162: ssize_t rsz, wsz;
! 163:
! 164: sfd = dfd = -1;
! 165: rc = 0;
! 166:
! 167: if (-1 == (dfd = open(dst, O_CREAT|O_TRUNC|O_WRONLY, 0644))) {
! 168: perror(dst);
! 169: goto out;
! 170: } else if (-1 == (sfd = open(src, O_RDONLY, 0))) {
! 171: perror(src);
! 172: goto out;
! 173: }
! 174:
! 175: while ((rsz = read(sfd, buf, BUFSIZ)) > 0)
! 176: if (-1 == (wsz = write(dfd, buf, (size_t)rsz))) {
! 177: perror(dst);
! 178: goto out;
! 179: } else if (wsz < rsz) {
! 180: fprintf(stderr, "%s: Short write\n", dst);
! 181: goto out;
! 182: }
! 183:
! 184: if (rsz < 0)
! 185: perror(src);
! 186: else
! 187: rc = 1;
! 188: out:
! 189: if (-1 != sfd)
! 190: close(sfd);
! 191: if (-1 != dfd)
! 192: close(dfd);
! 193:
! 194: return(rc);
! 195: }
! 196:
! 197: /*
! 198: * Clean up existing child.
! 199: * Return 1 if cleaned up fine (or none was started) and 0 otherwise.
! 200: */
! 201: static int
! 202: jobwait(pid_t pid)
! 203: {
! 204: int st;
! 205:
! 206: if (-1 == pid)
! 207: return(1);
! 208:
! 209: if (-1 == waitpid(pid, &st, 0)) {
! 210: perror(NULL);
! 211: exit(EXIT_FAILURE);
! 212: }
! 213:
! 214: return(WIFEXITED(st) && 0 == WEXITSTATUS(st));
! 215: }
! 216:
! 217: /*
! 218: * Start a job (child process), first making sure that the prior one has
! 219: * finished.
! 220: * Return 1 if the prior child exited and the new one started, else 0.
! 221: */
! 222: static int
! 223: jobstart(const char *dst, const char *src, pid_t *pid)
! 224: {
! 225: int fd;
! 226:
! 227: if ( ! jobwait(*pid))
! 228: return(0);
! 229:
! 230: if (-1 == (*pid = fork())) {
! 231: perror(NULL);
! 232: exit(EXIT_FAILURE);
! 233: } else if (*pid > 0)
! 234: return(1);
! 235:
! 236: if (-1 == (fd = open(dst, O_WRONLY|O_TRUNC|O_CREAT, 0644))) {
! 237: perror(dst);
! 238: exit(EXIT_FAILURE);
! 239: }
! 240:
! 241: if (-1 == dup2(fd, STDOUT_FILENO)) {
! 242: perror(NULL);
! 243: exit(EXIT_FAILURE);
! 244: }
! 245:
! 246: execlp("mandoc", "mandoc", "-T", "html",
! 247: "-O", "fragment",
! 248: "-O", "man=man.cgi?expr=%N&sec=%S",
! 249: src, (char *)NULL);
! 250:
! 251: perror("mandoc");
! 252: exit(EXIT_FAILURE);
! 253: /* NOTREACHED */
! 254: }
! 255:
! 256: /*
! 257: * Pass over the recno database and re-create HTML pages if they're
! 258: * found to be out of date.
! 259: * Returns -1 on fatal error, 1 on success.
! 260: */
! 261: static int
! 262: indexhtml(char *dst)
! 263: {
! 264: DB *db;
! 265: DBT key, val;
! 266: size_t sz;
! 267: int c, rc;
! 268: unsigned int fl;
! 269: const char *f;
! 270: char *d;
! 271: char fname[MAXPATHLEN];
! 272: pid_t pid;
! 273:
! 274: sz = strlen(dst);
! 275: pid = -1;
! 276:
! 277: xstrlcpy(fname, dst, MAXPATHLEN);
! 278: xstrlcat(fname, "/mandoc.index", MAXPATHLEN);
! 279:
! 280: db = dbopen(fname, O_RDONLY, 0, DB_RECNO, NULL);
! 281: if (NULL == db) {
! 282: perror(fname);
! 283: return(-1);
! 284: }
! 285:
! 286: fl = R_FIRST;
! 287: while (0 == (c = (*db->seq)(db, &key, &val, fl))) {
! 288: fl = R_NEXT;
! 289: f = (const char *)val.data;
! 290:
! 291: dst[(int)sz] = '\0';
! 292:
! 293: xstrlcat(dst, "/", MAXPATHLEN);
! 294: xstrlcat(dst, f, MAXPATHLEN);
! 295: xstrlcat(dst, ".html", MAXPATHLEN);
! 296:
! 297: if (-1 == (rc = isnewer(dst, f))) {
! 298: fprintf(stderr, "%s: Manpage missing\n", f);
! 299: break;
! 300: } else if (0 == rc)
! 301: continue;
! 302:
! 303: d = strrchr(dst, '/');
! 304: assert(NULL != d);
! 305: *d = '\0';
! 306:
! 307: if (-1 == mkpath(dst, 0755, 0755)) {
! 308: perror(dst);
! 309: break;
! 310: }
! 311:
! 312: *d = '/';
! 313: if ( ! jobstart(dst, f, &pid))
! 314: break;
! 315: if (verbose)
! 316: printf("%s\n", dst);
! 317: }
! 318:
! 319: (*db->close)(db);
! 320:
! 321: if (c < 0)
! 322: perror(fname);
! 323: if ( ! jobwait(pid))
! 324: c = -1;
! 325:
! 326: return(1 == c ? 1 : -1);
! 327: }
! 328:
! 329: /*
! 330: * Copy both recno and btree databases into the destination.
! 331: * Call in to begin recreating HTML files.
! 332: * Return -1 on fatal error and 1 if the update went well.
! 333: */
! 334: static int
! 335: update(char *dst, char *src)
! 336: {
! 337: size_t dsz, ssz;
! 338:
! 339: dsz = strlen(dst);
! 340: ssz = strlen(src);
! 341:
! 342: xstrlcat(src, "/mandoc.db", MAXPATHLEN);
! 343: xstrlcat(dst, "/mandoc.db", MAXPATHLEN);
! 344:
! 345: if ( ! filecpy(dst, src))
! 346: return(-1);
! 347: if (verbose)
! 348: printf("%s\n", dst);
! 349:
! 350: dst[(int)dsz] = src[(int)ssz] = '\0';
! 351:
! 352: xstrlcat(src, "/mandoc.index", MAXPATHLEN);
! 353: xstrlcat(dst, "/mandoc.index", MAXPATHLEN);
! 354:
! 355: if ( ! filecpy(dst, src))
! 356: return(-1);
! 357: if (verbose)
! 358: printf("%s\n", dst);
! 359:
! 360: dst[(int)dsz] = '\0';
! 361:
! 362: return(indexhtml(dst));
! 363: }
! 364:
! 365: /*
! 366: * See if btree or recno databases in the destination are out of date
! 367: * with respect to a single manpath component.
! 368: * Return -1 on fatal error, 0 if the source is no longer valid (and
! 369: * shouldn't be listed), and 1 if the update went well.
! 370: */
! 371: static int
! 372: treecpy(char *dst, char *src)
! 373: {
! 374: size_t dsz, ssz;
! 375: int rc;
! 376:
! 377: dsz = strlen(dst);
! 378: ssz = strlen(src);
! 379:
! 380: xstrlcat(src, "/mandoc.index", MAXPATHLEN);
! 381: xstrlcat(dst, "/mandoc.index", MAXPATHLEN);
! 382:
! 383: if (-1 == (rc = isnewer(dst, src)))
! 384: return(0);
! 385:
! 386: dst[(int)dsz] = src[(int)ssz] = '\0';
! 387:
! 388: if (1 == rc)
! 389: return(update(dst, src));
! 390:
! 391: xstrlcat(src, "/mandoc.db", MAXPATHLEN);
! 392: xstrlcat(dst, "/mandoc.db", MAXPATHLEN);
! 393:
! 394: if ((rc = isnewer(dst, src)) <= 0)
! 395: return(0);
! 396:
! 397: dst[(int)dsz] = src[(int)ssz] = '\0';
! 398:
! 399: return(update(dst, src));
! 400: }
! 401:
! 402: /*
! 403: * Update the destination's file-tree with respect to changes in the
! 404: * source manpath components.
! 405: * "Change" is defined by an updated index or btree database.
! 406: * Returns 1 on success, 0 on failure.
! 407: */
! 408: static int
! 409: manup(const struct manpaths *dirs, const char *dir)
! 410: {
! 411: char dst[MAXPATHLEN],
! 412: src[MAXPATHLEN];
! 413: const char *path;
! 414: int i, c;
! 415: size_t sz;
! 416: FILE *f;
! 417:
! 418: xstrlcpy(dst, dir, MAXPATHLEN);
! 419: xstrlcat(dst, "/etc", MAXPATHLEN);
! 420:
! 421: if (-1 == mkpath(dst, 0755, 0755)) {
! 422: perror(dst);
! 423: return(0);
! 424: }
! 425:
! 426: xstrlcat(dst, "/man.conf", MAXPATHLEN);
! 427:
! 428: if (NULL == (f = fopen(dst, "w"))) {
! 429: perror(dst);
! 430: return(0);
! 431: }
! 432:
! 433: xstrlcpy(dst, dir, MAXPATHLEN);
! 434: sz = strlen(dst);
! 435:
! 436: for (i = 0; i < dirs->sz; i++) {
! 437: path = dirs->paths[i];
! 438:
! 439: dst[(int)sz] = '\0';
! 440: xstrlcat(dst, path, MAXPATHLEN);
! 441:
! 442: if (-1 == mkpath(dst, 0755, 0755)) {
! 443: perror(dst);
! 444: break;
! 445: }
! 446:
! 447: xstrlcpy(src, path, MAXPATHLEN);
! 448:
! 449: if (-1 == (c = treecpy(dst, src)))
! 450: break;
! 451: else if (0 == c)
! 452: continue;
! 453:
! 454: fprintf(f, "_whatdb %s/whatis.db\n", path);
! 455: }
! 456:
! 457: fclose(f);
! 458: return(i == dirs->sz);
! 459: }
! 460:
! 461: /*
! 462: * Copyright (c) 1983, 1992, 1993
! 463: * The Regents of the University of California. All rights reserved.
! 464: *
! 465: * Redistribution and use in source and binary forms, with or without
! 466: * modification, are permitted provided that the following conditions
! 467: * are met:
! 468: * 1. Redistributions of source code must retain the above copyright
! 469: * notice, this list of conditions and the following disclaimer.
! 470: * 2. Redistributions in binary form must reproduce the above copyright
! 471: * notice, this list of conditions and the following disclaimer in the
! 472: * documentation and/or other materials provided with the distribution.
! 473: * 3. Neither the name of the University nor the names of its contributors
! 474: * may be used to endorse or promote products derived from this software
! 475: * without specific prior written permission.
! 476: *
! 477: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
! 478: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 479: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 480: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
! 481: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 482: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 483: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 484: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 485: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 486: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 487: * SUCH DAMAGE.
! 488: */
! 489: static int
! 490: mkpath(char *path, mode_t mode, mode_t dir_mode)
! 491: {
! 492: struct stat sb;
! 493: char *slash;
! 494: int done, exists;
! 495:
! 496: slash = path;
! 497:
! 498: for (;;) {
! 499: /* LINTED */
! 500: slash += strspn(slash, "/");
! 501: /* LINTED */
! 502: slash += strcspn(slash, "/");
! 503:
! 504: done = (*slash == '\0');
! 505: *slash = '\0';
! 506:
! 507: /* skip existing path components */
! 508: exists = !stat(path, &sb);
! 509: if (!done && exists && S_ISDIR(sb.st_mode)) {
! 510: *slash = '/';
! 511: continue;
! 512: }
! 513:
! 514: if (mkdir(path, done ? mode : dir_mode) == 0) {
! 515: if (mode > 0777 && chmod(path, mode) < 0)
! 516: return (-1);
! 517: } else {
! 518: if (!exists) {
! 519: /* Not there */
! 520: return (-1);
! 521: }
! 522: if (!S_ISDIR(sb.st_mode)) {
! 523: /* Is there, but isn't a directory */
! 524: errno = ENOTDIR;
! 525: return (-1);
! 526: }
! 527: }
! 528:
! 529: if (done)
! 530: break;
! 531:
! 532: *slash = '/';
! 533: }
! 534:
! 535: return (0);
! 536: }
CVSweb