Added a pair of new things to the query format:

the '>'-test, ':nothing' format variant and
	implemented '-q --changes-since=<e:v-r>' upon them.
This commit is contained in:
Дмитрий Левин 2002-07-20 10:39:40 +00:00
parent 3e8d542527
commit de25d1f6d1
8 changed files with 335 additions and 75 deletions

View File

@ -133,6 +133,17 @@ name after the tag name. Here are some examples:
The :shescape may be used on plain strings to get a string which can pass
through a single level of shell and give the original string.
On ALT's systems, rpm accepts a special format for printing nothing instead
of the value of the tag. This can be particularly useful in combination with
iterating over arrays:
\begin{verbatim}
[%{filenames:nothing}i]
\end{verbatim}
will print an i for each file your package contains (so, you'll get the number
of files in the package in unary form).
\subsection queryformat_expressions Query Expressions
Simple conditionals may be evaluated through query expressions. Expressions
@ -148,6 +159,34 @@ the SOMETAG tag is present, and "missing" otherwise:
Notice that the subformats "present" and "missing" must be inside of curly
braces.
In rpm used on an ALT's system, there is also one more (non-trivial) test
expression which is accepted as the first operand of \verb!%| ?{}:{} |!. Here's an example:
\begin{verbatim}
%|%{SOME}expression>epoch:version-release?{ultra-fresh}:{pre-historic}|
\end{verbatim}
It's idea is to test whether an expression evaluates to a value which is
more fresh than a specified epoch:versiobn-release triple (in the sense
rpm compares E:V-Rs). Things get more complicated because of the special
use of this hack: it should be used to filter changelog entries, that
contain \verb!%{CHANGELOGNAME}! of the form:
\begin{verbatim}
John Smith <js@my-distro.com> 5.0.1-my6
\end{verbatim}
so the test extracts the part of the value that follows the email address,
and compares it to the specified epoch:version-release. (If there is no
email address, it takes the whole value, so you could use a query like this
\begin{verbatim}
%|2-1mdk>2-alt1?{mdk}:{alt}|
\end{verbatim}
to determine which of the releases is considered by rpm to be more fresh.)
\subsection queryformat_example Example: Viewing the Verify Flags
The follwing example query is run against dev becuase I know %verify

View File

