Blame lib/rpmgi.c

2ff057
/** \ingroup rpmio
2ff057
 * \file lib/rpmgi.c
2ff057
 */
2ff057
#include "system.h"
2ff057
2ff057
#include <errno.h>
2ff057
2ff057
#include <rpm/rpmtypes.h>
2ff057
#include <rpm/rpmlib.h>		/* rpmReadPackageFile */
2ff057
#include <rpm/rpmts.h>
2ff057
#include <rpm/rpmmacro.h>		/* XXX rpmExpand */
2ff057
#include <rpm/rpmfileutil.h>
2ff057
#include <rpm/rpmlog.h>
2ff057
2ff057
#include "lib/rpmgi.h"
2ff057
#include "lib/manifest.h"
2ff057
2ff057
#include "debug.h"
2ff057
2ff057
#define MANIFEST_RECURSIONS 1000 /* Max. number of allowed manifest recursions */
2ff057
2ff057
RPM_GNUC_INTERNAL
2ff057
rpmgiFlags giFlags = RPMGI_NONE;
2ff057
2ff057
/** \ingroup rpmgi
2ff057
 */
2ff057
struct rpmgi_s {
2ff057
    rpmts ts;			/*!< Iterator transaction set. */
2ff057
2ff057
    rpmgiFlags flags;		/*!< Iterator control bits. */
2ff057
    int i;			/*!< Element index. */
2ff057
    int errors;
2ff057
2ff057
    ARGV_t argv;
2ff057
    int argc;
2ff057
2ff057
    int curLvl;			/*!< Current recursion level */
2ff057
    int	recLvls[MANIFEST_RECURSIONS]; /*!< Reversed end index for given level */
2ff057
2ff057
};
2ff057
2ff057
/**
2ff057
 * Open a file after macro expanding path.
2ff057
 * @todo There are two error messages printed on header, then manifest failures.
2ff057
 * @param path		file path
2ff057
 * @param fmode		open mode
2ff057
 * @return		file handle
2ff057
 */
2ff057
static FD_t rpmgiOpen(const char * path, const char * fmode)
2ff057
{
2ff057
    char * fn = rpmExpand(path, NULL);
2ff057
    FD_t fd = Fopen(fn, fmode);
2ff057
2ff057
    if (fd == NULL || Ferror(fd)) {
2ff057
	rpmlog(RPMLOG_ERR, _("open of %s failed: %s\n"), fn, Fstrerror(fd));
2ff057
	if (fd != NULL) (void) Fclose(fd);
2ff057
	fd = NULL;
2ff057
    }
2ff057
    free(fn);
2ff057
2ff057
    return fd;
2ff057
}
2ff057
2ff057
/**
2ff057
 * Load manifest into iterator arg list.
2ff057
 * @param gi		generalized iterator
2ff057
 * @param path		file path
2ff057
 * @return		RPMRC_OK on success
2ff057
 */
2ff057
static rpmRC rpmgiLoadManifest(rpmgi gi, const char * path)
2ff057
{
2ff057
    FD_t fd = rpmgiOpen(path, "r.ufdio");
2ff057
    rpmRC rpmrc = RPMRC_FAIL;
2ff057
2ff057
    if (fd != NULL) {
2ff057
	rpmrc = rpmReadPackageManifest(fd, &gi->argc, &gi->argv);
2ff057
	(void) Fclose(fd);
2ff057
    }
2ff057
    return rpmrc;
2ff057
}
2ff057
2ff057
/**
2ff057
 * Return header from package.
2ff057
 * @param gi		generalized iterator
2ff057
 * @param path		file path
2ff057
 * @retval hdrp		header (NULL on failure)
2ff057
 * @return		1 if path could be opened, 0 if not
2ff057
 */
2ff057
static int rpmgiReadHeader(rpmgi gi, const char * path, Header * hdrp)
2ff057
{
2ff057
    FD_t fd = rpmgiOpen(path, "r.ufdio");
2ff057
    Header h = NULL;
2ff057
2ff057
    if (fd != NULL) {
2ff057
	/* XXX what if path needs expansion? */
2ff057
	rpmRC rpmrc = rpmReadPackageFile(gi->ts, fd, path, &h);
2ff057
2ff057
	(void) Fclose(fd);
2ff057
2ff057
	switch (rpmrc) {
2ff057
	case RPMRC_NOTFOUND:
2ff057
	    /* XXX Read a package manifest. Restart ftswalk on success. */
2ff057
	case RPMRC_FAIL:
2ff057
	default:
2ff057
	    h = headerFree(h);
2ff057
	    break;
2ff057
	case RPMRC_NOTTRUSTED:
2ff057
	case RPMRC_NOKEY:
2ff057
	case RPMRC_OK:
2ff057
	    break;
2ff057
	}
2ff057
    }
2ff057
2ff057
    *hdrp = h;
2ff057
    return (fd != NULL);
2ff057
}
2ff057
2ff057
/**
2ff057
 * Read next header from package, lazily expanding manifests as found.
2ff057
 * @todo An empty file read as manifest truncates argv returning RPMRC_NOTFOUND.
2ff057
 * @todo Chained manifests lose an arg someplace.
2ff057
 * @param gi		generalized iterator
2ff057
 * @return		header on success
2ff057
 */
