File patchrpms.diff of Package rpm.SLE_11

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\