@ -479,48 +479,7 @@ static void alMakeIndex(availableList al)
}
}
/**
* Split EVR into epoch, version, and release components.
* @param evr [epoch:]version[-release] string
* @retval *ep pointer to epoch
* @retval *vp pointer to version
* @retval *rp pointer to release
*/
static void parseEVR(char * evr,
/*@exposed@*/ /*@out@*/ const char ** ep,
/*@exposed@*/ /*@out@*/ const char ** vp,
/*@exposed@*/ /*@out@*/ const char ** rp)
/*@modifies *ep, *vp, *rp @*/
{
const char *epoch;
const char *version; /* assume only version is present */
const char *release;
char *s, *se;
s = evr;
while (*s && xisdigit(*s)) s++; /* s points to epoch terminator */
se = strrchr(s, '-'); /* se points to version terminator */
if (*s == ':') {
epoch = evr;
*s++ = '\0';
version = s;
if (*epoch == '\0') epoch = "0";
} else {
epoch = NULL; /* XXX disable epoch compare if missing */
version = evr;
}
if (se) {
*se++ = '\0';
release = se;
} else {
release = NULL;
}
if (ep) *ep = epoch;
if (vp) *vp = version;
if (rp) *rp = release;
}
/* parseEVR() moved to rpmvercmp.c */
const char *rpmNAME = PACKAGE;
const char *rpmEVR = VERSION;
@ -559,25 +518,8 @@ int rpmRangesOverlap(const char * AName, const char * AEVR, int AFlags,
parseEVR(aEVR, &aE, &aV, &aR);
bEVR = xstrdup(BEVR);
parseEVR(bEVR, &bE, &bV, &bR);
/* Compare {A,B} [epoch:]version[-release] */
sense = 0;
if (aE && *aE && bE && *bE)
sense = rpmvercmp(aE, bE);
else if (aE && *aE && atol(aE) > 0) {
/* XXX legacy epoch-less requires/conflicts compatibility */
rpmMessage(RPMMESS_DEBUG, _("the \"B\" dependency needs an epoch (assuming same as \"A\")\n\tA %s\tB %s\n"),
aDepend, bDepend);
sense = 0;
} else if (bE && *bE && atol(bE) > 0)
sense = -1;
if (sense == 0) {
sense = rpmvercmp(aV, bV);
if (sense == 0 && aR && *aR && bR && *bR) {
sense = rpmvercmp(aR, bR);
}
}
/* rpmEVRcmp() is also shared; the code moved to rpmvercmp.c */
sense = rpmEVRcmp(aE, aV, aR, aDepend, bE, bV, bR, bDepend);
aEVR = _free(aEVR);
bEVR = _free(bEVR);

View File

@ -299,6 +299,28 @@ static /*@only@*/ char * depflagsFormat(int_32 type, const void * data,
return val;
}
/**
* @param type tag type
* @param data tag value
* @param formatPrefix
* @param padding
* @param element (unused)
* @return formatted string
*/
static /*@only@*/ char * nothingFormat(int_32 type, const void * data,
char * formatPrefix, int padding, /*@unused@*/ int element)
/*@modifies formatPrefix @*/
{ /* based on depflagsFormat() code. */
char * val;
val = xmalloc(2 + padding);
strcat(formatPrefix, "s");
/*@-formatconst@*/
sprintf(val, formatPrefix, "");
/*@=formatconst@*/
return val;
}
/**
* @param h header
* @retval type address of tag type
@ -736,6 +758,7 @@ const struct headerSprintfExtension_s rpmHeaderFormats[] = {
{ HEADER_EXT_FORMAT, "perms", { permsFormat } },
{ HEADER_EXT_FORMAT, "permissions", { permsFormat } },
{ HEADER_EXT_FORMAT, "triggertype", { triggertypeFormat } },
{ HEADER_EXT_FORMAT, "nothing", { nothingFormat } },
{ HEADER_EXT_MORE, NULL, { (void *) headerDefaultFormats } }
} ;
/*@=type@*/

View File

@ -16,6 +16,9 @@
#include <header_internal.h>
/* We get parseEVR() from there used in parsing the >-test in conditionals: */
#include <rpmlib.h>
#include "debug.h"
/*@-redecl@*/ /* FIX: avoid rpmlib.h, need for debugging. */
@ -79,6 +82,8 @@ static int typeSizes[] = {
/*@observer@*/ /*@unchecked@*/
HV_t hdrVec; /* forward reference */
/* Already defined in <rpmlib.h> */
#if 0
/**
* Wrapper to free(3), hides const compilation noise, permit NULL, return NULL.
* @param p memory to free
@ -90,6 +95,7 @@ _free(/*@only@*/ /*@null@*/ /*@out@*/ const void * p) /*@modifies *p @*/
if (p != NULL) free((void *)p);
return NULL;
}
#endif
/** \ingroup header
* Reference a header instance.
@ -2065,6 +2071,23 @@ freeFormat( /*@only@*/ /*@null@*/ sprintfToken format, int num)
format[i].u.cond.elseFormat =
freeFormat(format[i].u.cond.elseFormat,
format[i].u.cond.numElseTokens);
switch (format[i].u.cond.test.type) {
case TRIVIAL:
/* the usual old way; format[i].test.u.tag is a struct */
break;
case StringTAG_String3:
/* the added option */
format[i].u.cond.test.u.tag_str3.headFormat =
freeFormat(format[i].u.cond.test.u.tag_str3.headFormat,
format[i].u.cond.test.u.tag_str3.numHeadTokens);
/* We don't need to free the strings; we have not allocated them.
And PTOK_STRING case is analogous and also doesn't free the string. */
break;
default:
/* report an error */
rpmMessage(RPMMESS_WARNING, _("Unknown test type in \%|?:|; perhaps some memory is leaking right now.\n"));
break;
}
/*@switchbreak@*/ break;
case PTOK_NONE:
case PTOK_TAG:
@ -2493,19 +2516,67 @@ static int parseExpression(sprintfToken token, char * str,
*endPtr = chptr;
findTag(str, tags, extensions, &tag, &ext);
{ /* branching between the trivial old test for the conditional and
the added test for EVR comparison. */
char * str2 = strchr(str,'>');
if (str2) {
char * endOfParsed = NULL;
rpmMessage(RPMMESS_WARNING, _("Parsing non-standard test (>) for \%|?{}:{}|.\n"));
*str2 ='\0';
++str2; /* str2 is the beginning of the second part: after the > sign. */
if (tag) {
token->u.cond.tag.ext = NULL;
token->u.cond.tag.tag = tag->val;
} else if (ext) {
token->u.cond.tag.ext = ext->u.tagFunction;
token->u.cond.tag.extNum = ext - extensions;
} else {
token->u.cond.tag.ext = NULL;
token->u.cond.tag.tag = -1;
if ( parseFormat(str, tags, extensions,
&token->u.cond.test.u.tag_str3.headFormat,
&token->u.cond.test.u.tag_str3.numHeadTokens,
&endOfParsed, PARSER_IN_EXPR, errmsg)
/* this doesn't work, NULL is returned: || ( endOfParsed != str2 ) */ )
{
/*@-observertrans -readonlytrans@*/
if (errmsg
&& ! *errmsg /* *errmsg was set to NULL at the function beginnging */ )
*errmsg = _("the left part of >-expr finished before the > sign");
/*@=observertrans =readonlytrans@*/
token->u.cond.ifFormat =
freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
token->u.cond.elseFormat =
freeFormat(token->u.cond.elseFormat, token->u.cond.numElseTokens);
return 1;
}
token->u.cond.test.type = StringTAG_String3;
token->u.cond.test.u.tag_str3.predicate = &isChangeNameMoreFresh;
parseEVR(str2,
&token->u.cond.test.u.tag_str3.tail[0],
&token->u.cond.test.u.tag_str3.tail[1],
&token->u.cond.test.u.tag_str3.tail[2]);
/* We could strdup tail[j], but it seems we don't need this: the rest of similar code
doesn't perform this. And we don't have to free them. */
rpmMessage(RPMMESS_DEBUG, "Will cmp with e=%s, v=%s, r=%s\n",
token->u.cond.test.u.tag_str3.tail[0],
token->u.cond.test.u.tag_str3.tail[1],
token->u.cond.test.u.tag_str3.tail[2]);
}
else {
struct sprintfTag head;
rpmMessage(RPMMESS_DEBUG, _("The usual way of parsing the test part for \%|?:|\n"));
findTag(str, tags, extensions, &tag, &ext);
if (tag) {
head.ext = NULL;
head.tag = tag->val;
} else if (ext) {
head.ext = ext->u.tagFunction;
head.extNum = ext - extensions;
} else {
head.ext = NULL;
head.tag = -1;
}
token->u.cond.test.type = TRIVIAL;
token->u.cond.test.u.tag = head;
}
}
token->type = PTOK_COND;
return 0;
@ -2774,14 +2845,46 @@ static char * singleSprintf(Header h, sprintfToken token,
break;
case PTOK_COND:
if (token->u.cond.tag.ext ||
headerIsEntry(h, token->u.cond.tag.tag)) {
{
int testResult = 0; /* false by default */
switch (token->u.cond.test.type) {
case TRIVIAL:
testResult = token->u.cond.test.u.tag.ext ||
headerIsEntry(h, token->u.cond.test.u.tag.tag);
break;
case StringTAG_String3:
{ /* this piece if code is based on headerSprintf() */
char * head = NULL; int head_en = 0;
int head_alloced = 0;
head = xstrdup("");
for (i = 0; i < token->u.cond.test.u.tag_str3.numHeadTokens; i++)
/* head_t = -- what do we need the return value for? headerSprintf() discards it. */
singleSprintf(h, token->u.cond.test.u.tag_str3.headFormat + i,
extensions, extCache,
element, &head, &head_en, &head_alloced);
if (head != NULL && head_en < head_alloced)
head = xrealloc(head, head_en+1);
testResult = head
&& token->u.cond.test.u.tag_str3.predicate(head, token->u.cond.test.u.tag_str3.tail);
/* Do we free all the alloced data heer? */
_free(head);
}
break;
default:
/* report an error */
rpmMessage(RPMMESS_WARNING, _("Unknown test type in \%|?:|; assuming false.\n"));
testResult = 0;
break;
}
if (testResult) {
condFormat = token->u.cond.ifFormat;
condNumFormats = token->u.cond.numIfTokens;
} else {
condFormat = token->u.cond.elseFormat;
condNumFormats = token->u.cond.numElseTokens;
}
}
need = condNumFormats * 20;
if (condFormat == NULL || need <= 0) break;

View File

@ -116,7 +116,19 @@ struct sprintfToken {
int numIfTokens;
/*@only@*/ /*@null@*/ sprintfToken elseFormat;
int numElseTokens;
struct sprintfTag tag;
struct {
enum { TRIVIAL, StringTAG_String3 } type;
union {
struct sprintfTag tag;
struct {
int (*predicate)(const char * head, const char * const tail[3]);
/* args */
sprintfToken headFormat;
int numHeadTokens;
const char * tail[3];
} tag_str3;
} u;
} test;
} cond;
} u;
};

View File

@ -1463,6 +1463,29 @@ int rpmGetRpmlibProvides(/*@null@*/ /*@out@*/ const char *** provNames,
int rpmvercmp(const char * a, const char * b)
/*@*/;
int
rpmEVRcmp(const char * const aE, const char * const aV, const char * const aR,
const char * const aDepend,
const char * const bE, const char * const bV, const char * const bR,
const char * const bDepend);
/**
* Split EVR into epoch, version, and release components.
* @param evr [epoch:]version[-release] string
* @retval *ep pointer to epoch
* @retval *vp pointer to version
* @retval *rp pointer to release
*/
void parseEVR(char * evr,
/*@exposed@*/ /*@out@*/ const char ** ep,
/*@exposed@*/ /*@out@*/ const char ** vp,
/*@exposed@*/ /*@out@*/ const char ** rp);
/*@modifies *ep, *vp, *rp @*/
/* A predicate: */
int isChangeNameMoreFresh(const char * const head,
const char * const tail[3]);
/** \ingroup rpmtrans
* Compare two versioned dependency ranges, looking for overlap.
* @param AName 1st dependncy name string

View File

@ -3,6 +3,7 @@
*/
#include "system.h"
#include <ctype.h> /* This should be done by configure and friends, I guess. */
#include "rpmlib.h"
@ -107,3 +108,112 @@ int rpmvercmp(const char * a, const char * b)
/* whichever version still has characters left over wins */
if (!*one) return -1; else return 1;
}
/* Moved from depends.c, because we use it in other places, too. */
/**
* Split EVR into epoch, version, and release components.
* @param evr [epoch:]version[-release] string
* @retval *ep pointer to epoch
* @retval *vp pointer to version
* @retval *rp pointer to release
*/
void parseEVR(char * evr,
/*@exposed@*/ /*@out@*/ const char ** ep,
/*@exposed@*/ /*@out@*/ const char ** vp,
/*@exposed@*/ /*@out@*/ const char ** rp)
/*@modifies *ep, *vp, *rp @*/
{
const char *epoch;
const char *version; /* assume only version is present */
const char *release;
char *s, *se;
s = evr;
while (*s && xisdigit(*s)) s++; /* s points to epoch terminator */
se = strrchr(s, '-'); /* se points to version terminator */
if (*s == ':') {
epoch = evr;
*s++ = '\0';
version = s;
if (*epoch == '\0') epoch = "0";
} else {
epoch = NULL; /* XXX disable epoch compare if missing */
version = evr;
}
if (se) {
*se++ = '\0';
release = se;
} else {
release = NULL;
}
if (ep) *ep = epoch;
if (vp) *vp = version;
if (rp) *rp = release;
}
/* Compare {A,B} [epoch:]version[-release] */
int
rpmEVRcmp(const char * const aE, const char * const aV, const char * const aR,
const char * const aDepend,
const char * const bE, const char * const bV, const char * const bR,
const char * const bDepend)
{
int sense = 0;
rpmMessage(RPMMESS_DEBUG, "cmp e=%s, v=%s, r=%s\n and e=%s, v=%s, r=%s\n ",
aE, aV, aR, bE, bV, bR);
if (aE && *aE && bE && *bE)
sense = rpmvercmp(aE, bE);
else if (aE && *aE && atol(aE) > 0) {
/* XXX legacy epoch-less requires/conflicts compatibility */
rpmMessage(RPMMESS_DEBUG, _("the \"B\" dependency needs an epoch (assuming same as \"A\")\n\tA %s\tB %s\n"),
aDepend, bDepend);
sense = 0;
} else if (bE && *bE && atol(bE) > 0)
sense = -1;
if (sense == 0) {
sense = rpmvercmp(aV, bV);
if (sense == 0 && aR && *aR && bR && *bR) {
sense = rpmvercmp(aR, bR);
}
}
return sense;
}
int isChangeNameMoreFresh(const char * const head,
const char * const tail[3])
{
int result;
const char * evr[3];
const char * wordAfterEmail;
char * copy;
rpmMessage(RPMMESS_DEBUG, "test: is '%s' more fresh than e=%s, v=%s, r=%s?\n",
head, tail[0], tail[1], tail[2]);
/* find the next to <email> word begin */
if ( (wordAfterEmail = strrchr(head, '>')) )
++wordAfterEmail;
else
wordAfterEmail = head;
while ( *wordAfterEmail && isspace(*wordAfterEmail) )
++wordAfterEmail;
/* found. */
copy = xstrdup(wordAfterEmail);
parseEVR(copy, &evr[0], &evr[1], &evr[2]);
/* The order of two argument groups is important:
if evr[] (passed as B on the second place) has no epoch,
rpmEVRcmp() assumes the same as in tail[];
This fits our needs: the epoch may be omitted in a changelog entry (evr[])
but there are no problems in specifying it in the format (tail[]). */
result = rpmEVRcmp(tail[0], tail[1], tail[2], "",
evr[0], evr[1], evr[2], "") < 0;
_free(copy);
return result;
}

View File

@ -73,6 +73,10 @@ rpm alias --changelog -q --qf '[* %{CHANGELOGTIME:day} %{CHANGELOGNAME}\n\n%{CHA
--POPTdesc=$"list change logs for this package"
rpm alias --lastchange -q --qf '* %{CHANGELOGTIME:day} %{CHANGELOGNAME}\n\n%{CHANGELOGTEXT}\n' \
--POPTdesc=$"output last changelog entry for this package"
rpm alias --changes-since -q --qf '[%{CHANGELOGNAME:nothing}\
%|%{CHANGELOGNAME}>!#:+?{* %{CHANGELOGTIME:day} %{CHANGELOGNAME}\n\n%{CHANGELOGTEXT}\n\n}:{}|]' \
--POPTdesc=$"list change logs for this package that are fresher than <e:ver-rel> (as indicated by the word next to the packager's email)" \
--POPTargs=$"<option>"
rpm alias --triggerscripts -q --qf '\
[trigger%{TRIGGERTYPE} script (through %{TRIGGERSCRIPTPROG}) -- %{TRIGGERCONDS}\n\
@ -341,6 +345,10 @@ rpmq alias --changelog -q --qf '[* %{CHANGELOGTIME:day} %{CHANGELOGNAME}\n\n%{CH
--POPTdesc=$"list change logs for this package"
rpmq alias --lastchange -q --qf '* %{CHANGELOGTIME:day} %{CHANGELOGNAME}\n\n%{CHANGELOGTEXT}\n' \
--POPTdesc=$"output last changelog entry for this package"
rpmq alias --changes-since -q --qf '[%{CHANGELOGNAME:nothing}\
%|%{CHANGELOGNAME}>!#:+?{* %{CHANGELOGTIME:day} %{CHANGELOGNAME}\n\n%{CHANGELOGTEXT}\n\n}:{}|]' \
--POPTdesc=$"list change logs for this package that are fresher than <e:ver-rel> (as indicated by the word next to the packager's email)" \
--POPTargs=$"<option>"
rpmq alias --triggerscripts -q --qf '\
[trigger%{TRIGGERTYPE} script (through %{TRIGGERSCRIPTPROG}) -- %{TRIGGERCONDS}\n\