File patchrpms.diff of Package rpm

Add support for patch rpms. Maybe not needed that much any more,
as delta rpms are more efficient and do not need so much evil
rpm patchery.
rh#103205

Index: lib/depends.c
===================================================================
--- lib/depends.c.orig
+++ lib/depends.c
@@ -159,6 +159,7 @@ int rpmtsAddInstallElement(rpmts ts, Hea
     const char * os;
     rpmds oldChk, newChk;
     rpmds obsoletes;
+    rpmds patches;
     alKey pkgKey;	/* addedPackages key */
     int xx;
     int ec = 0;
@@ -399,6 +400,40 @@ addheader:
     }
     obsoletes = rpmdsFree(obsoletes);
 
+    patches = rpmdsLink(rpmteDS(p, RPMTAG_PATCHESNAME), "Patches");
+    patches = rpmdsInit(patches);
+    if (patches != NULL)
+    while (rpmdsNext(patches) >= 0) {
+	const char * Name;
+
+	if ((Name = rpmdsN(patches)) == NULL)
+	    continue;   /* XXX can't happen */
+
+	/* Ignore colored patches  not in our rainbow. */
+	dscolor = rpmdsColor(patches);
+	if (tscolor && dscolor && !(tscolor & dscolor))
+	    continue;
+
+	mi = rpmtsInitIterator(ts, RPMTAG_NAME, Name, 0);
+
+	xx = rpmdbPruneIterator(mi,
+	    ts->removedPackages, ts->numRemovedPackages, 1);
+
+	while((oh = rpmdbNextIterator(mi)) != NULL) {
+	    /* Ignore colored packages not in our rainbow. */
+	    ohcolor = hGetColor(oh);
+	    if (tscolor && hcolor && ohcolor && !(hcolor & ohcolor))
+		/*@innercontinue@*/ continue;
+	    if (rpmdsEVR(patches) == NULL
+	     || rpmdsNVRMatchesDep(oh, patches, _rpmds_nopromote)) {
+		if (rpmVersionCompare(h, oh))
+		    xx = removePackage(ts, oh, rpmdbGetIteratorOffset(mi), pkgKey);
+	    }
+	}
+	mi = rpmdbFreeIterator(mi);
+    }
+    patches = rpmdsFree(patches);
+
     ec = 0;
 
 exit:
@@ -656,6 +691,57 @@ exit:
     return rc;
 }
 
+static int checkPatchDeps(rpmts ts, rpmte p, int reportprobs)
+{
+    const char * Name;
+    Header h;
+    rpmds patches;
+    rpmds this;
+    rpmdbMatchIterator mi;
+
+    patches = rpmdsInit(rpmteDS(p, RPMTAG_PATCHESNAME));
+    if (!patches)
+	return 0;
+    this = rpmteDS(p, RPMTAG_NAME);
+
+    mi = rpmtsInitIterator(ts, RPMTAG_NAME, rpmdsN(this), 0);
+    while ((h = rpmdbNextIterator(mi)) != NULL) {
+	if (rpmdsNVRMatchesDep(h, this, _rpmds_nopromote)) {
+	    rpmdsNotify(this, _("(patch refresh)"), 0);
+	    p->hPatched = headerLink(h);
+	    p->isPatchRefresh = 1;
+	    mi = rpmdbFreeIterator(mi);
+	    return 0;
+	}
+    }
+    mi = rpmdbFreeIterator(mi);
+
+    while (rpmdsNext(patches) >= 0) {
+	if ((Name = rpmdsN(patches)) == NULL)
+	    return 1;   /* XXX can't happen */
+	mi = rpmtsInitIterator(ts, RPMTAG_NAME, Name, 0);
+	while ((h = rpmdbNextIterator(mi)) != NULL) {
+	    if (rpmdsNVRMatchesDep(h, patches, _rpmds_nopromote)) {
+		rpmdsNotify(patches, _("(db package)"), 0);
+		p->hPatched = headerLink(h);
+		p->isPatchRefresh = 0;
+		mi = rpmdbFreeIterator(mi);
+		return 0;
+	    }
+	}
+	mi = rpmdbFreeIterator(mi);
+    }
+
+    rpmdsNotify(patches, NULL, 1);
+    if (reportprobs) {
+	patches = rpmdsInit(patches);
+	rpmdsNext(patches);
+	rpmdsProblem(ts->probs, rpmteNEVR(p), patches, NULL, 1);
+    }
+    return 0;
+}
+
+
 /**
  * Check added requires/conflicts against against installed+added packages.
  * @param ts		transaction set
@@ -1730,6 +1816,7 @@ int rpmtsCheck(rpmts ts)
 			rpmteDS(p, RPMTAG_CONFLICTNAME),
 			NULL,
 			tscolor, 1);
+	rc |= checkPatchDeps(ts, p, 1);
 	if (rc)
 	    goto exit;
 
@@ -1827,3 +1914,22 @@ exit:
     /*@=branchstate@*/
     return rc;
 }
