Reorganize sources before implementing --repackage.
authorjbj <devnull@localhost>
Sat, 10 Feb 2001 16:47:40 +0000 (16:47 +0000)
committerjbj <devnull@localhost>
Sat, 10 Feb 2001 16:47:40 +0000 (16:47 +0000)
CVS patchset: 4543
CVS date: 2001/02/10 16:47:40

21 files changed:
Doxyfile.in
build.c
build/buildio.h
lib/Makefile.am
lib/cpio.c
lib/fsm.c [new file with mode: 0644]
lib/fsm.h [new file with mode: 0644]
lib/psm.c [moved from lib/install.c with 70% similarity]
lib/psm.h [new file with mode: 0644]
lib/rollback.c [deleted file]
lib/rollback.h [deleted file]
lib/rpmlib.h
lib/scriptlet.c [new file with mode: 0644]
lib/scriptlet.h [moved from lib/install.h with 59% similarity]
lib/transaction.c
lib/uninstall.c [deleted file]
lib/verify.c
po/POTFILES.in
po/rpm.pot
rpm.c
rpmqv.c

index c9ec29b..e4ee04b 100644 (file)
@@ -286,12 +286,12 @@ INPUT                  = \
        ./lib/fprint.c \
        ./lib/fprint.h \
        ./lib/fs.c \
+       ./lib/fsm.c \
+       ./lib/fsm.h \
        ./lib/hash.c \
        ./lib/hash.h \
        ./lib/header.c \
        ./lib/header.h \
-       ./lib/install.c \
-       ./lib/install.h \
        ./lib/md5.c \
        ./lib/md5.h \
        ./lib/md5sum.c \
@@ -301,8 +301,9 @@ INPUT                  = \
        ./lib/poptBT.c \
        ./lib/poptQV.c \
        ./lib/problems.c \
+       ./lib/psm.c \
+       ./lib/psm.h \
        ./lib/query.c \
-       ./lib/rollback.c \
        ./lib/rpmchecksig.c \
        ./lib/rpmdb.c \
        ./lib/rpmdb.h \
@@ -320,7 +321,6 @@ INPUT                  = \
        ./lib/tagName.c \
        ./lib/tagtable.c \
        ./lib/transaction.c \
-       ./lib/uninstall.c \
        ./lib/verify.c \
        ./rpmio/base64.c \
        ./rpmio/base64.h \
diff --git a/build.c b/build.c
index 573e9f4..b8f73dd 100644 (file)
--- a/build.c
+++ b/build.c
@@ -4,7 +4,6 @@
 #include <rpmurl.h>
 
 #include "build.h"
-#include "install.h"
 #include "debug.h"
 
 static int checkSpec(Header h)
index 887e62d..dd80378 100644 (file)
@@ -6,7 +6,7 @@
  *  XXX this information will move elsewhere eventually
  */
 
-#include "rollback.h"
+#include "psm.h"
 
 /**
  */
index 0d784ae..e3ab735 100644 (file)
@@ -11,8 +11,8 @@ pkgincdir = $(pkgincludedir)
 pkginc_HEADERS = \
        header.h misc.h rpmlib.h stringbuf.h
 noinst_HEADERS = \
-       cpio.h depends.h falloc.h fprint.h hash.h install.h \
-       md5.h rollback.h \
+       cpio.h depends.h falloc.h fprint.h fsm.h hash.h \
+       md5.h psm.h \
        rpmdb.h rpmlead.h signature.h
 
 mylibpaths =   -L$(top_builddir)/lib/.libs -L$(top_builddir)/rpmio/.libs \
@@ -23,12 +23,12 @@ LIBS =
 lib_LTLIBRARIES = librpm.la
 librpm_la_SOURCES = \
        cpio.c $(DBLIBSRCS) depends.c \
-       formats.c fprint.c fs.c hash.c header.c install.c \
+       formats.c fprint.c fs.c fsm.c hash.c header.c \
        md5.c md5sum.c misc.c package.c problems.c \
-       poptBT.c poptQV.c query.c rollback.c \
+       poptBT.c poptQV.c psm.c query.c \
        rpmchecksig.c rpmdb.c rpminstall.c \
        rpmlead.c rpmlibprov.c rpmrc.c scriptlet.c signature.c stringbuf.c \
-       tagName.c tagtable.c transaction.c uninstall.c verify.c
+       tagName.c tagtable.c transaction.c verify.c
 librpm_la_LDFLAGS = @libdb3@ @libdb2@ @libdb1@
 librpm_la_LIBADD = $(DBLIBOBJS)
 librpm_la_DEPENDENCIES = $(DBLIBOBJS)
index fead7f6..f633e66 100644 (file)
@@ -1,4 +1,4 @@
-/** \ingroup payload rpmio
+/** \ingroup payload
  * \file lib/cpio.c
  *  Handle cpio payloads within rpm packages.
  *
 #include "system.h"
 #include <rpmlib.h>
 
-#include "rollback.h"
+#include "fsm.h"
 #include "rpmerr.h"
 #include "debug.h"
 
-/*@access FD_t @*/
-/*@access rpmTransactionSet @*/
-/*@access TFI_t @*/
 /*@access FSM_t @*/
 
-#define        alloca_strdup(_s)       strcpy(alloca(strlen(_s)+1), (_s))
+extern int _fsm_debug;
 
+/**
+ * Wrapper to free(3), hides const compilation noise, permit NULL, return NULL.
+ * @param this         memory to free
+ * @retval             NULL always
+ */
 static /*@null@*/ void * _free(/*@only@*/ /*@null@*/ const void * this) {
     if (this)  free((void *)this);
     return NULL;
 }
 
