rpm  5.4.14
rpmevr.c
Go to the documentation of this file.
1 
4 #include "system.h"
5 
6 #include <rpmiotypes.h>
7 #include <rpmmacro.h>
8 #define _MIRE_INTERNAL
9 #include <mire.h>
10 
11 #include <rpmlog.h>
12 #include <set.h>
13 
14 #include <rpmtag.h>
15 #define _RPMEVR_INTERNAL
16 #include <rpmevr.h>
17 
18 #include "debug.h"
19 
20 #ifdef __cplusplus
21 GENfree(EVR_t)
22 #endif /* __cplusplus */
23 
24 /*@unchecked@*/
25 int _rpmevr_debug = 0;
26 
27 #if !defined(MAX)
28 #define MAX(x, y) ( ((x)>(y))?(x):(y) )
29 #endif
30 
31 EVR_t rpmEVRnew(uint32_t Flags, int initialize)
32 {
33  EVR_t evr = (EVR_t) xcalloc(1, sizeof(*evr));
34  evr->Flags = (evrFlags) Flags;
35  if (initialize) {
36 /*@-observertrans -readonlytrans @*/
37  evr->F[RPMEVR_E] = "0";
38  evr->F[RPMEVR_V] = "";
39  evr->F[RPMEVR_T] = "";
40  evr->F[RPMEVR_R] = "";
41  evr->F[RPMEVR_D] = "";
42 /*@=observertrans =readonlytrans @*/
43  }
44  return evr;
45 }
46 
48 {
49  if (evr != NULL) {
50  evr->str = _free(evr->str);
51  memset(evr, 0, sizeof(*evr));
52  evr = _free(evr);
53  }
54  return NULL;
55 }
56 
57 /* XXX Force digits to beat alphas. See bugzilla #50977. */
58 /*@unchecked@*/
59 #if defined(RPM_VENDOR_MANDRIVA) || defined(RPMVERCMP_DIGITS_BEAT_ALPHA) /* old-comparision-behaviour */
60 static int _invert_digits_alphas_comparison = -1;
61 #else
62 static int _invert_digits_alphas_comparison = 1;
63 #endif
64 
65 /* XXX Punctuation characters that are not treated as alphas */
66 /*@unchecked@*/ /*@observer@*/
67 static const char * _rpmnotalpha = ".:-";
68 
74 static inline int xisrpmalpha(int c)
75  /*@*/
76 {
77  int rc = xisalpha(c);
78  if (!rc)
79  rc = xispunct(c);
80  if (rc && _rpmnotalpha && *_rpmnotalpha)
81  rc = (strchr(_rpmnotalpha, c) == NULL);
82 /*@-globstate@*/
83  return rc;
84 /*@=globstate@*/
85 }
86 
87 int rpmEVRcmp(const char * a, const char * b)
88  /*@*/
89 {
90  const char * ae = NULL, * be = NULL;
91  int rc = 0; /* assume equal */
92 
93 assert(a != NULL);
94 assert(b != NULL);
95  /* Compare version strings segment by segment. */
96  for (; *a && *b && rc == 0; a = ae, b = be) {
97 
98  /* Skip leading non-alpha, non-digit characters. */
99  while (*a && !(xisdigit((int)*a) || xisrpmalpha((int)*a))) a++;
100  while (*b && !(xisdigit((int)*b) || xisrpmalpha((int)*b))) b++;
101 
102  /* Wildcard comparison? */
103  /* Note: limited to suffix-only wildcard matching at the moment. */
104  if (a[0] == '*' && a[1] == '\0') {
105  be = strchr(b, '\0'); /* XXX be = b + strlen(b); */
106  } else
107  if (b[0] == '*' && b[1] == '\0') {
108  ae = strchr(a, '\0'); /* XXX ae = a + strlen(a); */
109  } else
110  /* Digit string comparison? */
111  if (xisdigit((int)*a) || xisdigit((int)*b)) {
112  /* Discard leading zeroes. */
113  while (a[0] == '0' && xisdigit((int)a[1])) a++;
114  while (b[0] == '0' && xisdigit((int)b[1])) b++;
115 
116  /* Find end of digit strings. */
117  ae = a; while (xisdigit((int)*ae)) ae++;
118  be = b; while (xisdigit((int)*be)) be++;
119 
120  /* Calculate digit comparison return code. */
121  if (a == ae || b == be)
122  rc = (int)(*a - *b) * _invert_digits_alphas_comparison;
123  else {
124  rc = (ae - a) - (be - b);
125  if (!rc)
126  rc = strncmp(a, b, (ae - a));
127  }
128  } else {
129  /* Find end of alpha strings. */
130  ae = a; while (xisrpmalpha((int)*ae)) ae++;
131  be = b; while (xisrpmalpha((int)*be)) be++;
132 
133  /* Calculate alpha comparison return code. */
134  rc = strncmp(a, b, MAX((ae - a), (be - b)));
135  }
136  }
137 
138  /* Longer string wins. */
139  if (!rc)
140  rc = (int)(*a - *b);
141 
142  /* Force strict -1, 0, 1 return. */
143  rc = (rc > 0 ? 1
144  : rc < 0 ? -1
145  : 0);
146  return rc;
147 }
148 
149 /*@unchecked@*/ /*@observer@*/ /*@null@*/
150 static const char _evr_tuple_match[] =
151  "^(?:([^:-]+):)?([^:-]+)(?:-([^:-]+))?(?::([^:-]+))?$";
152 /*@unchecked@*/ /*@only@*/ /*@observer@*/ /*@null@*/
153 const char * evr_tuple_match = NULL;
154 /*@unchecked@*/ /*@refcounted@*/ /*@null@*/
156 
157 static miRE rpmEVRmire(void)
158  /*@*/
159 {
160 /*@-globs -internalglobs -mods @*/
161  if (evr_tuple_mire == NULL) {
162  int xx;
163  evr_tuple_match = rpmExpand("%{?evr_tuple_match}", NULL);
164  if (evr_tuple_match == NULL || evr_tuple_match[0] == '\0') {
165  evr_tuple_match = _free(evr_tuple_match); /* XXX coverity #1035894 */
166  evr_tuple_match = xstrdup(_evr_tuple_match);
167  }
168 
169  evr_tuple_mire = mireNew(RPMMIRE_REGEX, 0);
170  xx = mireSetCOptions(evr_tuple_mire, RPMMIRE_REGEX, 0, 0, NULL);
171  xx = mireRegcomp(evr_tuple_mire, evr_tuple_match);
172 
173  }
174 /*@=globs =internalglobs =mods @*/
175 assert(evr_tuple_match != NULL && evr_tuple_mire != NULL);
176 /*@-globstate -retalias@*/
177  return evr_tuple_mire;
178 /*@=globstate =retalias@*/
179 }
180 
181 int rpmEVRparse(const char * evrstr, EVR_t evr)
182  /*@modifies evrstr, evr @*/
183 {
184  miRE mire = rpmEVRmire();
185  int noffsets = 6 * 3;
186  int offsets[6 * 3];
187  size_t nb;
188  int xx;
189  int i;
190 
191  memset(evr, 0, sizeof(*evr));
192  evr->str = xstrdup(evrstr);
193  nb = strlen(evr->str);
194 
195  memset(offsets, -1, sizeof(offsets));
196  xx = mireSetEOptions(mire, offsets, noffsets);
197 
198  xx = mireRegexec(mire, evr->str, strlen(evr->str));
199 
200  for (i = 0; i < noffsets; i += 2) {
201  int ix;
202 
203  if (offsets[i] < 0)
204  continue;
205 
206  switch (i/2) {
207  default:
208  case 0: continue; /*@notreached@*/ /*@switchbreak@*/ break;
209  case 1: ix = RPMEVR_E; /*@switchbreak@*/break;
210  case 2: ix = RPMEVR_V; /*@switchbreak@*/break;
211  case 3: ix = RPMEVR_T; /*@switchbreak@*/break;
212  case 4: ix = RPMEVR_R; /*@switchbreak@*/break;
213  case 5: ix = RPMEVR_D; /*@switchbreak@*/break;
214  }
215 
216 assert(offsets[i ] >= 0 && offsets[i ] <= (int)nb);
217 assert(offsets[i+1] >= 0 && offsets[i+1] <= (int)nb);
218  { char * te = (char *) evr->str;
219  evr->F[ix] = te + offsets[i];
220  te += offsets[i+1];
221  *te = '\0';
222  }
223 
224  }
225 
226  /* XXX HACK: postpone committing to single "missing" value for now. */
227 /*@-observertrans -readonlytrans@*/
228  if (evr->F[RPMEVR_E] == NULL) evr->F[RPMEVR_E] = "0";
229  if (evr->F[RPMEVR_V] == NULL) evr->F[RPMEVR_V] = "";
230  if (evr->F[RPMEVR_T] == NULL) evr->F[RPMEVR_T] = "";
231  if (evr->F[RPMEVR_R] == NULL) evr->F[RPMEVR_R] = "";
232  if (evr->F[RPMEVR_D] == NULL) evr->F[RPMEVR_D] = "";
233 /*@=observertrans =readonlytrans@*/
234 
235  evr->Elong = strtoul(evr->F[RPMEVR_E], NULL, 10);
236 
237  xx = mireSetEOptions(mire, NULL, 0);
238 
239  return 0;
240 }
241 
248 static int compare_values(const char *a, const char *b)
249  /*@*/
250 {
251  return rpmvercmp(a, b);
252 }
253 
254 /*@unchecked@*/ /*@observer@*/ /*@null@*/
255 static const char _evr_tuple_order[] = "EVR";
256 /*@unchecked@*/ /*@only@*/ /*@observer@*/ /*@null@*/
257 static const char * evr_tuple_order = NULL;
258 
263 /*@observer@*/
264 static const char * rpmEVRorder(void)
265  /*@*/
266 {
267 /*@-globs -internalglobs -mods @*/
268  if (evr_tuple_order == NULL) {
269  evr_tuple_order = rpmExpand("%{?evr_tuple_order}", NULL);
270  if (evr_tuple_order == NULL || evr_tuple_order[0] == '\0') {
271  evr_tuple_order = _free(evr_tuple_order); /* XXX coverity #1035895 */
272  evr_tuple_order = xstrdup(_evr_tuple_order);
273  }
274  }
275 /*@=globs =internalglobs =mods @*/
276 assert(evr_tuple_order != NULL && evr_tuple_order[0] != '\0');
277  return evr_tuple_order;
278 }
279 
280 int rpmEVRcompare(const EVR_t a, const EVR_t b)
281 {
282  const char * s;
283  int rc = 0;
284 
285 assert(a->F[RPMEVR_E] != NULL);
286 assert(a->F[RPMEVR_V] != NULL);
287 assert(a->F[RPMEVR_T] != NULL);
288 assert(a->F[RPMEVR_R] != NULL);
289 assert(a->F[RPMEVR_D] != NULL);
290 assert(b->F[RPMEVR_E] != NULL);
291 assert(b->F[RPMEVR_V] != NULL);
292 assert(b->F[RPMEVR_T] != NULL);
293 assert(b->F[RPMEVR_R] != NULL);
294 assert(b->F[RPMEVR_D] != NULL);
295 
296  for (s = rpmEVRorder(); *s != '\0'; s++) {
297  int ix = 0;
298 
299  switch ((int)*s) {
300  default: continue; /*@notreached@*/ /*@switchbreak@*/break;
301  case 'E': ix = RPMEVR_E; /*@switchbreak@*/break;
302  case 'V': ix = RPMEVR_V; /*@switchbreak@*/break;
303  case 'T': ix = RPMEVR_T; /*@switchbreak@*/break;
304  case 'R': ix = RPMEVR_R; /*@switchbreak@*/break;
305  case 'D': ix = RPMEVR_D; /*@switchbreak@*/break;
306  }
307 #if defined(RPM_VENDOR_MANDRIVA) /* mdvbz#55810 */
308  if (ix >= RPMEVR_R && (b->Flags & (~RPMSENSE_GREATER & RPMSENSE_EQUAL))
309  && !(ix == RPMEVR_D && (b->Flags & RPMSENSE_LESS))
310  && *(b->F[ix]) == '\0')
311  break;
312 #endif
313 
314  /* XXX ALT version-set comparison */
315  if (ix == RPMEVR_V
316  && !strncmp(a->F[ix], "set:", sizeof("set:")-1)
317  && !strncmp(b->F[ix], "set:", sizeof("set:")-1))
318  {
319  rc = rpmsetCmp(a->F[ix], b->F[ix]);
320  if (rc < -1) {
321  if (rc == -3)
322  rpmlog(RPMLOG_WARNING, _("failed to decode %s\n"), a->F[ix]);
323  if (rc == -4)
324  rpmlog(RPMLOG_WARNING, _("failed to decode %s\n"), b->F[ix]);
325  /* neither is subset of each other */
326  rc = 0;
327  }
328  } else
329  if (ix == RPMEVR_T) /* XXX twiddle-in-version "negative" compare */
330  rc = -compare_values(a->F[ix], b->F[ix]);
331  else
332  rc = compare_values(a->F[ix], b->F[ix]);
333  if (rc)
334  break;
335  }
336  return rc;
337 }
338 
340 {
341  rpmsenseFlags aF = a->Flags;
342  rpmsenseFlags bF = b->Flags;
343  int sense;
344  int result;
345 
346  /* XXX HACK: postpone committing to single "missing" value for now. */
347 /*@-mods -observertrans -readonlytrans @*/
348  if (a->F[RPMEVR_E] == NULL) a->F[RPMEVR_E] = "0";
349  if (b->F[RPMEVR_E] == NULL) b->F[RPMEVR_E] = "0";
350  if (a->F[RPMEVR_V] == NULL) a->F[RPMEVR_V] = "";
351  if (b->F[RPMEVR_V] == NULL) b->F[RPMEVR_V] = "";
352  if (a->F[RPMEVR_T] == NULL) a->F[RPMEVR_T] = "";
353  if (b->F[RPMEVR_T] == NULL) b->F[RPMEVR_T] = "";
354  if (a->F[RPMEVR_R] == NULL) a->F[RPMEVR_R] = "";
355  if (b->F[RPMEVR_R] == NULL) b->F[RPMEVR_R] = "";
356  if (a->F[RPMEVR_D] == NULL) a->F[RPMEVR_D] = "";
357  if (b->F[RPMEVR_D] == NULL) b->F[RPMEVR_D] = "";
358 /*@=mods =observertrans =readonlytrans @*/
359  sense = rpmEVRcompare(a, b);
360 
361  /* Detect overlap of {A,B} range. */
362  if (aF == RPMSENSE_NOTEQUAL || bF == RPMSENSE_NOTEQUAL)
363  result = (sense != 0);
364  else if (sense < 0 && ((aF & RPMSENSE_GREATER) || (bF & RPMSENSE_LESS)))
365  result = 1;
366  else if (sense > 0 && ((aF & RPMSENSE_LESS) || (bF & RPMSENSE_GREATER)))
367  result = 1;
368  else if (sense == 0 &&
369  (((aF & RPMSENSE_EQUAL) && (bF & RPMSENSE_EQUAL)) ||
370  ((aF & RPMSENSE_LESS) && (bF & RPMSENSE_LESS)) ||
371  ((aF & RPMSENSE_GREATER) && (bF & RPMSENSE_GREATER))))
372  result = 1;
373  else
374  result = 0;
375  return result;
376 }
377 
378 /*@-redecl@*/
379 int (*rpmvercmp) (const char *a, const char *b) = rpmEVRcmp;
380 /*@=redecl@*/
381 
384 /*@unchecked@*/ /*@observer@*/
385 static struct EVRop_s {
386 /*@observer@*/ /*@null@*/
387  const char * opstr;
389 } cops[] = {
392 
393  { "==", (rpmsenseFlags) (RPMSENSE_EQUAL) },
394  { "!=", (rpmsenseFlags) (RPMSENSE_NOTEQUAL) },
395 
398 
399  { "<", (rpmsenseFlags) (RPMSENSE_LESS) },
400  { "=", (rpmsenseFlags) (RPMSENSE_EQUAL) },
401  { ">", (rpmsenseFlags) (RPMSENSE_GREATER) },
402 
403  { NULL, (rpmsenseFlags) 0 },
404 };
405 
406 rpmsenseFlags rpmEVRflags(const char *op, const char **end)
407 {
409  struct EVRop_s *cop;
410 
411  if (op == NULL || *op == '\0')
412  Flags = RPMSENSE_EQUAL;
413  else
414  for (cop = cops; cop->opstr != NULL; cop++) {
415  if (strncmp(op, cop->opstr, strlen(cop->opstr)))
416  continue;
417  Flags = cop->sense;
418  if (end)
419  *end = op + strlen(cop->opstr);
420  break;
421  }
422  return Flags;
423 }
424 
426 {
427  HE_t Ahe = (HE_t) memset(alloca(sizeof(*Ahe)), 0, sizeof(*Ahe));
428  HE_t Bhe = (HE_t) memset(alloca(sizeof(*Bhe)), 0, sizeof(*Bhe));
429  const char * one, * two;
430  rpmuint32_t Eone, Etwo;
431  const char * s;
432  int rc = 0;
433  int xx;
434 
435  for (s = rpmEVRorder(); *s != '\0'; s++) {
436  switch ((int)*s) {
437  default: continue; /*@notreached@*/ /*@switchbreak@*/break;
438  case 'E':
439  Ahe->tag = RPMTAG_EPOCH;
440  xx = headerGet(A, Ahe, 0);
441  Eone = (xx && Ahe->p.ui32p ? Ahe->p.ui32p[0] : 0);
442  Bhe->tag = RPMTAG_EPOCH;
443  xx = headerGet(B, Bhe, 0);
444  Etwo = (xx && Bhe->p.ui32p ? Bhe->p.ui32p[0] : 0);
445  if (Eone < Etwo)
446  rc = -1;
447  else if (Eone > Etwo)
448  rc = 1;
449  /*@switchbreak@*/ break;
450  case 'V':
451  Ahe->tag = RPMTAG_VERSION;
452  xx = headerGet(A, Ahe, 0);
453  one = (xx && Ahe->p.str ? Ahe->p.str : "");
454  Bhe->tag = RPMTAG_VERSION;
455  xx = headerGet(B, Bhe, 0);
456  two = (xx && Bhe->p.str ? Bhe->p.str : "");
457  rc = rpmvercmp(one, two);
458  /*@switchbreak@*/ break;
459  case 'R':
460  Ahe->tag = RPMTAG_RELEASE;
461  xx = headerGet(A, Ahe, 0);
462  one = (xx && Ahe->p.str ? Ahe->p.str : "");
463  Bhe->tag = RPMTAG_RELEASE;
464  xx = headerGet(B, Bhe, 0);
465  two = (xx && Bhe->p.str ? Bhe->p.str : "");
466  rc = rpmvercmp(one, two);
467  /*@switchbreak@*/ break;
468  case 'D':
469  Ahe->tag = RPMTAG_DISTEPOCH;
470  xx = headerGet(A, Ahe, 0);
471  one = (xx && Ahe->p.str ? Ahe->p.str : "");
472  Bhe->tag = RPMTAG_DISTEPOCH;
473  xx = headerGet(B, Bhe, 0);
474  two = (xx && Bhe->p.str ? Bhe->p.str : "");
475  rc = rpmvercmp(one, two);
476  /*@switchbreak@*/ break;
477  }
478  Ahe->p.ptr = _free(Ahe->p.ptr);
479  Bhe->p.ptr = _free(Bhe->p.ptr);
480  if (rc)
481  break;
482  }
483  return rc;
484 }
int rpmEVRcompare(const EVR_t a, const EVR_t b)
Compare EVR containers for equality.
Definition: rpmevr.c:280
const char * str
Definition: rpmtag.h:73
static int compare_values(const char *a, const char *b)
Dressed rpmEVRcmp, handling missing values.
Definition: rpmevr.c:248
int xx
Definition: spec.c:744
rpmTag tag
Definition: rpmtag.h:501
miRE evr_tuple_mire
Definition: poptALL.c:547
int mireSetEOptions(miRE mire, int *offsets, int noffsets)
Initialize pattern execute options (PCRE only).
Definition: mire.c:156
const char * evr_tuple_match
Definition: poptALL.c:546
miRE mireNew(rpmMireMode mode, int tag)
Create pattern container.
Definition: mire.c:113
rpmlog(RPMLOG_ERR,"%s\n", buf)
DBT * A
Definition: db3.c:1890
static const char * _rpmnotalpha
Definition: rpmevr.c:67
#define MAX(x, y)
Definition: rpmevr.c:28
char * xstrdup(const char *str)
Definition: rpmmalloc.c:321
rpmuint32_t * ui32p
Definition: rpmtag.h:70
static char *size_t nb
fgets(3) analogue that reads \ continuations.
Definition: macro.c:409
int rc
Definition: poptALL.c:670
The Header data structure.
int mireRegcomp(miRE mire, const char *pattern)
Compile pattern match.
Definition: mire.c:332
int mireSetCOptions(miRE mire, rpmMireMode mode, int tag, int options, const unsigned char *table)
Initialize pattern compile options.
Definition: mire.c:121
static miRE rpmEVRmire(void)
Definition: rpmevr.c:157
static const char _evr_tuple_order[]
Definition: rpmevr.c:255
int rpmEVRcmp(const char *a, const char *b)
Segmented string compare.
Definition: rpmevr.c:87
static int xisalpha(int c)
Definition: rpmiotypes.h:434
struct EVR_s * EVR_t
Definition: rpmevr.h:22
EVR_t rpmEVRnew(uint32_t Flags, int initialize)
Create a new EVR container.
Definition: rpmevr.c:31
const char * evr_tuple_order
Definition: poptALL.c:545
pid_t result
Definition: rpmsq.c:737
rpmsenseFlags rpmEVRflags(const char *op, const char **end)
Return comparison operator sense flags.
Definition: rpmevr.c:406
int rpmEVRparse(const char *evrstr, EVR_t evr)
Split EVR string into epoch, version, and release components.
Definition: rpmevr.c:181
enum evrFlags_e evrFlags
Dependency Attributes.
char * alloca()
#define RPMSENSE_NOTEQUAL
Definition: rpmevr.h:78
Yet Another syslog(3) API clone.
memset(_r, 0, sizeof(*_r))
unsigned int rpmuint32_t
Definition: rpmiotypes.h:28
static int xisrpmalpha(int c)
Return rpm&#39;s analogue of xisalpha.
Definition: rpmevr.c:74
void * xcalloc(size_t nmemb, size_t size)
Definition: rpmmalloc.c:300
assert(key->size==sizeof(hdrNum))
void * ptr
Definition: rpmtag.h:67
int ix
Definition: rpmps-py.c:174
int(* rpmvercmp)(const char *a, const char *b)
Segmented string compare vector.
Definition: rpmevr.c:379
RPM pattern matching.
enum evrFlags_e rpmsenseFlags
Definition: rpmevr.h:74
rpmTagData p
Definition: rpmtag.h:504
struct miRE_s * miRE
Definition: mire.h:60
struct _HE_s * HE_t
Destroy an extension cache.
Definition: rpmtag.h:59
int headerGet(Header h, HE_t he, unsigned int flags)
Retrieve extension or tag value from a header.
Definition: header.c:2230
int mireRegexec(miRE mire, const char *val, size_t vallen)
Execute pattern match.
Definition: mire.c:396
char * rpmExpand(const char *arg,...)
Return (malloc&#39;ed) concatenated macro expansion(s).
Definition: macro.c:3178
static struct EVRop_s cops[]
rpmsenseFlags sense
Definition: rpmevr.c:388
* op
Definition: rpmps-py.c:266
int rpmEVRoverlap(EVR_t a, EVR_t b)
Compare EVR containers for overlap.
Definition: rpmevr.c:339
static const char *char c
Return text between pl and matching pr characters.
Definition: macro.c:470
Definition: rpmtag.h:500
EVR_t rpmEVRfree(EVR_t evr)
Destroy an EVR container.
Definition: rpmevr.c:47
const char * s
Definition: poptALL.c:734
static int xisdigit(int c)
Definition: rpmiotypes.h:437
static int _invert_digits_alphas_comparison
Definition: rpmevr.c:62
char * be
Definition: macro.c:746
te
Definition: macro.c:552
const char * opstr
Definition: rpmevr.c:387
return NULL
Definition: poptALL.c:613
static const char _evr_tuple_match[]
Definition: rpmevr.c:150
static const char * rpmEVRorder(void)
Return precedence permutation string.
Definition: rpmevr.c:264
#define _(Text)
Definition: system.h:29
char * b
Definition: macro.c:746
int
Save source and expand field into target.
Definition: rpmds.c:2709
static void * _free(const void *p)
Wrapper to free(3), hides const compilation noise, permit NULL, return NULL.
Definition: rpmiotypes.h:647
evrFlags Flags
Definition: rpmds.c:2717
int i
Definition: spec.c:743
int _rpmevr_debug
Definition: rpmevr.c:25
int rpmVersionCompare(Header A, Header B)
Definition: rpmevr.c:425
static int xispunct(int c)
Definition: rpmiotypes.h:461