+
+void rpmtsPatchCheck(rpmts ts)
+{
+    int closeatexit = 0;
+    rpmtsi pi = NULL; rpmte p;
+
+    if (rpmtsGetRdb(ts) == NULL && ts->dbmode != -1) {
+	if ((rpmtsOpenDB(ts, ts->dbmode)) != 0)
+	    return;
+	closeatexit = 1;
+    }
+    pi = rpmtsiInit(ts);
+    while ((p = rpmtsiNext(pi, TR_ADDED)) != NULL)
+	if (p->key)     /* key is filename for install, zero for verify */
+	    (void)checkPatchDeps(ts, p, 0);
+    pi = rpmtsiFree(pi);
+    if (closeatexit)
+	(void)rpmtsCloseDB(ts);
+}
Index: lib/formats.c
===================================================================
--- lib/formats.c.orig
+++ lib/formats.c
@@ -232,6 +232,8 @@ static /*@only@*/ char * fflagsFormat(in
 	    strcat(buf, "l");
 	if (anint & RPMFILE_README)
 	    strcat(buf, "r");
+	if (anint & RPMFILE_UNPATCHED)
+	    strcat(buf, "u");
 /*@=boundswrite@*/
 
 	val = xmalloc(5 + padding);
Index: lib/fsm.c
===================================================================
--- lib/fsm.c.orig
+++ lib/fsm.c
@@ -707,7 +707,7 @@ assert(rpmteType(fi->te) == TR_ADDED);
 	    break;
 
 	case FA_BACKUP:
-	    if (!(fsm->fflags & RPMFILE_GHOST)) /* XXX Don't if %ghost file. */
+	    if (!(fsm->fflags & (RPMFILE_GHOST|RPMFILE_UNPATCHED))) /* XXX Don't if %ghost file. */
 	    switch (rpmteType(fi->te)) {
 	    case TR_ADDED:
 		fsm->osuffix = SUFFIX_RPMORIG;
@@ -720,13 +720,13 @@ assert(rpmteType(fi->te) == TR_ADDED);
 
 	case FA_ALTNAME:
 assert(rpmteType(fi->te) == TR_ADDED);
-	    if (!(fsm->fflags & RPMFILE_GHOST)) /* XXX Don't if %ghost file. */
+	    if (!(fsm->fflags & (RPMFILE_GHOST|RPMFILE_UNPATCHED))) /* XXX Don't if %ghost file. */
 		fsm->nsuffix = SUFFIX_RPMNEW;
 	    break;
 
 	case FA_SAVE:
 assert(rpmteType(fi->te) == TR_ADDED);
-	    if (!(fsm->fflags & RPMFILE_GHOST)) /* XXX Don't if %ghost file. */
+	    if (!(fsm->fflags & (RPMFILE_GHOST|RPMFILE_UNPATCHED))) /* XXX Don't if %ghost file. */
 		fsm->osuffix = SUFFIX_RPMSAVE;
 	    break;
 	case FA_ERASE:
@@ -1740,7 +1740,7 @@ int fsmStage(FSM_t fsm, fileStage stage)
 	}
 
 	if (fsm->goal == FSM_PKGBUILD) {
-	    if (fsm->fflags & RPMFILE_GHOST) /* XXX Don't if %ghost file. */
+	    if (fsm->fflags & (RPMFILE_GHOST|RPMFILE_UNPATCHED)) /* XXX Don't if %ghost file. */
 		break;
 	    if (!S_ISDIR(st->st_mode) && st->st_nlink > 1) {
 		struct hardLink_s * li, * prev;
Index: lib/poptQV.c
===================================================================
--- lib/poptQV.c.orig
+++ lib/poptQV.c
@@ -171,6 +171,7 @@ static void queryArgCallback(poptContext
     case 'l': qva->qva_flags |= QUERY_FOR_LIST; break;
     case 's': qva->qva_flags |= QUERY_FOR_STATE | QUERY_FOR_LIST;
 	break;
+    case 'P': qva->qva_flags |= QUERY_FOR_PATCHES; break;
     case POPT_DUMP: qva->qva_flags |= QUERY_FOR_DUMPFILES | QUERY_FOR_LIST;
 	break;
 
@@ -278,6 +279,8 @@ struct poptOption rpmQueryPoptTable[] =
         N_("skip %%readme files"), NULL },
 #endif
 
+ { "patches", 'P', 0, 0, 'P',
+	N_("list patches or patched files "), NULL },
  { "qf", '\0', POPT_ARG_STRING | POPT_ARGFLAG_DOC_HIDDEN, 0, 
 	POPT_QUERYFORMAT, NULL, NULL },
  { "queryformat", '\0', POPT_ARG_STRING, 0, POPT_QUERYFORMAT,
Index: lib/query.c
===================================================================
--- lib/query.c.orig
+++ lib/query.c
@@ -258,6 +258,10 @@ int showQueryPackage(QVA_t qva, rpmts ts
 	    te = t + tx;
 	}
 
+	/* If querying patches, skip unpatched files. */
+	if ((qva->qva_flags & QUERY_FOR_PATCHES) && (fflags & RPMFILE_UNPATCHED))
+	    continue;
+
 /*@-boundswrite@*/
 	if (!rpmIsVerbose() && prefix)
 	    te = stpcpy(te, prefix);
@@ -377,6 +381,21 @@ void rpmDisplayQueryTags(FILE * fp)
     }
 }
 
+static int isPatch(Header h)
+{
+    int i, requiresCount = 0;
+    const char ** requires;
+
+    if (!headerGetEntry(h, RPMTAG_REQUIRENAME, NULL, (void **) &requires, &requiresCount))
+	return 0;
+    for (i = 0; i < requiresCount; i++)
+	if (!strcmp("rpmlib(PatchRPMs)", requires[i]))
+	    break;
+    if (requiresCount)
+	free(requires);
+    return i < requiresCount;
+}
+
 static int rpmgiShowMatches(QVA_t qva, rpmts ts)
 	/*@globals rpmGlobalMacroContext, h_errno, internalState @*/
         /*@modifies qva, rpmGlobalMacroContext, h_errno, internalState @*/
@@ -391,6 +410,8 @@ static int rpmgiShowMatches(QVA_t qva, r
 	h = rpmgiHeader(gi);
 	if (h == NULL)		/* XXX perhaps stricter break instead? */
 	    continue;
+	if ((qva->qva_flags & QUERY_FOR_PATCHES) != 0 && !isPatch(h))
+	    continue;
 	if ((rc = qva->qva_showPackage(qva, ts, h)) != 0)
 	    ec = rc;
 	if (qva->qva_source == RPMQV_DBOFFSET)
@@ -406,6 +427,8 @@ int rpmcliShowMatches(QVA_t qva, rpmts t
 
     while ((h = rpmdbNextIterator(qva->qva_mi)) != NULL) {
 	int rc;
+	if ((qva->qva_flags & QUERY_FOR_PATCHES) != 0 && !isPatch(h))
+	    continue;
 	if ((rc = qva->qva_showPackage(qva, ts, h)) != 0)
 	    ec = rc;
 	if (qva->qva_source == RPMQV_DBOFFSET)
@@ -703,7 +726,17 @@ int rpmcliArgIter(rpmts ts, QVA_t qva, A
 
     switch (qva->qva_source) {
     case RPMQV_ALL:
-	qva->qva_gi = rpmgiNew(ts, RPMDBI_PACKAGES, NULL, 0);
+	if ((!argv || !*argv) && (qva->qva_flags & QUERY_FOR_PATCHES) != 0) {
+	    qva->qva_gi = rpmgiNew(ts, RPMTAG_REQUIRENAME, "rpmlib(PatchRPMs)", 0);
+	    qva->qva_gi->mi = rpmtsInitIterator(qva->qva_gi->ts, qva->qva_gi->tag, qva->qva_gi->keyp, qva->qva_gi->keylen);
+	    if (qva->qva_gi->mi == NULL) {
+		rpmError(RPMERR_QUERYINFO, _("no patch-rpm installed\n"));
+		break;
+	    }
+	    qva->qva_gi->mi = rpmdbFreeIterator(qva->qva_gi->mi);
+	} else {
+	    qva->qva_gi = rpmgiNew(ts, RPMDBI_PACKAGES, NULL, 0);
+	}
 	qva->qva_rc = rpmgiSetArgs(qva->qva_gi, argv, ftsOpts, RPMGI_NONE);
 
 	if (qva->qva_gi != NULL && (qva->qva_gi->flags & RPMGI_TSADD))	/* Load the ts with headers. */
Index: lib/rpmcli.h
===================================================================
--- lib/rpmcli.h.orig
+++ lib/rpmcli.h
@@ -165,7 +165,7 @@ typedef enum rpmQueryFlags_e {
     QUERY_SCRIPT	= (1 << 18),	/*!< verify: from --noscripts */
     QUERY_DIGEST	= (1 << 19),	/*!< verify: from --nodigest */
     QUERY_SIGNATURE	= (1 << 20),	/*!< verify: from --nosignature */
-    QUERY_PATCHES	= (1 << 21),	/*!< verify: from --nopatches */
+    QUERY_FOR_PATCHES	= (1 << 21),	/*!< verify: from --patches */
     QUERY_HDRCHK	= (1 << 22),	/*!< verify: from --nohdrchk */
 /*@=enummemuse@*/
     QUERY_FOR_LIST	= (1 << 23),	/*!< query:  from --list */
Index: lib/rpmds.c
===================================================================
--- lib/rpmds.c.orig
+++ lib/rpmds.c
@@ -87,6 +87,10 @@ fprintf(stderr, "*** ds %p\t%s[%d]\n", d
 	tagEVR = RPMTAG_TRIGGERVERSION;
 	tagF = RPMTAG_TRIGGERFLAGS;
     } else
+    if (ds->tagN == RPMTAG_PATCHESNAME) {
+	tagEVR = RPMTAG_PATCHESVERSION;
+	tagF = RPMTAG_PATCHESFLAGS;
+    } else
 	return NULL;
 
     /*@-branchstate@*/
@@ -325,6 +329,11 @@ rpmds rpmdsNew(Header h, rpmTag tagN, in
 	tagEVR = RPMTAG_ENHANCESVERSION;
 	tagF = RPMTAG_ENHANCESFLAGS;
     } else
+    if (tagN == RPMTAG_PATCHESNAME) {
+	Type = "patches";
+	tagEVR = RPMTAG_PATCHESVERSION;
+	tagF = RPMTAG_PATCHESFLAGS;
+    } else
 	goto exit;
 
     /*@-branchstate@*/
@@ -1127,14 +1136,28 @@ void rpmdsProblem(rpmps ps, const char *
     if (DNEVR == NULL) DNEVR = "? ?N? ?OP? ?EVR?";
     /*@=branchstate@*/
 
-    rpmMessage(RPMMESS_DEBUG, _("package %s has unsatisfied %s: %s\n"),
-	    pkgNEVR, ds->Type, DNEVR+2);
-
     switch ((unsigned)DNEVR[0]) {
     case 'C':	type = RPMPROB_CONFLICT;	break;
     default:
     case 'R':	type = RPMPROB_REQUIRES;	break;
     }
+    if (DNEVR[0] == 'p') {
+	const char *d;
+	char *dn;
+	rpmds pds = rpmdsInit(ds);
+        dn = xstrdup("p ");
+	while (rpmdsNext(pds) >= 0) {
+	    d = rpmdsDNEVR(ds) + 2;
+	    dn = xrealloc(dn, strlen(dn) + strlen(d) + 4);
+	    if (dn[2])
+		strcat(dn, " | ");
+	    strcat(dn, d);
+	}
+	DNEVR = (const char *)dn;
+    }
+
+    rpmMessage(RPMMESS_DEBUG, _("package %s has unsatisfied %s: %s\n"),
+	    pkgNEVR, ds->Type, DNEVR+2);
 
     key = (suggestedKeys ? suggestedKeys[0] : NULL);
     rpmpsAppend(ps, type, pkgNEVR, key, NULL, NULL, DNEVR, adding);
Index: lib/rpminstall.c
===================================================================
--- lib/rpminstall.c.orig
+++ lib/rpminstall.c
@@ -683,6 +683,11 @@ maybe_manifest:
 	    /*@=branchstate@*/
 	}
 	ps = rpmpsFree(ps);
+    } else if (eiu->numRPMS) {
+	/* needed in rpmtsOrder */
+	rpmalMakeIndex(ts->addedPackages);
+	/* need patch references */
+	rpmtsPatchCheck(ts);
     }
 
     if (eiu->numRPMS && !(ia->installInterfaceFlags & INSTALL_NOORDER)) {
@@ -789,7 +794,7 @@ int rpmErase(rpmts ts, struct rpmInstall
     {	int notifyFlags;
 	notifyFlags = ia->eraseInterfaceFlags | (rpmIsVerbose() ? INSTALL_LABEL : 0 );
 	xx = rpmtsSetNotifyCallback(ts,
-			rpmShowProgress, (void *) ((long)notifyFlags)
+			rpmShowProgress, (void *) ((long)notifyFlags))
     }
 #endif
 
Index: lib/rpmlibprov.c
===================================================================
--- lib/rpmlibprov.c.orig
+++ lib/rpmlibprov.c
@@ -33,6 +33,9 @@ static struct rpmlibProvides_s rpmlibPro
     { "rpmlib(PayloadIsBzip2)",		"3.0.5-1",
 	(RPMSENSE_RPMLIB|RPMSENSE_EQUAL),
     N_("package payload can be compressed using bzip2.") },
+    { "rpmlib(PatchRPMs)",		"3.0.6-1",
+        (RPMSENSE_RPMLIB|RPMSENSE_EQUAL),
+    N_("understand rpms that replace a subset of files.") },
     { "rpmlib(PayloadFilesHavePrefix)",	"4.0-1",
 	(RPMSENSE_RPMLIB|RPMSENSE_EQUAL),
     N_("package payload file(s) have \"./\" prefix.") },
Index: lib/rpmte.c
===================================================================
--- lib/rpmte.c.orig
+++ lib/rpmte.c
@@ -64,6 +64,7 @@ static void delTE(rpmte p)
     p->NEVRA = _free(p->NEVRA);
 
     p->h = headerFree(p->h);
+    p->hPatched = headerFree(p->hPatched);
 
 /*@-boundswrite@*/
     memset(p, 0, sizeof(*p));	/* XXX trash and burn */
@@ -183,6 +184,9 @@ static void addTE(rpmts ts, rpmte p, Hea
     p->requires = rpmdsNew(h, RPMTAG_REQUIRENAME, scareMem);
     p->conflicts = rpmdsNew(h, RPMTAG_CONFLICTNAME, scareMem);
     p->obsoletes = rpmdsNew(h, RPMTAG_OBSOLETENAME, scareMem);
+    p->patches = rpmdsNew(h, RPMTAG_PATCHESNAME, scareMem | 2);
+    p->hPatched = NULL;
+    p->isPatchRefresh = 0;
 
     savep = rpmtsSetRelocateElement(ts, p);
     p->fi = rpmfiNew(ts, h, RPMTAG_BASENAMES, scareMem);
@@ -520,6 +524,9 @@ rpmds rpmteDS(rpmte te, rpmTag tag)
     if (tag == RPMTAG_OBSOLETENAME)
 	return te->obsoletes;
     else
+    if (tag == RPMTAG_PATCHESNAME)
+	return te->patches;
+    else
 	return NULL;
     /*@=compdef =refcounttrans =retalias =retexpose =usereleased @*/
 }
Index: lib/rpmte.h
===================================================================
--- lib/rpmte.h.orig
+++ lib/rpmte.h
@@ -115,6 +115,9 @@ struct rpmte_s {
     int autorelocatex;		/*!< (TR_ADDED) Auto relocation entry index. */
 /*@refcounted@*/ /*@null@*/	
     FD_t fd;			/*!< (TR_ADDED) Payload file descriptor. */
+    rpmds patches;		/*!< Patches: dependencies. */
+    Header hPatched;		/*!< (TR_ADDED) Header of package we patch */
+    int isPatchRefresh;		/*!< (TR_ADDED) is a patch refresh */
 
 /*@-fielduse@*/	/* LCL: confused by union? */
     union {
Index: lib/transaction.c
===================================================================
--- lib/transaction.c.orig
+++ lib/transaction.c
@@ -199,6 +199,11 @@ static int handleInstInstalledFiles(cons
 	    int rConflicts;
 
 	    rConflicts = reportConflicts;
+	    if (rConflicts && p->hPatched && p->isPatchRefresh) {
+		/* If same package (patch refresh) turn off conflicts */
+		/* Handling of unpatched files not worth the trouble */
+		rConflicts = 0;
+	    }
 	    /* Resolve file conflicts to prefer Elf64 (if not forced). */
 	    if (tscolor != 0 && FColor != 0 && FColor != oFColor)
 	    {
@@ -975,6 +980,176 @@ rpmfi rpmtsiFi(const rpmtsi tsi)
     /*@=compdef =refcounttrans =usereleased @*/
 }
 
+static int_32 *dupint32(int_32 *old, int cnt)
+{
+    int i;
+    int_32 *new = xmalloc(cnt * sizeof(int_32));
+    for (i = 0; i < cnt; i++)
+	new[i] = old[i];
+    return new;
+}
+
+static void patchUnpatchedFiles(Header oldh, Header h, int isRefresh)
+{
+    int fileCount, oldfileCount, i, j, oldidx, oldidxj;
+    const char ** baseNames, ** dirNames;
+    int_32 * dirIndexes;
+    int_32 * fileMtimes;
+    int_32 * fileSizes;
+    int_32 * fileFlags;
+    char ** fileMd5s;
+    const char ** oldbaseNames, ** olddirNames;
+    int_32 * olddirIndexes;
+    int_32 * oldfileMtimes;
+    int_32 * oldfileSizes;
+    int_32 * oldfileFlags;
+    char ** oldfileMd5s;
+    const char * name, * version, * release;
+    char * evr;
+    int_32 sense;
+    int_32 *epochp;
+    int save = 0;
+    char epoch[20];
+    const char ** oldpatches, **oldpatchesEVR = NULL;
+    int_32 * oldpatchesFlags;
+    int oldpatchesCount;
+
+    if (!oldh) {
+	headerRemoveEntry(h, RPMTAG_PATCHESNAME);
+	headerRemoveEntry(h, RPMTAG_PATCHESFLAGS);
+	headerRemoveEntry(h, RPMTAG_PATCHESVERSION);
+#if 1
+	name = "(none)";
+	sense = 0;
+	evr = "";
+	headerAddEntry(h, RPMTAG_PATCHESNAME, RPM_STRING_ARRAY_TYPE, &name, 1);
+	headerAddEntry(h, RPMTAG_PATCHESFLAGS, RPM_INT32_TYPE, &sense, 1);
+	headerAddEntry(h, RPMTAG_PATCHESVERSION, RPM_STRING_ARRAY_TYPE, &evr, 1);
+#endif
+	return;
+    }
+    if (!headerGetEntry(h, RPMTAG_BASENAMES, NULL,
+		  (void **) &baseNames, &fileCount))
+	return;
+    headerGetEntry(h, RPMTAG_DIRNAMES, NULL,
+		(void **) &dirNames, NULL);
+    headerGetEntry(h, RPMTAG_DIRINDEXES, NULL,
+		(void **) &dirIndexes, NULL);
+    headerGetEntry(h, RPMTAG_FILESIZES, NULL,
+		(void **) &fileSizes, NULL);
+    headerGetEntry(h, RPMTAG_FILEMD5S, NULL,
+		(void **) &fileMd5s, NULL);
+    headerGetEntry(h, RPMTAG_FILEMTIMES, NULL,
+		(void **) &fileMtimes, NULL);
+    headerGetEntry(h, RPMTAG_FILEFLAGS, NULL,
+		(void **) &fileFlags, NULL);
+
+    if (!headerGetEntry(oldh, RPMTAG_BASENAMES, NULL,
+		  (void **) &oldbaseNames, &oldfileCount))
+	return;
+    headerGetEntry(oldh, RPMTAG_DIRNAMES, NULL,
+		(void **) &olddirNames, NULL);
+    headerGetEntry(oldh, RPMTAG_DIRINDEXES, NULL,
+		(void **) &olddirIndexes, NULL);
+    headerGetEntry(oldh, RPMTAG_FILESIZES, NULL,
+		(void **) &oldfileSizes, NULL);
+    headerGetEntry(oldh, RPMTAG_FILEMD5S, NULL,
+		(void **) &oldfileMd5s, NULL);
+    headerGetEntry(oldh, RPMTAG_FILEMTIMES, NULL,
+		(void **) &oldfileMtimes, NULL);
+    headerGetEntry(oldh, RPMTAG_FILEFLAGS, NULL,
+		(void **) &oldfileFlags, NULL);
+
+    oldidx = -1;
+    oldidxj = 0;
+    for (i = 0; i < fileCount; i++) {
+	if (!(fileFlags[i] & RPMFILE_UNPATCHED))
+	    continue;
+	if (dirIndexes[i] != oldidx) {
+	    for (j = 0; j < oldfileCount; j++)
+		if (strcmp(dirNames[dirIndexes[i]], olddirNames[olddirIndexes[j]]) == 0)
+		    break;
+	    if (j == oldfileCount) {
+		while (i + 1 < fileCount && dirIndexes[i] == dirIndexes[i + 1])
+		    i++;
+		continue;
+	    }
+	    oldidx = olddirIndexes[j];
+	    oldidxj = j;
+	}
+	for (j = oldidxj; j < oldfileCount; j++)
+	    if (olddirIndexes[j] == oldidx && !strcmp(baseNames[i], oldbaseNames[j])) {
+		if (!save) {
+		    /* duplicate fileSizes, fileMtimes, fileFlags
+		     * so we can modify them */
+		    fileSizes = dupint32(fileSizes, fileCount);
+		    fileMtimes = dupint32(fileMtimes, fileCount);
+		    fileFlags = dupint32(fileFlags, fileCount);
+		}
+		fileSizes[i] = oldfileSizes[j];
+		fileMtimes[i] = oldfileMtimes[j];
+		fileMd5s[i] = oldfileMd5s[j];
+		fileFlags[i] = oldfileFlags[j] | RPMFILE_UNPATCHED;
+		save = 1;
+		break;
+	    }
+    }
+    if (save) {
+	headerModifyEntry(h, RPMTAG_FILESIZES, RPM_INT32_TYPE,
+		    (void *) fileSizes, fileCount);
+	headerModifyEntry(h, RPMTAG_FILEMD5S, RPM_STRING_ARRAY_TYPE,
+		    (void *) fileMd5s, fileCount);
+	headerModifyEntry(h, RPMTAG_FILEMTIMES, RPM_INT32_TYPE,
+		    (void *) fileMtimes, fileCount);
+	headerModifyEntry(h, RPMTAG_FILEFLAGS, RPM_INT32_TYPE,
+		    (void *) fileFlags, fileCount);
+	free(fileSizes);
+	free(fileMtimes);
+	free(fileFlags);
+    }
+    free(baseNames);
+    free(dirNames);
+    free(fileMd5s);
+    free(oldbaseNames);
+    free(olddirNames);
+    free(oldfileMd5s);
+
+    if (isRefresh) {
+	/* same patch installed, this is just a refresh operation */
+	headerRemoveEntry(h, RPMTAG_PATCHESNAME);
+	headerRemoveEntry(h, RPMTAG_PATCHESFLAGS);
+	headerRemoveEntry(h, RPMTAG_PATCHESVERSION);
+	if (headerGetEntry(oldh, RPMTAG_PATCHESNAME, NULL, (void **) &oldpatches, &oldpatchesCount) && oldpatchesCount) {
+	    headerGetEntry(oldh, RPMTAG_PATCHESFLAGS, NULL, (void **) &oldpatchesFlags, &oldpatchesCount);
+	    headerGetEntry(oldh, RPMTAG_PATCHESVERSION, NULL, (void **) &oldpatchesEVR, &oldpatchesCount);
+	    headerAddEntry(h, RPMTAG_PATCHESNAME, RPM_STRING_ARRAY_TYPE, oldpatches, oldpatchesCount);
+	    headerAddEntry(h, RPMTAG_PATCHESFLAGS, RPM_INT32_TYPE, oldpatchesFlags, oldpatchesCount);
+	    headerAddEntry(h, RPMTAG_PATCHESVERSION, RPM_STRING_ARRAY_TYPE, oldpatchesEVR, oldpatchesCount);
+	    free(oldpatches);
+	    free(oldpatchesEVR);
+	}
+	return;
+    }
+    headerNVR(oldh, &name, &version, &release);
+    *epoch = 0;
+    if (headerGetEntry(h, RPMTAG_EPOCH, NULL, (void **) &epochp, NULL))
+	sprintf(epoch, "%d:", *epochp);
+    evr = xmalloc(strlen(epoch) + strlen(version) + strlen(release) + 2);
+    strcpy(evr, epoch);
+    strcat(evr, version);
+    strcat(evr, "-");
+    strcat(evr, release);
+    sense = RPMSENSE_EQUAL;
+    headerRemoveEntry(h, RPMTAG_PATCHESNAME);
+    headerRemoveEntry(h, RPMTAG_PATCHESFLAGS);
+    headerRemoveEntry(h, RPMTAG_PATCHESVERSION);
+    headerAddEntry(h, RPMTAG_PATCHESNAME, RPM_STRING_ARRAY_TYPE, &name, 1);
+    headerAddEntry(h, RPMTAG_PATCHESFLAGS, RPM_INT32_TYPE, &sense, 1);
+    headerAddEntry(h, RPMTAG_PATCHESVERSION, RPM_STRING_ARRAY_TYPE, &evr, 1);
+    free(evr);
+}
+
+
 /**
  * This is not a generalized function to be called from outside
  * librpm.  It is called internally by rpmtsRun() to rollback
@@ -2154,6 +2329,8 @@ assert(psm != NULL);
 		}
 		psm->fi = rpmfiLink(p->fi, NULL);
 
+		if (p->hPatched || rpmteDS(p, RPMTAG_PATCHESNAME))
+		    patchUnpatchedFiles(p->hPatched, p->fi->h, p->isPatchRefresh);
 /*@-nullstate@*/ /* FIX: psm->fi may be NULL */
 		if (rpmpsmStage(psm, PSM_PKGINSTALL)) {
 		    ourrc++;
Index: doc/rpm.8
===================================================================
--- doc/rpm.8.orig
+++ doc/rpm.8
@@ -68,7 +68,8 @@ rpm \- RPM Package Manager
 
 
  [\fB\fIPACKAGE_NAME\fB\fR] [\fB-a,--all\fR] [\fB-f,--file \fIFILE\fB\fR]
- [\fB-g,--group \fIGROUP\fB\fR] {\fB-p,--package \fIPACKAGE_FILE\fB\fR]
+ [\fB-g,--group \fIGROUP\fB\fR] [\fB-p,--package \fIPACKAGE_FILE\fB\fR]
+ [\fB-P,--patches\fR]
  [\fB--fileid \fIMD5\fB\fR] [\fB--hdrid \fISHA1\fB\fR] [\fB--pkgid \fIMD5\fB\fR] [\fB--tid \fITID\fB\fR]
  [\fB--querybynumber \fIHDRNUM\fB\fR] [\fB--triggeredby \fIPACKAGE_NAME\fB\fR]
  [\fB--whatprovides \fICAPABILITY\fB\fR] [\fB--whatrequires \fICAPABILITY\fB\fR]
@@ -77,7 +78,8 @@ rpm \- RPM Package Manager
 .PP
 
 
- [\fB--changelog\fR] [\fB-c,--configfiles\fR] [\fB-d,--docfiles\fR] [\fB--dump\fR]
+ [\fB--basedon\fR] [\fB--changelog\fR] [\fB-c,--configfiles\fR]
+ [\fB-d,--docfiles\fR] [\fB--dump\fR]
  [\fB--filesbypkg\fR] [\fB-i,--info\fR] [\fB--last\fR] [\fB-l,--list\fR]
  [\fB--provides\fR] [\fB--qf,--queryformat \fIQUERYFMT\fB\fR]
  [\fB-R,--requires\fR] [\fB--scripts\fR] [\fB-s,--state\fR]
@@ -547,6 +549,10 @@ that will be expanded to paths that are
 the package manifest as additional \fIPACKAGE_FILE\fR
 arguments to the query.
 .TP
+\fB-P, --patches\fP
+Limit the selected packages to patch-rpms. As a side effect, limit
+the file list to patched files.
+.TP
 \fB--pkgid \fIMD5\fB\fR
 Query package that contains a given package identifier, i.e. the
 \fIMD5\fR digest of the combined header and
@@ -581,6 +587,10 @@ Query all packages that requires \fICAPA
 .SS "PACKAGE QUERY OPTIONS:"
 .PP
 .TP
+\fB--basedon\fR
+Show what packages a patch-rpm is based on. A patch-rpm can only be
+installed if one of the packages it is based on is installed.
+.TP
 \fB--changelog\fR
 Display change information for the package.
 .TP
@@ -613,7 +623,8 @@ Orders the package listing by install ti
 packages are at the top.
 .TP
 \fB-l, --list\fR
-List files in package.
+List files in package. If the \fB\-P\fP option is also given, only
+patched files are shown.
 .TP
 \fB--provides\fR
 List capabilities this package provides.
Index: rpmpopt.in
===================================================================
--- rpmpopt.in.orig
+++ rpmpopt.in
@@ -84,6 +84,10 @@ rpm	alias --supplements	--qf \
   "[%|ENHANCESFLAGS:depflag_strong?{%{ENHANCESNAME} %{ENHANCESFLAGS:depflags} %{ENHANCESVERSION}\n}|]" \
 	--POPTdesc=$"list capabilities this package supplements"
 
+rpm	alias --basedon		--qf \
+  "[%{PATCHESNAME} %{PATCHESFLAGS:depflags} %{PATCHESVERSION}\n]" \
+	--POPTdesc=$"list packages this patch-rpm is based on"
+
 rpm	alias --info --qf 'Name        : %-27{NAME}  Relocations: %|PREFIXES?{[%{PREFIXES} ]}:{(not relocatable)}|\n\
 Version     : %-27{VERSION}       Vendor: %{VENDOR}\n\
 Release     : %-27{RELEASE}   Build Date: %{BUILDTIME:date}\n\
@@ -378,6 +382,10 @@ rpmq	alias --supplements	--qf \
   "[%|ENHANCESFLAGS:depflag_strong?{%{ENHANCESNAME} %{ENHANCESFLAGS:depflags} %{ENHANCESVERSION}\n}|]" \
 	--POPTdesc=$"list capabilities this package supplements"
 
+rpmq	alias --basedon		--qf \
+  "[%{PATCHESNAME} %{PATCHESFLAGS:depflags} %{PATCHESVERSION}\n]" \
+	--POPTdesc=$"list packages this patch-rpm is based on"
+
 rpmq	alias --info --qf 'Name        : %-27{NAME}  Relocations: %|PREFIXES?{[%{PREFIXES} ]}:{(not relocatable)}|\n\
 Version     : %-27{VERSION}       Vendor: %{VENDOR}\n\
 Release     : %-27{RELEASE}   Build Date: %{BUILDTIME:date}\n\
@@ -496,6 +504,10 @@ rpmquery	alias --supplements	--qf \
   "[%|ENHANCESFLAGS:depflag_strong?{%{ENHANCESNAME} %{ENHANCESFLAGS:depflags} %{ENHANCESVERSION}\n}|]" \
 	--POPTdesc=$"list capabilities this package supplements"
 
+rpmquery	alias --basedon		--qf \
+  "[%{PATCHESNAME} %{PATCHESFLAGS:depflags} %{PATCHESVERSION}\n]" \
+	--POPTdesc=$"list packages this patch-rpm is based on"
+
 rpmquery	alias --info --qf 'Name        : %-27{NAME}  Relocations: %|PREFIXES?{[%{PREFIXES} ]}:{(not relocatable)}|\n\
 Version     : %-27{VERSION}       Vendor: %{VENDOR}\n\
 Release     : %-27{RELEASE}   Build Date: %{BUILDTIME:date}\n\