-int _fsm_debug = 0;
-
-/** \ingroup payload
- * Keeps track of the set of all hard links to a file in an archive.
- */
-struct hardLink {
-/*@owned@*/ struct hardLink * next;
-/*@owned@*/ const char ** nsuffix;
-/*@owned@*/ int * filex;
-    dev_t dev;
-    ino_t inode;
-    int nlink;
-    int linksLeft;
-    int linkIndex;
-    int createdPath;
-};
-
-/**
- */
-typedef struct fsmIterator_s {
-/*@kept@*/ rpmTransactionSet ts;       /*!< transaction set. */
-/*@kept@*/ TFI_t fi;                   /*!< transaction element file info. */
-    int isave;                         /*!< last returned iterator index. */
-    int i;                             /*!< iterator index. */
-} * FSMI_t;
-
-/** \ingroup payload
- * File name and stat information.
- */
-struct fsm_s {
-/*@owned@*/ const char * path;         /*!< Current file name. */
-/*@owned@*/ const char * opath;                /*!< Original file name. */
-    FD_t cfd;                          /*!< Payload file handle. */
-    FD_t rfd;                          /*!<  read: File handle. */
-/*@dependent@*/ char * rdbuf;          /*!<  read: Buffer. */
-/*@owned@*/ char * rdb;                        /*!<  read: Buffer allocated. */
-    size_t rdsize;                     /*!<  read: Buffer allocated size. */
-    size_t rdlen;                      /*!<  read: Number of bytes requested. */
-    size_t rdnb;                       /*!<  read: Number of bytes returned. */
-    FD_t wfd;                          /*!< write: File handle. */
-/*@dependent@*/ char * wrbuf;          /*!< write: Buffer. */
-/*@owned@*/ char * wrb;                        /*!< write: Buffer allocated. */
-    size_t wrsize;                     /*!< write: Buffer allocated size. */
-    size_t wrlen;                      /*!< write: Number of bytes requested. */
-    size_t wrnb;                       /*!< write: Number of bytes returned. */
-/*@only@*/ FSMI_t iter;                        /*!< File iterator. */
-    int ix;                            /*!< Current file iterator index. */
-/*@only@*/ struct hardLink * links;    /*!< Pending hard linked file(s). */
-/*@only@*/ struct hardLink * li;       /*!< Current hard linked file(s). */
-/*@kept@*/ unsigned int * archiveSize; /*!< Pointer to archive size. */
-/*@kept@*/ const char ** failedFile;   /*!< First file name that failed. */
-/*@shared@*/ const char * subdir;      /*!< Current file sub-directory. */
-    char subbuf[64];   /* XXX eliminate */
-/*@observer@*/ const char * osuffix;   /*!< Old, preserved, file suffix. */
-/*@observer@*/ const char * nsuffix;   /*!< New, created, file suffix. */
-/*@shared@*/ const char * suffix;      /*!< Current file suffix. */
-    char sufbuf[64];   /* XXX eliminate */
-/*@only@*/ short * dnlx;               /*!< Last dirpath verified indexes. */
-/*@only@*/ char * ldn;                 /*!< Last dirpath verified. */
-    int ldnlen;                                /*!< Last dirpath current length. */
-    int ldnalloc;                      /*!< Last dirpath allocated length. */
-    int postpone;                      /*!< Skip remaining stages? */
-    int diskchecked;                   /*!< Has stat(2) been performed? */
-    int exists;                                /*!< Does current file exist on disk? */
-    int mkdirsdone;                    /*!< Have "orphan" dirs been created? */
-    int astriplen;                     /*!< Length of buildroot prefix. */
-    int rc;                            /*!< External file stage return code. */
-    int commit;                                /*!< Commit synchronously? */
-    cpioMapFlags mapFlags;             /*!< Bit(s) to control mapping. */
-/*@shared@*/ const char * archivePath; /*!< Path to store in cpio archive. */
-/*@shared@*/ const char * dirName;     /*!< File directory name. */
-/*@shared@*/ const char * baseName;    /*!< File base name. */
-/*@shared@*/ const char * fmd5sum;     /*!< File MD5 sum (NULL disables). */
-    unsigned fflags;                   /*!< File flags. */
-    fileAction action;                 /*!< File disposition. */
-    fileStage goal;                    /*!< Package state machine goal. */
-    fileStage stage;                   /*!< External file stage. */
-    struct stat sb;                    /*!< Current file stat(2) info. */
-    struct stat osb;                   /*!< Original file stat(2) info. */
-};
-
-rpmTransactionSet fsmGetTs(const FSM_t fsm) {
-    const FSMI_t iter = fsm->iter;
-    return (iter ? iter->ts : NULL);
-}
-
-TFI_t fsmGetFi(const FSM_t fsm) {
-    const FSMI_t iter = fsm->iter;
-    return (iter ? iter->fi : NULL);
-}
-
-#define        SUFFIX_RPMORIG  ".rpmorig"
-#define        SUFFIX_RPMSAVE  ".rpmsave"
-#define        SUFFIX_RPMNEW   ".rpmnew"
-
-/**
- */
-static /*@only@*//*@null@*/ const char * fsmFsPath(/*@null@*/ const FSM_t fsm,
-       /*@null@*/ const struct stat * st,
-       /*@null@*/ const char * subdir,
-       /*@null@*/ const char * suffix)
-{
-    const char * s = NULL;
-
-    if (fsm) {
-       int nb;
-       char * t;
-       nb = strlen(fsm->dirName) +
-           (st && subdir && !S_ISDIR(st->st_mode) ? strlen(subdir) : 0) +
-           (st && suffix && !S_ISDIR(st->st_mode) ? strlen(suffix) : 0) +
-           strlen(fsm->baseName) + 1;
-       s = t = xmalloc(nb);
-       t = stpcpy(t, fsm->dirName);
-       if (st && subdir && !S_ISDIR(st->st_mode))
-           t = stpcpy(t, subdir);
-       t = stpcpy(t, fsm->baseName);
-       if (st && suffix && !S_ISDIR(st->st_mode))
-           t = stpcpy(t, suffix);
-    }
-    return s;
-}
-
-/**
- */
-static /*@null@*/ void * mapFreeIterator(/*@only@*//*@null@*/const void * this) {
-    return _free((void *)this);
-}
-
-/**
- */
-static void *
-mapInitIterator(/*@kept@*/ const void * this, /*@kept@*/ const void * that)
-{
-    rpmTransactionSet ts = (void *)this;
-    TFI_t fi = (void *)that;
-    FSMI_t iter = NULL;
-
-    iter = xcalloc(1, sizeof(*iter));
-    iter->ts = ts;
-    iter->fi = fi;
-    switch (fi->type) {
-    case TR_ADDED:     iter->i = 0;            break;
-    case TR_REMOVED:   iter->i = fi->fc - 1;   break;
-    }
-    iter->isave = iter->i;
-    return iter;
-}
-
-/**
- */
-static int mapNextIterator(void * this) {
-    FSMI_t iter = this;
-    const TFI_t fi = iter->fi;
-    int i = -1;
-
-    switch (fi->type) {
-    case TR_ADDED:     if (iter->i < fi->fc)   i = iter->i++;  break;
-    case TR_REMOVED:   if (iter->i >= 0)       i = iter->i--;  break;
-    }
-    iter->isave = i;
-    return i;
-}
-
-/**
- */
-typedef struct dnli_s {
-/*@dependent@*/ TFI_t fi;
-/*@only@*/ /*@null@*/ char * active;
-    int reverse;
-    int isave;
-    int i;
-} * DNLI_t;
-
-/**
- */
-static /*@null@*/ void * dnlFreeIterator(/*@only@*//*@null@*/ const void * this)
-{
-    if (this) {
-       DNLI_t dnli = (void *)this;
-       if (dnli->active) free(dnli->active);
-    }
-    return _free(this);
-}
-
-/**
- */
-static inline int dnlCount(const DNLI_t dnli) {
-    return (dnli ? dnli->fi->dc : 0);
-}
-
-/**
- */
-static inline int dnlIndex(const DNLI_t dnli) {
-    return (dnli ? dnli->isave : -1);
-}
-
-/**
- */
-static /*@only@*/ void * dnlInitIterator(const FSM_t fsm, int reverse)
-{
-    TFI_t fi = fsmGetFi(fsm);
-    DNLI_t dnli;
-    int i, j;
-
-    if (fi == NULL)
-       return NULL;
-    dnli = xcalloc(1, sizeof(*dnli));
-    dnli->fi = fi;
-    dnli->reverse = reverse;
-    dnli->i = (reverse ? fi->dc : 0);
-
-    if (fi->dc) {
-       dnli->active = xcalloc(fi->dc, sizeof(*dnli->active));
-
-       /* Identify parent directories not skipped. */
-       for (i = 0; i < fi->fc; i++)
-            if (!XFA_SKIPPING(fi->actions[i])) dnli->active[fi->dil[i]] = 1;
-
-       /* Exclude parent directories that are explicitly included. */
-       for (i = 0; i < fi->fc; i++) {
-           int dil, dnlen, bnlen;
-
-           if (!S_ISDIR(fi->fmodes[i]))
-               continue;
-
-           dil = fi->dil[i];
-           dnlen = strlen(fi->dnl[dil]);
-           bnlen = strlen(fi->bnl[i]);
-
-           for (j = 0; j < fi->dc; j++) {
-               const char * dnl;
-               int jlen;
-
-               if (!dnli->active[j] || j == dil) continue;
-               dnl = fi->dnl[j];
-               jlen = strlen(dnl);
-               if (jlen != (dnlen+bnlen+1)) continue;
-               if (strncmp(dnl, fi->dnl[dil], dnlen)) continue;
-               if (strncmp(dnl+dnlen, fi->bnl[i], bnlen)) continue;
-               if (dnl[dnlen+bnlen] != '/' || dnl[dnlen+bnlen+1] != '\0')
-                   continue;
-               /* This directory is included in the package. */
-               dnli->active[j] = 0;
-               break;
-           }
-       }
-
-       /* Print only once per package. */
-       if (!reverse) {
-           j = 0;
-           for (i = 0; i < fi->dc; i++) {
-               if (!dnli->active[i]) continue;
-               if (j == 0) {
-                   j = 1;
-                   rpmMessage(RPMMESS_DEBUG,
-       _("========= Directories not explictly included in package:\n"));
-               }
-               rpmMessage(RPMMESS_DEBUG, _("%9d %s\n"), i, fi->dnl[i]);
-           }
-           if (j)
-               rpmMessage(RPMMESS_DEBUG, "=========\n");
-       }
-    }
-    return dnli;
-}
-
-/**
- */
-static const char * dnlNextIterator(/*@null@*/ DNLI_t dnli) {
-    const char * dn = NULL;
-
-    if (dnli && dnli->active) {
-       TFI_t fi = dnli->fi;
-       int i = -1;
-
-       do {
-           i = (!dnli->reverse ? dnli->i++ : --dnli->i);
-       } while (i >= 0 && i < fi->dc && !dnli->active[i]);
-
-       if (i >= 0 && i < fi->dc)
-           dn = fi->dnl[i];
-       else
-           i = -1;
-       dnli->isave = i;
-    }
-    return dn;
-}
-
-/**
- */
-static int cpioStrCmp(const void * a, const void * b) {
-    const char * afn = *(const char **)a;
-    const char * bfn = *(const char **)b;
-
-    /* Match rpm-4.0 payloads with ./ prefixes. */
-    if (afn[0] == '.' && afn[1] == '/')        afn += 2;
-    if (bfn[0] == '.' && bfn[1] == '/')        bfn += 2;
-
-    /* If either path is absolute, make it relative. */
-    if (afn[0] == '/') afn += 1;
-    if (bfn[0] == '/') bfn += 1;
-
-    return strcmp(afn, bfn);
-}
-
-/**
- */
-static int mapFind(void * this, const char * fsmPath) {
-    FSMI_t iter = this;
-    const TFI_t fi = iter->fi;
-    int ix = -1;
-
-    if (fi) {
-       const char ** p;
-
-       p = bsearch(&fsmPath, fi->apath, fi->fc, sizeof(fsmPath), cpioStrCmp);
-       if (p == NULL) {
-           fprintf(stderr, "*** not mapped %s\n", fsmPath);
-       } else {
-           iter->i = p - fi->apath;
-           ix = mapNextIterator(iter);
-       }
-    }
-    return ix;
-}
-
-/**
- * Save hard link in chain.
- * @return             Is chain only partially filled?
- */
-static int saveHardLink(FSM_t fsm)
-{
-    struct stat * st = &fsm->sb;
-    int rc = 0;
-    int ix = -1;
-    int j;
-
-    /* Find hard link set. */
-    for (fsm->li = fsm->links; fsm->li; fsm->li = fsm->li->next) {
-       if (fsm->li->inode == st->st_ino && fsm->li->dev == st->st_dev)
-           break;
-    }
-
-    /* New hard link encountered, add new link to set. */
-    if (fsm->li == NULL) {
-       fsm->li = xcalloc(1, sizeof(*fsm->li));
-       fsm->li->next = NULL;
-       fsm->li->nlink = st->st_nlink;
-       fsm->li->dev = st->st_dev;
-       fsm->li->inode = st->st_ino;
-       fsm->li->linkIndex = -1;
-       fsm->li->createdPath = -1;
-
-       fsm->li->filex = xcalloc(st->st_nlink, sizeof(fsm->li->filex[0]));
-       memset(fsm->li->filex, -1, (st->st_nlink * sizeof(fsm->li->filex[0])));
-       fsm->li->nsuffix = xcalloc(st->st_nlink, sizeof(*fsm->li->nsuffix));
-
-       if (fsm->goal == FSM_PKGBUILD)
-           fsm->li->linksLeft = st->st_nlink;
-       if (fsm->goal == FSM_PKGINSTALL)
-           fsm->li->linksLeft = 0;
-
-       fsm->li->next = fsm->links;
-       fsm->links = fsm->li;
-    }
-
-    if (fsm->goal == FSM_PKGBUILD) --fsm->li->linksLeft;
-    fsm->li->filex[fsm->li->linksLeft] = fsm->ix;
-    /*@-observertrans@*/
-    fsm->li->nsuffix[fsm->li->linksLeft] = fsm->nsuffix;
-    /*@=observertrans@*/
-    if (fsm->goal == FSM_PKGINSTALL) fsm->li->linksLeft++;
-
-#if 0
-fprintf(stderr, "*** %p link[%d:%d] %d filex %d %s\n", fsm->li, fsm->li->linksLeft, st->st_nlink, (int)st->st_size, fsm->li->filex[fsm->li->linksLeft], fsm->li->files[fsm->li->linksLeft]);
-#endif
-
-    if (fsm->goal == FSM_PKGBUILD)
-       return (fsm->li->linksLeft > 0);
-
-    if (fsm->goal != FSM_PKGINSTALL)
-       return 0;
-
-    if (!(st->st_size || fsm->li->linksLeft == st->st_nlink))
-       return 1;
-
-    /* Here come the bits, time to choose a non-skipped file name. */
-    {  TFI_t fi = fsmGetFi(fsm);
-
-       for (j = fsm->li->linksLeft - 1; j >= 0; j--) {
-           ix = fsm->li->filex[j];
-           if (ix < 0 || XFA_SKIPPING(fi->actions[ix]))
-               continue;
-           break;
-       }
-    }
-
-    /* Are all links skipped or not encountered yet? */
-    if (ix < 0 || j < 0)
-       return 1;       /* XXX W2DO? */
-
-    /* Save the non-skipped file name and map index. */
-    fsm->li->linkIndex = j;
-    fsm->path = _free(fsm->path);
-    fsm->ix = ix;
-    rc = fsmStage(fsm, FSM_MAP);
-    return rc;
-}
-
-/**
- * Destroy set of hard links.
- * @param li           set of hard links
- */
-static /*@null@*/ void * freeHardLink(/*@only@*/ /*@null@*/ struct hardLink * li)
-{
-    if (li) {
-       li->nsuffix = _free(li->nsuffix);       /* XXX elements are shared */
-       li->filex = _free(li->filex);
-    }
-    return _free(li);
-}
-
-FSM_t newFSM(void) {
-    FSM_t fsm = xcalloc(1, sizeof(*fsm));
-    return fsm;
-}
-
-FSM_t freeFSM(FSM_t fsm)
-{
-    if (fsm) {
-       if (fsm->path)  free((void *)fsm->path);
-       while ((fsm->li = fsm->links) != NULL) {
-           fsm->links = fsm->li->next;
-           fsm->li->next = NULL;
-           fsm->li = freeHardLink(fsm->li);
-       }
-       fsm->dnlx = _free(fsm->dnlx);
-       fsm->ldn = _free(fsm->ldn);
-       fsm->iter = mapFreeIterator(fsm->iter);
-    }
-    return _free(fsm);
-}
-
 /**
  * Convert string to unsigned integer (with buffer size check).
  * @param              input string
@@ -611,7 +170,7 @@ int cpioHeaderRead(FSM_t fsm, struct stat * st)
        if (!rc && fsm->rdnb != fsm->wrlen)
            rc = CPIOERR_BAD_HEADER;
        if (rc) {
-           free(t);
+           t = _free(t);
            fsm->path = NULL;
            return rc;
        }
@@ -623,1442 +182,6 @@ int cpioHeaderRead(FSM_t fsm, struct stat * st)
     return 0;
 }
 
-int fsmSetup(FSM_t fsm, fileStage goal,
-               const rpmTransactionSet ts, const TFI_t fi, FD_t cfd,
-               unsigned int * archiveSize, const char ** failedFile)
-{
-    size_t pos = 0;
-    int rc;
-
-    fsm->goal = goal;
-    if (cfd) {
-       fsm->cfd = fdLink(cfd, "persist (fsm)");
-       pos = fdGetCpioPos(fsm->cfd);
-       fdSetCpioPos(fsm->cfd, 0);
-    }
-    fsm->iter = mapInitIterator(ts, fi);
-
-    if (fsm->goal == FSM_PKGINSTALL) {
-       if (ts && ts->notify) {
-           (void)ts->notify(fi->h, RPMCALLBACK_INST_START, 0, fi->archiveSize,
-               (fi->ap ? fi->ap->key : NULL), ts->notifyData);
-       }
-    }
-
-    fsm->archiveSize = archiveSize;
-    if (fsm->archiveSize)
-       *fsm->archiveSize = 0;
-    fsm->failedFile = failedFile;
-    if (fsm->failedFile)
-       *fsm->failedFile = NULL;
-
-    memset(fsm->sufbuf, 0, sizeof(fsm->sufbuf));
-    if (fsm->goal == FSM_PKGINSTALL) {
-       if (ts->id > 0)
-           sprintf(fsm->sufbuf, ";%08x", (unsigned)ts->id);
-    }
-
-    rc = fsm->rc = 0;
-    rc = fsmStage(fsm, FSM_CREATE);
-
-    rc = fsmStage(fsm, fsm->goal);
-
-    if (!rc && fsm->archiveSize)
-       *fsm->archiveSize = (fdGetCpioPos(fsm->cfd) - pos);
-
-   return rc;
-}
-
-int fsmTeardown(FSM_t fsm) {
-    int rc = fsm->rc;
-
-    if (!rc)
-       rc = fsmStage(fsm, FSM_DESTROY);
-
-    fsm->iter = mapFreeIterator(fsm->iter);
-    if (fsm->cfd) {
-       fsm->cfd = fdFree(fsm->cfd, "persist (fsm)");
-       fsm->cfd = NULL;
-    }
-    fsm->failedFile = NULL;
-    return rc;
-}
-
-int fsmMapPath(FSM_t fsm)
-{
-    TFI_t fi = fsmGetFi(fsm);  /* XXX const except for fstates */
-    int rc = 0;
-    int i;
-
-    fsm->osuffix = NULL;
-    fsm->nsuffix = NULL;
-    fsm->astriplen = 0;
-    fsm->action = FA_UNKNOWN;
-    fsm->mapFlags = 0;
-
-    i = fsm->ix;
-    if (fi && i >= 0 && i < fi->fc) {
-
-       fsm->astriplen = fi->astriplen;
-       fsm->action = (fi->actions ? fi->actions[i] : fi->action);
-       fsm->fflags = (fi->fflags ? fi->fflags[i] : fi->flags);
-       fsm->mapFlags = (fi->fmapflags ? fi->fmapflags[i] : fi->mapflags);
-
-       /* src rpms have simple base name in payload. */
-       fsm->archivePath =
-               (fi->apath ? fi->apath[i] + fi->striplen : fi->bnl[i]);
-       fsm->dirName = fi->dnl[fi->dil[i]];
-       fsm->baseName = fi->bnl[i];
-
-       switch (fsm->action) {
-       case FA_SKIP:
-           break;
-       case FA_SKIPMULTILIB:   /* XXX RPMFILE_STATE_MULTILIB? */
-fprintf(stderr, "*** %s:%s %s\n", fiTypeString(fi), fileActionString(fsm->action), (fsm->path ? fsm->path : ""));
-           break;
-       case FA_UNKNOWN:
-fprintf(stderr, "*** %s:%s %s\n", fiTypeString(fi), fileActionString(fsm->action), (fsm->path ? fsm->path : ""));
-           break;
-
-       case FA_CREATE:
-           assert(fi->type == TR_ADDED);
-           break;
-
-       case FA_SKIPNSTATE:
-fprintf(stderr, "*** %s:%s %s\n", fiTypeString(fi), fileActionString(fsm->action), (fsm->path ? fsm->path : ""));
-           if (fi->type == TR_ADDED)
-               fi->fstates[i] = RPMFILE_STATE_NOTINSTALLED;
-           break;
-
-       case FA_SKIPNETSHARED:
-           if (fi->type == TR_ADDED)
-               fi->fstates[i] = RPMFILE_STATE_NETSHARED;
-           break;
-
-       case FA_BACKUP:
-fprintf(stderr, "*** %s:%s %s\n", fiTypeString(fi), fileActionString(fsm->action), (fsm->path ? fsm->path : ""));
-           switch (fi->type) {
-           case TR_ADDED:
-               fsm->osuffix = SUFFIX_RPMORIG;
-               break;
-           case TR_REMOVED:
-               fsm->osuffix = SUFFIX_RPMSAVE;
-               break;
-           }
-           break;
-
-       case FA_ALTNAME:
-           assert(fi->type == TR_ADDED);
-           fsm->nsuffix = SUFFIX_RPMNEW;
-           break;
-
-       case FA_SAVE:
-fprintf(stderr, "*** %s:%s %s\n", fiTypeString(fi), fileActionString(fsm->action), (fsm->path ? fsm->path : ""));
-           assert(fi->type == TR_ADDED);
-           fsm->osuffix = SUFFIX_RPMSAVE;
-           break;
-       case FA_ERASE:
-           assert(fi->type == TR_REMOVED);
-           break;
-       default:
-fprintf(stderr, "*** %s:%s %s\n", fiTypeString(fi), fileActionString(fsm->action), (fsm->path ? fsm->path : ""));
-           break;
-       }
-
-       if ((fsm->mapFlags & CPIO_MAP_PATH) || fsm->nsuffix) {
-           const struct stat * st = &fsm->sb;
-           fsm->path = _free(fsm->path);
-           fsm->path = fsmFsPath(fsm, st, fsm->subdir,
-               (fsm->suffix ? fsm->suffix : fsm->nsuffix));
-       }
-    }
-    return rc;
-}
-
-int fsmMapAttrs(FSM_t fsm)
-{
-    struct stat * st = &fsm->sb;
-    TFI_t fi = fsmGetFi(fsm);
-    int i = fsm->ix;
-
-    if (fi && i >= 0 && i < fi->fc) {
-       mode_t perms =
-               (S_ISDIR(st->st_mode) ? fi->dperms : fi->fperms);
-       mode_t finalMode =
-               (fi->fmodes ? fi->fmodes[i] : perms);
-       uid_t finalUid =
-               (fi->fuids ? fi->fuids[i] : fi->uid); /* XXX chmod u-s */
-       gid_t finalGid =
-               (fi->fgids ? fi->fgids[i] : fi->gid); /* XXX chmod g-s */
-
-       if (fsm->mapFlags & CPIO_MAP_MODE)
-           st->st_mode = (st->st_mode & S_IFMT) | finalMode;
-       if (fsm->mapFlags & CPIO_MAP_UID)
-           st->st_uid = finalUid;
-       if (fsm->mapFlags & CPIO_MAP_GID)
-           st->st_gid = finalGid;
-
-       fsm->fmd5sum = (fi->fmd5s ? fi->fmd5s[i] : NULL);
-
-    }
-    return 0;
-}
-
-/**
- * Create file from payload stream.
- * @todo Legacy: support brokenEndian MD5 checks?
- * @param fsm          file path and stat info
- * @return             0 on success
- */
-static int expandRegular(FSM_t fsm)
-               /*@modifies fileSystem, fsm @*/
-{
-    const char * fmd5sum;
-    const struct stat * st = &fsm->sb;
-    int left = st->st_size;
-    int rc = 0;
-
-    rc = fsmStage(fsm, FSM_WOPEN);
-    if (rc)
-       goto exit;
-
-    /* XXX md5sum's will break on repackaging that includes modified files. */
-    fmd5sum = fsm->fmd5sum;
-
-    /* XXX This doesn't support brokenEndian checks. */
-    if (st->st_size > 0 && fmd5sum)
-       fdInitMD5(fsm->wfd, 0);
-
-    while (left) {
-
-       fsm->wrlen = (left > fsm->wrsize ? fsm->wrsize : left);
-       rc = fsmStage(fsm, FSM_DREAD);
-       if (rc)
-           goto exit;
-
-       rc = fsmStage(fsm, FSM_WRITE);
-       if (rc)
-           goto exit;
-
-       left -= fsm->wrnb;
-
-       /* don't call this with fileSize == fileComplete */
-       if (!rc && left)
-           (void) fsmStage(fsm, FSM_NOTIFY);
-    }
-
-    if (st->st_size > 0 && fmd5sum) {
-       const char * md5sum = NULL;
-
-       Fflush(fsm->wfd);
-       fdFiniMD5(fsm->wfd, (void **)&md5sum, NULL, 1);
-
-       if (md5sum == NULL) {
-           rc = CPIOERR_MD5SUM_MISMATCH;
-       } else {
-           if (strcmp(md5sum, fmd5sum))
-               rc = CPIOERR_MD5SUM_MISMATCH;
-           md5sum = _free(md5sum);
-       }
-    }
-
-exit:
-    (void) fsmStage(fsm, FSM_WCLOSE);
-    return rc;
-}
-
-/**
- * Write next item to payload stream.
- * @retval sizep       address of no. bytes written
- * @param writeData    should data be written?
- * @return             0 on success
- */
-static int writeFile(FSM_t fsm, int writeData)
-       /*@modifies fsm @*/
-{
-    const char * path = fsm->path;
-    const char * opath = fsm->opath;
-    struct stat * st = &fsm->sb;
-    struct stat * ost = &fsm->osb;
-    size_t pos = fdGetCpioPos(fsm->cfd);
-    int left;
-    int rc;
-
-    st->st_size = (writeData ? ost->st_size : 0);
-    if (S_ISDIR(st->st_mode)) {
-       st->st_size = 0;
-    } else if (S_ISLNK(st->st_mode)) {
-       /*
-        * While linux puts the size of a symlink in the st_size field,
-        * I don't think that's a specified standard.
-        */
-       /* XXX NUL terminated result in fsm->rdbuf, len in fsm->rdnb. */
-       rc = fsmStage(fsm, FSM_READLINK);
-       if (rc) goto exit;
-       st->st_size = fsm->rdnb;
-    }
-
-    if (fsm->mapFlags & CPIO_MAP_PATH)
-       fsm->path = fsm->archivePath;
-    rc = fsmStage(fsm, FSM_HWRITE);
-    fsm->path = path;
-    if (rc) goto exit;
-
-    if (writeData && S_ISREG(st->st_mode)) {
-#if HAVE_MMAP
-       char * rdbuf = NULL;
-       void * mapped = (void *)-1;
-       size_t nmapped;
-#endif
-
-       rc = fsmStage(fsm, FSM_ROPEN);
-       if (rc) goto exit;
-
-       /* XXX unbuffered mmap generates *lots* of fdio debugging */
-#if HAVE_MMAP
-       nmapped = 0;
-       mapped = mmap(NULL, st->st_size, PROT_READ, MAP_SHARED, Fileno(fsm->rfd), 0);
-       if (mapped != (void *)-1) {
-           rdbuf = fsm->rdbuf;
-           fsm->rdbuf = (char *) mapped;
-           fsm->rdlen = nmapped = st->st_size;
-       }
-#endif
-
-       left = st->st_size;
-
-       while (left) {
-#if HAVE_MMAP
-         if (mapped != (void *)-1) {
-           fsm->rdnb = nmapped;
-         } else
-#endif
-         {
-           fsm->rdlen = (left > fsm->rdsize ? fsm->rdsize : left),
-           rc = fsmStage(fsm, FSM_READ);
-           if (rc) goto exit;
-         }
-
-           /* XXX DWRITE uses rdnb for I/O length. */
-           rc = fsmStage(fsm, FSM_DWRITE);
-           if (rc) goto exit;
-
-           left -= fsm->wrnb;
-       }
-
-#if HAVE_MMAP
-       if (mapped != (void *)-1) {
-           /*@-noeffect@*/ munmap(mapped, nmapped) /*@=noeffect@*/;
-           fsm->rdbuf = rdbuf;
-       }
-#endif
-
-    } else if (writeData && S_ISLNK(st->st_mode)) {
-       /* XXX DWRITE uses rdnb for I/O length. */
-       rc = fsmStage(fsm, FSM_DWRITE);
-       if (rc) goto exit;
-    }
-
-    rc = fsmStage(fsm, FSM_PAD);
-    if (rc) goto exit;
-
-    {  const rpmTransactionSet ts = fsmGetTs(fsm);
-       TFI_t fi = fsmGetFi(fsm);
-       if (ts && fi && ts->notify) {
-           size_t size = (fdGetCpioPos(fsm->cfd) - pos);
-           (void)ts->notify(fi->h, RPMCALLBACK_INST_PROGRESS, size, size,
-                       (fi->ap ? fi->ap->key : NULL), ts->notifyData);
-       }
-    }
-
-    rc = 0;
-
-exit:
-    if (fsm->rfd)
-       (void) fsmStage(fsm, FSM_RCLOSE);
-    fsm->opath = opath;
-    fsm->path = path;
-    return rc;
-}
-
-/**
- * Write set of linked files to payload stream.
- * @return             0 on success
- */
-static int writeLinkedFile(FSM_t fsm)
-       /*@modifies fsm @*/
-{
-    const char * path = fsm->path;
-    const char * nsuffix = fsm->nsuffix;
-    int iterIndex = fsm->ix;
-    int ec = 0;
-    int rc;
-    int i;
-
-    fsm->path = NULL;
-    fsm->nsuffix = NULL;
-    fsm->ix = -1;
-
-    for (i = fsm->li->nlink - 1; i >= 0; i--) {
-       if (fsm->li->filex[i] < 0) continue;
-
-       fsm->ix = fsm->li->filex[i];
-       rc = fsmStage(fsm, FSM_MAP);
-
-       /* Write data after last link. */
-       rc = writeFile(fsm, (i == 0));
-       if (rc && fsm->failedFile && *fsm->failedFile == NULL) {
-           ec = rc;
-           *fsm->failedFile = xstrdup(fsm->path);
-       }
-
-       fsm->path = _free(fsm->path);
-       fsm->li->filex[i] = -1;
-    }
-
-    fsm->ix = iterIndex;
-    fsm->nsuffix = nsuffix;
-    fsm->path = path;
-    return ec;
-}
-
-/**
- */
-static int fsmMakeLinks(FSM_t fsm)
-{
-    const char * path = fsm->path;
-    const char * opath = fsm->opath;
-    const char * nsuffix = fsm->nsuffix;
-    int iterIndex = fsm->ix;
-    int ec = 0;
-    int rc;
-    int i;
-
-    fsm->path = NULL;
-    fsm->opath = NULL;
-    fsm->nsuffix = NULL;
-    fsm->ix = -1;
-
-    fsm->ix = fsm->li->filex[fsm->li->createdPath];
-    rc = fsmStage(fsm, FSM_MAP);
-    fsm->opath = fsm->path;
-    fsm->path = NULL;
-    for (i = 0; i < fsm->li->nlink; i++) {
-       if (fsm->li->filex[i] < 0) continue;
-       if (i == fsm->li->createdPath) continue;
-
-       fsm->ix = fsm->li->filex[i];
-       rc = fsmStage(fsm, FSM_MAP);
-       rc = fsmStage(fsm, FSM_VERIFY);
-       if (!rc) continue;
-       if (rc != CPIOERR_LSTAT_FAILED) break;
-
-       /* XXX link(fsm->opath, fsm->path) */
-       rc = fsmStage(fsm, FSM_LINK);
-       if (rc && fsm->failedFile && *fsm->failedFile == NULL) {
-           ec = rc;
-           *fsm->failedFile = xstrdup(fsm->path);
-       }
-
-       fsm->li->linksLeft--;
-    }
-    fsm->opath = _free(fsm->opath);
-
-    fsm->ix = iterIndex;
-    fsm->nsuffix = nsuffix;
-    fsm->path = path;
-    fsm->opath = opath;
-    return ec;
-}
-
-/**
- */
-static int fsmCommitLinks(FSM_t fsm)
-{
-    const char * path = fsm->path;
-    const char * nsuffix = fsm->nsuffix;
-    int iterIndex = fsm->ix;
-    struct stat * st = &fsm->sb;
-    int rc = 0;
-    int i;
-
-    fsm->path = NULL;
-    fsm->nsuffix = NULL;
-    fsm->ix = -1;
-
-    for (fsm->li = fsm->links; fsm->li; fsm->li = fsm->li->next) {
-       if (fsm->li->inode == st->st_ino && fsm->li->dev == st->st_dev)
-           break;
-    }
-
-    for (i = 0; i < fsm->li->nlink; i++) {
-       if (fsm->li->filex[i] < 0) continue;
-       fsm->ix = fsm->li->filex[i];
-       rc = fsmStage(fsm, FSM_MAP);
-       rc = fsmStage(fsm, FSM_COMMIT);
-       fsm->path = _free(fsm->path);
-       fsm->li->filex[i] = -1;
-    }
-
-    fsm->ix = iterIndex;
-    fsm->nsuffix = nsuffix;
-    fsm->path = path;
-    return rc;
-}
-
-int fsmStage(FSM_t fsm, fileStage stage)
-{
-#ifdef UNUSED
-    fileStage prevStage = fsm->stage;
-    const char * const prev = fileStageString(prevStage);
-#endif
-    static int modulo = 4;
-    const char * const cur = fileStageString(stage);
-    struct stat * st = &fsm->sb;
-    struct stat * ost = &fsm->osb;
-    int saveerrno = errno;
-    int rc = fsm->rc;
-    int left;
-    int i;
-
-    if (stage & FSM_DEAD) {
-       /* do nothing */
-    } else if (stage & FSM_INTERNAL) {
-       if (_fsm_debug && !(stage & FSM_SYSCALL))
-           rpmMessage(RPMMESS_DEBUG, " %8s %06o%3d (%4d,%4d)%10d %s %s\n",
-               cur,
-               st->st_mode, st->st_nlink, st->st_uid, st->st_gid, st->st_size,
-               (fsm->path ? fsm->path : ""),
-               ((fsm->action != FA_UNKNOWN && fsm->action != FA_CREATE)
-                       ? fileActionString(fsm->action) : ""));
-    } else {
-       fsm->stage = stage;
-       if (_fsm_debug || !(stage & FSM_VERBOSE))
-           rpmMessage(RPMMESS_DEBUG, "%-8s  %06o%3d (%4d,%4d)%10d %s %s\n",
-               cur,
-               st->st_mode, st->st_nlink, st->st_uid, st->st_gid, st->st_size,
-               (fsm->path ? fsm->path + fsm->astriplen : ""),
-               ((fsm->action != FA_UNKNOWN && fsm->action != FA_CREATE)
-                       ? fileActionString(fsm->action) : ""));
-    }
-
-    switch (stage) {
-    case FSM_UNKNOWN:
-       break;
-    case FSM_PKGINSTALL:
-       while (1) {
-           /* Clean fsm, free'ing memory. Read next archive header. */
-           rc = fsmStage(fsm, FSM_INIT);
-
-           /* Exit on end-of-payload. */
-           if (rc == CPIOERR_HDR_TRAILER) {
-               rc = 0;
-               break;
-           }
-
-           /* Exit on error. */
-           if (rc) {
-               fsm->postpone = 1;
-               (void) fsmStage(fsm, FSM_UNDO);
-               break;
-           }
-
-           /* Extract file from archive. */
-           rc = fsmStage(fsm, FSM_PROCESS);
-           if (rc) {
-               (void) fsmStage(fsm, FSM_UNDO);
-               break;
-           }
-
-           /* Notify on success. */
-           (void) fsmStage(fsm, FSM_NOTIFY);
-
-           if (fsmStage(fsm, FSM_FINI))
-               break;
-       }
-       break;
-    case FSM_PKGERASE:
-    case FSM_PKGCOMMIT:
-       while (1) {
-           /* Clean fsm, free'ing memory. */
-           rc = fsmStage(fsm, FSM_INIT);
-
-           /* Exit on end-of-payload. */
-           if (rc == CPIOERR_HDR_TRAILER) {
-               rc = 0;
-               break;
-           }
-
-           /* Rename/erase next item. */
-           if (fsmStage(fsm, FSM_FINI))
-               break;
-       }
-       break;
-    case FSM_PKGBUILD:
-       while (1) {
-
-           rc = fsmStage(fsm, FSM_INIT);
-
-           /* Exit on end-of-payload. */
-           if (rc == CPIOERR_HDR_TRAILER) {
-               rc = 0;
-               break;
-           }
-
-           /* Exit on error. */
-           if (rc) {
-               fsm->postpone = 1;
-               (void) fsmStage(fsm, FSM_UNDO);
-               break;
-           }
-
-           /* Copy file into archive. */
-           rc = fsmStage(fsm, FSM_PROCESS);
-           if (rc) {
-               (void) fsmStage(fsm, FSM_UNDO);
-               break;
-           }
-
-           if (fsmStage(fsm, FSM_FINI))
-               break;
-       }
-
-       if (!rc)
-           rc = fsmStage(fsm, FSM_TRAILER);
-       break;
-    case FSM_CREATE:
-       {   rpmTransactionSet ts = fsmGetTs(fsm);
-#define        _tsmask (RPMTRANS_FLAG_PKGCOMMIT | RPMTRANS_FLAG_COMMIT)
-           fsm->commit = ((ts && (ts->transFlags & _tsmask) &&
-                       fsm->goal != FSM_PKGCOMMIT) ? 0 : 1);
-#undef _tsmask
-       }
-       fsm->path = _free(fsm->path);
-       fsm->opath = _free(fsm->opath);
-       fsm->dnlx = _free(fsm->dnlx);
-
-       fsm->ldn = _free(fsm->ldn);
-       fsm->ldnalloc = fsm->ldnlen = 0;
-
-       fsm->rdsize = fsm->wrsize = 0;
-       fsm->rdbuf = fsm->rdb = _free(fsm->rdb);
-       fsm->wrbuf = fsm->wrb = _free(fsm->wrb);
-       if (fsm->goal == FSM_PKGINSTALL || fsm->goal == FSM_PKGBUILD) {
-           fsm->rdsize = 8 * BUFSIZ;
-           fsm->rdbuf = fsm->rdb = xmalloc(fsm->rdsize);
-           fsm->wrsize = 8 * BUFSIZ;
-           fsm->wrbuf = fsm->wrb = xmalloc(fsm->wrsize);
-       }
-
-       fsm->mkdirsdone = 0;
-       fsm->ix = -1;
-       fsm->links = NULL;
-       fsm->li = NULL;
-       errno = 0;      /* XXX get rid of EBADF */
-
-       /* Detect and create directories not explicitly in package. */
-       if (fsm->goal == FSM_PKGINSTALL) {
-           rc = fsmStage(fsm, FSM_MKDIRS);
-           if (!rc) fsm->mkdirsdone = 1;
-       }
-
-       break;
-    case FSM_INIT:
-       fsm->path = _free(fsm->path);
-       fsm->postpone = 0;
-       fsm->diskchecked = fsm->exists = 0;
-       fsm->subdir = NULL;
-       fsm->suffix = (fsm->sufbuf[0] != '\0' ? fsm->sufbuf : NULL);
-       fsm->action = FA_UNKNOWN;
-       fsm->osuffix = NULL;
-       fsm->nsuffix = NULL;
-
-       if (fsm->goal == FSM_PKGINSTALL) {
-           /* Read next header from payload, checking for end-of-payload. */
-           rc = fsmStage(fsm, FSM_NEXT);
-       }
-       if (rc) break;
-
-       /* Identify mapping index. */
-       fsm->ix = ((fsm->goal == FSM_PKGINSTALL)
-               ? mapFind(fsm->iter, fsm->path) : mapNextIterator(fsm->iter));
-
-       /* On non-install, detect end-of-loop. */
-       if (fsm->goal != FSM_PKGINSTALL && fsm->ix < 0) {
-           rc = CPIOERR_HDR_TRAILER;
-           break;
-       }
-
-       /* On non-install, mode must be known so that dirs don't get suffix. */
-       if (fsm->goal != FSM_PKGINSTALL) {
-           TFI_t fi = fsmGetFi(fsm);
-           st->st_mode = fi->fmodes[fsm->ix];
-       }
-
-       /* Generate file path. */
-       rc = fsmStage(fsm, FSM_MAP);
-       if (rc) break;
-
-       /* Perform lstat/stat for disk file. */
-       rc = fsmStage(fsm, (!(fsm->mapFlags & CPIO_FOLLOW_SYMLINKS)
-                       ? FSM_LSTAT : FSM_STAT));
-       if (rc == CPIOERR_LSTAT_FAILED && errno == ENOENT) {
-           errno = saveerrno;
-           rc = 0;
-           fsm->exists = 0;
-       } else if (rc == 0) {
-           fsm->exists = 1;
-       }
-       fsm->diskchecked = 1;
-       if (rc) break;
-
-       /* On non-install, the disk file stat is what's remapped. */
-       if (fsm->goal != FSM_PKGINSTALL)
-           *st = *ost;                 /* structure assignment */
-
-       /* Remap file perms, owner, and group. */
-       rc = fsmMapAttrs(fsm);
-       if (rc) break;
-
-       fsm->postpone = XFA_SKIPPING(fsm->action);
-       if (fsm->goal == FSM_PKGINSTALL || fsm->goal == FSM_PKGBUILD) {
-           if (!S_ISDIR(st->st_mode) && st->st_nlink > 1)
-               fsm->postpone = saveHardLink(fsm);
-       }
-       break;
-    case FSM_PRE:
-       break;
-    case FSM_MAP:
-       rc = fsmMapPath(fsm);
-       break;
-    case FSM_MKDIRS:
-       {   const char * path = fsm->path;
-           mode_t st_mode = st->st_mode;
-           void * dnli = dnlInitIterator(fsm, 0);
-           char * dn = fsm->rdbuf;
-           int dc = dnlCount(dnli);
-
-           fsm->path = NULL;
-           dn[0] = '\0';
-           fsm->dnlx = (dc ? xcalloc(dc, sizeof(*fsm->dnlx)) : NULL);
-           while ((fsm->path = dnlNextIterator(dnli)) != NULL) {
-               int dnlen = strlen(fsm->path);
-               char * te;
-
-               dc = dnlIndex(dnli);
-               if (dc < 0) continue;
-               fsm->dnlx[dc] = dnlen;
-               if (dnlen <= 1)
-                   continue;
-               if (dnlen <= fsm->ldnlen && !strcmp(fsm->path, fsm->ldn))
-                   continue;
-
-               /* Copy to avoid const on fsm->path. */
-               (void) stpcpy(dn, fsm->path);
-               fsm->path = dn;
-
-               /* Initial mode for created dirs is 0700 */
-               st->st_mode &= ~07777;          /* XXX abuse st->st_mode */
-               st->st_mode |=  00700;
-
-               /* Assume '/' directory, otherwise "mkdir -p" */
-               for (i = 1, te = dn + 1; *te; te++, i++) {
-                   if (*te != '/') continue;
-
-                   *te = '\0';
-
-                   /* Already validated? */
-                   if (i < fsm->ldnlen &&
-                       (fsm->ldn[i] == '/' || fsm->ldn[i] == '\0') &&
-                       !strncmp(fsm->path, fsm->ldn, i))
-                   {
-                       *te = '/';
-                       /* Move pre-existing path marker forward. */
-                       fsm->dnlx[dc] = (te - dn);
-                       continue;
-                   }
-
-                   /* Validate next component of path. */
-                   rc = fsmStage(fsm, FSM_LSTAT);
-                   *te = '/';
-
-                   /* Directory already exists? */
-                   if (rc == 0 && S_ISDIR(ost->st_mode)) {
-                       /* Move pre-existing path marker forward. */
-                       fsm->dnlx[dc] = (te - dn);
-                   } else if (rc == CPIOERR_LSTAT_FAILED) {
-                       TFI_t fi = fsmGetFi(fsm);
-                       mode_t st_mode = st->st_mode;
-                       *te = '\0';
-                       st->st_mode = S_IFDIR | (fi->dperms & 07777);
-                       rc = fsmStage(fsm, FSM_MKDIR);
-                       if (!rc)
-                           rpmMessage(RPMMESS_WARNING,
-                               _("%s directory created with perms %04o.\n"),
-                               fsm->path, (st->st_mode & 07777));
-                       *te = '/';
-                       st->st_mode = st_mode;
-                   }
-                   if (rc) break;
-               }
-               if (rc) break;
-
-               /* Save last validated path. */
-               if (fsm->ldnalloc < (dnlen + 1)) {
-                   fsm->ldnalloc = dnlen + 100;
-                   fsm->ldn = xrealloc(fsm->ldn, fsm->ldnalloc);
-               }
-               strcpy(fsm->ldn, fsm->path);
-               fsm->ldnlen = dnlen;
-           }
-           dnli = dnlFreeIterator(dnli);
-           fsm->path = path;
-           st->st_mode = st_mode;              /* XXX restore st->st_mode */
-       }
-       break;
-    case FSM_RMDIRS:
-       if (fsm->dnlx) {
-           const char * path = fsm->path;
-           void * dnli = dnlInitIterator(fsm, 1);
-           char * dn = fsm->rdbuf;
-           int dc = dnlCount(dnli);
-
-           fsm->path = NULL;
-           dn[0] = '\0';
-           while ((fsm->path = dnlNextIterator(dnli)) != NULL) {
-               int dnlen = strlen(fsm->path);
-               char * te;
-
-               dc = dnlIndex(dnli);
-               if (fsm->dnlx[dc] < 1 || fsm->dnlx[dc] >= dnlen)
-                   continue;
-
-               /* Copy to avoid const on fsm->path. */
-               te = stpcpy(dn, fsm->path) - 1;
-               fsm->path = dn;
-
-               /* Remove generated directories. */
-               do {
-                   if (*te == '/') {
-                       *te = '\0';
-                       rc = fsmStage(fsm, FSM_RMDIR);
-                       *te = '/';
-                   }
-                   if (rc) break;
-                   te--;
-               } while ((te - dn) > fsm->dnlx[dc]);
-           }
-           dnli = dnlFreeIterator(dnli);
-           fsm->path = path;
-       }
-       break;
-    case FSM_PROCESS:
-       if (fsm->postpone) {
-           if (fsm->goal == FSM_PKGINSTALL)
-               rc = fsmStage(fsm, FSM_EAT);
-           break;
-       }
-
-       if (fsm->goal == FSM_PKGBUILD) {
-           if (!S_ISDIR(st->st_mode) && st->st_nlink > 1) {
-               struct hardLink * li, * prev;
-               rc = writeLinkedFile(fsm);
-               if (rc) break;  /* W2DO? */
-
-               for (li = fsm->links, prev = NULL; li; prev = li, li = li->next)
-                    if (li == fsm->li) break;
-
-               if (prev == NULL)
-                   fsm->links = fsm->li->next;
-               else
-                   prev->next = fsm->li->next;
-               fsm->li->next = NULL;
-               fsm->li = freeHardLink(fsm->li);
-           } else {
-               rc = writeFile(fsm, 1);
-           }
-           break;
-       }
-
-       if (fsm->goal != FSM_PKGINSTALL)
-           break;
-
-       if (S_ISREG(st->st_mode)) {
-           const char * path = fsm->path;
-           if (fsm->osuffix)
-               fsm->path = fsmFsPath(fsm, st, NULL, NULL);
-           rc = fsmStage(fsm, FSM_VERIFY);
-
-           if (rc == 0 && fsm->osuffix) {
-               const char * opath = fsm->opath;
-               fsm->opath = fsm->path;
-               fsm->path = fsmFsPath(fsm, st, NULL, fsm->osuffix);
-               rc = fsmStage(fsm, FSM_RENAME);
-               if (!rc)
-                   rpmMessage(RPMMESS_WARNING,
-                       _("%s saved as %s\n"), fsm->opath, fsm->path);
-               fsm->path = _free(fsm->path);
-               fsm->opath = opath;
-           }
-
-           fsm->path = path;
-           if (rc != CPIOERR_LSTAT_FAILED) return rc;
-           rc = expandRegular(fsm);
-       } else if (S_ISDIR(st->st_mode)) {
-           mode_t st_mode = st->st_mode;
-           rc = fsmStage(fsm, FSM_VERIFY);
-           if (rc == CPIOERR_LSTAT_FAILED) {
-               st->st_mode &= ~07777;          /* XXX abuse st->st_mode */
-               st->st_mode |=  00700;
-               rc = fsmStage(fsm, FSM_MKDIR);
-               st->st_mode = st_mode;          /* XXX restore st->st_mode */
-           }
-       } else if (S_ISLNK(st->st_mode)) {
-           const char * opath = fsm->opath;
-
-           if ((st->st_size + 1) > fsm->rdsize) {
-               rc = CPIOERR_HDR_SIZE;
-               break;
-           }
-
-           fsm->wrlen = st->st_size;
-           rc = fsmStage(fsm, FSM_DREAD);
-           if (!rc && fsm->rdnb != fsm->wrlen)
-               rc = CPIOERR_READ_FAILED;
-           if (rc) break;
-
-           fsm->wrbuf[st->st_size] = '\0';
-           /* XXX symlink(fsm->opath, fsm->path) */
-           fsm->opath = fsm->wrbuf;            /* XXX abuse fsm->path */
-           rc = fsmStage(fsm, FSM_VERIFY);
-           if (rc == CPIOERR_LSTAT_FAILED)
-               rc = fsmStage(fsm, FSM_SYMLINK);
-           fsm->opath = opath;         /* XXX restore fsm->path */
-       } else if (S_ISFIFO(st->st_mode)) {
-           mode_t st_mode = st->st_mode;
-           /* This mimics cpio S_ISSOCK() behavior but probably isnt' right */
-           rc = fsmStage(fsm, FSM_VERIFY);
-           if (rc == CPIOERR_LSTAT_FAILED) {
-               st->st_mode = 0000;             /* XXX abuse st->st_mode */
-               rc = fsmStage(fsm, FSM_MKFIFO);
-               st->st_mode = st_mode;  /* XXX restore st->st_mode */
-           }
-       } else if (S_ISCHR(st->st_mode) ||
-                  S_ISBLK(st->st_mode) ||
-                  S_ISSOCK(st->st_mode))
-       {
-           rc = fsmStage(fsm, FSM_VERIFY);
-           if (rc == CPIOERR_LSTAT_FAILED)
-               rc = fsmStage(fsm, FSM_MKNOD);
-       } else {
-           rc = CPIOERR_UNKNOWN_FILETYPE;
-       }
-       if (!S_ISDIR(st->st_mode) && st->st_nlink > 1) {
-           fsm->li->createdPath = fsm->li->linkIndex;
-           rc = fsmMakeLinks(fsm);
-       }
-       break;
-    case FSM_POST:
-       break;
-    case FSM_MKLINKS:
-       break;
-    case FSM_NOTIFY:           /* XXX move from fsm to psm -> tsm */
-       if (fsm->goal == FSM_PKGINSTALL || fsm->goal == FSM_PKGBUILD) {
-           rpmTransactionSet ts = fsmGetTs(fsm);
-           TFI_t fi = fsmGetFi(fsm);
-           if (ts && ts->notify && fi)
-               (void)ts->notify(fi->h, RPMCALLBACK_INST_PROGRESS,
-                       fdGetCpioPos(fsm->cfd), fi->archiveSize,
-                       (fi->ap ? fi->ap->key : NULL), ts->notifyData);
-       }
-       break;
-    case FSM_UNDO:
-       if (fsm->postpone)
-           break;
-       if (fsm->goal == FSM_PKGINSTALL) {
-           (void) fsmStage(fsm,
-               (S_ISDIR(st->st_mode) ? FSM_RMDIR : FSM_UNLINK));
-
-#ifdef NOTYET  /* XXX remove only dirs just created, not all. */
-           if (fsm->dnlx)
-               (void) fsmStage(fsm, FSM_RMDIRS);
-#endif
-           errno = saveerrno;
-       }
-       if (fsm->failedFile && *fsm->failedFile == NULL)
-           *fsm->failedFile = xstrdup(fsm->path);
-       break;
-    case FSM_FINI:
-       if (!fsm->postpone && fsm->commit) {
-           if (fsm->goal == FSM_PKGINSTALL)
-               rc = ((!S_ISDIR(st->st_mode) && st->st_nlink > 1)
-                       ? fsmCommitLinks(fsm) : fsmStage(fsm, FSM_COMMIT));
-           if (fsm->goal == FSM_PKGCOMMIT)
-               rc = fsmStage(fsm, FSM_COMMIT);
-           if (fsm->goal == FSM_PKGERASE)
-               rc = fsmStage(fsm, FSM_COMMIT);
-       }
-       fsm->path = _free(fsm->path);
-       fsm->opath = _free(fsm->opath);
-       memset(st, 0, sizeof(*st));
-       memset(ost, 0, sizeof(*ost));
-       break;
-    case FSM_COMMIT:
-       /* Rename pre-existing modified or unmanaged file. */
-       if (fsm->diskchecked && fsm->exists && fsm->osuffix) {
-           const char * opath = fsm->opath;
-           const char * path = fsm->path;
-           fsm->opath = fsmFsPath(fsm, st, NULL, NULL);
-           fsm->path = fsmFsPath(fsm, st, NULL, fsm->osuffix);
-           rc = fsmStage(fsm, FSM_RENAME);
-           if (!rc) {
-               rpmMessage(RPMMESS_WARNING, _("%s saved as %s\n"),
-                               fsm->opath, fsm->path);
-           }
-           fsm->path = _free(fsm->path);
-           fsm->path = path;
-           fsm->opath = _free(fsm->opath);
-           fsm->opath = opath;
-       }
-
-       /* Remove erased files. */
-       if (fsm->goal == FSM_PKGERASE) {
-           if (fsm->action == FA_ERASE) {
-               TFI_t fi = fsmGetFi(fsm);
-               if (S_ISDIR(st->st_mode)) {
-                   rc = fsmStage(fsm, FSM_RMDIR);
-                   if (!rc) break;
-                   switch (errno) {
-                   case ENOENT: /* XXX rmdir("/") linux 2.2.x kernel hack */
-                   case ENOTEMPTY:
-       /* XXX make sure that build side permits %missingok on directories. */
-                       if (fsm->fflags & RPMFILE_MISSINGOK)
-                           break;
-
-                       /* XXX common error message. */
-                       rpmError(RPMERR_RMDIR, 
-                           _("%s rmdir of %s failed: Directory not empty\n"), 
-                               fiTypeString(fi), fsm->path);
-                       break;
-                   default:
-                       rpmError(RPMERR_RMDIR,
-                               _("%s rmdir of %s failed: %s\n"),
-                               fiTypeString(fi), fsm->path, strerror(errno));
-                       break;
-                   }
-               } else {
-                   rc = fsmStage(fsm, FSM_UNLINK);
-                   if (!rc) break;
-                   if (!(errno == ENOENT && (fsm->fflags & RPMFILE_MISSINGOK)))
-                       rpmError(RPMERR_UNLINK,
-                               _("%s unlink of %s failed: %s\n"),
-                               fiTypeString(fi), fsm->path, strerror(errno));
-               }
-           }
-           break;
-       }
-
-       if (!S_ISSOCK(st->st_mode)) {   /* XXX /dev/log et al are skipped */
-           /* Rename temporary to final file name. */
-           if (!S_ISDIR(st->st_mode) &&
-               (fsm->subdir || fsm->suffix || fsm->nsuffix))
-           {
-               fsm->opath = fsm->path;
-               fsm->path = fsmFsPath(fsm, st, NULL, fsm->nsuffix);
-               rc = fsmStage(fsm, FSM_RENAME);
-               if (!rc && fsm->nsuffix) {
-                   const char * opath = fsmFsPath(fsm, st, NULL, NULL);
-                   rpmMessage(RPMMESS_WARNING, _("%s created as %s\n"),
-                               opath, fsm->path);
-                   opath = _free(opath);
-               }
-               fsm->opath = _free(fsm->opath);
-           }
-           if (S_ISLNK(st->st_mode)) {
-               if (!rc && !getuid())
-                   rc = fsmStage(fsm, FSM_LCHOWN);
-           } else {
-               if (!rc && !getuid())
-                   rc = fsmStage(fsm, FSM_CHOWN);
-               if (!rc)
-                   rc = fsmStage(fsm, FSM_CHMOD);
-               if (!rc) {
-                   time_t st_mtime = st->st_mtime;
-                   TFI_t fi = fsmGetFi(fsm);
-                   if (fi->fmtimes)
-                       st->st_mtime = fi->fmtimes[fsm->ix];
-                   rc = fsmStage(fsm, FSM_UTIME);
-                   st->st_mtime = st_mtime;
-               }
-           }
-       }
-
-       /* Notify on success. */
-       if (!rc)                rc = fsmStage(fsm, FSM_NOTIFY);
-       break;
-    case FSM_DESTROY:
-       fsm->path = _free(fsm->path);
-
-       /* Create any remaining links (if no error), and clean up. */
-       while ((fsm->li = fsm->links) != NULL) {
-           fsm->links = fsm->li->next;
-           fsm->li->next = NULL;
-           if (fsm->goal == FSM_PKGINSTALL && fsm->commit && fsm->li->linksLeft)
-           {
-               for (i = 0 ; i < fsm->li->linksLeft; i++) {
-                   if (fsm->li->filex[i] < 0) continue;
-                   rc = CPIOERR_MISSING_HARDLINK;
-                   if (fsm->failedFile && *fsm->failedFile == NULL) {
-                       fsm->ix = fsm->li->filex[i];
-                       if (!fsmStage(fsm, FSM_MAP)) {
-                           *fsm->failedFile = fsm->path;
-                           fsm->path = NULL;
-                       }
-                   }
-                   break;
-               }
-           }
-           if (fsm->goal == FSM_PKGBUILD) {
-               rc = CPIOERR_MISSING_HARDLINK;
-           }
-           fsm->li = freeHardLink(fsm->li);
-       }
-       fsm->ldn = _free(fsm->ldn);
-       fsm->ldnalloc = fsm->ldnlen = 0;
-       fsm->rdbuf = fsm->rdb = _free(fsm->rdb);
-       fsm->wrbuf = fsm->wrb = _free(fsm->wrb);
-       break;
-    case FSM_VERIFY:
-       if (fsm->diskchecked && !fsm->exists) {
-           rc = CPIOERR_LSTAT_FAILED;
-           break;
-       }
-       if (S_ISREG(st->st_mode)) {
-           char * path = alloca(strlen(fsm->path) + sizeof("-RPMDELETE"));
-           (void) stpcpy( stpcpy(path, fsm->path), "-RPMDELETE");
-           /*
-            * XXX HP-UX (and other os'es) don't permit unlink on busy
-            * XXX files.
-            */
-           fsm->opath = fsm->path;
-           fsm->path = path;
-           rc = fsmStage(fsm, FSM_RENAME);
-           if (!rc)
-                   (void) fsmStage(fsm, FSM_UNLINK);
-           else
-                   rc = CPIOERR_UNLINK_FAILED;
-           fsm->path = fsm->opath;
-           fsm->opath = NULL;
-           return (rc ? rc : CPIOERR_LSTAT_FAILED);    /* XXX HACK */
-           /*@notreached@*/ break;
-       } else if (S_ISDIR(st->st_mode)) {
-           if (S_ISDIR(ost->st_mode))          return 0;
-           if (S_ISLNK(ost->st_mode)) {
-               rc = fsmStage(fsm, FSM_STAT);
-               if (rc == CPIOERR_STAT_FAILED && errno == ENOENT) rc = 0;
-               if (rc) break;
-               errno = saveerrno;
-               if (S_ISDIR(ost->st_mode))      return 0;
-           }
-       } else if (S_ISLNK(st->st_mode)) {
-           if (S_ISLNK(ost->st_mode)) {
-       /* XXX NUL terminated result in fsm->rdbuf, len in fsm->rdnb. */
-               rc = fsmStage(fsm, FSM_READLINK);
-               errno = saveerrno;
-               if (rc) break;
-               if (!strcmp(fsm->opath, fsm->rdbuf))    return 0;
-           }
-       } else if (S_ISFIFO(st->st_mode)) {
-           if (S_ISFIFO(ost->st_mode))         return 0;
-       } else if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) {
-           if ((S_ISCHR(ost->st_mode) || S_ISBLK(ost->st_mode)) &&
-               (ost->st_rdev == st->st_rdev))  return 0;
-       } else if (S_ISSOCK(st->st_mode)) {
-           if (S_ISSOCK(ost->st_mode))         return 0;
-       }
-           /* XXX shouldn't do this with commit/undo. */
-       rc = 0;
-       if (fsm->stage == FSM_PROCESS) rc = fsmStage(fsm, FSM_UNLINK);
-       if (rc == 0)    rc = CPIOERR_LSTAT_FAILED;
-       return (rc ? rc : CPIOERR_LSTAT_FAILED);        /* XXX HACK */
-       /*@notreached@*/ break;
-
-    case FSM_UNLINK:
-       rc = Unlink(fsm->path);
-       if (_fsm_debug && (stage & FSM_SYSCALL))
-           rpmMessage(RPMMESS_DEBUG, " %8s (%s) %s\n", cur,
-               fsm->path, (rc < 0 ? strerror(errno) : ""));
-       if (rc < 0)     rc = CPIOERR_UNLINK_FAILED;
-       break;
-    case FSM_RENAME:
-       rc = Rename(fsm->opath, fsm->path);
-       if (_fsm_debug && (stage & FSM_SYSCALL))
-           rpmMessage(RPMMESS_DEBUG, " %8s (%s, %s) %s\n", cur,
-               fsm->opath, fsm->path, (rc < 0 ? strerror(errno) : ""));
-       if (rc < 0)     rc = CPIOERR_RENAME_FAILED;
-       break;
-    case FSM_MKDIR:
-       rc = Mkdir(fsm->path, (st->st_mode & 07777));
-       if (_fsm_debug && (stage & FSM_SYSCALL))
-           rpmMessage(RPMMESS_DEBUG, " %8s (%s, 0%04o) %s\n", cur,
-               fsm->path, (st->st_mode & 07777),
-               (rc < 0 ? strerror(errno) : ""));
-       if (rc < 0)     rc = CPIOERR_MKDIR_FAILED;
-       break;
-    case FSM_RMDIR:
-       rc = Rmdir(fsm->path);
-       if (_fsm_debug && (stage & FSM_SYSCALL))
-           rpmMessage(RPMMESS_DEBUG, " %8s (%s) %s\n", cur,
-               fsm->path, (rc < 0 ? strerror(errno) : ""));
-       if (rc < 0)     rc = CPIOERR_RMDIR_FAILED;
-       break;
-    case FSM_CHOWN:
-       rc = chown(fsm->path, st->st_uid, st->st_gid);
-       if (_fsm_debug && (stage & FSM_SYSCALL))
-           rpmMessage(RPMMESS_DEBUG, " %8s (%s, %d, %d) %s\n", cur,
-               fsm->path, st->st_uid, st->st_gid,
-               (rc < 0 ? strerror(errno) : ""));
-       if (rc < 0)     rc = CPIOERR_CHOWN_FAILED;
-       break;
-    case FSM_LCHOWN:
-#if ! CHOWN_FOLLOWS_SYMLINK
-       rc = lchown(fsm->path, st->st_uid, st->st_gid);
-       if (_fsm_debug && (stage & FSM_SYSCALL))
-           rpmMessage(RPMMESS_DEBUG, " %8s (%s, %d, %d) %s\n", cur,
-               fsm->path, st->st_uid, st->st_gid,
-               (rc < 0 ? strerror(errno) : ""));
-       if (rc < 0)     rc = CPIOERR_CHOWN_FAILED;
-#endif
-       break;
-    case FSM_CHMOD:
-       rc = chmod(fsm->path, (st->st_mode & 07777));
-       if (_fsm_debug && (stage & FSM_SYSCALL))
-           rpmMessage(RPMMESS_DEBUG, " %8s (%s, 0%04o) %s\n", cur,
-               fsm->path, (st->st_mode & 07777),
-               (rc < 0 ? strerror(errno) : ""));
-       if (rc < 0)     rc = CPIOERR_CHMOD_FAILED;
-       break;
-    case FSM_UTIME:
-       {   struct utimbuf stamp;
-           stamp.actime = st->st_mtime;
-           stamp.modtime = st->st_mtime;
-           rc = utime(fsm->path, &stamp);
-           if (_fsm_debug && (stage & FSM_SYSCALL))
-               rpmMessage(RPMMESS_DEBUG, " %8s (%s, 0x%x) %s\n", cur,
-                       fsm->path, (unsigned)st->st_mtime,
-                       (rc < 0 ? strerror(errno) : ""));
-           if (rc < 0) rc = CPIOERR_UTIME_FAILED;
-       }
-       break;
-    case FSM_SYMLINK:
-       rc = symlink(fsm->opath, fsm->path);
-       if (_fsm_debug && (stage & FSM_SYSCALL))
-           rpmMessage(RPMMESS_DEBUG, " %8s (%s, %s) %s\n", cur,
-               fsm->opath, fsm->path, (rc < 0 ? strerror(errno) : ""));
-       if (rc < 0)     rc = CPIOERR_SYMLINK_FAILED;
-       break;
-    case FSM_LINK:
-       rc = Link(fsm->opath, fsm->path);
-       if (_fsm_debug && (stage & FSM_SYSCALL))
-           rpmMessage(RPMMESS_DEBUG, " %8s (%s, %s) %s\n", cur,
-               fsm->opath, fsm->path, (rc < 0 ? strerror(errno) : ""));
-       if (rc < 0)     rc = CPIOERR_LINK_FAILED;
-       break;
-    case FSM_MKFIFO:
-       rc = mkfifo(fsm->path, (st->st_mode & 07777));
-       if (_fsm_debug && (stage & FSM_SYSCALL))
-           rpmMessage(RPMMESS_DEBUG, " %8s (%s, 0%04o) %s\n", cur,
-               fsm->path, (st->st_mode & 07777),
-               (rc < 0 ? strerror(errno) : ""));
-       if (rc < 0)     rc = CPIOERR_MKFIFO_FAILED;
-       break;
-    case FSM_MKNOD:
-       /*@-unrecog@*/
-       rc = mknod(fsm->path, (st->st_mode & ~07777), st->st_rdev);
-       if (_fsm_debug && (stage & FSM_SYSCALL))
-           rpmMessage(RPMMESS_DEBUG, " %8s (%s, 0%o, 0x%x) %s\n", cur,
-               fsm->path, (st->st_mode & ~07777), (unsigned)st->st_rdev,
-               (rc < 0 ? strerror(errno) : ""));
-       if (rc < 0)     rc = CPIOERR_MKNOD_FAILED;
-       /*@=unrecog@*/
-       break;
-    case FSM_LSTAT:
-       rc = Lstat(fsm->path, ost);
-       if (_fsm_debug && (stage & FSM_SYSCALL) && rc && errno != ENOENT)
-           rpmMessage(RPMMESS_DEBUG, " %8s (%s, ost) %s\n", cur,
-               fsm->path, (rc < 0 ? strerror(errno) : ""));
-       if (rc < 0)     rc = CPIOERR_LSTAT_FAILED;
-       break;
-    case FSM_STAT:
-       rc = Stat(fsm->path, ost);
-       if (_fsm_debug && (stage & FSM_SYSCALL) && rc && errno != ENOENT)
-           rpmMessage(RPMMESS_DEBUG, " %8s (%s, ost) %s\n", cur,
-               fsm->path, (rc < 0 ? strerror(errno) : ""));
-       if (rc < 0)     rc = CPIOERR_STAT_FAILED;
-       break;
-    case FSM_READLINK:
-       /* XXX NUL terminated result in fsm->rdbuf, len in fsm->rdnb. */
-       rc = Readlink(fsm->path, fsm->rdbuf, fsm->rdsize - 1);
-       if (_fsm_debug && (stage & FSM_SYSCALL))
-           rpmMessage(RPMMESS_DEBUG, " %8s (%s, rdbuf, %d) %s\n", cur,
-               fsm->path, fsm->rdlen, (rc < 0 ? strerror(errno) : ""));
-       if (rc < 0)     rc = CPIOERR_READLINK_FAILED;
-       else {
-           fsm->rdnb = rc;
-           fsm->rdbuf[fsm->rdnb] = '\0';
-           rc = 0;
-       }
-       break;
-    case FSM_CHROOT:
-       break;
-
-    case FSM_NEXT:
-       rc = fsmStage(fsm, FSM_HREAD);
-       if (rc) break;
-       if (!strcmp(fsm->path, CPIO_TRAILER)) { /* Detect end-of-payload. */
-           fsm->path = _free(fsm->path);
-           rc = CPIOERR_HDR_TRAILER;
-       }
-       if (!rc)
-           rc = fsmStage(fsm, FSM_POS);
-       break;
-    case FSM_EAT:
-       for (left = st->st_size; left > 0; left -= fsm->rdnb) {
-           fsm->wrlen = (left > fsm->wrsize ? fsm->wrsize : left);
-           rc = fsmStage(fsm, FSM_DREAD);
-           if (rc) break;
-       }
-       break;
-    case FSM_POS:
-       left = (modulo - (fdGetCpioPos(fsm->cfd) % modulo)) % modulo;
-       if (left) {
-           fsm->wrlen = left;
-           (void) fsmStage(fsm, FSM_DREAD);
-       }
-       break;
-    case FSM_PAD:
-       left = (modulo - (fdGetCpioPos(fsm->cfd) % modulo)) % modulo;
-       if (left) {
-           memset(fsm->rdbuf, 0, left);
-           /* XXX DWRITE uses rdnb for I/O length. */
-           fsm->rdnb = left;
-           (void) fsmStage(fsm, FSM_DWRITE);
-       }
-       break;
-    case FSM_TRAILER:
-       rc = cpioTrailerWrite(fsm);
-       break;
-    case FSM_HREAD:
-       rc = fsmStage(fsm, FSM_POS);
-       if (!rc)
-           rc = cpioHeaderRead(fsm, st);       /* Read next payload header. */
-       break;
-    case FSM_HWRITE:
-       rc = cpioHeaderWrite(fsm, st);          /* Write next payload header. */
-       break;
-    case FSM_DREAD:
-       fsm->rdnb = Fread(fsm->wrbuf, sizeof(*fsm->wrbuf), fsm->wrlen, fsm->cfd);
-       if (_fsm_debug && (stage & FSM_SYSCALL))
-           rpmMessage(RPMMESS_DEBUG, " %8s (%s, %d, cfd)\trdnb %d\n",
-               cur, (fsm->wrbuf == fsm->wrb ? "wrbuf" : "mmap"),
-               fsm->wrlen, fsm->rdnb);
-if (fsm->rdnb != fsm->wrlen) fprintf(stderr, "*** short read, had %d, got %d\n", (int)fsm->rdnb, (int)fsm->wrlen);
-#ifdef NOTYET
-       if (Ferror(fsm->rfd))
-           rc = CPIOERR_READ_FAILED;
-#endif
-       if (fsm->rdnb > 0)
-           fdSetCpioPos(fsm->cfd, fdGetCpioPos(fsm->cfd) + fsm->rdnb);
-       break;
-    case FSM_DWRITE:
-       fsm->wrnb = Fwrite(fsm->rdbuf, sizeof(*fsm->rdbuf), fsm->rdnb, fsm->cfd);
-       if (_fsm_debug && (stage & FSM_SYSCALL))
-           rpmMessage(RPMMESS_DEBUG, " %8s (%s, %d, cfd)\twrnb %d\n",
-               cur, (fsm->rdbuf == fsm->rdb ? "rdbuf" : "mmap"),
-               fsm->rdnb, fsm->wrnb);
-if (fsm->rdnb != fsm->wrnb) fprintf(stderr, "*** short write, had %d, got %d\n", (int)fsm->rdnb, (int)fsm->wrnb);
-#ifdef NOTYET
-       if (Ferror(fsm->wfd))
-           rc = CPIOERR_WRITE_FAILED;
-#endif
-       if (fsm->wrnb > 0)
-           fdSetCpioPos(fsm->cfd, fdGetCpioPos(fsm->cfd) + fsm->wrnb);
-       break;
-
-    case FSM_ROPEN:
-       fsm->rfd = Fopen(fsm->path, "r.ufdio");
-       if (fsm->rfd == NULL || Ferror(fsm->rfd)) {
-           if (fsm->rfd)       (void) fsmStage(fsm, FSM_RCLOSE);
-           fsm->rfd = NULL;
-           rc = CPIOERR_OPEN_FAILED;
-           break;
-       }
-       if (_fsm_debug && (stage & FSM_SYSCALL))
-           rpmMessage(RPMMESS_DEBUG, " %8s (%s, \"r\") rfd %p rdbuf %p\n", cur,
-               fsm->path, fsm->rfd, fsm->rdbuf);
-       break;
-    case FSM_READ:
-       fsm->rdnb = Fread(fsm->rdbuf, sizeof(*fsm->rdbuf), fsm->rdlen, fsm->rfd);
-       if (_fsm_debug && (stage & FSM_SYSCALL))
-           rpmMessage(RPMMESS_DEBUG, " %8s (rdbuf, %d, rfd)\trdnb %d\n",
-               cur, fsm->rdlen, fsm->rdnb);
-if (fsm->rdnb != fsm->rdlen) fprintf(stderr, "*** short read, had %d, got %d\n", (int)fsm->rdnb, (int)fsm->rdlen);
-#ifdef NOTYET
-       if (Ferror(fsm->rfd))
-           rc = CPIOERR_READ_FAILED;
-#endif
-       break;
-    case FSM_RCLOSE:
-       if (fsm->rfd) {
-           if (_fsm_debug && (stage & FSM_SYSCALL))
-               rpmMessage(RPMMESS_DEBUG, " %8s (%p)\n", cur, fsm->rfd);
-           (void) Fclose(fsm->rfd);
-           errno = saveerrno;
-       }
-       fsm->rfd = NULL;
-       break;
-    case FSM_WOPEN:
-       fsm->wfd = Fopen(fsm->path, "w.ufdio");
-       if (fsm->wfd == NULL || Ferror(fsm->wfd)) {
-           if (fsm->wfd)       (void) fsmStage(fsm, FSM_WCLOSE);
-           fsm->wfd = NULL;
-           rc = CPIOERR_OPEN_FAILED;
-       }
-       if (_fsm_debug && (stage & FSM_SYSCALL))
-           rpmMessage(RPMMESS_DEBUG, " %8s (%s, \"w\") wfd %p wrbuf %p\n", cur,
-               fsm->path, fsm->wfd, fsm->wrbuf);
-       break;
-    case FSM_WRITE:
-       fsm->wrnb = Fwrite(fsm->wrbuf, sizeof(*fsm->wrbuf), fsm->rdnb, fsm->wfd);
-       if (_fsm_debug && (stage & FSM_SYSCALL))
-           rpmMessage(RPMMESS_DEBUG, " %8s (wrbuf, %d, wfd)\twrnb %d\n",
-               cur, fsm->rdnb, fsm->wrnb);
-if (fsm->rdnb != fsm->wrnb) fprintf(stderr, "*** short write: had %d, got %d\n", (int)fsm->rdnb, (int)fsm->wrnb);
-#ifdef NOTYET
-       if (Ferror(fsm->wfd))
-           rc = CPIOERR_WRITE_FAILED;
-#endif
-       break;
-    case FSM_WCLOSE:
-       if (fsm->wfd) {
-           if (_fsm_debug && (stage & FSM_SYSCALL))
-               rpmMessage(RPMMESS_DEBUG, " %8s (%p)\n", cur, fsm->wfd);
-           (void) Fclose(fsm->wfd);
-           errno = saveerrno;
-       }
-       fsm->wfd = NULL;
-       break;
-
-    default:
-       break;
-    }
-
-    if (!(stage & FSM_INTERNAL)) {
-       fsm->rc = (rc == CPIOERR_HDR_TRAILER ? 0 : rc);
-    }
-    return rc;
-}
-
 const char *const cpioStrerror(int rc)
 {
     static char msg[256];
diff --git a/lib/fsm.c b/lib/fsm.c
new file mode 100644 (file)
index 0000000..195cc17
--- /dev/null
+++ b/lib/fsm.c
@@ -0,0 +1,1905 @@
+/** \ingroup payload
+ * \file lib/fsm.c
+ *  Handle payloads within rpm packages.
+ */
+
+#include "system.h"
+
+#include "psm.h"
+#include "rpmerr.h"
+#include "debug.h"
+
+/*@access FD_t @*/
+/*@access rpmTransactionSet @*/
+/*@access TFI_t @*/
+/*@access FSM_t @*/
+
+#define        alloca_strdup(_s)       strcpy(alloca(strlen(_s)+1), (_s))
+
+/**
+ * Wrapper to free(3), hides const compilation noise, permit NULL, return NULL.
+ * @param this         memory to free
+ * @retval             NULL always
+ */
+static /*@null@*/ void * _free(/*@only@*/ /*@null@*/ const void * this) {
+    if (this)  free((void *)this);
+    return NULL;
+}
+
+int _fsm_debug = 0;
+
+rpmTransactionSet fsmGetTs(const FSM_t fsm) {
+    const FSMI_t iter = fsm->iter;
+    return (iter ? iter->ts : NULL);
+}
+
+TFI_t fsmGetFi(const FSM_t fsm) {
+    const FSMI_t iter = fsm->iter;
+    return (iter ? iter->fi : NULL);
+}
+
+#define        SUFFIX_RPMORIG  ".rpmorig"
+#define        SUFFIX_RPMSAVE  ".rpmsave"
+#define        SUFFIX_RPMNEW   ".rpmnew"
+
+/** \ingroup payload
+ * Build path to file from file info, ornamented with subdir and suffix.
+ * @param fsm          file state machine data
+ * @param st           file stat info
+ * @param subdir       subdir to use (NULL disables)
+ * @param suffix       suffix to use (NULL disables)
+ * @retval             path to file
+ */
+static /*@only@*//*@null@*/ const char * fsmFsPath(/*@null@*/ const FSM_t fsm,
+       /*@null@*/ const struct stat * st,
+       /*@null@*/ const char * subdir,
+       /*@null@*/ const char * suffix)
+{
+    const char * s = NULL;
+
+    if (fsm) {
+       int nb;
+       char * t;
+       nb = strlen(fsm->dirName) +
+           (st && subdir && !S_ISDIR(st->st_mode) ? strlen(subdir) : 0) +
+           (st && suffix && !S_ISDIR(st->st_mode) ? strlen(suffix) : 0) +
+           strlen(fsm->baseName) + 1;
+       s = t = xmalloc(nb);
+       t = stpcpy(t, fsm->dirName);
+       if (st && subdir && !S_ISDIR(st->st_mode))
+           t = stpcpy(t, subdir);
+       t = stpcpy(t, fsm->baseName);
+       if (st && suffix && !S_ISDIR(st->st_mode))
+           t = stpcpy(t, suffix);
+    }
+    return s;
+}
+
+/** \ingroup payload
+ * Destroy file info iterator.
+ * @retval             NULL always
+ */
+static /*@null@*/ void * mapFreeIterator(/*@only@*//*@null@*/const void * this) {
+    return _free((void *)this);
+}
+
+/** \ingroup payload
+ */
+static void *
+mapInitIterator(/*@kept@*/ const void * this, /*@kept@*/ const void * that)
+{
+    rpmTransactionSet ts = (void *)this;
+    TFI_t fi = (void *)that;
+    FSMI_t iter = NULL;
+
+    iter = xcalloc(1, sizeof(*iter));
+    iter->ts = ts;
+    iter->fi = fi;
+    switch (fi->type) {
+    case TR_ADDED:     iter->i = 0;            break;
+    case TR_REMOVED:   iter->i = fi->fc - 1;   break;
+    }
+    iter->isave = iter->i;
+    return iter;
+}
+
+/** \ingroup payload
+ */
+static int mapNextIterator(void * this) {
+    FSMI_t iter = this;
+    const TFI_t fi = iter->fi;
+    int i = -1;
+
+    switch (fi->type) {
+    case TR_ADDED:     if (iter->i < fi->fc)   i = iter->i++;  break;
+    case TR_REMOVED:   if (iter->i >= 0)       i = iter->i--;  break;
+    }
+    iter->isave = i;
+    return i;
+}
+
+/** \ingroup payload
+ */
+typedef struct dnli_s {
+/*@dependent@*/ TFI_t fi;
+/*@only@*/ /*@null@*/ char * active;
+    int reverse;
+    int isave;
+    int i;
+} * DNLI_t;
+
+/** \ingroup payload
+ */
+static /*@null@*/ void * dnlFreeIterator(/*@only@*//*@null@*/ const void * this)
+{
+    if (this) {
+       DNLI_t dnli = (void *)this;
+       if (dnli->active) free(dnli->active);
+    }
+    return _free(this);
+}
+
+/** \ingroup payload
+ */
+static inline int dnlCount(const DNLI_t dnli) {
+    return (dnli ? dnli->fi->dc : 0);
+}
+
+/** \ingroup payload
+ */
+static inline int dnlIndex(const DNLI_t dnli) {
+    return (dnli ? dnli->isave : -1);
+}
+
+/** \ingroup payload
+ * @param fsm          file state machine data
+ */
+static /*@only@*/ void * dnlInitIterator(const FSM_t fsm, int reverse)
+{
+    TFI_t fi = fsmGetFi(fsm);
+    DNLI_t dnli;
+    int i, j;
+
+    if (fi == NULL)
+       return NULL;
+    dnli = xcalloc(1, sizeof(*dnli));
+    dnli->fi = fi;
+    dnli->reverse = reverse;
+    dnli->i = (reverse ? fi->dc : 0);
+
+    if (fi->dc) {
+       dnli->active = xcalloc(fi->dc, sizeof(*dnli->active));
+
+       /* Identify parent directories not skipped. */
+       for (i = 0; i < fi->fc; i++)
+            if (!XFA_SKIPPING(fi->actions[i])) dnli->active[fi->dil[i]] = 1;
+
+       /* Exclude parent directories that are explicitly included. */
+       for (i = 0; i < fi->fc; i++) {
+           int dil, dnlen, bnlen;
+
+           if (!S_ISDIR(fi->fmodes[i]))
+               continue;
+
+           dil = fi->dil[i];
+           dnlen = strlen(fi->dnl[dil]);
+           bnlen = strlen(fi->bnl[i]);
+
+           for (j = 0; j < fi->dc; j++) {
+               const char * dnl;
+               int jlen;
+
+               if (!dnli->active[j] || j == dil) continue;
+               dnl = fi->dnl[j];
+               jlen = strlen(dnl);
+               if (jlen != (dnlen+bnlen+1)) continue;
+               if (strncmp(dnl, fi->dnl[dil], dnlen)) continue;
+               if (strncmp(dnl+dnlen, fi->bnl[i], bnlen)) continue;
+               if (dnl[dnlen+bnlen] != '/' || dnl[dnlen+bnlen+1] != '\0')
+                   continue;
+               /* This directory is included in the package. */
+               dnli->active[j] = 0;
+               break;
+           }
+       }
+
+       /* Print only once per package. */
+       if (!reverse) {
+           j = 0;
+           for (i = 0; i < fi->dc; i++) {
+               if (!dnli->active[i]) continue;
+               if (j == 0) {
+                   j = 1;
+                   rpmMessage(RPMMESS_DEBUG,
+       _("========= Directories not explictly included in package:\n"));
+               }
+               rpmMessage(RPMMESS_DEBUG, _("%9d %s\n"), i, fi->dnl[i]);
+           }
+           if (j)
+               rpmMessage(RPMMESS_DEBUG, "=========\n");
+       }
+    }
+    return dnli;
+}
+
+/** \ingroup payload
+ */
+static const char * dnlNextIterator(/*@null@*/ DNLI_t dnli) {
+    const char * dn = NULL;
+
+    if (dnli && dnli->active) {
+       TFI_t fi = dnli->fi;
+       int i = -1;
+
+       do {
+           i = (!dnli->reverse ? dnli->i++ : --dnli->i);
+       } while (i >= 0 && i < fi->dc && !dnli->active[i]);
+
+       if (i >= 0 && i < fi->dc)
+           dn = fi->dnl[i];
+       else
+           i = -1;
+       dnli->isave = i;
+    }
+    return dn;
+}
+
+/** \ingroup payload
+ */
+static int cpioStrCmp(const void * a, const void * b) {
+    const char * afn = *(const char **)a;
+    const char * bfn = *(const char **)b;
+
+    /* Match rpm-4.0 payloads with ./ prefixes. */
+    if (afn[0] == '.' && afn[1] == '/')        afn += 2;
+    if (bfn[0] == '.' && bfn[1] == '/')        bfn += 2;
+
+    /* If either path is absolute, make it relative. */
+    if (afn[0] == '/') afn += 1;
+    if (bfn[0] == '/') bfn += 1;
+
+    return strcmp(afn, bfn);
+}
+
+/** \ingroup payload
+ */
+static int mapFind(void * this, const char * fsmPath) {
+    FSMI_t iter = this;
+    const TFI_t fi = iter->fi;
+    int ix = -1;
+
+    if (fi) {
+       const char ** p;
+
+       p = bsearch(&fsmPath, fi->apath, fi->fc, sizeof(fsmPath), cpioStrCmp);
+       if (p == NULL) {
+           fprintf(stderr, "*** not mapped %s\n", fsmPath);
+       } else {
+           iter->i = p - fi->apath;
+           ix = mapNextIterator(iter);
+       }
+    }
+    return ix;
+}
+
+/** \ingroup payload
+ * Save hard link in chain.
+ * @param fsm          file state machine data
+ * @return             Is chain only partially filled?
+ */
+static int saveHardLink(FSM_t fsm)
+{
+    struct stat * st = &fsm->sb;
+    int rc = 0;
+    int ix = -1;
+    int j;
+
+    /* Find hard link set. */
+    for (fsm->li = fsm->links; fsm->li; fsm->li = fsm->li->next) {
+       if (fsm->li->inode == st->st_ino && fsm->li->dev == st->st_dev)
+           break;
+    }
+
+    /* New hard link encountered, add new link to set. */
+    if (fsm->li == NULL) {
+       fsm->li = xcalloc(1, sizeof(*fsm->li));
+       fsm->li->next = NULL;
+       fsm->li->nlink = st->st_nlink;
+       fsm->li->dev = st->st_dev;
+       fsm->li->inode = st->st_ino;
+       fsm->li->linkIndex = -1;
+       fsm->li->createdPath = -1;
+
+       fsm->li->filex = xcalloc(st->st_nlink, sizeof(fsm->li->filex[0]));
+       memset(fsm->li->filex, -1, (st->st_nlink * sizeof(fsm->li->filex[0])));
+       fsm->li->nsuffix = xcalloc(st->st_nlink, sizeof(*fsm->li->nsuffix));
+
+       if (fsm->goal == FSM_PKGBUILD)
+           fsm->li->linksLeft = st->st_nlink;
+       if (fsm->goal == FSM_PKGINSTALL)
+           fsm->li->linksLeft = 0;
+
+       fsm->li->next = fsm->links;
+       fsm->links = fsm->li;
+    }
+
+    if (fsm->goal == FSM_PKGBUILD) --fsm->li->linksLeft;
+    fsm->li->filex[fsm->li->linksLeft] = fsm->ix;
+    /*@-observertrans@*/
+    fsm->li->nsuffix[fsm->li->linksLeft] = fsm->nsuffix;
+    /*@=observertrans@*/
+    if (fsm->goal == FSM_PKGINSTALL) fsm->li->linksLeft++;
+
+#if 0
+fprintf(stderr, "*** %p link[%d:%d] %d filex %d %s\n", fsm->li, fsm->li->linksLeft, st->st_nlink, (int)st->st_size, fsm->li->filex[fsm->li->linksLeft], fsm->li->files[fsm->li->linksLeft]);
+#endif
+
+    if (fsm->goal == FSM_PKGBUILD)
+       return (fsm->li->linksLeft > 0);
+
+    if (fsm->goal != FSM_PKGINSTALL)
+       return 0;
+
+    if (!(st->st_size || fsm->li->linksLeft == st->st_nlink))
+       return 1;
+
+    /* Here come the bits, time to choose a non-skipped file name. */
+    {  TFI_t fi = fsmGetFi(fsm);
+
+       for (j = fsm->li->linksLeft - 1; j >= 0; j--) {
+           ix = fsm->li->filex[j];
+           if (ix < 0 || XFA_SKIPPING(fi->actions[ix]))
+               continue;
+           break;
+       }
+    }
+
+    /* Are all links skipped or not encountered yet? */
+    if (ix < 0 || j < 0)
+       return 1;       /* XXX W2DO? */
+
+    /* Save the non-skipped file name and map index. */
+    fsm->li->linkIndex = j;
+    fsm->path = _free(fsm->path);
+    fsm->ix = ix;
+    rc = fsmStage(fsm, FSM_MAP);
+    return rc;
+}
+
+/** \ingroup payload
+ * Destroy set of hard links.
+ * @param li           set of hard links
+ */
+static /*@null@*/ void * freeHardLink(/*@only@*/ /*@null@*/ struct hardLink * li)
+{
+    if (li) {
+       li->nsuffix = _free(li->nsuffix);       /* XXX elements are shared */
+       li->filex = _free(li->filex);
+    }
+    return _free(li);
+}
+
+FSM_t newFSM(void) {
+    FSM_t fsm = xcalloc(1, sizeof(*fsm));
+    return fsm;
+}
+
+FSM_t freeFSM(FSM_t fsm)
+{
+    if (fsm) {
+       if (fsm->path)  free((void *)fsm->path);
+       while ((fsm->li = fsm->links) != NULL) {
+           fsm->links = fsm->li->next;
+           fsm->li->next = NULL;
+           fsm->li = freeHardLink(fsm->li);
+       }
+       fsm->dnlx = _free(fsm->dnlx);
+       fsm->ldn = _free(fsm->ldn);
+       fsm->iter = mapFreeIterator(fsm->iter);
+    }
+    return _free(fsm);
+}
+
+int fsmSetup(FSM_t fsm, fileStage goal,
+               const rpmTransactionSet ts, const TFI_t fi, FD_t cfd,
+               unsigned int * archiveSize, const char ** failedFile)
+{
+    size_t pos = 0;
+    int rc;
+
+    fsm->goal = goal;
+    if (cfd) {
+       fsm->cfd = fdLink(cfd, "persist (fsm)");
+       pos = fdGetCpioPos(fsm->cfd);
+       fdSetCpioPos(fsm->cfd, 0);
+    }
+    fsm->iter = mapInitIterator(ts, fi);
+
+    if (fsm->goal == FSM_PKGINSTALL) {
+       if (ts && ts->notify) {
+           (void)ts->notify(fi->h, RPMCALLBACK_INST_START, 0, fi->archiveSize,
+               (fi->ap ? fi->ap->key : NULL), ts->notifyData);
+       }
+    }
+
+    fsm->archiveSize = archiveSize;
+    if (fsm->archiveSize)
+       *fsm->archiveSize = 0;
+    fsm->failedFile = failedFile;
+    if (fsm->failedFile)
+       *fsm->failedFile = NULL;
+
+    memset(fsm->sufbuf, 0, sizeof(fsm->sufbuf));
+    if (fsm->goal == FSM_PKGINSTALL) {
+       if (ts->id > 0)
+           sprintf(fsm->sufbuf, ";%08x", (unsigned)ts->id);
+    }
+
+    rc = fsm->rc = 0;
+    rc = fsmStage(fsm, FSM_CREATE);
+
+    rc = fsmStage(fsm, fsm->goal);
+
+    if (!rc && fsm->archiveSize)
+       *fsm->archiveSize = (fdGetCpioPos(fsm->cfd) - pos);
+
+   return rc;
+}
+
+int fsmTeardown(FSM_t fsm) {
+    int rc = fsm->rc;
+
+    if (!rc)
+       rc = fsmStage(fsm, FSM_DESTROY);
+
+    fsm->iter = mapFreeIterator(fsm->iter);
+    if (fsm->cfd) {
+       fsm->cfd = fdFree(fsm->cfd, "persist (fsm)");
+       fsm->cfd = NULL;
+    }
+    fsm->failedFile = NULL;
+    return rc;
+}
+
+int fsmMapPath(FSM_t fsm)
+{
+    TFI_t fi = fsmGetFi(fsm);  /* XXX const except for fstates */
+    int rc = 0;
+    int i;
+
+    fsm->osuffix = NULL;
+    fsm->nsuffix = NULL;
+    fsm->astriplen = 0;
+    fsm->action = FA_UNKNOWN;
+    fsm->mapFlags = 0;
+
+    i = fsm->ix;
+    if (fi && i >= 0 && i < fi->fc) {
+
+       fsm->astriplen = fi->astriplen;
+       fsm->action = (fi->actions ? fi->actions[i] : fi->action);
+       fsm->fflags = (fi->fflags ? fi->fflags[i] : fi->flags);
+       fsm->mapFlags = (fi->fmapflags ? fi->fmapflags[i] : fi->mapflags);
+
+       /* src rpms have simple base name in payload. */
+       fsm->archivePath =
+               (fi->apath ? fi->apath[i] + fi->striplen : fi->bnl[i]);
+       fsm->dirName = fi->dnl[fi->dil[i]];
+       fsm->baseName = fi->bnl[i];
+
+       switch (fsm->action) {
+       case FA_SKIP:
+           break;
+       case FA_SKIPMULTILIB:   /* XXX RPMFILE_STATE_MULTILIB? */
+fprintf(stderr, "*** %s:%s %s\n", fiTypeString(fi), fileActionString(fsm->action), (fsm->path ? fsm->path : ""));
+           break;
+       case FA_UNKNOWN:
+fprintf(stderr, "*** %s:%s %s\n", fiTypeString(fi), fileActionString(fsm->action), (fsm->path ? fsm->path : ""));
+           break;
+
+       case FA_CREATE:
+           assert(fi->type == TR_ADDED);
+           break;
+
+       case FA_SKIPNSTATE:
+fprintf(stderr, "*** %s:%s %s\n", fiTypeString(fi), fileActionString(fsm->action), (fsm->path ? fsm->path : ""));
+           if (fi->type == TR_ADDED)
+               fi->fstates[i] = RPMFILE_STATE_NOTINSTALLED;
+           break;
+
+       case FA_SKIPNETSHARED:
+           if (fi->type == TR_ADDED)
+               fi->fstates[i] = RPMFILE_STATE_NETSHARED;
+           break;
+
+       case FA_BACKUP:
+fprintf(stderr, "*** %s:%s %s\n", fiTypeString(fi), fileActionString(fsm->action), (fsm->path ? fsm->path : ""));
+           switch (fi->type) {
+           case TR_ADDED:
+               fsm->osuffix = SUFFIX_RPMORIG;
+               break;
+           case TR_REMOVED:
+               fsm->osuffix = SUFFIX_RPMSAVE;
+               break;
+           }
+           break;
+
+       case FA_ALTNAME:
+           assert(fi->type == TR_ADDED);
+           fsm->nsuffix = SUFFIX_RPMNEW;
+           break;
+
+       case FA_SAVE:
+fprintf(stderr, "*** %s:%s %s\n", fiTypeString(fi), fileActionString(fsm->action), (fsm->path ? fsm->path : ""));
+           assert(fi->type == TR_ADDED);
+           fsm->osuffix = SUFFIX_RPMSAVE;
+           break;
+       case FA_ERASE:
+           assert(fi->type == TR_REMOVED);
+           break;
+       default:
+fprintf(stderr, "*** %s:%s %s\n", fiTypeString(fi), fileActionString(fsm->action), (fsm->path ? fsm->path : ""));
+           break;
+       }
+
+       if ((fsm->mapFlags & CPIO_MAP_PATH) || fsm->nsuffix) {
+           const struct stat * st = &fsm->sb;
+           fsm->path = _free(fsm->path);
+           fsm->path = fsmFsPath(fsm, st, fsm->subdir,
+               (fsm->suffix ? fsm->suffix : fsm->nsuffix));
+       }
+    }
+    return rc;
+}
+
+int fsmMapAttrs(FSM_t fsm)
+{
+    struct stat * st = &fsm->sb;
+    TFI_t fi = fsmGetFi(fsm);
+    int i = fsm->ix;
+
+    if (fi && i >= 0 && i < fi->fc) {
+       mode_t perms =
+               (S_ISDIR(st->st_mode) ? fi->dperms : fi->fperms);
+       mode_t finalMode =
+               (fi->fmodes ? fi->fmodes[i] : perms);
+       uid_t finalUid =
+               (fi->fuids ? fi->fuids[i] : fi->uid); /* XXX chmod u-s */
+       gid_t finalGid =
+               (fi->fgids ? fi->fgids[i] : fi->gid); /* XXX chmod g-s */
+
+       if (fsm->mapFlags & CPIO_MAP_MODE)
+           st->st_mode = (st->st_mode & S_IFMT) | finalMode;
+       if (fsm->mapFlags & CPIO_MAP_UID)
+           st->st_uid = finalUid;
+       if (fsm->mapFlags & CPIO_MAP_GID)
+           st->st_gid = finalGid;
+
+       fsm->fmd5sum = (fi->fmd5s ? fi->fmd5s[i] : NULL);
+
+    }
+    return 0;
+}
+
+/** \ingroup payload
+ * Create file from payload stream.
+ * @todo Legacy: support brokenEndian MD5 checks?
+ * @param fsm          file state machine data
+ * @return             0 on success
+ */
+static int expandRegular(FSM_t fsm)
+               /*@modifies fileSystem, fsm @*/
+{
+    const char * fmd5sum;
+    const struct stat * st = &fsm->sb;
+    int left = st->st_size;
+    int rc = 0;
+
+    rc = fsmStage(fsm, FSM_WOPEN);
+    if (rc)
+       goto exit;
+
+    /* XXX md5sum's will break on repackaging that includes modified files. */
+    fmd5sum = fsm->fmd5sum;
+
+    /* XXX This doesn't support brokenEndian checks. */
+    if (st->st_size > 0 && fmd5sum)
+       fdInitMD5(fsm->wfd, 0);
+
+    while (left) {
+
+       fsm->wrlen = (left > fsm->wrsize ? fsm->wrsize : left);
+       rc = fsmStage(fsm, FSM_DREAD);
+       if (rc)
+           goto exit;
+
+       rc = fsmStage(fsm, FSM_WRITE);
+       if (rc)
+           goto exit;
+
+       left -= fsm->wrnb;
+
+       /* don't call this with fileSize == fileComplete */
+       if (!rc && left)
+           (void) fsmStage(fsm, FSM_NOTIFY);
+    }
+
+    if (st->st_size > 0 && fmd5sum) {
+       const char * md5sum = NULL;
+
+       Fflush(fsm->wfd);
+       fdFiniMD5(fsm->wfd, (void **)&md5sum, NULL, 1);
+
+       if (md5sum == NULL) {
+           rc = CPIOERR_MD5SUM_MISMATCH;
+       } else {
+           if (strcmp(md5sum, fmd5sum))
+               rc = CPIOERR_MD5SUM_MISMATCH;
+           md5sum = _free(md5sum);
+       }
+    }
+
+exit:
+    (void) fsmStage(fsm, FSM_WCLOSE);
+    return rc;
+}
+
+/** \ingroup payload
+ * Write next item to payload stream.
+ * @param fsm          file state machine data
+ * @param writeData    should data be written?
+ * @return             0 on success
+ */
+static int writeFile(FSM_t fsm, int writeData)
+       /*@modifies fsm @*/
+{
+    const char * path = fsm->path;
+    const char * opath = fsm->opath;
+    struct stat * st = &fsm->sb;
+    struct stat * ost = &fsm->osb;
+    size_t pos = fdGetCpioPos(fsm->cfd);
+    int left;
+    int rc;
+
+    st->st_size = (writeData ? ost->st_size : 0);
+    if (S_ISDIR(st->st_mode)) {
+       st->st_size = 0;
+    } else if (S_ISLNK(st->st_mode)) {
+       /*
+        * While linux puts the size of a symlink in the st_size field,
+        * I don't think that's a specified standard.
+        */
+       /* XXX NUL terminated result in fsm->rdbuf, len in fsm->rdnb. */
+       rc = fsmStage(fsm, FSM_READLINK);
+       if (rc) goto exit;
+       st->st_size = fsm->rdnb;
+    }
+
+    if (fsm->mapFlags & CPIO_MAP_PATH)
+       fsm->path = fsm->archivePath;
+    rc = fsmStage(fsm, FSM_HWRITE);
+    fsm->path = path;
+    if (rc) goto exit;
+
+    if (writeData && S_ISREG(st->st_mode)) {
+#if HAVE_MMAP
+       char * rdbuf = NULL;
+       void * mapped = (void *)-1;
+       size_t nmapped;
+#endif
+
+       rc = fsmStage(fsm, FSM_ROPEN);
+       if (rc) goto exit;
+
+       /* XXX unbuffered mmap generates *lots* of fdio debugging */
+#if HAVE_MMAP
+       nmapped = 0;
+       mapped = mmap(NULL, st->st_size, PROT_READ, MAP_SHARED, Fileno(fsm->rfd), 0);
+       if (mapped != (void *)-1) {
+           rdbuf = fsm->rdbuf;
+           fsm->rdbuf = (char *) mapped;
+           fsm->rdlen = nmapped = st->st_size;
+       }
+#endif
+
+       left = st->st_size;
+
+       while (left) {
+#if HAVE_MMAP
+         if (mapped != (void *)-1) {
+           fsm->rdnb = nmapped;
+         } else
+#endif
+         {
+           fsm->rdlen = (left > fsm->rdsize ? fsm->rdsize : left),
+           rc = fsmStage(fsm, FSM_READ);
+           if (rc) goto exit;
+         }
+
+           /* XXX DWRITE uses rdnb for I/O length. */
+           rc = fsmStage(fsm, FSM_DWRITE);
+           if (rc) goto exit;
+
+           left -= fsm->wrnb;
+       }
+
+#if HAVE_MMAP
+       if (mapped != (void *)-1) {
+           /*@-noeffect@*/ munmap(mapped, nmapped) /*@=noeffect@*/;
+           fsm->rdbuf = rdbuf;
+       }
+#endif
+
+    } else if (writeData && S_ISLNK(st->st_mode)) {
+       /* XXX DWRITE uses rdnb for I/O length. */
+       rc = fsmStage(fsm, FSM_DWRITE);
+       if (rc) goto exit;
+    }
+
+    rc = fsmStage(fsm, FSM_PAD);
+    if (rc) goto exit;
+
+    {  const rpmTransactionSet ts = fsmGetTs(fsm);
+       TFI_t fi = fsmGetFi(fsm);
+       if (ts && fi && ts->notify) {
+           size_t size = (fdGetCpioPos(fsm->cfd) - pos);
+           (void)ts->notify(fi->h, RPMCALLBACK_INST_PROGRESS, size, size,
+                       (fi->ap ? fi->ap->key : NULL), ts->notifyData);
+       }
+    }
+
+    rc = 0;
+
+exit:
+    if (fsm->rfd)
+       (void) fsmStage(fsm, FSM_RCLOSE);
+    fsm->opath = opath;
+    fsm->path = path;
+    return rc;
+}
+
+/** \ingroup payload
+ * Write set of linked files to payload stream.
+ * @param fsm          file state machine data
+ * @return             0 on success
+ */
+static int writeLinkedFile(FSM_t fsm)
+       /*@modifies fsm @*/
+{
+    const char * path = fsm->path;
+    const char * nsuffix = fsm->nsuffix;
+    int iterIndex = fsm->ix;
+    int ec = 0;
+    int rc;
+    int i;
+
+    fsm->path = NULL;
+    fsm->nsuffix = NULL;
+    fsm->ix = -1;
+
+    for (i = fsm->li->nlink - 1; i >= 0; i--) {
+       if (fsm->li->filex[i] < 0) continue;
+
+       fsm->ix = fsm->li->filex[i];
+       rc = fsmStage(fsm, FSM_MAP);
+
+       /* Write data after last link. */
+       rc = writeFile(fsm, (i == 0));
+       if (rc && fsm->failedFile && *fsm->failedFile == NULL) {
+           ec = rc;
+           *fsm->failedFile = xstrdup(fsm->path);
+       }
+
+       fsm->path = _free(fsm->path);
+       fsm->li->filex[i] = -1;
+    }
+
+    fsm->ix = iterIndex;
+    fsm->nsuffix = nsuffix;
+    fsm->path = path;
+    return ec;
+}
+
+/** \ingroup payload
+ * @param fsm          file state machine data
+ */
+static int fsmMakeLinks(FSM_t fsm)
+{
+    const char * path = fsm->path;
+    const char * opath = fsm->opath;
+    const char * nsuffix = fsm->nsuffix;
+    int iterIndex = fsm->ix;
+    int ec = 0;
+    int rc;
+    int i;
+
+    fsm->path = NULL;
+    fsm->opath = NULL;
+    fsm->nsuffix = NULL;
+    fsm->ix = -1;
+
+    fsm->ix = fsm->li->filex[fsm->li->createdPath];
+    rc = fsmStage(fsm, FSM_MAP);
+    fsm->opath = fsm->path;
+    fsm->path = NULL;
+    for (i = 0; i < fsm->li->nlink; i++) {
+       if (fsm->li->filex[i] < 0) continue;
+       if (i == fsm->li->createdPath) continue;
+
+       fsm->ix = fsm->li->filex[i];
+       rc = fsmStage(fsm, FSM_MAP);
+       rc = fsmStage(fsm, FSM_VERIFY);
+       if (!rc) continue;
+       if (rc != CPIOERR_LSTAT_FAILED) break;
+
+       /* XXX link(fsm->opath, fsm->path) */
+       rc = fsmStage(fsm, FSM_LINK);
+       if (rc && fsm->failedFile && *fsm->failedFile == NULL) {
+           ec = rc;
+           *fsm->failedFile = xstrdup(fsm->path);
+       }
+
+       fsm->li->linksLeft--;
+    }
+    fsm->opath = _free(fsm->opath);
+
+    fsm->ix = iterIndex;
+    fsm->nsuffix = nsuffix;
+    fsm->path = path;
+    fsm->opath = opath;
+    return ec;
+}
+
+/** \ingroup payload
+ * @param fsm          file state machine data
+ */
+static int fsmCommitLinks(FSM_t fsm)
+{
+    const char * path = fsm->path;
+    const char * nsuffix = fsm->nsuffix;
+    int iterIndex = fsm->ix;
+    struct stat * st = &fsm->sb;
+    int rc = 0;
+    int i;
+
+    fsm->path = NULL;
+    fsm->nsuffix = NULL;
+    fsm->ix = -1;
+
+    for (fsm->li = fsm->links; fsm->li; fsm->li = fsm->li->next) {
+       if (fsm->li->inode == st->st_ino && fsm->li->dev == st->st_dev)
+           break;
+    }
+
+    for (i = 0; i < fsm->li->nlink; i++) {
+       if (fsm->li->filex[i] < 0) continue;
+       fsm->ix = fsm->li->filex[i];
+       rc = fsmStage(fsm, FSM_MAP);
+       rc = fsmStage(fsm, FSM_COMMIT);
+       fsm->path = _free(fsm->path);
+       fsm->li->filex[i] = -1;
+    }
+
+    fsm->ix = iterIndex;
+    fsm->nsuffix = nsuffix;
+    fsm->path = path;
+    return rc;
+}
+
+int fsmStage(FSM_t fsm, fileStage stage)
+{
+#ifdef UNUSED
+    fileStage prevStage = fsm->stage;
+    const char * const prev = fileStageString(prevStage);
+#endif
+    static int modulo = 4;
+    const char * const cur = fileStageString(stage);
+    struct stat * st = &fsm->sb;
+    struct stat * ost = &fsm->osb;
+    int saveerrno = errno;
+    int rc = fsm->rc;
+    int left;
+    int i;
+
+    if (stage & FSM_DEAD) {
+       /* do nothing */
+    } else if (stage & FSM_INTERNAL) {
+       if (_fsm_debug && !(stage & FSM_SYSCALL))
+           rpmMessage(RPMMESS_DEBUG, " %8s %06o%3d (%4d,%4d)%10d %s %s\n",
+               cur,
+               st->st_mode, st->st_nlink, st->st_uid, st->st_gid, st->st_size,
+               (fsm->path ? fsm->path : ""),
+               ((fsm->action != FA_UNKNOWN && fsm->action != FA_CREATE)
+                       ? fileActionString(fsm->action) : ""));
+    } else {
+       fsm->stage = stage;
+       if (_fsm_debug || !(stage & FSM_VERBOSE))
+           rpmMessage(RPMMESS_DEBUG, "%-8s  %06o%3d (%4d,%4d)%10d %s %s\n",
+               cur,
+               st->st_mode, st->st_nlink, st->st_uid, st->st_gid, st->st_size,
+               (fsm->path ? fsm->path + fsm->astriplen : ""),
+               ((fsm->action != FA_UNKNOWN && fsm->action != FA_CREATE)
+                       ? fileActionString(fsm->action) : ""));
+    }
+
+    switch (stage) {
+    case FSM_UNKNOWN:
+       break;
+    case FSM_PKGINSTALL:
+       while (1) {
+           /* Clean fsm, free'ing memory. Read next archive header. */
+           rc = fsmStage(fsm, FSM_INIT);
+
+           /* Exit on end-of-payload. */
+           if (rc == CPIOERR_HDR_TRAILER) {
+               rc = 0;
+               break;
+           }
+
+           /* Exit on error. */
+           if (rc) {
+               fsm->postpone = 1;
+               (void) fsmStage(fsm, FSM_UNDO);
+               break;
+           }
+
+           /* Extract file from archive. */
+           rc = fsmStage(fsm, FSM_PROCESS);
+           if (rc) {
+               (void) fsmStage(fsm, FSM_UNDO);
+               break;
+           }
+
+           /* Notify on success. */
+           (void) fsmStage(fsm, FSM_NOTIFY);
+
+           if (fsmStage(fsm, FSM_FINI))
+               break;
+       }
+       break;
+    case FSM_PKGERASE:
+    case FSM_PKGCOMMIT:
+       while (1) {
+           /* Clean fsm, free'ing memory. */
+           rc = fsmStage(fsm, FSM_INIT);
+
+           /* Exit on end-of-payload. */
+           if (rc == CPIOERR_HDR_TRAILER) {
+               rc = 0;
+               break;
+           }
+
+           /* Rename/erase next item. */
+           if (fsmStage(fsm, FSM_FINI))
+               break;
+       }
+       break;
+    case FSM_PKGBUILD:
+       while (1) {
+
+           rc = fsmStage(fsm, FSM_INIT);
+
+           /* Exit on end-of-payload. */
+           if (rc == CPIOERR_HDR_TRAILER) {
+               rc = 0;
+               break;
+           }
+
+           /* Exit on error. */
+           if (rc) {
+               fsm->postpone = 1;
+               (void) fsmStage(fsm, FSM_UNDO);
+               break;
+           }
+
+           /* Copy file into archive. */
+           rc = fsmStage(fsm, FSM_PROCESS);
+           if (rc) {
+               (void) fsmStage(fsm, FSM_UNDO);
+               break;
+           }
+
+           if (fsmStage(fsm, FSM_FINI))
+               break;
+       }
+
+       if (!rc)
+           rc = fsmStage(fsm, FSM_TRAILER);
+       break;
+    case FSM_CREATE:
+       {   rpmTransactionSet ts = fsmGetTs(fsm);
+#define        _tsmask (RPMTRANS_FLAG_PKGCOMMIT | RPMTRANS_FLAG_COMMIT)
+           fsm->commit = ((ts && (ts->transFlags & _tsmask) &&
+                       fsm->goal != FSM_PKGCOMMIT) ? 0 : 1);
+#undef _tsmask
+       }
+       fsm->path = _free(fsm->path);
+       fsm->opath = _free(fsm->opath);
+       fsm->dnlx = _free(fsm->dnlx);
+
+       fsm->ldn = _free(fsm->ldn);
+       fsm->ldnalloc = fsm->ldnlen = 0;
+
+       fsm->rdsize = fsm->wrsize = 0;
+       fsm->rdbuf = fsm->rdb = _free(fsm->rdb);
+       fsm->wrbuf = fsm->wrb = _free(fsm->wrb);
+       if (fsm->goal == FSM_PKGINSTALL || fsm->goal == FSM_PKGBUILD) {
+           fsm->rdsize = 8 * BUFSIZ;
+           fsm->rdbuf = fsm->rdb = xmalloc(fsm->rdsize);
+           fsm->wrsize = 8 * BUFSIZ;
+           fsm->wrbuf = fsm->wrb = xmalloc(fsm->wrsize);
+       }
+
+       fsm->mkdirsdone = 0;
+       fsm->ix = -1;
+       fsm->links = NULL;
+       fsm->li = NULL;
+       errno = 0;      /* XXX get rid of EBADF */
+
+       /* Detect and create directories not explicitly in package. */
+       if (fsm->goal == FSM_PKGINSTALL) {
+           rc = fsmStage(fsm, FSM_MKDIRS);
+           if (!rc) fsm->mkdirsdone = 1;
+       }
+
+       break;
+    case FSM_INIT:
+       fsm->path = _free(fsm->path);
+       fsm->postpone = 0;
+       fsm->diskchecked = fsm->exists = 0;
+       fsm->subdir = NULL;
+       fsm->suffix = (fsm->sufbuf[0] != '\0' ? fsm->sufbuf : NULL);
+       fsm->action = FA_UNKNOWN;
+       fsm->osuffix = NULL;
+       fsm->nsuffix = NULL;
+
+       if (fsm->goal == FSM_PKGINSTALL) {
+           /* Read next header from payload, checking for end-of-payload. */
+           rc = fsmStage(fsm, FSM_NEXT);
+       }
+       if (rc) break;
+
+       /* Identify mapping index. */
+       fsm->ix = ((fsm->goal == FSM_PKGINSTALL)
+               ? mapFind(fsm->iter, fsm->path) : mapNextIterator(fsm->iter));
+
+       /* On non-install, detect end-of-loop. */
+       if (fsm->goal != FSM_PKGINSTALL && fsm->ix < 0) {
+           rc = CPIOERR_HDR_TRAILER;
+           break;
+       }
+
+       /* On non-install, mode must be known so that dirs don't get suffix. */
+       if (fsm->goal != FSM_PKGINSTALL) {
+           TFI_t fi = fsmGetFi(fsm);
+           st->st_mode = fi->fmodes[fsm->ix];
+       }
+
+       /* Generate file path. */
+       rc = fsmStage(fsm, FSM_MAP);
+       if (rc) break;
+
+       /* Perform lstat/stat for disk file. */
+       rc = fsmStage(fsm, (!(fsm->mapFlags & CPIO_FOLLOW_SYMLINKS)
+                       ? FSM_LSTAT : FSM_STAT));
+       if (rc == CPIOERR_LSTAT_FAILED && errno == ENOENT) {
+           errno = saveerrno;
+           rc = 0;
+           fsm->exists = 0;
+       } else if (rc == 0) {
+           fsm->exists = 1;
+       }
+       fsm->diskchecked = 1;
+       if (rc) break;
+
+       /* On non-install, the disk file stat is what's remapped. */
+       if (fsm->goal != FSM_PKGINSTALL)
+           *st = *ost;                 /* structure assignment */
+
+       /* Remap file perms, owner, and group. */
+       rc = fsmMapAttrs(fsm);
+       if (rc) break;
+
+       fsm->postpone = XFA_SKIPPING(fsm->action);
+       if (fsm->goal == FSM_PKGINSTALL || fsm->goal == FSM_PKGBUILD) {
+           if (!S_ISDIR(st->st_mode) && st->st_nlink > 1)
+               fsm->postpone = saveHardLink(fsm);
+       }
+       break;
+    case FSM_PRE:
+       break;
+    case FSM_MAP:
+       rc = fsmMapPath(fsm);
+       break;
+    case FSM_MKDIRS:
+       {   const char * path = fsm->path;
+           mode_t st_mode = st->st_mode;
+           void * dnli = dnlInitIterator(fsm, 0);
+           char * dn = fsm->rdbuf;
+           int dc = dnlCount(dnli);
+
+           fsm->path = NULL;
+           dn[0] = '\0';
+           fsm->dnlx = (dc ? xcalloc(dc, sizeof(*fsm->dnlx)) : NULL);
+           while ((fsm->path = dnlNextIterator(dnli)) != NULL) {
+               int dnlen = strlen(fsm->path);
+               char * te;
+
+               dc = dnlIndex(dnli);
+               if (dc < 0) continue;
+               fsm->dnlx[dc] = dnlen;
+               if (dnlen <= 1)
+                   continue;
+               if (dnlen <= fsm->ldnlen && !strcmp(fsm->path, fsm->ldn))
+                   continue;
+
+               /* Copy to avoid const on fsm->path. */
+               (void) stpcpy(dn, fsm->path);
+               fsm->path = dn;
+
+               /* Initial mode for created dirs is 0700 */
+               st->st_mode &= ~07777;          /* XXX abuse st->st_mode */
+               st->st_mode |=  00700;
+
+               /* Assume '/' directory, otherwise "mkdir -p" */
+               for (i = 1, te = dn + 1; *te; te++, i++) {
+                   if (*te != '/') continue;
+
+                   *te = '\0';
+
+                   /* Already validated? */
+                   if (i < fsm->ldnlen &&
+                       (fsm->ldn[i] == '/' || fsm->ldn[i] == '\0') &&
+                       !strncmp(fsm->path, fsm->ldn, i))
+                   {
+                       *te = '/';
+                       /* Move pre-existing path marker forward. */
+                       fsm->dnlx[dc] = (te - dn);
+                       continue;
+                   }
+
+                   /* Validate next component of path. */
+                   rc = fsmStage(fsm, FSM_LSTAT);
+                   *te = '/';
+
+                   /* Directory already exists? */
+                   if (rc == 0 && S_ISDIR(ost->st_mode)) {
+                       /* Move pre-existing path marker forward. */
+                       fsm->dnlx[dc] = (te - dn);
+                   } else if (rc == CPIOERR_LSTAT_FAILED) {
+                       TFI_t fi = fsmGetFi(fsm);
+                       mode_t st_mode = st->st_mode;
+                       *te = '\0';
+                       st->st_mode = S_IFDIR | (fi->dperms & 07777);
+                       rc = fsmStage(fsm, FSM_MKDIR);
+                       if (!rc)
+                           rpmMessage(RPMMESS_WARNING,
+                               _("%s directory created with perms %04o.\n"),
+                               fsm->path, (st->st_mode & 07777));
+                       *te = '/';
+                       st->st_mode = st_mode;
+                   }
+                   if (rc) break;
+               }
+               if (rc) break;
+
+               /* Save last validated path. */
+               if (fsm->ldnalloc < (dnlen + 1)) {
+                   fsm->ldnalloc = dnlen + 100;
+                   fsm->ldn = xrealloc(fsm->ldn, fsm->ldnalloc);
+               }
+               strcpy(fsm->ldn, fsm->path);
+               fsm->ldnlen = dnlen;
+           }
+           dnli = dnlFreeIterator(dnli);
+           fsm->path = path;
+           st->st_mode = st_mode;              /* XXX restore st->st_mode */
+       }
+       break;
+    case FSM_RMDIRS:
+       if (fsm->dnlx) {
+           const char * path = fsm->path;
+           void * dnli = dnlInitIterator(fsm, 1);
+           char * dn = fsm->rdbuf;
+           int dc = dnlCount(dnli);
+
+           fsm->path = NULL;
+           dn[0] = '\0';
+           while ((fsm->path = dnlNextIterator(dnli)) != NULL) {
+               int dnlen = strlen(fsm->path);
+               char * te;
+
+               dc = dnlIndex(dnli);
+               if (fsm->dnlx[dc] < 1 || fsm->dnlx[dc] >= dnlen)
+                   continue;
+
+               /* Copy to avoid const on fsm->path. */
+               te = stpcpy(dn, fsm->path) - 1;
+               fsm->path = dn;
+
+               /* Remove generated directories. */
+               do {
+                   if (*te == '/') {
+                       *te = '\0';
+                       rc = fsmStage(fsm, FSM_RMDIR);
+                       *te = '/';
+                   }
+                   if (rc) break;
+                   te--;
+               } while ((te - dn) > fsm->dnlx[dc]);
+           }
+           dnli = dnlFreeIterator(dnli);
+           fsm->path = path;
+       }
+       break;
+    case FSM_PROCESS:
+       if (fsm->postpone) {
+           if (fsm->goal == FSM_PKGINSTALL)
+               rc = fsmStage(fsm, FSM_EAT);
+           break;
+       }
+
+       if (fsm->goal == FSM_PKGBUILD) {
+           if (!S_ISDIR(st->st_mode) && st->st_nlink > 1) {
+               struct hardLink * li, * prev;
+               rc = writeLinkedFile(fsm);
+               if (rc) break;  /* W2DO? */
+
+               for (li = fsm->links, prev = NULL; li; prev = li, li = li->next)
+                    if (li == fsm->li) break;
+
+               if (prev == NULL)
+                   fsm->links = fsm->li->next;
+               else
+                   prev->next = fsm->li->next;
+               fsm->li->next = NULL;
+               fsm->li = freeHardLink(fsm->li);
+           } else {
+               rc = writeFile(fsm, 1);
+           }
+           break;
+       }
+
+       if (fsm->goal != FSM_PKGINSTALL)
+           break;
+
+       if (S_ISREG(st->st_mode)) {
+           const char * path = fsm->path;
+           if (fsm->osuffix)
+               fsm->path = fsmFsPath(fsm, st, NULL, NULL);
+           rc = fsmStage(fsm, FSM_VERIFY);
+
+           if (rc == 0 && fsm->osuffix) {
+               const char * opath = fsm->opath;
+               fsm->opath = fsm->path;
+               fsm->path = fsmFsPath(fsm, st, NULL, fsm->osuffix);
+               rc = fsmStage(fsm, FSM_RENAME);
+               if (!rc)
+                   rpmMessage(RPMMESS_WARNING,
+                       _("%s saved as %s\n"), fsm->opath, fsm->path);
+               fsm->path = _free(fsm->path);
+               fsm->opath = opath;
+           }
+
+           fsm->path = path;
+           if (rc != CPIOERR_LSTAT_FAILED) return rc;
+           rc = expandRegular(fsm);
+       } else if (S_ISDIR(st->st_mode)) {
+           mode_t st_mode = st->st_mode;
+           rc = fsmStage(fsm, FSM_VERIFY);
+           if (rc == CPIOERR_LSTAT_FAILED) {
+               st->st_mode &= ~07777;          /* XXX abuse st->st_mode */
+               st->st_mode |=  00700;
+               rc = fsmStage(fsm, FSM_MKDIR);
+               st->st_mode = st_mode;          /* XXX restore st->st_mode */
+           }
+       } else if (S_ISLNK(st->st_mode)) {
+           const char * opath = fsm->opath;
+
+           if ((st->st_size + 1) > fsm->rdsize) {
+               rc = CPIOERR_HDR_SIZE;
+               break;
+           }
+
+           fsm->wrlen = st->st_size;
+           rc = fsmStage(fsm, FSM_DREAD);
+           if (!rc && fsm->rdnb != fsm->wrlen)
+               rc = CPIOERR_READ_FAILED;
+           if (rc) break;
+
+           fsm->wrbuf[st->st_size] = '\0';
+           /* XXX symlink(fsm->opath, fsm->path) */
+           fsm->opath = fsm->wrbuf;            /* XXX abuse fsm->path */
+           rc = fsmStage(fsm, FSM_VERIFY);
+           if (rc == CPIOERR_LSTAT_FAILED)
+               rc = fsmStage(fsm, FSM_SYMLINK);
+           fsm->opath = opath;         /* XXX restore fsm->path */
+       } else if (S_ISFIFO(st->st_mode)) {
+           mode_t st_mode = st->st_mode;
+           /* This mimics cpio S_ISSOCK() behavior but probably isnt' right */
+           rc = fsmStage(fsm, FSM_VERIFY);
+           if (rc == CPIOERR_LSTAT_FAILED) {
+               st->st_mode = 0000;             /* XXX abuse st->st_mode */
+               rc = fsmStage(fsm, FSM_MKFIFO);
+               st->st_mode = st_mode;  /* XXX restore st->st_mode */
+           }
+       } else if (S_ISCHR(st->st_mode) ||
+                  S_ISBLK(st->st_mode) ||
+                  S_ISSOCK(st->st_mode))
+       {
+           rc = fsmStage(fsm, FSM_VERIFY);
+           if (rc == CPIOERR_LSTAT_FAILED)
+               rc = fsmStage(fsm, FSM_MKNOD);
+       } else {
+           rc = CPIOERR_UNKNOWN_FILETYPE;
+       }
+       if (!S_ISDIR(st->st_mode) && st->st_nlink > 1) {
+           fsm->li->createdPath = fsm->li->linkIndex;
+           rc = fsmMakeLinks(fsm);
+       }
+       break;
+    case FSM_POST:
+       break;
+    case FSM_MKLINKS:
+       break;
+    case FSM_NOTIFY:           /* XXX move from fsm to psm -> tsm */
+       if (fsm->goal == FSM_PKGINSTALL || fsm->goal == FSM_PKGBUILD) {
+           rpmTransactionSet ts = fsmGetTs(fsm);
+           TFI_t fi = fsmGetFi(fsm);
+           if (ts && ts->notify && fi)
+               (void)ts->notify(fi->h, RPMCALLBACK_INST_PROGRESS,
+                       fdGetCpioPos(fsm->cfd), fi->archiveSize,
+                       (fi->ap ? fi->ap->key : NULL), ts->notifyData);
+       }
+       break;
+    case FSM_UNDO:
+       if (fsm->postpone)
+           break;
+       if (fsm->goal == FSM_PKGINSTALL) {
+           (void) fsmStage(fsm,
+               (S_ISDIR(st->st_mode) ? FSM_RMDIR : FSM_UNLINK));
+
+#ifdef NOTYET  /* XXX remove only dirs just created, not all. */
+           if (fsm->dnlx)
+               (void) fsmStage(fsm, FSM_RMDIRS);
+#endif
+           errno = saveerrno;
+       }
+       if (fsm->failedFile && *fsm->failedFile == NULL)
+           *fsm->failedFile = xstrdup(fsm->path);
+       break;
+    case FSM_FINI:
+       if (!fsm->postpone && fsm->commit) {
+           if (fsm->goal == FSM_PKGINSTALL)
+               rc = ((!S_ISDIR(st->st_mode) && st->st_nlink > 1)
+                       ? fsmCommitLinks(fsm) : fsmStage(fsm, FSM_COMMIT));
+           if (fsm->goal == FSM_PKGCOMMIT)
+               rc = fsmStage(fsm, FSM_COMMIT);
+           if (fsm->goal == FSM_PKGERASE)
+               rc = fsmStage(fsm, FSM_COMMIT);
+       }
+       fsm->path = _free(fsm->path);
+       fsm->opath = _free(fsm->opath);
+       memset(st, 0, sizeof(*st));
+       memset(ost, 0, sizeof(*ost));
+       break;
+    case FSM_COMMIT:
+       /* Rename pre-existing modified or unmanaged file. */
+       if (fsm->diskchecked && fsm->exists && fsm->osuffix) {
+           const char * opath = fsm->opath;
+           const char * path = fsm->path;
+           fsm->opath = fsmFsPath(fsm, st, NULL, NULL);
+           fsm->path = fsmFsPath(fsm, st, NULL, fsm->osuffix);
+           rc = fsmStage(fsm, FSM_RENAME);
+           if (!rc) {
+               rpmMessage(RPMMESS_WARNING, _("%s saved as %s\n"),
+                               fsm->opath, fsm->path);
+           }
+           fsm->path = _free(fsm->path);
+           fsm->path = path;
+           fsm->opath = _free(fsm->opath);
+           fsm->opath = opath;
+       }
+
+       /* Remove erased files. */
+       if (fsm->goal == FSM_PKGERASE) {
+           if (fsm->action == FA_ERASE) {
+               TFI_t fi = fsmGetFi(fsm);
+               if (S_ISDIR(st->st_mode)) {
+                   rc = fsmStage(fsm, FSM_RMDIR);
+                   if (!rc) break;
+                   switch (errno) {
+                   case ENOENT: /* XXX rmdir("/") linux 2.2.x kernel hack */
+                   case ENOTEMPTY:
+       /* XXX make sure that build side permits %missingok on directories. */
+                       if (fsm->fflags & RPMFILE_MISSINGOK)
+                           break;
+
+                       /* XXX common error message. */
+                       rpmError(RPMERR_RMDIR, 
+                           _("%s rmdir of %s failed: Directory not empty\n"), 
+                               fiTypeString(fi), fsm->path);
+                       break;
+                   default:
+                       rpmError(RPMERR_RMDIR,
+                               _("%s rmdir of %s failed: %s\n"),
+                               fiTypeString(fi), fsm->path, strerror(errno));
+                       break;
+                   }
+               } else {
+                   rc = fsmStage(fsm, FSM_UNLINK);
+                   if (!rc) break;
+                   if (!(errno == ENOENT && (fsm->fflags & RPMFILE_MISSINGOK)))
+                       rpmError(RPMERR_UNLINK,
+                               _("%s unlink of %s failed: %s\n"),
+                               fiTypeString(fi), fsm->path, strerror(errno));
+               }
+           }
+           break;
+       }
+
+       if (!S_ISSOCK(st->st_mode)) {   /* XXX /dev/log et al are skipped */
+           /* Rename temporary to final file name. */
+           if (!S_ISDIR(st->st_mode) &&
+               (fsm->subdir || fsm->suffix || fsm->nsuffix))
+           {
+               fsm->opath = fsm->path;
+               fsm->path = fsmFsPath(fsm, st, NULL, fsm->nsuffix);
+               rc = fsmStage(fsm, FSM_RENAME);
+               if (!rc && fsm->nsuffix) {
+                   const char * opath = fsmFsPath(fsm, st, NULL, NULL);
+                   rpmMessage(RPMMESS_WARNING, _("%s created as %s\n"),
+                               opath, fsm->path);
+                   opath = _free(opath);
+               }
+               fsm->opath = _free(fsm->opath);
+           }
+           if (S_ISLNK(st->st_mode)) {
+               if (!rc && !getuid())
+                   rc = fsmStage(fsm, FSM_LCHOWN);
+           } else {
+               if (!rc && !getuid())
+                   rc = fsmStage(fsm, FSM_CHOWN);
+               if (!rc)
+                   rc = fsmStage(fsm, FSM_CHMOD);
+               if (!rc) {
+                   time_t st_mtime = st->st_mtime;
+                   TFI_t fi = fsmGetFi(fsm);
+                   if (fi->fmtimes)
+                       st->st_mtime = fi->fmtimes[fsm->ix];
+                   rc = fsmStage(fsm, FSM_UTIME);
+                   st->st_mtime = st_mtime;
+               }
+           }
+       }
+
+       /* Notify on success. */
+       if (!rc)                rc = fsmStage(fsm, FSM_NOTIFY);
+       break;
+    case FSM_DESTROY:
+       fsm->path = _free(fsm->path);
+
+       /* Create any remaining links (if no error), and clean up. */
+       while ((fsm->li = fsm->links) != NULL) {
+           fsm->links = fsm->li->next;
+           fsm->li->next = NULL;
+           if (fsm->goal == FSM_PKGINSTALL && fsm->commit && fsm->li->linksLeft)
+           {
+               for (i = 0 ; i < fsm->li->linksLeft; i++) {
+                   if (fsm->li->filex[i] < 0) continue;
+                   rc = CPIOERR_MISSING_HARDLINK;
+                   if (fsm->failedFile && *fsm->failedFile == NULL) {
+                       fsm->ix = fsm->li->filex[i];
+                       if (!fsmStage(fsm, FSM_MAP)) {
+                           *fsm->failedFile = fsm->path;
+                           fsm->path = NULL;
+                       }
+                   }
+                   break;
+               }
+           }
+           if (fsm->goal == FSM_PKGBUILD) {
+               rc = CPIOERR_MISSING_HARDLINK;
+           }
+           fsm->li = freeHardLink(fsm->li);
+       }
+       fsm->ldn = _free(fsm->ldn);
+       fsm->ldnalloc = fsm->ldnlen = 0;
+       fsm->rdbuf = fsm->rdb = _free(fsm->rdb);
+       fsm->wrbuf = fsm->wrb = _free(fsm->wrb);
+       break;
+    case FSM_VERIFY:
+       if (fsm->diskchecked && !fsm->exists) {
+           rc = CPIOERR_LSTAT_FAILED;
+           break;
+       }
+       if (S_ISREG(st->st_mode)) {
+           char * path = alloca(strlen(fsm->path) + sizeof("-RPMDELETE"));
+           (void) stpcpy( stpcpy(path, fsm->path), "-RPMDELETE");
+           /*
+            * XXX HP-UX (and other os'es) don't permit unlink on busy
+            * XXX files.
+            */
+           fsm->opath = fsm->path;
+           fsm->path = path;
+           rc = fsmStage(fsm, FSM_RENAME);
+           if (!rc)
+                   (void) fsmStage(fsm, FSM_UNLINK);
+           else
+                   rc = CPIOERR_UNLINK_FAILED;
+           fsm->path = fsm->opath;
+           fsm->opath = NULL;
+           return (rc ? rc : CPIOERR_LSTAT_FAILED);    /* XXX HACK */
+           /*@notreached@*/ break;
+       } else if (S_ISDIR(st->st_mode)) {
+           if (S_ISDIR(ost->st_mode))          return 0;
+           if (S_ISLNK(ost->st_mode)) {
+               rc = fsmStage(fsm, FSM_STAT);
+               if (rc == CPIOERR_STAT_FAILED && errno == ENOENT) rc = 0;
+               if (rc) break;
+               errno = saveerrno;
+               if (S_ISDIR(ost->st_mode))      return 0;
+           }
+       } else if (S_ISLNK(st->st_mode)) {
+           if (S_ISLNK(ost->st_mode)) {
+       /* XXX NUL terminated result in fsm->rdbuf, len in fsm->rdnb. */
+               rc = fsmStage(fsm, FSM_READLINK);
+               errno = saveerrno;
+               if (rc) break;
+               if (!strcmp(fsm->opath, fsm->rdbuf))    return 0;
+           }
+       } else if (S_ISFIFO(st->st_mode)) {
+           if (S_ISFIFO(ost->st_mode))         return 0;
+       } else if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) {
+           if ((S_ISCHR(ost->st_mode) || S_ISBLK(ost->st_mode)) &&
+               (ost->st_rdev == st->st_rdev))  return 0;
+       } else if (S_ISSOCK(st->st_mode)) {
+           if (S_ISSOCK(ost->st_mode))         return 0;
+       }
+           /* XXX shouldn't do this with commit/undo. */
+       rc = 0;
+       if (fsm->stage == FSM_PROCESS) rc = fsmStage(fsm, FSM_UNLINK);
+       if (rc == 0)    rc = CPIOERR_LSTAT_FAILED;
+       return (rc ? rc : CPIOERR_LSTAT_FAILED);        /* XXX HACK */
+       /*@notreached@*/ break;
+
+    case FSM_UNLINK:
+       rc = Unlink(fsm->path);
+       if (_fsm_debug && (stage & FSM_SYSCALL))
+           rpmMessage(RPMMESS_DEBUG, " %8s (%s) %s\n", cur,
+               fsm->path, (rc < 0 ? strerror(errno) : ""));
+       if (rc < 0)     rc = CPIOERR_UNLINK_FAILED;
+       break;
+    case FSM_RENAME:
+       rc = Rename(fsm->opath, fsm->path);
+       if (_fsm_debug && (stage & FSM_SYSCALL))
+           rpmMessage(RPMMESS_DEBUG, " %8s (%s, %s) %s\n", cur,
+               fsm->opath, fsm->path, (rc < 0 ? strerror(errno) : ""));
+       if (rc < 0)     rc = CPIOERR_RENAME_FAILED;
+       break;
+    case FSM_MKDIR:
+       rc = Mkdir(fsm->path, (st->st_mode & 07777));
+       if (_fsm_debug && (stage & FSM_SYSCALL))
+           rpmMessage(RPMMESS_DEBUG, " %8s (%s, 0%04o) %s\n", cur,
+               fsm->path, (st->st_mode & 07777),
+               (rc < 0 ? strerror(errno) : ""));
+       if (rc < 0)     rc = CPIOERR_MKDIR_FAILED;
+       break;
+    case FSM_RMDIR:
+       rc = Rmdir(fsm->path);
+       if (_fsm_debug && (stage & FSM_SYSCALL))
+           rpmMessage(RPMMESS_DEBUG, " %8s (%s) %s\n", cur,
+               fsm->path, (rc < 0 ? strerror(errno) : ""));
+       if (rc < 0)     rc = CPIOERR_RMDIR_FAILED;
+       break;
+    case FSM_CHOWN:
+       rc = chown(fsm->path, st->st_uid, st->st_gid);
+       if (_fsm_debug && (stage & FSM_SYSCALL))
+           rpmMessage(RPMMESS_DEBUG, " %8s (%s, %d, %d) %s\n", cur,
+               fsm->path, st->st_uid, st->st_gid,
+               (rc < 0 ? strerror(errno) : ""));
+       if (rc < 0)     rc = CPIOERR_CHOWN_FAILED;
+       break;
+    case FSM_LCHOWN:
+#if ! CHOWN_FOLLOWS_SYMLINK
+       rc = lchown(fsm->path, st->st_uid, st->st_gid);
+       if (_fsm_debug && (stage & FSM_SYSCALL))
+           rpmMessage(RPMMESS_DEBUG, " %8s (%s, %d, %d) %s\n", cur,
+               fsm->path, st->st_uid, st->st_gid,
+               (rc < 0 ? strerror(errno) : ""));
+       if (rc < 0)     rc = CPIOERR_CHOWN_FAILED;
+#endif
+       break;
+    case FSM_CHMOD:
+       rc = chmod(fsm->path, (st->st_mode & 07777));
+       if (_fsm_debug && (stage & FSM_SYSCALL))
+           rpmMessage(RPMMESS_DEBUG, " %8s (%s, 0%04o) %s\n", cur,
+               fsm->path, (st->st_mode & 07777),
+               (rc < 0 ? strerror(errno) : ""));
+       if (rc < 0)     rc = CPIOERR_CHMOD_FAILED;
+       break;
+    case FSM_UTIME:
+       {   struct utimbuf stamp;
+           stamp.actime = st->st_mtime;
+           stamp.modtime = st->st_mtime;
+           rc = utime(fsm->path, &stamp);
+           if (_fsm_debug && (stage & FSM_SYSCALL))
+               rpmMessage(RPMMESS_DEBUG, " %8s (%s, 0x%x) %s\n", cur,
+                       fsm->path, (unsigned)st->st_mtime,
+                       (rc < 0 ? strerror(errno) : ""));
+           if (rc < 0) rc = CPIOERR_UTIME_FAILED;
+       }
+       break;
+    case FSM_SYMLINK:
+       rc = symlink(fsm->opath, fsm->path);
+       if (_fsm_debug && (stage & FSM_SYSCALL))
+           rpmMessage(RPMMESS_DEBUG, " %8s (%s, %s) %s\n", cur,
+               fsm->opath, fsm->path, (rc < 0 ? strerror(errno) : ""));
+       if (rc < 0)     rc = CPIOERR_SYMLINK_FAILED;
+       break;
+    case FSM_LINK:
+       rc = Link(fsm->opath, fsm->path);
+       if (_fsm_debug && (stage & FSM_SYSCALL))
+           rpmMessage(RPMMESS_DEBUG, " %8s (%s, %s) %s\n", cur,
+               fsm->opath, fsm->path, (rc < 0 ? strerror(errno) : ""));
+       if (rc < 0)     rc = CPIOERR_LINK_FAILED;
+       break;
+    case FSM_MKFIFO:
+       rc = mkfifo(fsm->path, (st->st_mode & 07777));
+       if (_fsm_debug && (stage & FSM_SYSCALL))
+           rpmMessage(RPMMESS_DEBUG, " %8s (%s, 0%04o) %s\n", cur,
+               fsm->path, (st->st_mode & 07777),
+               (rc < 0 ? strerror(errno) : ""));
+       if (rc < 0)     rc = CPIOERR_MKFIFO_FAILED;
+       break;
+    case FSM_MKNOD:
+       /*@-unrecog@*/
+       rc = mknod(fsm->path, (st->st_mode & ~07777), st->st_rdev);
+       if (_fsm_debug && (stage & FSM_SYSCALL))
+           rpmMessage(RPMMESS_DEBUG, " %8s (%s, 0%o, 0x%x) %s\n", cur,
+               fsm->path, (st->st_mode & ~07777), (unsigned)st->st_rdev,
+               (rc < 0 ? strerror(errno) : ""));
+       if (rc < 0)     rc = CPIOERR_MKNOD_FAILED;
+       /*@=unrecog@*/
+       break;
+    case FSM_LSTAT:
+       rc = Lstat(fsm->path, ost);
+       if (_fsm_debug && (stage & FSM_SYSCALL) && rc && errno != ENOENT)
+           rpmMessage(RPMMESS_DEBUG, " %8s (%s, ost) %s\n", cur,
+               fsm->path, (rc < 0 ? strerror(errno) : ""));
+       if (rc < 0)     rc = CPIOERR_LSTAT_FAILED;
+       break;
+    case FSM_STAT:
+       rc = Stat(fsm->path, ost);
+       if (_fsm_debug && (stage & FSM_SYSCALL) && rc && errno != ENOENT)
+           rpmMessage(RPMMESS_DEBUG, " %8s (%s, ost) %s\n", cur,
+               fsm->path, (rc < 0 ? strerror(errno) : ""));
+       if (rc < 0)     rc = CPIOERR_STAT_FAILED;
+       break;
+    case FSM_READLINK:
+       /* XXX NUL terminated result in fsm->rdbuf, len in fsm->rdnb. */
+       rc = Readlink(fsm->path, fsm->rdbuf, fsm->rdsize - 1);
+       if (_fsm_debug && (stage & FSM_SYSCALL))
+           rpmMessage(RPMMESS_DEBUG, " %8s (%s, rdbuf, %d) %s\n", cur,
+               fsm->path, fsm->rdlen, (rc < 0 ? strerror(errno) : ""));
+       if (rc < 0)     rc = CPIOERR_READLINK_FAILED;
+       else {
+           fsm->rdnb = rc;
+           fsm->rdbuf[fsm->rdnb] = '\0';
+           rc = 0;
+       }
+       break;
+    case FSM_CHROOT:
+       break;
+
+    case FSM_NEXT:
+       rc = fsmStage(fsm, FSM_HREAD);
+       if (rc) break;
+       if (!strcmp(fsm->path, CPIO_TRAILER)) { /* Detect end-of-payload. */
+           fsm->path = _free(fsm->path);
+           rc = CPIOERR_HDR_TRAILER;
+       }
+       if (!rc)
+           rc = fsmStage(fsm, FSM_POS);
+       break;
+    case FSM_EAT:
+       for (left = st->st_size; left > 0; left -= fsm->rdnb) {
+           fsm->wrlen = (left > fsm->wrsize ? fsm->wrsize : left);
+           rc = fsmStage(fsm, FSM_DREAD);
+           if (rc) break;
+       }
+       break;
+    case FSM_POS:
+       left = (modulo - (fdGetCpioPos(fsm->cfd) % modulo)) % modulo;
+       if (left) {
+           fsm->wrlen = left;
+           (void) fsmStage(fsm, FSM_DREAD);
+       }
+       break;
+    case FSM_PAD:
+       left = (modulo - (fdGetCpioPos(fsm->cfd) % modulo)) % modulo;
+       if (left) {
+           memset(fsm->rdbuf, 0, left);
+           /* XXX DWRITE uses rdnb for I/O length. */
+           fsm->rdnb = left;
+           (void) fsmStage(fsm, FSM_DWRITE);
+       }
+       break;
+    case FSM_TRAILER:
+       rc = cpioTrailerWrite(fsm);
+       break;
+    case FSM_HREAD:
+       rc = fsmStage(fsm, FSM_POS);
+       if (!rc)
+           rc = cpioHeaderRead(fsm, st);       /* Read next payload header. */
+       break;
+    case FSM_HWRITE:
+       rc = cpioHeaderWrite(fsm, st);          /* Write next payload header. */
+       break;
+    case FSM_DREAD:
+       fsm->rdnb = Fread(fsm->wrbuf, sizeof(*fsm->wrbuf), fsm->wrlen, fsm->cfd);
+       if (_fsm_debug && (stage & FSM_SYSCALL))
+           rpmMessage(RPMMESS_DEBUG, " %8s (%s, %d, cfd)\trdnb %d\n",
+               cur, (fsm->wrbuf == fsm->wrb ? "wrbuf" : "mmap"),
+               fsm->wrlen, fsm->rdnb);
+if (fsm->rdnb != fsm->wrlen) fprintf(stderr, "*** short read, had %d, got %d\n", (int)fsm->rdnb, (int)fsm->wrlen);
+#ifdef NOTYET
+       if (Ferror(fsm->rfd))
+           rc = CPIOERR_READ_FAILED;
+#endif
+       if (fsm->rdnb > 0)
+           fdSetCpioPos(fsm->cfd, fdGetCpioPos(fsm->cfd) + fsm->rdnb);
+       break;
+    case FSM_DWRITE:
+       fsm->wrnb = Fwrite(fsm->rdbuf, sizeof(*fsm->rdbuf), fsm->rdnb, fsm->cfd);
+       if (_fsm_debug && (stage & FSM_SYSCALL))
+           rpmMessage(RPMMESS_DEBUG, " %8s (%s, %d, cfd)\twrnb %d\n",
+               cur, (fsm->rdbuf == fsm->rdb ? "rdbuf" : "mmap"),
+               fsm->rdnb, fsm->wrnb);
+if (fsm->rdnb != fsm->wrnb) fprintf(stderr, "*** short write, had %d, got %d\n", (int)fsm->rdnb, (int)fsm->wrnb);
+#ifdef NOTYET
+       if (Ferror(fsm->wfd))
+           rc = CPIOERR_WRITE_FAILED;
+#endif
+       if (fsm->wrnb > 0)
+           fdSetCpioPos(fsm->cfd, fdGetCpioPos(fsm->cfd) + fsm->wrnb);
+       break;
+
+    case FSM_ROPEN:
+       fsm->rfd = Fopen(fsm->path, "r.ufdio");
+       if (fsm->rfd == NULL || Ferror(fsm->rfd)) {
+           if (fsm->rfd)       (void) fsmStage(fsm, FSM_RCLOSE);
+           fsm->rfd = NULL;
+           rc = CPIOERR_OPEN_FAILED;
+           break;
+       }
+       if (_fsm_debug && (stage & FSM_SYSCALL))
+           rpmMessage(RPMMESS_DEBUG, " %8s (%s, \"r\") rfd %p rdbuf %p\n", cur,
+               fsm->path, fsm->rfd, fsm->rdbuf);
+       break;
+    case FSM_READ:
+       fsm->rdnb = Fread(fsm->rdbuf, sizeof(*fsm->rdbuf), fsm->rdlen, fsm->rfd);
+       if (_fsm_debug && (stage & FSM_SYSCALL))
+           rpmMessage(RPMMESS_DEBUG, " %8s (rdbuf, %d, rfd)\trdnb %d\n",
+               cur, fsm->rdlen, fsm->rdnb);
+if (fsm->rdnb != fsm->rdlen) fprintf(stderr, "*** short read, had %d, got %d\n", (int)fsm->rdnb, (int)fsm->rdlen);
+#ifdef NOTYET
+       if (Ferror(fsm->rfd))
+           rc = CPIOERR_READ_FAILED;
+#endif
+       break;
+    case FSM_RCLOSE:
+       if (fsm->rfd) {
+           if (_fsm_debug && (stage & FSM_SYSCALL))
+               rpmMessage(RPMMESS_DEBUG, " %8s (%p)\n", cur, fsm->rfd);
+           (void) Fclose(fsm->rfd);
+           errno = saveerrno;
+       }
+       fsm->rfd = NULL;
+       break;
+    case FSM_WOPEN:
+       fsm->wfd = Fopen(fsm->path, "w.ufdio");
+       if (fsm->wfd == NULL || Ferror(fsm->wfd)) {
+           if (fsm->wfd)       (void) fsmStage(fsm, FSM_WCLOSE);
+           fsm->wfd = NULL;
+           rc = CPIOERR_OPEN_FAILED;
+       }
+       if (_fsm_debug && (stage & FSM_SYSCALL))
+           rpmMessage(RPMMESS_DEBUG, " %8s (%s, \"w\") wfd %p wrbuf %p\n", cur,
+               fsm->path, fsm->wfd, fsm->wrbuf);
+       break;
+    case FSM_WRITE:
+       fsm->wrnb = Fwrite(fsm->wrbuf, sizeof(*fsm->wrbuf), fsm->rdnb, fsm->wfd);
+       if (_fsm_debug && (stage & FSM_SYSCALL))
+           rpmMessage(RPMMESS_DEBUG, " %8s (wrbuf, %d, wfd)\twrnb %d\n",
+               cur, fsm->rdnb, fsm->wrnb);
+if (fsm->rdnb != fsm->wrnb) fprintf(stderr, "*** short write: had %d, got %d\n", (int)fsm->rdnb, (int)fsm->wrnb);
+#ifdef NOTYET
+       if (Ferror(fsm->wfd))
+           rc = CPIOERR_WRITE_FAILED;
+#endif
+       break;
+    case FSM_WCLOSE:
+       if (fsm->wfd) {
+           if (_fsm_debug && (stage & FSM_SYSCALL))
+               rpmMessage(RPMMESS_DEBUG, " %8s (%p)\n", cur, fsm->wfd);
+           (void) Fclose(fsm->wfd);
+           errno = saveerrno;
+       }
+       fsm->wfd = NULL;
+       break;
+
+    default:
+       break;
+    }
+
+    if (!(stage & FSM_INTERNAL)) {
+       fsm->rc = (rc == CPIOERR_HDR_TRAILER ? 0 : rc);
+    }
+    return rc;
+}
+
+/*@observer@*/ const char *const fileStageString(fileStage a) {
+    switch(a) {
+    case FSM_UNKNOWN:  return "unknown";
+
+    case FSM_PKGINSTALL:return "pkginstall";
+    case FSM_PKGERASE: return "pkgerase";
+    case FSM_PKGBUILD: return "pkgbuild";
+    case FSM_PKGCOMMIT:        return "pkgcommit";
+    case FSM_PKGUNDO:  return "pkgundo";
+
+    case FSM_CREATE:   return "create";
+    case FSM_INIT:     return "init";
+    case FSM_MAP:      return "map";
+    case FSM_MKDIRS:   return "mkdirs";
+    case FSM_RMDIRS:   return "rmdirs";
+    case FSM_PRE:      return "pre";
+    case FSM_PROCESS:  return "process";
+    case FSM_POST:     return "post";
+    case FSM_MKLINKS:  return "mklinks";
+    case FSM_NOTIFY:   return "notify";
+    case FSM_UNDO:     return "undo";
+    case FSM_FINI:     return "fini";
+    case FSM_COMMIT:   return "commit";
+    case FSM_DESTROY:  return "destroy";
+    case FSM_VERIFY:   return "verify";
+
+    case FSM_UNLINK:   return "Unlink";
+    case FSM_RENAME:   return "Rename";
+    case FSM_MKDIR:    return "Mkdir";
+    case FSM_RMDIR:    return "rmdir";
+    case FSM_CHOWN:    return "chown";
+    case FSM_LCHOWN:   return "lchown";
+    case FSM_CHMOD:    return "chmod";
+    case FSM_UTIME:    return "utime";
+    case FSM_SYMLINK:  return "symlink";
+    case FSM_LINK:     return "Link";
+    case FSM_MKFIFO:   return "mkfifo";
+    case FSM_MKNOD:    return "mknod";
+    case FSM_LSTAT:    return "Lstat";
+    case FSM_STAT:     return "Stat";
+    case FSM_READLINK: return "Readlink";
+    case FSM_CHROOT:   return "chroot";
+
+    case FSM_NEXT:     return "next";
+    case FSM_EAT:      return "eat";
+    case FSM_POS:      return "pos";
+    case FSM_PAD:      return "pad";
+    case FSM_TRAILER:  return "trailer";
+    case FSM_HREAD:    return "hread";
+    case FSM_HWRITE:   return "hwrite";
+    case FSM_DREAD:    return "Fread";
+    case FSM_DWRITE:   return "Fwrite";
+
+    case FSM_ROPEN:    return "Fopen";
+    case FSM_READ:     return "Fread";
+    case FSM_RCLOSE:   return "Fclose";
+    case FSM_WOPEN:    return "Fopen";
+    case FSM_WRITE:    return "Fwrite";
+    case FSM_WCLOSE:   return "Fclose";
+
+    default:           return "???";
+    }
+    /*@noteached@*/
+}
diff --git a/lib/fsm.h b/lib/fsm.h
new file mode 100644 (file)
index 0000000..f2c7513
--- /dev/null
+++ b/lib/fsm.h
@@ -0,0 +1,255 @@
+#ifndef H_FSM
+#define H_FSM
+
+/** \file lib/fsm.h
+ */
+
+#include <rpmlib.h>
+#include "cpio.h"
+
+/**
+ */
+#define        FSM_VERBOSE     0x8000
+#define        FSM_INTERNAL    0x4000
+#define        FSM_SYSCALL     0x2000
+#define        FSM_DEAD        0x1000
+#define        _fv(_a)         ((_a) | FSM_VERBOSE)
+#define        _fi(_a)         ((_a) | FSM_INTERNAL)
+#define        _fs(_a)         ((_a) | (FSM_INTERNAL | FSM_SYSCALL))
+#define        _fd(_a)         ((_a) | (FSM_INTERNAL | FSM_DEAD))
+typedef enum fileStage_e {
+    FSM_UNKNOWN =   0,
+    FSM_INIT   =  _fd(1),
+    FSM_PRE    =  _fd(2),
+    FSM_PROCESS        =  _fv(3),
+    FSM_POST   =  _fd(4),
+    FSM_UNDO   =  5,
+    FSM_FINI   =  6,
+
+    FSM_PKGINSTALL     = _fd(7),
+    FSM_PKGERASE       = _fd(8),
+    FSM_PKGBUILD       = _fd(9),
+    FSM_PKGCOMMIT      = _fd(10),
+    FSM_PKGUNDO                = _fd(11),
+
+    FSM_CREATE =  _fd(17),
+    FSM_MAP    =  _fd(18),
+    FSM_MKDIRS =  _fi(19),
+    FSM_RMDIRS =  _fi(20),
+    FSM_MKLINKS        =  _fi(21),
+    FSM_NOTIFY =  _fd(22),
+    FSM_DESTROY        =  _fd(23),
+    FSM_VERIFY =  _fd(24),
+    FSM_COMMIT =  _fd(25),
+
+    FSM_UNLINK =  _fs(33),
+    FSM_RENAME =  _fs(34),
+    FSM_MKDIR  =  _fs(35),
+    FSM_RMDIR  =  _fs(36),
+    FSM_CHOWN  =  _fs(37),
+    FSM_LCHOWN =  _fs(38),
+    FSM_CHMOD  =  _fs(39),
+    FSM_UTIME  =  _fs(40),
+    FSM_SYMLINK        =  _fs(41),
+    FSM_LINK   =  _fs(42),
+    FSM_MKFIFO =  _fs(43),
+    FSM_MKNOD  =  _fs(44),
+    FSM_LSTAT  =  _fs(45),
+    FSM_STAT   =  _fs(46),
+    FSM_READLINK=  _fs(47),
+    FSM_CHROOT =  _fs(48),
+
+    FSM_NEXT   =  _fd(65),
+    FSM_EAT    =  _fd(66),
+    FSM_POS    =  _fd(67),
+    FSM_PAD    =  _fd(68),
+    FSM_TRAILER        =  _fd(69),
+    FSM_HREAD  =  _fd(70),
+    FSM_HWRITE =  _fd(71),
+    FSM_DREAD  =  _fs(72),
+    FSM_DWRITE =  _fs(73),
+
+    FSM_ROPEN  =  _fs(129),
+    FSM_READ   =  _fs(130),
+    FSM_RCLOSE =  _fs(131),
+    FSM_WOPEN  =  _fs(132),
+    FSM_WRITE  =  _fs(133),
+    FSM_WCLOSE =  _fs(134),
+} fileStage;
+#undef _fv
+#undef _fi
+#undef _fs
+#undef _fd
+
+/** \ingroup payload
+ * Keeps track of the set of all hard links to a file in an archive.
+ */
+struct hardLink {
+/*@owned@*/ struct hardLink * next;
+/*@owned@*/ const char ** nsuffix;
+/*@owned@*/ int * filex;
+    dev_t dev;
+    ino_t inode;
+    int nlink;
+    int linksLeft;
+    int linkIndex;
+    int createdPath;
+};
+
+/** \ingroup payload
+ * Iterator across package file info, forward on install, backward on erase.
+ */
+struct fsmIterator_s {
+/*@kept@*/ rpmTransactionSet ts;       /*!< transaction set. */
+/*@kept@*/ TFI_t fi;                   /*!< transaction element file info. */
+    int isave;                         /*!< last returned iterator index. */
+    int i;                             /*!< iterator index. */
+};
+
+/** \ingroup payload
+ * File name and stat information.
+ */
+struct fsm_s {
+/*@owned@*/ const char * path;         /*!< Current file name. */
+/*@owned@*/ const char * opath;                /*!< Original file name. */
+    FD_t cfd;                          /*!< Payload file handle. */
+    FD_t rfd;                          /*!<  read: File handle. */
+/*@dependent@*/ char * rdbuf;          /*!<  read: Buffer. */
+/*@owned@*/ char * rdb;                        /*!<  read: Buffer allocated. */
+    size_t rdsize;                     /*!<  read: Buffer allocated size. */
+    size_t rdlen;                      /*!<  read: Number of bytes requested. */
+    size_t rdnb;                       /*!<  read: Number of bytes returned. */
+    FD_t wfd;                          /*!< write: File handle. */
+/*@dependent@*/ char * wrbuf;          /*!< write: Buffer. */
+/*@owned@*/ char * wrb;                        /*!< write: Buffer allocated. */
+    size_t wrsize;                     /*!< write: Buffer allocated size. */
+    size_t wrlen;                      /*!< write: Number of bytes requested. */
+    size_t wrnb;                       /*!< write: Number of bytes returned. */
+/*@only@*/ FSMI_t iter;                        /*!< File iterator. */
+    int ix;                            /*!< Current file iterator index. */
+/*@only@*/ struct hardLink * links;    /*!< Pending hard linked file(s). */
+/*@only@*/ struct hardLink * li;       /*!< Current hard linked file(s). */
+/*@kept@*/ unsigned int * archiveSize; /*!< Pointer to archive size. */
+/*@kept@*/ const char ** failedFile;   /*!< First file name that failed. */
+/*@shared@*/ const char * subdir;      /*!< Current file sub-directory. */
+    char subbuf[64];   /* XXX eliminate */
+/*@observer@*/ const char * osuffix;   /*!< Old, preserved, file suffix. */
+/*@observer@*/ const char * nsuffix;   /*!< New, created, file suffix. */
+/*@shared@*/ const char * suffix;      /*!< Current file suffix. */
+    char sufbuf[64];   /* XXX eliminate */
+/*@only@*/ short * dnlx;               /*!< Last dirpath verified indexes. */
+/*@only@*/ char * ldn;                 /*!< Last dirpath verified. */
+    int ldnlen;                                /*!< Last dirpath current length. */
+    int ldnalloc;                      /*!< Last dirpath allocated length. */
+    int postpone;                      /*!< Skip remaining stages? */
+    int diskchecked;                   /*!< Has stat(2) been performed? */
+    int exists;                                /*!< Does current file exist on disk? */
+    int mkdirsdone;                    /*!< Have "orphan" dirs been created? */
+    int astriplen;                     /*!< Length of buildroot prefix. */
+    int rc;                            /*!< External file stage return code. */
+    int commit;                                /*!< Commit synchronously? */
+    cpioMapFlags mapFlags;             /*!< Bit(s) to control mapping. */
+/*@shared@*/ const char * archivePath; /*!< Path to store in cpio archive. */
+/*@shared@*/ const char * dirName;     /*!< File directory name. */
+/*@shared@*/ const char * baseName;    /*!< File base name. */
+/*@shared@*/ const char * fmd5sum;     /*!< File MD5 sum (NULL disables). */
+    unsigned fflags;                   /*!< File flags. */
+    fileAction action;                 /*!< File disposition. */
+    fileStage goal;                    /*!< Package state machine goal. */
+    fileStage stage;                   /*!< External file stage. */
+    struct stat sb;                    /*!< Current file stat(2) info. */
+    struct stat osb;                   /*!< Original file stat(2) info. */
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Return formatted string representation of file stages.
+ * @param a            file stage
+ * @return             formatted string
+ */
+/*@observer@*/ const char *const fileStageString(fileStage a);
+
+/**
+ * Create file state machine instance.
+ * @return             file state machine data
+ */
+/*@only@*/ /*@null@*/ FSM_t newFSM(void);
+
+/**
+ * Destroy file state machine instance.
+ * @param fsm          file state machine data
+ * @return             always NULL
+ */
+/*@null@*/ FSM_t freeFSM(/*@only@*/ /*@null@*/ FSM_t fsm);
+
+/**
+ * Load external data into file state machine.
+ * @param fsm          file state machine data
+ * @param goal
+ * @param ts           transaction set
+ * @param fi           transaction element file info
+ * @param archiveSize  pointer to archive size
+ * @param failedFile   pointer to first file name that failed.
+ * @return             0 on success
+ */
+int fsmSetup(FSM_t fsm, fileStage goal,
+       /*@kept@*/ const rpmTransactionSet ts,
+       /*@kept@*/ const TFI_t fi,
+       FD_t cfd,
+       /*@out@*/ unsigned int * archiveSize,
+       /*@out@*/ const char ** failedFile)
+               /*@modifies fsm, *archiveSize, *failedFile  @*/;
+
+/**
+ * Clean file state machine.
+ * @param fsm          file state machine data
+ * @return             0 on success
+ */
+int fsmTeardown(FSM_t fsm)
+               /*@modifies fsm @*/;
+
+/**
+ * Retrieve transaction set from file state machine iterator.
+ * @param fsm          file state machine data
+ * @return             transaction set
+ */
+/*@kept@*/ rpmTransactionSet fsmGetTs(const FSM_t fsm) /*@*/;
+
+/**
+ * Retrieve transaction element file info from file state machine iterator.
+ * @param fsm          file state machine data
+ * @return             transaction element file info
+ */
+/*@kept@*/ TFI_t fsmGetFi(const FSM_t fsm)     /*@*/;
+
+/**
+ * Map next file path and action.
+ * @param fsm          file state machine data
+ */
+int fsmMapPath(FSM_t fsm)
+               /*@modifies fsm @*/;
+
+/**
+ * Map file stat(2) info.
+ * @param fsm          file state machine data
+ */
+int fsmMapAttrs(FSM_t fsm)
+               /*@modifies fsm @*/;
+
+/**
+ * File state machine driver.
+ * @param fsm          file state machine data
+ * @param stage                next stage
+ * @return             0 on success
+ */
+int fsmStage(FSM_t fsm, fileStage stage)
+               /*@modifies fsm @*/;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* H_FSM */
similarity index 70%
rename from lib/install.c
rename to lib/psm.c
index 8f8f7c8..b086483 100644 (file)
+++ b/lib/psm.c
@@ -1,5 +1,5 @@
 /** \ingroup rpmtrans payload
- * \file lib/install.c
+ * \file lib/psm.c
  */
 
 #include "system.h"
 #include <rpmmacro.h>
 #include <rpmurl.h>
 
-#include "rollback.h"
+#include "psm.h"
 #include "misc.h"
 #include "debug.h"
 
-/*@access Header @*/           /* XXX compared with NULL */
-/*@access rpmTransactionSet @*/        /* XXX compared with NULL */
+/*@access Header @*/           /* compared with NULL */
+/*@access rpmTransactionSet @*/        /* compared with NULL */
+/*@access TFI_t @*/            /* compared with NULL */
+
+extern int _fsm_debug;
+
+/**
+ * Wrapper to free(3), hides const compilation noise, permit NULL, return NULL.
+ * @param this         memory to free
+ * @retval             NULL always
+ */
+static /*@null@*/ void * _free(/*@only@*/ /*@null@*/ const void * this) {
+    if (this)   free((void *)this);
+    return NULL;
+}
+
+void loadFi(Header h, TFI_t fi)
+{
+    HGE_t hge;
+    HFD_t hfd;
+    uint_32 * uip;
+    int len;
+    int rc;
+    int i;
+    
+    if (fi->fsm == NULL)
+       fi->fsm = newFSM();
+
+    /* XXX avoid gcc noise on pointer (4th arg) cast(s) */
+    hge = (fi->type == TR_ADDED)
+       ? (HGE_t) headerGetEntryMinMemory : (HGE_t) headerGetEntry;
+    fi->hge = hge;
+
+    fi->hfd = hfd = headerFreeData;
+
+    if (h && fi->h == NULL)    fi->h = headerLink(h);
+
+    /* Duplicate name-version-release so that headers can be free'd. */
+    hge(fi->h, RPMTAG_NAME, NULL, (void **) &fi->name, NULL);
+    fi->name = xstrdup(fi->name);
+    hge(fi->h, RPMTAG_VERSION, NULL, (void **) &fi->version, NULL);
+    fi->version = xstrdup(fi->version);
+    hge(fi->h, RPMTAG_RELEASE, NULL, (void **) &fi->release, NULL);
+    fi->release = xstrdup(fi->release);
+
+    /* -1 means not found */
+    rc = hge(fi->h, RPMTAG_EPOCH, NULL, (void **) &uip, NULL);
+    fi->epoch = (rc ? *uip : -1);
+    /* 0 means unknown */
+    rc = hge(fi->h, RPMTAG_ARCHIVESIZE, NULL, (void **) &uip, NULL);
+    fi->archiveSize = (rc ? *uip : 0);
+
+    if (!hge(fi->h, RPMTAG_BASENAMES, NULL, (void **) &fi->bnl, &fi->fc)) {
+       fi->dc = 0;
+       fi->fc = 0;
+       return;
+    }
+
+    hge(fi->h, RPMTAG_DIRINDEXES, NULL, (void **) &fi->dil, NULL);
+    hge(fi->h, RPMTAG_DIRNAMES, NULL, (void **) &fi->dnl, &fi->dc);
+    hge(fi->h, RPMTAG_FILEMODES, NULL, (void **) &fi->fmodes, NULL);
+    hge(fi->h, RPMTAG_FILEFLAGS, NULL, (void **) &fi->fflags, NULL);
+    hge(fi->h, RPMTAG_FILESIZES, NULL, (void **) &fi->fsizes, NULL);
+    hge(fi->h, RPMTAG_FILESTATES, NULL, (void **) &fi->fstates, NULL);
+
+    fi->action = FA_UNKNOWN;
+    fi->flags = 0;
+
+    /* actions is initialized earlier for added packages */
+    if (fi->actions == NULL)
+       fi->actions = xcalloc(fi->fc, sizeof(*fi->actions));
+
+    switch (fi->type) {
+    case TR_ADDED:
+       fi->mapflags = CPIO_MAP_PATH | CPIO_MAP_MODE | CPIO_MAP_UID | CPIO_MAP_GID;
+       hge(fi->h, RPMTAG_FILEMD5S, NULL, (void **) &fi->fmd5s, NULL);
+       hge(fi->h, RPMTAG_FILELINKTOS, NULL, (void **) &fi->flinks, NULL);
+       hge(fi->h, RPMTAG_FILELANGS, NULL, (void **) &fi->flangs, NULL);
+       hge(fi->h, RPMTAG_FILEMTIMES, NULL, (void **) &fi->fmtimes, NULL);
+
+       /* 0 makes for noops */
+       fi->replacedSizes = xcalloc(fi->fc, sizeof(*fi->replacedSizes));
+
+       break;
+    case TR_REMOVED:
+       fi->mapflags = CPIO_MAP_PATH;
+       hge(fi->h, RPMTAG_FILEMD5S, NULL, (void **) &fi->fmd5s, NULL);
+       hge(fi->h, RPMTAG_FILELINKTOS, NULL, (void **) &fi->flinks, NULL);
+       fi->fsizes = memcpy(xmalloc(fi->fc * sizeof(*fi->fsizes)),
+                               fi->fsizes, fi->fc * sizeof(*fi->fsizes));
+       fi->fflags = memcpy(xmalloc(fi->fc * sizeof(*fi->fflags)),
+                               fi->fflags, fi->fc * sizeof(*fi->fflags));
+       fi->fmodes = memcpy(xmalloc(fi->fc * sizeof(*fi->fmodes)),
+                               fi->fmodes, fi->fc * sizeof(*fi->fmodes));
+       /* XXX there's a tedious segfault here for some version(s) of rpm */
+       if (fi->fstates)
+           fi->fstates = memcpy(xmalloc(fi->fc * sizeof(*fi->fstates)),
+                               fi->fstates, fi->fc * sizeof(*fi->fstates));
+       else
+           fi->fstates = xcalloc(1, fi->fc * sizeof(*fi->fstates));
+       fi->dil = memcpy(xmalloc(fi->fc * sizeof(*fi->dil)),
+                               fi->dil, fi->fc * sizeof(*fi->dil));
+       headerFree(fi->h);
+       fi->h = NULL;
+       break;
+    }
+
+    fi->dnlmax = -1;
+    for (i = 0; i < fi->dc; i++) {
+       if ((len = strlen(fi->dnl[i])) > fi->dnlmax)
+           fi->dnlmax = len;
+    }
+
+    fi->bnlmax = -1;
+    for (i = 0; i < fi->fc; i++) {
+       if ((len = strlen(fi->bnl[i])) > fi->bnlmax)
+           fi->bnlmax = len;
+    }
+
+    fi->dperms = 0755;
+    fi->fperms = 0644;
+
+    return;
+}
+
+void freeFi(TFI_t fi)
+{
+    HFD_t hfd = (fi->hfd ? fi->hfd : headerFreeData);
+
+    fi->name = _free(fi->name);
+    fi->version = _free(fi->version);
+    fi->release = _free(fi->release);
+    fi->actions = _free(fi->actions);
+    fi->replacedSizes = _free(fi->replacedSizes);
+    fi->replaced = _free(fi->replaced);
+
+    fi->bnl = hfd(fi->bnl, -1);
+    fi->dnl = hfd(fi->dnl, -1);
+    fi->obnl = hfd(fi->obnl, -1);
+    fi->odnl = hfd(fi->odnl, -1);
+    fi->flinks = hfd(fi->flinks, -1);
+    fi->fmd5s = hfd(fi->fmd5s, -1);
+    fi->fuser = hfd(fi->fuser, -1);
+    fi->fgroup = hfd(fi->fgroup, -1);
+    fi->flangs = hfd(fi->flangs, -1);
+
+    fi->apath = _free(fi->apath);
+    fi->fuids = _free(fi->fuids);
+    fi->fgids = _free(fi->fgids);
+    fi->fmapflags = _free(fi->fmapflags);
+
+    fi->fsm = freeFSM(fi->fsm);
+
+    switch (fi->type) {
+    case TR_ADDED:
+           break;
+    case TR_REMOVED:
+       fi->fsizes = hfd(fi->fsizes, -1);
+       fi->fflags = hfd(fi->fflags, -1);
+       fi->fmodes = hfd(fi->fmodes, -1);
+       fi->fstates = hfd(fi->fstates, -1);
+       fi->dil = hfd(fi->dil, -1);
+       break;
+    }
+    if (fi->h) {
+       headerFree(fi->h); fi->h = NULL;
+    }
+}
+
+/*@observer@*/ const char *const fiTypeString(TFI_t fi) {
+    switch(fi->type) {
+    case TR_ADDED:     return " install";
+    case TR_REMOVED:   return "   erase";
+    default:           return "???";
+    }
+    /*@noteached@*/
+}
+
+/*@obserever@*/ const char *const fileActionString(fileAction a)
+{
+    switch (a) {
+    case FA_UNKNOWN:   return "unknown";
+    case FA_CREATE:    return "create";
+    case FA_BACKUP:    return "backup";
+    case FA_SAVE:      return "save";
+    case FA_SKIP:      return "skip";
+    case FA_ALTNAME:   return "altname";
+    case FA_ERASE:     return "erase";
+    case FA_SKIPNSTATE: return "skipnstate";
+    case FA_SKIPNETSHARED: return "skipnetshared";
+    case FA_SKIPMULTILIB: return "skipmultilib";
+    default:           return "???";
+    }
+    /*@notreached@*/
+}
+
+#ifdef DYING
+/**
+ */
+struct pkgIterator {
+/*@dependent@*/ /*@kept@*/ TFI_t fi;
+    int i;
+};
+
+/**
+ */
+static /*@null@*/ void * pkgFreeIterator(/*@only@*/ /*@null@*/ void * this) {
+    return _free(this);
+}
+
+/**
+ */
+static /*@only@*/ void * pkgInitIterator(/*@kept@*/ rpmTransactionSet ts,
+       /*@kept@*/ TFI_t fi)
+{
+    struct pkgIterator * pi = NULL;
+    if (ts && fi) {
+       pi = xcalloc(sizeof(*pi), 1);
+       pi->fi = fi;
+       switch (fi->type) {
+       case TR_ADDED:  pi->i = 0;      break;
+       case TR_REMOVED:        pi->i = fi->fc; break;
+       }
+    }
+    return pi;
+}
+
+/**
+ */
+static int pkgNextIterator(/*@null@*/ void * this) {
+    struct pkgIterator * pi = this;
+    int i = -1;
+
+    if (pi) {
+       TFI_t fi = pi->fi;
+       switch (fi->type) {
+       case TR_ADDED:
+           if (pi->i < fi->fc)
+               i = pi->i++;
+           break;
+       case TR_REMOVED:
+           if (pi->i >= 0)
+               i = --pi->i;
+           break;
+       }
+    }
+    return i;
+}
+
+int pkgActions(const rpmTransactionSet ts, TFI_t fi, fileStage a)
+{
+    int rc = 0;
+
+    if (fi->actions) {
+       void * pi = pkgInitIterator(ts, fi);
+       int i;
+       while ((i = pkgNextIterator(pi)) != -1) {
+           if (pkgAction(ts, fi, i, a))
+               rc++;
+       }
+       pi = pkgFreeIterator(pi);
+    }
+    return rc;
+}
+#endif
+
 
 /**
  * Macros to be defined from per-header tag values.
@@ -911,3 +1175,103 @@ exit:
        headerFree(oldH);
     return ec;
 }
+
+int removeBinaryPackage(const rpmTransactionSet ts, TFI_t fi)
+{
+/*@observer@*/ static char * stepName = "   erase";
+    Header h;
+    int chrootDone = 0;
+    const char * failedFile = NULL;
+    const void * pkgKey = NULL;
+    int rc = 0;
+
+    rpmMessage(RPMMESS_DEBUG, _("%s: %s-%s-%s has %d files, test = %d\n"),
+               stepName, fi->name, fi->version, fi->release,
+               fi->fc, (ts->transFlags & RPMTRANS_FLAG_TEST));
+
+    /*
+     * When we run scripts, we pass an argument which is the number of 
+     * versions of this package that will be installed when we are finished.
+     */
+    fi->scriptArg = rpmdbCountPackages(ts->rpmdb, fi->name) - 1;
+    if (fi->scriptArg < 0)
+       return 1;
+
+    {  rpmdbMatchIterator mi = NULL;
+
+       mi = rpmdbInitIterator(ts->rpmdb, RPMDBI_PACKAGES,
+                               &fi->record, sizeof(fi->record));
+
+       h = rpmdbNextIterator(mi);
+       if (h == NULL) {
+           rpmdbFreeIterator(mi);
+           return 2;
+       }
+       h = headerLink(h);
+       rpmdbFreeIterator(mi);
+    }
+
+    if (ts->rootDir && !ts->chrootDone) {
+       chdir("/");
+       /*@-unrecog@*/ chroot(ts->rootDir); /*@=unrecog@*/
+       chrootDone = ts->chrootDone = 1;
+    }
+
+    if (!(ts->transFlags & RPMTRANS_FLAG_NOTRIGGERS)) {
+       /* run triggers from this package which are keyed on installed 
+          packages */
+       if (runImmedTriggers(ts, RPMSENSE_TRIGGERUN, h, -1))
+           return 2;
+
+       /* run triggers which are set off by the removal of this package */
+       if (runTriggers(ts, RPMSENSE_TRIGGERUN, h, -1))
+           return 1;
+    }
+
+    rpmMessage(RPMMESS_DEBUG, _("%s: running %s script(s) (if any)\n"),
+               stepName, "pre-erase");
+
+    rc = runInstScript(ts, h, RPMTAG_PREUN, RPMTAG_PREUNPROG, fi->scriptArg,
+                         (ts->transFlags & RPMTRANS_FLAG_NOSCRIPTS));
+    if (rc)
+       return 1;
+
+    if (fi->fc > 0 && !(ts->transFlags & RPMTRANS_FLAG_JUSTDB)) {
+
+       if (ts->notify)
+           (void)ts->notify(h, RPMCALLBACK_UNINST_START, fi->fc, fi->fc,
+               pkgKey, ts->notifyData);
+
+       rc = fsmSetup(fi->fsm, FSM_PKGERASE, ts, fi, NULL, NULL, &failedFile);
+       (void) fsmTeardown(fi->fsm);
+
+       if (ts->notify)
+           (void)ts->notify(h, RPMCALLBACK_UNINST_STOP, 0, fi->fc,
+                       pkgKey, ts->notifyData);
+    }
+
+    rpmMessage(RPMMESS_DEBUG, _("%s: running %s script(s) (if any)\n"),
+               stepName, "post-erase");
+
+    rc = runInstScript(ts, h, RPMTAG_POSTUN, RPMTAG_POSTUNPROG,
+               fi->scriptArg, (ts->transFlags & RPMTRANS_FLAG_NOSCRIPTS));
+    /* XXX postun failures are not cause for erasure failure. */
+
+    if (!(ts->transFlags & RPMTRANS_FLAG_NOTRIGGERS)) {
+       /* Run postun triggers which are set off by this package's removal. */
+       rc = runTriggers(ts, RPMSENSE_TRIGGERPOSTUN, h, -1);
+       if (rc)
+           return 2;
+    }
+
+    if (ts->rootDir && chrootDone) {
+       /*@-unrecog@*/ chroot("."); /*@=unrecog@*/
+       chrootDone = ts->chrootDone = 0;
+       chdir(ts->currDir);
+    }
+
+    if (!(ts->transFlags & RPMTRANS_FLAG_TEST))
+       rpmdbRemove(ts->rpmdb, ts->id, fi->record);
+
+    return 0;
+}
diff --git a/lib/psm.h b/lib/psm.h
new file mode 100644 (file)
index 0000000..8b18a90
--- /dev/null
+++ b/lib/psm.h
@@ -0,0 +1,152 @@
+#ifndef H_PSM
+#define H_PSM
+
+/** \file lib/psm.h
+ */
+
+#include <rpmlib.h>
+#include "depends.h"
+#include "scriptlet.h"
+#include "fsm.h"
+
+/**
+ */
+typedef        enum rollbackDir_e {
+    ROLLBACK_SAVE      = 1,    /*!< Save files. */
+    ROLLBACK_RESTORE   = 2,    /*!< Restore files. */
+} rollbackDir;
+
+/**
+ */
+struct sharedFile {
+    int mainFileNumber;
+    int secRecOffset;
+    int secFileNumber;
+} ;
+
+/**
+ */
+struct sharedFileInfo {
+    int pkgFileNum;
+    int otherFileNum;
+    int otherPkg;
+    int isRemoved;
+};
+
+/**
+ */
+struct transactionFileInfo_s {
+  /* for all packages */
+    enum rpmTransactionType type;
+    fileAction action;         /*!< File disposition default. */
+/*@owned@*/ fileAction * actions;      /*!< File disposition(s) */
+/*@owned@*/ struct fingerPrint_s * fps;        /*!< File fingerprint(s) */
+    HGE_t hge;                 /*!< Vector to headerGetEntry() */
+    HFD_t hfd;                 /*!< Vector to headerFreeData() */
+    Header h;                  /*!< Package header */
+/*@owned@*/ const char * name;
+/*@owned@*/ const char * version;
+/*@owned@*/ const char * release;
+    int_32 epoch;
+    uint_32 flags;             /*!< File flag default. */
+    const uint_32 * fflags;    /*!< File flag(s) (from header) */
+    const uint_32 * fsizes;    /*!< File size(s) (from header) */
+    const uint_32 * fmtimes;   /*!< File modification time(s) (from header) */
+/*@owned@*/ const char ** bnl; /*!< Base name(s) (from header) */
+/*@owned@*/ const char ** dnl; /*!< Directory name(s) (from header) */
+    int_32 * dil;              /*!< Directory indice(s) (from header) */
+/*@owned@*/ const char ** obnl;        /*!< Original base name(s) (from header) */
+/*@owned@*/ const char ** odnl;        /*!< Original directory name(s) (from header) */
+    int_32 * odil;     /*!< Original directory indice(s) (from header) */
+/*@owned@*/ const char ** fmd5s;/*!< File MD5 sum(s) (from header) */
+/*@owned@*/ const char ** flinks;      /*!< File link(s) (from header) */
+/* XXX setuid/setgid bits are turned off if fuser/fgroup doesn't map. */
+    uint_16 * fmodes;          /*!< File mode(s) (from header) */
+/*@owned@*/ char * fstates;    /*!< File state(s) (from header) */
+/*@owned@*/ const char ** fuser;       /*!< File owner(s) */
+/*@owned@*/ const char ** fgroup;      /*!< File group(s) */
+/*@owned@*/ const char ** flangs;      /*!< File lang(s) */
+    int fc;                    /*!< No. of files. */
+    int dc;                    /*!< No. of directories. */
+    int bnlmax;                        /*!< Length (in bytes) of longest base name. */
+    int dnlmax;                        /*!< Length (in bytes) of longest dir name. */
+    int astriplen;
+    int striplen;
+    int scriptArg;
+    unsigned int archiveSize;
+    mode_t dperms;             /*!< Directory perms (0755) if not mapped. */
+    mode_t fperms;             /*!< File perms (0644) if not mapped. */
+/*@owned@*/ const char ** apath;
+    int mapflags;
+/*@owned@*/ int * fmapflags;
+    uid_t uid;
+/*@owned@*/ /*@null@*/ uid_t * fuids;  /*!< File uid(s) */
+    gid_t gid;
+/*@owned@*/ /*@null@*/ gid_t * fgids;  /*!< File gid(s) */
+    int magic;
+#define        TFIMAGIC        0x09697923
+/*@owned@*/ FSM_t fsm;         /*!< File state machine data. */
+
+  /* these are for TR_ADDED packages */
+/*@dependent@*/ struct availablePackage * ap;
+/*@owned@*/ struct sharedFileInfo * replaced;
+/*@owned@*/ uint_32 * replacedSizes;
+  /* for TR_REMOVED packages */
+    unsigned int record;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Load data from header into transaction file element info.
+ * @param h            header
+ * @param fi           transaction element file info
+ */
+void loadFi(Header h, TFI_t fi)
+       /*@modifies h, fi @*/;
+
+/**
+ * Destroy transaction element file info.
+ * @param fi           transaction element file info
+ */
+void freeFi(TFI_t fi)
+       /*@modifies fi @*/;
+
+/**
+ * Return formatted string representation of package disposition.
+ * @param a            package dispostion
+ * @return             formatted string
+ */
+/*@observer@*/ const char *const fiTypeString(TFI_t fi);
+
+/**
+ * Return formatted string representation of file disposition.
+ * @param a            file dispostion
+ * @return             formatted string
+ */
+/*@observer@*/ const char *const fileActionString(fileAction a);
+
+/**
+ * Install binary package (from transaction set).
+ * @param ts           transaction set
+ * @param fi           transaction element file info
+ * @return             0 on success, 1 on bad magic, 2 on error
+ */
+int installBinaryPackage(const rpmTransactionSet ts, TFI_t fi);
+
+/**
+ * Erase binary package (from transaction set).
+ * @param ts           transaction set
+ * @param fi           transaction element file info
+ * @param pkgKey       package private data
+ * @return             0 on success
+ */
+int removeBinaryPackage(const rpmTransactionSet ts, TFI_t fi);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* H_ROLLBACK */
diff --git a/lib/rollback.c b/lib/rollback.c
deleted file mode 100644 (file)
index 6fadd03..0000000
+++ /dev/null
@@ -1,335 +0,0 @@
-/** \ingroup rpmtrans payload
- * \file lib/rollback.c
- */
-
-#include "system.h"
-
-#include <rpmlib.h>
-
-#include "rollback.h"
-
-#include "debug.h"
-
-/*@access Header @*/           /* compared with NULL */
-/*@access rpmTransactionSet @*/        /* compared with NULL */
-/*@access TFI_t @*/            /* compared with NULL */
-
-static /*@null@*/ void * _free(/*@only@*/ /*@null@*/ const void * this) {
-    if (this)   free((void *)this);
-    return NULL;
-}
-
-void loadFi(Header h, TFI_t fi)
-{
-    HGE_t hge;
-    HFD_t hfd;
-    uint_32 * uip;
-    int len;
-    int rc;
-    int i;
-    
-    if (fi->fsm == NULL)
-       fi->fsm = newFSM();
-
-    /* XXX avoid gcc noise on pointer (4th arg) cast(s) */
-    hge = (fi->type == TR_ADDED)
-       ? (HGE_t) headerGetEntryMinMemory : (HGE_t) headerGetEntry;
-    fi->hge = hge;
-
-    fi->hfd = hfd = headerFreeData;
-
-    if (h && fi->h == NULL)    fi->h = headerLink(h);
-
-    /* Duplicate name-version-release so that headers can be free'd. */
-    hge(fi->h, RPMTAG_NAME, NULL, (void **) &fi->name, NULL);
-    fi->name = xstrdup(fi->name);
-    hge(fi->h, RPMTAG_VERSION, NULL, (void **) &fi->version, NULL);
-    fi->version = xstrdup(fi->version);
-    hge(fi->h, RPMTAG_RELEASE, NULL, (void **) &fi->release, NULL);
-    fi->release = xstrdup(fi->release);
-
-    /* -1 means not found */
-    rc = hge(fi->h, RPMTAG_EPOCH, NULL, (void **) &uip, NULL);
-    fi->epoch = (rc ? *uip : -1);
-    /* 0 means unknown */
-    rc = hge(fi->h, RPMTAG_ARCHIVESIZE, NULL, (void **) &uip, NULL);
-    fi->archiveSize = (rc ? *uip : 0);
-
-    if (!hge(fi->h, RPMTAG_BASENAMES, NULL, (void **) &fi->bnl, &fi->fc)) {
-       fi->dc = 0;
-       fi->fc = 0;
-       return;
-    }
-
-    hge(fi->h, RPMTAG_DIRINDEXES, NULL, (void **) &fi->dil, NULL);
-    hge(fi->h, RPMTAG_DIRNAMES, NULL, (void **) &fi->dnl, &fi->dc);
-    hge(fi->h, RPMTAG_FILEMODES, NULL, (void **) &fi->fmodes, NULL);
-    hge(fi->h, RPMTAG_FILEFLAGS, NULL, (void **) &fi->fflags, NULL);
-    hge(fi->h, RPMTAG_FILESIZES, NULL, (void **) &fi->fsizes, NULL);
-    hge(fi->h, RPMTAG_FILESTATES, NULL, (void **) &fi->fstates, NULL);
-
-    fi->action = FA_UNKNOWN;
-    fi->flags = 0;
-
-    /* actions is initialized earlier for added packages */
-    if (fi->actions == NULL)
-       fi->actions = xcalloc(fi->fc, sizeof(*fi->actions));
-
-    switch (fi->type) {
-    case TR_ADDED:
-       fi->mapflags = CPIO_MAP_PATH | CPIO_MAP_MODE | CPIO_MAP_UID | CPIO_MAP_GID;
-       hge(fi->h, RPMTAG_FILEMD5S, NULL, (void **) &fi->fmd5s, NULL);
-       hge(fi->h, RPMTAG_FILELINKTOS, NULL, (void **) &fi->flinks, NULL);
-       hge(fi->h, RPMTAG_FILELANGS, NULL, (void **) &fi->flangs, NULL);
-       hge(fi->h, RPMTAG_FILEMTIMES, NULL, (void **) &fi->fmtimes, NULL);
-
-       /* 0 makes for noops */
-       fi->replacedSizes = xcalloc(fi->fc, sizeof(*fi->replacedSizes));
-
-       break;
-    case TR_REMOVED:
-       fi->mapflags = CPIO_MAP_PATH;
-       hge(fi->h, RPMTAG_FILEMD5S, NULL, (void **) &fi->fmd5s, NULL);
-       hge(fi->h, RPMTAG_FILELINKTOS, NULL, (void **) &fi->flinks, NULL);
-       fi->fsizes = memcpy(xmalloc(fi->fc * sizeof(*fi->fsizes)),
-                               fi->fsizes, fi->fc * sizeof(*fi->fsizes));
-       fi->fflags = memcpy(xmalloc(fi->fc * sizeof(*fi->fflags)),
-                               fi->fflags, fi->fc * sizeof(*fi->fflags));
-       fi->fmodes = memcpy(xmalloc(fi->fc * sizeof(*fi->fmodes)),
-                               fi->fmodes, fi->fc * sizeof(*fi->fmodes));
-       /* XXX there's a tedious segfault here for some version(s) of rpm */
-       if (fi->fstates)
-           fi->fstates = memcpy(xmalloc(fi->fc * sizeof(*fi->fstates)),
-                               fi->fstates, fi->fc * sizeof(*fi->fstates));
-       else
-           fi->fstates = xcalloc(1, fi->fc * sizeof(*fi->fstates));
-       fi->dil = memcpy(xmalloc(fi->fc * sizeof(*fi->dil)),
-                               fi->dil, fi->fc * sizeof(*fi->dil));
-       headerFree(fi->h);
-       fi->h = NULL;
-       break;
-    }
-
-    fi->dnlmax = -1;
-    for (i = 0; i < fi->dc; i++) {
-       if ((len = strlen(fi->dnl[i])) > fi->dnlmax)
-           fi->dnlmax = len;
-    }
-
-    fi->bnlmax = -1;
-    for (i = 0; i < fi->fc; i++) {
-       if ((len = strlen(fi->bnl[i])) > fi->bnlmax)
-           fi->bnlmax = len;
-    }
-
-    fi->dperms = 0755;
-    fi->fperms = 0644;
-
-    return;
-}
-
-void freeFi(TFI_t fi)
-{
-    HFD_t hfd = (fi->hfd ? fi->hfd : headerFreeData);
-
-    fi->name = _free(fi->name);
-    fi->version = _free(fi->version);
-    fi->release = _free(fi->release);
-    fi->actions = _free(fi->actions);
-    fi->replacedSizes = _free(fi->replacedSizes);
-    fi->replaced = _free(fi->replaced);
-
-    fi->bnl = hfd(fi->bnl, -1);
-    fi->dnl = hfd(fi->dnl, -1);
-    fi->obnl = hfd(fi->obnl, -1);
-    fi->odnl = hfd(fi->odnl, -1);
-    fi->flinks = hfd(fi->flinks, -1);
-    fi->fmd5s = hfd(fi->fmd5s, -1);
-    fi->fuser = hfd(fi->fuser, -1);
-    fi->fgroup = hfd(fi->fgroup, -1);
-    fi->flangs = hfd(fi->flangs, -1);
-
-    fi->apath = _free(fi->apath);
-    fi->fuids = _free(fi->fuids);
-    fi->fgids = _free(fi->fgids);
-    fi->fmapflags = _free(fi->fmapflags);
-
-    fi->fsm = freeFSM(fi->fsm);
-
-    switch (fi->type) {
-    case TR_ADDED:
-           break;
-    case TR_REMOVED:
-       fi->fsizes = hfd(fi->fsizes, -1);
-       fi->fflags = hfd(fi->fflags, -1);
-       fi->fmodes = hfd(fi->fmodes, -1);
-       fi->fstates = hfd(fi->fstates, -1);
-       fi->dil = hfd(fi->dil, -1);
-       break;
-    }
-    if (fi->h) {
-       headerFree(fi->h); fi->h = NULL;
-    }
-}
-
-/*@observer@*/ const char *const fiTypeString(TFI_t fi) {
-    switch(fi->type) {
-    case TR_ADDED:     return " install";
-    case TR_REMOVED:   return "   erase";
-    default:           return "???";
-    }
-    /*@noteached@*/
-}
-
-/*@observer@*/ const char *const fileStageString(fileStage a) {
-    switch(a) {
-    case FSM_UNKNOWN:  return "unknown";
-
-    case FSM_PKGINSTALL:return "pkginstall";
-    case FSM_PKGERASE: return "pkgerase";
-    case FSM_PKGBUILD: return "pkgbuild";
-    case FSM_PKGCOMMIT:        return "pkgcommit";
-    case FSM_PKGUNDO:  return "pkgundo";
-
-    case FSM_CREATE:   return "create";
-    case FSM_INIT:     return "init";
-    case FSM_MAP:      return "map";
-    case FSM_MKDIRS:   return "mkdirs";
-    case FSM_RMDIRS:   return "rmdirs";
-    case FSM_PRE:      return "pre";
-    case FSM_PROCESS:  return "process";
-    case FSM_POST:     return "post";
-    case FSM_MKLINKS:  return "mklinks";
-    case FSM_NOTIFY:   return "notify";
-    case FSM_UNDO:     return "undo";
-    case FSM_FINI:     return "fini";
-    case FSM_COMMIT:   return "commit";
-    case FSM_DESTROY:  return "destroy";
-    case FSM_VERIFY:   return "verify";
-
-    case FSM_UNLINK:   return "Unlink";
-    case FSM_RENAME:   return "Rename";
-    case FSM_MKDIR:    return "Mkdir";
-    case FSM_RMDIR:    return "rmdir";
-    case FSM_CHOWN:    return "chown";
-    case FSM_LCHOWN:   return "lchown";
-    case FSM_CHMOD:    return "chmod";
-    case FSM_UTIME:    return "utime";
-    case FSM_SYMLINK:  return "symlink";
-    case FSM_LINK:     return "Link";
-    case FSM_MKFIFO:   return "mkfifo";
-    case FSM_MKNOD:    return "mknod";
-    case FSM_LSTAT:    return "Lstat";
-    case FSM_STAT:     return "Stat";
-    case FSM_READLINK: return "Readlink";
-    case FSM_CHROOT:   return "chroot";
-
-    case FSM_NEXT:     return "next";
-    case FSM_EAT:      return "eat";
-    case FSM_POS:      return "pos";
-    case FSM_PAD:      return "pad";
-    case FSM_TRAILER:  return "trailer";
-    case FSM_HREAD:    return "hread";
-    case FSM_HWRITE:   return "hwrite";
-    case FSM_DREAD:    return "Fread";
-    case FSM_DWRITE:   return "Fwrite";
-
-    case FSM_ROPEN:    return "Fopen";
-    case FSM_READ:     return "Fread";
-    case FSM_RCLOSE:   return "Fclose";
-    case FSM_WOPEN:    return "Fopen";
-    case FSM_WRITE:    return "Fwrite";
-    case FSM_WCLOSE:   return "Fclose";
-
-    default:           return "???";
-    }
-    /*@noteached@*/
-}
-
-/*@obserever@*/ const char *const fileActionString(fileAction a)
-{
-    switch (a) {
-    case FA_UNKNOWN:   return "unknown";
-    case FA_CREATE:    return "create";
-    case FA_BACKUP:    return "backup";
-    case FA_SAVE:      return "save";
-    case FA_SKIP:      return "skip";
-    case FA_ALTNAME:   return "altname";
-    case FA_ERASE:     return "erase";
-    case FA_SKIPNSTATE: return "skipnstate";
-    case FA_SKIPNETSHARED: return "skipnetshared";
-    case FA_SKIPMULTILIB: return "skipmultilib";
-    default:           return "???";
-    }
-    /*@notreached@*/
-}
-
-#ifdef DYING
-/**
- */
-struct pkgIterator {
-/*@dependent@*/ /*@kept@*/ TFI_t fi;
-    int i;
-};
-
-/**
- */
-static /*@null@*/ void * pkgFreeIterator(/*@only@*/ /*@null@*/ void * this) {
-    return _free(this);
-}
-
-/**
- */
-static /*@only@*/ void * pkgInitIterator(/*@kept@*/ rpmTransactionSet ts,
-       /*@kept@*/ TFI_t fi)
-{
-    struct pkgIterator * pi = NULL;
-    if (ts && fi) {
-       pi = xcalloc(sizeof(*pi), 1);
-       pi->fi = fi;
-       switch (fi->type) {
-       case TR_ADDED:  pi->i = 0;      break;
-       case TR_REMOVED:        pi->i = fi->fc; break;
-       }
-    }
-    return pi;
-}
-
-/**
- */
-static int pkgNextIterator(/*@null@*/ void * this) {
-    struct pkgIterator * pi = this;
-    int i = -1;
-
-    if (pi) {
-       TFI_t fi = pi->fi;
-       switch (fi->type) {
-       case TR_ADDED:
-           if (pi->i < fi->fc)
-               i = pi->i++;
-           break;
-       case TR_REMOVED:
-           if (pi->i >= 0)
-               i = --pi->i;
-           break;
-       }
-    }
-    return i;
-}
-
-int pkgActions(const rpmTransactionSet ts, TFI_t fi, fileStage a)
-{
-    int rc = 0;
-
-    if (fi->actions) {
-       void * pi = pkgInitIterator(ts, fi);
-       int i;
-       while ((i = pkgNextIterator(pi)) != -1) {
-           if (pkgAction(ts, fi, i, a))
-               rc++;
-       }
-       pi = pkgFreeIterator(pi);
-    }
-    return rc;
-}
-#endif
diff --git a/lib/rollback.h b/lib/rollback.h
deleted file mode 100644 (file)
index a772c9a..0000000
+++ /dev/null
@@ -1,336 +0,0 @@
-#ifndef H_ROLLBACK
-#define H_ROLLBACK
-
-/** \file lib/rollback.h
- */
-
-#include "depends.h"
-#include "install.h"
-
-/**
- */
-typedef /*@abstract@*/ struct fsm_s * FSM_t;
-
-#include "cpio.h"
-
-/**
- */
-#define        FSM_VERBOSE     0x8000
-#define        FSM_INTERNAL    0x4000
-#define        FSM_SYSCALL     0x2000
-#define        FSM_DEAD        0x1000
-#define        _fv(_a)         ((_a) | FSM_VERBOSE)
-#define        _fi(_a)         ((_a) | FSM_INTERNAL)
-#define        _fs(_a)         ((_a) | (FSM_INTERNAL | FSM_SYSCALL))
-#define        _fd(_a)         ((_a) | (FSM_INTERNAL | FSM_DEAD))
-typedef enum fileStage_e {
-    FSM_UNKNOWN =   0,
-    FSM_INIT   =  _fd(1),
-    FSM_PRE    =  _fd(2),
-    FSM_PROCESS        =  _fv(3),
-    FSM_POST   =  _fd(4),
-    FSM_UNDO   =  5,
-    FSM_FINI   =  6,
-
-    FSM_PKGINSTALL     = _fd(7),
-    FSM_PKGERASE       = _fd(8),
-    FSM_PKGBUILD       = _fd(9),
-    FSM_PKGCOMMIT      = _fd(10),
-    FSM_PKGUNDO                = _fd(11),
-
-    FSM_CREATE =  _fd(17),
-    FSM_MAP    =  _fd(18),
-    FSM_MKDIRS =  _fi(19),
-    FSM_RMDIRS =  _fi(20),
-    FSM_MKLINKS        =  _fi(21),
-    FSM_NOTIFY =  _fd(22),
-    FSM_DESTROY        =  _fd(23),
-    FSM_VERIFY =  _fd(24),
-    FSM_COMMIT =  _fd(25),
-
-    FSM_UNLINK =  _fs(33),
-    FSM_RENAME =  _fs(34),
-    FSM_MKDIR  =  _fs(35),
-    FSM_RMDIR  =  _fs(36),
-    FSM_CHOWN  =  _fs(37),
-    FSM_LCHOWN =  _fs(38),
-    FSM_CHMOD  =  _fs(39),
-    FSM_UTIME  =  _fs(40),
-    FSM_SYMLINK        =  _fs(41),
-    FSM_LINK   =  _fs(42),
-    FSM_MKFIFO =  _fs(43),
-    FSM_MKNOD  =  _fs(44),
-    FSM_LSTAT  =  _fs(45),
-    FSM_STAT   =  _fs(46),
-    FSM_READLINK=  _fs(47),
-    FSM_CHROOT =  _fs(48),
-
-    FSM_NEXT   =  _fd(65),
-    FSM_EAT    =  _fd(66),
-    FSM_POS    =  _fd(67),
-    FSM_PAD    =  _fd(68),
-    FSM_TRAILER        =  _fd(69),
-    FSM_HREAD  =  _fd(70),
-    FSM_HWRITE =  _fd(71),
-    FSM_DREAD  =  _fs(72),
-    FSM_DWRITE =  _fs(73),
-
-    FSM_ROPEN  =  _fs(129),
-    FSM_READ   =  _fs(130),
-    FSM_RCLOSE =  _fs(131),
-    FSM_WOPEN  =  _fs(132),
-    FSM_WRITE  =  _fs(133),
-    FSM_WCLOSE =  _fs(134),
-} fileStage;
-#undef _fi
-
-/**
- * File disposition(s) during package install/erase transaction.
- */
-typedef enum fileAction_e {
-    FA_UNKNOWN = 0,    /*!< initial action (default action for source rpm) */
-    FA_CREATE,         /*!< ... to be replaced. */
-    FA_BACKUP,         /*!< ... renamed with ".rpmorig" extension. */
-    FA_SAVE,           /*!< ... renamed with ".rpmsave" extension. */
-    FA_SKIP,           /*!< ... already replaced, don't remove. */
-    FA_ALTNAME,                /*!< ... create with ".rpmnew" extension. */
-    FA_ERASE,          /*!< ... to be removed. */
-    FA_SKIPNSTATE,     /*!< ... untouched, state "not installed". */
-    FA_SKIPNETSHARED,  /*!< ... untouched, state "netshared". */
-    FA_SKIPMULTILIB,   /*!< ... untouched. @todo state "multilib" ???. */
-} fileAction;
-
-
-#define XFA_SKIPPING(_a)       \
-    ((_a) == FA_SKIP || (_a) == FA_SKIPNSTATE || (_a) == FA_SKIPNETSHARED || (_a) == FA_SKIPMULTILIB)
-
-/**
- */
-typedef        enum rollbackDir_e {
-    ROLLBACK_SAVE      = 1,    /*!< Save files. */
-    ROLLBACK_RESTORE   = 2,    /*!< Restore files. */
-} rollbackDir;
-
-/**
- * File types.
- * These are the types of files used internally by rpm. The file
- * type is determined by applying stat(2) macros like S_ISDIR to
- * the file mode tag from a header. The values are arbitrary,
- * but are identical to the linux stat(2) file types.
- */
-enum fileTypes {
-    PIPE       = 1,    /*!< pipe/fifo */
-    CDEV       = 2,    /*!< character device */
-    XDIR       = 4,    /*!< directory */
-    BDEV       = 6,    /*!< block device */
-    REG                = 8,    /*!< regular file */
-    LINK       = 10,   /*!< hard link */
-    SOCK       = 12,   /*!< socket */
-};
-
-/**
- */
-struct transactionFileInfo_s {
-  /* for all packages */
-    enum rpmTransactionType type;
-    fileAction action;         /*!< File disposition default. */
-/*@owned@*/ fileAction * actions;      /*!< File disposition(s) */
-/*@owned@*/ struct fingerPrint_s * fps;        /*!< File fingerprint(s) */
-    HGE_t hge;                 /*!< Vector to headerGetEntry() */
-    HFD_t hfd;                 /*!< Vector to headerFreeData() */
-    Header h;                  /*!< Package header */
-/*@owned@*/ const char * name;
-/*@owned@*/ const char * version;
-/*@owned@*/ const char * release;
-    int_32 epoch;
-    uint_32 flags;             /*!< File flag default. */
-    const uint_32 * fflags;    /*!< File flag(s) (from header) */
-    const uint_32 * fsizes;    /*!< File size(s) (from header) */
-    const uint_32 * fmtimes;   /*!< File modification time(s) (from header) */
-/*@owned@*/ const char ** bnl; /*!< Base name(s) (from header) */
-/*@owned@*/ const char ** dnl; /*!< Directory name(s) (from header) */
-    int_32 * dil;              /*!< Directory indice(s) (from header) */
-/*@owned@*/ const char ** obnl;        /*!< Original base name(s) (from header) */
-/*@owned@*/ const char ** odnl;        /*!< Original directory name(s) (from header) */
-    int_32 * odil;     /*!< Original directory indice(s) (from header) */
-/*@owned@*/ const char ** fmd5s;/*!< File MD5 sum(s) (from header) */
-/*@owned@*/ const char ** flinks;      /*!< File link(s) (from header) */
-/* XXX setuid/setgid bits are turned off if fuser/fgroup doesn't map. */
-    uint_16 * fmodes;          /*!< File mode(s) (from header) */
-/*@owned@*/ char * fstates;    /*!< File state(s) (from header) */
-/*@owned@*/ const char ** fuser;       /*!< File owner(s) */
-/*@owned@*/ const char ** fgroup;      /*!< File group(s) */
-/*@owned@*/ const char ** flangs;      /*!< File lang(s) */
-    int fc;                    /*!< No. of files. */
-    int dc;                    /*!< No. of directories. */
-    int bnlmax;                        /*!< Length (in bytes) of longest base name. */
-    int dnlmax;                        /*!< Length (in bytes) of longest dir name. */
-    int astriplen;
-    int striplen;
-    int scriptArg;
-    unsigned int archiveSize;
-    mode_t dperms;             /*!< Directory perms (0755) if not mapped. */
-    mode_t fperms;             /*!< File perms (0644) if not mapped. */
-/*@owned@*/ const char ** apath;
-    int mapflags;
-/*@owned@*/ int * fmapflags;
-    uid_t uid;
-/*@owned@*/ /*@null@*/ uid_t * fuids;  /*!< File uid(s) */
-    gid_t gid;
-/*@owned@*/ /*@null@*/ gid_t * fgids;  /*!< File gid(s) */
-    int magic;
-#define        TFIMAGIC        0x09697923
-/*@owned@*/ FSM_t fsm;         /*!< File state machine data. */
-
-  /* these are for TR_ADDED packages */
-/*@dependent@*/ struct availablePackage * ap;
-/*@owned@*/ struct sharedFileInfo * replaced;
-/*@owned@*/ uint_32 * replacedSizes;
-  /* for TR_REMOVED packages */
-    unsigned int record;
-};
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * Create file state machine instance.
- * @return             file state machine data
- */
-/*@only@*/ /*@null@*/ FSM_t newFSM(void);
-
-/**
- * Destroy file state machine instance.
- * @param fsm          file state machine data
- * @return             always NULL
- */
-/*@null@*/ FSM_t freeFSM(/*@only@*/ /*@null@*/ FSM_t fsm);
-
-/**
- * Load data from header into transaction file element info.
- * @param h            header
- * @param fi           transaction element file info
- */
-void loadFi(Header h, TFI_t fi)
-       /*@modifies h, fi @*/;
-
-/**
- * Destroy transaction element file info.
- * @param fi           transaction element file info
- */
-void freeFi(TFI_t fi)
-       /*@modifies fi @*/;
-
-/**
- * Return formatted string representation of package disposition.
- * @param a            package dispostion
- * @return             formatted string
- */
-/*@observer@*/ const char *const fiTypeString(TFI_t fi);
-
-/**
- * Return formatted string representation of file stages.
- * @param a            file stage
- * @return             formatted string
- */
-/*@observer@*/ const char *const fileStageString(fileStage a);
-
-/**
- * Return formatted string representation of file disposition.
- * @param a            file dispostion
- * @return             formatted string
- */
-/*@observer@*/ const char *const fileActionString(fileAction a);
-
-#ifdef DYING
-/**
- * Perform package install/remove actions for s single file.
- * @todo Eliminate.
- * @param ts           transaction set
- * @param fi           transaction element file info
- * @param i            file index
- * @param a            file stage
- * @return             0 on success, 1 on failure
- */
-int pkgAction(const rpmTransactionSet ts, TFI_t fi, int i, fileStage a);
-
-/**
- * Perform package pre-install and remove actions.
- * @todo Eliminate.
- * @param ts           transaction set
- * @param fi           transaction element file info
- * @param a            file stage
- * @return             0 on success, otherwise no. of failures
- */
-int pkgActions(const rpmTransactionSet ts, TFI_t fi, fileStage a);
-#endif
-
-/**
- * Load external data into file state machine.
- * @param fsm          file state machine data
- * @param goal
- * @param ts           transaction set
- * @param fi           transaction element file info
- * @param archiveSize  pointer to archive size
- * @param failedFile   pointer to first file name that failed.
- * @return             0 on success
- */
-int fsmSetup(FSM_t fsm, fileStage goal,
-       /*@kept@*/ const rpmTransactionSet ts,
-       /*@kept@*/ const TFI_t fi,
-       FD_t cfd,
-       /*@out@*/ unsigned int * archiveSize,
-       /*@out@*/ const char ** failedFile)
-               /*@modifies fsm, *archiveSize, *failedFile  @*/;
-
-/**
- * Clean file state machine.
- * @param fsm          file state machine data
- * @return             0 on success
- */
-int fsmTeardown(FSM_t fsm)
-               /*@modifies fsm @*/;
-
-/**
- * Retrieve transaction set from file state machine iterator.
- * @param fsm          file state machine data
- * @return             transaction set
- */
-/*@kept@*/ rpmTransactionSet fsmGetTs(const FSM_t fsm) /*@*/;
-
-/**
- * Retrieve transaction element file info from file state machine iterator.
- * @param fsm          file state machine data
- * @return             transaction element file info
- */
-/*@kept@*/ TFI_t fsmGetFi(const FSM_t fsm)     /*@*/;
-
-/**
- * Map next file path and action.
- * @param fsm          file state machine data
- */
-int fsmMapPath(FSM_t fsm)
-               /*@modifies fsm @*/;
-
-/**
- * Map file stat(2) info.
- * @param fsm          file state machine data
- */
-int fsmMapAttrs(FSM_t fsm)
-               /*@modifies fsm @*/;
-
-/**
- * File state machine driver.
- * @param fsm          file state machine data
- * @param stage                next stage
- * @return             0 on success
- */
-int fsmStage(FSM_t fsm, fileStage stage)
-               /*@modifies fsm @*/;
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* H_ROLLBACK */
index b1eb8ea..1ce0029 100644 (file)
@@ -868,6 +868,56 @@ int rpmInstallSourcePackage(const char * root, FD_t fd,
  */
 int rpmVersionCompare(Header first, Header second);
 
+/**
+ * File disposition(s) during package install/erase transaction.
+ */
+typedef enum fileAction_e {
+    FA_UNKNOWN = 0,    /*!< initial action for file ... */
+    FA_CREATE,         /*!< ... to be replaced. */
+    FA_BACKUP,         /*!< ... renamed with ".rpmorig" extension. */
+    FA_SAVE,           /*!< ... renamed with ".rpmsave" extension. */
+    FA_SKIP,           /*!< ... already replaced, don't remove. */
+    FA_ALTNAME,                /*!< ... create with ".rpmnew" extension. */
+    FA_ERASE,          /*!< ... to be removed. */
+    FA_SKIPNSTATE,     /*!< ... untouched, state "not installed". */
+    FA_SKIPNETSHARED,  /*!< ... untouched, state "netshared". */
+    FA_SKIPMULTILIB,   /*!< ... untouched. @todo state "multilib" ???. */
+} fileAction;
+
+#define XFA_SKIPPING(_a)       \
+    ((_a) == FA_SKIP || (_a) == FA_SKIPNSTATE || (_a) == FA_SKIPNETSHARED || (_a) == FA_SKIPMULTILIB)
+
+/**
+ * File types.
+ * These are the types of files used internally by rpm. The file
+ * type is determined by applying stat(2) macros like S_ISDIR to
+ * the file mode tag from a header. The values are arbitrary,
+ * but are identical to the linux stat(2) file types.
+ */
+typedef enum fileTypes_e {
+    PIPE       =  1,   /*!< pipe/fifo */
+    CDEV       =  2,   /*!< character device */
+    XDIR       =  4,   /*!< directory */
+    BDEV       =  6,   /*!< block device */
+    REG                =  8,   /*!< regular file */
+    LINK       = 10,   /*!< hard link */
+    SOCK       = 12,   /*!< socket */
+} fileTypes;
+
+/** \ingroup payload
+ * Iterator across package file info, forward on install, backward on erase.
+ */
+typedef /*@abstract@*/ struct fsmIterator_s * FSMI_t;
+
+/** \ingroup payload
+ * File state machine data.
+ */
+typedef /*@abstract@*/ struct fsm_s * FSM_t;
+
+/** \ingroup rpmtrans
+ */
+typedef /*@abstract@*/ struct transactionFileInfo_s * TFI_t;
+
 /** \ingroup rpmtrans
  * The RPM Transaction Set.
  * Transaction sets are inherently unordered! RPM may reorder transaction
diff --git a/lib/scriptlet.c b/lib/scriptlet.c
new file mode 100644 (file)
index 0000000..d37c0fc
--- /dev/null
@@ -0,0 +1,453 @@
+/** \ingroup rpmtrans payload
+ * \file lib/scriptlet.c
+ */
+
+#include "system.h"
+
+#include <rpmlib.h>
+#include <rpmurl.h>
+#include <rpmmacro.h>  /* XXX for rpmExpand */
+
+#include "depends.h"   /* XXX for headerMatchesDepFlags */
+#include "scriptlet.h"
+#include "misc.h"      /* XXX for makeTempFile, doputenv */
+#include "debug.h"
+
+/*@access Header@*/            /* XXX compared with NULL */
+
+static char * SCRIPT_PATH = "PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin";
+
+/**
+ * Return scriptlet name from tag.
+ * @param tag          scriptlet tag
+ * @return             name of scriptlet
+ */
+static /*@observer@*/ const char * const tag2sln(int tag)
+{
+    switch (tag) {
+    case RPMTAG_PREIN:         return "%pre";
+    case RPMTAG_POSTIN:                return "%post";
+    case RPMTAG_PREUN:         return "%preun";
+    case RPMTAG_POSTUN:                return "%postun";
+    case RPMTAG_VERIFYSCRIPT:  return "%verify";
+    }
+    return "%unknownscript";
+}
+
+/**
+ * Run scriptlet with args.
+ *
+ * Run a script with an interpreter. If the interpreter is not specified,
+ * /bin/sh will be used. If the interpreter is /bin/sh, then the args from
+ * the header will be ignored, passing instead arg1 and arg2.
+ * 
+ * @param ts           transaction set
+ * @param h            header
+ * @param sln          name of scriptlet section
+ * @param progArgc     no. of args from header
+ * @param progArgv     args from header, progArgv[0] is the interpreter to use
+ * @param script       scriptlet from header
+ * @param arg1         no. instances of package installed after scriptlet exec
+ *                     (-1 is no arg)
+ * @param arg2         ditto, but for the target package
+ * @return             0 on success, 1 on error
+ */
+static int runScript(const rpmTransactionSet ts, Header h,
+               const char * sln,
+               int progArgc, const char ** progArgv, 
+               const char * script, int arg1, int arg2)
+{
+    const char ** argv = NULL;
+    int argc = 0;
+    const char ** prefixes = NULL;
+    int numPrefixes;
+    const char * oldPrefix;
+    int maxPrefixLength;
+    int len;
+    char * prefixBuf = NULL;
+    pid_t child;
+    int status = 0;
+    const char * fn = NULL;
+    int i;
+    int freePrefixes = 0;
+    FD_t out;
+    int rc = 0;
+    const char *n, *v, *r;
+
+    if (!progArgv && !script)
+       return 0;
+
+    if (!progArgv) {
+       argv = alloca(5 * sizeof(char *));
+       argv[0] = "/bin/sh";
+       argc = 1;
+    } else {
+       argv = alloca((progArgc + 4) * sizeof(char *));
+       memcpy(argv, progArgv, progArgc * sizeof(char *));
+       argc = progArgc;
+    }
+
+    headerNVR(h, &n, &v, &r);
+    if (headerGetEntry(h, RPMTAG_INSTPREFIXES, NULL, (void **) &prefixes,
+                      &numPrefixes)) {
+       freePrefixes = 1;
+    } else if (headerGetEntry(h, RPMTAG_INSTALLPREFIX, NULL, 
+                       (void **) &oldPrefix, NULL))
+    {
+       prefixes = &oldPrefix;
+       numPrefixes = 1;
+    } else {
+       numPrefixes = 0;
+    }
+
+    maxPrefixLength = 0;
+    for (i = 0; i < numPrefixes; i++) {
+       len = strlen(prefixes[i]);
+       if (len > maxPrefixLength) maxPrefixLength = len;
+    }
+    prefixBuf = alloca(maxPrefixLength + 50);
+
+    if (script) {
+       FD_t fd;
+       if (makeTempFile((!ts->chrootDone ? ts->rootDir : "/"), &fn, &fd)) {
+           if (freePrefixes) free(prefixes);
+           return 1;
+       }
+
+       if (rpmIsDebug() &&
+           (!strcmp(argv[0], "/bin/sh") || !strcmp(argv[0], "/bin/bash")))
+           (void)Fwrite("set -x\n", sizeof(char), 7, fd);
+
+       (void)Fwrite(script, sizeof(script[0]), strlen(script), fd);
+       Fclose(fd);
+
+       {   const char * sn = fn;
+           if (!ts->chrootDone &&
+               !(ts->rootDir[0] == '/' && ts->rootDir[1] == '\0'))
+           {
+               sn += strlen(ts->rootDir)-1;
+           }
+           argv[argc++] = sn;
+       }
+
+       if (arg1 >= 0) {
+           char *av = alloca(20);
+           sprintf(av, "%d", arg1);
+           argv[argc++] = av;
+       }
+       if (arg2 >= 0) {
+           char *av = alloca(20);
+           sprintf(av, "%d", arg2);
+           argv[argc++] = av;
+       }
+    }
+
+    argv[argc] = NULL;
+
+    if (ts->scriptFd != NULL) {
+       if (rpmIsVerbose()) {
+           out = fdDup(Fileno(ts->scriptFd));
+       } else {
+           out = Fopen("/dev/null", "w.fdio");
+           if (Ferror(out)) {
+               out = fdDup(Fileno(ts->scriptFd));
+           }
+       }
+    } else {
+       out = fdDup(STDOUT_FILENO);
+       out = fdLink(out, "runScript persist");
+    }
+    
+    if (!(child = fork())) {
+       const char * rootDir;
+       int pipes[2];
+
+       pipes[0] = pipes[1] = 0;
+       /* make stdin inaccessible */
+       pipe(pipes);
+       close(pipes[1]);
+       dup2(pipes[0], STDIN_FILENO);
+       close(pipes[0]);
+
+       if (ts->scriptFd != NULL) {
+           if (Fileno(ts->scriptFd) != STDERR_FILENO)
+               dup2(Fileno(ts->scriptFd), STDERR_FILENO);
+           if (Fileno(out) != STDOUT_FILENO)
+               dup2(Fileno(out), STDOUT_FILENO);
+           /* make sure we don't close stdin/stderr/stdout by mistake! */
+           if (Fileno(out) > STDERR_FILENO && Fileno(out) != Fileno(ts->scriptFd)) {
+               Fclose (out);
+           }
+           if (Fileno(ts->scriptFd) > STDERR_FILENO) {
+               Fclose (ts->scriptFd);
+           }
+       }
+
+       {   const char *ipath = rpmExpand("PATH=%{_install_script_path}", NULL);
+           const char *path = SCRIPT_PATH;
+
+           if (ipath && ipath[5] != '%')
+               path = ipath;
+           doputenv(path);
+           if (ipath)  free((void *)ipath);
+       }
+
+       for (i = 0; i < numPrefixes; i++) {
+           sprintf(prefixBuf, "RPM_INSTALL_PREFIX%d=%s", i, prefixes[i]);
+           doputenv(prefixBuf);
+
+           /* backwards compatibility */
+           if (i == 0) {
+               sprintf(prefixBuf, "RPM_INSTALL_PREFIX=%s", prefixes[i]);
+               doputenv(prefixBuf);
+           }
+       }
+
+       rootDir = ts->rootDir;
+       switch(urlIsURL(rootDir)) {
+       case URL_IS_PATH:
+           rootDir += sizeof("file://") - 1;
+           rootDir = strchr(rootDir, '/');
+           /*@fallthrough@*/
+       case URL_IS_UNKNOWN:
+           if (!ts->chrootDone && !(rootDir[0] == '/' && rootDir[1] == '\0')) {
+               /*@-unrecog@*/ chroot(rootDir); /*@=unrecog@*/
+           }
+           chdir("/");
+           execv(argv[0], (char *const *)argv);
+           break;
+       default:
+           break;
+       }
+
+       _exit(-1);
+       /*@notreached@*/
+    }
+
+    if (waitpid(child, &status, 0) < 0) {
+       rpmError(RPMERR_SCRIPT,
+                _("execution of %s scriptlet from %s-%s-%s failed, waitpid returned %s\n"),
+                sln, n, v, r, strerror (errno));
+       /* XXX what to do here? */
+       rc = 0;
+    } else {
+       if (!WIFEXITED(status) || WEXITSTATUS(status)) {
+           rpmError(RPMERR_SCRIPT,
+                    _("execution of %s scriptlet from %s-%s-%s failed, exit status %d\n"),
+                    sln, n, v, r, WEXITSTATUS(status));
+           rc = 1;
+       }
+    }
+
+    if (freePrefixes) free(prefixes);
+
+    Fclose(out);       /* XXX dup'd STDOUT_FILENO */
+    
+    if (script) {
+       if (!rpmIsDebug()) unlink(fn);
+       free((void *)fn);
+    }
+
+    return rc;
+}
+
+int runInstScript(const rpmTransactionSet ts, Header h,
+               int scriptTag, int progTag, int arg, int norunScripts)
+{
+    void ** programArgv;
+    int programArgc;
+    const char ** argv;
+    int programType;
+    char * script;
+    int rc;
+
+    if (norunScripts) return 0;
+
+    /* headerGetEntry() sets the data pointer to NULL if the entry does
+       not exist */
+    headerGetEntry(h, progTag, &programType, (void **) &programArgv,
+                  &programArgc);
+    headerGetEntry(h, scriptTag, NULL, (void **) &script, NULL);
+
+    if (programArgv && programType == RPM_STRING_TYPE) {
+       argv = alloca(sizeof(char *));
+       *argv = (const char *) programArgv;
+    } else {
+       argv = (const char **) programArgv;
+    }
+
+    rc = runScript(ts, h, tag2sln(scriptTag), programArgc, argv, script,
+               arg, -1);
+    programArgv = headerFreeData(programArgv, programType);
+    return rc;
+}
+
+/**
+ * @param ts           transaction set
+ * @param sense
+ * @param sourceH
+ * @param triggeredH
+ * @param arg1correction
+ * @param arg2
+ * @param triggersAlreadyRun
+ * @return
+ */
+static int handleOneTrigger(const rpmTransactionSet ts, int sense,
+                       Header sourceH, Header triggeredH,
+                       int arg1correction, int arg2,
+                       char * triggersAlreadyRun)
+{
+    const char ** triggerNames;
+    const char ** triggerEVR;
+    const char ** triggerScripts;
+    const char ** triggerProgs;
+    int_32 * triggerFlags;
+    int_32 * triggerIndices;
+    const char * triggerPackageName;
+    const char * sourceName;
+    int numTriggers;
+    int rc = 0;
+    int i;
+    int skip;
+
+    if (!headerGetEntry(triggeredH, RPMTAG_TRIGGERNAME, NULL, 
+                       (void **) &triggerNames, &numTriggers)) {
+       return 0;
+    }
+
+    headerNVR(sourceH, &sourceName, NULL, NULL);
+
+    headerGetEntry(triggeredH, RPMTAG_TRIGGERFLAGS, NULL, 
+                  (void **) &triggerFlags, NULL);
+    headerGetEntry(triggeredH, RPMTAG_TRIGGERVERSION, NULL, 
+                  (void **) &triggerEVR, NULL);
+
+    for (i = 0; i < numTriggers; i++) {
+
+       if (!(triggerFlags[i] & sense)) continue;
+       if (strcmp(triggerNames[i], sourceName)) continue;
+
+       /*
+        * For some reason, the TRIGGERVERSION stuff includes the name of
+        * the package which the trigger is based on. We need to skip
+        * over that here. I suspect that we'll change our minds on this
+        * and remove that, so I'm going to just 'do the right thing'.
+        */
+       skip = strlen(triggerNames[i]);
+       if (!strncmp(triggerEVR[i], triggerNames[i], skip) &&
+           (triggerEVR[i][skip] == '-'))
+           skip++;
+       else
+           skip = 0;
+
+       if (!headerMatchesDepFlags(sourceH, triggerNames[i],
+               triggerEVR[i] + skip, triggerFlags[i]))
+           continue;
+
+       headerGetEntry(triggeredH, RPMTAG_TRIGGERINDEX, NULL,
+                      (void **) &triggerIndices, NULL);
+       headerGetEntry(triggeredH, RPMTAG_TRIGGERSCRIPTS, NULL,
+                      (void **) &triggerScripts, NULL);
+       headerGetEntry(triggeredH, RPMTAG_TRIGGERSCRIPTPROG, NULL,
+                      (void **) &triggerProgs, NULL);
+
+       headerNVR(triggeredH, &triggerPackageName, NULL, NULL);
+
+       {   int arg1;
+           int index;
+
+           if ((arg1 = rpmdbCountPackages(ts->rpmdb, triggerPackageName)) < 0) {
+               rc = 1; /* XXX W2DO? same as "execution of script failed" */
+           } else {
+               arg1 += arg1correction;
+               index = triggerIndices[i];
+               if (!triggersAlreadyRun || !triggersAlreadyRun[index]) {
+                   rc = runScript(ts, triggeredH, "%trigger", 1,
+                           triggerProgs + index, triggerScripts[index], 
+                           arg1, arg2);
+                   if (triggersAlreadyRun) triggersAlreadyRun[index] = 1;
+               }
+           }
+       }
+
+       free(triggerScripts);
+       free(triggerProgs);
+
+       /* each target/source header pair can only result in a single
+          script being run */
+       break;
+    }
+
+    free(triggerNames);
+
+    return rc;
+}
+
+int runTriggers(const rpmTransactionSet ts, int sense, Header h,
+               int countCorrection)
+{
+    const char * name;
+    int numPackage;
+    int rc = 0;
+
+    headerNVR(h, &name, NULL, NULL);
+
+    numPackage = rpmdbCountPackages(ts->rpmdb, name) + countCorrection;
+    if (numPackage < 0)
+       return 1;
+
+    {  Header triggeredH;
+       rpmdbMatchIterator mi;
+
+       mi = rpmdbInitIterator(ts->rpmdb, RPMTAG_TRIGGERNAME, name, 0);
+       while((triggeredH = rpmdbNextIterator(mi)) != NULL) {
+           rc |= handleOneTrigger(ts, sense, h, triggeredH, 0, numPackage, 
+                              NULL);
+       }
+
+       rpmdbFreeIterator(mi);
+    }
+
+    return rc;
+}
+
+int runImmedTriggers(const rpmTransactionSet ts, int sense, Header h,
+                    int countCorrection)
+{
+    const char ** triggerNames;
+    int numTriggers;
+    int_32 * triggerIndices;
+    int numTriggerIndices;
+    char * triggersRun;
+    int rc = 0;
+
+    if (!headerGetEntry(h, RPMTAG_TRIGGERNAME, NULL, (void **) &triggerNames, 
+                       &numTriggers))
+       return 0;
+    headerGetEntry(h, RPMTAG_TRIGGERINDEX, NULL, (void **) &triggerIndices, 
+                  &numTriggerIndices);
+    triggersRun = alloca(sizeof(*triggersRun) * numTriggerIndices);
+    memset(triggersRun, 0, sizeof(*triggersRun) * numTriggerIndices);
+
+    {  Header sourceH = NULL;
+       int i;
+
+       for (i = 0; i < numTriggers; i++) {
+           rpmdbMatchIterator mi;
+           const char * name = triggerNames[i];
+
+           if (triggersRun[triggerIndices[i]]) continue;
+       
+           mi = rpmdbInitIterator(ts->rpmdb, RPMTAG_NAME, name, 0);
+
+           while((sourceH = rpmdbNextIterator(mi)) != NULL) {
+               rc |= handleOneTrigger(ts, sense, sourceH, h, 
+                                  countCorrection, rpmdbGetIteratorCount(mi),
+                                  triggersRun);
+           }
+
+           rpmdbFreeIterator(mi);
+       }
+    }
+    return rc;
+}
similarity index 59%
rename from lib/install.h
rename to lib/scriptlet.h
index c8860f5..6bc4f19 100644 (file)
@@ -1,32 +1,11 @@
-#ifndef H_INSTALL
-#define H_INSTALL
+#ifndef H_SCRIPTLET
+#define H_SCRIPTLET
 
-/** \file lib/install.h
+/** \file lib/scriptlet.h
  */
 
 #include <rpmlib.h>
 
-/**
- */
-struct sharedFile {
-    int mainFileNumber;
-    int secRecOffset;
-    int secFileNumber;
-} ;
-
-/**
- */
-struct sharedFileInfo {
-    int pkgFileNum;
-    int otherFileNum;
-    int otherPkg;
-    int isRemoved;
-};
-
-/**
- */
-typedef /*@abstract@*/ struct transactionFileInfo_s * TFI_t;
-
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -66,25 +45,8 @@ int runTriggers(const rpmTransactionSet ts, int sense, Header h,
 int runImmedTriggers(const rpmTransactionSet ts, int sense, Header h,
                int countCorrection);
 
-/**
- * Install binary package (from transaction set).
- * @param ts           transaction set
- * @param fi           transaction element file info
- * @return             0 on success, 1 on bad magic, 2 on error
- */
-int installBinaryPackage(const rpmTransactionSet ts, TFI_t fi);
-
-/**
- * Erase binary package (from transaction set).
- * @param ts           transaction set
- * @param fi           transaction element file info
- * @param pkgKey       package private data
- * @return             0 on success
- */
-int removeBinaryPackage(const rpmTransactionSet ts, TFI_t fi);
-
 #ifdef __cplusplus
 }
 #endif
 
-#endif /* H_INSTALL */
+#endif /* H_SCRIPTLET */
index a2b6260..f13d9ed 100644 (file)
@@ -7,7 +7,7 @@
 #include <rpmlib.h>
 #include <rpmmacro.h>  /* XXX for rpmExpand */
 
-#include "rollback.h"
+#include "psm.h"
 #include "fprint.h"
 #include "hash.h"
 #include "md5.h"
@@ -242,7 +242,7 @@ void rpmProblemSetFree(rpmProblemSet probs)
     free(probs);
 }
 
-static /*@observer@*/ const char *const ftstring (enum fileTypes ft)
+static /*@observer@*/ const char *const ftstring (fileTypes ft)
 {
     switch (ft) {
     case XDIR: return "directory";
@@ -257,7 +257,7 @@ static /*@observer@*/ const char *const ftstring (enum fileTypes ft)
     /*@notreached@*/
 }
 
-static enum fileTypes whatis(uint_16 mode)
+static fileTypes whatis(uint_16 mode)
 {
     if (S_ISDIR(mode)) return XDIR;
     if (S_ISCHR(mode)) return CDEV;
@@ -502,7 +502,7 @@ static Header relocateFileList(const rpmTransactionSet ts,
 
        /* On install, a relocate to NULL means skip the path. */
        if (relocations[j].newPath == NULL) {
-           enum fileTypes ft = whatis(fModes[i]);
+           fileTypes ft = whatis(fModes[i]);
            int k;
            if (ft == XDIR) {
                /* Start with the parent, looking for directory to exclude. */
@@ -711,7 +711,7 @@ static fileAction decideFileFate(const char * dirName,
 {
     char buffer[1024];
     const char * dbAttr, * newAttr;
-    enum fileTypes dbWhat, newWhat, diskWhat;
+    fileTypes dbWhat, newWhat, diskWhat;
     struct stat sb;
     int i, rc;
     int save = (newFlags & RPMFILE_NOREPLACE) ? FA_ALTNAME : FA_SAVE;
@@ -802,8 +802,8 @@ static fileAction decideFileFate(const char * dirName,
 static int filecmp(short mode1, const char * md51, const char * link1,
                   short mode2, const char * md52, const char * link2)
 {
-    enum fileTypes what1 = whatis(mode1);
-    enum fileTypes what2 = whatis(mode2);
+    fileTypes what1 = whatis(mode1);
+    fileTypes what2 = whatis(mode2);
 
     if (what1 != what2) return 1;
 
diff --git a/lib/uninstall.c b/lib/uninstall.c
deleted file mode 100644 (file)
index 11f4e8d..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-/** \ingroup rpmtrans payload
- * \file lib/uninstall.c
- */
-
-#include "system.h"
-
-#include <rpmlib.h>
-#include <rpmurl.h>
-#include <rpmmacro.h>  /* XXX for rpmExpand */
-
-#include "rollback.h"
-#include "misc.h"      /* XXX for makeTempFile, doputenv */
-#include "debug.h"
-
-/*@access Header@*/            /* XXX compared with NULL */
-
-int removeBinaryPackage(const rpmTransactionSet ts, TFI_t fi)
-{
-/*@observer@*/ static char * stepName = "   erase";
-    Header h;
-    int chrootDone = 0;
-    const char * failedFile = NULL;
-    const void * pkgKey = NULL;
-    int rc = 0;
-
-    rpmMessage(RPMMESS_DEBUG, _("%s: %s-%s-%s has %d files, test = %d\n"),
-               stepName, fi->name, fi->version, fi->release,
-               fi->fc, (ts->transFlags & RPMTRANS_FLAG_TEST));
-
-    /*
-     * When we run scripts, we pass an argument which is the number of 
-     * versions of this package that will be installed when we are finished.
-     */
-    fi->scriptArg = rpmdbCountPackages(ts->rpmdb, fi->name) - 1;
-    if (fi->scriptArg < 0)
-       return 1;
-
-    {  rpmdbMatchIterator mi = NULL;
-
-       mi = rpmdbInitIterator(ts->rpmdb, RPMDBI_PACKAGES,
-                               &fi->record, sizeof(fi->record));
-
-       h = rpmdbNextIterator(mi);
-       if (h == NULL) {
-           rpmdbFreeIterator(mi);
-           return 2;
-       }
-       h = headerLink(h);
-       rpmdbFreeIterator(mi);
-    }
-
-    if (ts->rootDir && !ts->chrootDone) {
-       chdir("/");
-       /*@-unrecog@*/ chroot(ts->rootDir); /*@=unrecog@*/
-       chrootDone = ts->chrootDone = 1;
-    }
-
-    if (!(ts->transFlags & RPMTRANS_FLAG_NOTRIGGERS)) {
-       /* run triggers from this package which are keyed on installed 
-          packages */
-       if (runImmedTriggers(ts, RPMSENSE_TRIGGERUN, h, -1))
-           return 2;
-
-       /* run triggers which are set off by the removal of this package */
-       if (runTriggers(ts, RPMSENSE_TRIGGERUN, h, -1))
-           return 1;
-    }
-
-    rpmMessage(RPMMESS_DEBUG, _("%s: running %s script(s) (if any)\n"),
-               stepName, "pre-erase");
-
-    rc = runInstScript(ts, h, RPMTAG_PREUN, RPMTAG_PREUNPROG, fi->scriptArg,
-                         (ts->transFlags & RPMTRANS_FLAG_NOSCRIPTS));
-    if (rc)
-       return 1;
-
-    if (fi->fc > 0 && !(ts->transFlags & RPMTRANS_FLAG_JUSTDB)) {
-
-       if (ts->notify)
-           (void)ts->notify(h, RPMCALLBACK_UNINST_START, fi->fc, fi->fc,
-               pkgKey, ts->notifyData);
-
-       rc = fsmSetup(fi->fsm, FSM_PKGERASE, ts, fi, NULL, NULL, &failedFile);
-       (void) fsmTeardown(fi->fsm);
-
-       if (ts->notify)
-           (void)ts->notify(h, RPMCALLBACK_UNINST_STOP, 0, fi->fc,
-                       pkgKey, ts->notifyData);
-    }
-
-    rpmMessage(RPMMESS_DEBUG, _("%s: running %s script(s) (if any)\n"),
-               stepName, "post-erase");
-
-    rc = runInstScript(ts, h, RPMTAG_POSTUN, RPMTAG_POSTUNPROG,
-               fi->scriptArg, (ts->transFlags & RPMTRANS_FLAG_NOSCRIPTS));
-    /* XXX postun failures are not cause for erasure failure. */
-
-    if (!(ts->transFlags & RPMTRANS_FLAG_NOTRIGGERS)) {
-       /* Run postun triggers which are set off by this package's removal. */
-       rc = runTriggers(ts, RPMSENSE_TRIGGERPOSTUN, h, -1);
-       if (rc)
-           return 2;
-    }
-
-    if (ts->rootDir && chrootDone) {
-       /*@-unrecog@*/ chroot("."); /*@=unrecog@*/
-       chrootDone = ts->chrootDone = 0;
-       chdir(ts->currDir);
-    }
-
-    if (!(ts->transFlags & RPMTRANS_FLAG_TEST))
-       rpmdbRemove(ts->rpmdb, ts->id, fi->record);
-
-    return 0;
-}
index 39ce0e0..2a9e3f7 100644 (file)
@@ -5,11 +5,9 @@
 #include "system.h"
 
 #include <rpmlib.h>
-#include <rpmbuild.h>
 #include <rpmurl.h>
 
-#include "depends.h"
-#include "install.h"
+#include "psm.h"
 #include "md5.h"
 #include "misc.h"
 #include "debug.h"
index e07092c..6f32c31 100644 (file)
@@ -33,9 +33,9 @@ lib/falloc.c
 lib/formats.c
 lib/fprint.c
 lib/fs.c
+lib/fsm.c
 lib/hash.c
 lib/header.c
-lib/install.c
 lib/md5.c
 lib/md5sum.c
 lib/misc.c
@@ -43,8 +43,8 @@ lib/package.c
 lib/poptBT.c
 lib/poptQV.c
 lib/problems.c
+lib/psm.c
 lib/query.c
-lib/rollback.c
 lib/rpmchecksig.c
 lib/rpmdb.c
 lib/rpminstall.c
@@ -54,7 +54,6 @@ lib/scriptlet.c
 lib/signature.c
 lib/stringbuf.c
 lib/transaction.c
-lib/uninstall.c
 lib/verify.c
 rpmio/base64.c
 rpmio/digest.c
index 25024ed..ec772b7 100644 (file)
@@ -6,7 +6,7 @@
 msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
-"POT-Creation-Date: 2001-02-10 09:07-0500\n"
+"POT-Creation-Date: 2001-02-10 11:46-0500\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -14,1072 +14,1072 @@ msgstr ""
 "Content-Type: text/plain; charset=CHARSET\n"
 "Content-Transfer-Encoding: ENCODING\n"
 
-#: build.c:26
+#: build.c:25
 #, c-format
 msgid "cannot open rpm database in %s\n"
 msgstr ""
 
-#: build.c:36
+#: build.c:35
 msgid "failed build dependencies:\n"
 msgstr ""
 
-#: build.c:65
+#: build.c:64
 #, c-format
 msgid "Unable to open spec file %s: %s\n"
 msgstr ""
 
-#: build.c:132 build.c:144
+#: build.c:131 build.c:143
 #, c-format
 msgid "Failed to open tar pipe: %m\n"
 msgstr ""
 
 #. Give up
-#: build.c:151
+#: build.c:150
 #, c-format
 msgid "Failed to read spec file from %s\n"
 msgstr ""
 
-#: build.c:179
+#: build.c:178
 #, c-format
 msgid "Failed to rename %s to %s: %m\n"
 msgstr ""
 
-#: build.c:218
+#: build.c:217
 #, c-format
 msgid "failed to stat %s: %m\n"
 msgstr ""
 
-#: build.c:223
+#: build.c:222
 #, c-format
 msgid "File %s is not a regular file.\n"
 msgstr ""
 
-#: build.c:232
+#: build.c:231
 #, c-format
 msgid "File %s does not appear to be a specfile.\n"
 msgstr ""
 
 #. parse up the build operators
-#: build.c:289
+#: build.c:288
 #, c-format
 msgid "Building target platforms: %s\n"
 msgstr ""
 
-#: build.c:304
+#: build.c:303
 #, c-format
 msgid "Building for target %s\n"
 msgstr ""
 
-#: rpm.c:201 rpmqv.c:357
+#: rpm.c:200 rpmqv.c:356
 #, c-format
 msgid "rpm: %s\n"
 msgstr ""
 
-#: rpm.c:212 rpmqv.c:362
+#: rpm.c:211 rpmqv.c:361
 #, c-format
 msgid "RPM version %s\n"
 msgstr ""
 
-#: rpm.c:216 rpmqv.c:366
+#: rpm.c:215 rpmqv.c:365
 msgid "Copyright (C) 1998-2000 - Red Hat, Inc."
 msgstr ""
 
-#: rpm.c:217 rpmqv.c:367
+#: rpm.c:216 rpmqv.c:366
 msgid "This program may be freely redistributed under the terms of the GNU GPL"
 msgstr ""
 
-#: rpm.c:225
+#: rpm.c:224
 msgid "Usage: rpm {--help}"
 msgstr ""
 
-#: rpm.c:226
+#: rpm.c:225
 msgid "       rpm {--version}"
 msgstr ""
 
-#: rpm.c:227
+#: rpm.c:226
 msgid "       rpm {--initdb}   [--dbpath <dir>]"
 msgstr ""
 
-#: rpm.c:228
+#: rpm.c:227
 msgid ""
 "       rpm {--install -i} [-v] [--hash -h] [--percent] [--force] [--test]"
 msgstr ""
 
-#: rpm.c:229
+#: rpm.c:228
 msgid "                        [--replacepkgs] [--replacefiles] [--root <dir>]"
 msgstr ""
 
-#: rpm.c:230
+#: rpm.c:229
 msgid "                        [--excludedocs] [--includedocs] [--noscripts]"
 msgstr ""
 
-#: rpm.c:231
+#: rpm.c:230
 msgid ""
 "                        [--rcfile <file>] [--ignorearch] [--dbpath <dir>]"
 msgstr ""
 
-#: rpm.c:232
+#: rpm.c:231
 msgid ""
 "                        [--prefix <dir>] [--ignoreos] [--nodeps] [--allfiles]"
 msgstr ""
 
-#: rpm.c:233 rpm.c:242 rpm.c:252
+#: rpm.c:232 rpm.c:241 rpm.c:251
 msgid "                        [--ftpproxy <host>] [--ftpport <port>]"
 msgstr ""
 
-#: rpm.c:234 rpm.c:253
+#: rpm.c:233 rpm.c:252
 msgid "                        [--httpproxy <host>] [--httpport <port>]"
 msgstr ""
 
-#: rpm.c:235
+#: rpm.c:234
 msgid ""
 "                        [--justdb] [--noorder] [--relocate oldpath=newpath]"
 msgstr ""
 
-#: rpm.c:236
+#: rpm.c:235
 msgid ""
 "                        [--badreloc] [--notriggers] [--excludepath <path>]"
 msgstr ""
 
-#: rpm.c:237
+#: rpm.c:236
 msgid "                        [--ignoresize] file1.rpm ... fileN.rpm"
 msgstr ""
 
-#: rpm.c:238
+#: rpm.c:237
 msgid ""
 "       rpm {--upgrade -U} [-v] [--hash -h] [--percent] [--force] [--test]"
 msgstr ""
 
-#: rpm.c:239
+#: rpm.c:238
 msgid "                        [--oldpackage] [--root <dir>] [--noscripts]"
 msgstr ""
 
-#: rpm.c:240
+#: rpm.c:239
 msgid ""
 "                        [--excludedocs] [--includedocs] [--rcfile <file>]"
 msgstr ""
 
-#: rpm.c:241
+#: rpm.c:240
 msgid ""
 "                        [--ignorearch]  [--dbpath <dir>] [--prefix <dir>] "
 msgstr ""
 
-#: rpm.c:243
+#: rpm.c:242
 msgid "                        [--httpproxy <host>] [--httpport <port>] "
 msgstr ""
 
-#: rpm.c:244
+#: rpm.c:243
 msgid "                        [--ignoreos] [--nodeps] [--allfiles] [--justdb]"
 msgstr ""
 
-#: rpm.c:245
+#: rpm.c:244
 msgid "                        [--noorder] [--relocate oldpath=newpath]"
 msgstr ""
 
-#: rpm.c:246
+#: rpm.c:245
 msgid ""
 "                        [--badreloc] [--excludepath <path>] [--ignoresize]"
 msgstr ""
 
-#: rpm.c:247
+#: rpm.c:246
 msgid "                        file1.rpm ... fileN.rpm"
 msgstr ""
 
-#: rpm.c:248
+#: rpm.c:247
 msgid "       rpm {--query -q} [-afpg] [-i] [-l] [-s] [-d] [-c] [-v] [-R]"
 msgstr ""
 
-#: rpm.c:249
+#: rpm.c:248
 msgid "                        [--scripts] [--root <dir>] [--rcfile <file>]"
 msgstr ""
 
-#: rpm.c:250
+#: rpm.c:249
 msgid "                        [--whatprovides] [--whatrequires] [--requires]"
 msgstr ""
 
-#: rpm.c:251
+#: rpm.c:250
 msgid "                        [--triggeredby]"
 msgstr ""
 
-#: rpm.c:254
+#: rpm.c:253
 msgid "                        [--provides] [--triggers] [--dump]"
 msgstr ""
 
-#: rpm.c:255
+#: rpm.c:254
 msgid "                        [--changelog] [--dbpath <dir>] [targets]"
 msgstr ""
 
-#: rpm.c:256
+#: rpm.c:255
 msgid "       rpm {--verify -V -y} [-afpg] [--root <dir>] [--rcfile <file>]"
 msgstr ""
 
-#: rpm.c:257
+#: rpm.c:256
 msgid ""
 "                        [--dbpath <dir>] [--nodeps] [--nofiles] [--noscripts]"
 msgstr ""
 
-#: rpm.c:258
+#: rpm.c:257
 msgid "                        [--nomd5] [targets]"
 msgstr ""
 
-#: rpm.c:259
+#: rpm.c:258
 msgid "       rpm {--setperms} [-afpg] [target]"
 msgstr ""
 
-#: rpm.c:260
+#: rpm.c:259
 msgid "       rpm {--setugids} [-afpg] [target]"
 msgstr ""
 
-#: rpm.c:261
+#: rpm.c:260
 msgid "       rpm {--freshen -F} file1.rpm ... fileN.rpm"
 msgstr ""
 
-#: rpm.c:262
+#: rpm.c:261
 msgid "       rpm {--erase -e} [--root <dir>] [--noscripts] [--rcfile <file>]"
 msgstr ""
 
-#: rpm.c:263
+#: rpm.c:262
 msgid "                        [--dbpath <dir>] [--nodeps] [--allmatches]"
 msgstr ""
 
-#: rpm.c:264
+#: rpm.c:263
 msgid "                        [--justdb] [--notriggers] package1 ... packageN"
 msgstr ""
 
-#: rpm.c:265
+#: rpm.c:264
 msgid "       rpm {--resign} [--rcfile <file>] package1 package2 ... packageN"
 msgstr ""
 
-#: rpm.c:266
+#: rpm.c:265
 msgid "       rpm {--addsign} [--rcfile <file>] package1 package2 ... packageN"
 msgstr ""
 
-#: rpm.c:267
+#: rpm.c:266
 msgid ""
 "       rpm {--checksig -K} [--nopgp] [--nogpg] [--nomd5] [--rcfile <file>]"
 msgstr ""
 
-#: rpm.c:268
+#: rpm.c:267
 msgid "                           package1 ... packageN"
 msgstr ""
 
-#: rpm.c:269
+#: rpm.c:268
 msgid "       rpm {--rebuilddb} [--rcfile <file>] [--dbpath <dir>]"
 msgstr ""
 
-#: rpm.c:270
+#: rpm.c:269
 msgid "       rpm {--querytags}"
 msgstr ""
 
-#: rpm.c:304 rpmqv.c:442
+#: rpm.c:303 rpmqv.c:441
 msgid "Usage:"
 msgstr ""
 
-#: rpm.c:306 rpmqv.c:444
+#: rpm.c:305 rpmqv.c:443
 msgid "print this message"
 msgstr ""
 
-#: rpm.c:308 rpmqv.c:129 rpmqv.c:446
+#: rpm.c:307 rpmqv.c:128 rpmqv.c:445
 msgid "print the version of rpm being used"
 msgstr ""
 
-#: rpm.c:311
+#: rpm.c:310
 msgid "   All modes support the following arguments:"
 msgstr ""
 
-#: rpm.c:312
+#: rpm.c:311
 msgid "    --define '<name> <body>'"
 msgstr ""
 
-#: rpm.c:313 rpmqv.c:136 rpmqv.c:451
+#: rpm.c:312 rpmqv.c:135 rpmqv.c:450
 msgid "define macro <name> with value <body>"
 msgstr ""
 
-#: rpm.c:314
+#: rpm.c:313
 msgid "    --eval '<name>+'      "
 msgstr ""
 
-#: rpm.c:315
+#: rpm.c:314
 msgid "print the expansion of macro <name> to stdout"
 msgstr ""
 
-#: rpm.c:316
+#: rpm.c:315
 msgid "    --pipe <cmd>          "
 msgstr ""
 
-#: rpm.c:317 rpmqv.c:142 rpmqv.c:455
+#: rpm.c:316 rpmqv.c:141 rpmqv.c:454
 msgid "send stdout to <cmd>"
 msgstr ""
 
-#: rpm.c:318
+#: rpm.c:317
 msgid "    --rcfile <file>       "
 msgstr ""
 
-#: rpm.c:319
+#: rpm.c:318
 msgid "use <file> instead of /etc/rpmrc and $HOME/.rpmrc"
 msgstr ""
 
-#: rpm.c:321 rpmqv.c:160 rpmqv.c:459
+#: rpm.c:320 rpmqv.c:159 rpmqv.c:458
 msgid "display final rpmrc and macro configuration"
 msgstr ""
 
-#: rpm.c:323 rpmqv.c:467
+#: rpm.c:322 rpmqv.c:466
 msgid "be a little more verbose"
 msgstr ""
 
-#: rpm.c:325 rpmqv.c:469
+#: rpm.c:324 rpmqv.c:468
 msgid "be incredibly verbose (for debugging)"
 msgstr ""
 
-#: rpm.c:328
+#: rpm.c:327
 msgid "   Install, upgrade and query (with -p) allow URL's to be used in place"
 msgstr ""
 
-#: rpm.c:329
+#: rpm.c:328
 msgid "   of file names as well as the following options:"
 msgstr ""
 
-#: rpm.c:330
+#: rpm.c:329
 msgid "      --ftpproxy <host>   "
 msgstr ""
 
-#: rpm.c:331 rpmqv.c:476
+#: rpm.c:330 rpmqv.c:475
 msgid "hostname or IP of ftp proxy"
 msgstr ""
 
-#: rpm.c:332
+#: rpm.c:331
 msgid "      --ftpport <port>    "
 msgstr ""
 
-#: rpm.c:333 rpmqv.c:478
+#: rpm.c:332 rpmqv.c:477
 msgid "port number of ftp server (or proxy)"
 msgstr ""
 
-#: rpm.c:334
+#: rpm.c:333
 msgid "      --httpproxy <host>  "
 msgstr ""
 
-#: rpm.c:335 rpmqv.c:480
+#: rpm.c:334 rpmqv.c:479
 msgid "hostname or IP of http proxy"
 msgstr ""
 
-#: rpm.c:336
+#: rpm.c:335
 msgid "      --httpport <port>   "
 msgstr ""
 
-#: rpm.c:337 rpmqv.c:482
+#: rpm.c:336 rpmqv.c:481
 msgid "port number of http server (or proxy)"
 msgstr ""
 
-#: rpm.c:341 rpmqv.c:502
+#: rpm.c:340 rpmqv.c:501
 msgid "query mode"
 msgstr ""
 
-#: rpm.c:342 rpm.c:388 rpm.c:413 rpm.c:465 rpm.c:539
+#: rpm.c:341 rpm.c:387 rpm.c:412 rpm.c:464 rpm.c:538
 msgid "      --dbpath <dir>      "
 msgstr ""
 
-#: rpm.c:343 rpm.c:389 rpm.c:414 rpm.c:466 rpm.c:540 rpmqv.c:462
+#: rpm.c:342 rpm.c:388 rpm.c:413 rpm.c:465 rpm.c:539 rpmqv.c:461
 msgid "use <dir> as the directory for the database"
 msgstr ""
 
-#: rpm.c:344
+#: rpm.c:343
 msgid "      --queryformat <qfmt>"
 msgstr ""
 
-#: rpm.c:345 rpmqv.c:504
+#: rpm.c:344 rpmqv.c:503
 msgid "use <qfmt> as the header format (implies --info)"
 msgstr ""
 
-#: rpm.c:346 rpm.c:390 rpm.c:448 rpm.c:477
+#: rpm.c:345 rpm.c:389 rpm.c:447 rpm.c:476
 msgid "      --root <dir>        "
 msgstr ""
 
-#: rpm.c:347 rpm.c:391 rpm.c:449 rpm.c:478 rpm.c:542 rpmqv.c:145 rpmqv.c:464
+#: rpm.c:346 rpm.c:390 rpm.c:448 rpm.c:477 rpm.c:541 rpmqv.c:144 rpmqv.c:463
 msgid "use <dir> as the top level directory"
 msgstr ""
 
-#: rpm.c:348
+#: rpm.c:347
 msgid "      Package specification options:"
 msgstr ""
 
-#: rpm.c:350
+#: rpm.c:349
 msgid "query all packages"
 msgstr ""
 
-#: rpm.c:351
+#: rpm.c:350
 msgid "        -f <file>+        "
 msgstr ""
 
-#: rpm.c:352
+#: rpm.c:351
 msgid "query package owning <file>"
 msgstr ""
 
-#: rpm.c:353
+#: rpm.c:352
 msgid "        -p <packagefile>+ "
 msgstr ""
 
-#: rpm.c:354
+#: rpm.c:353
 msgid "query (uninstalled) package <packagefile>"
 msgstr ""
 
-#: rpm.c:355
+#: rpm.c:354
 msgid "        --triggeredby <pkg>"
 msgstr ""
 
-#: rpm.c:356
+#: rpm.c:355
 msgid "query packages triggered by <pkg>"
 msgstr ""
 
-#: rpm.c:357
+#: rpm.c:356
 msgid "        --whatprovides <cap>"
 msgstr ""
 
-#: rpm.c:358
+#: rpm.c:357
 msgid "query packages which provide <cap> capability"
 msgstr ""
 
-#: rpm.c:359
+#: rpm.c:358
 msgid "        --whatrequires <cap>"
 msgstr ""
 
-#: rpm.c:360
+#: rpm.c:359
 msgid "query packages which require <cap> capability"
 msgstr ""
 
-#: rpm.c:361
+#: rpm.c:360
 msgid "      Information selection options:"
 msgstr ""
 
-#: rpm.c:363 rpmqv.c:508
+#: rpm.c:362 rpmqv.c:507
 msgid "display package information"
 msgstr ""
 
-#: rpm.c:365 rpmqv.c:510
+#: rpm.c:364 rpmqv.c:509
 msgid "display the package's change log"
 msgstr ""
 
-#: rpm.c:367 rpmqv.c:512
+#: rpm.c:366 rpmqv.c:511
 msgid "display package file list"
 msgstr ""
 
-#: rpm.c:369 rpmqv.c:514
+#: rpm.c:368 rpmqv.c:513
 msgid "show file states (implies -l)"
 msgstr ""
 
-#: rpm.c:371 rpmqv.c:516
+#: rpm.c:370 rpmqv.c:515
 msgid "list only documentation files (implies -l)"
 msgstr ""
 
-#: rpm.c:373 rpmqv.c:518
+#: rpm.c:372 rpmqv.c:517
 msgid "list only configuration files (implies -l)"
 msgstr ""
 
-#: rpm.c:375 rpmqv.c:520
+#: rpm.c:374 rpmqv.c:519
 msgid ""
 "show all verifiable information for each file (must be used with -l, -c, or "
 "-d)"
 msgstr ""
 
-#: rpm.c:377
+#: rpm.c:376
 msgid "list capabilities package provides"
 msgstr ""
 
-#: rpm.c:379
+#: rpm.c:378
 msgid "list package dependencies"
 msgstr ""
 
-#: rpm.c:381
+#: rpm.c:380
 msgid "print the various [un]install scripts"
 msgstr ""
 
-#: rpm.c:383
+#: rpm.c:382
 msgid "show the trigger scripts contained in the package"
 msgstr ""
 
-#: rpm.c:387 rpmqv.c:531
+#: rpm.c:386 rpmqv.c:530
 msgid ""
 "verify a package installation using the same same package specification "
 "options as -q"
 msgstr ""
 
-#: lib/poptBT.c:183 lib/verify.c:56 rpm.c:393 rpm.c:435 rpm.c:470 rpmqv.c:263
-#: rpmqv.c:533 rpmqv.c:581 rpmqv.c:615
+#: lib/poptBT.c:183 lib/verify.c:54 rpm.c:392 rpm.c:434 rpm.c:469 rpmqv.c:262
+#: rpmqv.c:532 rpmqv.c:580 rpmqv.c:614
 msgid "do not verify package dependencies"
 msgstr ""
 
-#: lib/verify.c:62 rpm.c:395 rpmqv.c:209 rpmqv.c:537
+#: lib/verify.c:60 rpm.c:394 rpmqv.c:208 rpmqv.c:536
 msgid "do not verify file md5 checksums"
 msgstr ""
 
-#: rpm.c:397 rpmqv.c:535
+#: rpm.c:396 rpmqv.c:534
 msgid "do not verify file attributes"
 msgstr ""
 
-#: rpm.c:399 rpmqv.c:542
+#: rpm.c:398 rpmqv.c:541
 msgid "list the tags that can be used in a query format"
 msgstr ""
 
-#: rpm.c:402
+#: rpm.c:401
 msgid "    --install <packagefile>"
 msgstr ""
 
-#: rpm.c:403
+#: rpm.c:402
 msgid "    -i <packagefile>      "
 msgstr ""
 
-#: rpm.c:404 rpmqv.c:259 rpmqv.c:556
+#: rpm.c:403 rpmqv.c:258 rpmqv.c:555
 msgid "install package"
 msgstr ""
 
-#: rpm.c:405
+#: rpm.c:404
 msgid "      --excludepath <path>"
 msgstr ""
 
-#: rpm.c:406
+#: rpm.c:405
 msgid "skip files in path <path>"
 msgstr ""
 
-#: rpm.c:407
+#: rpm.c:406
 msgid "      --relocate <oldpath>=<newpath>"
 msgstr ""
 
-#: rpm.c:408 rpmqv.c:593
+#: rpm.c:407 rpmqv.c:592
 msgid "relocate files from <oldpath> to <newpath>"
 msgstr ""
 
-#: rpm.c:410 rpmqv.c:231 rpmqv.c:561
+#: rpm.c:409 rpmqv.c:230 rpmqv.c:560
 msgid "relocate files in non-relocateable package"
 msgstr ""
 
-#: rpm.c:411
+#: rpm.c:410
 msgid "      --prefix <dir>      "
 msgstr ""
 
-#: rpm.c:412 rpmqv.c:277 rpmqv.c:591
+#: rpm.c:411 rpmqv.c:276 rpmqv.c:590
 msgid "relocate the package to <dir>, if relocatable"
 msgstr ""
 
-#: rpm.c:416 rpmqv.c:237 rpmqv.c:563
+#: rpm.c:415 rpmqv.c:236 rpmqv.c:562
 msgid "do not install documentation"
 msgstr ""
 
-#: rpm.c:418 rpmqv.c:242 rpmqv.c:567
+#: rpm.c:417 rpmqv.c:241 rpmqv.c:566
 msgid "short hand for --replacepkgs --replacefiles"
 msgstr ""
 
-#: rpm.c:420 rpmqv.c:248 rpmqv.c:569
+#: rpm.c:419 rpmqv.c:247 rpmqv.c:568
 msgid "print hash marks as package installs (good with -v)"
 msgstr ""
 
-#: rpm.c:422 rpmqv.c:225 rpmqv.c:558
+#: rpm.c:421 rpmqv.c:224 rpmqv.c:557
 msgid "install all files, even configurations which might otherwise be skipped"
 msgstr ""
 
-#: rpm.c:425 rpmqv.c:250 rpmqv.c:571
+#: rpm.c:424 rpmqv.c:249 rpmqv.c:570
 msgid "don't verify package architecture"
 msgstr ""
 
-#: rpm.c:427 rpmqv.c:255 rpmqv.c:573
+#: rpm.c:426 rpmqv.c:254 rpmqv.c:572
 msgid "don't check disk space before installing"
 msgstr ""
 
-#: rpm.c:429 rpmqv.c:252 rpmqv.c:575
+#: rpm.c:428 rpmqv.c:251 rpmqv.c:574
 msgid "don't verify package operating system"
 msgstr ""
 
-#: rpm.c:431 rpmqv.c:257 rpmqv.c:577
+#: rpm.c:430 rpmqv.c:256 rpmqv.c:576
 msgid "install documentation"
 msgstr ""
 
-#: rpm.c:433 rpm.c:468 rpmqv.c:261 rpmqv.c:579 rpmqv.c:613
+#: rpm.c:432 rpm.c:467 rpmqv.c:260 rpmqv.c:578 rpmqv.c:612
 msgid "update the database, but do not modify the filesystem"
 msgstr ""
 
-#: rpm.c:437 rpm.c:472 rpmqv.c:265 rpmqv.c:583 rpmqv.c:617
+#: rpm.c:436 rpm.c:471 rpmqv.c:264 rpmqv.c:582 rpmqv.c:616
 msgid "do not reorder package installation to satisfy dependencies"
 msgstr ""
 
-#: rpm.c:439
+#: rpm.c:438
 msgid "don't execute any installation scripts"
 msgstr ""
 
-#: rpm.c:441 rpm.c:476 rpmqv.c:621
+#: rpm.c:440 rpm.c:475 rpmqv.c:620
 msgid "don't execute any scripts triggered by this package"
 msgstr ""
 
-#: rpm.c:443 rpmqv.c:275 rpmqv.c:589
+#: rpm.c:442 rpmqv.c:274 rpmqv.c:588
 msgid "print percentages as package installs"
 msgstr ""
 
-#: rpm.c:445 rpmqv.c:286 rpmqv.c:595
+#: rpm.c:444 rpmqv.c:285 rpmqv.c:594
 msgid "install even if the package replaces installed files"
 msgstr ""
 
-#: rpm.c:447 rpmqv.c:288 rpmqv.c:597
+#: rpm.c:446 rpmqv.c:287 rpmqv.c:596
 msgid "reinstall if the package is already present"
 msgstr ""
 
-#: rpm.c:451 rpmqv.c:290 rpmqv.c:599
+#: rpm.c:450 rpmqv.c:289 rpmqv.c:598
 msgid "don't install, but tell if it would work or not"
 msgstr ""
 
-#: rpm.c:454
+#: rpm.c:453
 msgid "    --upgrade <packagefile>"
 msgstr ""
 
-#: rpm.c:455
+#: rpm.c:454
 msgid "    -U <packagefile>      "
 msgstr ""
 
-#: rpm.c:456 rpmqv.c:603
+#: rpm.c:455 rpmqv.c:602
 msgid "upgrade package (same options as --install, plus)"
 msgstr ""
 
-#: rpm.c:458 rpmqv.c:272 rpmqv.c:605
+#: rpm.c:457 rpmqv.c:271 rpmqv.c:604
 msgid ""
 "upgrade to an old version of the package (--force on upgrades does this "
 "automatically)"
 msgstr ""
 
-#: rpm.c:460
+#: rpm.c:459
 msgid "    --erase <package>"
 msgstr ""
 
-#: rpm.c:462 rpmqv.c:235 rpmqv.c:609
+#: rpm.c:461 rpmqv.c:234 rpmqv.c:608
 msgid "erase (uninstall) package"
 msgstr ""
 
-#: rpm.c:464 rpmqv.c:228 rpmqv.c:611
+#: rpm.c:463 rpmqv.c:227 rpmqv.c:610
 msgid ""
 "remove all packages which match <package> (normally an error is generated if "
 "<package> specified multiple packages)"
 msgstr ""
 
-#: rpm.c:474 rpmqv.c:619
+#: rpm.c:473 rpmqv.c:618
 msgid "do not execute any package specific scripts"
 msgstr ""
 
-#: rpm.c:480
+#: rpm.c:479
 msgid "    -b<stage> <spec>      "
 msgstr ""
 
-#: rpm.c:481
+#: rpm.c:480
 msgid "    -t<stage> <tarball>   "
 msgstr ""
 
-#: rpm.c:482
+#: rpm.c:481
 msgid "build package, where <stage> is one of:"
 msgstr ""
 
-#: rpm.c:484
+#: rpm.c:483
 msgid "prep (unpack sources and apply patches)"
 msgstr ""
 
-#: rpm.c:486
+#: rpm.c:485
 #, c-format
 msgid "list check (do some cursory checks on %files)"
 msgstr ""
 
-#: rpm.c:488
+#: rpm.c:487
 msgid "compile (prep and compile)"
 msgstr ""
 
-#: rpm.c:490
+#: rpm.c:489
 msgid "install (prep, compile, install)"
 msgstr ""
 
-#: rpm.c:492
+#: rpm.c:491
 msgid "binary package (prep, compile, install, package)"
 msgstr ""
 
-#: rpm.c:494
+#: rpm.c:493
 msgid "bin/src package (prep, compile, install, package)"
 msgstr ""
 
-#: lib/poptBT.c:194 rpm.c:496
+#: lib/poptBT.c:194 rpm.c:495
 msgid "skip straight to specified stage (only for c,i)"
 msgstr ""
 
-#: lib/poptBT.c:173 rpm.c:498
+#: lib/poptBT.c:173 rpm.c:497
 msgid "remove build tree when done"
 msgstr ""
 
-#: lib/poptBT.c:190 rpm.c:500
+#: lib/poptBT.c:190 rpm.c:499
 msgid "remove sources when done"
 msgstr ""
 
-#: rpm.c:502
+#: rpm.c:501
 msgid "remove spec file when done"
 msgstr ""
 
-#: lib/poptBT.c:196 rpm.c:504 rpmqv.c:201
+#: lib/poptBT.c:196 rpm.c:503 rpmqv.c:200
 msgid "generate PGP/GPG signature"
 msgstr ""
 
-#: rpm.c:505
+#: rpm.c:504
 msgid "      --buildroot <dir>   "
 msgstr ""
 
-#: rpm.c:506
+#: rpm.c:505
 msgid "use <dir> as the build root"
 msgstr ""
 
-#: rpm.c:507
+#: rpm.c:506
 msgid "      --target=<platform>+"
 msgstr ""
 
-#: rpm.c:508
+#: rpm.c:507
 msgid "build the packages for the build targets platform1...platformN."
 msgstr ""
 
-#: rpm.c:510
+#: rpm.c:509
 msgid "do not execute any stages"
 msgstr ""
 
-#: rpm.c:511
+#: rpm.c:510
 msgid "      --timecheck <secs>  "
 msgstr ""
 
-#: rpm.c:512
+#: rpm.c:511
 msgid "set the time check to <secs> seconds (0 disables)"
 msgstr ""
 
-#: rpm.c:514
+#: rpm.c:513
 msgid "    --rebuild <src_pkg>   "
 msgstr ""
 
-#: rpm.c:515
+#: rpm.c:514
 msgid ""
 "install source package, build binary package and remove spec file, sources, "
 "patches, and icons."
 msgstr ""
 
-#: rpm.c:516
+#: rpm.c:515
 msgid "    --recompile <src_pkg> "
 msgstr ""
 
-#: rpm.c:517
+#: rpm.c:516
 msgid "like --rebuild, but don't build any package"
 msgstr ""
 
-#: rpm.c:520
+#: rpm.c:519
 msgid "    --resign <pkg>+       "
 msgstr ""
 
-#: rpm.c:521 rpmqv.c:199 rpmqv.c:627
+#: rpm.c:520 rpmqv.c:198 rpmqv.c:626
 msgid "sign a package (discard current signature)"
 msgstr ""
 
-#: rpm.c:522
+#: rpm.c:521
 msgid "    --addsign <pkg>+      "
 msgstr ""
 
-#: rpm.c:523 rpmqv.c:197 rpmqv.c:629
+#: rpm.c:522 rpmqv.c:196 rpmqv.c:628
 msgid "add a signature to a package"
 msgstr ""
 
-#: rpm.c:524
+#: rpm.c:523
 msgid "    --checksig <pkg>+"
 msgstr ""
 
-#: rpm.c:525
+#: rpm.c:524
 msgid "    -K <pkg>+             "
 msgstr ""
 
-#: rpm.c:526 rpmqv.c:203 rpmqv.c:633
+#: rpm.c:525 rpmqv.c:202 rpmqv.c:632
 msgid "verify package signature"
 msgstr ""
 
-#: rpm.c:528 rpmqv.c:205 rpmqv.c:635
+#: rpm.c:527 rpmqv.c:204 rpmqv.c:634
 msgid "skip any PGP signatures"
 msgstr ""
 
-#: rpm.c:530 rpmqv.c:207 rpmqv.c:637
+#: rpm.c:529 rpmqv.c:206 rpmqv.c:636
 msgid "skip any GPG signatures"
 msgstr ""
 
-#: rpm.c:532 rpmqv.c:639
+#: rpm.c:531 rpmqv.c:638
 msgid "skip any MD5 signatures"
 msgstr ""
 
-#: rpm.c:536
+#: rpm.c:535
 msgid "make sure a valid database exists"
 msgstr ""
 
-#: rpm.c:538
+#: rpm.c:537
 msgid "rebuild database from existing database"
 msgstr ""
 
-#: rpm.c:546 rpmqv.c:544
+#: rpm.c:545 rpmqv.c:543
 msgid ""
 "set the file permissions to those in the package database using the same "
 "package specification options as -q"
 msgstr ""
 
-#: rpm.c:549 rpmqv.c:547
+#: rpm.c:548 rpmqv.c:546
 msgid ""
 "set the file owner and group to those in the package database using the same "
 "package specification options as -q"
 msgstr ""
 
-#: rpm.c:689 rpm.c:695 rpm.c:704 rpm.c:726 rpm.c:732 rpm.c:739 rpm.c:747
-#: rpm.c:755 rpm.c:776 rpm.c:839 rpmqv.c:832 rpmqv.c:838 rpmqv.c:845
-#: rpmqv.c:851 rpmqv.c:885 rpmqv.c:893 rpmqv.c:899 rpmqv.c:907 rpmqv.c:974
+#: rpm.c:688 rpm.c:694 rpm.c:703 rpm.c:725 rpm.c:731 rpm.c:738 rpm.c:746
+#: rpm.c:754 rpm.c:775 rpm.c:838 rpmqv.c:831 rpmqv.c:837 rpmqv.c:844
+#: rpmqv.c:850 rpmqv.c:884 rpmqv.c:892 rpmqv.c:898 rpmqv.c:906 rpmqv.c:973
 msgid "only one major mode may be specified"
 msgstr ""
 
-#: rpm.c:697
+#: rpm.c:696
 msgid "-u and --uninstall are deprecated and no longer work.\n"
 msgstr ""
 
-#: rpm.c:699
+#: rpm.c:698
 msgid "Use -e or --erase instead.\n"
 msgstr ""
 
-#: rpm.c:782 rpmqv.c:869
+#: rpm.c:781 rpmqv.c:868
 msgid "relocations must begin with a /"
 msgstr ""
 
-#: rpm.c:784 rpmqv.c:871
+#: rpm.c:783 rpmqv.c:870
 msgid "relocations must contain a ="
 msgstr ""
 
-#: rpm.c:787 rpmqv.c:874
+#: rpm.c:786 rpmqv.c:873
 msgid "relocations must have a / following the ="
 msgstr ""
 
-#: rpm.c:796 rpmqv.c:858
+#: rpm.c:795 rpmqv.c:857
 msgid "exclude paths must begin with a /"
 msgstr ""
 
-#: rpm.c:805 rpmqv.c:928
+#: rpm.c:804 rpmqv.c:927
 msgid "The --rcfile option has been eliminated.\n"
 msgstr ""
 
-#: rpm.c:806
+#: rpm.c:805
 msgid "Use --macros with a colon separated list of macro files to read.\n"
 msgstr ""
 
-#: rpm.c:811 rpmqv.c:934
+#: rpm.c:810 rpmqv.c:933
 #, c-format
 msgid "Internal error in argument processing (%d) :-(\n"
 msgstr ""
 
-#: rpm.c:846 rpmqv.c:989
+#: rpm.c:845 rpmqv.c:988
 msgid "one type of query/verify may be performed at a time"
 msgstr ""
 
-#: rpm.c:851 rpmqv.c:993
+#: rpm.c:850 rpmqv.c:992
 msgid "unexpected query flags"
 msgstr ""
 
-#: rpm.c:854 rpmqv.c:996
+#: rpm.c:853 rpmqv.c:995
 msgid "unexpected query format"
 msgstr ""
 
-#: rpm.c:857 rpmqv.c:999
+#: rpm.c:856 rpmqv.c:998
 msgid "unexpected query source"
 msgstr ""
 
-#: rpm.c:860 rpmqv.c:1009
+#: rpm.c:859 rpmqv.c:1008
 msgid "only installation, upgrading, rmsource and rmspec may be forced"
 msgstr ""
 
-#: rpm.c:863 rpmqv.c:1014
+#: rpm.c:862 rpmqv.c:1013
 msgid "files may only be relocated during package installation"
 msgstr ""
 
-#: rpm.c:866 rpmqv.c:1017
+#: rpm.c:865 rpmqv.c:1016
 msgid "only one of --prefix or --relocate may be used"
 msgstr ""
 
-#: rpm.c:869 rpmqv.c:1020
+#: rpm.c:868 rpmqv.c:1019
 msgid ""
 "--relocate and --excludepath may only be used when installing new packages"
 msgstr ""
 
-#: rpm.c:872 rpmqv.c:1023
+#: rpm.c:871 rpmqv.c:1022
 msgid "--prefix may only be used when installing new packages"
 msgstr ""
 
-#: rpm.c:875 rpmqv.c:1026
+#: rpm.c:874 rpmqv.c:1025
 msgid "arguments to --prefix must begin with a /"
 msgstr ""
 
-#: rpm.c:878 rpmqv.c:1029
+#: rpm.c:877 rpmqv.c:1028
 msgid "--hash (-h) may only be specified during package installation"
 msgstr ""
 
-#: rpm.c:882 rpmqv.c:1033
+#: rpm.c:881 rpmqv.c:1032
 msgid "--percent may only be specified during package installation"
 msgstr ""
 
-#: rpm.c:886 rpmqv.c:1038
+#: rpm.c:885 rpmqv.c:1037
 msgid "--replacefiles may only be specified during package installation"
 msgstr ""
 
-#: rpm.c:890 rpmqv.c:1042
+#: rpm.c:889 rpmqv.c:1041
 msgid "--replacepkgs may only be specified during package installation"
 msgstr ""
 
-#: rpm.c:894 rpmqv.c:1046
+#: rpm.c:893 rpmqv.c:1045
 msgid "--excludedocs may only be specified during package installation"
 msgstr ""
 
-#: rpm.c:898 rpmqv.c:1050
+#: rpm.c:897 rpmqv.c:1049
 msgid "--includedocs may only be specified during package installation"
 msgstr ""
 
-#: rpm.c:902 rpmqv.c:1054
+#: rpm.c:901 rpmqv.c:1053
 msgid "only one of --excludedocs and --includedocs may be specified"
 msgstr ""
 
-#: rpm.c:906 rpmqv.c:1058
+#: rpm.c:905 rpmqv.c:1057
 msgid "--ignorearch may only be specified during package installation"
 msgstr ""
 
-#: rpm.c:910 rpmqv.c:1062
+#: rpm.c:909 rpmqv.c:1061
 msgid "--ignoreos may only be specified during package installation"
 msgstr ""
 
-#: rpm.c:914 rpmqv.c:1067
+#: rpm.c:913 rpmqv.c:1066
 msgid "--ignoresize may only be specified during package installation"
 msgstr ""
 
-#: rpm.c:918 rpmqv.c:1071
+#: rpm.c:917 rpmqv.c:1070
 msgid "--allmatches may only be specified during package erasure"
 msgstr ""
 
-#: rpm.c:922 rpmqv.c:1075
+#: rpm.c:921 rpmqv.c:1074
 msgid "--allfiles may only be specified during package installation"
 msgstr ""
 
-#: rpm.c:926 rpmqv.c:1080
+#: rpm.c:925 rpmqv.c:1079
 msgid "--justdb may only be specified during package installation and erasure"
 msgstr ""
 
-#: rpm.c:931
+#: rpm.c:930
 msgid ""
 "--noscripts may only be specified during package installation, erasure, and "
 "verification"
 msgstr ""
 
-#: rpm.c:935
+#: rpm.c:934
 msgid ""
 "--notriggers may only be specified during package installation, erasure, and "
 "verification"
 msgstr ""
 
-#: rpm.c:939 rpmqv.c:1091
+#: rpm.c:938 rpmqv.c:1090
 msgid ""
 "--nodeps may only be specified during package building, rebuilding, "
 "recompilation, installation,erasure, and verification"
 msgstr ""
 
-#: rpm.c:944 rpmqv.c:1096
+#: rpm.c:943 rpmqv.c:1095
 msgid ""
 "--test may only be specified during package installation, erasure, and "
 "building"
 msgstr ""
 
-#: rpm.c:948 rpmqv.c:1101
+#: rpm.c:947 rpmqv.c:1100
 msgid ""
 "--root (-r) may only be specified during installation, erasure, querying, "
 "and database rebuilds"
 msgstr ""
 
-#: rpm.c:960 rpmqv.c:1113
+#: rpm.c:959 rpmqv.c:1112
 msgid "arguments to --root (-r) must begin with a /"
 msgstr ""
 
-#: rpm.c:966
+#: rpm.c:965
 msgid "--oldpackage may only be used during upgrades"
 msgstr ""
 
-#: rpm.c:969 rpmqv.c:1120
+#: rpm.c:968 rpmqv.c:1119
 msgid "--nopgp may only be used during signature checking"
 msgstr ""
 
-#: rpm.c:972 rpmqv.c:1123
+#: rpm.c:971 rpmqv.c:1122
 msgid "--nogpg may only be used during signature checking"
 msgstr ""
 
-#: rpm.c:975 rpmqv.c:1128
+#: rpm.c:974 rpmqv.c:1127
 msgid ""
 "--nomd5 may only be used during signature checking and package verification"
 msgstr ""
 
-#: rpm.c:986 rpmqv.c:1144
+#: rpm.c:985 rpmqv.c:1143
 msgid "no files to sign\n"
 msgstr ""
 
-#: rpm.c:991 rpmqv.c:1149
+#: rpm.c:990 rpmqv.c:1148
 #, c-format
 msgid "cannot access file %s\n"
 msgstr ""
 
-#: rpm.c:1006 rpmqv.c:1165
+#: rpm.c:1005 rpmqv.c:1164
 msgid "pgp not found: "
 msgstr ""
 
-#: rpm.c:1010 rpmqv.c:1169
+#: rpm.c:1009 rpmqv.c:1168
 msgid "Enter pass phrase: "
 msgstr ""
 
-#: rpm.c:1012 rpmqv.c:1171
+#: rpm.c:1011 rpmqv.c:1170
 msgid "Pass phrase check failed\n"
 msgstr ""
 
-#: rpm.c:1015 rpmqv.c:1174
+#: rpm.c:1014 rpmqv.c:1173
 msgid "Pass phrase is good.\n"
 msgstr ""
 
-#: rpm.c:1020 rpmqv.c:1179
+#: rpm.c:1019 rpmqv.c:1178
 msgid "Invalid %%_signature spec in macro file.\n"
 msgstr ""
 
-#: rpm.c:1026 rpmqv.c:1185
+#: rpm.c:1025 rpmqv.c:1184
 msgid "--sign may only be used during package building"
 msgstr ""
 
-#: rpm.c:1041 rpmqv.c:1201
+#: rpm.c:1040 rpmqv.c:1200
 msgid "exec failed\n"
 msgstr ""
 
-#: rpm.c:1060 rpmqv.c:1445
+#: rpm.c:1059 rpmqv.c:1444
 msgid "unexpected arguments to --querytags "
 msgstr ""
 
-#: rpm.c:1071 rpmqv.c:1467
+#: rpm.c:1070 rpmqv.c:1466
 msgid "no packages given for signature check"
 msgstr ""
 
-#: rpm.c:1082 rpmqv.c:1478
+#: rpm.c:1081 rpmqv.c:1477
 msgid "no packages given for signing"
 msgstr ""
 
-#: rpm.c:1098 rpmqv.c:1341
+#: rpm.c:1097 rpmqv.c:1340
 msgid "no packages given for uninstall"
 msgstr ""
 
-#: rpm.c:1162 rpmqv.c:1370
+#: rpm.c:1161 rpmqv.c:1369
 msgid "no packages given for install"
 msgstr ""
 
-#: rpm.c:1186 rpmqv.c:1411
+#: rpm.c:1185 rpmqv.c:1410
 msgid "extra arguments given for query of all packages"
 msgstr ""
 
-#: rpm.c:1191 rpmqv.c:1416
+#: rpm.c:1190 rpmqv.c:1415
 msgid "no arguments given for query"
 msgstr ""
 
-#: rpm.c:1208 rpmqv.c:1433
+#: rpm.c:1207 rpmqv.c:1432
 msgid "extra arguments given for verify of all packages"
 msgstr ""
 
-#: rpm.c:1212 rpmqv.c:1437
+#: rpm.c:1211 rpmqv.c:1436
 msgid "no arguments given for verify"
 msgstr ""
 
@@ -1101,374 +1101,374 @@ msgstr ""
 msgid "cannot re-open payload: %s\n"
 msgstr ""
 
-#: rpmqv.c:132
+#: rpmqv.c:131
 msgid "provide less detailed output"
 msgstr ""
 
-#: rpmqv.c:134
+#: rpmqv.c:133
 msgid "provide more detailed output"
 msgstr ""
 
-#: rpmqv.c:137
+#: rpmqv.c:136
 msgid "'<name> <body>'"
 msgstr ""
 
-#: rpmqv.c:139
+#: rpmqv.c:138
 msgid "print macro expansion of <expr>+"
 msgstr ""
 
-#: rpmqv.c:140
+#: rpmqv.c:139
 msgid "<expr>+"
 msgstr ""
 
-#: rpmqv.c:143
+#: rpmqv.c:142
 msgid "<cmd>"
 msgstr ""
 
-#: rpmqv.c:146 rpmqv.c:278
+#: rpmqv.c:145 rpmqv.c:277
 msgid "<dir>"
 msgstr ""
 
-#: rpmqv.c:148
+#: rpmqv.c:147
 msgid "read <file:...> instead of default macro file(s)"
 msgstr ""
 
-#: rpmqv.c:149 rpmqv.c:153 rpmqv.c:157
+#: rpmqv.c:148 rpmqv.c:152 rpmqv.c:156
 msgid "<file:...>"
 msgstr ""
 
-#: rpmqv.c:152 rpmqv.c:156
+#: rpmqv.c:151 rpmqv.c:155
 msgid "read <file:...> instead of default rpmrc file(s)"
 msgstr ""
 
-#: rpmqv.c:165
+#: rpmqv.c:164
 msgid "disable use of libio(3) API"
 msgstr ""
 
-#: rpmqv.c:168
+#: rpmqv.c:167
 msgid "debug protocol data stream"
 msgstr ""
 
-#: rpmqv.c:170
+#: rpmqv.c:169
 msgid "debug rpmio I/O"
 msgstr ""
 
-#: rpmqv.c:172
+#: rpmqv.c:171
 msgid "debug URL cache handling"
 msgstr ""
 
-#: rpmqv.c:180
+#: rpmqv.c:179
 msgid "initialize database"
 msgstr ""
 
-#: rpmqv.c:182
+#: rpmqv.c:181
 msgid "rebuild database inverted lists from installed package headers"
 msgstr ""
 
-#: rpmqv.c:185
+#: rpmqv.c:184
 msgid "generate headers compatible with (legacy) rpm[23] packaging"
 msgstr ""
 
-#: lib/poptBT.c:175 rpmqv.c:188
+#: lib/poptBT.c:175 rpmqv.c:187
 msgid "generate headers compatible with rpm4 packaging"
 msgstr ""
 
-#: rpmqv.c:233
+#: rpmqv.c:232
 msgid "save erased package files by renaming into sub-directory"
 msgstr ""
 
-#: rpmqv.c:235
+#: rpmqv.c:234
 msgid "<package>+"
 msgstr ""
 
-#: rpmqv.c:239 rpmqv.c:565
+#: rpmqv.c:238 rpmqv.c:564
 msgid "skip files with leading component <path> "
 msgstr ""
 
-#: rpmqv.c:240
+#: rpmqv.c:239
 msgid "<path>"
 msgstr ""
 
-#: rpmqv.c:245
+#: rpmqv.c:244
 msgid "upgrade package(s) if already installed"
 msgstr ""
 
-#: rpmqv.c:246 rpmqv.c:259 rpmqv.c:293
+#: rpmqv.c:245 rpmqv.c:258 rpmqv.c:292
 msgid "<packagefile>+"
 msgstr ""
 
-#: rpmqv.c:268 rpmqv.c:539
+#: rpmqv.c:267 rpmqv.c:538
 msgid "do not execute scripts (if any)"
 msgstr ""
 
-#: rpmqv.c:270 rpmqv.c:587
+#: rpmqv.c:269 rpmqv.c:586
 msgid "don't execute any scriptlets triggered by this package"
 msgstr ""
 
-#: rpmqv.c:280
+#: rpmqv.c:279
 msgid "relocate files from path <old> to <new>"
 msgstr ""
 
-#: rpmqv.c:281
+#: rpmqv.c:280
 msgid "<old>=<new>"
 msgstr ""
 
-#: rpmqv.c:283
+#: rpmqv.c:282
 msgid "save erased package files by repackaging"
 msgstr ""
 
-#: rpmqv.c:292
+#: rpmqv.c:291
 msgid "upgrade package(s)"
 msgstr ""
 
-#: rpmqv.c:312
+#: rpmqv.c:311
 msgid "Query options (with -q or --query):"
 msgstr ""
 
-#: rpmqv.c:315
+#: rpmqv.c:314
 msgid "Verify options (with -V or --verify):"
 msgstr ""
 
-#: rpmqv.c:321
+#: rpmqv.c:320
 msgid "Signature options:"
 msgstr ""
 
-#: rpmqv.c:327
+#: rpmqv.c:326
 msgid "Database options:"
 msgstr ""
 
-#: rpmqv.c:333
+#: rpmqv.c:332
 msgid "Build options with [ <specfile> | <tarball> | <source package> ]:"
 msgstr ""
 
-#: rpmqv.c:339
+#: rpmqv.c:338
 msgid "Install/Upgrade/Erase options:"
 msgstr ""
 
-#: rpmqv.c:344
+#: rpmqv.c:343
 msgid "Common options for all rpm modes:"
 msgstr ""
 
-#: rpmqv.c:378
+#: rpmqv.c:377
 #, c-format
 msgid "Usage: %s {--help}\n"
 msgstr ""
 
-#: rpmqv.c:449
+#: rpmqv.c:448
 msgid "  All modes support the following options:"
 msgstr ""
 
-#: rpmqv.c:450
+#: rpmqv.c:449
 msgid "   --define '<name> <body>'"
 msgstr ""
 
-#: rpmqv.c:452
+#: rpmqv.c:451
 msgid "   --eval '<expr>+'       "
 msgstr ""
 
-#: rpmqv.c:453
+#: rpmqv.c:452
 msgid "print the expansion of macro <expr> to stdout"
 msgstr ""
 
-#: rpmqv.c:454
+#: rpmqv.c:453
 msgid "   --pipe <cmd>           "
 msgstr ""
 
-#: rpmqv.c:456
+#: rpmqv.c:455
 msgid "   --rcfile <file:...>    "
 msgstr ""
 
-#: rpmqv.c:457
+#: rpmqv.c:456
 msgid "use <file:...> instead of default list of macro files"
 msgstr ""
 
-#: rpmqv.c:461
+#: rpmqv.c:460
 msgid "   --dbpath <dir>         "
 msgstr ""
 
-#: rpmqv.c:463
+#: rpmqv.c:462
 msgid "   --root <dir>           "
 msgstr ""
 
-#: rpmqv.c:473
+#: rpmqv.c:472
 msgid ""
 "  Install, upgrade and query (with -p) modes allow URL's to be used in place"
 msgstr ""
 
-#: rpmqv.c:474
+#: rpmqv.c:473
 msgid "  of file names as well as the following options:"
 msgstr ""
 
-#: rpmqv.c:475
+#: rpmqv.c:474
 msgid "     --ftpproxy <host>    "
 msgstr ""
 
-#: rpmqv.c:477
+#: rpmqv.c:476
 msgid "     --ftpport <port>     "
 msgstr ""
 
-#: rpmqv.c:479
+#: rpmqv.c:478
 msgid "     --httpproxy <host>   "
 msgstr ""
 
-#: rpmqv.c:481
+#: rpmqv.c:480
 msgid "     --httpport <port>    "
 msgstr ""
 
-#: rpmqv.c:487
+#: rpmqv.c:486
 msgid "  Package specification options:"
 msgstr ""
 
-#: lib/poptQV.c:68 rpmqv.c:489
+#: lib/poptQV.c:68 rpmqv.c:488
 msgid "query/verify all packages"
 msgstr ""
 
-#: rpmqv.c:490
+#: rpmqv.c:489
 msgid "     -f <file>+           "
 msgstr ""
 
-#: rpmqv.c:491
+#: rpmqv.c:490
 msgid "query/verify package owning <file>"
 msgstr ""
 
-#: rpmqv.c:492
+#: rpmqv.c:491
 msgid "     -p <packagefile>+    "
 msgstr ""
 
-#: rpmqv.c:493
+#: rpmqv.c:492
 msgid "query/verify (uninstalled) package <packagefile>"
 msgstr ""
 
-#: rpmqv.c:494
+#: rpmqv.c:493
 msgid "     --triggeredby <pkg>  "
 msgstr ""
 
-#: rpmqv.c:495
+#: rpmqv.c:494
 msgid "query/verify packages triggered by <pkg>"
 msgstr ""
 
-#: rpmqv.c:496
+#: rpmqv.c:495
 msgid "     --whatprovides <cap> "
 msgstr ""
 
-#: rpmqv.c:497
+#: rpmqv.c:496
 msgid "query/verify packages which provide <cap> capability"
 msgstr ""
 
-#: rpmqv.c:498
+#: rpmqv.c:497
 msgid "     --whatrequires <cap> "
 msgstr ""
 
-#: rpmqv.c:499
+#: rpmqv.c:498
 msgid "query/verify packages which require <cap> capability"
 msgstr ""
 
-#: rpmqv.c:503
+#: rpmqv.c:502
 msgid "     --queryformat <qfmt> "
 msgstr ""
 
-#: rpmqv.c:506
+#: rpmqv.c:505
 msgid "    Information selection options:"
 msgstr ""
 
-#: rpmqv.c:522
+#: rpmqv.c:521
 msgid "list capabilities provided by package"
 msgstr ""
 
-#: rpmqv.c:524
+#: rpmqv.c:523
 msgid "list capabilities required by package"
 msgstr ""
 
-#: rpmqv.c:526
+#: rpmqv.c:525
 msgid "print the various [un]install scriptlets"
 msgstr ""
 
-#: rpmqv.c:528
+#: rpmqv.c:527
 msgid "show the trigger scriptlets contained in the package"
 msgstr ""
 
-#: rpmqv.c:554
+#: rpmqv.c:553
 msgid "   --install <packagefile>"
 msgstr ""
 
-#: rpmqv.c:555
+#: rpmqv.c:554
 msgid "   -i <packagefile>       "
 msgstr ""
 
-#: rpmqv.c:564
+#: rpmqv.c:563
 msgid "     --excludepath <path> "
 msgstr ""
 
-#: rpmqv.c:585
+#: rpmqv.c:584
 msgid "don't execute any installation scriptlets"
 msgstr ""
 
-#: rpmqv.c:590
+#: rpmqv.c:589
 msgid "     --prefix <dir>       "
 msgstr ""
 
-#: rpmqv.c:592
+#: rpmqv.c:591
 msgid "     --relocate <oldpath>=<newpath>"
 msgstr ""
 
-#: rpmqv.c:601
+#: rpmqv.c:600
 msgid "   --upgrade <packagefile>"
 msgstr ""
 
-#: rpmqv.c:602
+#: rpmqv.c:601
 msgid "   -U <packagefile>       "
 msgstr ""
 
-#: rpmqv.c:607
+#: rpmqv.c:606
 msgid "   --erase <package>"
 msgstr ""
 
-#: rpmqv.c:626
+#: rpmqv.c:625
 msgid "   --resign <pkg>+        "
 msgstr ""
 
-#: rpmqv.c:628
+#: rpmqv.c:627
 msgid "   --addsign <pkg>+       "
 msgstr ""
 
-#: rpmqv.c:631
+#: rpmqv.c:630
 msgid "   --checksig <pkg>+"
 msgstr ""
 
-#: rpmqv.c:632
+#: rpmqv.c:631
 msgid "   -K <pkg>+             "
 msgstr ""
 
-#: rpmqv.c:645
+#: rpmqv.c:644
 msgid "initalize database (unnecessary, legacy use)"
 msgstr ""
 
-#: rpmqv.c:647
+#: rpmqv.c:646
 msgid "rebuild database indices from existing database headers"
 msgstr ""
 
-#: rpmqv.c:929
+#: rpmqv.c:928
 msgid "Use \"--macros <file:...>\" instead.\n"
 msgstr ""
 
-#: rpmqv.c:1003
+#: rpmqv.c:1002
 msgid "--dbpath given for operation that does not use a database"
 msgstr ""
 
-#: rpmqv.c:1087
+#: rpmqv.c:1086
 msgid ""
 "--notriggers may only be specified during package installation and erasure"
 msgstr ""
 
-#: rpmqv.c:1241
+#: rpmqv.c:1240
 msgid "no packages files given for rebuild"
 msgstr ""
 
-#: rpmqv.c:1310
+#: rpmqv.c:1309
 msgid "no spec files given for build"
 msgstr ""
 
-#: rpmqv.c:1312
+#: rpmqv.c:1311
 msgid "no tar files given for build"
 msgstr ""
 
@@ -2185,79 +2185,40 @@ msgstr ""
 msgid "line %d: Bad %s number: %s\n"
 msgstr ""
 
-#: lib/cpio.c:284
-msgid "========= Directories not explictly included in package:\n"
-msgstr ""
-
-#: lib/cpio.c:286
-#, c-format
-msgid "%9d %s\n"
-msgstr ""
-
-#: lib/cpio.c:1397
-#, c-format
-msgid "%s directory created with perms %04o.\n"
-msgstr ""
-
-#: lib/cpio.c:1499 lib/cpio.c:1615
-#, c-format
-msgid "%s saved as %s\n"
-msgstr ""
-
-#: lib/cpio.c:1640
-#, c-format
-msgid "%s rmdir of %s failed: Directory not empty\n"
-msgstr ""
-
-#: lib/cpio.c:1645
-#, c-format
-msgid "%s rmdir of %s failed: %s\n"
-msgstr ""
-
-#: lib/cpio.c:1654
-#, c-format
-msgid "%s unlink of %s failed: %s\n"
-msgstr ""
-
-#: lib/cpio.c:1671
-#, c-format
-msgid "%s created as %s\n"
-msgstr ""
-
-#: lib/cpio.c:2072
+#: lib/cpio.c:195
 #, c-format
 msgid "(error 0x%x)"
 msgstr ""
 
-#: lib/cpio.c:2075
+#: lib/cpio.c:198
 msgid "Bad magic"
 msgstr ""
 
-#: lib/cpio.c:2076
+#: lib/cpio.c:199
 msgid "Bad/unreadable  header"
 msgstr ""
 
-#: lib/cpio.c:2097
+#: lib/cpio.c:220
 msgid "Header size too big"
 msgstr ""
 
-#: lib/cpio.c:2098
+#: lib/cpio.c:221
 msgid "Unknown file type"
 msgstr ""
 
-#: lib/cpio.c:2099
+#: lib/cpio.c:222
 msgid "Missing hard link"
 msgstr ""
 
-#: lib/cpio.c:2100
+#: lib/cpio.c:223
 msgid "MD5 sum mismatch"
 msgstr ""
 
-#: lib/cpio.c:2101
+#: lib/cpio.c:224
 msgid "Internal error"
 msgstr ""
 
-#: lib/cpio.c:2110
+#: lib/cpio.c:233
 msgid " failed - "
 msgstr ""
 
@@ -2531,12 +2492,51 @@ msgstr ""
 msgid "file %s is on an unknown device\n"
 msgstr ""
 
+#: lib/fsm.c:214
+msgid "========= Directories not explictly included in package:\n"
+msgstr ""
+
+#: lib/fsm.c:216
+#, c-format
+msgid "%9d %s\n"
+msgstr ""
+
+#: lib/fsm.c:1177
+#, c-format
+msgid "%s directory created with perms %04o.\n"
+msgstr ""
+
+#: lib/fsm.c:1279 lib/fsm.c:1395
+#, c-format
+msgid "%s saved as %s\n"
+msgstr ""
+
+#: lib/fsm.c:1420
+#, c-format
+msgid "%s rmdir of %s failed: Directory not empty\n"
+msgstr ""
+
+#: lib/fsm.c:1425
+#, c-format
+msgid "%s rmdir of %s failed: %s\n"
+msgstr ""
+
+#: lib/fsm.c:1434
+#, c-format
+msgid "%s unlink of %s failed: %s\n"
+msgstr ""
+
+#: lib/fsm.c:1451
+#, c-format
+msgid "%s created as %s\n"
+msgstr ""
+
 #. This should not be allowed
 #: lib/header.c:172
 msgid "dataLength() RPM_STRING_TYPE count must be 1.\n"
 msgstr ""
 
-#: lib/header.c:207 lib/header.c:1036 lib/install.c:236
+#: lib/header.c:207 lib/header.c:1036 lib/psm.c:500
 #, c-format
 msgid "Data type %d not supported\n"
 msgstr ""
@@ -2621,66 +2621,6 @@ msgstr ""
 msgid "(unknown type)"
 msgstr ""
 
-#: lib/install.c:80
-#, c-format
-msgid "user %s does not exist - using root\n"
-msgstr ""
-
-#: lib/install.c:88
-#, c-format
-msgid "group %s does not exist - using root\n"
-msgstr ""
-
-#.
-#. * This would probably be a good place to check if disk space
-#. * was used up - if so, we should return a different error.
-#.
-#. XXX FIXME: Fclose with libio destroys errno
-#: lib/install.c:461
-#, c-format
-msgid "unpacking of archive failed%s%s: %s\n"
-msgstr ""
-
-#: lib/install.c:462
-msgid " on file "
-msgstr ""
-
-#: lib/install.c:503
-#, c-format
-msgid "cannot create %s %s\n"
-msgstr ""
-
-#: lib/install.c:509
-#, c-format
-msgid "cannot write to %s\n"
-msgstr ""
-
-#: lib/install.c:530
-msgid "installing a source package\n"
-msgstr ""
-
-#: lib/install.c:582
-msgid "source package contains no .spec file\n"
-msgstr ""
-
-#: lib/install.c:662
-msgid "source package expected, binary found\n"
-msgstr ""
-
-#: lib/install.c:733 lib/uninstall.c:26
-#, c-format
-msgid "%s: %s-%s-%s has %d files, test = %d\n"
-msgstr ""
-
-#: lib/install.c:797 lib/install.c:879 lib/uninstall.c:69 lib/uninstall.c:91
-#, c-format
-msgid "%s: running %s script(s) (if any)\n"
-msgstr ""
-
-#: lib/install.c:804
-msgid "skipping %s-%s-%s install, %%pre scriptlet failed rc %d\n"
-msgstr ""
-
 #: lib/misc.c:328 lib/misc.c:333 lib/misc.c:339
 #, c-format
 msgid "error creating temporary file %s\n"
@@ -2964,6 +2904,66 @@ msgstr ""
 msgid "unknown error %d encountered while manipulating package %s"
 msgstr ""
 
+#: lib/psm.c:344
+#, c-format
+msgid "user %s does not exist - using root\n"
+msgstr ""
+
+#: lib/psm.c:352
+#, c-format
+msgid "group %s does not exist - using root\n"
+msgstr ""
+
+#.
+#. * This would probably be a good place to check if disk space
+#. * was used up - if so, we should return a different error.
+#.
+#. XXX FIXME: Fclose with libio destroys errno
+#: lib/psm.c:725
+#, c-format
+msgid "unpacking of archive failed%s%s: %s\n"
+msgstr ""
+
+#: lib/psm.c:726
+msgid " on file "
+msgstr ""
+
+#: lib/psm.c:767
+#, c-format
+msgid "cannot create %s %s\n"
+msgstr ""
+
+#: lib/psm.c:773
+#, c-format
+msgid "cannot write to %s\n"
+msgstr ""
+
+#: lib/psm.c:794
+msgid "installing a source package\n"
+msgstr ""
+
+#: lib/psm.c:846
+msgid "source package contains no .spec file\n"
+msgstr ""
+
+#: lib/psm.c:926
+msgid "source package expected, binary found\n"
+msgstr ""
+
+#: lib/psm.c:997 lib/psm.c:1188
+#, c-format
+msgid "%s: %s-%s-%s has %d files, test = %d\n"
+msgstr ""
+
+#: lib/psm.c:1061 lib/psm.c:1143 lib/psm.c:1231 lib/psm.c:1253
+#, c-format
+msgid "%s: running %s script(s) (if any)\n"
+msgstr ""
+
+#: lib/psm.c:1068
+msgid "skipping %s-%s-%s install, %%pre scriptlet failed rc %d\n"
+msgstr ""
+
 #: lib/query.c:151
 #, c-format
 msgid "incorrect format: %s\n"
@@ -3709,28 +3709,28 @@ msgstr ""
 msgid "%s skipped due to missingok flag\n"
 msgstr ""
 
-#: lib/verify.c:59
+#: lib/verify.c:57
 msgid "don't verify files in package"
 msgstr ""
 
-#: lib/verify.c:65
+#: lib/verify.c:63
 msgid "do not execute %verifyscript (if any)"
 msgstr ""
 
-#: lib/verify.c:241
+#: lib/verify.c:239
 msgid "package lacks both user name and id lists (this should never happen)\n"
 msgstr ""
 
-#: lib/verify.c:259
+#: lib/verify.c:257
 msgid "package lacks both group name and id lists (this should never happen)\n"
 msgstr ""
 
-#: lib/verify.c:321
+#: lib/verify.c:319
 #, c-format
 msgid "missing    %s"
 msgstr ""
 
-#: lib/verify.c:402
+#: lib/verify.c:400
 #, c-format
 msgid "Unsatisfied dependencies for %s-%s-%s: "
 msgstr ""
diff --git a/rpm.c b/rpm.c
index c8dc031..235f8b0 100755 (executable)
--- a/rpm.c
+++ b/rpm.c
@@ -4,7 +4,6 @@
 #include <rpmurl.h>
 
 #include "build.h"
-#include "install.h"
 #include "signature.h"
 #include "debug.h"
 
diff --git a/rpmqv.c b/rpmqv.c
index cb7927b..384e552 100755 (executable)
--- a/rpmqv.c
+++ b/rpmqv.c
@@ -25,7 +25,6 @@ static int initdb = 0;
 #endif
 
 #ifdef IAM_RPMEIU
-#include "install.h"
 #define GETOPT_INSTALL         1014
 #define GETOPT_RELOCATE                1016
 #define GETOPT_EXCLUDEPATH     1019