fix: legacy packages required provides EVR/Flags to be added.
[platform/upstream/rpm.git] / lib / misc.c
1 #include "system.h"
2
3 static int _debug = 0;
4
5 #include <rpmlib.h>
6 #include <rpmurl.h>
7 #include <rpmmacro.h>   /* XXX for rpmGetPath */
8
9 #include "misc.h"
10
11 char * RPMVERSION = VERSION;    /* just to put a marker in librpm.a */
12
13 char ** splitString(const char * str, int length, char sep)
14 {
15     const char * source;
16     char * s, * dest;
17     char ** list;
18     int i;
19     int fields;
20
21     s = xmalloc(length + 1);
22
23     fields = 1;
24     for (source = str, dest = s, i = 0; i < length; i++, source++, dest++) {
25         *dest = *source;
26         if (*dest == sep) fields++;
27     }
28
29     *dest = '\0';
30
31     list = xmalloc(sizeof(char *) * (fields + 1));
32
33     dest = s;
34     list[0] = dest;
35     i = 1;
36     while (i < fields) {
37         if (*dest == sep) {
38             list[i++] = dest + 1;
39             *dest = 0;
40         }
41         dest++;
42     }
43
44     list[i] = NULL;
45
46     return list;
47 }
48
49 void freeSplitString(char ** list)
50 {
51     free(list[0]);
52     free(list);
53 }
54
55 int rpmfileexists(const char * urlfn)
56 {
57     const char *fn;
58     int urltype = urlPath(urlfn, &fn);
59     struct stat buf;
60
61     if (*fn == '\0') fn = "/";
62     switch (urltype) {
63     case URL_IS_FTP:    /* XXX WRONG WRONG WRONG */
64     case URL_IS_HTTP:   /* XXX WRONG WRONG WRONG */
65     case URL_IS_PATH:
66     case URL_IS_UNKNOWN:
67         if (Stat(fn, &buf)) {
68             switch(errno) {
69             case ENOENT:
70             case EINVAL:
71                 return 0;
72             }
73         }
74         break;
75     case URL_IS_DASH:
76     default:
77         return 0;
78         /*@notreached@*/ break;
79     }
80
81     return 1;
82 }
83
84 /* compare alpha and numeric segments of two versions */
85 /* return 1: a is newer than b */
86 /*        0: a and b are the same version */
87 /*       -1: b is newer than a */
88 int rpmvercmp(const char * a, const char * b)
89 {
90     char oldch1, oldch2;
91     char * str1, * str2;
92     char * one, * two;
93     int rc;
94     int isnum;
95
96     /* easy comparison to see if versions are identical */
97     if (!strcmp(a, b)) return 0;
98
99     str1 = alloca(strlen(a) + 1);
100     str2 = alloca(strlen(b) + 1);
101
102     strcpy(str1, a);
103     strcpy(str2, b);
104
105     one = str1;
106     two = str2;
107
108     /* loop through each version segment of str1 and str2 and compare them */
109     while (*one && *two) {
110         while (*one && !isalnum(*one)) one++;
111         while (*two && !isalnum(*two)) two++;
112
113         str1 = one;
114         str2 = two;
115
116         /* grab first completely alpha or completely numeric segment */
117         /* leave one and two pointing to the start of the alpha or numeric */
118         /* segment and walk str1 and str2 to end of segment */
119         if (isdigit(*str1)) {
120             while (*str1 && isdigit(*str1)) str1++;
121             while (*str2 && isdigit(*str2)) str2++;
122             isnum = 1;
123         } else {
124             while (*str1 && isalpha(*str1)) str1++;
125             while (*str2 && isalpha(*str2)) str2++;
126             isnum = 0;
127         }
128
129         /* save character at the end of the alpha or numeric segment */
130         /* so that they can be restored after the comparison */
131         oldch1 = *str1;
132         *str1 = '\0';
133         oldch2 = *str2;
134         *str2 = '\0';
135
136         /* take care of the case where the two version segments are */
137         /* different types: one numeric and one alpha */
138         if (one == str1) return -1;     /* arbitrary */
139         if (two == str2) return -1;
140
141         if (isnum) {
142             /* this used to be done by converting the digit segments */
143             /* to ints using atoi() - it's changed because long  */
144             /* digit segments can overflow an int - this should fix that. */
145
146             /* throw away any leading zeros - it's a number, right? */
147             while (*one == '0') one++;
148             while (*two == '0') two++;
149
150             /* whichever number has more digits wins */
151             if (strlen(one) > strlen(two)) return 1;
152             if (strlen(two) > strlen(one)) return -1;
153         }
154
155         /* strcmp will return which one is greater - even if the two */
156         /* segments are alpha or if they are numeric.  don't return  */
157         /* if they are equal because there might be more segments to */
158         /* compare */
159         rc = strcmp(one, two);
160         if (rc) return rc;
161
162         /* restore character that was replaced by null above */
163         *str1 = oldch1;
164         one = str1;
165         *str2 = oldch2;
166         two = str2;
167     }
168
169     /* this catches the case where all numeric and alpha segments have */
170     /* compared identically but the segment sepparating characters were */
171     /* different */
172     if ((!*one) && (!*two)) return 0;
173
174     /* whichever version still has characters left over wins */
175     if (!*one) return -1; else return 1;
176 }
177
178 void stripTrailingSlashes(char * str)
179 {
180     char * chptr;
181
182     chptr = str + strlen(str) - 1;
183     while (*chptr == '/' && chptr >= str) {
184         *chptr = '\0';
185         chptr--;
186     }
187 }
188
189 int doputenv(const char *str)
190 {
191     char * a;
192
193     /* FIXME: this leaks memory! */
194
195     a = xmalloc(strlen(str) + 1);
196     strcpy(a, str);
197
198     return putenv(a);
199 }
200
201 int dosetenv(const char *name, const char *value, int overwrite)
202 {
203     int i;
204     char * a;
205
206     /* FIXME: this leaks memory! */
207
208     if (!overwrite && getenv(name)) return 0;
209
210     i = strlen(name) + strlen(value) + 2;
211     a = xmalloc(i);
212     if (!a) return 1;
213
214     strcpy(a, name);
215     strcat(a, "=");
216     strcat(a, value);
217
218     return putenv(a);
219 }
220
221 /* unameToUid(), uidTouname() and the group variants are really poorly
222    implemented. They really ought to use hash tables. I just made the
223    guess that most files would be owned by root or the same person/group
224    who owned the last file. Those two values are cached, everything else
225    is looked up via getpw() and getgr() functions.  If this performs
226    too poorly I'll have to implement it properly :-( */
227
228 int unameToUid(const char * thisUname, uid_t * uid)
229 {
230     /*@only@*/ static char * lastUname = NULL;
231     static int lastUnameLen = 0;
232     static int lastUnameAlloced;
233     static uid_t lastUid;
234     struct passwd * pwent;
235     int thisUnameLen;
236
237     if (!thisUname) {
238         lastUnameLen = 0;
239         return -1;
240     } else if (!strcmp(thisUname, "root")) {
241         *uid = 0;
242         return 0;
243     }
244
245     thisUnameLen = strlen(thisUname);
246     if (!lastUname || thisUnameLen != lastUnameLen ||
247         strcmp(thisUname, lastUname)) {
248         if (lastUnameAlloced < thisUnameLen + 1) {
249             lastUnameAlloced = thisUnameLen + 10;
250             lastUname = xrealloc(lastUname, lastUnameAlloced);  /* XXX memory leak */
251         }
252         strcpy(lastUname, thisUname);
253
254         pwent = getpwnam(thisUname);
255         if (!pwent) {
256             endpwent();
257             pwent = getpwnam(thisUname);
258             if (!pwent) return -1;
259         }
260
261         lastUid = pwent->pw_uid;
262     }
263
264     *uid = lastUid;
265
266     return 0;
267 }
268
269 int gnameToGid(const char * thisGname, gid_t * gid)
270 {
271     /*@only@*/ static char * lastGname = NULL;
272     static int lastGnameLen = 0;
273     static int lastGnameAlloced;
274     static uid_t lastGid;
275     int thisGnameLen;
276     struct group * grent;
277
278     if (!thisGname) {
279         lastGnameLen = 0;
280         return -1;
281     } else if (!strcmp(thisGname, "root")) {
282         *gid = 0;
283         return 0;
284     }
285
286     thisGnameLen = strlen(thisGname);
287     if (!lastGname || thisGnameLen != lastGnameLen ||
288         strcmp(thisGname, lastGname)) {
289         if (lastGnameAlloced < thisGnameLen + 1) {
290             lastGnameAlloced = thisGnameLen + 10;
291             lastGname = xrealloc(lastGname, lastGnameAlloced);  /* XXX memory leak */
292         }
293         strcpy(lastGname, thisGname);
294
295         grent = getgrnam(thisGname);
296         if (!grent) {
297             endgrent();
298             grent = getgrnam(thisGname);
299             if (!grent) return -1;
300         }
301         lastGid = grent->gr_gid;
302     }
303
304     *gid = lastGid;
305
306     return 0;
307 }
308
309 char * uidToUname(uid_t uid)
310 {
311     static int lastUid = -1;
312     /*@only@*/ static char * lastUname = NULL;
313     static int lastUnameLen = 0;
314     struct passwd * pwent;
315     int len;
316
317     if (uid == (uid_t) -1) {
318         lastUid = -1;
319         return NULL;
320     } else if (!uid) {
321         return "root";
322     } else if (uid == lastUid) {
323         return lastUname;
324     } else {
325         pwent = getpwuid(uid);
326         if (!pwent) return NULL;
327
328         lastUid = uid;
329         len = strlen(pwent->pw_name);
330         if (lastUnameLen < len + 1) {
331             lastUnameLen = len + 20;
332             lastUname = xrealloc(lastUname, lastUnameLen);
333         }
334         strcpy(lastUname, pwent->pw_name);
335
336         return lastUname;
337     }
338 }
339
340 char * gidToGname(gid_t gid)
341 {
342     static int lastGid = -1;
343     /*@only@*/ static char * lastGname = NULL;
344     static int lastGnameLen = 0;
345     struct group * grent;
346     int len;
347
348     if (gid == (gid_t) -1) {
349         lastGid = -1;
350         return NULL;
351     } else if (!gid) {
352         return "root";
353     } else if (gid == lastGid) {
354         return lastGname;
355     } else {
356         grent = getgrgid(gid);
357         if (!grent) return NULL;
358
359         lastGid = gid;
360         len = strlen(grent->gr_name);
361         if (lastGnameLen < len + 1) {
362             lastGnameLen = len + 20;
363             lastGname = xrealloc(lastGname, lastGnameLen);
364         }
365         strcpy(lastGname, grent->gr_name);
366
367         return lastGname;
368     }
369 }
370
371 int makeTempFile(const char * prefix, const char ** fnptr, FD_t * fdptr)
372 {
373     const char * tempfn = NULL;
374     const char * tfn = NULL;
375     int temput;
376     FD_t fd = NULL;
377     int ran;
378
379     if (!prefix) prefix = "";
380
381     /* XXX should probably use mktemp here */
382     srand(time(NULL));
383     ran = rand() % 100000;
384
385     /* maybe this should use link/stat? */
386
387     do {
388         char tfnbuf[64];
389 #ifndef NOTYET
390         sprintf(tfnbuf, "rpm-tmp.%d", ran++);
391         if (tempfn)     xfree(tempfn);
392         tempfn = rpmGenPath(prefix, "%{_tmppath}/", tfnbuf);
393 #else
394         strcpy(tfnbuf, "rpm-tmp.XXXXXX");
395         if (tempfn)     xfree(tempfn);
396         tempfn = rpmGenPath(prefix, "%{_tmppath}/", mktemp(tfnbuf));
397 #endif
398
399         temput = urlPath(tempfn, &tfn);
400         if (*tfn == '\0') goto errxit;
401
402         switch (temput) {
403         case URL_IS_HTTP:
404         case URL_IS_DASH:
405             goto errxit;
406             /*@notreached@*/ break;
407         default:
408             break;
409         }
410
411         fd = Fopen(tempfn, "w+x.ufdio");
412         /* XXX FIXME: errno may not be correct for ufdio */
413     } while ((fd == NULL || Ferror(fd)) && errno == EEXIST);
414
415     if (fd == NULL || Ferror(fd))
416         goto errxit;
417
418     switch(temput) {
419         struct stat sb, sb2;
420     case URL_IS_PATH:
421     case URL_IS_UNKNOWN:
422         if (!stat(tfn, &sb) && S_ISLNK(sb.st_mode)) {
423             rpmError(RPMERR_SCRIPT, _("error creating temporary file %s"), tfn);
424             goto errxit;
425         }
426
427         if (sb.st_nlink != 1) {
428             rpmError(RPMERR_SCRIPT, _("error creating temporary file %s"), tfn);
429             goto errxit;
430         }
431
432         if (fstat(Fileno(fd), &sb2) == 0) {
433             if (sb2.st_ino != sb.st_ino || sb2.st_dev != sb.st_dev) {
434                 rpmError(RPMERR_SCRIPT, _("error creating temporary file %s"), tfn);
435                 goto errxit;
436             }
437         }
438         break;
439     default:
440         break;
441     }
442
443     if (fnptr)
444         *fnptr = tempfn;
445     else if (tempfn) {
446         xfree(tempfn);
447         tempfn = NULL;
448     }
449     *fdptr = fd;
450
451     return 0;
452
453 errxit:
454     if (tempfn) xfree(tempfn);
455     if (fd) Fclose(fd);
456     return 1;
457 }
458
459 char * currentDirectory(void)
460 {
461     int currDirLen;
462     char * currDir;
463
464     currDirLen = 50;
465     currDir = xmalloc(currDirLen);
466     while (!getcwd(currDir, currDirLen) && errno == ERANGE) {
467         currDirLen += 50;
468         currDir = xrealloc(currDir, currDirLen);
469     }
470
471     return currDir;
472 }
473
474 int _noDirTokens = 0;
475
476 static int dncmp(const void * a, const void * b)
477 {
478     const char *const * first = a;
479     const char *const * second = b;
480     return strcmp(*first, *second);
481 }
482
483 void compressFilelist(Header h)
484 {
485     char ** fileNames;
486     const char ** dirNames;
487     const char ** baseNames;
488     int_32 * dirIndexes;
489     int count;
490     int i;
491     int dirIndex = -1;
492
493     /*
494      * This assumes the file list is already sorted, and begins with a
495      * single '/'. That assumption isn't critical, but it makes things go
496      * a bit faster.
497      */
498
499     if (!headerGetEntry(h, RPMTAG_OLDFILENAMES, NULL,
500                         (void **) &fileNames, &count))
501         return;         /* no file list */
502
503     dirNames = alloca(sizeof(*dirNames) * count);       /* worst case */
504     baseNames = alloca(sizeof(*dirNames) * count);
505     dirIndexes = alloca(sizeof(*dirIndexes) * count);
506
507     if (fileNames[0][0] != '/') {
508         /* HACK. Source RPM, so just do things differently */
509         dirIndex = 0;
510         dirNames[dirIndex] = "";
511         for (i = 0; i < count; i++) {
512             dirIndexes[i] = dirIndex;
513             baseNames[i] = fileNames[i];
514         }
515         goto exit;
516     }
517
518     for (i = 0; i < count; i++) {
519         const char ** needle;
520         char *baseName = strrchr(fileNames[i], '/') + 1;
521         char savechar;
522         int len = baseName - fileNames[i];
523
524         savechar = *baseName;
525         *baseName = '\0';
526         if (dirIndex < 0 ||
527             (needle = bsearch(&fileNames[i], dirNames, dirIndex + 1, sizeof(dirNames[0]), dncmp)) == NULL) {
528             char *s = alloca(len + 1);
529             memcpy(s, fileNames[i], len + 1);
530             s[len] = '\0';
531             dirIndexes[i] = ++dirIndex;
532             dirNames[dirIndex] = s;
533         } else
534             dirIndexes[i] = needle - dirNames;
535
536         *baseName = savechar;
537         baseNames[i] = baseName;
538     }
539
540 exit:
541     headerAddEntry(h, RPMTAG_DIRNAMES, RPM_STRING_ARRAY_TYPE,
542                         dirNames, dirIndex + 1);
543     headerAddEntry(h, RPMTAG_DIRINDEXES, RPM_INT32_TYPE,
544                         dirIndexes, count);
545     headerAddEntry(h, RPMTAG_BASENAMES, RPM_STRING_ARRAY_TYPE,
546                         baseNames, count);
547
548     xfree(fileNames);
549
550     headerRemoveEntry(h, RPMTAG_OLDFILENAMES);
551 }
552
553 /*
554  * This is pretty straight-forward. The only thing that even resembles a trick
555  * is getting all of this into a single xmalloc'd block.
556  */
557 static void doBuildFileList(Header h, /*@out@*/ const char *** fileListPtr,
558                             /*@out@*/ int * fileCountPtr, int baseNameTag,
559                             int dirNameTag, int dirIndexesTag)
560 {
561     const char ** baseNames;
562     const char ** dirNames;
563     int * dirIndexes;
564     int count;
565     const char ** fileNames;
566     int size;
567     char * data;
568     int i;
569
570     if (!headerGetEntry(h, baseNameTag, NULL, (void **) &baseNames, &count)) {
571         *fileListPtr = NULL;
572         *fileCountPtr = 0;
573         return;         /* no file list */
574     }
575
576     headerGetEntry(h, dirNameTag, NULL, (void **) &dirNames, NULL);
577     headerGetEntry(h, dirIndexesTag, NULL, (void **) &dirIndexes, &count);
578
579     size = sizeof(*fileNames) * count;
580     for (i = 0; i < count; i++)
581         size += strlen(baseNames[i]) + strlen(dirNames[dirIndexes[i]]) + 1;
582
583     fileNames = xmalloc(size);
584     data = ((char *) fileNames) + (sizeof(*fileNames) * count);
585     for (i = 0; i < count; i++) {
586         fileNames[i] = data;
587         data = stpcpy( stpcpy(data, dirNames[dirIndexes[i]]), baseNames[i]);
588         *data++ = '\0';
589     }
590     xfree(baseNames);
591     xfree(dirNames);
592
593     *fileListPtr = fileNames;
594     *fileCountPtr = count;
595 }
596
597 void expandFilelist(Header h)
598 {
599     const char ** fileNames = NULL;
600     int count = 0;
601
602     doBuildFileList(h, &fileNames, &count, RPMTAG_BASENAMES,
603                         RPMTAG_DIRNAMES, RPMTAG_DIRINDEXES);
604
605     if (fileNames == NULL || count <= 0)
606         return;
607
608     headerAddEntry(h, RPMTAG_OLDFILENAMES, RPM_STRING_ARRAY_TYPE,
609                         fileNames, count);
610
611     xfree(fileNames);
612
613     headerRemoveEntry(h, RPMTAG_BASENAMES);
614     headerRemoveEntry(h, RPMTAG_DIRNAMES);
615     headerRemoveEntry(h, RPMTAG_DIRINDEXES);
616 }
617
618
619 void rpmBuildFileList(Header h, const char *** fileListPtr, int * fileCountPtr)
620 {
621     doBuildFileList(h, fileListPtr, fileCountPtr, RPMTAG_BASENAMES,
622                         RPMTAG_DIRNAMES, RPMTAG_DIRINDEXES);
623 }
624
625 void buildOrigFileList(Header h, const char *** fileListPtr, int * fileCountPtr)
626 {
627     doBuildFileList(h, fileListPtr, fileCountPtr, RPMTAG_ORIGBASENAMES,
628                         RPMTAG_ORIGDIRNAMES, RPMTAG_ORIGDIRINDEXES);
629 }
630
631 /* glob_pattern_p() taken from bash
632  * Copyright (C) 1985, 1988, 1989 Free Software Foundation, Inc.
633  *
634  * Return nonzero if PATTERN has any special globbing chars in it.
635  */
636 int myGlobPatternP (const char *patternURL)
637 {
638     const char *p;
639     char c;
640     int open = 0;
641   
642     (void) urlPath(patternURL, &p);
643     while ((c = *p++) != '\0')
644         switch (c) {
645         case '?':
646         case '*':
647             return (1);
648         case '[':      /* Only accept an open brace if there is a close */
649             open++;    /* brace to match it.  Bracket expressions must be */
650             continue;  /* complete, according to Posix.2 */
651         case ']':
652             if (open)
653                 return (1);
654             continue;      
655         case '\\':
656             if (*p++ == '\0')
657                 return (0);
658         }
659
660     return (0);
661 }
662
663 static int glob_error(/*@unused@*/const char *foo, /*@unused@*/int bar)
664 {
665     return 1;
666 }
667
668 int rpmGlob(const char * patterns, int * argcPtr, const char *** argvPtr)
669 {
670     int ac = 0;
671     const char ** av = NULL;
672     int argc = 0;
673     const char ** argv = NULL;
674     const char * path;
675     const char * globURL;
676     char * globRoot = NULL;
677     size_t maxb, nb;
678     glob_t gl;
679     int ut;
680     int i, j;
681     int rc;
682
683     rc = poptParseArgvString(patterns, &ac, &av);
684     if (rc)
685         return rc;
686
687     for (j = 0; j < ac; j++) {
688         if (!myGlobPatternP(av[j])) {
689             if (argc == 0)
690                 argv = xmalloc((argc+2) * sizeof(*argv));
691             else
692                 argv = xrealloc(argv, (argc+2) * sizeof(*argv));
693 if (_debug)
694 fprintf(stderr, "*** rpmGlob argv[%d] \"%s\"\n", argc, av[j]);
695             argv[argc++] = xstrdup(av[j]);
696             continue;
697         }
698         
699         gl.gl_pathc = 0;
700         gl.gl_pathv = NULL;
701         rc = Glob(av[j], 0, glob_error, &gl);
702         if (rc)
703             goto exit;
704
705         /* XXX Prepend the URL leader for globs that have stripped it off */
706         maxb = 0;
707         for (i = 0; i < gl.gl_pathc; i++) {
708             if ((nb = strlen(&(gl.gl_pathv[i][0]))) > maxb)
709                 maxb = nb;
710         }
711         
712         ut = urlPath(av[j], &path);
713         nb = ((ut > URL_IS_DASH) ? (path - av[j]) : 0);
714         maxb += nb;
715         maxb += 1;
716         globURL = globRoot = xmalloc(maxb);
717
718         switch (ut) {
719         case URL_IS_HTTP:
720         case URL_IS_FTP:
721         case URL_IS_PATH:
722         case URL_IS_DASH:
723             strncpy(globRoot, av[j], nb);
724             break;
725         case URL_IS_UNKNOWN:
726             break;
727         }
728         globRoot += nb;
729         *globRoot = '\0';
730 if (_debug)
731 fprintf(stderr, "*** GLOB maxb %d diskURL %d %*s globURL %p %s\n", (int)maxb, (int)nb, (int)nb, av[j], globURL, globURL);
732         
733         if (argc == 0)
734             argv = xmalloc((gl.gl_pathc+1) * sizeof(*argv));
735         else if (gl.gl_pathc > 0)
736             argv = xrealloc(argv, (argc+gl.gl_pathc+1) * sizeof(*argv));
737         for (i = 0; i < gl.gl_pathc; i++) {
738             const char * globFile = &(gl.gl_pathv[i][0]);
739             if (globRoot > globURL && globRoot[-1] == '/')
740                 while (*globFile == '/') globFile++;
741             strcpy(globRoot, globFile);
742 if (_debug)
743 fprintf(stderr, "*** rpmGlob argv[%d] \"%s\"\n", argc, globURL);
744             argv[argc++] = xstrdup(globURL);
745         }
746         Globfree(&gl);
747         xfree(globURL);
748     }
749     argv[argc] = NULL;
750     if (argvPtr)
751         *argvPtr = argv;
752     if (argcPtr)
753         *argcPtr = argc;
754     rc = 0;
755
756 exit:
757     if (av)
758         xfree(av);
759     if ((rc || argvPtr == NULL) && argv) {
760         for (i = 0; i < argc; i++)
761             xfree(argv[i]);
762         xfree(argv);
763         argv = NULL;
764     }
765     return rc;
766 }
767
768 /*
769  * XXX This is a "dressed" entry to headerGetEntry to do:
770  *      1) DIRNAME/BASENAME/DIRINDICES -> FILENAMES tag conversions.
771  *      2) i18n lookaside (if enabled).
772  */
773 int rpmHeaderGetEntry(Header h, int_32 tag, int_32 *type,
774         void **p, int_32 *c)
775 {
776     switch (tag) {
777     case RPMTAG_OLDFILENAMES:
778     {   const char ** fl = NULL;
779         int count;
780         rpmBuildFileList(h, &fl, &count);
781         if (count > 0) {
782             *p = fl;
783             if (c)      *c = count;
784             if (type)   *type = RPM_STRING_ARRAY_TYPE;
785             return 1;
786         }
787         if (c)  *c = 0;
788         return 0;
789     }   /*@notreached@*/ break;
790
791     case RPMTAG_GROUP:
792     case RPMTAG_DESCRIPTION:
793     case RPMTAG_SUMMARY:
794     {   char fmt[128];
795         const char * msgstr;
796         const char * errstr;
797
798         fmt[0] = '\0';
799         (void) stpcpy( stpcpy( stpcpy( fmt, "%{"), tagName(tag)), "}\n");
800
801         /* XXX FIXME: memory leak. */
802         msgstr = headerSprintf(h, fmt, rpmTagTable, rpmHeaderFormats, &errstr);
803         if (msgstr) {
804             *p = (void *) msgstr;
805             if (type)   *type = RPM_STRING_TYPE;
806             if (c)      *c = 1;
807             return 1;
808         } else {
809             if (c)      *c = 0;
810             return 0;
811         }
812     }   /*@notreached@*/ break;
813
814     default:
815         return headerGetEntry(h, tag, type, p, c);
816         /*@notreached@*/ break;
817     }
818     /*@notreached@*/
819 }
820
821 /*
822  * Up to rpm 3.0.4, packages implicitly provided their own name-version-release.
823  * Retrofit an explicit "Provides: name = epoch:version-release.
824  */
825 void providePackageNVR(Header h)
826 {
827     const char *name, *version, *release;
828     int_32 * epoch;
829     const char *pEVR;
830     char *p;
831     int_32 pFlags = RPMSENSE_EQUAL;
832     const char ** provides = NULL;
833     const char ** providesEVR = NULL;
834     int_32 * provideFlags = NULL;
835     int providesCount;
836     int i;
837     int bingo = 1;
838
839     /* Generate provides for this package name-version-release. */
840     headerNVR(h, &name, &version, &release);
841     pEVR = p = alloca(21 + strlen(version) + 1 + strlen(release) + 1);
842     *p = '\0';
843     if (headerGetEntry(h, RPMTAG_EPOCH, NULL, (void **) &epoch, NULL)) {
844         sprintf(p, "%d:", *epoch);
845         while (*p)
846             p++;
847     }
848     (void) stpcpy( stpcpy( stpcpy(p, version) , "-") , release);
849
850     /*
851      * Rpm prior to 3.0.3 does not have versioned provides.
852      * If no provides at all are available, we can just add.
853      */
854     if (!headerGetEntry(h, RPMTAG_PROVIDENAME, NULL,
855                 (void **) &provides, &providesCount)) {
856         goto exit;
857     }
858
859     /*
860      * Otherwise, fill in entries on legacy packages.
861      */
862     if (!headerGetEntry(h, RPMTAG_PROVIDEVERSION, NULL,
863                 (void **) &providesEVR, NULL)) {
864         for (i = 0; i < providesCount; i++) {
865             char * vdummy = "";
866             int_32 fdummy = RPMSENSE_ANY;
867             headerAddOrAppendEntry(h, RPMTAG_PROVIDEVERSION, RPM_STRING_ARRAY_TYPE,
868                         &vdummy, 1);
869             headerAddOrAppendEntry(h, RPMTAG_PROVIDEFLAGS, RPM_INT32_TYPE,
870                         &fdummy, 1);
871         }
872         goto exit;
873     }
874
875     headerGetEntry(h, RPMTAG_PROVIDEFLAGS, NULL,
876         (void **) &provideFlags, NULL);
877
878     for (i = 0; i < providesCount; i++) {
879         if (!(provideFlags[i] == RPMSENSE_EQUAL &&
880             !strcmp(name, provides[i]) && !strcmp(pEVR, providesEVR[i])))
881             continue;
882         bingo = 0;
883         break;
884     }
885
886 exit:
887     if (provides) xfree(provides);
888     if (providesEVR) xfree(providesEVR);
889
890     if (bingo) {
891         headerAddOrAppendEntry(h, RPMTAG_PROVIDENAME, RPM_STRING_ARRAY_TYPE,
892                 &name, 1);
893         headerAddOrAppendEntry(h, RPMTAG_PROVIDEVERSION, RPM_STRING_ARRAY_TYPE,
894                 &pEVR, 1);
895         headerAddOrAppendEntry(h, RPMTAG_PROVIDEFLAGS, RPM_INT32_TYPE,
896                 &pFlags, 1);
897     }
898 }