- stupid macros to configure public key file paths.
[platform/upstream/rpm.git] / lib / misc.c
1 /**
2  * \file lib/misc.c
3  */
4
5 #include "system.h"
6
7 /*@unchecked@*/
8 static int _debug = 0;
9
10 #include "rpmio_internal.h"
11 #include <rpmurl.h>
12 #include <rpmmacro.h>   /* XXX for rpmGetPath */
13 #include <rpmlib.h>
14
15 #include "misc.h"
16 #include "debug.h"
17
18 /*@access Header@*/             /* XXX compared with NULL */
19 /*@access FD_t@*/               /* XXX compared with NULL */
20
21 int domd5(const char * fn, unsigned char * digest, int asAscii)
22 {
23     int rc;
24
25     FD_t fd = Fopen(fn, "r.ufdio");
26     unsigned char buf[BUFSIZ];
27     unsigned char * md5sum = NULL;
28     size_t md5len;
29
30     if (fd == NULL || Ferror(fd)) {
31         if (fd)
32             (void) Fclose(fd);
33         return 1;
34     }
35
36     fdInitDigest(fd, PGPHASHALGO_MD5, 0);
37
38     while ((rc = Fread(buf, sizeof(buf[0]), sizeof(buf), fd)) > 0)
39         {};
40     fdFiniDigest(fd, PGPHASHALGO_MD5, (void **)&md5sum, &md5len, asAscii);
41
42     if (Ferror(fd))
43         rc = 1;
44     (void) Fclose(fd);
45
46     if (!rc)
47         memcpy(digest, md5sum, md5len);
48     md5sum = _free(md5sum);
49
50     return rc;
51 }
52
53 /*@-exportheadervar@*/
54 /* just to put a marker in librpm.a */
55 /*@unchecked@*/
56 /*@unused@*/ /*@observer@*/ char * RPMVERSION = VERSION;
57 /*@=exportheadervar@*/
58
59 char ** splitString(const char * str, int length, char sep)
60 {
61     const char * source;
62     char * s, * dest;
63     char ** list;
64     int i;
65     int fields;
66
67     s = xmalloc(length + 1);
68
69     fields = 1;
70     for (source = str, dest = s, i = 0; i < length; i++, source++, dest++) {
71         *dest = *source;
72         if (*dest == sep) fields++;
73     }
74
75     *dest = '\0';
76
77     list = xmalloc(sizeof(*list) * (fields + 1));
78
79     dest = s;
80     list[0] = dest;
81     i = 1;
82     while (i < fields) {
83         if (*dest == sep) {
84             list[i++] = dest + 1;
85             *dest = 0;
86         }
87         dest++;
88     }
89
90     list[i] = NULL;
91
92     return list;
93 }
94
95 void freeSplitString(char ** list)
96 {
97     /*@-unqualifiedtrans@*/
98     list[0] = _free(list[0]);
99     /*@=unqualifiedtrans@*/
100     list = _free(list);
101 }
102
103 int doputenv(const char *str)
104 {
105     char * a;
106
107     /* FIXME: this leaks memory! */
108     a = xmalloc(strlen(str) + 1);
109     strcpy(a, str);
110     return putenv(a);
111 }
112
113 int dosetenv(const char * name, const char * value, int overwrite)
114 {
115     char * a;
116
117     if (!overwrite && getenv(name)) return 0;
118
119     /* FIXME: this leaks memory! */
120     a = xmalloc(strlen(name) + strlen(value) + sizeof("="));
121     (void) stpcpy( stpcpy( stpcpy( a, name), "="), value);
122     return putenv(a);
123 }
124
125 static int rpmMkpath(const char * path, mode_t mode, uid_t uid, gid_t gid)
126         /*@globals fileSystem @*/
127         /*@modifies fileSystem @*/
128 {
129     char * d, * de;
130     int created = 0;
131     int rc;
132
133     if (path == NULL)
134         return -1;
135     d = alloca(strlen(path)+2);
136     de = stpcpy(d, path);
137     de[1] = '\0';
138     for (de = d; *de != '\0'; de++) {
139         struct stat st;
140         char savec;
141
142         while (*de && *de != '/') de++;
143         savec = de[1];
144         de[1] = '\0';
145
146         rc = stat(d, &st);
147         if (rc) {
148             switch(errno) {
149             default:
150                 return errno;
151                 /*@notreached@*/ /*@switchbreak@*/ break;
152             case ENOENT:
153                 /*@switchbreak@*/ break;
154             }
155             rc = mkdir(d, mode);
156             if (rc)
157                 return errno;
158             created = 1;
159             if (!(uid == (uid_t) -1 && gid == (gid_t) -1)) {
160                 rc = chown(d, uid, gid);
161                 if (rc)
162                     return errno;
163             }
164         } else if (!S_ISDIR(st.st_mode)) {
165             return ENOTDIR;
166         }
167         de[1] = savec;
168     }
169     rc = 0;
170     if (created)
171         rpmMessage(RPMMESS_WARNING, "created %%_tmppath directory %s\n", path);
172     return rc;
173 }
174
175 int makeTempFile(const char * prefix, const char ** fnptr, FD_t * fdptr)
176 {
177     const char * tpmacro = "%{?_tmppath:%{_tmppath}}%{!?_tmppath:/var/tmp}";
178     const char * tempfn = NULL;
179     const char * tfn = NULL;
180     static int _initialized = 0;
181     int temput;
182     FD_t fd = NULL;
183     int ran;
184
185     /*@-branchstate@*/
186     if (!prefix) prefix = "";
187     /*@=branchstate@*/
188
189     /* Create the temp directory if it doesn't already exist. */
190     /*@-branchstate@*/
191     if (!_initialized) {
192         _initialized = 1;
193         tempfn = rpmGenPath(prefix, tpmacro, NULL);
194         if (rpmMkpath(tempfn, 0755, (uid_t) -1, (gid_t) -1))
195             goto errxit;
196     }
197     /*@=branchstate@*/
198
199     /* XXX should probably use mkstemp here */
200     srand(time(NULL));
201     ran = rand() % 100000;
202
203     /* maybe this should use link/stat? */
204
205     do {
206         char tfnbuf[64];
207 #ifndef NOTYET
208         sprintf(tfnbuf, "rpm-tmp.%d", ran++);
209         tempfn = _free(tempfn);
210         tempfn = rpmGenPath(prefix, tpmacro, tfnbuf);
211 #else
212         strcpy(tfnbuf, "rpm-tmp.XXXXXX");
213         tempfn = _free(tempfn);
214         tempfn = rpmGenPath(prefix, tpmacro, mktemp(tfnbuf));
215 #endif
216
217         temput = urlPath(tempfn, &tfn);
218         if (*tfn == '\0') goto errxit;
219
220         switch (temput) {
221         case URL_IS_HTTP:
222         case URL_IS_DASH:
223             goto errxit;
224             /*@notreached@*/ /*@switchbreak@*/ break;
225         default:
226             /*@switchbreak@*/ break;
227         }
228
229         fd = Fopen(tempfn, "w+x.ufdio");
230         /* XXX FIXME: errno may not be correct for ufdio */
231     } while ((fd == NULL || Ferror(fd)) && errno == EEXIST);
232
233     if (fd == NULL || Ferror(fd))
234         goto errxit;
235
236     switch(temput) {
237     case URL_IS_PATH:
238     case URL_IS_UNKNOWN:
239       { struct stat sb, sb2;
240         if (!stat(tfn, &sb) && S_ISLNK(sb.st_mode)) {
241             rpmError(RPMERR_SCRIPT, _("error creating temporary file %s\n"), tfn);
242             goto errxit;
243         }
244
245         if (sb.st_nlink != 1) {
246             rpmError(RPMERR_SCRIPT, _("error creating temporary file %s\n"), tfn);
247             goto errxit;
248         }
249
250         if (fstat(Fileno(fd), &sb2) == 0) {
251             if (sb2.st_ino != sb.st_ino || sb2.st_dev != sb.st_dev) {
252                 rpmError(RPMERR_SCRIPT, _("error creating temporary file %s\n"), tfn);
253                 goto errxit;
254             }
255         }
256       } break;
257     default:
258         break;
259     }
260
261     /*@-branchstate@*/
262     if (fnptr)
263         *fnptr = tempfn;
264     else 
265         tempfn = _free(tempfn);
266     /*@=branchstate@*/
267     *fdptr = fd;
268
269     return 0;
270
271 errxit:
272     tempfn = _free(tempfn);
273     /*@-usereleased@*/
274     if (fd) (void) Fclose(fd);
275     /*@=usereleased@*/
276     return 1;
277 }
278
279 char * currentDirectory(void)
280 {
281     int currDirLen;
282     char * currDir;
283
284     currDirLen = 50;
285     currDir = xmalloc(currDirLen);
286     while (!getcwd(currDir, currDirLen) && errno == ERANGE) {
287         currDirLen += 50;
288         currDir = xrealloc(currDir, currDirLen);
289     }
290
291     return currDir;
292 }
293
294 /*@-exportheadervar@*/
295 /*@unchecked@*/
296 int _noDirTokens = 0;
297 /*@=exportheadervar@*/
298
299 static int dncmp(const void * a, const void * b)
300 {
301     const char *const * first = a;
302     const char *const * second = b;
303     return strcmp(*first, *second);
304 }
305
306 void compressFilelist(Header h)
307 {
308     HGE_t hge = (HGE_t)headerGetEntryMinMemory;
309     HAE_t hae = (HAE_t)headerAddEntry;
310     HRE_t hre = (HRE_t)headerRemoveEntry;
311     HFD_t hfd = headerFreeData;
312     char ** fileNames;
313     const char ** dirNames;
314     const char ** baseNames;
315     int_32 * dirIndexes;
316     rpmTagType fnt;
317     int count;
318     int i, xx;
319     int dirIndex = -1;
320
321     /*
322      * This assumes the file list is already sorted, and begins with a
323      * single '/'. That assumption isn't critical, but it makes things go
324      * a bit faster.
325      */
326
327     if (headerIsEntry(h, RPMTAG_DIRNAMES)) {
328         xx = hre(h, RPMTAG_OLDFILENAMES);
329         return;         /* Already converted. */
330     }
331
332     if (!hge(h, RPMTAG_OLDFILENAMES, &fnt, (void **) &fileNames, &count))
333         return;         /* no file list */
334     if (fileNames == NULL || count <= 0)
335         return;
336
337     dirNames = alloca(sizeof(*dirNames) * count);       /* worst case */
338     baseNames = alloca(sizeof(*dirNames) * count);
339     dirIndexes = alloca(sizeof(*dirIndexes) * count);
340
341     if (fileNames[0][0] != '/') {
342         /* HACK. Source RPM, so just do things differently */
343         dirIndex = 0;
344         dirNames[dirIndex] = "";
345         for (i = 0; i < count; i++) {
346             dirIndexes[i] = dirIndex;
347             baseNames[i] = fileNames[i];
348         }
349         goto exit;
350     }
351
352     /*@-branchstate@*/
353     for (i = 0; i < count; i++) {
354         const char ** needle;
355         char savechar;
356         char * baseName;
357         int len;
358
359         if (fileNames[i] == NULL)       /* XXX can't happen */
360             continue;
361         baseName = strrchr(fileNames[i], '/') + 1;
362         len = baseName - fileNames[i];
363         needle = dirNames;
364         savechar = *baseName;
365         *baseName = '\0';
366         if (dirIndex < 0 ||
367             (needle = bsearch(&fileNames[i], dirNames, dirIndex + 1, sizeof(dirNames[0]), dncmp)) == NULL) {
368             char *s = alloca(len + 1);
369             memcpy(s, fileNames[i], len + 1);
370             s[len] = '\0';
371             dirIndexes[i] = ++dirIndex;
372             dirNames[dirIndex] = s;
373         } else
374             dirIndexes[i] = needle - dirNames;
375
376         *baseName = savechar;
377         baseNames[i] = baseName;
378     }
379     /*@=branchstate@*/
380
381 exit:
382     if (count > 0) {
383         xx = hae(h, RPMTAG_DIRINDEXES, RPM_INT32_TYPE, dirIndexes, count);
384         xx = hae(h, RPMTAG_BASENAMES, RPM_STRING_ARRAY_TYPE,
385                         baseNames, count);
386         xx = hae(h, RPMTAG_DIRNAMES, RPM_STRING_ARRAY_TYPE,
387                         dirNames, dirIndex + 1);
388     }
389
390     fileNames = hfd(fileNames, fnt);
391
392     xx = hre(h, RPMTAG_OLDFILENAMES);
393 }
394
395 /*
396  * This is pretty straight-forward. The only thing that even resembles a trick
397  * is getting all of this into a single xmalloc'd block.
398  */
399 static void doBuildFileList(Header h, /*@out@*/ const char *** fileListPtr,
400                             /*@out@*/ int * fileCountPtr, rpmTag baseNameTag,
401                             rpmTag dirNameTag, rpmTag dirIndexesTag)
402         /*@modifies *fileListPtr, *fileCountPtr @*/
403 {
404     HGE_t hge = (HGE_t)headerGetEntryMinMemory;
405     HFD_t hfd = headerFreeData;
406     const char ** baseNames;
407     const char ** dirNames;
408     int * dirIndexes;
409     int count;
410     const char ** fileNames;
411     int size;
412     rpmTagType bnt, dnt;
413     char * data;
414     int i, xx;
415
416     if (!hge(h, baseNameTag, &bnt, (void **) &baseNames, &count)) {
417         if (fileListPtr) *fileListPtr = NULL;
418         if (fileCountPtr) *fileCountPtr = 0;
419         return;         /* no file list */
420     }
421
422     xx = hge(h, dirNameTag, &dnt, (void **) &dirNames, NULL);
423     xx = hge(h, dirIndexesTag, NULL, (void **) &dirIndexes, &count);
424
425     size = sizeof(*fileNames) * count;
426     for (i = 0; i < count; i++)
427         size += strlen(baseNames[i]) + strlen(dirNames[dirIndexes[i]]) + 1;
428
429     fileNames = xmalloc(size);
430     data = ((char *) fileNames) + (sizeof(*fileNames) * count);
431     /*@-branchstate@*/
432     for (i = 0; i < count; i++) {
433         fileNames[i] = data;
434         data = stpcpy( stpcpy(data, dirNames[dirIndexes[i]]), baseNames[i]);
435         *data++ = '\0';
436     }
437     /*@=branchstate@*/
438     baseNames = hfd(baseNames, bnt);
439     dirNames = hfd(dirNames, dnt);
440
441     /*@-branchstate@*/
442     if (fileListPtr)
443         *fileListPtr = fileNames;
444     else
445         fileNames = _free(fileNames);
446     /*@=branchstate@*/
447     if (fileCountPtr) *fileCountPtr = count;
448 }
449
450 void expandFilelist(Header h)
451 {
452     HAE_t hae = (HAE_t)headerAddEntry;
453     HRE_t hre = (HRE_t)headerRemoveEntry;
454     const char ** fileNames = NULL;
455     int count = 0;
456     int xx;
457
458     /*@-branchstate@*/
459     if (!headerIsEntry(h, RPMTAG_OLDFILENAMES)) {
460         doBuildFileList(h, &fileNames, &count, RPMTAG_BASENAMES,
461                         RPMTAG_DIRNAMES, RPMTAG_DIRINDEXES);
462         if (fileNames == NULL || count <= 0)
463             return;
464         xx = hae(h, RPMTAG_OLDFILENAMES, RPM_STRING_ARRAY_TYPE,
465                         fileNames, count);
466         fileNames = _free(fileNames);
467     }
468     /*@=branchstate@*/
469
470     xx = hre(h, RPMTAG_DIRNAMES);
471     xx = hre(h, RPMTAG_BASENAMES);
472     xx = hre(h, RPMTAG_DIRINDEXES);
473 }
474
475
476 void rpmBuildFileList(Header h, const char *** fileListPtr, int * fileCountPtr)
477 {
478     doBuildFileList(h, fileListPtr, fileCountPtr, RPMTAG_BASENAMES,
479                         RPMTAG_DIRNAMES, RPMTAG_DIRINDEXES);
480 }
481
482 void buildOrigFileList(Header h, const char *** fileListPtr, int * fileCountPtr)
483 {
484     doBuildFileList(h, fileListPtr, fileCountPtr, RPMTAG_ORIGBASENAMES,
485                         RPMTAG_ORIGDIRNAMES, RPMTAG_ORIGDIRINDEXES);
486 }
487
488 /* glob_pattern_p() taken from bash
489  * Copyright (C) 1985, 1988, 1989 Free Software Foundation, Inc.
490  *
491  * Return nonzero if PATTERN has any special globbing chars in it.
492  */
493 int myGlobPatternP (const char *patternURL)
494 {
495     const char *p;
496     char c;
497     int open = 0;
498   
499     (void) urlPath(patternURL, &p);
500     while ((c = *p++) != '\0')
501         switch (c) {
502         case '?':
503         case '*':
504             return (1);
505         case '[':      /* Only accept an open brace if there is a close */
506             open++;    /* brace to match it.  Bracket expressions must be */
507             continue;  /* complete, according to Posix.2 */
508         case ']':
509             if (open)
510                 return (1);
511             continue;      
512         case '\\':
513             if (*p++ == '\0')
514                 return (0);
515         }
516
517     return (0);
518 }
519
520 static int glob_error(/*@unused@*/const char *foo, /*@unused@*/int bar)
521 {
522     return 1;
523 }
524
525 int rpmGlob(const char * patterns, int * argcPtr, const char *** argvPtr)
526 {
527     int ac = 0;
528     const char ** av = NULL;
529     int argc = 0;
530     const char ** argv = NULL;
531     const char * path;
532     const char * globURL;
533     char * globRoot = NULL;
534     size_t maxb, nb;
535     glob_t gl;
536     int ut;
537     int i, j;
538     int rc;
539
540     rc = poptParseArgvString(patterns, &ac, &av);
541     if (rc)
542         return rc;
543
544     for (j = 0; j < ac; j++) {
545         if (!myGlobPatternP(av[j])) {
546             if (argc == 0)
547                 argv = xmalloc((argc+2) * sizeof(*argv));
548             else
549                 argv = xrealloc(argv, (argc+2) * sizeof(*argv));
550             argv[argc] = xstrdup(av[j]);
551 if (_debug)
552 fprintf(stderr, "*** rpmGlob argv[%d] \"%s\"\n", argc, argv[argc]);
553             argc++;
554             continue;
555         }
556         
557         gl.gl_pathc = 0;
558         gl.gl_pathv = NULL;
559         rc = Glob(av[j], 0, glob_error, &gl);
560         if (rc)
561             goto exit;
562
563         /* XXX Prepend the URL leader for globs that have stripped it off */
564         maxb = 0;
565         for (i = 0; i < gl.gl_pathc; i++) {
566             if ((nb = strlen(&(gl.gl_pathv[i][0]))) > maxb)
567                 maxb = nb;
568         }
569         
570         ut = urlPath(av[j], &path);
571         nb = ((ut > URL_IS_DASH) ? (path - av[j]) : 0);
572         maxb += nb;
573         maxb += 1;
574         globURL = globRoot = xmalloc(maxb);
575
576         switch (ut) {
577         case URL_IS_HTTP:
578         case URL_IS_FTP:
579         case URL_IS_PATH:
580         case URL_IS_DASH:
581             strncpy(globRoot, av[j], nb);
582             /*@switchbreak@*/ break;
583         case URL_IS_UNKNOWN:
584             /*@switchbreak@*/ break;
585         }
586         globRoot += nb;
587         *globRoot = '\0';
588 if (_debug)
589 fprintf(stderr, "*** GLOB maxb %d diskURL %d %*s globURL %p %s\n", (int)maxb, (int)nb, (int)nb, av[j], globURL, globURL);
590         
591         /*@-branchstate@*/
592         if (argc == 0)
593             argv = xmalloc((gl.gl_pathc+1) * sizeof(*argv));
594         else if (gl.gl_pathc > 0)
595             argv = xrealloc(argv, (argc+gl.gl_pathc+1) * sizeof(*argv));
596         /*@=branchstate@*/
597         for (i = 0; i < gl.gl_pathc; i++) {
598             const char * globFile = &(gl.gl_pathv[i][0]);
599             if (globRoot > globURL && globRoot[-1] == '/')
600                 while (*globFile == '/') globFile++;
601             strcpy(globRoot, globFile);
602 if (_debug)
603 fprintf(stderr, "*** rpmGlob argv[%d] \"%s\"\n", argc, globURL);
604             argv[argc++] = xstrdup(globURL);
605         }
606         /*@-immediatetrans@*/
607         Globfree(&gl);
608         /*@=immediatetrans@*/
609         globURL = _free(globURL);
610     }
611     if (argv != NULL && argc > 0) {
612         argv[argc] = NULL;
613         if (argvPtr)
614             *argvPtr = argv;
615         if (argcPtr)
616             *argcPtr = argc;
617         rc = 0;
618     } else
619         rc = 1;
620
621
622 exit:
623     av = _free(av);
624     if (rc || argvPtr == NULL) {
625         if (argv != NULL)
626         for (i = 0; i < argc; i++)
627             argv[i] = _free(argv[i]);
628         argv = _free(argv);
629     }
630     return rc;
631 }
632
633 /*
634  * XXX This is a "dressed" entry to headerGetEntry to do:
635  *      1) DIRNAME/BASENAME/DIRINDICES -> FILENAMES tag conversions.
636  *      2) i18n lookaside (if enabled).
637  */
638 int rpmHeaderGetEntry(Header h, int_32 tag, int_32 *type,
639         void **p, int_32 *c)
640 {
641     switch (tag) {
642     case RPMTAG_OLDFILENAMES:
643     {   const char ** fl = NULL;
644         int count;
645         rpmBuildFileList(h, &fl, &count);
646         if (count > 0) {
647             *p = fl;
648             if (c)      *c = count;
649             if (type)   *type = RPM_STRING_ARRAY_TYPE;
650             return 1;
651         }
652         if (c)  *c = 0;
653         return 0;
654     }   /*@notreached@*/ break;
655
656     case RPMTAG_GROUP:
657     case RPMTAG_DESCRIPTION:
658     case RPMTAG_SUMMARY:
659     {   char fmt[128];
660         const char * msgstr;
661         const char * errstr;
662
663         fmt[0] = '\0';
664         (void) stpcpy( stpcpy( stpcpy( fmt, "%{"), tagName(tag)), "}\n");
665
666         /* XXX FIXME: memory leak. */
667         msgstr = headerSprintf(h, fmt, rpmTagTable, rpmHeaderFormats, &errstr);
668         if (msgstr) {
669             *p = (void *) msgstr;
670             if (type)   *type = RPM_STRING_TYPE;
671             if (c)      *c = 1;
672             return 1;
673         } else {
674             if (c)      *c = 0;
675             return 0;
676         }
677     }   /*@notreached@*/ break;
678
679     default:
680         return headerGetEntry(h, tag, type, p, c);
681         /*@notreached@*/ break;
682     }
683     /*@notreached@*/
684 }
685
686 /*
687  * XXX Yet Another dressed entry to unify signature/header tag retrieval.
688  */
689 int rpmPackageGetEntry( /*@unused@*/ void *leadp, Header sigs, Header h,
690         int_32 tag, int_32 *type, void **p, int_32 *c)
691 {
692     int_32 sigtag;
693
694     switch (tag) {
695     case RPMTAG_SIGSIZE:        sigtag = RPMSIGTAG_SIZE;        break;
696     case RPMTAG_SIGLEMD5_1:     sigtag = RPMSIGTAG_LEMD5_1;     break;
697     case RPMTAG_SIGPGP:         sigtag = RPMSIGTAG_PGP;         break;
698     case RPMTAG_SIGLEMD5_2:     sigtag = RPMSIGTAG_LEMD5_2;     break;
699     case RPMTAG_SIGMD5:         sigtag = RPMSIGTAG_MD5;         break;
700     case RPMTAG_SIGGPG:         sigtag = RPMSIGTAG_GPG;         break;
701     case RPMTAG_SIGPGP5:        sigtag = RPMSIGTAG_GPG;         break;
702         
703     default:
704         return rpmHeaderGetEntry(h, tag, type, p, c);
705         /*@notreached@*/ break;
706     }
707
708     if (headerIsEntry(h, tag))
709         return rpmHeaderGetEntry(h, tag, type, p, c);
710
711     if (sigs == NULL) {
712         if (c)  *c = 0;
713         return 0;
714     }
715
716     return headerGetEntry(sigs, sigtag, type, p, c);
717 }
718
719 /*
720  * Up to rpm 3.0.4, packages implicitly provided their own name-version-release.
721  * Retrofit an explicit "Provides: name = epoch:version-release.
722  */
723 void providePackageNVR(Header h)
724 {
725     HGE_t hge = (HGE_t)headerGetEntryMinMemory;
726     HFD_t hfd = headerFreeData;
727     const char *name, *version, *release;
728     int_32 * epoch;
729     const char *pEVR;
730     char *p;
731     int_32 pFlags = RPMSENSE_EQUAL;
732     const char ** provides = NULL;
733     const char ** providesEVR = NULL;
734     rpmTagType pnt, pvt;
735     int_32 * provideFlags = NULL;
736     int providesCount;
737     int i, xx;
738     int bingo = 1;
739
740     /* Generate provides for this package name-version-release. */
741     xx = headerNVR(h, &name, &version, &release);
742     if (!(name && version && release))
743         return;
744     pEVR = p = alloca(21 + strlen(version) + 1 + strlen(release) + 1);
745     *p = '\0';
746     if (hge(h, RPMTAG_EPOCH, NULL, (void **) &epoch, NULL)) {
747         sprintf(p, "%d:", *epoch);
748         while (*p != '\0')
749             p++;
750     }
751     (void) stpcpy( stpcpy( stpcpy(p, version) , "-") , release);
752
753     /*
754      * Rpm prior to 3.0.3 does not have versioned provides.
755      * If no provides at all are available, we can just add.
756      */
757     if (!hge(h, RPMTAG_PROVIDENAME, &pnt, (void **) &provides, &providesCount))
758         goto exit;
759
760     /*
761      * Otherwise, fill in entries on legacy packages.
762      */
763     if (!hge(h, RPMTAG_PROVIDEVERSION, &pvt, (void **) &providesEVR, NULL)) {
764         for (i = 0; i < providesCount; i++) {
765             char * vdummy = "";
766             int_32 fdummy = RPMSENSE_ANY;
767             xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEVERSION, RPM_STRING_ARRAY_TYPE,
768                         &vdummy, 1);
769             xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEFLAGS, RPM_INT32_TYPE,
770                         &fdummy, 1);
771         }
772         goto exit;
773     }
774
775     xx = hge(h, RPMTAG_PROVIDEFLAGS, NULL, (void **) &provideFlags, NULL);
776
777     if (provides && providesEVR && provideFlags)
778     for (i = 0; i < providesCount; i++) {
779         if (!(provides[i] && providesEVR[i]))
780             continue;
781         if (!(provideFlags[i] == RPMSENSE_EQUAL &&
782             !strcmp(name, provides[i]) && !strcmp(pEVR, providesEVR[i])))
783             continue;
784         bingo = 0;
785         break;
786     }
787
788 exit:
789     provides = hfd(provides, pnt);
790     providesEVR = hfd(providesEVR, pvt);
791
792     if (bingo) {
793         xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDENAME, RPM_STRING_ARRAY_TYPE,
794                 &name, 1);
795         xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEFLAGS, RPM_INT32_TYPE,
796                 &pFlags, 1);
797         xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEVERSION, RPM_STRING_ARRAY_TYPE,
798                 &pEVR, 1);
799     }
800 }