]> Zhao Yanbai Git Server - minix.git/commitdiff
Import NetBSD xinstall
authorThomas Veerman <thomas@minix3.org>
Wed, 6 Jun 2012 14:31:59 +0000 (14:31 +0000)
committerThomas Veerman <thomas@minix3.org>
Mon, 18 Jun 2012 10:54:50 +0000 (10:54 +0000)
Also, fix mk files for cross compilation.

commands/Makefile
commands/install/Makefile [deleted file]
commands/install/install.c [deleted file]
docs/UPDATING
share/mk/bsd.own.mk
share/mk/sys.mk
usr.bin/Makefile
usr.bin/xinstall/Makefile [new file with mode: 0644]
usr.bin/xinstall/install.1 [new file with mode: 0644]
usr.bin/xinstall/pathnames.h [new file with mode: 0644]
usr.bin/xinstall/xinstall.c [new file with mode: 0644]

index 97c96af06d72052efa24b24283cfecbca7f9eafd..4dda5c87819378bc719d24e5eb4da52d2a98c810 100644 (file)
@@ -13,7 +13,7 @@ SUBDIR=       add_route arp ash at \
        eject elvis env expand factor fbdctl \
        find finger fingerd fix fold format fortune fsck.mfs \
        ftp101 gcore gcov-pull getty grep head hexdump host \
-       hostaddr id ifconfig ifdef install \
+       hostaddr id ifconfig ifdef \
        intr ipcrm ipcs irdpd isoread join kill last \
        less loadkeys loadramdisk logger look lp \
        lpd ls lspci mail MAKEDEV \
