7 #include <rpmte.h> /* XXX rpmElementType */
9 #define _RPMGI_INTERNAL
10 #define _RPMTS_INTERNAL /* XXX ts->probs et al */
14 #include <rpmmacro.h> /* XXX rpmExpand */
22 rpmgiFlags giFlags = RPMGI_NONE;
24 static int indent = 2;
26 static const char * ftsInfoStrings[] = {
44 static const char * ftsInfoStr(int fts_info)
47 if (!(fts_info >= 1 && fts_info <= 14))
49 return ftsInfoStrings[ fts_info ];
53 * Open a file after macro expanding path.
54 * @todo There are two error messages printed on header, then manifest failures.
55 * @param path file path
56 * @param fmode open mode
59 static FD_t rpmgiOpen(const char * path, const char * fmode)
61 const char * fn = rpmExpand(path, NULL);
62 FD_t fd = Fopen(fn, fmode);
64 if (fd == NULL || Ferror(fd)) {
65 rpmlog(RPMERR_OPEN, _("open of %s failed: %s\n"), fn, Fstrerror(fd));
66 if (fd != NULL) (void) Fclose(fd);
75 * Load manifest into iterator arg list.
76 * @param gi generalized iterator
77 * @param path file path
78 * @return RPMRC_OK on success
80 static rpmRC rpmgiLoadManifest(rpmgi gi, const char * path)
82 FD_t fd = rpmgiOpen(path, "r.ufdio");
83 rpmRC rpmrc = RPMRC_FAIL;
86 rpmrc = rpmReadPackageManifest(fd, &gi->argc, &gi->argv);
93 * Return header from package.
94 * @param gi generalized iterator
95 * @param path file path
96 * @return header (NULL on failure)
98 static Header rpmgiReadHeader(rpmgi gi, const char * path)
100 FD_t fd = rpmgiOpen(path, "r.ufdio");
104 /* XXX what if path needs expansion? */
105 rpmRC rpmrc = rpmReadPackageFile(gi->ts, fd, path, &h);
111 /* XXX Read a package manifest. Restart ftswalk on success. */
116 case RPMRC_NOTTRUSTED:
127 * Read next header from package, lazily expanding manifests as found.
128 * @todo An empty file read as manifest truncates argv returning RPMRC_NOTFOUND.
129 * @todo Errors, e.g. non-existent path in manifest, will terminate iteration.
130 * @todo Chained manifests lose an arg someplace.
131 * @param gi generalized iterator
132 * @return RPMRC_OK on success
134 static rpmRC rpmgiLoadReadHeader(rpmgi gi)
136 rpmRC rpmrc = RPMRC_NOTFOUND;
139 if (gi->argv != NULL && gi->argv[gi->i] != NULL)
141 const char * fn; /* XXX gi->hdrPath? */
143 fn = gi->argv[gi->i];
144 if (!(gi->flags & RPMGI_NOHEADER)) {
145 h = rpmgiReadHeader(gi, fn);
151 if (rpmrc == RPMRC_OK || gi->flags & RPMGI_NOMANIFEST)
153 if (errno == ENOENT) {
157 /* Not a header, so try for a manifest. */
158 gi->argv[gi->i] = NULL; /* Mark the insertion point */
159 rpmrc = rpmgiLoadManifest(gi, fn);
160 if (rpmrc != RPMRC_OK) {
161 gi->argv[gi->i] = fn; /* Manifest failed, restore fn */
165 rpmrc = RPMRC_NOTFOUND;
168 if (rpmrc == RPMRC_OK && h != NULL)
169 gi->h = headerLink(h);
176 * Filter file tree walk path.
177 * @param gi generalized iterator
178 * @return RPMRC_OK on success
180 static rpmRC rpmgiWalkPathFilter(rpmgi gi)
182 FTSENT * fts = gi->fts;
183 rpmRC rpmrc = RPMRC_NOTFOUND;
186 if (_rpmgi_debug < 0)
187 rpmlog(RPMLOG_DEBUG, "FTS_%s\t%*s %s%s\n", ftsInfoStr(fts->fts_info),
188 indent * (fts->fts_level < 0 ? 0 : fts->fts_level), "",
190 ((fts->fts_info == FTS_D || fts->fts_info == FTS_DP) ? "/" : ""));
192 switch (fts->fts_info) {
193 case FTS_D: /* preorder directory */
195 case FTS_DP: /* postorder directory */
197 case FTS_F: /* regular file */
198 /* Ignore all but *.rpm files. */
199 s = fts->fts_name + fts->fts_namelen + 1 - sizeof(".rpm");
200 if (strcmp(s, ".rpm"))
204 case FTS_NS: /* stat(2) failed */
205 case FTS_DNR: /* unreadable directory */
206 case FTS_ERR: /* error; errno is set */
208 case FTS_DC: /* directory that causes cycles */
209 case FTS_DEFAULT: /* none of the above */
210 case FTS_DOT: /* dot or dot-dot */
211 case FTS_INIT: /* initialized only */
212 case FTS_NSOK: /* no stat(2) requested */
213 case FTS_SL: /* symbolic link */
214 case FTS_SLNONE: /* symbolic link without target */
215 case FTS_W: /* whiteout object */
223 * Read header from next package, lazily walking file tree.
224 * @param gi generalized iterator
225 * @return RPMRC_OK on success
227 static rpmRC rpmgiWalkReadHeader(rpmgi gi)
229 rpmRC rpmrc = RPMRC_NOTFOUND;
231 if (gi->ftsp != NULL)
232 while ((gi->fts = Fts_read(gi->ftsp)) != NULL) {
233 rpmrc = rpmgiWalkPathFilter(gi);
234 if (rpmrc == RPMRC_OK)
238 if (rpmrc == RPMRC_OK) {
240 if (!(gi->flags & RPMGI_NOHEADER)) {
241 /* XXX rpmrc = rpmgiLoadReadHeader(gi); */
242 if (gi->fts != NULL) /* XXX can't happen */
243 h = rpmgiReadHeader(gi, gi->fts->fts_path);
246 gi->h = headerLink(h);
254 * Append globbed arg list to iterator.
255 * @param gi generalized iterator
256 * @param argv arg list to be globbed (or NULL)
257 * @returns RPMRC_OK on success
259 static rpmRC rpmgiGlobArgv(rpmgi gi, ARGV_t argv)
262 rpmRC rpmrc = RPMRC_OK;
266 /* XXX Expand globs only if requested or for gi specific tags */
267 if ((gi->flags & RPMGI_NOGLOB)
268 || !(gi->tag == RPMDBI_HDLIST || gi->tag == RPMDBI_ARGLIST || gi->tag == RPMDBI_FTSWALK))
271 while (argv[ac] != NULL)
273 /* XXX argv is not NULL */
274 xx = argvAppend(&gi->argv, argv);
281 while ((arg = *argv++) != NULL) {
284 xx = rpmGlob(arg, &ac, &av);
285 xx = argvAppend(&gi->argv, av);
294 * Return rpmdb match iterator with filters (if any) set.
295 * @param gi generalized iterator
296 * @returns RPMRC_OK on success
298 static rpmRC rpmgiInitFilter(rpmgi gi)
300 rpmRC rpmrc = RPMRC_OK;
304 gi->mi = rpmtsInitIterator(gi->ts, gi->tag, gi->keyp, gi->keylen);
306 if (_rpmgi_debug < 0)
307 fprintf(stderr, "*** gi %p\tmi %p\n", gi, gi->mi);
309 if (gi->argv != NULL)
310 for (av = (const char **) gi->argv; *av != NULL; av++) {
311 int tag = RPMTAG_NAME;
315 pat = a = xstrdup(*av);
318 /* Parse for "tag=pattern" args. */
319 if ((ae = strchr(a, '=')) != NULL) {
321 tag = rpmTagGetValue(a);
323 rpmlog(RPMERR_QUERYINFO, _("unknown tag: \"%s\"\n"), a);
330 if (_rpmgi_debug < 0)
331 fprintf(stderr, "\tav %p[%ld]: \"%s\" -> %s ~= \"%s\"\n", gi->argv, (av - gi->argv), *av, rpmTagGetName(tag), pat);
332 res = rpmdbSetIteratorRE(gi->mi, tag, RPMMIRE_DEFAULT, pat);
339 gi->mi = rpmdbFreeIterator(gi->mi); /* XXX odd side effect? */
347 rpmgi XrpmgiUnlink(rpmgi gi, const char * msg, const char * fn, unsigned ln)
349 if (gi == NULL) return NULL;
351 if (_rpmgi_debug && msg != NULL)
352 fprintf(stderr, "--> gi %p -- %d %s at %s:%u\n", gi, gi->nrefs, msg, fn, ln);
358 rpmgi XrpmgiLink(rpmgi gi, const char * msg, const char * fn, unsigned ln)
360 if (gi == NULL) return NULL;
363 if (_rpmgi_debug && msg != NULL)
364 fprintf(stderr, "--> gi %p ++ %d %s at %s:%u\n", gi, gi->nrefs, msg, fn, ln);
369 rpmgi rpmgiFree(rpmgi gi)
375 return rpmgiUnlink(gi, __FUNCTION__);
377 (void) rpmgiUnlink(gi, __FUNCTION__);
380 gi->hdrPath = _free(gi->hdrPath);
381 gi->h = headerFree(gi->h);
383 gi->argv = argvFree(gi->argv);
385 if (gi->ftsp != NULL) {
387 xx = Fts_close(gi->ftsp);
391 if (gi->fd != NULL) {
392 (void) Fclose(gi->fd);
395 gi->tsi = rpmtsiFree(gi->tsi);
396 gi->mi = rpmdbFreeIterator(gi->mi);
397 gi->ts = rpmtsFree(gi->ts);
399 memset(gi, 0, sizeof(*gi)); /* XXX trash and burn */
404 rpmgi rpmgiNew(rpmts ts, int tag, const void * keyp, size_t keylen)
406 rpmgi gi = xcalloc(1, sizeof(*gi));
411 gi->ts = rpmtsLink(ts, __FUNCTION__);
425 gi->argv = xcalloc(1, sizeof(*gi->argv));
431 gi = rpmgiLink(gi, __FUNCTION__);
436 rpmRC rpmgiNext(rpmgi gi)
439 rpmRC rpmrc = RPMRC_NOTFOUND;
445 /* Free header from previous iteration. */
446 gi->h = headerFree(gi->h);
447 gi->hdrPath = _free(gi->hdrPath);
453 case RPMDBI_PACKAGES:
455 rpmrc = rpmgiInitFilter(gi);
456 if (rpmrc != RPMRC_OK) {
457 gi->mi = rpmdbFreeIterator(gi->mi); /* XXX unnecessary */
460 rpmrc = RPMRC_NOTFOUND; /* XXX hack */
463 if (gi->mi != NULL) { /* XXX unnecessary */
464 Header h = rpmdbNextIterator(gi->mi);
466 if (!(gi->flags & RPMGI_NOHEADER))
467 gi->h = headerLink(h);
468 sprintf(hnum, "%u", rpmdbGetIteratorOffset(gi->mi));
469 gi->hdrPath = rpmExpand("rpmdb h# ", hnum, NULL);
471 /* XXX header reference held by iterator, so no headerFree */
474 if (rpmrc != RPMRC_OK) {
475 gi->mi = rpmdbFreeIterator(gi->mi);
483 gi->tsi = rpmtsiInit(gi->ts);
486 if ((p = rpmtsiNext(gi->tsi, TR_ADDED)) != NULL) {
487 Header h = rpmteHeader(p);
489 if (!(gi->flags & RPMGI_NOHEADER)) {
490 gi->h = headerLink(h);
491 sprintf(hnum, "%u", (unsigned)gi->i);
492 gi->hdrPath = rpmExpand("added h# ", hnum, NULL);
497 if (rpmrc != RPMRC_OK) {
498 gi->tsi = rpmtsiFree(gi->tsi);
504 const char * path = "/usr/share/comps/%{_arch}/hdlist";
505 gi->fd = rpmgiOpen(path, "r.ufdio");
508 if (gi->fd != NULL) {
509 Header h = headerRead(gi->fd, HEADER_MAGIC_YES);
511 if (!(gi->flags & RPMGI_NOHEADER))
512 gi->h = headerLink(h);
513 sprintf(hnum, "%u", (unsigned)gi->i);
514 gi->hdrPath = rpmExpand("hdlist h# ", hnum, NULL);
519 if (rpmrc != RPMRC_OK) {
520 if (gi->fd != NULL) (void) Fclose(gi->fd);
526 /* XXX gi->active initialize? */
527 if (_rpmgi_debug < 0)
528 fprintf(stderr, "*** gi %p\t%p[%d]: %s\n", gi, gi->argv, gi->i, gi->argv[gi->i]);
529 /* Read next header, lazily expanding manifests as found. */
530 rpmrc = rpmgiLoadReadHeader(gi);
532 if (rpmrc != RPMRC_OK) /* XXX check this */
535 gi->hdrPath = xstrdup(gi->argv[gi->i]);
538 if (gi->argv == NULL) /* HACK */
542 gi->ftsp = Fts_open((char *const *)gi->argv, gi->ftsOpts, NULL);
543 /* XXX NULL with open(2)/malloc(3) errno set */
547 /* Read next header, lazily walking file tree. */
548 rpmrc = rpmgiWalkReadHeader(gi);
550 if (rpmrc != RPMRC_OK) {
551 xx = Fts_close(gi->ftsp);
557 gi->hdrPath = xstrdup(gi->fts->fts_path);
561 if ((gi->flags & RPMGI_TSADD) && gi->h != NULL) {
562 /* XXX rpmgi hack: Save header in transaction element. */
563 xx = rpmtsAddInstallElement(gi->ts, gi->h, (fnpyKey)gi->hdrPath, 2, NULL);
569 if (gi->flags & RPMGI_TSORDER) {
574 /* XXX installed database needs close here. */
575 xx = rpmtsCloseDB(ts);
576 ts->dbmode = -1; /* XXX disable lazy opens */
580 /* XXX query/verify will need the glop added to a buffer instead. */
581 ps = rpmtsProblems(ts);
582 if (rpmpsNumProblems(ps) > 0) {
583 /* XXX rpminstall will need RPMLOG_ERR */
584 rpmlog(RPMLOG_INFO, _("Failed dependencies:\n"));
586 rpmpsPrint(NULL, ps);
588 if (ts->suggests != NULL && ts->nsuggests > 0) {
589 rpmlog(RPMLOG_INFO, _(" Suggested resolutions:\n"));
590 for (i = 0; i < ts->nsuggests; i++) {
591 const char * str = ts->suggests[i];
596 rpmlog(RPMLOG_INFO, "\t%s\n", str);
598 ts->suggests[i] = NULL;
601 ts->suggests = _free(ts->suggests);
606 ts->probs = rpmpsFree(ts->probs); /* XXX hackery */
610 gi->tag = RPMDBI_ADDED; /* XXX hackery */
611 gi->flags &= ~(RPMGI_TSADD|RPMGI_TSORDER);
615 gi->h = headerFree(gi->h);
616 gi->hdrPath = _free(gi->hdrPath);
622 const char * rpmgiHdrPath(rpmgi gi)
624 return (gi != NULL ? gi->hdrPath : NULL);
627 Header rpmgiHeader(rpmgi gi)
629 return (gi != NULL ? gi->h : NULL);
632 rpmts rpmgiTs(rpmgi gi)
634 return (gi != NULL ? gi->ts : NULL);
637 rpmRC rpmgiSetArgs(rpmgi gi, ARGV_t argv, int ftsOpts, rpmgiFlags flags)
639 gi->ftsOpts = ftsOpts;
641 return rpmgiGlobArgv(gi, argv);