7 #include <rpmmacro.h> /* XXX for rpmGetPath */
11 char * RPMVERSION = VERSION; /* just to put a marker in librpm.a */
13 char ** splitString(const char * str, int length, char sep)
21 s = xmalloc(length + 1);
24 for (source = str, dest = s, i = 0; i < length; i++, source++, dest++) {
26 if (*dest == sep) fields++;
31 list = xmalloc(sizeof(char *) * (fields + 1));
49 void freeSplitString(char ** list)
55 int rpmfileexists(const char * urlfn)
58 int urltype = urlPath(urlfn, &fn);
61 if (*fn == '\0') fn = "/";
63 case URL_IS_FTP: /* XXX WRONG WRONG WRONG */
64 case URL_IS_HTTP: /* XXX WRONG WRONG WRONG */
78 /*@notreached@*/ break;
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)
96 /* easy comparison to see if versions are identical */
97 if (!strcmp(a, b)) return 0;
99 str1 = alloca(strlen(a) + 1);
100 str2 = alloca(strlen(b) + 1);
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++;
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++;
124 while (*str1 && isalpha(*str1)) str1++;
125 while (*str2 && isalpha(*str2)) str2++;
129 /* save character at the end of the alpha or numeric segment */
130 /* so that they can be restored after the comparison */
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;
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. */
146 /* throw away any leading zeros - it's a number, right? */
147 while (*one == '0') one++;
148 while (*two == '0') two++;
150 /* whichever number has more digits wins */
151 if (strlen(one) > strlen(two)) return 1;
152 if (strlen(two) > strlen(one)) return -1;
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 */
159 rc = strcmp(one, two);
162 /* restore character that was replaced by null above */
169 /* this catches the case where all numeric and alpha segments have */
170 /* compared identically but the segment sepparating characters were */
172 if ((!*one) && (!*two)) return 0;
174 /* whichever version still has characters left over wins */
175 if (!*one) return -1; else return 1;
178 void stripTrailingSlashes(char * str)
182 chptr = str + strlen(str) - 1;
183 while (*chptr == '/' && chptr >= str) {
189 int doputenv(const char *str)
193 /* FIXME: this leaks memory! */
195 a = xmalloc(strlen(str) + 1);
201 int dosetenv(const char *name, const char *value, int overwrite)
206 /* FIXME: this leaks memory! */
208 if (!overwrite && getenv(name)) return 0;
210 i = strlen(name) + strlen(value) + 2;
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 :-( */
228 int unameToUid(const char * thisUname, uid_t * uid)
230 /*@only@*/ static char * lastUname = NULL;
231 static int lastUnameLen = 0;
232 static int lastUnameAlloced;
233 static uid_t lastUid;
234 struct passwd * pwent;
240 } else if (!strcmp(thisUname, "root")) {
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 */
252 strcpy(lastUname, thisUname);
254 pwent = getpwnam(thisUname);
257 pwent = getpwnam(thisUname);
258 if (!pwent) return -1;
261 lastUid = pwent->pw_uid;
269 int gnameToGid(const char * thisGname, gid_t * gid)
271 /*@only@*/ static char * lastGname = NULL;
272 static int lastGnameLen = 0;
273 static int lastGnameAlloced;
274 static uid_t lastGid;
276 struct group * grent;
281 } else if (!strcmp(thisGname, "root")) {
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 */
293 strcpy(lastGname, thisGname);
295 grent = getgrnam(thisGname);
298 grent = getgrnam(thisGname);
299 if (!grent) return -1;
301 lastGid = grent->gr_gid;
309 char * uidToUname(uid_t uid)
311 static int lastUid = -1;
312 /*@only@*/ static char * lastUname = NULL;
313 static int lastUnameLen = 0;
314 struct passwd * pwent;
317 if (uid == (uid_t) -1) {
322 } else if (uid == lastUid) {
325 pwent = getpwuid(uid);
326 if (!pwent) return NULL;
329 len = strlen(pwent->pw_name);
330 if (lastUnameLen < len + 1) {
331 lastUnameLen = len + 20;
332 lastUname = xrealloc(lastUname, lastUnameLen);
334 strcpy(lastUname, pwent->pw_name);
340 char * gidToGname(gid_t gid)
342 static int lastGid = -1;
343 /*@only@*/ static char * lastGname = NULL;
344 static int lastGnameLen = 0;
345 struct group * grent;
348 if (gid == (gid_t) -1) {
353 } else if (gid == lastGid) {
356 grent = getgrgid(gid);
357 if (!grent) return NULL;
360 len = strlen(grent->gr_name);
361 if (lastGnameLen < len + 1) {
362 lastGnameLen = len + 20;
363 lastGname = xrealloc(lastGname, lastGnameLen);
365 strcpy(lastGname, grent->gr_name);
371 int makeTempFile(const char * prefix, const char ** fnptr, FD_t * fdptr)
373 const char * tempfn = NULL;
374 const char * tfn = NULL;
379 if (!prefix) prefix = "";
381 /* XXX should probably use mktemp here */
383 ran = rand() % 100000;
385 /* maybe this should use link/stat? */
390 sprintf(tfnbuf, "rpm-tmp.%d", ran++);
391 if (tempfn) xfree(tempfn);
392 tempfn = rpmGenPath(prefix, "%{_tmppath}/", tfnbuf);
394 strcpy(tfnbuf, "rpm-tmp.XXXXXX");
395 if (tempfn) xfree(tempfn);
396 tempfn = rpmGenPath(prefix, "%{_tmppath}/", mktemp(tfnbuf));
399 temput = urlPath(tempfn, &tfn);
400 if (*tfn == '\0') goto errxit;
406 /*@notreached@*/ break;
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);
415 if (fd == NULL || Ferror(fd))
422 if (!stat(tfn, &sb) && S_ISLNK(sb.st_mode)) {
423 rpmError(RPMERR_SCRIPT, _("error creating temporary file %s"), tfn);
427 if (sb.st_nlink != 1) {
428 rpmError(RPMERR_SCRIPT, _("error creating temporary file %s"), tfn);
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);
454 if (tempfn) xfree(tempfn);
459 char * currentDirectory(void)
465 currDir = xmalloc(currDirLen);
466 while (!getcwd(currDir, currDirLen) && errno == ERANGE) {
468 currDir = xrealloc(currDir, currDirLen);
474 int _noDirTokens = 0;
476 static int dncmp(const void * a, const void * b)
478 const char *const * first = a;
479 const char *const * second = b;
480 return strcmp(*first, *second);
483 void compressFilelist(Header h)
486 const char ** dirNames;
487 const char ** baseNames;
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
499 if (!headerGetEntry(h, RPMTAG_OLDFILENAMES, NULL,
500 (void **) &fileNames, &count))
501 return; /* no file list */
503 dirNames = alloca(sizeof(*dirNames) * count); /* worst case */
504 baseNames = alloca(sizeof(*dirNames) * count);
505 dirIndexes = alloca(sizeof(*dirIndexes) * count);
507 if (fileNames[0][0] != '/') {
508 /* HACK. Source RPM, so just do things differently */
510 dirNames[dirIndex] = "";
511 for (i = 0; i < count; i++) {
512 dirIndexes[i] = dirIndex;
513 baseNames[i] = fileNames[i];
518 for (i = 0; i < count; i++) {
519 const char ** needle;
520 char *baseName = strrchr(fileNames[i], '/') + 1;
522 int len = baseName - fileNames[i];
524 savechar = *baseName;
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);
531 dirIndexes[i] = ++dirIndex;
532 dirNames[dirIndex] = s;
534 dirIndexes[i] = needle - dirNames;
536 *baseName = savechar;
537 baseNames[i] = baseName;
541 headerAddEntry(h, RPMTAG_DIRNAMES, RPM_STRING_ARRAY_TYPE,
542 dirNames, dirIndex + 1);
543 headerAddEntry(h, RPMTAG_DIRINDEXES, RPM_INT32_TYPE,
545 headerAddEntry(h, RPMTAG_BASENAMES, RPM_STRING_ARRAY_TYPE,
550 headerRemoveEntry(h, RPMTAG_OLDFILENAMES);
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.
557 static void doBuildFileList(Header h, /*@out@*/ const char *** fileListPtr,
558 /*@out@*/ int * fileCountPtr, int baseNameTag,
559 int dirNameTag, int dirIndexesTag)
561 const char ** baseNames;
562 const char ** dirNames;
565 const char ** fileNames;
570 if (!headerGetEntry(h, baseNameTag, NULL, (void **) &baseNames, &count)) {
573 return; /* no file list */
576 headerGetEntry(h, dirNameTag, NULL, (void **) &dirNames, NULL);
577 headerGetEntry(h, dirIndexesTag, NULL, (void **) &dirIndexes, &count);
579 size = sizeof(*fileNames) * count;
580 for (i = 0; i < count; i++)
581 size += strlen(baseNames[i]) + strlen(dirNames[dirIndexes[i]]) + 1;
583 fileNames = xmalloc(size);
584 data = ((char *) fileNames) + (sizeof(*fileNames) * count);
585 for (i = 0; i < count; i++) {
587 data = stpcpy( stpcpy(data, dirNames[dirIndexes[i]]), baseNames[i]);
593 *fileListPtr = fileNames;
594 *fileCountPtr = count;
597 void expandFilelist(Header h)
599 const char ** fileNames = NULL;
602 doBuildFileList(h, &fileNames, &count, RPMTAG_BASENAMES,
603 RPMTAG_DIRNAMES, RPMTAG_DIRINDEXES);
605 if (fileNames == NULL || count <= 0)
608 headerAddEntry(h, RPMTAG_OLDFILENAMES, RPM_STRING_ARRAY_TYPE,
613 headerRemoveEntry(h, RPMTAG_BASENAMES);
614 headerRemoveEntry(h, RPMTAG_DIRNAMES);
615 headerRemoveEntry(h, RPMTAG_DIRINDEXES);
619 void rpmBuildFileList(Header h, const char *** fileListPtr, int * fileCountPtr)
621 doBuildFileList(h, fileListPtr, fileCountPtr, RPMTAG_BASENAMES,
622 RPMTAG_DIRNAMES, RPMTAG_DIRINDEXES);
625 void buildOrigFileList(Header h, const char *** fileListPtr, int * fileCountPtr)
627 doBuildFileList(h, fileListPtr, fileCountPtr, RPMTAG_ORIGBASENAMES,
628 RPMTAG_ORIGDIRNAMES, RPMTAG_ORIGDIRINDEXES);
631 /* glob_pattern_p() taken from bash
632 * Copyright (C) 1985, 1988, 1989 Free Software Foundation, Inc.
634 * Return nonzero if PATTERN has any special globbing chars in it.
636 int myGlobPatternP (const char *patternURL)
642 (void) urlPath(patternURL, &p);
643 while ((c = *p++) != '\0')
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 */
663 static int glob_error(/*@unused@*/const char *foo, /*@unused@*/int bar)
668 int rpmGlob(const char * patterns, int * argcPtr, const char *** argvPtr)
671 const char ** av = NULL;
673 const char ** argv = NULL;
675 const char * globURL;
676 char * globRoot = NULL;
683 rc = poptParseArgvString(patterns, &ac, &av);
687 for (j = 0; j < ac; j++) {
688 if (!myGlobPatternP(av[j])) {
690 argv = xmalloc((argc+2) * sizeof(*argv));
692 argv = xrealloc(argv, (argc+2) * sizeof(*argv));
694 fprintf(stderr, "*** rpmGlob argv[%d] \"%s\"\n", argc, av[j]);
695 argv[argc++] = xstrdup(av[j]);
701 rc = Glob(av[j], 0, glob_error, &gl);
705 /* XXX Prepend the URL leader for globs that have stripped it off */
707 for (i = 0; i < gl.gl_pathc; i++) {
708 if ((nb = strlen(&(gl.gl_pathv[i][0]))) > maxb)
712 ut = urlPath(av[j], &path);
713 nb = ((ut > URL_IS_DASH) ? (path - av[j]) : 0);
716 globURL = globRoot = xmalloc(maxb);
723 strncpy(globRoot, av[j], nb);
731 fprintf(stderr, "*** GLOB maxb %d diskURL %d %*s globURL %p %s\n", (int)maxb, (int)nb, (int)nb, av[j], globURL, globURL);
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);
743 fprintf(stderr, "*** rpmGlob argv[%d] \"%s\"\n", argc, globURL);
744 argv[argc++] = xstrdup(globURL);
759 if ((rc || argvPtr == NULL) && argv) {
760 for (i = 0; i < argc; i++)
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).
773 int rpmHeaderGetEntry(Header h, int_32 tag, int_32 *type,
777 case RPMTAG_OLDFILENAMES:
778 { const char ** fl = NULL;
780 rpmBuildFileList(h, &fl, &count);
784 if (type) *type = RPM_STRING_ARRAY_TYPE;
789 } /*@notreached@*/ break;
792 case RPMTAG_DESCRIPTION:
799 (void) stpcpy( stpcpy( stpcpy( fmt, "%{"), tagName(tag)), "}\n");
801 /* XXX FIXME: memory leak. */
802 msgstr = headerSprintf(h, fmt, rpmTagTable, rpmHeaderFormats, &errstr);
804 *p = (void *) msgstr;
805 if (type) *type = RPM_STRING_TYPE;
812 } /*@notreached@*/ break;
815 return headerGetEntry(h, tag, type, p, c);
816 /*@notreached@*/ break;
822 * Up to rpm 3.0.4, packages implicitly provided their own name-version-release.
823 * Retrofit an explicit "Provides: name = epoch:version-release.
825 void providePackageNVR(Header h)
827 const char *name, *version, *release;
831 int_32 pFlags = RPMSENSE_EQUAL;
832 const char ** provides = NULL;
833 const char ** providesEVR = NULL;
834 int_32 * provideFlags = NULL;
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);
843 if (headerGetEntry(h, RPMTAG_EPOCH, NULL, (void **) &epoch, NULL)) {
844 sprintf(p, "%d:", *epoch);
848 (void) stpcpy( stpcpy( stpcpy(p, version) , "-") , release);
851 * Rpm prior to 3.0.3 does not have versioned provides.
852 * If no provides at all are available, we can just add.
854 if (!headerGetEntry(h, RPMTAG_PROVIDENAME, NULL,
855 (void **) &provides, &providesCount)) {
860 * Otherwise, fill in entries on legacy packages.
862 if (!headerGetEntry(h, RPMTAG_PROVIDEVERSION, NULL,
863 (void **) &providesEVR, NULL)) {
864 for (i = 0; i < providesCount; i++) {
866 int_32 fdummy = RPMSENSE_ANY;
867 headerAddOrAppendEntry(h, RPMTAG_PROVIDEVERSION, RPM_STRING_ARRAY_TYPE,
869 headerAddOrAppendEntry(h, RPMTAG_PROVIDEFLAGS, RPM_INT32_TYPE,
875 headerGetEntry(h, RPMTAG_PROVIDEFLAGS, NULL,
876 (void **) &provideFlags, NULL);
878 for (i = 0; i < providesCount; i++) {
879 if (!(provideFlags[i] == RPMSENSE_EQUAL &&
880 !strcmp(name, provides[i]) && !strcmp(pEVR, providesEVR[i])))
887 if (provides) xfree(provides);
888 if (providesEVR) xfree(providesEVR);
891 headerAddOrAppendEntry(h, RPMTAG_PROVIDENAME, RPM_STRING_ARRAY_TYPE,
893 headerAddOrAppendEntry(h, RPMTAG_PROVIDEVERSION, RPM_STRING_ARRAY_TYPE,
895 headerAddOrAppendEntry(h, RPMTAG_PROVIDEFLAGS, RPM_INT32_TYPE,