[BACK]Return to catman.c CVS log [TXT][DIR] Up to [cvsweb.bsd.lv] / mandoc

Diff for /mandoc/catman.c between version 1.11.2.1 and 1.29

version 1.11.2.1, 2013/10/05 20:30:05 version 1.29, 2025/06/30 12:23:42
Line 1 
Line 1 
 /*      $Id$ */  /* $Id$ */
 /*  /*
  * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>   * Copyright (c) 2017, 2025 Ingo Schwarze <schwarze@openbsd.org>
    * Copyright (c) 2017 Michael Stapelberg <stapelberg@debian.org>
  *   *
  * Permission to use, copy, modify, and distribute this software for any   * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above   * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.   * copyright notice and this permission notice appear in all copies.
  *   *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES   * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF   * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR   * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES   * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN   * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF   * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.   * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */   */
 #ifdef HAVE_CONFIG_H  
 #include "config.h"  #include "config.h"
   
   #if NEED_XPG4_2
   #define _XPG4_2
 #endif  #endif
   
 #include <sys/param.h>  #include <sys/types.h>
   #include <sys/socket.h>
 #include <sys/stat.h>  #include <sys/stat.h>
 #include <sys/wait.h>  
   
 #include <assert.h>  #include <assert.h>
   #if HAVE_ERR
   #include <err.h>
   #endif
 #include <errno.h>  #include <errno.h>
 #include <fcntl.h>  #include <fcntl.h>
 #include <getopt.h>  #if HAVE_FTS
   #include <fts.h>
   #else
   #include "compat_fts.h"
   #endif
   #include <signal.h>
   #include <stdint.h>
 #include <stdio.h>  #include <stdio.h>
 #include <stdlib.h>  #include <stdlib.h>
 #include <string.h>  #include <string.h>
   #include <time.h>
 #include <unistd.h>  #include <unistd.h>
   
 #ifdef __linux__  int             verbose_flag = 0;
 # include <db_185.h>  sig_atomic_t    got_signal = 0;
 #else  
 # include <db.h>  
 #endif  
   
 #include "manpath.h"  int      process_manpage(int, int, const char *);
 #include "mandocdb.h"  int      process_tree(int, int);
   void     run_mandocd(int, const char *, const char *)
                   __attribute__((__noreturn__));
   void     signal_handler(int);
   ssize_t  sock_fd_write(int, int, int, int);
   void     usage(void) __attribute__((__noreturn__));
   
 #define xstrlcpy(_dst, _src, _sz) \  
         do if (strlcpy((_dst), (_src), (_sz)) >= (_sz)) { \  
                 fprintf(stderr, "%s: Path too long", (_dst)); \  
                 exit(EXIT_FAILURE); \  
         } while (/* CONSTCOND */0)  
   
 #define xstrlcat(_dst, _src, _sz) \  void
         do if (strlcat((_dst), (_src), (_sz)) >= (_sz)) { \  signal_handler(int signum)
                 fprintf(stderr, "%s: Path too long", (_dst)); \  
                 exit(EXIT_FAILURE); \  
         } while (/* CONSTCOND */0)  
   
 static  int              indexhtml(char *, size_t, char *, size_t);  
 static  int              manup(const struct manpaths *, char *);  
 static  int              mkpath(char *, mode_t, mode_t);  
 static  int              treecpy(char *, char *);  
 static  int              update(char *, char *);  
 static  void             usage(void);  
   
 static  const char      *progname;  
 static  int              verbose;  
 static  int              force;  
   
 int  
 main(int argc, char *argv[])  
 {  {
         int              ch;          got_signal = signum;
         char            *aux, *base, *conf_file;  
         struct manpaths  dirs;  
         char             buf[MAXPATHLEN];  
         extern char     *optarg;  
         extern int       optind;  
   
         progname = strrchr(argv[0], '/');  
         if (progname == NULL)  
                 progname = argv[0];  
         else  
                 ++progname;  
   
         aux = base = conf_file = NULL;  
         xstrlcpy(buf, "/var/www/cache/man.cgi", MAXPATHLEN);  
   
         while (-1 != (ch = getopt(argc, argv, "C:fm:M:o:v")))  
                 switch (ch) {  
                 case ('C'):  
                         conf_file = optarg;  
                         break;  
                 case ('f'):  
                         force = 1;  
                         break;  
                 case ('m'):  
                         aux = optarg;  
                         break;  
                 case ('M'):  
                         base = optarg;  
                         break;  
                 case ('o'):  
                         xstrlcpy(buf, optarg, MAXPATHLEN);  
                         break;  
                 case ('v'):  
                         verbose++;  
                         break;  
                 default:  
                         usage();  
                         return(EXIT_FAILURE);  
                 }  
   
         argc -= optind;  
         argv += optind;  
   
         if (argc > 0) {  
                 usage();  
                 return(EXIT_FAILURE);  
         }  
   
         memset(&dirs, 0, sizeof(struct manpaths));  
         manpath_parse(&dirs, conf_file, base, aux);  
         ch = manup(&dirs, buf);  
         manpath_free(&dirs);  
         return(ch ? EXIT_SUCCESS : EXIT_FAILURE);  
 }  }
   
 static void  void
 usage(void)  run_mandocd(int sockfd, const char *outtype, const char* defos)
 {  {
           char     sockfdstr[10];
         fprintf(stderr, "usage: %s "          int      len;
                         "[-fv] "  
                         "[-C file] "  
                         "[-o path] "  
                         "[-m manpath] "  
                         "[-M manpath]\n",  
                         progname);  
 }  
   
 /*          len = snprintf(sockfdstr, sizeof(sockfdstr), "%d", sockfd);
  * If "src" file doesn't exist (errors out), return -1.  Otherwise,          if (len >= (int)sizeof(sockfdstr)) {
  * return 1 if "src" is newer (which also happens "dst" doesn't exist)                  errno = EOVERFLOW;
  * and 0 otherwise.                  len = -1;
  */          }
 static int          if (len < 0)
 isnewer(const char *dst, const char *src)                  err(1, "snprintf");
 {          if (defos == NULL)
         struct stat      s1, s2;                  execlp("mandocd", "mandocd", "-T", outtype,
                       sockfdstr, (char *)NULL);
         if (-1 == stat(src, &s1))  
                 return(-1);  
         if (force)  
                 return(1);  
   
         return(-1 == stat(dst, &s2) ? 1 : s1.st_mtime > s2.st_mtime);  
 }  
   
 /*  
  * Copy the contents of one file into another.  
  * Returns 0 on failure, 1 on success.  
  */  
 static int  
 filecpy(const char *dst, const char *src)  
 {  
         char             buf[BUFSIZ];  
         int              sfd, dfd, rc;  
         ssize_t          rsz, wsz;  
   
         sfd = dfd = -1;  
         rc = 0;  
   
         if (-1 == (dfd = open(dst, O_CREAT|O_TRUNC|O_WRONLY, 0644))) {  
                 perror(dst);  
                 goto out;  
         } else if (-1 == (sfd = open(src, O_RDONLY, 0))) {  
                 perror(src);  
                 goto out;  
         }  
   
         while ((rsz = read(sfd, buf, BUFSIZ)) > 0)  
                 if (-1 == (wsz = write(dfd, buf, (size_t)rsz))) {  
                         perror(dst);  
                         goto out;  
                 } else if (wsz < rsz) {  
                         fprintf(stderr, "%s: Short write\n", dst);  
                         goto out;  
                 }  
   
         if (rsz < 0)  
                 perror(src);  
         else          else
                 rc = 1;                  execlp("mandocd", "mandocd", "-T", outtype,
 out:                      "-I", defos, sockfdstr, (char *)NULL);
         if (-1 != sfd)          err(1, "exec(mandocd)");
                 close(sfd);  
         if (-1 != dfd)  
                 close(dfd);  
   
         return(rc);  
 }  }
   
 /*  ssize_t
  * Pass over the recno database and re-create HTML pages if they're  sock_fd_write(int fd, int fd0, int fd1, int fd2)
  * found to be out of date.  
  * Returns -1 on fatal error, 1 on success.  
  */  
 static int  
 indexhtml(char *src, size_t ssz, char *dst, size_t dsz)  
 {  {
         DB              *idx;          const struct timespec timeout = { 0, 10000000 };  /* 0.01 s */
         DBT              key, val;          struct msghdr    msg;
         int              c, rc;          struct iovec     iov;
         unsigned int     fl;          union {
         const char      *f;                  struct cmsghdr   cmsghdr;
         char            *d;                  char             control[CMSG_SPACE(3 * sizeof(int))];
         char             fname[MAXPATHLEN];          } cmsgu;
           struct cmsghdr  *cmsg;
           int             *walk;
           ssize_t          sz;
           unsigned char    dummy[1] = {'\0'};
   
         xstrlcpy(fname, dst, MAXPATHLEN);          iov.iov_base = dummy;
         xstrlcat(fname, "/", MAXPATHLEN);          iov.iov_len = sizeof(dummy);
         xstrlcat(fname, MANDOC_IDX, MAXPATHLEN);  
   
         idx = dbopen(fname, O_RDONLY, 0, DB_RECNO, NULL);          msg.msg_name = NULL;
         if (NULL == idx) {          msg.msg_namelen = 0;
                 perror(fname);          msg.msg_iov = &iov;
                 return(-1);          msg.msg_iovlen = 1;
         }  
   
         fl = R_FIRST;          msg.msg_control = cmsgu.control;
         while (0 == (c = (*idx->seq)(idx, &key, &val, fl))) {          msg.msg_controllen = sizeof(cmsgu.control);
                 fl = R_NEXT;  
                 /*  
                  * If the record is zero-length, then it's unassigned.  
                  * Skip past these.  
                  */  
                 if (0 == val.size)  
                         continue;  
   
                 f = (const char *)val.data + 1;          cmsg = CMSG_FIRSTHDR(&msg);
                 if (NULL == memchr(f, '\0', val.size - 1))          cmsg->cmsg_len = CMSG_LEN(3 * sizeof(int));
                         break;          cmsg->cmsg_level = SOL_SOCKET;
           cmsg->cmsg_type = SCM_RIGHTS;
   
                 src[(int)ssz] = dst[(int)dsz] = '\0';          walk = (int *)CMSG_DATA(cmsg);
           *(walk++) = fd0;
           *(walk++) = fd1;
           *(walk++) = fd2;
   
                 xstrlcat(dst, "/", MAXPATHLEN);          /*
                 xstrlcat(dst, f, MAXPATHLEN);           * It appears that on some systems, sendmsg(3)
            * may return EAGAIN even in blocking mode.
                 xstrlcat(src, "/", MAXPATHLEN);           * Seen for example on Oracle Solaris 11.2.
                 xstrlcat(src, f, MAXPATHLEN);           * The sleeping time was chosen by experimentation,
            * to neither cause more than a handful of retries
                 if (-1 == (rc = isnewer(dst, src))) {           * in normal operation nor unnecessary delays.
                         fprintf(stderr, "%s: File missing\n", f);           */
           while ((sz = sendmsg(fd, &msg, 0)) == -1) {
                   if (errno != EAGAIN) {
                           warn("FATAL: sendmsg");
                         break;                          break;
                 } else if (0 == rc)  
                         continue;  
   
                 d = strrchr(dst, '/');  
                 assert(NULL != d);  
                 *d = '\0';  
   
                 if (-1 == mkpath(dst, 0755, 0755)) {  
                         perror(dst);  
                         break;  
                 }                  }
                   nanosleep(&timeout, NULL);
                 *d = '/';  
   
                 if ( ! filecpy(dst, src))  
                         break;  
                 if (verbose)  
                         printf("%s\n", dst);  
         }          }
           return sz;
         (*idx->close)(idx);  
   
         if (c < 0)  
                 perror(fname);  
         else if (0 == c)  
                 fprintf(stderr, "%s: Corrupt index\n", fname);  
   
         return(1 == c ? 1 : -1);  
 }  }
   
 /*  int
  * Copy both recno and btree databases into the destination.  process_manpage(int srv_fd, int dstdir_fd, const char *path)
  * Call in to begin recreating HTML files.  
  * Return -1 on fatal error and 1 if the update went well.  
  */  
 static int  
 update(char *dst, char *src)  
 {  {
         size_t           dsz, ssz;          int      in_fd, out_fd;
           int      irc;
   
         dsz = strlen(dst);          if ((in_fd = open(path, O_RDONLY)) == -1) {
         ssz = strlen(src);                  warn("open %s for reading", path);
                   fflush(stderr);
                   return 0;
           }
   
         xstrlcat(src, "/", MAXPATHLEN);          if ((out_fd = openat(dstdir_fd, path,
         xstrlcat(dst, "/", MAXPATHLEN);              O_WRONLY | O_NOFOLLOW | O_CREAT | O_TRUNC,
               S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1) {
                   warn("openat %s for writing", path);
                   fflush(stderr);
                   close(in_fd);
                   return 0;
           }
   
         xstrlcat(src, MANDOC_DB, MAXPATHLEN);          irc = sock_fd_write(srv_fd, in_fd, out_fd, STDERR_FILENO);
         xstrlcat(dst, MANDOC_DB, MAXPATHLEN);  
   
         if ( ! filecpy(dst, src))          close(in_fd);
                 return(-1);          close(out_fd);
         if (verbose)  
                 printf("%s\n", dst);  
   
         dst[(int)dsz] = src[(int)ssz] = '\0';          return irc;
   
         xstrlcat(src, "/", MAXPATHLEN);  
         xstrlcat(dst, "/", MAXPATHLEN);  
   
         xstrlcat(src, MANDOC_IDX, MAXPATHLEN);  
         xstrlcat(dst, MANDOC_IDX, MAXPATHLEN);  
   
         if ( ! filecpy(dst, src))  
                 return(-1);  
         if (verbose)  
                 printf("%s\n", dst);  
   
         dst[(int)dsz] = src[(int)ssz] = '\0';  
   
         return(indexhtml(src, ssz, dst, dsz));  
 }  }
   
 /*  int
  * See if btree or recno databases in the destination are out of date  process_tree(int srv_fd, int dstdir_fd)
  * with respect to a single manpath component.  
  * Return -1 on fatal error, 0 if the source is no longer valid (and  
  * shouldn't be listed), and 1 if the update went well.  
  */  
 static int  
 treecpy(char *dst, char *src)  
 {  {
         size_t           dsz, ssz;          const struct timespec timeout = { 0, 10000000 };  /* 0.01 s */
         int              rc;          const int        max_inflight = 16;
   
         dsz = strlen(dst);          FTS             *ftsp;
         ssz = strlen(src);          FTSENT          *entry;
           const char      *argv[2];
         xstrlcat(src, "/", MAXPATHLEN);  
         xstrlcat(dst, "/", MAXPATHLEN);  
   
         xstrlcat(src, MANDOC_IDX, MAXPATHLEN);  
         xstrlcat(dst, MANDOC_IDX, MAXPATHLEN);  
   
         if (-1 == (rc = isnewer(dst, src)))  
                 return(0);  
   
         dst[(int)dsz] = src[(int)ssz] = '\0';  
   
         if (1 == rc)  
                 return(update(dst, src));  
   
         xstrlcat(src, "/", MAXPATHLEN);  
         xstrlcat(dst, "/", MAXPATHLEN);  
   
         xstrlcat(src, MANDOC_DB, MAXPATHLEN);  
         xstrlcat(dst, MANDOC_DB, MAXPATHLEN);  
   
         if (-1 == (rc = isnewer(dst, src)))  
                 return(0);  
         else if (rc == 0)  
                 return(1);  
   
         dst[(int)dsz] = src[(int)ssz] = '\0';  
   
         return(update(dst, src));  
 }  
   
 /*  
  * Update the destination's file-tree with respect to changes in the  
  * source manpath components.  
  * "Change" is defined by an updated index or btree database.  
  * Returns 1 on success, 0 on failure.  
  */  
 static int  
 manup(const struct manpaths *dirs, char *base)  
 {  
         char             dst[MAXPATHLEN],  
                          src[MAXPATHLEN];  
         const char      *path;          const char      *path;
         size_t           i;          int              inflight, irc, decr, fatal;
         int              c;          int              gooddirs, baddirs, goodfiles, badfiles;
         size_t           sz;          char             dummy[1];
         FILE            *f;  
   
         /* Create the path and file for the catman.conf file. */          argv[0] = ".";
           argv[1] = (char *)NULL;
   
         sz = strlen(base);          if ((ftsp = fts_open((char * const *)argv,
         xstrlcpy(dst, base, MAXPATHLEN);              FTS_PHYSICAL | FTS_NOCHDIR, NULL)) == NULL) {
         xstrlcat(dst, "/etc", MAXPATHLEN);                  warn("fts_open");
         if (-1 == mkpath(dst, 0755, 0755)) {                  return -1;
                 perror(dst);  
                 return(0);  
         }          }
   
         xstrlcat(dst, "/catman.conf", MAXPATHLEN);          if (verbose_flag >= 2) {
         if (NULL == (f = fopen(dst, "w"))) {                  warnx("allowing up to %d files in flight", max_inflight);
                 perror(dst);                  fflush(stderr);
                 return(0);          }
         } else if (verbose)          inflight = fatal = gooddirs = baddirs = goodfiles = badfiles = 0;
                 printf("%s\n", dst);          while (fatal == 0 && got_signal == 0 &&
               (entry = fts_read(ftsp)) != NULL) {
         for (i = 0; i < dirs->sz; i++) {                  if (inflight >= max_inflight) {
                 path = dirs->paths[i];                          while (recv(srv_fd, dummy, sizeof(dummy), 0) == -1) {
                 dst[(int)sz] = '\0';                                  if (errno != EAGAIN) {
                 xstrlcat(dst, path, MAXPATHLEN);                                          warn("FATAL: recv");
                 if (-1 == mkpath(dst, 0755, 0755)) {                                          fatal = errno;
                         perror(dst);                                          break;
                                   }
                                   nanosleep(&timeout, NULL);
                           }
                           if (fatal != 0)
                                   break;
                           decr = 1;
                           while ((irc = recv(srv_fd, dummy, sizeof(dummy),
                               MSG_DONTWAIT)) > 0)
                                   decr++;
                           assert(inflight >= decr);
                           if (verbose_flag >= 2 && decr > 1) {
                                   warnx("files in flight: %d - %d = %d",
                                       inflight, decr, inflight - decr);
                                   fflush(stderr);
                           }
                           inflight -= decr;
                           if (irc == 0) {
                                   errno = ECONNRESET;
                                   inflight = -1;
                           }
                           if (errno != EAGAIN) {
                                   warn("FATAL: recv");
                                   fatal = errno;
                                   break;
                           }
                   }
                   path = entry->fts_path + 2;
                   switch (entry->fts_info) {
                   case FTS_F:
                           switch (process_manpage(srv_fd, dstdir_fd, path)) {
                           case -1:
                                   fatal = errno;
                                   break;
                           case 0:
                                   badfiles++;
                                   break;
                           default:
                                   goodfiles++;
                                   inflight++;
                                   break;
                           }
                         break;                          break;
                   case FTS_D:
                           if (*path != '\0' &&
                               mkdirat(dstdir_fd, path, S_IRWXU | S_IRGRP |
                                 S_IXGRP | S_IROTH | S_IXOTH) == -1 &&
                               errno != EEXIST) {
                                   warn("mkdirat %s", path);
                                   fflush(stderr);
                                   (void)fts_set(ftsp, entry, FTS_SKIP);
                                   baddirs++;
                           } else
                                   gooddirs++;
                           break;
                   case FTS_DP:
                           break;
                   case FTS_DNR:
                           warnx("directory %s unreadable: %s",
                               path, strerror(entry->fts_errno));
                           fflush(stderr);
                           baddirs++;
                           break;
                   case FTS_DC:
                           warnx("directory %s causes cycle", path);
                           fflush(stderr);
                           baddirs++;
                           break;
                   case FTS_ERR:
                   case FTS_NS:
                           warnx("file %s: %s",
                               path, strerror(entry->fts_errno));
                           fflush(stderr);
                           badfiles++;
                           break;
                   default:
                           warnx("file %s: not a regular file", path);
                           fflush(stderr);
                           badfiles++;
                           break;
                 }                  }
           }
                 xstrlcpy(src, path, MAXPATHLEN);          if (got_signal != 0) {
                 if (-1 == (c = treecpy(dst, src)))                  switch (got_signal) {
                   case SIGCHLD:
                           warnx("FATAL: mandocd child died: got SIGCHLD");
                         break;                          break;
                 else if (0 == c)                  case SIGPIPE:
                         continue;                          warnx("FATAL: mandocd child died: got SIGPIPE");
                           break;
                   default:
                           warnx("FATAL: signal SIG%s", sys_signame[got_signal]);
                           break;
                   }
                   inflight = -1;
                   fatal = 1;
           } else if (fatal == 0 && (fatal = errno) != 0)
                   warn("FATAL: fts_read");
   
                 /*          fts_close(ftsp);
                  * We want to use a relative path here because manpath.h          if (verbose_flag >= 2 && inflight > 0) {
                  * will realpath() when invoked with man.cgi, and we'll                  warnx("waiting for %d files in flight", inflight);
                  * make sure to chdir() into the cache directory before.                  fflush(stderr);
                  *  
                  * This allows the cache directory to be in an arbitrary  
                  * place, working in both chroot() and non-chroot()  
                  * "safe" modes.  
                  */  
                 assert('/' == path[0]);  
                 fprintf(f, "_whatdb %s/whatis.db\n", path + 1);  
         }          }
           while (inflight > 0) {
         fclose(f);                  irc = recv(srv_fd, dummy, sizeof(dummy), 0);
         return(i == dirs->sz);                  if (irc > 0)
                           inflight--;
                   else if (irc == -1 && errno == EAGAIN)
                           nanosleep(&timeout, NULL);
                   else {
                           if (irc == 0)
                                   errno = ECONNRESET;
                           warn("recv");
                           inflight = -1;
                   }
           }
           if (verbose_flag)
                   warnx("processed %d files in %d directories",
                       goodfiles, gooddirs);
           if (baddirs > 0)
                   warnx("skipped %d %s due to errors", baddirs,
                       baddirs == 1 ? "directory" : "directories");
           if (badfiles > 0)
                   warnx("skipped %d %s due to errors", badfiles,
                       badfiles == 1 ? "file" : "files");
           if (fatal != 0) {
                   warnx("processing aborted due to fatal error, "
                       "results are probably incomplete");
                   inflight = -1;
           }
           return inflight;
 }  }
   
 /*  int
  * Copyright (c) 1983, 1992, 1993  main(int argc, char **argv)
  *      The Regents of the University of California.  All rights reserved.  
  *  
  * Redistribution and use in source and binary forms, with or without  
  * modification, are permitted provided that the following conditions  
  * are met:  
  * 1. Redistributions of source code must retain the above copyright  
  *    notice, this list of conditions and the following disclaimer.  
  * 2. Redistributions in binary form must reproduce the above copyright  
  *    notice, this list of conditions and the following disclaimer in the  
  *    documentation and/or other materials provided with the distribution.  
  * 3. Neither the name of the University nor the names of its contributors  
  *    may be used to endorse or promote products derived from this software  
  *    without specific prior written permission.  
  *  
  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND  
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE  
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE  
  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE  
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL  
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS  
  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)  
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT  
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY  
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF  
  * SUCH DAMAGE.  
  */  
 static int  
 mkpath(char *path, mode_t mode, mode_t dir_mode)  
 {  {
         struct stat sb;          struct sigaction sa;
         char *slash;          const char      *defos, *outtype;
         int done, exists;          int              srv_fds[2];
           int              dstdir_fd;
           int              opt;
           pid_t            pid;
   
         slash = path;          defos = NULL;
           outtype = "ascii";
         for (;;) {          while ((opt = getopt(argc, argv, "I:T:v")) != -1) {
                 /* LINTED */                  switch (opt) {
                 slash += strspn(slash, "/");                  case 'I':
                 /* LINTED */                          defos = optarg;
                 slash += strcspn(slash, "/");                          break;
                   case 'T':
                 done = (*slash == '\0');                          outtype = optarg;
                 *slash = '\0';                          break;
                   case 'v':
                 /* skip existing path components */                          verbose_flag += 1;
                 exists = !stat(path, &sb);                          break;
                 if (!done && exists && S_ISDIR(sb.st_mode)) {                  default:
                         *slash = '/';                          usage();
                         continue;  
                 }                  }
           }
   
                 if (mkdir(path, done ? mode : dir_mode) == 0) {          if (argc > 0) {
                         if (mode > 0777 && chmod(path, mode) < 0)                  argc -= optind;
                                 return (-1);                  argv += optind;
                 } else {          }
                         if (!exists) {          if (argc != 2) {
                                 /* Not there */                  switch (argc) {
                                 return (-1);                  case 0:
                         }                          warnx("missing arguments: srcdir and dstdir");
                         if (!S_ISDIR(sb.st_mode)) {                          break;
                                 /* Is there, but isn't a directory */                  case 1:
                                 errno = ENOTDIR;                          warnx("missing argument: dstdir");
                                 return (-1);                          break;
                         }                  default:
                           warnx("too many arguments: %s", argv[2]);
                           break;
                 }                  }
                   usage();
           }
   
                 if (done)          memset(&sa, 0, sizeof(sa));
                         break;          sa.sa_handler = &signal_handler;
           sa.sa_flags = SA_NOCLDWAIT;
           if (sigfillset(&sa.sa_mask) == -1)
                   err(1, "sigfillset");
           if (sigaction(SIGHUP, &sa, NULL) == -1)
                   err(1, "sigaction(SIGHUP)");
           if (sigaction(SIGINT, &sa, NULL) == -1)
                   err(1, "sigaction(SIGINT)");
           if (sigaction(SIGPIPE, &sa, NULL) == -1)
                   err(1, "sigaction(SIGPIPE)");
           if (sigaction(SIGTERM, &sa, NULL) == -1)
                   err(1, "sigaction(SIGTERM)");
           if (sigaction(SIGCHLD, &sa, NULL) == -1)
                   err(1, "sigaction(SIGCHLD)");
   
                 *slash = '/';          if (socketpair(AF_LOCAL, SOCK_STREAM, AF_UNSPEC, srv_fds) == -1)
                   err(1, "socketpair");
   
           pid = fork();
           switch (pid) {
           case -1:
                   err(1, "fork");
           case 0:
                   close(srv_fds[0]);
                   run_mandocd(srv_fds[1], outtype, defos);
           default:
                   break;
         }          }
           close(srv_fds[1]);
   
         return (0);          if ((dstdir_fd = open(argv[1], O_RDONLY | O_DIRECTORY)) == -1)
                   err(1, "open destination %s", argv[1]);
   
           if (chdir(argv[0]) == -1)
                   err(1, "chdir to source %s", argv[0]);
   
           return process_tree(srv_fds[0], dstdir_fd) == -1 ? 1 : 0;
   }
   
   void
   usage(void)
   {
           fprintf(stderr, "usage: %s [-I os=name] [-T output] "
               "srcdir dstdir\n", BINM_CATMAN);
           exit(1);
 }  }

Legend:
Removed from v.1.11.2.1  
changed lines
  Added in v.1.29

CVSweb