diff --git a/commands/install/Makefile b/commands/install/Makefile
deleted file mode 100644 (file)
index 1c52030..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-PROG=  xinstall
-SRCS=  install.c
-BINDIR=        /bin
-BINMODE= 4755
-MAN=
-
-PROGNAME=install
-
-.include <bsd.prog.mk>
diff --git a/commands/install/install.c b/commands/install/install.c
deleted file mode 100644 (file)
index b30f604..0000000
+++ /dev/null
@@ -1,657 +0,0 @@
-/*     install 1.11 - install files.                   Author: Kees J. Bot
- *                                                             21 Feb 1993
- */
-#define nil 0
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <string.h>
-#include <errno.h>
-#include <a.out.h>
-#include <limits.h>
-#include <pwd.h>
-#include <grp.h>
-#include <utime.h>
-#include <signal.h>
-
-/* First line used on a self-decompressing executable. */
-char ZCAT[]=   "#!/usr/bin/zexec /usr/bin/zcat\n";
-char GZCAT[]=  "#!/usr/bin/zexec /usr/bin/gzcat\n";
-
-/* Compression filters. */
-char *COMPRESS[]=      { "compress", nil };
-char *GZIP[]=          { "gzip", "-#", nil };
-
-int excode= 0;         /* Exit code. */
-
-void report(char *label)
-{
-       if (label == nil || label[0] == 0)
-               fprintf(stderr, "install: %s\n", strerror(errno));
-       else
-               fprintf(stderr, "install: %s: %s\n", label, strerror(errno));
-       excode= 1;
-}
-
-void fatal(char *label)
-{
-       report(label);
-       exit(1);
-}
-
-void *allocate(void *mem, size_t size)
-/* Safe malloc/realloc. */
-{
-       mem= mem == nil ? malloc(size) : realloc(mem, size);
-
-       if (mem == nil) fatal(nil);
-       return mem;
-}
-
-void deallocate(void *mem)
-{
-       if (mem != nil) free(mem);
-}
-
-int lflag= 0;          /* Make a link if possible. */
-int cflag= 0;          /* Copy if you can't link, otherwise symlink. */
-int dflag= 0;          /* Create a directory. */
-int pflag= 0;          /* Preserve timestamps. */
-int strip= 0;          /* Strip the copy. */
-char **compress= nil;  /* Compress utility to make a compressed executable. */
-char *zcat= nil;       /* Line one to decompress. */
-
-int make_hardlink = 0; /* Make a hard link */
-int make_symlink = 0;  /* Make a symbolic link */
-
-long stack= -1;                /* Amount of heap + stack. */
-int wordpow= 1;                /* Must be multiplied with wordsize ** wordpow */
-                       /* So 8kb for an 8086 and 16kb for the rest. */
-
-pid_t filter(int fd, char **command)
-/* Let a command filter the output to fd. */
-{
-       pid_t pid;
-       int pfd[2];
-
-       if (pipe(pfd) < 0) {
-               report("pipe()");
-               return -1;
-       }
-
-       switch ((pid= fork())) {
-       case -1:
-               report("fork()");
-               return -1;
-       case 0:
-               /* Run the filter. */
-               dup2(pfd[0], 0);
-               dup2(fd, 1);
-               close(pfd[0]);
-               close(pfd[1]);
-               close(fd);
-               signal(SIGPIPE, SIG_DFL);
-               execvp(command[0], command);
-               fatal(command[0]);
-       }
-       /* Connect fd to the pipe. */
-       dup2(pfd[1], fd);
-       close(pfd[0]);
-       close(pfd[1]);
-       return pid;
-}
-
-int mkdirp(char *dir, int mode, int owner, int group)
-/* mkdir -p dir */
-{
-       int keep;
-       char *sep, *pref;
-
-       sep= dir;
-       while (*sep == '/') sep++;
-       
-       if (*sep == 0) {
-               errno= EINVAL;
-               return -1;
-       }
-
-       do {
-               while (*sep != '/' && *sep != 0) sep++;
-               pref= sep;
-               while (*sep == '/') sep++;
-
-               keep= *pref; *pref= 0;
-
-               if (strcmp(dir, ".") == 0 || strcmp(dir, "..") == 0) continue;
-
-               if (mkdir(dir, mode) < 0) {
-                       if (errno != EEXIST || *sep == 0) {
-                               /* On purpose not doing: *pref= keep; */
-                               return -1;
-                       }
-               } else {
-                       if (chown(dir, owner, group) < 0 && errno != EPERM)
-                               return -1;
-               }
-       } while (*pref= keep, *sep != 0);
-       return 0;
-}
-
-void makedir(char *dir, int mode, int owner, int group)
-/* Install a directory, and set it's modes. */
-{
-       struct stat st;
-
-       if (stat(dir, &st) < 0) {
-               if (errno != ENOENT) { report(dir); return; }
-
-               /* The target doesn't exist, make it. */
-               if (mode == -1) mode= 0755;
-               if (owner == -1) owner= getuid();
-               if (group == -1) group= getgid();
-
-               if (mkdirp(dir, mode, owner, group) < 0) {
-                       report(dir); return;
-               }
-       } else {
-               /* The target does exist, change mode and ownership. */
-               if (mode == -1) mode= (st.st_mode & 07777) | 0555;
-
-               if ((st.st_mode & 07777) != mode) {
-                       if (chmod(dir, mode) < 0) { report(dir); return; }
-               }
-               if (owner == -1) owner= st.st_uid;
-               if (group == -1) group= st.st_gid;
-               if (st.st_uid != owner || st.st_gid != group) {
-                       if (chown(dir, owner, group) < 0 && errno != EPERM) {
-                               report(dir); return;
-                       }
-                       /* Set the mode again, chown may have wrecked it. */
-                       (void) chmod(dir, mode);
-               }
-               
-               if (pflag) {
-                       struct utimbuf ubuf;
-
-                       ubuf.actime= st.st_atime;
-                       ubuf.modtime= st.st_mtime;
-
-                       if (utime(dir, &ubuf) < 0 && errno != EPERM) {
-                               report(dir); return;
-                       }
-               }
-       }
-}
-
-int setstack(struct exec *hdr)
-/* Set the stack size in a header.  Return true if something changed. */
-{
-       long total;
-
-       total= stack;
-       while (wordpow > 0) {
-               total *= hdr->a_cpu == A_I8086 ? 2 : 4;
-               wordpow--;
-       }
-       total+= hdr->a_data + hdr->a_bss;
-
-       if (!(hdr->a_flags & A_SEP)) {
-               total+= hdr->a_text;
-#ifdef A_PAL
-               if (hdr->a_flags & A_PAL) total+= hdr->a_hdrlen;
-#endif
-       }
-       if (hdr->a_cpu == A_I8086 && total > 0x10000L)
-               total= 0x10000L;
-
-       if (hdr->a_total != total) {
-               /* Need to change stack allocation. */
-               hdr->a_total= total;
-
-               return 1;
-       }
-       return 0;
-}
-
-void copylink(char *source, char *dest, int mode, int owner, int group)
-{
-       struct stat sst, dst;
-       int sfd, dfd, n;
-       int r, same= 0, change= 0, docopy= 1;
-       char buf[4096];
-#      define hdr ((struct exec *) buf)
-       pid_t pid = 0;
-       int status = 0;
-       char *p;
-
-       /* Source must exist as a plain file, dest may exist as a plain file. */
-       if (make_symlink && source[0] != '/'
-           && (p = strrchr(dest, '/')) != NULL ) {
-               /* creating a relative symlink; compute real target */
-               strlcpy(buf, dest, sizeof(buf));
-               if (p - dest + 1 >= (int)sizeof(buf))
-                       p = dest + sizeof(buf) - 2;
-               buf[p - dest + 1] = '\0';
-               strlcat(buf, source, sizeof(buf));
-               r = stat(buf, &sst);
-       } else
-               r = stat(source, &sst);
-       if (r < 0) { report(source); return; }
-
-       if (mode == -1) {
-               mode= sst.st_mode & 07777;
-               if (!lflag || cflag) {
-                       mode|= 0444;
-                       if (mode & 0111) mode|= 0111;
-               }
-       }
-       if (owner == -1) owner= sst.st_uid;
-       if (group == -1) group= sst.st_gid;
-
-       if (!make_symlink && !S_ISREG(sst.st_mode)) {
-               fprintf(stderr, "install: %s is not a regular file\n", source);
-               excode= 1;
-               return;
-       }
-       r= lstat(dest, &dst);
-       if (r < 0) {
-               if (errno != ENOENT) { report(dest); return; }
-       } else {
-               if (!make_symlink && !S_ISREG(dst.st_mode)) {
-                       fprintf(stderr, "install: %s is not a regular file\n",
-                                                                       dest);
-                       excode= 1;
-                       return;
-               }
-
-               /* Are the files the same? */
-               if (sst.st_dev == dst.st_dev && sst.st_ino == dst.st_ino) {
-                       if (!lflag && cflag) {
-                               fprintf(stderr,
-                               "install: %s and %s are the same, can't copy\n",
-                                       source, dest);
-                               excode= 1;
-                               return;
-                       }
-                       same= 1;
-               }
-       }
-
-       if (lflag) {
-               /* Try to link the files. */
-
-               if (r >= 0 && unlink(dest) < 0) {
-                       report(dest); return;
-               }
-
-                if (make_hardlink) {
-                   r = link(source, dest);
-                }
-                else if (make_symlink) {
-                   r = symlink(source, dest);
-                } else {
-                   fprintf(stderr,
-                           "install: must specify hardlink or symlink\n");
-                }
-
-               if (r >= 0) {
-                       docopy= 0;
-               } else {
-                       if (!cflag || errno != EXDEV) {
-                               fprintf(stderr,
-                                       "install: can't link %s to %s: %s\n",
-                                       source, dest, strerror(errno));
-                               excode= 1;
-                               return;
-                       }
-               }
-       }
-
-       if (docopy && !same) {
-               /* Copy the files, stripping if necessary. */
-               long count= LONG_MAX;
-               int first= 1;
-
-               if ((sfd= open(source, O_RDONLY)) < 0) {
-                       report(source); return;
-               }
-
-               /* Open for write is less simple, its mode may be 444. */
-               dfd= open(dest, O_WRONLY|O_CREAT|O_TRUNC, mode | 0600);
-               if (dfd < 0 && errno == EACCES) {
-                       (void) chmod(dest, mode | 0600);
-                       dfd= open(dest, O_WRONLY|O_TRUNC);
-               }
-               if (dfd < 0) {
-                       report(dest);
-                       close(sfd);
-                       return;
-               }
-
-               pid= 0;
-               while (count > 0 && (n= read(sfd, buf, sizeof(buf))) > 0) {
-                       if (first && n >= A_MINHDR && !BADMAG(*hdr)) {
-                               if (strip) {
-                                       count= hdr->a_hdrlen
-                                               + hdr->a_text + hdr->a_data;
-#ifdef A_NSYM
-                                       hdr->a_flags &= ~A_NSYM;
-#endif
-                                       hdr->a_syms= 0;
-                               }
-                               if (stack != -1 && setstack(hdr)) change= 1;
-
-                               if (compress != nil) {
-                                       /* Write first #! line. */
-                                       (void) write(dfd, zcat, strlen(zcat));
-
-                                       /* Put a compressor in between. */
-                                       if ((pid= filter(dfd, compress)) < 0) {
-                                               close(sfd);
-                                               close(dfd);
-                                               return;
-                                       }
-                                       change= 1;
-                               }
-                       }
-                       if (count < n) n= count;
-
-                       if (write(dfd, buf, n) < 0) {
-                               report(dest);
-                               close(sfd);
-                               close(dfd);
-                               if (pid != 0) (void) waitpid(pid, nil, 0);
-                               return;
-                       }
-                       count-= n;
-                       first= 0;
-               }
-               if (n < 0) report(source);
-               close(sfd);
-               close(dfd);
-               if (pid != 0 && waitpid(pid, &status, 0) < 0 || status != 0) {
-                       excode= 1;
-                       return;
-               }
-               if (n < 0) return;
-       } else {
-               if (stack != -1) {
-                       /* The file has been linked into place.  Set the
-                        * stack size.
-                        */
-                       if ((dfd= open(dest, O_RDWR)) < 0) {
-                               report(dest);
-                               return;
-                       }
-
-                       if ((n= read(dfd, buf, sizeof(*hdr))) < 0) {
-                               report(dest); return;
-                       }
-
-                       if (n >= A_MINHDR && !BADMAG(*hdr) && setstack(hdr)) {
-                               if (lseek(dfd, (off_t) 0, SEEK_SET) == -1
-                                       || write(dfd, buf, n) < 0
-                               ) {
-                                       report(dest);
-                                       close(dfd);
-                                       return;
-                               }
-                               change= 1;
-                       }
-                       close(dfd);
-               }
-       }
-
-        /* Don't modify metadata if symlink target doesn't exist */
-        if (make_symlink && stat(dest, &dst) < 0) return;
-
-       if (stat(dest, &dst) < 0) { report(dest); return; }
-
-       if ((dst.st_mode & 07777) != mode) {
-               if (chmod(dest, mode) < 0) { report(dest); return; }
-       }
-       if (dst.st_uid != owner || dst.st_gid != group) {
-               if (chown(dest, owner, group) < 0 && errno != EPERM) {
-                       report(dest); return;
-               }
-               /* Set the mode again, chown may have wrecked it. */
-               (void) chmod(dest, mode);
-       }
-       if (!change || pflag) {
-               struct utimbuf ubuf;
-
-               ubuf.actime= dst.st_atime;
-               ubuf.modtime= sst.st_mtime;
-
-               if (utime(dest, &ubuf) < 0 && errno != EPERM) {
-                       report(dest); return;
-               }
-       }
-}
-
-void usage(void)
-{
-       fprintf(stderr, "\
-Usage:\n\
-  install [-lcpsz#] [-o owner] [-g group] [-m mode] [-S stack] [file1] file2\n\
-  install [-lcpsz#] [-o owner] [-g group] [-m mode] [-S stack] file ... dir\n\
-  install [-c] -d [-o owner] [-g group] [-m mode] directory\n");
-       exit(1);
-}
-
-int main(int argc, char **argv)
-{
-       int i= 1;
-       int mode= -1;           /* Mode of target. */
-       int owner= -1;          /* Owner. */
-       int group= -1;          /* Group. */
-       int super = 0;
-#if NGROUPS_MAX > 0
-       gid_t groups[NGROUPS_MAX];
-       int ngroups;
-       int g;
-#endif
-
-       /* Only those in group 0 are allowed to set owner and group. */
-       if (getgid() == 0) super = 1;
-#if NGROUPS_MAX > 0
-       ngroups= getgroups(NGROUPS_MAX, groups);
-       for (g= 0; g < ngroups; g++) if (groups[g] == 0) super= 1;
-#endif
-       if (!super) {
-               setgid(getgid());
-               setuid(getuid());
-       }
-
-       /* May use a filter. */
-       signal(SIGPIPE, SIG_IGN);
-
-       while (i < argc && argv[i][0] == '-') {
-               char *p= argv[i++]+1;
-               char *end;
-               unsigned long num;
-               int wp;
-               struct passwd *pw;
-               struct group *gr;
-
-               if (strcmp(p, "-") == 0) break;
-
-               while (*p != 0) {
-                       switch (*p++) {
-                       case 'c':       cflag= 1;       break;
-                       case 's':       strip= 1;       break;
-                       case 'd':       dflag= 1;       break;
-                       case 'p':       pflag= 1;       break;
-                       case 'z':
-                               if (compress == nil) {
-                                       compress= COMPRESS;
-                                       zcat= ZCAT;
-                               }
-                               break;
-                       case 'o':
-                               if (*p == 0) {
-                                       if (i == argc) usage();
-                                       p= argv[i++];
-                                       if (*p == 0) usage();
-                               }
-                               num= strtoul(p, &end, 10);
-                               if (*end == 0) {
-                                       if ((uid_t) num != num) usage();
-                                       owner= num;
-                               } else {
-                                       if ((pw= getpwnam(p)) == nil) {
-                                               fprintf(stderr,
-                                               "install: %s: unknown user\n",
-                                                       p);
-                                               exit(1);
-                                       }
-                                       owner= pw->pw_uid;
-                               }
-                               p= "";
-                               break;
-                       case 'g':
-                               if (*p == 0) {
-                                       if (i == argc) usage();
-                                       p= argv[i++];
-                                       if (*p == 0) usage();
-                               }
-                               num= strtoul(p, &end, 10);
-                               if (*end == 0) {
-                                       if ((gid_t) num != num) usage();
-                                       group= num;
-                               } else {
-                                       if ((gr= getgrnam(p)) == nil) {
-                                               fprintf(stderr,
-                                               "install: %s: unknown group\n",
-                                                       p);
-                                               exit(1);
-                                       }
-                                       group= gr->gr_gid;
-                               }
-                               p= "";
-                               break;
-                       case 'm':
-                               if (*p == 0) {
-                                       if (i == argc) usage();
-                                       p= argv[i++];
-                                       if (*p == 0) usage();
-                               }
-                               num= strtoul(p, &end, 010);
-                               if (*end != 0 || (num & 07777) != num) usage();
-                               mode= num;
-                               if ((mode & S_ISUID) && super && owner == -1) {
-                                       /* Setuid what?  Root most likely. */
-                                       owner= 0;
-                               }
-                               if ((mode & S_ISGID) && super && group == -1) {
-                                       group= 0;
-                               }
-                               p= "";
-                               break;
-                       case 'l':
-                                lflag= 1;
-                               if (*p == 0) {
-                                       if (i == argc) usage();
-                                       p= argv[i++];
-                                       if (*p == 0) usage();
-                               }
-                                switch(*p) {
-                                   case 'h':
-                                      make_hardlink = 1;
-                                      break;
-                                   case 's':
-                                      make_symlink = 1;
-                                      break;
-                                   default:
-                                      break;
-                                }
-                               p= "";
-                              break;
-                       case 'S':
-                               if (*p == 0) {
-                                       if (i == argc) usage();
-                                       p= argv[i++];
-                                       if (*p == 0) usage();
-                               }
-                               stack= strtol(p, &end, 0);
-                               wp= 0;
-                               if (end == p || stack < 0) usage();
-                               p= end;
-                               while (*p != 0) {
-                                       switch (*p++) {
-                                       case 'm':
-                                       case 'M': num= 1024 * 1024L; break;
-                                       case 'k':
-                                       case 'K': num= 1024; break;
-                                       case 'w':
-                                       case 'W': num= 4; wp++; break;
-                                       case 'b':
-                                       case 'B': num= 1; break;
-                                       default: usage();
-                                       }
-                                       if (stack > LONG_MAX / num) usage();
-                                       stack*= num;
-                               }
-                               wordpow= 0;
-                               while (wp > 0) { stack /= 4; wordpow++; wp--; }
-                               break;
-                       default:
-                               if ((unsigned) (p[-1] - '1') <= ('9' - '1')) {
-                                       compress= GZIP;
-                                       GZIP[1][1]= p[-1];
-                                       zcat= GZCAT;
-                                       break;
-                               }
-                               usage();
-                       }
-               }
-       }
-       /* Some options don't mix. */
-       if (dflag && (lflag || strip)) usage();
-
-       /* Don't let the user umask interfere. */
-       umask(000);
-
-       if (dflag) {
-               /* install directory */
-               if ((argc - i) != 1) usage();
-
-               makedir(argv[i], mode, owner, group);
-       } else {
-               struct stat st;
-
-               if ((argc - i) < 1) usage();
-               if ((lflag || cflag) && (argc - i) == 1) usage();
-
-               if (lstat(argv[argc-1], &st) >= 0 && S_ISDIR(st.st_mode)) {
-                       /* install file ... dir */
-                       char *target= nil;
-                       char *base;
-
-                       if ((argc - i) == 1) usage();
-
-                       while (i < argc-1) {
-                               if ((base= strrchr(argv[i], '/')) == nil)
-                                       base= argv[i];
-                               else
-                                       base++;
-                               target= allocate(target, strlen(argv[argc-1])
-                                               + 1 + strlen(base) + 1);
-                               strcpy(target, argv[argc-1]);
-                               strcat(target, "/");
-                               strcat(target, base);
-
-                               copylink(argv[i++], target, mode, owner, group);
-                       }
-               } else {
-                       /* install [file1] file2 */
-
-                       copylink(argv[i], argv[argc-1], mode, owner, group);
-               }
-       }
-       exit(excode);
-}
index 14cbc73053ff37f4769609bfb08b0246dbf1c81d..337a40c647a0c94f7e0577abc02e4be7d1318c80 100644 (file)
@@ -1,3 +1,15 @@
+20120608:
+       New install and mk files require the following steps:
+
+       # cp /usr/src/share/mk/*.mk /usr/share/mk
+       # cd /usr/src
+       # make -C usr.bin/xinstall all
+       # cp usr.bin/xinstall/xinstall /usr/bin/install
+       # rm /bin/install
+
+       then do
+       # make clean world
+
 20120510:
        WARNING: the shared libraries major revision set to 0 will break
        existing dynamically linked binaries if they exist.
