Eliminate static prototypes.
authorjbj <devnull@localhost>
Wed, 14 Jul 1999 14:36:10 +0000 (14:36 +0000)
committerjbj <devnull@localhost>
Wed, 14 Jul 1999 14:36:10 +0000 (14:36 +0000)
CVS patchset: 3153
CVS date: 1999/07/14 14:36:10

lib/transaction.c
lib/uninstall.c

index bee30a1..92cfbd1 100644 (file)
@@ -65,37 +65,7 @@ struct diskspaceInfo {
    probably right :-( */
 #define BLOCK_ROUND(size, block) (((size) + (block) - 1) / (block))
 
-static rpmProblemSet psCreate(void);
-static void psAppend(rpmProblemSet probs, rpmProblemType type, 
-                    const void * key, Header h, const char * str1, 
-                    Header altHeader, unsigned long ulong1);
-static int archOkay(Header h);
-static int osOkay(Header h);
-static Header relocateFileList(struct availablePackage * alp, 
-                              rpmProblemSet probs, Header h, 
-                              enum fileActions * actions,
-                              int allowBadRelocate);
-static int psTrim(rpmProblemSet filter, rpmProblemSet target);
-static int sharedCmp(const void * one, const void * two);
-static enum fileActions decideFileFate(const char * filespec, short dbMode, 
-                               char * dbMd5, char * dbLink, short newMode, 
-                               char * newMd5, char * newLink, int newFlags,
-                               int brokenMd5);
-enum fileTypes whatis(short mode);
-static int filecmp(short mode1, char * md51, char * link1, 
-                  short mode2, char * md52, char * link2);
-static int handleInstInstalledFiles(struct fileInfo * fi, rpmdb db,
-                                   struct sharedFileInfo * shared,
-                                   int sharedCount, int reportConflicts,
-                                   rpmProblemSet probs);
-static int handleRmvdInstalledFiles(struct fileInfo * fi, rpmdb db,
-                                   struct sharedFileInfo * shared,
-                                   int sharedCount);
-void handleOverlappedFiles(struct fileInfo * fi, hashTable ht,
-                          rpmProblemSet probs, struct diskspaceInfo * dsl);
-static int ensureOlder(rpmdb db, Header new, int dbOffset, rpmProblemSet probs,
-                      const void * key);
-static void skipFiles(struct fileInfo * fi, int noDocs);
+#define XSTRCMP(a, b) ((!(a) && !(b)) || ((a) && (b) && !strcmp((a), (b))))
 
 static void freeFi(struct fileInfo *fi)
 {
@@ -147,1179 +117,1195 @@ static void freeFl(rpmTransactionSet ts, struct fileInfo *flList)
     }
 }
 
-#define XSTRCMP(a, b) ((!(a) && !(b)) || ((a) && (b) && !strcmp((a), (b))))
-
-#define        NOTIFY(_x)      if (notify) notify _x
+void rpmtransSetScriptFd(rpmTransactionSet ts, FD_t fd)
+{
+    ts->scriptFd = fd;
+}
 
-/* Return -1 on error, > 0 if newProbs is set, 0 if everything
-   happened */
-int rpmRunTransactions(rpmTransactionSet ts, rpmCallbackFunction notify,
-                      void * notifyData, rpmProblemSet okProbs,
-                      rpmProblemSet * newProbs, int flags, int ignoreSet) {
-    int i, j;
-    int rc, ourrc = 0;
-    struct availablePackage * alp;
+static rpmProblemSet psCreate(void)
+{
     rpmProblemSet probs;
-    dbiIndexSet dbi, * matches;
-    Header * hdrs;
-    int fileCount;
-    int totalFileCount = 0;
-    hashTable ht;
-    struct fileInfo * flList, * fi;
-    struct sharedFileInfo * shared, * sharedList;
-    int numShared;
-    int flEntries;
-    int last;
-    int lastFailed;
-    int beingRemoved;
-    char * currDir, * chptr;
-    FD_t fd;
-    const char ** filesystems;
-    int filesystemCount;
-    struct diskspaceInfo * di = NULL;
-    int oc;
-
-    /* FIXME: what if the same package is included in ts twice? */
-
-    if (!(ignoreSet & RPMPROB_FILTER_DISKSPACE) &&
-               !rpmGetFilesystemList(&filesystems, &filesystemCount)) {
-       struct stat sb;
 
-       di = alloca(sizeof(*di) * (filesystemCount + 1));
+    probs = malloc(sizeof(*probs));
+    probs->numProblems = probs->numProblemsAlloced = 0;
+    probs->probs = NULL;
 
-       for (i = 0; (i < filesystemCount) && di; i++) {
-#if STATFS_IN_SYS_STATVFS
-           struct statvfs sfb;
-           if (statvfs(filesystems[i], &sfb))
-#else
-           struct statfs sfb;
-#  if STAT_STATFS4
-/* this platform has the 4-argument version of the statfs call.  The last two
- * should be the size of struct statfs and 0, respectively.  The 0 is the
- * filesystem type, and is always 0 when statfs is called on a mounted
- * filesystem, as we're doing.
- */
-           if (statfs(filesystems[i], &sfb, sizeof(sfb), 0))
-#  else
-           if (statfs(filesystems[i], &sfb))
-#  endif
-#endif
-           {
-               di = NULL;
-           } else {
-               di[i].block = sfb.f_bsize;
-               di[i].needed = 0;
-#ifdef STATFS_HAS_F_BAVAIL
-               di[i].avail = sfb.f_bavail;
-#else
-/* FIXME: the statfs struct doesn't have a member to tell how many blocks are
- * available for non-superusers.  f_blocks - f_bfree is probably too big, but
- * it's about all we can do.
- */
-               di[i].avail = sfb.f_blocks - sfb.f_bfree;
-#endif
+    return probs;
+}
 
+static void psAppend(rpmProblemSet probs, rpmProblemType type, 
+                    const void * key, Header h, const char * str1, 
+                    Header altH, unsigned long ulong1)
+{
+    if (probs->numProblems == probs->numProblemsAlloced) {
+       if (probs->numProblemsAlloced)
+           probs->numProblemsAlloced *= 2;
+       else
+           probs->numProblemsAlloced = 2;
+       probs->probs = realloc(probs->probs, 
+                       probs->numProblemsAlloced * sizeof(*probs->probs));
+    }
 
-               stat(filesystems[i], &sb);
-               di[i].dev = sb.st_dev;
-           }
-       }
+    probs->probs[probs->numProblems].type = type;
+    probs->probs[probs->numProblems].key = key;
+    probs->probs[probs->numProblems].h = headerLink(h);
+    probs->probs[probs->numProblems].ulong1 = ulong1;
+    if (str1)
+       probs->probs[probs->numProblems].str1 = strdup(str1);
+    else
+       probs->probs[probs->numProblems].str1 = NULL;
 
-       if (di) di[i].block = 0;
-    }
+    if (altH)
+       probs->probs[probs->numProblems].altH = headerLink(altH);
+    else
+       probs->probs[probs->numProblems].altH = NULL;
 
-    probs = psCreate();
-    *newProbs = probs;
-    hdrs = alloca(sizeof(*hdrs) * ts->addedPackages.size);
+    probs->probs[probs->numProblems++].ignoreProblem = 0;
+}
 
-    /* The ordering doesn't matter here */
-    for (alp = ts->addedPackages.list; (alp - ts->addedPackages.list) < 
-           ts->addedPackages.size; alp++) {
-       if (!archOkay(alp->h) && !(ignoreSet & RPMPROB_FILTER_IGNOREARCH))
-           psAppend(probs, RPMPROB_BADARCH, alp->key, alp->h, NULL, NULL, 0);
+static int archOkay(Header h)
+{
+    int_8 * pkgArchNum;
+    void * pkgArch;
+    int type, count, archNum;
 
-       if (!osOkay(alp->h) && !(ignoreSet & RPMPROB_FILTER_IGNOREOS)) {
-           psAppend(probs, RPMPROB_BADOS, alp->key, alp->h, NULL, NULL, 0);
+    /* make sure we're trying to install this on the proper architecture */
+    headerGetEntry(h, RPMTAG_ARCH, &type, (void **) &pkgArch, &count);
+    if (type == RPM_INT8_TYPE) {
+       /* old arch handling */
+       rpmGetArchInfo(NULL, &archNum);
+       pkgArchNum = pkgArch;
+       if (archNum != *pkgArchNum) {
+           return 0;
+       }
+    } else {
+       /* new arch handling */
+       if (!rpmMachineScore(RPM_MACHTABLE_INSTARCH, pkgArch)) {
+           return 0;
        }
+    }
 
-       if (!(ignoreSet & RPMPROB_FILTER_OLDPACKAGE)) {
-           rc = rpmdbFindPackage(ts->db, alp->name, &dbi);
-           if (rc == 2) {
-               return -1;
-           } else if (!rc) {
-               for (i = 0; i < dbi.count; i++) 
-                   ensureOlder(ts->db, alp->h, dbi.recs[i].recOffset, 
-                                     probs, alp->key);
+    return 1;
+}
 
-               dbiFreeIndexRecord(dbi);
-           }
-       }
+static int osOkay(Header h)
+{
+    void * pkgOs;
+    int type, count;
 
-       rc = findMatches(ts->db, alp->name, alp->version, alp->release, &dbi);
-       if (rc == 2) {
-           return -1;
-       } else if (!rc) {
-           if (!(ignoreSet & RPMPROB_FILTER_REPLACEPKG))
-               psAppend(probs, RPMPROB_PKG_INSTALLED, alp->key, alp->h, NULL, 
-                        NULL, 0);
-           dbiFreeIndexRecord(dbi);
+    /* make sure we're trying to install this on the proper os */
+    headerGetEntry(h, RPMTAG_OS, &type, (void **) &pkgOs, &count);
+    if (type == RPM_INT8_TYPE) {
+       /* v1 packages and v2 packages both used improper OS numbers, so just
+          deal with it hope things work */
+       return 1;
+    } else {
+       /* new os handling */
+       if (!rpmMachineScore(RPM_MACHTABLE_INSTOS, pkgOs)) {
+           return 0;
        }
-
-       if (headerGetEntry(alp->h, RPMTAG_FILENAMES, NULL, NULL, &fileCount))
-           totalFileCount += fileCount;
     }
 
-    /* FIXME: it seems a bit silly to read in all of these headers twice */
-    /* The ordering doesn't matter here */
-    for (i = 0; i < ts->numRemovedPackages; i++) {
-       Header h;
+    return 1;
+}
 
-       if ((h = rpmdbGetRecord(ts->db, ts->removedPackages[i]))) {
-           if (headerGetEntry(h, RPMTAG_FILENAMES, NULL, NULL, 
-                              &fileCount))
-               totalFileCount += fileCount;
-           headerFree(h);      /* XXX ==> LEAK */
-       }
+void rpmProblemSetFree(rpmProblemSet probs)
+{
+    int i;
+
+    for (i = 0; i < probs->numProblems; i++) {
+       headerFree(probs->probs[i].h);
+       if (probs->probs[i].str1) free(probs->probs[i].str1);
+       if (probs->probs[i].altH) headerFree(probs->probs[i].altH);
     }
+    free(probs);
+}
 
-    flEntries = ts->addedPackages.size + ts->numRemovedPackages;
-    flList = alloca(sizeof(*flList) * (flEntries));
+static Header relocateFileList(struct availablePackage * alp, 
+                              rpmProblemSet probs, Header origH,
+                              enum fileActions * actions,
+                              int allowBadRelocate)
+{
+    int numValid, numRelocations;
+    int i, j, madeSwap, rc;
+    rpmRelocation * nextReloc, * relocations = NULL;
+    rpmRelocation * rawRelocations = alp->relocs;
+    rpmRelocation tmpReloc;
+    char ** validRelocations, ** actualRelocations;
+    char ** names;
+    char ** origNames;
+    int len = 0;
+    char * newName;
+    int_32 fileCount;
+    Header h;
+    int relocated = 0;
 
-    ht = htCreate(totalFileCount * 2, 0, fpHashFunction, fpEqual);
+    if (!headerGetEntry(origH, RPMTAG_PREFIXES, NULL,
+                       (void **) &validRelocations, &numValid))
+       numValid = 0;
 
-    /* FIXME?: we'd be better off assembling one very large file list and
-       calling fpLookupList only once. I'm not sure that the speedup is
-       worth the trouble though. */
-    for (fi = flList, oc = 0; oc < ts->orderCount; fi++, oc++) {
-       memset(fi, 0, sizeof(*fi));
+    if (!rawRelocations && !numValid) return headerLink(origH);
 
-       if (ts->order[oc].type == TR_ADDED) {
-           i = ts->order[oc].u.addedIndex;
-           alp = ts->addedPackages.list + ts->order[oc].u.addedIndex;
+    h = headerCopy(origH);
 
-           if (!headerGetEntryMinMemory(alp->h, RPMTAG_FILENAMES, NULL, 
-                                        NULL, &fi->fc)) {
-               fi->h = headerLink(alp->h);
-               hdrs[i] = headerLink(fi->h);
-               continue;
-           }
+    if (rawRelocations) {
+       for (i = 0; rawRelocations[i].newPath || rawRelocations[i].oldPath; 
+               i++) ;
+       numRelocations = i;
+    } else {
+       numRelocations = 0;
+    }
+    relocations = alloca(sizeof(*relocations) * numRelocations);
 
-           fi->actions = calloc(sizeof(*fi->actions), fi->fc);
-           hdrs[i] = relocateFileList(alp, probs, alp->h, fi->actions, 
-                                 ignoreSet & RPMPROB_FILTER_FORCERELOCATE);
-           fi->h = headerLink(hdrs[i]);
-           fi->type = TR_ADDED;
-           fi->ap = alp;
+    for (i = 0; i < numRelocations; i++) {
+       /* FIXME: default relocations (oldPath == NULL) need to be handled
+          in the UI, not rpmlib */
+
+       relocations[i].oldPath = 
+           alloca(strlen(rawRelocations[i].oldPath) + 1);
+       strcpy(relocations[i].oldPath, rawRelocations[i].oldPath);
+       stripTrailingSlashes(relocations[i].oldPath);
+
+       if (rawRelocations[i].newPath) {
+           relocations[i].newPath = 
+               alloca(strlen(rawRelocations[i].newPath) + 1);
+           strcpy(relocations[i].newPath, rawRelocations[i].newPath);
+           stripTrailingSlashes(relocations[i].newPath);
        } else {
-           fi->record = ts->order[oc].u.removed.dboffset;
-           fi->h = rpmdbGetRecord(ts->db, fi->record);
-           if (!fi->h) {
-               /* ACK! */
-               continue;
-           }
-           fi->type = TR_REMOVED;
+           relocations[i].newPath = NULL;
        }
 
-       if (!headerGetEntry(fi->h, RPMTAG_FILENAMES, NULL, 
-                                    (void **) &fi->fl, &fi->fc)) {
-           /* This catches removed packages w/ no file lists */
-           fi->fc = 0;
-           continue;
+       if (relocations[i].newPath) {
+           for (j = 0; j < numValid; j++) 
+               if (!strcmp(validRelocations[j],
+                           relocations[i].oldPath)) break;
+           if (j == numValid && !allowBadRelocate) 
+               psAppend(probs, RPMPROB_BADRELOCATE, alp->key, alp->h, 
+                        relocations[i].oldPath, NULL,0 );
        }
+    }
 
-       /* actions is initialized earlier for added packages */
-       if (!fi->actions) 
-           fi->actions = calloc(sizeof(*fi->actions), fi->fc);
-
-       headerGetEntry(fi->h, RPMTAG_FILEMODES, NULL, 
-                               (void **) &fi->fmodes, NULL);
-       headerGetEntry(fi->h, RPMTAG_FILEFLAGS, NULL, 
-                               (void **) &fi->fflags, NULL);
-       headerGetEntry(fi->h, RPMTAG_FILESIZES, NULL, 
-                               (void **) &fi->fsizes, NULL);
-       headerGetEntry(fi->h, RPMTAG_FILESTATES, NULL, 
-                               (void **) &fi->fstates, NULL);
-
-       if (ts->order[oc].type == TR_REMOVED) {
-           headerGetEntry(fi->h, RPMTAG_FILEMD5S, NULL, 
-                                   (void **) &fi->fmd5s, NULL);
-           headerGetEntry(fi->h, RPMTAG_FILELINKTOS, NULL, 
-                                   (void **) &fi->flinks, NULL);
-           fi->fsizes = memcpy(malloc(fi->fc * sizeof(*fi->fsizes)),
-                               fi->fsizes, fi->fc * sizeof(*fi->fsizes));
-           fi->fflags = memcpy(malloc(fi->fc * sizeof(*fi->fflags)),
-                               fi->fflags, fi->fc * sizeof(*fi->fflags));
-           fi->fmodes = memcpy(malloc(fi->fc * sizeof(*fi->fmodes)),
-                               fi->fmodes, fi->fc * sizeof(*fi->fmodes));
-           fi->fstates = memcpy(malloc(fi->fc * sizeof(*fi->fstates)),
-                               fi->fstates, fi->fc * sizeof(*fi->fstates));
-           headerFree(fi->h);
-           fi->h = NULL;
-       } else {
-           /* ADDED package */
+    /* stupid bubble sort, but it's probably faster here */
+    for (i = 0; i < numRelocations; i++) {
+       madeSwap = 0;
+       for (j = 1; j < numRelocations; j++) {
+           if (strcmp(relocations[j - 1].oldPath, 
+                      relocations[j].oldPath) > 0) {
+               tmpReloc = relocations[j - 1];
+               relocations[j - 1] = relocations[j];
+               relocations[j] = tmpReloc;
+               madeSwap = 1;
+           }
+       }
+       if (!madeSwap) break;
+    }
 
-           headerGetEntryMinMemory(fi->h, RPMTAG_FILEMD5S, NULL, 
-                                   (void **) &fi->fmd5s, NULL);
-           headerGetEntryMinMemory(fi->h, RPMTAG_FILELINKTOS, NULL, 
-                                   (void **) &fi->flinks, NULL);
+    if (numValid) {
+       actualRelocations = malloc(sizeof(*actualRelocations) * numValid);
+       for (i = 0; i < numValid; i++) {
+           for (j = 0; j < numRelocations; j++) {
+               if (!strcmp(validRelocations[i], relocations[j].oldPath)) {
+                   actualRelocations[i] = relocations[j].newPath;
+                   break;
+               }
+           }
 
-           /* 0 makes for noops */
-           fi->replacedSizes = calloc(fi->fc, sizeof(*fi->replacedSizes));
-           skipFiles(fi, flags & RPMTRANS_FLAG_NODOCS);
+           if (j == numRelocations)
+               actualRelocations[i] = validRelocations[i];
        }
 
-        fi->fps = malloc(fi->fc * sizeof(*fi->fps));
+       headerAddEntry(h, RPMTAG_INSTPREFIXES, RPM_STRING_ARRAY_TYPE,
+                      (void **) actualRelocations, numValid);
+
+       free(actualRelocations);
+       free(validRelocations);
     }
 
-    chptr = currentDirectory();
-    currDir = alloca(strlen(chptr) + 1);
-    strcpy(currDir, chptr);
-    free(chptr);
-    chdir("/");
-    chroot(ts->root);
+    headerGetEntry(h, RPMTAG_FILENAMES, NULL, (void **) &names, 
+                  &fileCount);
 
-    for (fi = flList; (fi - flList) < flEntries; fi++) {
-       fpLookupList(fi->fl, fi->fps, fi->fc, 1);
-       for (i = 0; i < fi->fc; i++) {
-           if (fi->actions[i] != FA_SKIP && fi->actions[i] != FA_SKIPNSTATE)
-               htAddEntry(ht, fi->fps + i, fi);
+    /* go through things backwards so that /usr/local relocations take
+       precedence over /usr ones */
+    for (i = fileCount - 1; i >= 0; i--) {
+       for (j = numRelocations - 1; j >= 0; j--) {
+           nextReloc = relocations + j;
+           len = strlen(relocations[j].oldPath);
+           rc = (!strncmp(relocations[j].oldPath, names[i], len) && 
+                   ((names[i][len] == '/') || !names[i][len]));
+           if (rc) break;
        }
-    }
 
-    NOTIFY((NULL, RPMCALLBACK_TRANS_START, 6, flEntries, NULL, notifyData));
+       if (j >= 0) {
+           nextReloc = relocations + j;
+           if (nextReloc->newPath) {
+               newName = alloca(strlen(nextReloc->newPath) + 
+                                strlen(names[i]) + 1);
+               strcpy(newName, nextReloc->newPath);
+               strcat(newName, names[i] + len);
+               rpmMessage(RPMMESS_DEBUG, _("relocating %s to %s\n"),
+                          names[i], newName);
+               names[i] = newName;
+               relocated = 1;
+           } else if (actions) {
+               actions[i] = FA_SKIPNSTATE;
+               rpmMessage(RPMMESS_DEBUG, _("excluding %s\n"), names[i]);
+           }
+       } 
+    }
 
-    for (fi = flList; (fi - flList) < flEntries; fi++) {
-       int k, ro;
-       int knownBad;
+    if (relocated) {
+       headerGetEntry(h, RPMTAG_FILENAMES, NULL, (void **) &origNames, NULL);
+       headerAddEntry(h, RPMTAG_ORIGFILENAMES, RPM_STRING_ARRAY_TYPE, 
+                         origNames, fileCount);
+       free(origNames);
+       headerModifyEntry(h, RPMTAG_FILENAMES, RPM_STRING_ARRAY_TYPE, 
+                         names, fileCount);
+    }
 
-       NOTIFY((NULL, RPMCALLBACK_TRANS_PROGRESS, (fi - flList), flEntries,
-              NULL, notifyData));
+    free(names);
 
-       matches = malloc(sizeof(*matches) * fi->fc);
-       if (rpmdbFindFpList(ts->db, fi->fps, matches, fi->fc)) return 1;
+    return h;
+}
 
-       numShared = 0;
-       for (i = 0; i < fi->fc; i++)
-           numShared += matches[i].count;
+static int psTrim(rpmProblemSet filter, rpmProblemSet target)
+{
+    /* As the problem sets are generated in an order solely dependent
+       on the ordering of the packages in the transaction, and that
+       ordering can't be changed, the problem sets must be parallel to
+       on another. Additionally, the filter set must be a subset of the
+       target set, given the operations available on transaction set.
+       This is good, as it lets us perform this trim in linear time, rather
+       then logarithmic or quadratic. */
+    rpmProblem * f, * t;
+    int gotProblems = 0;
 
-       shared = sharedList = malloc(sizeof(*sharedList) * (numShared + 1));
-       knownBad = 0;
-       for (i = 0; i < fi->fc; i++) {
-           /* Take care not to mark files as replaced in packages that will
-              have been removed before we got here. */
-           for (j = 0; j < matches[i].count; j++) {
-               ro = matches[i].recs[j].recOffset;
-               if (ro != knownBad) {
-                   for (k = 0; k < ts->orderCount; k++) {
-                       if (ts->order[k].type == TR_REMOVED &&
-                           ts->order[k].u.removed.dboffset == ro) break;
-                   }
-                   if (k < ts->orderCount) {
-                       knownBad = ro;
-                   }
-               }
+    f = filter->probs;
+    t = target->probs;
 
-               shared->pkgFileNum = i;
-               shared->otherPkg = matches[i].recs[j].recOffset;
-               shared->otherFileNum = matches[i].recs[j].fileNumber;
-               shared->isRemoved = (knownBad == ro);
-               shared++;
-           }
-           dbiFreeIndexRecord(matches[i]);
+    while ((f - filter->probs) < filter->numProblems) {
+       if (!f->ignoreProblem) {
+           f++;
+           continue;
+       }
+       while ((t - target->probs) < target->numProblems) {
+           if (f->h == t->h && f->type == t->type && t->key == f->key &&
+                    XSTRCMP(f->str1, t->str1))
+               break;
+           t++;
+           gotProblems = 1;
        }
-       numShared = shared - sharedList;
-       shared->otherPkg = -1;
-       free(matches);
-
-       qsort(sharedList, numShared, sizeof(*shared), sharedCmp);
 
-       i = 0;
-       while (i < numShared) {
-           last = i + 1;
-           j = sharedList[i].otherPkg;
-           while ((sharedList[last].otherPkg == j) && (last < numShared))
-               last++;
-           last--;
+       if ((t - target->probs) == target->numProblems) {
+           /* this can't happen ;-) lets be sane if it doesn though */
+           break;
+       }
 
-           for (j = 0; j < ts->numRemovedPackages; j++) {
-               if (ts->removedPackages[j] == sharedList[i].otherPkg)
-                   break;
-           }
-           beingRemoved = (j < ts->numRemovedPackages);
+       t->ignoreProblem = f->ignoreProblem;
+       t++, f++;
+    }
 
-           if (fi->type == TR_ADDED)
-               handleInstInstalledFiles(fi, ts->db, sharedList + i, 
-                        last - i + 1, 
-                        !(beingRemoved || 
-                               (ignoreSet & RPMPROB_FILTER_REPLACEOLDFILES)), 
-                        probs);
-           else if (fi->type == TR_REMOVED && !beingRemoved)
-               handleRmvdInstalledFiles(fi, ts->db, sharedList + i, 
-                                        last - i + 1);
+    if ((t - target->probs) < target->numProblems)
+       gotProblems = 1;
 
-           i = last + 1;
-       }
+    return gotProblems;
+}
 
-       free(sharedList);
+static int sharedCmp(const void * one, const void * two)
+{
+    const struct sharedFileInfo * a = one;
+    const struct sharedFileInfo * b = two;
 
-       handleOverlappedFiles(fi, ht, 
-              (ignoreSet & RPMPROB_FILTER_REPLACENEWFILES) ? NULL : probs, di);
+    if (a->otherPkg < b->otherPkg)
+       return -1;
+    else if (a->otherPkg > b->otherPkg)
+       return 1;
 
-       if (di && fi->type == TR_ADDED && fi->fc) {
-           for (i = 0; i < filesystemCount; i++) {
-               if (adj_fs_blocks(di[i].needed) > di[i].avail) {
-                   psAppend(probs, RPMPROB_DISKSPACE, fi->ap->key, fi->ap->h,
-                            filesystems[i], NULL, 
-                            (adj_fs_blocks(di[i].needed) - di[i].avail) *
-                               di[i].block);
-               }
-           }
-       }
-    }
+    return 0;
+}
 
-    NOTIFY((NULL, RPMCALLBACK_TRANS_STOP, 6, flEntries, NULL, notifyData));
+static enum fileTypes whatis(short mode)
+{
+    enum fileTypes result;
 
-    chroot(".");
-    chdir(currDir);
+    if (S_ISDIR(mode))
+       result = XDIR;
+    else if (S_ISCHR(mode))
+       result = CDEV;
+    else if (S_ISBLK(mode))
+       result = BDEV;
+    else if (S_ISLNK(mode))
+       result = LINK;
+    else if (S_ISSOCK(mode))
+       result = SOCK;
+    else if (S_ISFIFO(mode))
+       result = PIPE;
+    else
+       result = REG;
+    return result;
+}
 
-    htFree(ht);
+static enum fileActions decideFileFate(const char * filespec, short dbMode, 
+                               char * dbMd5, char * dbLink, short newMode, 
+                               char * newMd5, char * newLink, int newFlags,
+                               int brokenMd5)
+{
+    char buffer[1024];
+    char * dbAttr, * newAttr;
+    enum fileTypes dbWhat, newWhat, diskWhat;
+    struct stat sb;
+    int i, rc;
+    int save = (newFlags & RPMFILE_NOREPLACE) ? FA_ALTNAME : FA_SAVE;
 
-    for (oc = 0, fi = flList; oc < ts->orderCount; oc++, fi++) {
-       if (fi->fc) {
-           free(fi->fl); fi->fl = NULL;
-           if (fi->type == TR_ADDED) {
-               free(fi->fmd5s); fi->fmd5s = NULL;
-               free(fi->flinks); fi->flinks = NULL;
-               free(fi->fps); fi->fps = NULL;
-           }
-       }
+    if (lstat(filespec, &sb)) {
+       /* the file doesn't exist on the disk create it unless the new
+          package has marked it as missingok */
+       if (newFlags & RPMFILE_MISSINGOK) {
+           rpmMessage(RPMMESS_DEBUG, _("%s skipped due to missingok flag\n"),
+                       filespec);
+           return FA_SKIP;
+       } else
+           return FA_CREATE;
     }
 
-    if ((flags & RPMTRANS_FLAG_BUILD_PROBS) || 
-           (probs->numProblems && (!okProbs || psTrim(okProbs, probs)))) {
-       *newProbs = probs;
+    diskWhat = whatis(sb.st_mode);
+    dbWhat = whatis(dbMode);
+    newWhat = whatis(newMode);
 
-       for (alp = ts->addedPackages.list, fi = flList; 
-               (alp - ts->addedPackages.list) < ts->addedPackages.size; 
-               alp++, fi++) {
-           headerFree(hdrs[alp - ts->addedPackages.list]);
-       }
+    /* RPM >= 2.3.10 shouldn't create config directories -- we'll ignore
+       them in older packages as well */
+    if (newWhat == XDIR)
+       return FA_CREATE;
 
-       freeFl(ts, flList);
-       return ts->orderCount;
+    if (diskWhat != newWhat) {
+       return save;
+    } else if (newWhat != dbWhat && diskWhat != dbWhat) {
+       return save;
+    } else if (dbWhat != newWhat) {
+       return FA_CREATE;
+    } else if (dbWhat != LINK && dbWhat != REG) {
+       return FA_CREATE;
     }
 
-    lastFailed = -2;
-    for (oc = 0, fi = flList; oc < ts->orderCount; oc++, fi++) {
-       if (ts->order[oc].type == TR_ADDED) {
-           alp = ts->addedPackages.list + ts->order[oc].u.addedIndex;
-           i = ts->order[oc].u.addedIndex;
-
-           if (alp->fd) {
-               fd = alp->fd;
-           } else {
-               fd = notify(fi->h, RPMCALLBACK_INST_OPEN_FILE, 0, 0, 
-                           alp->key, notifyData);
-               if (fd) {
-                   Header h;
-
-                   headerFree(hdrs[i]);
-                   rc = rpmReadPackageHeader(fd, &h, NULL, NULL, NULL);
-                   if (rc) {
-                       notify(fi->h, RPMCALLBACK_INST_CLOSE_FILE, 0, 0, 
-                                   alp->key, notifyData);
-                       ourrc++;
-                       fd = NULL;
-                   } else {
-                       hdrs[i] = relocateFileList(alp, probs, h, NULL, 1);
-                       headerFree(h);
-                   }
-               }
-           }
-
-           if (fd) {
-               if (installBinaryPackage(ts->root, ts->db, fd, 
-                                        hdrs[i], flags, notify, 
-                                        notifyData, alp->key, fi->actions, 
-                                        fi->fc ? fi->replaced : NULL,
-                                        ts->scriptFd)) {
-                   ourrc++;
-                   lastFailed = i;
-               }
-           } else {
-               ourrc++;
-               lastFailed = i;
-           }
+    if (dbWhat == REG) {
+       if (brokenMd5)
+           rc = mdfileBroken(filespec, buffer);
+       else
+           rc = mdfile(filespec, buffer);
 
-           headerFree(hdrs[i]);
+       if (rc) {
+           /* assume the file has been removed, don't freak */
+           return FA_CREATE;
+       }
+       dbAttr = dbMd5;
+       newAttr = newMd5;
+    } else /* dbWhat == LINK */ {
+       memset(buffer, 0, sizeof(buffer));
+       i = readlink(filespec, buffer, sizeof(buffer) - 1);
+       if (i == -1) {
+           /* assume the file has been removed, don't freak */
+           return FA_CREATE;
+       }
+       dbAttr = dbLink;
+       newAttr = newLink;
+     }
 
-           if (!alp->fd && fd)
-               notify(fi->h, RPMCALLBACK_INST_CLOSE_FILE, 0, 0, alp->key, 
-                  notifyData);
-       } else if (ts->order[oc].u.removed.dependsOnIndex != lastFailed) {
-           if (removeBinaryPackage(ts->root, ts->db, fi->record,
-                                   flags, fi->actions, ts->scriptFd))
+    /* this order matters - we'd prefer to CREATE the file if at all 
+       possible in case something else (like the timestamp) has changed */
 
-               ourrc++;
-       }
+    if (!strcmp(dbAttr, buffer)) {
+       /* this config file has never been modified, so 
+          just replace it */
+       return FA_CREATE;
     }
 
-    freeFl(ts, flList);
+    if (!strcmp(dbAttr, newAttr)) {
+       /* this file is the same in all versions of this package */
+       return FA_SKIP;
+    }
 
-    if (ourrc) 
-       return -1;
-    else
-       return 0;
+    /* the config file on the disk has been modified, but
+       the ones in the two packages are different. It would
+       be nice if RPM was smart enough to at least try and
+       merge the difference ala CVS, but... */
+           
+    return save;
 }
 
-void rpmtransSetScriptFd(rpmTransactionSet ts, FD_t fd) {
-    ts->scriptFd = fd;
-}
+static int filecmp(short mode1, char * md51, char * link1, 
+                  short mode2, char * md52, char * link2)
+{
+    enum fileTypes what1, what2;
 
-static rpmProblemSet psCreate(void) {
-    rpmProblemSet probs;
+    what1 = whatis(mode1);
+    what2 = whatis(mode2);
 
-    probs = malloc(sizeof(*probs));
-    probs->numProblems = probs->numProblemsAlloced = 0;
-    probs->probs = NULL;
+    if (what1 != what2) return 1;
 
-    return probs;
+    if (what1 == LINK)
+       return strcmp(link1, link2);
+    else if (what1 == REG)
+       return strcmp(md51, md52);
+
+    return 0;
 }
 
-static void psAppend(rpmProblemSet probs, rpmProblemType type, 
-                    const void * key, Header h, const char * str1, 
-                    Header altH, unsigned long ulong1) {
-    if (probs->numProblems == probs->numProblemsAlloced) {
-       if (probs->numProblemsAlloced)
-           probs->numProblemsAlloced *= 2;
-       else
-           probs->numProblemsAlloced = 2;
-       probs->probs = realloc(probs->probs, 
-                       probs->numProblemsAlloced * sizeof(*probs->probs));
-    }
+static int handleInstInstalledFiles(struct fileInfo * fi, rpmdb db,
+                                   struct sharedFileInfo * shared,
+                                   int sharedCount, int reportConflicts,
+                                   rpmProblemSet probs)
+{
+    Header h;
+    int i;
+    char ** otherMd5s, ** otherLinks;
+    char * otherStates;
+    uint_32 * otherFlags, * otherSizes;
+    uint_16 * otherModes;
+    int otherFileNum;
+    int fileNum;
+    int numReplaced = 0;
 
-    probs->probs[probs->numProblems].type = type;
-    probs->probs[probs->numProblems].key = key;
-    probs->probs[probs->numProblems].h = headerLink(h);
-    probs->probs[probs->numProblems].ulong1 = ulong1;
-    if (str1)
-       probs->probs[probs->numProblems].str1 = strdup(str1);
-    else
-       probs->probs[probs->numProblems].str1 = NULL;
+    if (!(h = rpmdbGetRecord(db, shared->otherPkg)))
+       return 1;
 
-    if (altH)
-       probs->probs[probs->numProblems].altH = headerLink(altH);
-    else
-       probs->probs[probs->numProblems].altH = NULL;
+    headerGetEntryMinMemory(h, RPMTAG_FILEMD5S, NULL,
+                           (void **) &otherMd5s, NULL);
+    headerGetEntryMinMemory(h, RPMTAG_FILELINKTOS, NULL,
+                           (void **) &otherLinks, NULL);
+    headerGetEntryMinMemory(h, RPMTAG_FILESTATES, NULL,
+                           (void **) &otherStates, NULL);
+    headerGetEntryMinMemory(h, RPMTAG_FILEMODES, NULL,
+                           (void **) &otherModes, NULL);
+    headerGetEntryMinMemory(h, RPMTAG_FILEFLAGS, NULL,
+                           (void **) &otherFlags, NULL);
+    headerGetEntryMinMemory(h, RPMTAG_FILESIZES, NULL,
+                           (void **) &otherSizes, NULL);
 
-    probs->probs[probs->numProblems++].ignoreProblem = 0;
-}
-
-static int archOkay(Header h) {
-    int_8 * pkgArchNum;
-    void * pkgArch;
-    int type, count, archNum;
-
-    /* make sure we're trying to install this on the proper architecture */
-    headerGetEntry(h, RPMTAG_ARCH, &type, (void **) &pkgArch, &count);
-    if (type == RPM_INT8_TYPE) {
-       /* old arch handling */
-       rpmGetArchInfo(NULL, &archNum);
-       pkgArchNum = pkgArch;
-       if (archNum != *pkgArchNum) {
-           return 0;
-       }
-    } else {
-       /* new arch handling */
-       if (!rpmMachineScore(RPM_MACHTABLE_INSTARCH, pkgArch)) {
-           return 0;
-       }
-    }
+    fi->replaced = malloc(sizeof(*fi->replaced) * sharedCount);
 
-    return 1;
-}
+    for (i = 0; i < sharedCount; i++, shared++) {
+       otherFileNum = shared->otherFileNum;
+       fileNum = shared->pkgFileNum;
+       if (otherStates[otherFileNum] == RPMFILE_STATE_NORMAL) {
+           if (filecmp(otherModes[otherFileNum],
+                       otherMd5s[otherFileNum],
+                       otherLinks[otherFileNum],
+                       fi->fmodes[fileNum],
+                       fi->fmd5s[fileNum],
+                       fi->flinks[fileNum])) {
+               if (reportConflicts)
+                   psAppend(probs, RPMPROB_FILE_CONFLICT, fi->ap->key, 
+                            fi->ap->h, fi->fl[fileNum], h,0 );
+               if (!(otherFlags[otherFileNum] | fi->fflags[fileNum])
+                           & RPMFILE_CONFIG) {
+                   if (!shared->isRemoved)
+                       fi->replaced[numReplaced++] = *shared;
+               }
+           }
 
-static int osOkay(Header h) {
-    void * pkgOs;
-    int type, count;
+           if ((otherFlags[otherFileNum] | fi->fflags[fileNum])
+                       & RPMFILE_CONFIG) {
+               fi->actions[fileNum] = decideFileFate(fi->fl[fileNum],
+                       otherModes[otherFileNum], 
+                       otherMd5s[otherFileNum],
+                       otherLinks[otherFileNum],
+                       fi->fmodes[fileNum],
+                       fi->fmd5s[fileNum],
+                       fi->flinks[fileNum],
+                       fi->fflags[fileNum], 
+                       !headerIsEntry(h, RPMTAG_RPMVERSION));
+           }
 
-    /* make sure we're trying to install this on the proper os */
-    headerGetEntry(h, RPMTAG_OS, &type, (void **) &pkgOs, &count);
-    if (type == RPM_INT8_TYPE) {
-       /* v1 packages and v2 packages both used improper OS numbers, so just
-          deal with it hope things work */
-       return 1;
-    } else {
-       /* new os handling */
-       if (!rpmMachineScore(RPM_MACHTABLE_INSTOS, pkgOs)) {
-           return 0;
+           fi->replacedSizes[fileNum] = otherSizes[otherFileNum];
        }
     }
 
-    return 1;
-}
+    free(otherMd5s);
+    free(otherLinks);
+    headerFree(h);
 
-void rpmProblemSetFree(rpmProblemSet probs) {
-    int i;
+    fi->replaced = realloc(fi->replaced,
+                          sizeof(*fi->replaced) * (numReplaced + 1));
+    fi->replaced[numReplaced].otherPkg = 0;
 
-    for (i = 0; i < probs->numProblems; i++) {
-       headerFree(probs->probs[i].h);
-       if (probs->probs[i].str1) free(probs->probs[i].str1);
-       if (probs->probs[i].altH) headerFree(probs->probs[i].altH);
-    }
-    free(probs);
+    return 0;
 }
 
-static Header relocateFileList(struct availablePackage * alp, 
-                              rpmProblemSet probs, Header origH,
-                              enum fileActions * actions,
-                              int allowBadRelocate) {
-    int numValid, numRelocations;
-    int i, j, madeSwap, rc;
-    rpmRelocation * nextReloc, * relocations = NULL;
-    rpmRelocation * rawRelocations = alp->relocs;
-    rpmRelocation tmpReloc;
-    char ** validRelocations, ** actualRelocations;
-    char ** names;
-    char ** origNames;
-    int len = 0;
-    char * newName;
-    int_32 fileCount;
+static int handleRmvdInstalledFiles(struct fileInfo * fi, rpmdb db,
+                                   struct sharedFileInfo * shared,
+                                   int sharedCount)
+{
     Header h;
-    int relocated = 0;
-
-    if (!headerGetEntry(origH, RPMTAG_PREFIXES, NULL,
-                       (void **) &validRelocations, &numValid))
-       numValid = 0;
+    int otherFileNum;
+    int fileNum;
+    char * otherStates;
+    int i;
+    
+    if (!(h = rpmdbGetRecord(db, shared->otherPkg)))
+       return 1;
 
-    if (!rawRelocations && !numValid) return headerLink(origH);
+    headerGetEntryMinMemory(h, RPMTAG_FILESTATES, NULL,
+                           (void **) &otherStates, NULL);
 
-    h = headerCopy(origH);
+    for (i = 0; i < sharedCount; i++, shared++) {
+       otherFileNum = shared->otherFileNum;
+       fileNum = shared->pkgFileNum;
 
-    if (rawRelocations) {
-       for (i = 0; rawRelocations[i].newPath || rawRelocations[i].oldPath; 
-               i++) ;
-       numRelocations = i;
-    } else {
-       numRelocations = 0;
+       if (otherStates[otherFileNum] == RPMFILE_STATE_NORMAL)
+           fi->actions[fileNum] = FA_SKIP;
     }
-    relocations = alloca(sizeof(*relocations) * numRelocations);
 
-    for (i = 0; i < numRelocations; i++) {
-       /* FIXME: default relocations (oldPath == NULL) need to be handled
-          in the UI, not rpmlib */
+    headerFree(h);
 
-       relocations[i].oldPath = 
-           alloca(strlen(rawRelocations[i].oldPath) + 1);
-       strcpy(relocations[i].oldPath, rawRelocations[i].oldPath);
-       stripTrailingSlashes(relocations[i].oldPath);
+    return 0;
+}
 
-       if (rawRelocations[i].newPath) {
-           relocations[i].newPath = 
-               alloca(strlen(rawRelocations[i].newPath) + 1);
-           strcpy(relocations[i].newPath, rawRelocations[i].newPath);
-           stripTrailingSlashes(relocations[i].newPath);
-       } else {
-           relocations[i].newPath = NULL;
-       }
+static void handleOverlappedFiles(struct fileInfo * fi, hashTable ht,
+                          rpmProblemSet probs, struct diskspaceInfo * dsl)
+{
+    int i, j;
+    struct fileInfo ** recs;
+    int numRecs;
+    int otherPkgNum, otherFileNum;
+    struct stat sb;
+    char mdsum[50];
+    int rc;
+    struct diskspaceInfo * ds = NULL;
+    uint_32 fixupSize = 0;
+   
+    for (i = 0; i < fi->fc; i++) {
+       if (fi->actions[i] == FA_SKIP || fi->actions[i] == FA_SKIPNSTATE)
+           continue;
 
-       if (relocations[i].newPath) {
-           for (j = 0; j < numValid; j++) 
-               if (!strcmp(validRelocations[j],
-                           relocations[i].oldPath)) break;
-           if (j == numValid && !allowBadRelocate) 
-               psAppend(probs, RPMPROB_BADRELOCATE, alp->key, alp->h, 
-                        relocations[i].oldPath, NULL,0 );
-       }
-    }
+       if (dsl) {
+           ds = dsl;
+           while (ds->block && ds->dev != fi->fps[i].dev) ds++;
+           if (!ds->block) ds = NULL;
+           fixupSize = 0;
+       } 
 
-    /* stupid bubble sort, but it's probably faster here */
-    for (i = 0; i < numRelocations; i++) {
-       madeSwap = 0;
-       for (j = 1; j < numRelocations; j++) {
-           if (strcmp(relocations[j - 1].oldPath, 
-                      relocations[j].oldPath) > 0) {
-               tmpReloc = relocations[j - 1];
-               relocations[j - 1] = relocations[j];
-               relocations[j] = tmpReloc;
-               madeSwap = 1;
-           }
-       }
-       if (!madeSwap) break;
-    }
+       htGetEntry(ht, &fi->fps[i], (void ***) &recs, &numRecs, NULL);
 
-    if (numValid) {
-       actualRelocations = malloc(sizeof(*actualRelocations) * numValid);
-       for (i = 0; i < numValid; i++) {
-           for (j = 0; j < numRelocations; j++) {
-               if (!strcmp(validRelocations[i], relocations[j].oldPath)) {
-                   actualRelocations[i] = relocations[j].newPath;
+       /* We need to figure out the current fate of this file. So,
+          work backwards from this file and look for a final action
+          we can work against. */
+       for (j = 0; recs[j] != fi; j++);
+
+       otherPkgNum = j - 1;
+       otherFileNum = -1;                      /* keep gcc quiet */
+       while (otherPkgNum >= 0) {
+           if (recs[otherPkgNum]->type == TR_ADDED) {
+               /* TESTME: there are more efficient searches in the world... */
+               for (otherFileNum = 0; otherFileNum < recs[otherPkgNum]->fc; 
+                    otherFileNum++)
+                   if (FP_EQUAL(fi->fps[i], 
+                                recs[otherPkgNum]->fps[otherFileNum])) 
+                       break;
+               if ((otherFileNum >= 0) && 
+                   (recs[otherPkgNum]->actions[otherFileNum] != FA_UNKNOWN))
                    break;
-               }
            }
-
-           if (j == numRelocations)
-               actualRelocations[i] = validRelocations[i];
+           otherPkgNum--;
        }
 
-       headerAddEntry(h, RPMTAG_INSTPREFIXES, RPM_STRING_ARRAY_TYPE,
-                      (void **) actualRelocations, numValid);
-
-       free(actualRelocations);
-       free(validRelocations);
-    }
+       if (fi->type == TR_ADDED && otherPkgNum < 0) {
+           if (fi->actions[i] == FA_UNKNOWN) {
+               if ((fi->fflags[i] & RPMFILE_CONFIG) && 
+                           !lstat(fi->fl[i], &sb)) {
+                   fi->actions[i] = (fi->fflags[i] & RPMFILE_NOREPLACE)
+                       ? FA_ALTNAME : FA_BACKUP;
+               } else {
+                   fi->actions[i] = FA_CREATE;
+               }
+           }
+       } else if (fi->type == TR_ADDED) {
+           if (probs && filecmp(recs[otherPkgNum]->fmodes[otherFileNum],
+                       recs[otherPkgNum]->fmd5s[otherFileNum],
+                       recs[otherPkgNum]->flinks[otherFileNum],
+                       fi->fmodes[i],
+                       fi->fmd5s[i],
+                       fi->flinks[i])) {
+               psAppend(probs, RPMPROB_NEW_FILE_CONFLICT, fi->ap->key, 
+                        fi->ap->h, fi->fl[i], recs[otherPkgNum]->ap->h, 0);
+           }
 
-    headerGetEntry(h, RPMTAG_FILENAMES, NULL, (void **) &names, 
-                  &fileCount);
+           fixupSize = recs[otherPkgNum]->fsizes[otherFileNum];
 
-    /* go through things backwards so that /usr/local relocations take
-       precedence over /usr ones */
-    for (i = fileCount - 1; i >= 0; i--) {
-       for (j = numRelocations - 1; j >= 0; j--) {
-           nextReloc = relocations + j;
-           len = strlen(relocations[j].oldPath);
-           rc = (!strncmp(relocations[j].oldPath, names[i], len) && 
-                   ((names[i][len] == '/') || !names[i][len]));
-           if (rc) break;
-       }
-
-       if (j >= 0) {
-           nextReloc = relocations + j;
-           if (nextReloc->newPath) {
-               newName = alloca(strlen(nextReloc->newPath) + 
-                                strlen(names[i]) + 1);
-               strcpy(newName, nextReloc->newPath);
-               strcat(newName, names[i] + len);
-               rpmMessage(RPMMESS_DEBUG, _("relocating %s to %s\n"),
-                          names[i], newName);
-               names[i] = newName;
-               relocated = 1;
-           } else if (actions) {
-               actions[i] = FA_SKIPNSTATE;
-               rpmMessage(RPMMESS_DEBUG, _("excluding %s\n"), names[i]);
+           /* FIXME: is this right??? it locks us into the config
+              file handling choice we already made, which may very
+              well be exactly right. What about noreplace files?? */
+           fi->actions[i] = FA_CREATE;
+       } else if (fi->type == TR_REMOVED && otherPkgNum >= 0) {
+           fi->actions[i] = FA_SKIP;
+       } else if (fi->type == TR_REMOVED) {
+           if (fi->actions[i] != FA_SKIP && fi->actions[i] != FA_SKIPNSTATE &&
+                       fi->fstates[i] == RPMFILE_STATE_NORMAL ) {
+               if (S_ISREG(fi->fmodes[i]) && 
+                           (fi->fflags[i] & RPMFILE_CONFIG)) {
+                   rc = mdfile(fi->fl[i], mdsum);
+                   if (!rc && strcmp(fi->fmd5s[i], mdsum)) {
+                       fi->actions[i] = FA_BACKUP;
+                   } else {
+                       /* FIXME: config files may need to be saved */
+                       fi->actions[i] = FA_REMOVE;
+                   }
+               } else {
+                   fi->actions[i] = FA_REMOVE;
+               }
            }
-       } 
-    }
-
-    if (relocated) {
-       headerGetEntry(h, RPMTAG_FILENAMES, NULL, (void **) &origNames, NULL);
-       headerAddEntry(h, RPMTAG_ORIGFILENAMES, RPM_STRING_ARRAY_TYPE, 
-                         origNames, fileCount);
-       free(origNames);
-       headerModifyEntry(h, RPMTAG_FILENAMES, RPM_STRING_ARRAY_TYPE, 
-                         names, fileCount);
-    }
+       }
 
-    free(names);
+       if (ds) {
+           uint_32 s = BLOCK_ROUND(fi->fsizes[i], ds->block);
 
-    return h;
-}
+           switch (fi->actions[i]) {
+             case FA_BACKUP:
+             case FA_SAVE:
+             case FA_ALTNAME:
+               ds->needed += s;
+               break;
 
-static int psTrim(rpmProblemSet filter, rpmProblemSet target) {
-    /* As the problem sets are generated in an order solely dependent
-       on the ordering of the packages in the transaction, and that
-       ordering can't be changed, the problem sets must be parallel to
-       on another. Additionally, the filter set must be a subset of the
-       target set, given the operations available on transaction set.
-       This is good, as it lets us perform this trim in linear time, rather
-       then logarithmic or quadratic. */
-    rpmProblem * f, * t;
-    int gotProblems = 0;
+             /* FIXME: If a two packages share a file (same md5sum), and 
+                that file is being replaced on disk, will ds->needed get
+                decremented twice? Quite probably! */
+             case FA_CREATE:
+               ds->needed += s;
+               ds->needed -= BLOCK_ROUND(fi->replacedSizes[i], ds->block);
+               break;
 
-    f = filter->probs;
-    t = target->probs;
+             case FA_REMOVE:
+               ds->needed -= s;
+               break;
 
-    while ((f - filter->probs) < filter->numProblems) {
-       if (!f->ignoreProblem) {
-           f++;
-           continue;
-       }
-       while ((t - target->probs) < target->numProblems) {
-           if (f->h == t->h && f->type == t->type && t->key == f->key &&
-                    XSTRCMP(f->str1, t->str1))
+             default:
                break;
-           t++;
-           gotProblems = 1;
-       }
+           }
 
-       if ((t - target->probs) == target->numProblems) {
-           /* this can't happen ;-) lets be sane if it doesn though */
-           break;
+           ds->needed -= BLOCK_ROUND(fixupSize, ds->block);
        }
-
-       t->ignoreProblem = f->ignoreProblem;
-       t++, f++;
     }
+}
 
-    if ((t - target->probs) < target->numProblems)
-       gotProblems = 1;
+static int ensureOlder(rpmdb db, Header new, int dbOffset, rpmProblemSet probs,
+                      const void * key)
+{
+    Header old;
+    int result, rc = 0;
 
-    return gotProblems;
-}
+    old = rpmdbGetRecord(db, dbOffset);
+    if (old == NULL) return 1;
 
-static int sharedCmp(const void * one, const void * two) {
-    const struct sharedFileInfo * a = one;
-    const struct sharedFileInfo * b = two;
+    result = rpmVersionCompare(old, new);
+    if (result <= 0)
+       rc = 0;
+    else if (result > 0) {
+       rc = 1;
+       psAppend(probs, RPMPROB_OLDPACKAGE, key, new, NULL, old, 0);
+    }
 
-    if (a->otherPkg < b->otherPkg)
-       return -1;
-    else if (a->otherPkg > b->otherPkg)
-       return 1;
+    headerFree(old);
 
-    return 0;
+    return rc;
 }
 
-static enum fileActions decideFileFate(const char * filespec, short dbMode, 
-                               char * dbMd5, char * dbLink, short newMode, 
-                               char * newMd5, char * newLink, int newFlags,
-                               int brokenMd5) {
-    char buffer[1024];
-    char * dbAttr, * newAttr;
-    enum fileTypes dbWhat, newWhat, diskWhat;
-    struct stat sb;
-    int i, rc;
-    int save = (newFlags & RPMFILE_NOREPLACE) ? FA_ALTNAME : FA_SAVE;
+static void skipFiles(struct fileInfo * fi, int noDocs)
+{
+    int i, j;
+    char ** netsharedPaths = NULL, ** nsp;
+    char ** fileLangs, ** languages, ** lang;
+    char * oneLang[2] = { NULL, NULL };
+    int freeLanguages = 0;
+    char * chptr;
 
-    if (lstat(filespec, &sb)) {
-       /* the file doesn't exist on the disk create it unless the new
-          package has marked it as missingok */
-       if (newFlags & RPMFILE_MISSINGOK) {
-           rpmMessage(RPMMESS_DEBUG, _("%s skipped due to missingok flag\n"),
-                       filespec);
-           return FA_SKIP;
-       } else
-           return FA_CREATE;
-    }
+    if (!noDocs)
+       noDocs = rpmExpandNumeric("%{_excludedocs}");
 
-    diskWhat = whatis(sb.st_mode);
-    dbWhat = whatis(dbMode);
-    newWhat = whatis(newMode);
+    {  const char *tmpPath = rpmExpand("%{_netsharedpath}", NULL);
+       if (tmpPath && *tmpPath != '%')
+           netsharedPaths = splitString(tmpPath, strlen(tmpPath), ':');
+       xfree(tmpPath);
+    }
 
-    /* RPM >= 2.3.10 shouldn't create config directories -- we'll ignore
-       them in older packages as well */
-    if (newWhat == XDIR)
-       return FA_CREATE;
+    if (!headerGetEntry(fi->h, RPMTAG_FILELANGS, NULL, (void **) &fileLangs, 
+                       NULL))
+       fileLangs = NULL;
 
-    if (diskWhat != newWhat) {
-       return save;
-    } else if (newWhat != dbWhat && diskWhat != dbWhat) {
-       return save;
-    } else if (dbWhat != newWhat) {
-       return FA_CREATE;
-    } else if (dbWhat != LINK && dbWhat != REG) {
-       return FA_CREATE;
+    if ((chptr = getenv("LINGUAS"))) {
+       languages = splitString(chptr, strlen(chptr), ':');
+       freeLanguages = 1;
+    } else if ((oneLang[0] = getenv("LANG"))) {
+       languages = oneLang;
+    } else {
+       oneLang[0] = "en";
+       languages = oneLang;
     }
 
-    if (dbWhat == REG) {
-       if (brokenMd5)
-           rc = mdfileBroken(filespec, buffer);
-       else
-           rc = mdfile(filespec, buffer);
+    for (i = 0; i < fi->fc; i++) {
+       if (fi->actions[i] == FA_SKIP || fi->actions[i] == FA_SKIPNSTATE)
+           continue;
 
-       if (rc) {
-           /* assume the file has been removed, don't freak */
-           return FA_CREATE;
+       /* netsharedPaths are not relative to the current root (though 
+          they do need to take package relocations into account) */
+       for (nsp = netsharedPaths; nsp && *nsp; nsp++) {
+           j = strlen(*nsp);
+           if (!strncmp(fi->fl[i], *nsp, j) &&
+               (fi->fl[i][j] == '\0' ||
+                fi->fl[i][j] == '/'))
+               break;
        }
-       dbAttr = dbMd5;
-       newAttr = newMd5;
-    } else /* dbWhat == LINK */ {
-       memset(buffer, 0, sizeof(buffer));
-       i = readlink(filespec, buffer, sizeof(buffer) - 1);
-       if (i == -1) {
-           /* assume the file has been removed, don't freak */
-           return FA_CREATE;
+
+       if (nsp && *nsp) {
+           fi->actions[i] = FA_SKIPNSTATE;
+           continue;
        }
-       dbAttr = dbLink;
-       newAttr = newLink;
-     }
 
-    /* this order matters - we'd prefer to CREATE the file if at all 
-       possible in case something else (like the timestamp) has changed */
+       if (fileLangs && languages && *fileLangs[i]) {
+           for (lang = languages; *lang; lang++) {
+               const char *l, *le;
+               for (l = fileLangs[i]; *l; l = le) {
+                   for (le = l; *le && *le != '|'; le++)
+                       ;
+                   if ((le-l) > 0 && !strncmp(*lang, l, (le-l)))
+                       goto lingo;
+                   if (*le == '|') le++;       /* skip over | */
+               }
+           }
+       lingo:
+           if (!*lang) {
+               fi->actions[i] = FA_SKIPNSTATE;
+               continue;
+           }
+       }
 
-    if (!strcmp(dbAttr, buffer)) {
-       /* this config file has never been modified, so 
-          just replace it */
-       return FA_CREATE;
+       if (noDocs && (fi->fflags[i] & RPMFILE_DOC))
+           fi->actions[i] = FA_SKIPNSTATE;
     }
 
-    if (!strcmp(dbAttr, newAttr)) {
-       /* this file is the same in all versions of this package */
-       return FA_SKIP;
-    }
+    if (netsharedPaths) freeSplitString(netsharedPaths);
+    if (fileLangs) free(fileLangs);
+    if (freeLanguages) freeSplitString(languages);
+}
 
-    /* the config file on the disk has been modified, but
-       the ones in the two packages are different. It would
-       be nice if RPM was smart enough to at least try and
-       merge the difference ala CVS, but... */
-           
-    return save;
-}
+#define        NOTIFY(_x)      if (notify) notify _x
 
-enum fileTypes whatis(short mode) {
-    enum fileTypes result;
+/* Return -1 on error, > 0 if newProbs is set, 0 if everything happened */
 
-    if (S_ISDIR(mode))
-       result = XDIR;
-    else if (S_ISCHR(mode))
-       result = CDEV;
-    else if (S_ISBLK(mode))
-       result = BDEV;
-    else if (S_ISLNK(mode))
-       result = LINK;
-    else if (S_ISSOCK(mode))
-       result = SOCK;
-    else if (S_ISFIFO(mode))
-       result = PIPE;
-    else
-       result = REG;
-    return result;
-}
+int rpmRunTransactions(rpmTransactionSet ts, rpmCallbackFunction notify,
+                      void * notifyData, rpmProblemSet okProbs,
+                      rpmProblemSet * newProbs, int flags, int ignoreSet)
+{
+    int i, j;
+    int rc, ourrc = 0;
+    struct availablePackage * alp;
+    rpmProblemSet probs;
+    dbiIndexSet dbi, * matches;
+    Header * hdrs;
+    int fileCount;
+    int totalFileCount = 0;
+    hashTable ht;
+    struct fileInfo * flList, * fi;
+    struct sharedFileInfo * shared, * sharedList;
+    int numShared;
+    int flEntries;
+    int last;
+    int lastFailed;
+    int beingRemoved;
+    char * currDir, * chptr;
+    FD_t fd;
+    const char ** filesystems;
+    int filesystemCount;
+    struct diskspaceInfo * di = NULL;
+    int oc;
 
-static int filecmp(short mode1, char * md51, char * link1, 
-                  short mode2, char * md52, char * link2) {
-    enum fileTypes what1, what2;
+    /* FIXME: what if the same package is included in ts twice? */
 
-    what1 = whatis(mode1);
-    what2 = whatis(mode2);
+    if (!(ignoreSet & RPMPROB_FILTER_DISKSPACE) &&
+               !rpmGetFilesystemList(&filesystems, &filesystemCount)) {
+       struct stat sb;
 
-    if (what1 != what2) return 1;
+       di = alloca(sizeof(*di) * (filesystemCount + 1));
 
-    if (what1 == LINK)
-       return strcmp(link1, link2);
-    else if (what1 == REG)
-       return strcmp(md51, md52);
+       for (i = 0; (i < filesystemCount) && di; i++) {
+#if STATFS_IN_SYS_STATVFS
+           struct statvfs sfb;
+           if (statvfs(filesystems[i], &sfb))
+#else
+           struct statfs sfb;
+#  if STAT_STATFS4
+/* this platform has the 4-argument version of the statfs call.  The last two
+ * should be the size of struct statfs and 0, respectively.  The 0 is the
+ * filesystem type, and is always 0 when statfs is called on a mounted
+ * filesystem, as we're doing.
+ */
+           if (statfs(filesystems[i], &sfb, sizeof(sfb), 0))
+#  else
+           if (statfs(filesystems[i], &sfb))
+#  endif
+#endif
+           {
+               di = NULL;
+           } else {
+               di[i].block = sfb.f_bsize;
+               di[i].needed = 0;
+#ifdef STATFS_HAS_F_BAVAIL
+               di[i].avail = sfb.f_bavail;
+#else
+/* FIXME: the statfs struct doesn't have a member to tell how many blocks are
+ * available for non-superusers.  f_blocks - f_bfree is probably too big, but
+ * it's about all we can do.
+ */
+               di[i].avail = sfb.f_blocks - sfb.f_bfree;
+#endif
 
-    return 0;
-}
 
-static int handleInstInstalledFiles(struct fileInfo * fi, rpmdb db,
-                                   struct sharedFileInfo * shared,
-                                   int sharedCount, int reportConflicts,
-                                   rpmProblemSet probs) {
-    Header h;
-    int i;
-    char ** otherMd5s, ** otherLinks;
-    char * otherStates;
-    uint_32 * otherFlags, * otherSizes;
-    uint_16 * otherModes;
-    int otherFileNum;
-    int fileNum;
-    int numReplaced = 0;
+               stat(filesystems[i], &sb);
+               di[i].dev = sb.st_dev;
+           }
+       }
 
-    if (!(h = rpmdbGetRecord(db, shared->otherPkg)))
-       return 1;
+       if (di) di[i].block = 0;
+    }
 
-    headerGetEntryMinMemory(h, RPMTAG_FILEMD5S, NULL,
-                           (void **) &otherMd5s, NULL);
-    headerGetEntryMinMemory(h, RPMTAG_FILELINKTOS, NULL,
-                           (void **) &otherLinks, NULL);
-    headerGetEntryMinMemory(h, RPMTAG_FILESTATES, NULL,
-                           (void **) &otherStates, NULL);
-    headerGetEntryMinMemory(h, RPMTAG_FILEMODES, NULL,
-                           (void **) &otherModes, NULL);
-    headerGetEntryMinMemory(h, RPMTAG_FILEFLAGS, NULL,
-                           (void **) &otherFlags, NULL);
-    headerGetEntryMinMemory(h, RPMTAG_FILESIZES, NULL,
-                           (void **) &otherSizes, NULL);
+    probs = psCreate();
+    *newProbs = probs;
+    hdrs = alloca(sizeof(*hdrs) * ts->addedPackages.size);
 
-    fi->replaced = malloc(sizeof(*fi->replaced) * sharedCount);
+    /* The ordering doesn't matter here */
+    for (alp = ts->addedPackages.list; (alp - ts->addedPackages.list) < 
+           ts->addedPackages.size; alp++) {
+       if (!archOkay(alp->h) && !(ignoreSet & RPMPROB_FILTER_IGNOREARCH))
+           psAppend(probs, RPMPROB_BADARCH, alp->key, alp->h, NULL, NULL, 0);
 
-    for (i = 0; i < sharedCount; i++, shared++) {
-       otherFileNum = shared->otherFileNum;
-       fileNum = shared->pkgFileNum;
-       if (otherStates[otherFileNum] == RPMFILE_STATE_NORMAL) {
-           if (filecmp(otherModes[otherFileNum],
-                       otherMd5s[otherFileNum],
-                       otherLinks[otherFileNum],
-                       fi->fmodes[fileNum],
-                       fi->fmd5s[fileNum],
-                       fi->flinks[fileNum])) {
-               if (reportConflicts)
-                   psAppend(probs, RPMPROB_FILE_CONFLICT, fi->ap->key, 
-                            fi->ap->h, fi->fl[fileNum], h,0 );
-               if (!(otherFlags[otherFileNum] | fi->fflags[fileNum])
-                           & RPMFILE_CONFIG) {
-                   if (!shared->isRemoved)
-                       fi->replaced[numReplaced++] = *shared;
-               }
-           }
+       if (!osOkay(alp->h) && !(ignoreSet & RPMPROB_FILTER_IGNOREOS)) {
+           psAppend(probs, RPMPROB_BADOS, alp->key, alp->h, NULL, NULL, 0);
+       }
 
-           if ((otherFlags[otherFileNum] | fi->fflags[fileNum])
-                       & RPMFILE_CONFIG) {
-               fi->actions[fileNum] = decideFileFate(fi->fl[fileNum],
-                       otherModes[otherFileNum], 
-                       otherMd5s[otherFileNum],
-                       otherLinks[otherFileNum],
-                       fi->fmodes[fileNum],
-                       fi->fmd5s[fileNum],
-                       fi->flinks[fileNum],
-                       fi->fflags[fileNum], 
-                       !headerIsEntry(h, RPMTAG_RPMVERSION));
+       if (!(ignoreSet & RPMPROB_FILTER_OLDPACKAGE)) {
+           rc = rpmdbFindPackage(ts->db, alp->name, &dbi);
+           if (rc == 2) {
+               return -1;
+           } else if (!rc) {
+               for (i = 0; i < dbi.count; i++) 
+                   ensureOlder(ts->db, alp->h, dbi.recs[i].recOffset, 
+                                     probs, alp->key);
+
+               dbiFreeIndexRecord(dbi);
            }
+       }
 
-           fi->replacedSizes[fileNum] = otherSizes[otherFileNum];
+       rc = findMatches(ts->db, alp->name, alp->version, alp->release, &dbi);
+       if (rc == 2) {
+           return -1;
+       } else if (!rc) {
+           if (!(ignoreSet & RPMPROB_FILTER_REPLACEPKG))
+               psAppend(probs, RPMPROB_PKG_INSTALLED, alp->key, alp->h, NULL, 
+                        NULL, 0);
+           dbiFreeIndexRecord(dbi);
        }
-    }
 
-    free(otherMd5s);
-    free(otherLinks);
-    headerFree(h);
+       if (headerGetEntry(alp->h, RPMTAG_FILENAMES, NULL, NULL, &fileCount))
+           totalFileCount += fileCount;
+    }
 
-    fi->replaced = realloc(fi->replaced,
-                          sizeof(*fi->replaced) * (numReplaced + 1));
-    fi->replaced[numReplaced].otherPkg = 0;
+    /* FIXME: it seems a bit silly to read in all of these headers twice */
+    /* The ordering doesn't matter here */
+    for (i = 0; i < ts->numRemovedPackages; i++) {
+       Header h;
 
-    return 0;
-}
+       if ((h = rpmdbGetRecord(ts->db, ts->removedPackages[i]))) {
+           if (headerGetEntry(h, RPMTAG_FILENAMES, NULL, NULL, 
+                              &fileCount))
+               totalFileCount += fileCount;
+           headerFree(h);      /* XXX ==> LEAK */
+       }
+    }
 
-static int handleRmvdInstalledFiles(struct fileInfo * fi, rpmdb db,
-                                   struct sharedFileInfo * shared,
-                                   int sharedCount) {
-    Header h;
-    int otherFileNum;
-    int fileNum;
-    char * otherStates;
-    int i;
-    
-    if (!(h = rpmdbGetRecord(db, shared->otherPkg)))
-       return 1;
+    flEntries = ts->addedPackages.size + ts->numRemovedPackages;
+    flList = alloca(sizeof(*flList) * (flEntries));
 
-    headerGetEntryMinMemory(h, RPMTAG_FILESTATES, NULL,
-                           (void **) &otherStates, NULL);
+    ht = htCreate(totalFileCount * 2, 0, fpHashFunction, fpEqual);
 
-    for (i = 0; i < sharedCount; i++, shared++) {
-       otherFileNum = shared->otherFileNum;
-       fileNum = shared->pkgFileNum;
+    /* FIXME?: we'd be better off assembling one very large file list and
+       calling fpLookupList only once. I'm not sure that the speedup is
+       worth the trouble though. */
+    for (fi = flList, oc = 0; oc < ts->orderCount; fi++, oc++) {
+       memset(fi, 0, sizeof(*fi));
 
-       if (otherStates[otherFileNum] == RPMFILE_STATE_NORMAL)
-           fi->actions[fileNum] = FA_SKIP;
-    }
+       if (ts->order[oc].type == TR_ADDED) {
+           i = ts->order[oc].u.addedIndex;
+           alp = ts->addedPackages.list + ts->order[oc].u.addedIndex;
 
-    headerFree(h);
+           if (!headerGetEntryMinMemory(alp->h, RPMTAG_FILENAMES, NULL, 
+                                        NULL, &fi->fc)) {
+               fi->h = headerLink(alp->h);
+               hdrs[i] = headerLink(fi->h);
+               continue;
+           }
 
-    return 0;
-}
+           fi->actions = calloc(sizeof(*fi->actions), fi->fc);
+           hdrs[i] = relocateFileList(alp, probs, alp->h, fi->actions, 
+                                 ignoreSet & RPMPROB_FILTER_FORCERELOCATE);
+           fi->h = headerLink(hdrs[i]);
+           fi->type = TR_ADDED;
+           fi->ap = alp;
+       } else {
+           fi->record = ts->order[oc].u.removed.dboffset;
+           fi->h = rpmdbGetRecord(ts->db, fi->record);
+           if (!fi->h) {
+               /* ACK! */
+               continue;
+           }
+           fi->type = TR_REMOVED;
+       }
 
-void handleOverlappedFiles(struct fileInfo * fi, hashTable ht,
-                          rpmProblemSet probs, struct diskspaceInfo * dsl) {
-    int i, j;
-    struct fileInfo ** recs;
-    int numRecs;
-    int otherPkgNum, otherFileNum;
-    struct stat sb;
-    char mdsum[50];
-    int rc;
-    struct diskspaceInfo * ds = NULL;
-    uint_32 fixupSize = 0;
-   
-    for (i = 0; i < fi->fc; i++) {
-       if (fi->actions[i] == FA_SKIP || fi->actions[i] == FA_SKIPNSTATE)
+       if (!headerGetEntry(fi->h, RPMTAG_FILENAMES, NULL, 
+                                    (void **) &fi->fl, &fi->fc)) {
+           /* This catches removed packages w/ no file lists */
+           fi->fc = 0;
            continue;
+       }
 
-       if (dsl) {
-           ds = dsl;
-           while (ds->block && ds->dev != fi->fps[i].dev) ds++;
-           if (!ds->block) ds = NULL;
-           fixupSize = 0;
-       } 
+       /* actions is initialized earlier for added packages */
+       if (!fi->actions) 
+           fi->actions = calloc(sizeof(*fi->actions), fi->fc);
 
-       htGetEntry(ht, &fi->fps[i], (void ***) &recs, &numRecs, NULL);
+       headerGetEntry(fi->h, RPMTAG_FILEMODES, NULL, 
+                               (void **) &fi->fmodes, NULL);
+       headerGetEntry(fi->h, RPMTAG_FILEFLAGS, NULL, 
+                               (void **) &fi->fflags, NULL);
+       headerGetEntry(fi->h, RPMTAG_FILESIZES, NULL, 
+                               (void **) &fi->fsizes, NULL);
+       headerGetEntry(fi->h, RPMTAG_FILESTATES, NULL, 
+                               (void **) &fi->fstates, NULL);
 
-       /* We need to figure out the current fate of this file. So,
-          work backwards from this file and look for a final action
-          we can work against. */
-       for (j = 0; recs[j] != fi; j++);
+       if (ts->order[oc].type == TR_REMOVED) {
+           headerGetEntry(fi->h, RPMTAG_FILEMD5S, NULL, 
+                                   (void **) &fi->fmd5s, NULL);
+           headerGetEntry(fi->h, RPMTAG_FILELINKTOS, NULL, 
+                                   (void **) &fi->flinks, NULL);
+           fi->fsizes = memcpy(malloc(fi->fc * sizeof(*fi->fsizes)),
+                               fi->fsizes, fi->fc * sizeof(*fi->fsizes));
+           fi->fflags = memcpy(malloc(fi->fc * sizeof(*fi->fflags)),
+                               fi->fflags, fi->fc * sizeof(*fi->fflags));
+           fi->fmodes = memcpy(malloc(fi->fc * sizeof(*fi->fmodes)),
+                               fi->fmodes, fi->fc * sizeof(*fi->fmodes));
+           fi->fstates = memcpy(malloc(fi->fc * sizeof(*fi->fstates)),
+                               fi->fstates, fi->fc * sizeof(*fi->fstates));
+           headerFree(fi->h);
+           fi->h = NULL;
+       } else {
+           /* ADDED package */
 
-       otherPkgNum = j - 1;
-       otherFileNum = -1;                      /* keep gcc quiet */
-       while (otherPkgNum >= 0) {
-           if (recs[otherPkgNum]->type == TR_ADDED) {
-               /* TESTME: there are more efficient searches in the world... */
-               for (otherFileNum = 0; otherFileNum < recs[otherPkgNum]->fc; 
-                    otherFileNum++)
-                   if (FP_EQUAL(fi->fps[i], 
-                                recs[otherPkgNum]->fps[otherFileNum])) 
-                       break;
-               if ((otherFileNum >= 0) && 
-                   (recs[otherPkgNum]->actions[otherFileNum] != FA_UNKNOWN))
-                   break;
-           }
-           otherPkgNum--;
+           headerGetEntryMinMemory(fi->h, RPMTAG_FILEMD5S, NULL, 
+                                   (void **) &fi->fmd5s, NULL);
+           headerGetEntryMinMemory(fi->h, RPMTAG_FILELINKTOS, NULL, 
+                                   (void **) &fi->flinks, NULL);
+
+           /* 0 makes for noops */
+           fi->replacedSizes = calloc(fi->fc, sizeof(*fi->replacedSizes));
+           skipFiles(fi, flags & RPMTRANS_FLAG_NODOCS);
        }
 
-       if (fi->type == TR_ADDED && otherPkgNum < 0) {
-           if (fi->actions[i] == FA_UNKNOWN) {
-               if ((fi->fflags[i] & RPMFILE_CONFIG) && 
-                           !lstat(fi->fl[i], &sb)) {
-                   fi->actions[i] = (fi->fflags[i] & RPMFILE_NOREPLACE)
-                       ? FA_ALTNAME : FA_BACKUP;
-               } else {
-                   fi->actions[i] = FA_CREATE;
-               }
-           }
-       } else if (fi->type == TR_ADDED) {
-           if (probs && filecmp(recs[otherPkgNum]->fmodes[otherFileNum],
-                       recs[otherPkgNum]->fmd5s[otherFileNum],
-                       recs[otherPkgNum]->flinks[otherFileNum],
-                       fi->fmodes[i],
-                       fi->fmd5s[i],
-                       fi->flinks[i])) {
-               psAppend(probs, RPMPROB_NEW_FILE_CONFLICT, fi->ap->key, 
-                        fi->ap->h, fi->fl[i], recs[otherPkgNum]->ap->h, 0);
-           }
+        fi->fps = malloc(fi->fc * sizeof(*fi->fps));
+    }
 
-           fixupSize = recs[otherPkgNum]->fsizes[otherFileNum];
+    chptr = currentDirectory();
+    currDir = alloca(strlen(chptr) + 1);
+    strcpy(currDir, chptr);
+    free(chptr);
+    chdir("/");
+    chroot(ts->root);
+
+    for (fi = flList; (fi - flList) < flEntries; fi++) {
+       fpLookupList(fi->fl, fi->fps, fi->fc, 1);
+       for (i = 0; i < fi->fc; i++) {
+           if (fi->actions[i] != FA_SKIP && fi->actions[i] != FA_SKIPNSTATE)
+               htAddEntry(ht, fi->fps + i, fi);
+       }
+    }
+
+    NOTIFY((NULL, RPMCALLBACK_TRANS_START, 6, flEntries, NULL, notifyData));
+
+    for (fi = flList; (fi - flList) < flEntries; fi++) {
+       int k, ro;
+       int knownBad;
+
+       NOTIFY((NULL, RPMCALLBACK_TRANS_PROGRESS, (fi - flList), flEntries,
+              NULL, notifyData));
+
+       matches = malloc(sizeof(*matches) * fi->fc);
+       if (rpmdbFindFpList(ts->db, fi->fps, matches, fi->fc)) return 1;
+
+       numShared = 0;
+       for (i = 0; i < fi->fc; i++)
+           numShared += matches[i].count;
 
-           /* FIXME: is this right??? it locks us into the config
-              file handling choice we already made, which may very
-              well be exactly right. What about noreplace files?? */
-           fi->actions[i] = FA_CREATE;
-       } else if (fi->type == TR_REMOVED && otherPkgNum >= 0) {
-           fi->actions[i] = FA_SKIP;
-       } else if (fi->type == TR_REMOVED) {
-           if (fi->actions[i] != FA_SKIP && fi->actions[i] != FA_SKIPNSTATE &&
-                       fi->fstates[i] == RPMFILE_STATE_NORMAL ) {
-               if (S_ISREG(fi->fmodes[i]) && 
-                           (fi->fflags[i] & RPMFILE_CONFIG)) {
-                   rc = mdfile(fi->fl[i], mdsum);
-                   if (!rc && strcmp(fi->fmd5s[i], mdsum)) {
-                       fi->actions[i] = FA_BACKUP;
-                   } else {
-                       /* FIXME: config files may need to be saved */
-                       fi->actions[i] = FA_REMOVE;
+       shared = sharedList = malloc(sizeof(*sharedList) * (numShared + 1));
+       knownBad = 0;
+       for (i = 0; i < fi->fc; i++) {
+           /* Take care not to mark files as replaced in packages that will
+              have been removed before we got here. */
+           for (j = 0; j < matches[i].count; j++) {
+               ro = matches[i].recs[j].recOffset;
+               if (ro != knownBad) {
+                   for (k = 0; k < ts->orderCount; k++) {
+                       if (ts->order[k].type == TR_REMOVED &&
+                           ts->order[k].u.removed.dboffset == ro) break;
+                   }
+                   if (k < ts->orderCount) {
+                       knownBad = ro;
                    }
-               } else {
-                   fi->actions[i] = FA_REMOVE;
                }
+
+               shared->pkgFileNum = i;
+               shared->otherPkg = matches[i].recs[j].recOffset;
+               shared->otherFileNum = matches[i].recs[j].fileNumber;
+               shared->isRemoved = (knownBad == ro);
+               shared++;
            }
+           dbiFreeIndexRecord(matches[i]);
        }
+       numShared = shared - sharedList;
+       shared->otherPkg = -1;
+       free(matches);
 
-       if (ds) {
-           uint_32 s = BLOCK_ROUND(fi->fsizes[i], ds->block);
-
-           switch (fi->actions[i]) {
-             case FA_BACKUP:
-             case FA_SAVE:
-             case FA_ALTNAME:
-               ds->needed += s;
-               break;
-
-             /* FIXME: If a two packages share a file (same md5sum), and 
-                that file is being replaced on disk, will ds->needed get
-                decremented twice? Quite probably! */
-             case FA_CREATE:
-               ds->needed += s;
-               ds->needed -= BLOCK_ROUND(fi->replacedSizes[i], ds->block);
-               break;
+       qsort(sharedList, numShared, sizeof(*shared), sharedCmp);
 
-             case FA_REMOVE:
-               ds->needed -= s;
-               break;
+       i = 0;
+       while (i < numShared) {
+           last = i + 1;
+           j = sharedList[i].otherPkg;
+           while ((sharedList[last].otherPkg == j) && (last < numShared))
+               last++;
+           last--;
 
-             default:
-               break;
+           for (j = 0; j < ts->numRemovedPackages; j++) {
+               if (ts->removedPackages[j] == sharedList[i].otherPkg)
+                   break;
            }
+           beingRemoved = (j < ts->numRemovedPackages);
 
-           ds->needed -= BLOCK_ROUND(fixupSize, ds->block);
+           if (fi->type == TR_ADDED)
+               handleInstInstalledFiles(fi, ts->db, sharedList + i, 
+                        last - i + 1, 
+                        !(beingRemoved || 
+                               (ignoreSet & RPMPROB_FILTER_REPLACEOLDFILES)), 
+                        probs);
+           else if (fi->type == TR_REMOVED && !beingRemoved)
+               handleRmvdInstalledFiles(fi, ts->db, sharedList + i, 
+                                        last - i + 1);
+
+           i = last + 1;
        }
-    }
-}
 
-static int ensureOlder(rpmdb db, Header new, int dbOffset, rpmProblemSet probs,
-                      const void * key) {
-    Header old;
-    int result, rc = 0;
+       free(sharedList);
 
-    old = rpmdbGetRecord(db, dbOffset);
-    if (old == NULL) return 1;
+       handleOverlappedFiles(fi, ht, 
+              (ignoreSet & RPMPROB_FILTER_REPLACENEWFILES) ? NULL : probs, di);
 
-    result = rpmVersionCompare(old, new);
-    if (result <= 0)
-       rc = 0;
-    else if (result > 0) {
-       rc = 1;
-       psAppend(probs, RPMPROB_OLDPACKAGE, key, new, NULL, old, 0);
+       if (di && fi->type == TR_ADDED && fi->fc) {
+           for (i = 0; i < filesystemCount; i++) {
+               if (adj_fs_blocks(di[i].needed) > di[i].avail) {
+                   psAppend(probs, RPMPROB_DISKSPACE, fi->ap->key, fi->ap->h,
+                            filesystems[i], NULL, 
+                            (adj_fs_blocks(di[i].needed) - di[i].avail) *
+                               di[i].block);
+               }
+           }
+       }
     }
 
-    headerFree(old);
-
-    return rc;
-}
+    NOTIFY((NULL, RPMCALLBACK_TRANS_STOP, 6, flEntries, NULL, notifyData));
 
-static void skipFiles(struct fileInfo * fi, int noDocs) {
-    int i, j;
-    char ** netsharedPaths = NULL, ** nsp;
-    char ** fileLangs, ** languages, ** lang;
-    char * oneLang[2] = { NULL, NULL };
-    int freeLanguages = 0;
-    char * chptr;
+    chroot(".");
+    chdir(currDir);
 
-    if (!noDocs)
-       noDocs = rpmExpandNumeric("%{_excludedocs}");
+    htFree(ht);
 
-    {  const char *tmpPath = rpmExpand("%{_netsharedpath}", NULL);
-       if (tmpPath && *tmpPath != '%')
-           netsharedPaths = splitString(tmpPath, strlen(tmpPath), ':');
-       xfree(tmpPath);
+    for (oc = 0, fi = flList; oc < ts->orderCount; oc++, fi++) {
+       if (fi->fc) {
+           free(fi->fl); fi->fl = NULL;
+           if (fi->type == TR_ADDED) {
+               free(fi->fmd5s); fi->fmd5s = NULL;
+               free(fi->flinks); fi->flinks = NULL;
+               free(fi->fps); fi->fps = NULL;
+           }
+       }
     }
 
-    if (!headerGetEntry(fi->h, RPMTAG_FILELANGS, NULL, (void **) &fileLangs, 
-                       NULL))
-       fileLangs = NULL;
+    if ((flags & RPMTRANS_FLAG_BUILD_PROBS) || 
+           (probs->numProblems && (!okProbs || psTrim(okProbs, probs)))) {
+       *newProbs = probs;
 
-    if ((chptr = getenv("LINGUAS"))) {
-       languages = splitString(chptr, strlen(chptr), ':');
-       freeLanguages = 1;
-    } else if ((oneLang[0] = getenv("LANG"))) {
-       languages = oneLang;
-    } else {
-       oneLang[0] = "en";
-       languages = oneLang;
-    }
+       for (alp = ts->addedPackages.list, fi = flList; 
+               (alp - ts->addedPackages.list) < ts->addedPackages.size; 
+               alp++, fi++) {
+           headerFree(hdrs[alp - ts->addedPackages.list]);
+       }
 
-    for (i = 0; i < fi->fc; i++) {
-       if (fi->actions[i] == FA_SKIP || fi->actions[i] == FA_SKIPNSTATE)
-           continue;
+       freeFl(ts, flList);
+       return ts->orderCount;
+    }
 
-       /* netsharedPaths are not relative to the current root (though 
-          they do need to take package relocations into account) */
-       for (nsp = netsharedPaths; nsp && *nsp; nsp++) {
-           j = strlen(*nsp);
-           if (!strncmp(fi->fl[i], *nsp, j) &&
-               (fi->fl[i][j] == '\0' ||
-                fi->fl[i][j] == '/'))
-               break;
-       }
+    lastFailed = -2;
+    for (oc = 0, fi = flList; oc < ts->orderCount; oc++, fi++) {
+       if (ts->order[oc].type == TR_ADDED) {
+           alp = ts->addedPackages.list + ts->order[oc].u.addedIndex;
+           i = ts->order[oc].u.addedIndex;
 
-       if (nsp && *nsp) {
-           fi->actions[i] = FA_SKIPNSTATE;
-           continue;
-       }
+           if (alp->fd) {
+               fd = alp->fd;
+           } else {
+               fd = notify(fi->h, RPMCALLBACK_INST_OPEN_FILE, 0, 0, 
+                           alp->key, notifyData);
+               if (fd) {
+                   Header h;
 
-       if (fileLangs && languages && *fileLangs[i]) {
-           for (lang = languages; *lang; lang++) {
-               const char *l, *le;
-               for (l = fileLangs[i]; *l; l = le) {
-                   for (le = l; *le && *le != '|'; le++)
-                       ;
-                   if ((le-l) > 0 && !strncmp(*lang, l, (le-l)))
-                       goto lingo;
-                   if (*le == '|') le++;       /* skip over | */
+                   headerFree(hdrs[i]);
+                   rc = rpmReadPackageHeader(fd, &h, NULL, NULL, NULL);
+                   if (rc) {
+                       notify(fi->h, RPMCALLBACK_INST_CLOSE_FILE, 0, 0, 
+                                   alp->key, notifyData);
+                       ourrc++;
+                       fd = NULL;
+                   } else {
+                       hdrs[i] = relocateFileList(alp, probs, h, NULL, 1);
+                       headerFree(h);
+                   }
                }
            }
-       lingo:
-           if (!*lang) {
-               fi->actions[i] = FA_SKIPNSTATE;
-               continue;
+
+           if (fd) {
+               if (installBinaryPackage(ts->root, ts->db, fd, 
+                                        hdrs[i], flags, notify, 
+                                        notifyData, alp->key, fi->actions, 
+                                        fi->fc ? fi->replaced : NULL,
+                                        ts->scriptFd)) {
+                   ourrc++;
+                   lastFailed = i;
+               }
+           } else {
+               ourrc++;
+               lastFailed = i;
            }
-       }
 
-       if (noDocs && (fi->fflags[i] & RPMFILE_DOC))
-           fi->actions[i] = FA_SKIPNSTATE;
+           headerFree(hdrs[i]);
+
+           if (!alp->fd && fd)
+               notify(fi->h, RPMCALLBACK_INST_CLOSE_FILE, 0, 0, alp->key, 
+                  notifyData);
+       } else if (ts->order[oc].u.removed.dependsOnIndex != lastFailed) {
+           if (removeBinaryPackage(ts->root, ts->db, fi->record,
+                                   flags, fi->actions, ts->scriptFd))
+
+               ourrc++;
+       }
     }
 
-    if (netsharedPaths) freeSplitString(netsharedPaths);
-    if (fileLangs) free(fileLangs);
-    if (freeLanguages) freeSplitString(languages);
+    freeFl(ts, flList);
+
+    if (ourrc) 
+       return -1;
+    else
+       return 0;
 }
index 8b5a170..ab5474a 100644 (file)
@@ -8,16 +8,65 @@
 #include "misc.h"
 #include "rpmdb.h"
 
-static char * SCRIPT_PATH = "PATH=/sbin:/bin:/usr/sbin:/usr/bin:"
-                                        "/usr/X11R6/bin";
+static char * SCRIPT_PATH = "PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin";
 
 static int removeFile(char * file, unsigned int flags, short mode, 
-                     enum fileActions action);
-static int runScript(Header h, const char * root, int progArgc, const char ** progArgv, 
-                    const char * script, int arg1, int arg2, FD_t errfd);
+                     enum fileActions action)
+{
+    int rc = 0;
+    char * newfile;
+       
+    switch (action) {
+
+      case FA_BACKUP:
+       newfile = alloca(strlen(file) + 20);
+       strcpy(newfile, file);
+       strcat(newfile, ".rpmsave");
+       if (rename(file, newfile)) {
+           rpmError(RPMERR_RENAME, _("rename of %s to %s failed: %s"),
+                       file, newfile, strerror(errno));
+           rc = 1;
+       }
+       break;
+
+      case FA_REMOVE:
+       if (S_ISDIR(mode)) {
+           if (rmdir(file)) {
+               if (errno == ENOTEMPTY)
+                   rpmError(RPMERR_RMDIR, 
+                       _("cannot remove %s - directory not empty"), 
+                       file);
+               else
+                   rpmError(RPMERR_RMDIR, _("rmdir of %s failed: %s"),
+                               file, strerror(errno));
+               rc = 1;
+           }
+       } else {
+           if (unlink(file)) {
+               if (errno != ENOENT || !(flags & RPMFILE_MISSINGOK)) {
+                   rpmError(RPMERR_UNLINK, 
+                             _("removal of %s failed: %s"),
+                               file, strerror(errno));
+               }
+               rc = 1;
+           }
+       }
+       break;
+      case FA_UNKNOWN:
+      case FA_CREATE:
+      case FA_SAVE:
+      case FA_SKIP:
+      case FA_SKIPNSTATE:
+      case FA_ALTNAME:
+       break;
+    }
+    return 0;
+}
 
 int removeBinaryPackage(char * prefix, rpmdb db, unsigned int offset, 
-                       int flags, enum fileActions * actions, FD_t scriptFd) {
+                       int flags, enum fileActions * actions, FD_t scriptFd)
+{
     Header h;
     int i;
     int fileCount;
@@ -143,7 +192,8 @@ int removeBinaryPackage(char * prefix, rpmdb db, unsigned int offset,
 }
 
 static int runScript(Header h, const char * root, int progArgc, const char ** progArgv, 
-                    const char * script, int arg1, int arg2, FD_t errfd) {
+                    const char * script, int arg1, int arg2, FD_t errfd)
+{
     const char ** argv;
     int argc;
     const char ** prefixes = NULL;
@@ -294,7 +344,8 @@ static int runScript(Header h, const char * root, int progArgc, const char ** pr
 }
 
 int runInstScript(const char * root, Header h, int scriptTag, int progTag,
-                 int arg, int norunScripts, FD_t err) {
+                 int arg, int norunScripts, FD_t err)
+{
     void ** programArgv;
     int programArgc;
     const char ** argv;
@@ -322,62 +373,10 @@ int runInstScript(const char * root, Header h, int scriptTag, int progTag,
     return rc;
 }
 
-static int removeFile(char * file, unsigned int flags, short mode, 
-                     enum fileActions action) {
-    int rc = 0;
-    char * newfile;
-       
-    switch (action) {
-
-      case FA_BACKUP:
-       newfile = alloca(strlen(file) + 20);
-       strcpy(newfile, file);
-       strcat(newfile, ".rpmsave");
-       if (rename(file, newfile)) {
-           rpmError(RPMERR_RENAME, _("rename of %s to %s failed: %s"),
-                       file, newfile, strerror(errno));
-           rc = 1;
-       }
-       break;
-
-      case FA_REMOVE:
-       if (S_ISDIR(mode)) {
-           if (rmdir(file)) {
-               if (errno == ENOTEMPTY)
-                   rpmError(RPMERR_RMDIR, 
-                       _("cannot remove %s - directory not empty"), 
-                       file);
-               else
-                   rpmError(RPMERR_RMDIR, _("rmdir of %s failed: %s"),
-                               file, strerror(errno));
-               rc = 1;
-           }
-       } else {
-           if (unlink(file)) {
-               if (errno != ENOENT || !(flags & RPMFILE_MISSINGOK)) {
-                   rpmError(RPMERR_UNLINK, 
-                             _("removal of %s failed: %s"),
-                               file, strerror(errno));
-               }
-               rc = 1;
-           }
-       }
-       break;
-      case FA_UNKNOWN:
-      case FA_CREATE:
-      case FA_SAVE:
-      case FA_SKIP:
-      case FA_SKIPNSTATE:
-      case FA_ALTNAME:
-       break;
-    }
-    return 0;
-}
-
 static int handleOneTrigger(const char * root, rpmdb db, int sense, Header sourceH,
                            Header triggeredH, int arg1correction, int arg2,
-                           char * triggersAlreadyRun, FD_t scriptFd) {
+                           char * triggersAlreadyRun, FD_t scriptFd)
+{
     const char ** triggerNames;
     const char ** triggerVersions;
     const char ** triggerScripts;
@@ -457,7 +456,8 @@ static int handleOneTrigger(const char * root, rpmdb db, int sense, Header sourc
 }
 
 int runTriggers(const char * root, rpmdb db, int sense, Header h,
-               int countCorrection, FD_t scriptFd) {
+               int countCorrection, FD_t scriptFd)
+{
     char * packageName;
     dbiIndexSet matches, otherMatches;
     Header triggeredH;
@@ -495,7 +495,8 @@ int runTriggers(const char * root, rpmdb db, int sense, Header h,
 }
 
 int runImmedTriggers(const char * root, rpmdb db, int sense, Header h,
-                    int countCorrection, FD_t scriptFd) {
+                    int countCorrection, FD_t scriptFd)
+{
     int rc = 0;
     dbiIndexSet matches;
     char ** triggerNames;