2ff057
static Header rpmgiLoadReadHeader(rpmgi gi)
2ff057
{
2ff057
    Header h = NULL;
2ff057
2ff057
    if (gi->argv != NULL && gi->argv[gi->i] != NULL)
2ff057
    do {
2ff057
	char * fn = gi->argv[gi->i];
2ff057
	int rc;
2ff057
2ff057
	while (gi->recLvls[gi->curLvl] > gi->argc - gi->i)
2ff057
	    gi->curLvl--;
2ff057
2ff057
	rc = rpmgiReadHeader(gi, fn, &h);
2ff057
2ff057
	if (h != NULL || (gi->flags & RPMGI_NOMANIFEST) || rc == 0)
2ff057
	    break;
2ff057
2ff057
	if (gi->curLvl == MANIFEST_RECURSIONS - 1) {
2ff057
	    rpmlog(RPMLOG_ERR,
2ff057
		   _("Max level of manifest recursion exceeded: %s\n"), fn);
2ff057
	    break;
2ff057
	}
2ff057
	gi->curLvl++;
2ff057
	gi->recLvls[gi->curLvl] = gi->argc - gi->i;
2ff057
2ff057
	/* Not a header, so try for a manifest. */
2ff057
	gi->argv[gi->i] = NULL;		/* Mark the insertion point */
2ff057
	if (rpmgiLoadManifest(gi, fn) != RPMRC_OK) {
2ff057
	    gi->argv[gi->i] = fn;	/* Manifest failed, restore fn */
2ff057
	    rpmlog(RPMLOG_ERR, 
2ff057
		   _("%s: not an rpm package (or package manifest)\n"), fn);
2ff057
	    break;
2ff057
	}
2ff057
	fn = _free(fn);
2ff057
    } while (1);
2ff057
2ff057
    return h;
2ff057
}
2ff057
2ff057
2ff057
/**
2ff057
 * Append globbed arg list to iterator.
2ff057
 * @param gi		generalized iterator
2ff057
 * @param argv		arg list to be globbed (or NULL)
2ff057
 */
2ff057
static void rpmgiGlobArgv(rpmgi gi, ARGV_const_t argv)
2ff057
{
2ff057
    if (argv == NULL) return;
2ff057
2ff057
    /* XXX Expand globs only if requested */
2ff057
    if ((gi->flags & RPMGI_NOGLOB)) {
2ff057
	argvAppend(&gi->argv, argv);
2ff057
    } else {
2ff057
	const char * arg;
2ff057
	while ((arg = *argv++) != NULL) {
2ff057
	    char * t = rpmEscapeSpaces(arg);
2ff057
	    char ** av = NULL;
2ff057
2ff057
	    if (rpmGlob(t, NULL, &av) == 0) {
2ff057
		argvAppend(&gi->argv, av);
2ff057
		argvFree(av);
2ff057
	    }
2ff057
	    free(t);
2ff057
	}
2ff057
    }
2ff057
    gi->argc = argvCount(gi->argv);
2ff057
2ff057
    return;
2ff057
}
2ff057
2ff057
rpmgi rpmgiFree(rpmgi gi)
2ff057
{
2ff057
    if (gi != NULL) {
2ff057
	rpmtsFree(gi->ts);
2ff057
	argvFree(gi->argv);
2ff057
2ff057
	memset(gi, 0, sizeof(*gi)); /* XXX trash and burn */
2ff057
	free(gi);
2ff057
    }
2ff057
    return NULL;
2ff057
}
2ff057
2ff057
rpmgi rpmgiNew(rpmts ts, rpmgiFlags flags, ARGV_const_t argv)
2ff057
{
2ff057
    rpmgi gi = xcalloc(1, sizeof(*gi));
2ff057
2ff057
    gi->ts = rpmtsLink(ts);
2ff057
2ff057
    gi->flags = flags;
2ff057
    gi->i = -1;
2ff057
    gi->errors = 0;
2ff057
2ff057
    gi->argv = argvNew();
2ff057
    gi->argc = 0;
2ff057
    rpmgiGlobArgv(gi, argv);
2ff057
2ff057
    gi->curLvl = 0;
2ff057
    gi->recLvls[gi->curLvl] = 1;
2ff057
2ff057
    return gi;
2ff057
}
2ff057
2ff057
Header rpmgiNext(rpmgi gi)
2ff057
{
2ff057
    Header h = NULL;
2ff057
2ff057
    if (gi != NULL && ++gi->i >= 0) {
2ff057
	/* 
2ff057
 	 * Read next header, lazily expanding manifests as found,
2ff057
 	 * count + skip errors.
2ff057
 	 */
2ff057
	while (gi->i < gi->argc) {
2ff057
	    if ((h = rpmgiLoadReadHeader(gi)) != NULL) 
2ff057
		break;
2ff057
	    gi->errors++;
2ff057
	    gi->i++;
2ff057
        }
2ff057
2ff057
	/* Out of things to try, end of iteration */
2ff057
	if (h == NULL)
2ff057
	    gi->i = -1;
2ff057
    }
2ff057
2ff057
    return h;
2ff057
}
2ff057
2ff057
int rpmgiNumErrors(rpmgi gi)
2ff057
{
2ff057
    return (gi != NULL ? gi->errors : -1);
2ff057
}