index 1c64f1fdd5b1ac14e7bff76ff4775255ba45adf7..97cb3404512aa8120514990f0c3930a0edc94340 100644 (file)
@@ -56,6 +56,9 @@ TOOLCHAIN_MISSING?=   no
 # default to GCC4
 .if !defined(HAVE_GCC) && !defined(HAVE_PCC)
 HAVE_GCC=      4
+.if !defined(_GCC_LIBGCCDIR)
+_GCC_LIBGCCDIR= ${DESTDIR}/usr/lib
+.endif
 .endif
 
 .if \
@@ -128,8 +131,10 @@ _SRC_TOP_OBJ_!=            cd ${_SRC_TOP_} && ${PRINTOBJDIR}
 
 .if (${_SRC_TOP_} != "") && \
     (${TOOLCHAIN_MISSING} == "no" || defined(EXTERNAL_TOOLCHAIN))
+.if (defined(BUILDSH))
 USETOOLS?=     yes
 .endif
+.endif
 USETOOLS?=     no
 
 
@@ -756,10 +761,11 @@ _MKVARS.no= \
        MKMANDOC MKMANZ MKOBJDIRS \
        MKPCC MKPCCCMDS \
        MKSOFTFLOAT MKSTRIPIDENT \
-       MKUNPRIVED MKUPDATE MKX11 MKZFS MKBSDTAR
+       MKUNPRIVED MKUPDATE MKX11 MKZFS MKBSDTAR \
+       MKARZERO
 #MINIX-specific vars
 _MKVARS.no+= \
-       MKIMAGEONLY MKSMALL MKBUILDEXT2RD
+       MKIMAGEONLY MKSMALL MKBUILDEXT2RD USETOOLS
 .for var in ${_MKVARS.no}
 ${var}?=no
 .endfor
index 8ae1cf18258a8be3b145a268b83fdd54780d37ed..e944935772d5a6fcecb60d39e29bbc8ca9f8869a 100644 (file)
@@ -6,7 +6,7 @@ unix?=          We run MINIX.
 # This variable should be used to differentiate Minix builds in Makefiles.
 __MINIX=       yes
 
-.SUFFIXES: .a .o .ln .s .S .c .cc .cpp .cxx .C .f .F .r .p .l .y #.sh
+.SUFFIXES: .a .o .ln .s .S .c .cc .cpp .cxx .C .f .F .r .p .l .y .sh
 
 .LIBS:         .a
 
index f2db6633d8365d6904abd6269b92c4e243c6dd76..29b4e90423ad69b55cfd0134ecb77b257f3d62b1 100644 (file)
@@ -5,7 +5,7 @@
 # NetBSD imports
 SUBDIR= login indent m4 make mktemp stat tic sed mkdep uniq seq du man \
        apropos chpass newgrp passwd bzip2 bzip2recover gzip su genassym \
-       ldd/elf32 .WAIT ldd
+       xinstall ldd/elf32 .WAIT ldd
 
 # Non-NetBSD imports
 SUBDIR+= ministat
