summaryrefslogtreecommitdiff
path: root/rpmio/rpmfileutil.c
diff options
context:
space:
mode:
Diffstat (limited to 'rpmio/rpmfileutil.c')
-rw-r--r--rpmio/rpmfileutil.c777
1 files changed, 777 insertions, 0 deletions
diff --git a/rpmio/rpmfileutil.c b/rpmio/rpmfileutil.c
new file mode 100644
index 0000000..3641341
--- /dev/null
+++ b/rpmio/rpmfileutil.c
@@ -0,0 +1,777 @@
+#include "system.h"
+
+#if HAVE_GELF_H
+
+#include <gelf.h>
+
+#if !defined(DT_GNU_PRELINKED)
+#define DT_GNU_PRELINKED 0x6ffffdf5
+#endif
+#if !defined(DT_GNU_LIBLIST)
+#define DT_GNU_LIBLIST 0x6ffffef9
+#endif
+
+#endif
+
+#if defined(HAVE_MMAP)
+#include <sys/mman.h>
+#endif
+
+#include <sys/wait.h>
+#include <errno.h>
+#include <popt.h>
+#include <ctype.h>
+
+#include <rpm/rpmfileutil.h>
+#include <rpm/rpmurl.h>
+#include <rpm/rpmmacro.h>
+#include <rpm/rpmlog.h>
+#include <rpm/argv.h>
+
+#include "rpmio/rpmio_internal.h"
+
+#include "debug.h"
+
+static const char *rpm_config_dir = NULL;
+
+static int open_dso(const char * path, pid_t * pidp, rpm_loff_t *fsizep)
+{
+ static const char * cmd = NULL;
+ static int initted = 0;
+ int fdno;
+
+ if (!initted) {
+ cmd = rpmExpand("%{?__prelink_undo_cmd}", NULL);
+ initted++;
+ }
+
+ if (pidp) *pidp = 0;
+
+ if (fsizep) {
+ struct stat sb, * st = &sb;
+ if (stat(path, st) < 0)
+ return -1;
+ *fsizep = st->st_size;
+ }
+
+ fdno = open(path, O_RDONLY);
+ if (fdno < 0)
+ return fdno;
+
+ if (!(cmd && *cmd))
+ return fdno;
+
+#if HAVE_GELF_H && HAVE_LIBELF
+ { Elf *elf = NULL;
+ Elf_Scn *scn = NULL;
+ Elf_Data *data = NULL;
+ GElf_Ehdr ehdr;
+ GElf_Shdr shdr;
+ GElf_Dyn dyn;
+ int bingo;
+
+ (void) elf_version(EV_CURRENT);
+
+ if ((elf = elf_begin (fdno, ELF_C_READ, NULL)) == NULL
+ || elf_kind(elf) != ELF_K_ELF
+ || gelf_getehdr(elf, &ehdr) == NULL
+ || !(ehdr.e_type == ET_DYN || ehdr.e_type == ET_EXEC))
+ goto exit;
+
+ bingo = 0;
+ while (!bingo && (scn = elf_nextscn(elf, scn)) != NULL) {
+ (void) gelf_getshdr(scn, &shdr);
+ if (shdr.sh_type != SHT_DYNAMIC)
+ continue;
+ while (!bingo && (data = elf_getdata (scn, data)) != NULL) {
+ int maxndx = data->d_size / shdr.sh_entsize;
+ int ndx;
+
+ for (ndx = 0; ndx < maxndx; ++ndx) {
+ (void) gelf_getdyn (data, ndx, &dyn);
+ if (!(dyn.d_tag == DT_GNU_PRELINKED || dyn.d_tag == DT_GNU_LIBLIST))
+ continue;
+ bingo = 1;
+ break;
+ }
+ }
+ }
+
+ if (pidp != NULL && bingo) {
+ int pipes[2];
+ pid_t pid;
+ int xx;
+
+ xx = close(fdno);
+ pipes[0] = pipes[1] = -1;
+ xx = pipe(pipes);
+ if (!(pid = fork())) {
+ ARGV_t av, lib;
+ argvSplit(&av, cmd, " ");
+
+ xx = close(pipes[0]);
+ xx = dup2(pipes[1], STDOUT_FILENO);
+ xx = close(pipes[1]);
+ if ((lib = argvSearch(av, "library", NULL)) != NULL) {
+ *lib = (char *) path;
+ unsetenv("MALLOC_CHECK_");
+ xx = execve(av[0], av+1, environ);
+ }
+ _exit(127);
+ }
+ *pidp = pid;
+ fdno = pipes[0];
+ xx = close(pipes[1]);
+ }
+
+exit:
+ if (elf) (void) elf_end(elf);
+ }
+#endif
+
+ return fdno;
+}
+
+int rpmDoDigest(int algo, const char * fn,int asAscii,
+ unsigned char * digest, rpm_loff_t * fsizep)
+{
+ const char * path;
+ urltype ut = urlPath(fn, &path);
+ unsigned char * dig = NULL;
+ size_t diglen;
+ unsigned char buf[32*BUFSIZ];
+ FD_t fd;
+ rpm_loff_t fsize = 0;
+ pid_t pid = 0;
+ int rc = 0;
+ int fdno;
+
+ fdno = open_dso(path, &pid, &fsize);
+ if (fdno < 0) {
+ rc = 1;
+ goto exit;
+ }
+
+ /* file to large (32 MB), do not mmap file */
+ if (fsize > (size_t) 32*1024*1024)
+ if (ut == URL_IS_PATH || ut == URL_IS_UNKNOWN)
+ ut = URL_IS_DASH; /* force fd io */
+
+ switch(ut) {
+ case URL_IS_PATH:
+ case URL_IS_UNKNOWN:
+#ifdef HAVE_MMAP
+ if (pid == 0) {
+ int xx;
+ DIGEST_CTX ctx;
+ void * mapped;
+
+ if (fsize) {
+ mapped = mmap(NULL, fsize, PROT_READ, MAP_SHARED, fdno, 0);
+ if (mapped == MAP_FAILED) {
+ xx = close(fdno);
+ rc = 1;
+ break;
+ }
+
+#ifdef MADV_SEQUENTIAL
+ xx = madvise(mapped, fsize, MADV_SEQUENTIAL);
+#endif
+ }
+
+ ctx = rpmDigestInit(algo, RPMDIGEST_NONE);
+ if (fsize)
+ xx = rpmDigestUpdate(ctx, mapped, fsize);
+ xx = rpmDigestFinal(ctx, (void **)&dig, &diglen, asAscii);
+ if (fsize)
+ xx = munmap(mapped, fsize);
+ xx = close(fdno);
+ break;
+ }
+#endif
+ case URL_IS_HTTPS:
+ case URL_IS_HTTP:
+ case URL_IS_FTP:
+ case URL_IS_HKP:
+ case URL_IS_DASH:
+ default:
+ /* Either use the pipe to prelink -y or open the URL. */
+ fd = (pid != 0) ? fdDup(fdno) : Fopen(fn, "r.ufdio");
+ (void) close(fdno);
+ if (fd == NULL || Ferror(fd)) {
+ rc = 1;
+ if (fd != NULL)
+ (void) Fclose(fd);
+ break;
+ }
+
+ fdInitDigest(fd, algo, 0);
+ fsize = 0;
+ while ((rc = Fread(buf, sizeof(buf[0]), sizeof(buf), fd)) > 0)
+ fsize += rc;
+ fdFiniDigest(fd, algo, (void **)&dig, &diglen, asAscii);
+ if (Ferror(fd))
+ rc = 1;
+
+ (void) Fclose(fd);
+ break;
+ }
+
+ /* Reap the prelink -y helper. */
+ if (pid) {
+ int status;
+ (void) waitpid(pid, &status, 0);
+ if (!WIFEXITED(status) || WEXITSTATUS(status))
+ rc = 1;
+ }
+
+exit:
+ if (fsizep)
+ *fsizep = fsize;
+ if (!rc)
+ memcpy(digest, dig, diglen);
+ dig = _free(dig);
+
+ return rc;
+}
+
+FD_t rpmMkTemp(char *templ)
+{
+ int sfd;
+ FD_t tfd = NULL;
+
+ sfd = mkstemp(templ);
+ if (sfd < 0) {
+ goto exit;
+ }
+
+ tfd = fdDup(sfd);
+ close(sfd);
+
+exit:
+ return tfd;
+}
+
+FD_t rpmMkTempFile(const char * prefix, char **fn)
+{
+ const char *tpmacro = "%{_tmppath}"; /* always set from rpmrc */
+ char *tempfn;
+ static int _initialized = 0;
+ FD_t tfd = NULL;
+
+ if (!prefix) prefix = "";
+
+ /* Create the temp directory if it doesn't already exist. */
+ if (!_initialized) {
+ _initialized = 1;
+ tempfn = rpmGenPath(prefix, tpmacro, NULL);
+ if (rpmioMkpath(tempfn, 0755, (uid_t) -1, (gid_t) -1))
+ goto exit;
+ free(tempfn);
+ }
+
+ tempfn = rpmGetPath(prefix, tpmacro, "/rpm-tmp.XXXXXX", NULL);
+ tfd = rpmMkTemp(tempfn);
+
+ if (tfd == NULL || Ferror(tfd)) {
+ rpmlog(RPMLOG_ERR, _("error creating temporary file %s: %m\n"), tempfn);
+ goto exit;
+ }
+
+exit:
+ if (tfd != NULL && fn)
+ *fn = tempfn;
+ else
+ free(tempfn);
+
+ return tfd;
+}
+
+int rpmioMkpath(const char * path, mode_t mode, uid_t uid, gid_t gid)
+{
+ char *d, *de;
+ int rc;
+
+ if (path == NULL || *path == '\0')
+ return -1;
+ d = rstrcat(NULL, path);
+ if (d[strlen(d)-1] != '/') {
+ rstrcat(&d,"/");
+ }
+ de = d;
+ for (;(de=strchr(de+1,'/'));) {
+ struct stat st;
+ *de = '\0';
+ rc = stat(d, &st);
+ if (rc) {
+ if (errno != ENOENT)
+ goto exit;
+ rc = mkdir(d, mode);
+ if (rc)
+ goto exit;
+ rpmlog(RPMLOG_DEBUG, "created directory(s) %s mode 0%o\n", path, mode);
+ if (!(uid == (uid_t) -1 && gid == (gid_t) -1)) {
+ rc = chown(d, uid, gid);
+ if (rc)
+ goto exit;
+ }
+ } else if (!S_ISDIR(st.st_mode)) {
+ rc = ENOTDIR;
+ goto exit;
+ }
+ *de = '/';
+ }
+ rc = 0;
+exit:
+ free(d);
+ return rc;
+}
+
+int rpmFileIsCompressed(const char * file, rpmCompressedMagic * compressed)
+{
+ FD_t fd;
+ ssize_t nb;
+ int rc = -1;
+ unsigned char magic[13];
+
+ *compressed = COMPRESSED_NOT;
+
+ fd = Fopen(file, "r.ufdio");
+ if (fd == NULL || Ferror(fd)) {
+ /* XXX Fstrerror */
+ rpmlog(RPMLOG_ERR, _("File %s: %s\n"), file, Fstrerror(fd));
+ if (fd) (void) Fclose(fd);
+ return 1;
+ }
+ nb = Fread(magic, sizeof(magic[0]), sizeof(magic), fd);
+ if (nb < 0) {
+ rpmlog(RPMLOG_ERR, _("File %s: %s\n"), file, Fstrerror(fd));
+ rc = 1;
+ } else if (nb < sizeof(magic)) {
+ rpmlog(RPMLOG_ERR, _("File %s is smaller than %u bytes\n"),
+ file, (unsigned)sizeof(magic));
+ rc = 0;
+ }
+ (void) Fclose(fd);
+ if (rc >= 0)
+ return rc;
+
+ rc = 0;
+
+ if ((magic[0] == 'B') && (magic[1] == 'Z')) {
+ *compressed = COMPRESSED_BZIP2;
+ } else if ((magic[0] == 'P') && (magic[1] == 'K') &&
+ (((magic[2] == 3) && (magic[3] == 4)) ||
+ ((magic[2] == '0') && (magic[3] == '0')))) { /* pkzip */
+ *compressed = COMPRESSED_ZIP;
+ } else if ((magic[0] == 0xfd) && (magic[1] == 0x37) &&
+ (magic[2] == 0x7a) && (magic[3] == 0x58) &&
+ (magic[4] == 0x5a) && (magic[5] == 0x00)) {
+ /* new style xz (lzma) with magic */
+ *compressed = COMPRESSED_XZ;
+ } else if ((magic[0] == 'L') && (magic[1] == 'Z') &&
+ (magic[2] == 'I') && (magic[3] == 'P')) {
+ *compressed = COMPRESSED_LZIP;
+ } else if ((magic[0] == 'L') && (magic[1] == 'R') &&
+ (magic[2] == 'Z') && (magic[3] == 'I')) {
+ *compressed = COMPRESSED_LRZIP;
+ } else if (((magic[0] == 0037) && (magic[1] == 0213)) || /* gzip */
+ ((magic[0] == 0037) && (magic[1] == 0236)) || /* old gzip */
+ ((magic[0] == 0037) && (magic[1] == 0036)) || /* pack */
+ ((magic[0] == 0037) && (magic[1] == 0240)) || /* SCO lzh */
+ ((magic[0] == 0037) && (magic[1] == 0235)) /* compress */
+ ) {
+ *compressed = COMPRESSED_OTHER;
+ } else if (rpmFileHasSuffix(file, ".lzma")) {
+ *compressed = COMPRESSED_LZMA;
+ }
+
+ return rc;
+}
+
+/* @todo "../sbin/./../bin/" not correct. */
+char *rpmCleanPath(char * path)
+{
+ const char *s;
+ char *se, *t, *te;
+ int begin = 1;
+
+ if (path == NULL)
+ return NULL;
+
+/*fprintf(stderr, "*** RCP %s ->\n", path); */
+ s = t = te = path;
+ while (*s != '\0') {
+/*fprintf(stderr, "*** got \"%.*s\"\trest \"%s\"\n", (t-path), path, s); */
+ switch(*s) {
+ case ':': /* handle url's */
+ if (s[1] == '/' && s[2] == '/') {
+ *t++ = *s++;
+ *t++ = *s++;
+ break;
+ }
+ begin=1;
+ break;
+ case '/':
+ /* Move parent dir forward */
+ for (se = te + 1; se < t && *se != '/'; se++)
+ {};
+ if (se < t && *se == '/') {
+ te = se;
+/*fprintf(stderr, "*** next pdir \"%.*s\"\n", (te-path), path); */
+ }
+ while (s[1] == '/')
+ s++;
+ while (t > path && t[-1] == '/')
+ t--;
+ break;
+ case '.':
+ /* Leading .. is special */
+ /* Check that it is ../, so that we don't interpret */
+ /* ..?(i.e. "...") or ..* (i.e. "..bogus") as "..". */
+ /* in the case of "...", this ends up being processed*/
+ /* as "../.", and the last '.' is stripped. This */
+ /* would not be correct processing. */
+ if (begin && s[1] == '.' && (s[2] == '/' || s[2] == '\0')) {
+/*fprintf(stderr, " leading \"..\"\n"); */
+ *t++ = *s++;
+ break;
+ }
+ /* Single . is special */
+ if (begin && s[1] == '\0') {
+ break;
+ }
+ /* Handle the ./ cases */
+ if (t > path && t[-1] == '/') {
+ /* Trim embedded ./ */
+ if (s[1] == '/') {
+ s+=2;
+ continue;
+ }
+ /* Trim trailing /. */
+ if (s[1] == '\0') {
+ s++;
+ continue;
+ }
+ }
+ /* Trim embedded /../ and trailing /.. */
+ if (!begin && t > path && t[-1] == '/' && s[1] == '.' && (s[2] == '/' || s[2] == '\0')) {
+ t = te;
+ /* Move parent dir forward */
+ if (te > path)
+ for (--te; te > path && *te != '/'; te--)
+ {};
+/*fprintf(stderr, "*** prev pdir \"%.*s\"\n", (te-path), path); */
+ s++;
+ s++;
+ continue;
+ }
+ break;
+ default:
+ begin = 0;
+ break;
+ }
+ *t++ = *s++;
+ }
+
+ /* Trim trailing / (but leave single / alone) */
+ if (t > &path[1] && t[-1] == '/')
+ t--;
+ *t = '\0';
+
+/*fprintf(stderr, "\t%s\n", path); */
+ return path;
+}
+
+/* Merge 3 args into path, any or all of which may be a url. */
+
+char * rpmGenPath(const char * urlroot, const char * urlmdir,
+ const char *urlfile)
+{
+ char * xroot = rpmGetPath(urlroot, NULL);
+ const char * root = xroot;
+ char * xmdir = rpmGetPath(urlmdir, NULL);
+ const char * mdir = xmdir;
+ char * xfile = rpmGetPath(urlfile, NULL);
+ const char * file = xfile;
+ char * result;
+ char * url = NULL;
+ int nurl = 0;
+ int ut;
+
+ ut = urlPath(xroot, &root);
+ if (url == NULL && ut > URL_IS_DASH) {
+ url = xroot;
+ nurl = root - xroot;
+ }
+ if (root == NULL || *root == '\0') root = "/";
+
+ ut = urlPath(xmdir, &mdir);
+ if (url == NULL && ut > URL_IS_DASH) {
+ url = xmdir;
+ nurl = mdir - xmdir;
+ }
+ if (mdir == NULL || *mdir == '\0') mdir = "/";
+
+ ut = urlPath(xfile, &file);
+ if (url == NULL && ut > URL_IS_DASH) {
+ url = xfile;
+ nurl = file - xfile;
+ }
+
+ if (url && nurl > 0) {
+ char *t = rstrcat(NULL, url);
+ t[nurl] = '\0';
+ url = t;
+ } else
+ url = xstrdup("");
+
+ result = rpmGetPath(url, root, "/", mdir, "/", file, NULL);
+
+ xroot = _free(xroot);
+ xmdir = _free(xmdir);
+ xfile = _free(xfile);
+ free(url);
+ return result;
+}
+
+/* Return concatenated and expanded canonical path. */
+
+char * rpmGetPath(const char *path, ...)
+{
+ va_list ap;
+ char *dest = NULL, *res;
+ const char *s;
+
+ if (path == NULL)
+ return xstrdup("");
+
+ va_start(ap, path);
+ for (s = path; s; s = va_arg(ap, const char *)) {
+ rstrcat(&dest, s);
+ }
+ va_end(ap);
+
+ res = rpmExpand(dest, NULL);
+ free(dest);
+
+ return rpmCleanPath(res);
+}
+
+int rpmGlob(const char * patterns, int * argcPtr, ARGV_t * argvPtr)
+{
+ int ac = 0;
+ const char ** av = NULL;
+ int argc = 0;
+ ARGV_t argv = NULL;
+ char * globRoot = NULL;
+ const char *home = getenv("HOME");
+ int gflags = 0;
+#ifdef ENABLE_NLS
+ char * old_collate = NULL;
+ char * old_ctype = NULL;
+ const char * t;
+#endif
+ size_t maxb, nb;
+ int i, j;
+ int rc;
+
+ if (home != NULL && strlen(home) > 0)
+ gflags |= GLOB_TILDE;
+
+ /* Can't use argvSplit() here, it doesn't handle whitespace etc escapes */
+ rc = poptParseArgvString(patterns, &ac, &av);
+ if (rc)
+ return rc;
+
+#ifdef ENABLE_NLS
+ t = setlocale(LC_COLLATE, NULL);
+ if (t)
+ old_collate = xstrdup(t);
+ t = setlocale(LC_CTYPE, NULL);
+ if (t)
+ old_ctype = xstrdup(t);
+ (void) setlocale(LC_COLLATE, "C");
+ (void) setlocale(LC_CTYPE, "C");
+#endif
+
+ if (av != NULL)
+ for (j = 0; j < ac; j++) {
+ char * globURL;
+ const char * path;
+ int ut = urlPath(av[j], &path);
+ int local = (ut == URL_IS_PATH) || (ut == URL_IS_UNKNOWN);
+ size_t plen = strlen(path);
+ int flags = gflags;
+ int dir_only = (plen > 0 && path[plen-1] == '/');
+ glob_t gl;
+
+ if (!local || (!glob_pattern_p(av[j], 0) && strchr(path, '~') == NULL)) {
+ argvAdd(&argv, av[j]);
+ continue;
+ }
+
+#ifdef GLOB_ONLYDIR
+ if (dir_only)
+ flags |= GLOB_ONLYDIR;
+#endif
+
+ gl.gl_pathc = 0;
+ gl.gl_pathv = NULL;
+
+ rc = glob(av[j], flags, NULL, &gl);
+ if (rc)
+ goto exit;
+
+ /* XXX Prepend the URL leader for globs that have stripped it off */
+ maxb = 0;
+ for (i = 0; i < gl.gl_pathc; i++) {
+ if ((nb = strlen(&(gl.gl_pathv[i][0]))) > maxb)
+ maxb = nb;
+ }
+
+ nb = ((ut == URL_IS_PATH) ? (path - av[j]) : 0);
+ maxb += nb;
+ maxb += 1;
+ globURL = globRoot = xmalloc(maxb);
+
+ switch (ut) {
+ case URL_IS_PATH:
+ case URL_IS_DASH:
+ strncpy(globRoot, av[j], nb);
+ break;
+ case URL_IS_HTTPS:
+ case URL_IS_HTTP:
+ case URL_IS_FTP:
+ case URL_IS_HKP:
+ case URL_IS_UNKNOWN:
+ default:
+ break;
+ }
+ globRoot += nb;
+ *globRoot = '\0';
+
+ for (i = 0; i < gl.gl_pathc; i++) {
+ const char * globFile = &(gl.gl_pathv[i][0]);
+
+ if (dir_only) {
+ struct stat sb;
+ if (lstat(gl.gl_pathv[i], &sb) || !S_ISDIR(sb.st_mode))
+ continue;
+ }
+
+ if (globRoot > globURL && globRoot[-1] == '/')
+ while (*globFile == '/') globFile++;
+ strcpy(globRoot, globFile);
+ argvAdd(&argv, globURL);
+ }
+ globfree(&gl);
+ globURL = _free(globURL);
+ }
+
+ argc = argvCount(argv);
+ if (argc > 0) {
+ if (argvPtr)
+ *argvPtr = argv;
+ if (argcPtr)
+ *argcPtr = argc;
+ rc = 0;
+ } else
+ rc = 1;
+
+
+exit:
+#ifdef ENABLE_NLS
+ if (old_collate) {
+ (void) setlocale(LC_COLLATE, old_collate);
+ old_collate = _free(old_collate);
+ }
+ if (old_ctype) {
+ (void) setlocale(LC_CTYPE, old_ctype);
+ old_ctype = _free(old_ctype);
+ }
+#endif
+ av = _free(av);
+ if (rc || argvPtr == NULL) {
+ argvFree(argv);
+ }
+ return rc;
+}
+
+char * rpmEscapeSpaces(const char * s)
+{
+ const char * se;
+ char * t;
+ char * te;
+ size_t nb = 0;
+
+ for (se = s; *se; se++) {
+ if (isspace(*se))
+ nb++;
+ nb++;
+ }
+ nb++;
+
+ t = te = xmalloc(nb);
+ for (se = s; *se; se++) {
+ if (isspace(*se))
+ *te++ = '\\';
+ *te++ = *se;
+ }
+ *te = '\0';
+ return t;
+}
+
+int rpmFileHasSuffix(const char *path, const char *suffix)
+{
+ size_t plen = strlen(path);
+ size_t slen = strlen(suffix);
+ return (plen >= slen && rstreq(path+plen-slen, suffix));
+}
+
+char * rpmGetCwd(void)
+{
+ int currDirLen = 0;
+ char * currDir = NULL;
+
+ do {
+ currDirLen += 128;
+ currDir = xrealloc(currDir, currDirLen);
+ memset(currDir, 0, currDirLen);
+ } while (getcwd(currDir, currDirLen) == NULL && errno == ERANGE);
+
+ return currDir;
+}
+
+int rpmMkdirs(const char *root, const char *pathstr)
+{
+ ARGV_t dirs = NULL;
+ int rc = 0;
+ argvSplit(&dirs, pathstr, ":");
+
+ for (char **d = dirs; *d; d++) {
+ char *path = rpmGetPath(root ? root : "", *d, NULL);
+ if ((rc = rpmioMkpath(path, 0755, -1, -1)) != 0) {
+ const char *msg = _("failed to create directory");
+ /* try to be more informative if the failing part was a macro */
+ if (**d == '%') {
+ rpmlog(RPMLOG_ERR, "%s %s: %s: %m\n", msg, *d, path);
+ } else {
+ rpmlog(RPMLOG_ERR, "%s %s: %m\n", msg, path);
+ }
+ }
+ free(path);
+ if (rc) break;
+ }
+ argvFree(dirs);
+ return rc;
+}
+
+const char *rpmConfigDir(void)
+{
+ if (rpm_config_dir == NULL) {
+ char *rpmenv = getenv("RPM_CONFIGDIR");
+ rpm_config_dir = rpmenv ? xstrdup(rpmenv) : RPMCONFIGDIR;
+ }
+ return rpm_config_dir;
+}