diff --git a/usr.bin/xinstall/Makefile b/usr.bin/xinstall/Makefile
new file mode 100644 (file)
index 0000000..c1862e2
--- /dev/null
@@ -0,0 +1,23 @@
+#      $NetBSD: Makefile,v 1.22 2011/08/17 14:00:30 christos Exp $
+#      @(#)Makefile    8.1 (Berkeley) 6/6/93
+
+.include <bsd.own.mk>
+
+PROG=  xinstall
+SRCS=  xinstall.c getid.c
+MAN=   install.1
+
+.PATH:         ${NETBSDSRCDIR}/usr.sbin/mtree
+CPPFLAGS+=     -I${NETBSDSRCDIR}/usr.sbin/mtree
+
+.if (${HOSTPROG:U} == "")
+DPADD+= ${LIBUTIL}
+LDADD+= -lutil
+.endif
+
+COPTS.xinstall.c += -Wno-format-nonliteral
+
+
+PROGNAME=install
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/xinstall/install.1 b/usr.bin/xinstall/install.1
new file mode 100644 (file)
index 0000000..7967759
--- /dev/null
@@ -0,0 +1,338 @@
+.\"    $NetBSD: install.1,v 1.47 2012/04/08 22:00:40 wiz Exp $
+.\"
+.\" Copyright (c) 1987, 1990, 1993
+.\"    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.
+.\"
+.\"     @(#)install.1  8.1 (Berkeley) 6/6/93
+.\"
+.Dd May 1, 2009
+.Dt INSTALL 1
+.Os
+.Sh NAME
+.Nm install
+.Nd install binaries
+.Sh SYNOPSIS
+.Nm
+.Op Fl bcprsU
+.Op Fl a Ar command
+.Op Fl B Ar suffix
+.Op Fl D Ar destdir
+.Op Fl f Ar flags
+.Op Fl g Ar group
+.Op Fl h Ar hash
+.Op Fl l Ar linkflags
+.Op Fl M Ar metalog
+.Op Fl m Ar mode
+.Op Fl N Ar dbdir
+.Op Fl o Ar owner
+.Op Fl S Ar stripflag
+.Op Fl T Ar tags
+.Ar file1 file2
+.Nm
+.Op Fl bcprsU
+.Op Fl a Ar command
+.Op Fl B Ar suffix
+.Op Fl D Ar destdir
+.Op Fl f Ar flags
+.Op Fl g Ar group
+.Op Fl h Ar hash
+.Op Fl l Ar linkflags
+.Op Fl M Ar metalog
+.Op Fl m Ar mode
+.Op Fl N Ar dbdir
+.Op Fl o Ar owner
+.Op Fl S Ar stripflag
+.Op Fl T Ar tags
+.Ar file1 ...\&
+.Ar fileN directory
+.Nm
+.Fl d
+.Op Fl pU
+.Op Fl a Ar command
+.Op Fl D Ar destdir
+.Op Fl g Ar group
+.Op Fl M Ar metalog
+.Op Fl m Ar mode
+.Op Fl N Ar dbdir
+.Op Fl o Ar owner
+.Op Fl T Ar tags
+.Ar directory ...\&
+.Sh DESCRIPTION
+The file(s) are copied
+(or linked if the
+.Fl l
+option is specified) to the target file or directory.
+If the destination is a directory, then the
+.Ar file
+is copied into
+.Ar directory
+with its original filename.
+If the target file already exists, it is
+either renamed to
+.Ar file.old
+if the
+.Fl b
+option is given
+or overwritten
+if permissions allow; an alternate backup suffix may be specified via the
+.Fl B
+option's argument.
+.Pp
+.Bl -tag -width XsXXstripflagsXX
+.It Fl a Ar command
+Run
+.Ar command
+on the target after installation and stripping
+.Pq Fl s ,
+but before
+ownership, permissions or timestamps are set and before renaming
+.Pq Fl r
+occurs.
+.Ar command
+is invoked via the
+.Xr sh 1
+shell, allowing a single
+.Fl a
+argument be to specified to
+.Nm
+which the shell can then tokenize.
+.It Fl B Ar suffix
+Use
+.Ar suffix
+as the backup suffix if
+.Fl b
+is given.
+If
+.Ar suffix
+contains a '%' sign, a numbered backup will be performed, and the
+%-pattern will be expanded using
+.Xr sprintf 3 ,
+given an integer counter as the backup number.
+The counter used starts from 0, and the first available name resulting
+from the expansion is used.
+.It Fl b
+Backup any existing files before overwriting them by renaming
+them to
+.Ar file.old . See
+.Fl B
+for specifying a different backup suffix.
+.It Fl c
+Copy the file.
+This is the default behavior; the flag is maintained for backwards
+compatibility only.
+.It Fl D Ar destdir
+Specify the
+.Ev DESTDIR
+(top of the file hierarchy) that the items are installed in to.
+If
+.Fl M Ar metalog
+is in use, a leading string of
+.Dq Ar destdir
+will be removed from the file names logged to the
+.Ar metalog .
+This option does not affect where the actual files are installed.
+.It Fl d
+Create directories.
+Missing parent directories are created as required.
+.It Fl f Ar flags
+Specify the target's file flags.
+(See
+.Xr chflags 1
+for a list of possible flags and their meanings.)
+.It Fl g Ar group
+Specify a group.
+.It Fl h Ar hash
+When copying, calculate the digest of the files with
+.Ar hash
+to store in the
+.Fl M Ar metalog .
+Supported digests:
+.Bl -tag -width rmd160 -offset indent
+.It Sy none
+No hash.
+This is the default.
+.It Sy md5
+The MD5 cryptographic message digest.
+.It Sy rmd160
+The RMD-160 cryptographic message digest.
+.It Sy sha1
+The SHA-1 cryptographic message digest.
+.It Sy sha256
+The 256-bits
+.Tn SHA-2
+cryptographic message digest of the file.
+.It Sy sha384
+The 384-bits
+.Tn SHA-2
+cryptographic message digest of the file.
+.It Sy sha512
+The 512-bits
+.Tn SHA-2
+cryptographic message digest of the file.
+.El
+.It Fl l Ar linkflags
+Instead of copying the file make a link to the source.
+The type of the link is determined by the
+.Ar linkflags
+argument.
+Valid
+.Ar linkflags
+are:
+.Ar a
+(absolute),
+.Ar r
+(relative),
+.Ar h
+(hard),
+.Ar s
+(symbolic),
+.Ar m
+(mixed).
+Absolute and relative have effect only for symbolic links.
+Mixed links
+are hard links for files on the same filesystem, symbolic otherwise.
+.It Fl M Ar metalog
+Write the metadata associated with each item installed to
+.Ar metalog
+in an
+.Xr mtree 8
+.Dq full path
+specification line.
+The metadata includes: the file name and file type, and depending upon
+other options, the owner, group, file flags, modification time, and tags.
+.It Fl m Ar mode
+Specify an alternative mode.
+The default mode is set to rwxr-xr-x (0755).
+The specified mode may be either an octal or symbolic value; see
+.Xr chmod 1
+for a description of possible mode values.
+.It Fl N Ar dbdir
+Use the user database text file
+.Pa master.passwd
+and group database text file
+.Pa group
+from
+.Ar dbdir ,
+rather than using the results from the system's
+.Xr getpwnam 3
+and
+.Xr getgrnam 3
+(and related) library calls.
+.It Fl o Ar owner
+Specify an owner.
+.It Fl p
+Preserve the source files access and modification times.
+.It Fl r
+Install to a temporary file and then rename the file to its final destination
+name.
+This can be used for precious files, to avoid truncation of the original
+when error conditions (filesystem full etc.) occur.
+.It Fl S Ar stripflags
+.Nm
+passes
+.Ar stripflags
+as option arguments to
+.Xr strip 1 .
+When
+.Fl S
+is used,
+.Xr strip 1
+is invoked via the
+.Xr sh 1
+shell, allowing a single
+.Fl S
+argument be to specified to
+.Nm
+which the shell can then tokenize.
+Normally,
+.Nm
+invokes
+.Xr strip 1
+directly.
+This flag implies
+.Fl s .
+.It Fl s
+.Nm
+exec's the command
+.Xr strip 1
+to strip binaries so that install can be portable over a large
+number of systems and binary types.
+If the environment variable
+.Ev STRIP
+is set, it is used as the
+.Xr strip 1
+program.
+.It Fl T Ar tags
+Specify the
+.Xr mtree 8
+tags to write out for the file when using
+.Fl M Ar metalog .
+.It Fl U
+Indicate that install is running unprivileged, and that it should not
+try to change the owner, the group, or the file flags of the destination.
+The information that would have been updated can be stored in a log
+file with
+.Fl M Ar metalog .
+.El
+.Pp
+By default,
+.Nm
+preserves all file flags, with the exception of the ``nodump'' flag.
+.Pp
+The
+.Nm
+utility attempts to prevent copying a file onto itself.
+.Pp
+Installing
+.Pa /dev/null
+creates an empty file.
+.Sh ENVIRONMENT
+.Bl -tag -width Fl
+.It Ev STRIP
+The program used to strip installed binaries when the
+.Fl s
+option is used.
+If unspecified,
+.Pa /usr/bin/strip
+is used.
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Xr chflags 1 ,
+.Xr chgrp 1 ,
+.Xr chmod 1 ,
+.Xr cp 1 ,
+.Xr mv 1 ,
+.Xr strip 1 ,
+.Xr chown 8 ,
+.Xr mtree 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.2 .
diff --git a/usr.bin/xinstall/pathnames.h b/usr.bin/xinstall/pathnames.h
new file mode 100644 (file)
index 0000000..88ad1e2
--- /dev/null
@@ -0,0 +1,36 @@
+/*     $NetBSD: pathnames.h,v 1.6 2003/08/07 11:17:50 agc Exp $        */
+
+/*
+ * Copyright (c) 1989, 1993
+ *     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.
+ *
+ *     @(#)pathnames.h 8.1 (Berkeley) 6/6/93
+ */
+
+#ifndef _PATH_STRIP
+#define        _PATH_STRIP     "/usr/bin/strip"
+#endif
diff --git a/usr.bin/xinstall/xinstall.c b/usr.bin/xinstall/xinstall.c
new file mode 100644 (file)
index 0000000..c9555c0
--- /dev/null
@@ -0,0 +1,1287 @@
+/*     $NetBSD: xinstall.c,v 1.115 2011/09/06 18:50:32 joerg Exp $     */
+
+/*
+ * Copyright (c) 1987, 1993
+ *     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.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#else
+#define HAVE_FUTIMES 1
+#define HAVE_STRUCT_STAT_ST_FLAGS 1
+#endif
+
+#include <sys/cdefs.h>
+#if defined(__COPYRIGHT) && !defined(lint)
+__COPYRIGHT("@(#) Copyright (c) 1987, 1993\
+ The Regents of the University of California.  All rights reserved.");
+#endif /* not lint */
+
+#if defined(__RCSID) && !defined(lint)
+#if 0
+static char sccsid[] = "@(#)xinstall.c 8.1 (Berkeley) 7/21/93";
+#else
+__RCSID("$NetBSD: xinstall.c,v 1.115 2011/09/06 18:50:32 joerg Exp $");
+#endif
+#endif /* not lint */
+
+#define __MKTEMP_OK__  /* All uses of mktemp have been checked */
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <libgen.h>
+#include <paths.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <util.h>
+#include <vis.h>
+
+#include <md5.h>
+#include <rmd160.h>
+#include <sha1.h>
+#include <sha2.h>
+
+#include "pathnames.h"
+#include "mtree.h"
+
+#define STRIP_ARGS_MAX 32
+#define BACKUP_SUFFIX ".old"
+
+static int     dobackup, dodir, dostrip, dolink, dopreserve, dorename, dounpriv;
+static int     haveopt_f, haveopt_g, haveopt_m, haveopt_o;
+static int     numberedbackup;
+static int     mode = S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
+static char    pathbuf[MAXPATHLEN];
+static uid_t   uid = -1;
+static gid_t   gid = -1;
+static char    *group, *owner, *fflags, *tags;
+static FILE    *metafp;
+static char    *metafile;
+static u_long  fileflags;
+static char    *stripArgs;
+static char    *afterinstallcmd;
+static const char *suffix = BACKUP_SUFFIX;
+static char    *destdir;
+
+enum {
+       DIGEST_NONE = 0,
+       DIGEST_MD5,
+       DIGEST_RMD160,
+       DIGEST_SHA1,
+       DIGEST_SHA256,
+       DIGEST_SHA384,
+       DIGEST_SHA512,
+} digesttype = DIGEST_NONE;
+
+static char    *digest;
+
+#define LN_ABSOLUTE    0x01
+#define LN_RELATIVE    0x02
+#define LN_HARD                0x04
+#define LN_SYMBOLIC    0x08
+#define LN_MIXED       0x10
+
+#define        DIRECTORY       0x01            /* Tell install it's a directory. */
+#define        SETFLAGS        0x02            /* Tell install to set flags. */
+#define        HASUID          0x04            /* Tell install the uid was given */
+#define        HASGID          0x08            /* Tell install the gid was given */
+
+static void    afterinstall(const char *, const char *, int);
+static void    backup(const char *);
+static char   *copy(int, char *, int, char *, off_t);
+static int     do_link(char *, char *);
+static void    do_symlink(char *, char *);
+static void    install(char *, char *, u_int);
+static void    install_dir(char *, u_int);
+static void    makelink(char *, char *);
+static void    metadata_log(const char *, const char *, struct timeval *,
+           const char *, const char *, off_t);
+static int     parseid(char *, id_t *);
+static void    strip(char *);
+__dead static void     usage(void);
+static char   *xbasename(char *);
+static char   *xdirname(char *);
+
+int
+main(int argc, char *argv[])
+{
+       struct stat     from_sb, to_sb;
+       void            *set;
+       u_int           iflags;
+       int             ch, no_target;
+       char            *p, *to_name;
+
+       setprogname(argv[0]);
+
+       iflags = 0;
+       while ((ch = getopt(argc, argv, "a:cbB:dD:f:g:h:l:m:M:N:o:prsS:T:U"))
+           != -1)
+               switch((char)ch) {
+               case 'a':
+                       afterinstallcmd = strdup(optarg);
+                       if (afterinstallcmd == NULL)
+                               errx(1, "%s", strerror(ENOMEM));
+                       break;
+               case 'B':
+                       suffix = optarg;
+                       numberedbackup = 0;
+                       {
+                               /* Check if given suffix really generates
+                                  different suffixes - catch e.g. ".%" */
+                               char suffix_expanded0[FILENAME_MAX],
+                                    suffix_expanded1[FILENAME_MAX];
+                               (void)snprintf(suffix_expanded0, FILENAME_MAX,
+                                              suffix, 0);
+                               (void)snprintf(suffix_expanded1, FILENAME_MAX,
+                                              suffix, 1);
+                               if (strcmp(suffix_expanded0, suffix_expanded1)
+                                   != 0)
+                                       numberedbackup = 1;
+                       }
+                       /* fall through; -B implies -b */
+                       /*FALLTHROUGH*/
+               case 'b':
+                       dobackup = 1;
+                       break;
+               case 'c':
+                       /* ignored; was "docopy" which is now the default. */
+                       break;
+               case 'd':
+                       dodir = 1;
+                       break;
+               case 'D':
+                       destdir = optarg;
+                       break;
+#if ! HAVE_NBTOOL_CONFIG_H
+               case 'f':
+                       haveopt_f = 1;
+                       fflags = optarg;
+                       break;
+#endif
+               case 'g':
+                       haveopt_g = 1;
+                       group = optarg;
+                       break;
+               case 'h':
+                       digest = optarg;
+                       break;
+               case 'l':
+                       for (p = optarg; *p; p++)
+                               switch (*p) {
+                               case 's':
+                                       dolink &= ~(LN_HARD|LN_MIXED);
+                                       dolink |= LN_SYMBOLIC;
+                                       break;
+                               case 'h':
+                                       dolink &= ~(LN_SYMBOLIC|LN_MIXED);
+                                       dolink |= LN_HARD;
+                                       break;
+                               case 'm':
+                                       dolink &= ~(LN_SYMBOLIC|LN_HARD);
+                                       dolink |= LN_MIXED;
+                                       break;
+                               case 'a':
+                                       dolink &= ~LN_RELATIVE;
+                                       dolink |= LN_ABSOLUTE;
+                                       break;
+                               case 'r':
+                                       dolink &= ~LN_ABSOLUTE;
+                                       dolink |= LN_RELATIVE;
+                                       break;
+                               default:
+                                       errx(1, "%c: invalid link type", *p);
+                                       /* NOTREACHED */
+                               }
+                       break;
+               case 'm':
+                       haveopt_m = 1;
+                       if (!(set = setmode(optarg)))
+                               err(1, "Cannot set file mode `%s'", optarg);
+                       mode = getmode(set, 0);
+                       free(set);
+                       break;
+               case 'M':
+                       metafile = optarg;
+                       break;
+               case 'N':
+                       if (! setup_getid(optarg))
+                               errx(1,
+                           "Unable to use user and group databases in `%s'",
+                                   optarg);
+                       break;
+               case 'o':
+                       haveopt_o = 1;
+                       owner = optarg;
+                       break;
+               case 'p':
+                       dopreserve = 1;
+                       errx(1,
+                           "Minix lacks support for futimes(3)/utimes(2)");
+                       break;
+               case 'r':
+                       dorename = 1;
+                       break;
+               case 'S':
+                       stripArgs = strdup(optarg);
+                       if (stripArgs == NULL)
+                               errx(1, "%s", strerror(ENOMEM));
+                       /* fall through; -S implies -s */
+                       /*FALLTHROUGH*/
+               case 's':
+                       dostrip = 1;
+                       break;
+               case 'T':
+                       tags = optarg;
+                       break;
+               case 'U':
+                       dounpriv = 1;
+                       break;
+               case '?':
+               default:
+                       usage();
+               }
+       argc -= optind;
+       argv += optind;
+
+       /* strip and link options make no sense when creating directories */
+       if ((dostrip || dolink) && dodir)
+               usage();
+
+       /* strip and flags make no sense with links */
+       if ((dostrip || fflags) && dolink)
+               usage();
+
+       /* must have at least two arguments, except when creating directories */
+       if (argc < 2 && !dodir)
+               usage();
+
+       if (digest) {
+               if (0) {
+               } else if (strcmp(digest, "none") == 0) {
+                       digesttype = DIGEST_NONE;
+               } else if (strcmp(digest, "md5") == 0) {
+                       digesttype = DIGEST_MD5;
+               } else if (strcmp(digest, "rmd160") == 0) {
+                       digesttype = DIGEST_RMD160;
+               } else if (strcmp(digest, "sha1") == 0) {
+                       digesttype = DIGEST_SHA1;
+               } else if (strcmp(digest, "sha256") == 0) {
+                       digesttype = DIGEST_SHA256;
+               } else if (strcmp(digest, "sha384") == 0) {
+                       digesttype = DIGEST_SHA384;
+               } else if (strcmp(digest, "sha512") == 0) {
+                       digesttype = DIGEST_SHA512;
+               } else {
+                       warnx("unknown digest `%s'", digest);
+                       usage();
+               }
+       }
+
+       /* get group and owner id's */
+       if (group && !dounpriv) {
+               if (gid_from_group(group, &gid) == -1) {
+                       id_t id;
+                       if (!parseid(group, &id))
+                               errx(1, "unknown group %s", group);
+                       gid = id;
+               }
+               iflags |= HASGID;
+       }
+       if (owner && !dounpriv) {
+               if (uid_from_user(owner, &uid) == -1) {
+                       id_t id;
+                       if (!parseid(owner, &id))
+                               errx(1, "unknown user %s", owner);
+                       uid = id;
+               }
+               iflags |= HASUID;
+       }
+
+#if ! HAVE_NBTOOL_CONFIG_H
+       if (fflags && !dounpriv) {
+               if (string_to_flags(&fflags, &fileflags, NULL))
+                       errx(1, "%s: invalid flag", fflags);
+               /* restore fflags since string_to_flags() changed it */
+               fflags = flags_to_string(fileflags, "-");
+               iflags |= SETFLAGS;
+       }
+#endif
+
+       if (metafile) {
+               if ((metafp = fopen(metafile, "a")) == NULL)
+                       warn("open %s", metafile);
+       } else
+               digesttype = DIGEST_NONE;
+
+       if (dodir) {
+               for (; *argv != NULL; ++argv)
+                       install_dir(*argv, iflags);
+               exit (0);
+       }
+
+       no_target = stat(to_name = argv[argc - 1], &to_sb);
+       if (!no_target && S_ISDIR(to_sb.st_mode)) {
+               for (; *argv != to_name; ++argv)
+                       install(*argv, to_name, iflags | DIRECTORY);
+               exit(0);
+       }
+
+       /* can't do file1 file2 directory/file */
+       if (argc != 2) {
+               errx(EXIT_FAILURE, "the last argument (%s) "
+                   "must name an existing directory", argv[argc - 1]);
+               /* NOTREACHED */
+       }
+
+       if (!no_target) {
+               /* makelink() handles checks for links */
+               if (!dolink) {
+                       if (stat(*argv, &from_sb))
+                               err(1, "%s: stat", *argv);
+                       if (!S_ISREG(to_sb.st_mode))
+                               errx(1, "%s: not a regular file", to_name);
+                       if (to_sb.st_dev == from_sb.st_dev &&
+                           to_sb.st_ino == from_sb.st_ino)
+                               errx(1, "%s and %s are the same file", *argv,
+                                   to_name);
+               }
+               /*
+                * Unlink now... avoid ETXTBSY errors later.  Try and turn
+                * off the append/immutable bits -- if we fail, go ahead,
+                * it might work.
+                */
+#ifndef __minix
+#if ! HAVE_NBTOOL_CONFIG_H
+#define        NOCHANGEBITS    (UF_IMMUTABLE | UF_APPEND | SF_IMMUTABLE | SF_APPEND)
+               if (to_sb.st_flags & NOCHANGEBITS)
+                       (void)chflags(to_name,
+                           to_sb.st_flags & ~(NOCHANGEBITS));
+#endif
+#endif
+               if (dobackup)
+                       backup(to_name);
+               else if (!dorename)
+                       (void)unlink(to_name);
+       }
+       install(*argv, to_name, iflags);
+       exit(0);
+}
+
+/*
+ * parseid --
+ *     parse uid or gid from arg into id, returning non-zero if successful
+ */
+static int
+parseid(char *name, id_t *id)
+{
+       char    *ep;
+
+       errno = 0;
+       *id = (id_t)strtoul(name, &ep, 10);
+       if (errno || *ep != '\0')
+               return (0);
+       return (1);
+}
+
+/*
+ * do_link --
+ *     make a hard link, obeying dorename if set
+ *     return -1 on failure
+ */
+static int
+do_link(char *from_name, char *to_name)
+{
+       char tmpl[MAXPATHLEN];
+       int ret;
+
+       if (dorename) {
+               (void)snprintf(tmpl, sizeof(tmpl), "%s.inst.XXXXXX", to_name);
+               /* This usage is safe. */
+               if (mktemp(tmpl) == NULL)
+                       err(1, "%s: mktemp", tmpl);
+               ret = link(from_name, tmpl);
+               if (ret == 0) {
+                       ret = rename(tmpl, to_name);
+                       /* If rename has posix semantics, then the temporary
+                        * file may still exist when from_name and to_name point
+                        * to the same file, so unlink it unconditionally.
+                        */
+                       (void)unlink(tmpl);
+               }
+               return (ret);
+       } else
+               return (link(from_name, to_name));
+}
+
+/*
+ * do_symlink --
+ *     make a symbolic link, obeying dorename if set
+ *     exit on failure
+ */
+static void
+do_symlink(char *from_name, char *to_name)
+{
+       char tmpl[MAXPATHLEN];
+
+       if (dorename) {
+               (void)snprintf(tmpl, sizeof(tmpl), "%s.inst.XXXXXX", to_name);
+               /* This usage is safe. */
+               if (mktemp(tmpl) == NULL)
+                       err(1, "%s: mktemp", tmpl);
+
+               if (symlink(from_name, tmpl) == -1)
+                       err(1, "symlink %s -> %s", from_name, tmpl);
+               if (rename(tmpl, to_name) == -1) {
+                       /* remove temporary link before exiting */
+                       (void)unlink(tmpl);
+                       err(1, "%s: rename", to_name);
+               }
+       } else {
+               if (symlink(from_name, to_name) == -1)
+                       err(1, "symlink %s -> %s", from_name, to_name);
+       }
+}
+
+/*
+ * makelink --
+ *     make a link from source to destination
+ */
+static void
+makelink(char *from_name, char *to_name)
+{
+       char    src[MAXPATHLEN], dst[MAXPATHLEN], lnk[MAXPATHLEN];
+       struct stat     to_sb;
+
+       /* Try hard links first */
+       if (dolink & (LN_HARD|LN_MIXED)) {
+               if (do_link(from_name, to_name) == -1) {
+                       if ((dolink & LN_HARD) || errno != EXDEV)
+                               err(1, "link %s -> %s", from_name, to_name);
+               } else {
+                       if (stat(to_name, &to_sb))
+                               err(1, "%s: stat", to_name);
+                       if (S_ISREG(to_sb.st_mode)) {
+                                       /* XXX: hard links to anything
+                                        * other than plain files are not
+                                        * metalogged
+                                        */
+                               int omode;
+                               char *oowner, *ogroup, *offlags;
+                               char *dres;
+
+                                       /* XXX: use underlying perms,
+                                        * unless overridden on command line.
+                                        */
+                               omode = mode;
+                               if (!haveopt_m)
+                                       mode = (to_sb.st_mode & 0777);
+                               oowner = owner;
+                               if (!haveopt_o)
+                                       owner = NULL;
+                               ogroup = group;
+                               if (!haveopt_g)
+                                       group = NULL;
+                               offlags = fflags;
+                               if (!haveopt_f)
+                                       fflags = NULL;
+                               switch (digesttype) {
+                               case DIGEST_MD5:
+                                       dres = MD5File(from_name, NULL);
+                                       break;
+                               case DIGEST_RMD160:
+                                       dres = RMD160File(from_name, NULL);
+                                       break;
+                               case DIGEST_SHA1:
+                                       dres = SHA1File(from_name, NULL);
+                                       break;
+                               case DIGEST_SHA256:
+                                       dres = SHA256_File(from_name, NULL);
+                                       break;
+                               case DIGEST_SHA384:
+                                       dres = SHA384_File(from_name, NULL);
+                                       break;
+                               case DIGEST_SHA512:
+                                       dres = SHA512_File(from_name, NULL);
+                                       break;
+                               default:
+                                       dres = NULL;
+                               }
+                               metadata_log(to_name, "file", NULL, NULL,
+                                   dres, to_sb.st_size);
+                               free(dres);
+                               mode = omode;
+                               owner = oowner;
+                               group = ogroup;
+                               fflags = offlags;
+                       }
+                       return;
+               }
+       }
+
+       /* Symbolic links */
+       if (dolink & LN_ABSOLUTE) {
+               /* Convert source path to absolute */
+               if (realpath(from_name, src) == NULL)
+                       err(1, "%s: realpath", from_name);
+               do_symlink(src, to_name);
+                       /* XXX: src may point outside of destdir */
+               metadata_log(to_name, "link", NULL, src, NULL, 0);
+               return;
+       }
+
+       if (dolink & LN_RELATIVE) {
+               char *cp, *d, *s;
+
+               /* Resolve pathnames */
+               if (realpath(from_name, src) == NULL)
+                       err(1, "%s: realpath", from_name);
+
+               /*
+                * The last component of to_name may be a symlink,
+                * so use realpath to resolve only the directory.
+                */
+               cp = xdirname(to_name);
+               if (realpath(cp, dst) == NULL)
+                       err(1, "%s: realpath", cp);
+               /* .. and add the last component */
+               if (strcmp(dst, "/") != 0) {
+                       if (strlcat(dst, "/", sizeof(dst)) > sizeof(dst))
+                               errx(1, "resolved pathname too long");
+               }
+               cp = xbasename(to_name);
+               if (strlcat(dst, cp, sizeof(dst)) > sizeof(dst))
+                       errx(1, "resolved pathname too long");
+
+               /* trim common path components */
+               for (s = src, d = dst; *s == *d; s++, d++)
+                       continue;
+               while (*s != '/')
+                       s--, d--;
+
+               /* count the number of directories we need to backtrack */
+               for (++d, lnk[0] = '\0'; *d; d++)
+                       if (*d == '/')
+                               (void)strlcat(lnk, "../", sizeof(lnk));
+
+               (void)strlcat(lnk, ++s, sizeof(lnk));
+
+               do_symlink(lnk, to_name);
+                       /* XXX: lnk may point outside of destdir */
+               metadata_log(to_name, "link", NULL, lnk, NULL, 0);
+               return;
+       }
+
+       /*
+        * If absolute or relative was not specified, 
+        * try the names the user provided
+        */
+       do_symlink(from_name, to_name);
+               /* XXX: from_name may point outside of destdir */
+       metadata_log(to_name, "link", NULL, from_name, NULL, 0);
+}
+
+/*
+ * install --
+ *     build a path name and install the file
+ */
+static void
+install(char *from_name, char *to_name, u_int flags)
+{
+       struct stat     from_sb;
+       struct stat     to_sb;
+       struct timeval  tv[2];
+       off_t           size;
+       int             devnull, from_fd, to_fd, serrno, tmpmode;
+       char            *p, tmpl[MAXPATHLEN], *oto_name, *digestresult;
+
+       size = -1;
+       if (!dolink) {
+                       /* ensure that from_sb & tv are sane if !dolink */
+               if (stat(from_name, &from_sb))
+                       err(1, "%s: stat", from_name);
+               size = from_sb.st_size;
+#if BSD4_4 && !HAVE_NBTOOL_CONFIG_H
+               TIMESPEC_TO_TIMEVAL(&tv[0], &from_sb.st_atimespec);
+               TIMESPEC_TO_TIMEVAL(&tv[1], &from_sb.st_mtimespec);
+#else
+               tv[0].tv_sec = from_sb.st_atime;
+               tv[0].tv_usec = 0;
+               tv[1].tv_sec = from_sb.st_mtime;
+               tv[1].tv_usec = 0;
+#endif
+       }
+
+       if (flags & DIRECTORY || strcmp(from_name, _PATH_DEVNULL) != 0) {
+               devnull = 0;
+               if (!dolink) {
+                       if (!S_ISREG(from_sb.st_mode))
+                               errx(1, "%s: not a regular file", from_name);
+               }
+               /* Build the target path. */
+               if (flags & DIRECTORY) {
+                       (void)snprintf(pathbuf, sizeof(pathbuf), "%s/%s",
+                           to_name,
+                           (p = strrchr(from_name, '/')) ? ++p : from_name);
+                       to_name = pathbuf;
+               }
+       } else {
+               devnull = 1;
+               size = 0;
+#if HAVE_STRUCT_STAT_ST_FLAGS
+               from_sb.st_flags = 0;   /* XXX */
+#endif
+       }
+
+       /*
+        * Unlink now... avoid ETXTBSY errors later.  Try and turn
+        * off the append/immutable bits -- if we fail, go ahead,
+        * it might work.
+        */
+#ifndef __minix
+#if ! HAVE_NBTOOL_CONFIG_H
+       if (stat(to_name, &to_sb) == 0 &&
+           to_sb.st_flags & (NOCHANGEBITS))
+               (void)chflags(to_name, to_sb.st_flags & ~(NOCHANGEBITS));
+#endif
+#endif
+       if (dorename) {
+               (void)snprintf(tmpl, sizeof(tmpl), "%s.inst.XXXXXX", to_name);
+               oto_name = to_name;
+               to_name = tmpl;
+       } else {
+               oto_name = NULL;        /* pacify gcc */
+               if (dobackup)
+                       backup(to_name);
+               else
+                       (void)unlink(to_name);
+       }
+
+       if (dolink) {
+               makelink(from_name, dorename ? oto_name : to_name);
+               return;
+       }
+
+       /* Create target. */
+       if (dorename) {
+               if ((to_fd = mkstemp(to_name)) == -1)
+                       err(1, "%s: mkstemp", to_name);
+       } else {
+               if ((to_fd = open(to_name,
+                   O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR)) < 0)
+                       err(1, "%s: open", to_name);
+       }
+       digestresult = NULL;
+       if (!devnull) {
+               if ((from_fd = open(from_name, O_RDONLY, 0)) < 0) {
+                       (void)unlink(to_name);
+                       err(1, "%s: open", from_name);
+               }
+               digestresult =
+                   copy(from_fd, from_name, to_fd, to_name, from_sb.st_size);
+               (void)close(from_fd);
+       }
+
+       if (dostrip) {
+               strip(to_name);
+
+               /*
+                * Re-open our fd on the target, in case we used a strip
+                *  that does not work in-place -- like gnu binutils strip.
+                */
+               close(to_fd);
+               if ((to_fd = open(to_name, O_RDONLY, S_IRUSR | S_IWUSR)) < 0)
+                       err(1, "stripping %s", to_name);
+
+               /*
+                * Recalculate size and digestresult after stripping.
+                */
+               if (fstat(to_fd, &to_sb) != 0)
+                       err(1, "%s: fstat", to_name);
+               size = to_sb.st_size;
+               digestresult =
+                   copy(to_fd, to_name, -1, NULL, size);
+
+       }
+
+       if (afterinstallcmd != NULL) {
+               afterinstall(afterinstallcmd, to_name, 1);
+
+               /*
+                * Re-open our fd on the target, in case we used an
+                * after-install command that does not work in-place
+                */
+               close(to_fd);
+               if ((to_fd = open(to_name, O_RDONLY, S_IRUSR | S_IWUSR)) < 0)
+                       err(1, "running after install command on %s", to_name);
+       }
+
+       /*
+        * Set owner, group, mode for target; do the chown first,
+        * chown may lose the setuid bits.
+        */
+       if (!dounpriv &&
+           (flags & (HASUID | HASGID)) && fchown(to_fd, uid, gid) == -1) {
+               serrno = errno;
+               (void)unlink(to_name);
+               errx(1, "%s: chown/chgrp: %s", to_name, strerror(serrno));
+       }
+       tmpmode = mode;
+       if (dounpriv)
+               tmpmode &= S_IRWXU|S_IRWXG|S_IRWXO;
+       if (fchmod(to_fd, tmpmode) == -1) {
+               serrno = errno;
+               (void)unlink(to_name);
+               errx(1, "%s: chmod: %s", to_name, strerror(serrno));
+       }
+
+       /*
+        * Preserve the date of the source file.
+        */
+#ifndef __minix
+       if (dopreserve) {
+#if HAVE_FUTIMES
+               if (futimes(to_fd, tv) == -1)
+                       warn("%s: futimes", to_name);
+#else
+               if (utimes(to_name, tv) == -1)
+                       warn("%s: utimes", to_name);
+#endif
+       }
+#endif
+
+       (void)close(to_fd);
+
+       if (dorename) {
+               if (rename(to_name, oto_name) == -1)
+                       err(1, "%s: rename", to_name);
+               to_name = oto_name;
+       }
+
+       /*
+        * If provided a set of flags, set them, otherwise, preserve the
+        * flags, except for the dump flag.
+        */
+#ifndef __minix
+#if ! HAVE_NBTOOL_CONFIG_H
+       if (!dounpriv && chflags(to_name,
+           flags & SETFLAGS ? fileflags : from_sb.st_flags & ~UF_NODUMP) == -1)
+       {
+               if (errno != EOPNOTSUPP || (from_sb.st_flags & ~UF_NODUMP) != 0)
+                       warn("%s: chflags", to_name);
+       }
+#endif
+#endif
+
+       metadata_log(to_name, "file", tv, NULL, digestresult, size);
+       free(digestresult);
+}
+
+/*
+ * copy --
+ *     copy from one file to another, returning a digest.
+ *
+ *     If to_fd < 0, just calculate a digest, don't copy.
+ */
+static char *
+copy(int from_fd, char *from_name, int to_fd, char *to_name, off_t size)
+{
+       ssize_t nr, nw;
+       int     serrno;
+       u_char  *p;
+       u_char  buf[MAXBSIZE];
+       MD5_CTX         ctxMD5;
+       RMD160_CTX      ctxRMD160;
+       SHA1_CTX        ctxSHA1;
+       SHA256_CTX      ctxSHA256;
+       SHA384_CTX      ctxSHA384;
+       SHA512_CTX      ctxSHA512;
+
+       switch (digesttype) {
+       case DIGEST_MD5:
+               MD5Init(&ctxMD5);
+               break;
+       case DIGEST_RMD160:
+               RMD160Init(&ctxRMD160);
+               break;
+       case DIGEST_SHA1:
+               SHA1Init(&ctxSHA1);
+               break;
+       case DIGEST_SHA256:
+               SHA256_Init(&ctxSHA256);
+               break;
+       case DIGEST_SHA384:
+               SHA384_Init(&ctxSHA384);
+               break;
+       case DIGEST_SHA512:
+               SHA512_Init(&ctxSHA512);
+               break;
+       case DIGEST_NONE:
+               if (to_fd < 0)
+                       return NULL; /* no need to do anything */
+       default:
+               break;
+       }
+       /*
+        * There's no reason to do anything other than close the file
+        * now if it's empty, so let's not bother.
+        */
+       if (size > 0) {
+
+               /*
+                * Mmap and write if less than 8M (the limit is so we
+                * don't totally trash memory on big files).  This is
+                * really a minor hack, but it wins some CPU back.
+                */
+
+               if (size <= 8 * 1048576) {
+#ifdef __minix
+                       goto mmap_failed;
+#else
+                       if ((p = mmap(NULL, (size_t)size, PROT_READ,
+                           MAP_FILE|MAP_SHARED, from_fd, (off_t)0))
+                           == MAP_FAILED) {
+                               goto mmap_failed;
+                       }
+#if defined(MADV_SEQUENTIAL) && !defined(__APPLE__)
+                       if (madvise(p, (size_t)size, MADV_SEQUENTIAL) == -1
+                           && errno != EOPNOTSUPP)
+                               warnx("madvise: %s", strerror(errno));
+#endif
+
+                       if (to_fd >= 0 && write(to_fd, p, size) != size) {
+                               serrno = errno;
+                               (void)unlink(to_name);
+                               errx(1, "%s: write: %s",
+                                   to_name, strerror(serrno));
+                       }
+                       switch (digesttype) {
+                       case DIGEST_MD5:
+                               MD5Update(&ctxMD5, p, size);
+                               break;
+                       case DIGEST_RMD160:
+                               RMD160Update(&ctxRMD160, p, size);
+                               break;
+                       case DIGEST_SHA1:
+                               SHA1Update(&ctxSHA1, p, size);
+                               break;
+                       case DIGEST_SHA256:
+                               SHA256_Update(&ctxSHA256, p, size);
+                               break;
+                       case DIGEST_SHA384:
+                               SHA384_Update(&ctxSHA384, p, size);
+                               break;
+                       case DIGEST_SHA512:
+                               SHA512_Update(&ctxSHA512, p, size);
+                               break;
+                       default:
+                               break;
+                       }
+                       (void)munmap(p, size);
+#endif
+               } else {
+ mmap_failed:
+                       while ((nr = read(from_fd, buf, sizeof(buf))) > 0) {
+                               if (to_fd >= 0 &&
+                                   (nw = write(to_fd, buf, nr)) != nr) {
+                                       serrno = errno;
+                                       (void)unlink(to_name);
+                                       errx(1, "%s: write: %s", to_name,
+                                           strerror(nw > 0 ? EIO : serrno));
+                               }
+                               switch (digesttype) {
+                               case DIGEST_MD5:
+                                       MD5Update(&ctxMD5, buf, nr);
+                                       break;
+                               case DIGEST_RMD160:
+                                       RMD160Update(&ctxRMD160, buf, nr);
+                                       break;
+                               case DIGEST_SHA1:
+                                       SHA1Update(&ctxSHA1, buf, nr);
+                                       break;
+                               case DIGEST_SHA256:
+                                       SHA256_Update(&ctxSHA256, buf, nr);
+                                       break;
+                               case DIGEST_SHA384:
+                                       SHA384_Update(&ctxSHA384, buf, nr);
+                                       break;
+                               case DIGEST_SHA512:
+                                       SHA512_Update(&ctxSHA512, buf, nr);
+                                       break;
+                               default:
+                                       break;
+                               }
+                       }
+                       if (nr != 0) {
+                               serrno = errno;
+                               (void)unlink(to_name);
+                               errx(1, "%s: read: %s", from_name, strerror(serrno));
+                       }
+               }
+       }
+       switch (digesttype) {
+       case DIGEST_MD5:
+               return MD5End(&ctxMD5, NULL);
+       case DIGEST_RMD160:
+               return RMD160End(&ctxRMD160, NULL);
+       case DIGEST_SHA1:
+               return SHA1End(&ctxSHA1, NULL);
+       case DIGEST_SHA256:
+               return SHA256_End(&ctxSHA256, NULL);
+       case DIGEST_SHA384:
+               return SHA384_End(&ctxSHA384, NULL);
+       case DIGEST_SHA512:
+               return SHA512_End(&ctxSHA512, NULL);
+       default:
+               return NULL;
+       }
+}
+
+/*
+ * strip --
+ *     use strip(1) to strip the target file
+ */
+static void
+strip(char *to_name)
+{
+       static const char exec_failure[] = ": exec of strip failed: ";
+       int     serrno, status;
+       const char * volatile stripprog, *progname;
+       char *cmd;
+
+       if ((stripprog = getenv("STRIP")) == NULL || *stripprog == '\0') {
+#ifdef TARGET_STRIP
+               stripprog = TARGET_STRIP;
+#else
+               stripprog = _PATH_STRIP;
+#endif
+       }
+
+       cmd = NULL;
+
+       if (stripArgs) {
+               /*
+                * Build up a command line and let /bin/sh
+                * parse the arguments.
+                */
+               int ret = asprintf(&cmd, "%s %s %s", stripprog, stripArgs,
+                   to_name);
+
+               if (ret == -1 || cmd == NULL)
+                       err(1, "asprintf failed");
+       }
+
+       switch (vfork()) {
+       case -1:
+               serrno = errno;
+               (void)unlink(to_name);
+               errx(1, "vfork: %s", strerror(serrno));
+               /*NOTREACHED*/
+       case 0:
+
+               if (stripArgs)
+                       execl(_PATH_BSHELL, "sh", "-c", cmd, NULL);
+               else
+                       execlp(stripprog, "strip", to_name, NULL);
+
+               progname = getprogname();
+               write(STDERR_FILENO, progname, strlen(progname));
+               write(STDERR_FILENO, exec_failure, strlen(exec_failure));
+               write(STDERR_FILENO, stripprog, strlen(stripprog));
+               write(STDERR_FILENO, "\n", 1);
+               _exit(1);
+               /*NOTREACHED*/
+       default:
+               if (wait(&status) == -1 || status)
+                       (void)unlink(to_name);
+       }
+
+       free(cmd);
+}
+
+/*
+ * afterinstall --
+ *     run provided command on the target file or directory after it's been
+ *     installed and stripped, but before permissions are set or it's renamed
+ */
+static void
+afterinstall(const char *command, const char *to_name, int errunlink)
+{
+       int     serrno, status;
+       char    *cmd;
+
+       switch (vfork()) {
+       case -1:
+               serrno = errno;
+               if (errunlink)
+                       (void)unlink(to_name);
+               errx(1, "vfork: %s", strerror(serrno));
+               /*NOTREACHED*/
+       case 0:
+               /*
+                * build up a command line and let /bin/sh
+                * parse the arguments
+                */
+               cmd = (char*)malloc(sizeof(char)*
+                                         (2+strlen(command)+
+                                            strlen(to_name)));
+
+               if (cmd == NULL)
+                       errx(1, "%s", strerror(ENOMEM));
+
+               sprintf(cmd, "%s %s", command, to_name);
+
+               execl(_PATH_BSHELL, "sh", "-c", cmd, NULL);
+
+               warn("%s: exec of after install command", command);
+               _exit(1);
+               /*NOTREACHED*/
+       default:
+               if ((wait(&status) == -1 || status) && errunlink)
+                       (void)unlink(to_name);
+       }
+}
+
+/*
+ * backup --
+ *     backup file "to_name" to to_name.suffix
+ *     if suffix contains a "%", it's taken as a printf(3) pattern
+ *     used for a numbered backup.
+ */
+static void
+backup(const char *to_name)
+{
+       char    bname[FILENAME_MAX];
+       
+       if (numberedbackup) {
+               /* Do numbered backup */
+               int cnt;
+               char suffix_expanded[FILENAME_MAX];
+               
+               cnt=0;
+               do {
+                       (void)snprintf(suffix_expanded, FILENAME_MAX, suffix,
+                           cnt);
+                       (void)snprintf(bname, FILENAME_MAX, "%s%s", to_name,
+                           suffix_expanded);
+                       cnt++;
+               } while (access(bname, F_OK) == 0); 
+       } else {
+               /* Do simple backup */
+               (void)snprintf(bname, FILENAME_MAX, "%s%s", to_name, suffix);
+       }
+       
+       (void)rename(to_name, bname);
+}
+
+/*
+ * install_dir --
+ *     build directory hierarchy
+ */
+static void
+install_dir(char *path, u_int flags)
+{
+       char            *p;
+       struct stat     sb;
+       int             ch;
+
+       for (p = path;; ++p)
+               if (!*p || (p != path && *p  == '/')) {
+                       ch = *p;
+                       *p = '\0';
+                       if (mkdir(path, 0777) < 0) {
+                               /*
+                                * Can't create; path exists or no perms.
+                                * stat() path to determine what's there now.
+                                */
+                               int sverrno;
+                               sverrno = errno;
+                               if (stat(path, &sb) < 0) {
+                                       /* Not there; use mkdir()s error */
+                                       errno = sverrno;
+                                       err(1, "%s: mkdir", path);
+                               }
+                               if (!S_ISDIR(sb.st_mode)) {
+                                       errx(1,
+                                           "%s exists but is not a directory",
+                                           path);
+                               }
+                       }
+                       if (!(*p = ch))
+                               break;
+               }
+
+       if (afterinstallcmd != NULL)
+               afterinstall(afterinstallcmd, path, 0);
+
+       if (!dounpriv && (
+           ((flags & (HASUID | HASGID)) && chown(path, uid, gid) == -1)
+           || chmod(path, mode) == -1 )) {
+               warn("%s: chown/chmod", path);
+       }
+       metadata_log(path, "dir", NULL, NULL, NULL, 0);
+}
+
+/*
+ * metadata_log --
+ *     if metafp is not NULL, output mtree(8) full path name and settings to
+ *     metafp, to allow permissions to be set correctly by other tools,
+ *     or to allow integrity checks to be performed.
+ */
+static void
+metadata_log(const char *path, const char *type, struct timeval *tv,
+       const char *slink, const char *digestresult, off_t size)
+{
+       static const char       extra[] = { ' ', '\t', '\n', '\\', '#', '\0' };
+       const char      *p;
+       char            *buf;
+       size_t          destlen;
+       struct flock    metalog_lock;
+
+       if (!metafp)    
+               return;
+       buf = (char *)malloc(4 * strlen(path) + 1);     /* buf for strsvis(3) */
+       if (buf == NULL) {
+               warnx("%s", strerror(ENOMEM));
+               return;
+       }
+                                                       /* lock log file */
+       metalog_lock.l_start = 0;
+       metalog_lock.l_len = 0;
+       metalog_lock.l_whence = SEEK_SET;
+       metalog_lock.l_type = F_WRLCK;
+       if (fcntl(fileno(metafp), F_SETLKW, &metalog_lock) == -1) {
+               warn("can't lock %s", metafile);
+               free(buf);
+               return;
+       }
+
+       p = path;                                       /* remove destdir */
+       if (destdir) {
+               destlen = strlen(destdir);
+               if (strncmp(p, destdir, destlen) == 0 &&
+                   (p[destlen] == '/' || p[destlen] == '\0'))
+                       p += destlen;
+       }
+       while (*p && *p == '/')                         /* remove leading /s */
+               p++;
+       strsvis(buf, p, VIS_CSTYLE, extra);             /* encode name */
+       p = buf;
+                                                       /* print details */
+       fprintf(metafp, ".%s%s type=%s", *p ? "/" : "", p, type);
+       if (owner)
+               fprintf(metafp, " uname=%s", owner);
+       if (group)
+               fprintf(metafp, " gname=%s", group);
+       fprintf(metafp, " mode=%#o", mode);
+       if (slink) {
+               strsvis(buf, slink, VIS_CSTYLE, extra); /* encode link */
+               fprintf(metafp, " link=%s", buf);
+       }
+       if (*type == 'f') /* type=file */
+               fprintf(metafp, " size=%lld", (long long)size);
+       if (tv != NULL && dopreserve)
+               fprintf(metafp, " time=%lld.%ld",
+                       (long long)tv[1].tv_sec, (long)tv[1].tv_usec);
+       if (digestresult && digest)
+               fprintf(metafp, " %s=%s", digest, digestresult);
+       if (fflags)
+               fprintf(metafp, " flags=%s", fflags);
+       if (tags)
+               fprintf(metafp, " tags=%s", tags);
+       fputc('\n', metafp);
+       fflush(metafp);                                 /* flush output */
+                                                       /* unlock log file */
+       metalog_lock.l_type = F_UNLCK;
+       if (fcntl(fileno(metafp), F_SETLKW, &metalog_lock) == -1) {
+               warn("can't unlock %s", metafile);
+       }
+       free(buf);
+}
+
+/*
+ * xbasename --
+ *     libc basename(3) that returns a pointer to a static buffer
+ *     instead of overwriting that passed-in string.
+ */
+static char *
+xbasename(char *path)
+{
+       static char tmp[MAXPATHLEN];
+
+       (void)strlcpy(tmp, path, sizeof(tmp));
+       return (basename(tmp));
+}
+
+/*
+ * xdirname --
+ *     libc dirname(3) that returns a pointer to a static buffer
+ *     instead of overwriting that passed-in string.
+ */
+static char *
+xdirname(char *path)
+{
+       static char tmp[MAXPATHLEN];
+
+       (void)strlcpy(tmp, path, sizeof(tmp));
+       return (dirname(tmp));
+}
+
+/*
+ * usage --
+ *     print a usage message and die
+ */
+static void
+usage(void)
+{
+       const char *prog;
+
+       prog = getprogname();
+
+       (void)fprintf(stderr,
+"usage: %s [-Ubcprs] [-M log] [-D dest] [-T tags] [-B suffix]\n"
+"           [-a aftercmd] [-f flags] [-m mode] [-N dbdir] [-o owner] [-g group] \n"
+"           [-l linkflags] [-h hash] [-S stripflags] file1 file2\n"
+"       %s [-Ubcprs] [-M log] [-D dest] [-T tags] [-B suffix]\n"
+"           [-a aftercmd] [-f flags] [-m mode] [-N dbdir] [-o owner] [-g group]\n"
+"           [-l linkflags] [-h hash] [-S stripflags] file1 ... fileN directory\n"
+"       %s -d [-Up] [-M log] [-D dest] [-T tags] [-a aftercmd] [-m mode]\n"
+"           [-N dbdir] [-o owner] [-g group] directory ...\n",
+           prog, prog, prog);
+       exit(1);
+}