Add rpmtsCheck() and rpmtsOrder() to iterator.
[platform/upstream/rpm.git] / lib / rpmgi.c
1 /*@-modfilesys@*/
2 /** \ingroup rpmio
3  * \file rpmio/rpmio.c
4  */
5 #include "system.h"
6
7 #define _RPMGI_INTERNAL
8 #include <rpmgi.h>
9
10 #include <rpmdb.h>
11 #include <rpmmacro.h>           /* XXX rpmExpand */
12 #include "manifest.h"
13
14 #include "debug.h"
15
16 /*@access rpmdbMatchIterator @*/
17
18 /*@unchecked@*/
19 int _rpmgi_debug = 0;
20
21 /*@unchecked@*/
22 static int indent = 2;
23
24 /*@unchecked@*/ /*@observer@*/
25 static const char * ftsInfoStrings[] = {
26     "UNKNOWN",
27     "D",
28     "DC",
29     "DEFAULT",
30     "DNR",
31     "DOT",
32     "DP",
33     "ERR",
34     "F",
35     "INIT",
36     "NS",
37     "NSOK",
38     "SL",
39     "SLNONE",
40     "W",
41 };
42
43 /*@observer@*/
44 static const char * ftsInfoStr(int fts_info)
45         /*@*/
46 {
47
48     if (!(fts_info >= 1 && fts_info <= 14))
49         fts_info = 0;
50 /*@-compmempass@*/
51     return ftsInfoStrings[ fts_info ];
52 /*@=compmempass@*/
53 }
54
55 /**
56  * Open a file after macro expanding path.
57  * @param path          file path
58  * @param fmode         open mode
59  * @return              file handle
60  */
61 /*@null@*/
62 static FD_t rpmgiOpen(const char * path, const char * fmode)
63         /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
64         /*@modifies rpmGlobalMacroContext, h_errno, internalState @*/
65 {
66     const char * fn = rpmExpand(path, NULL);
67     FD_t fd = Fopen(fn, fmode);
68
69     if (fd == NULL || Ferror(fd)) {
70         rpmError(RPMERR_OPEN, _("open of %s failed: %s\n"), fn, Fstrerror(fd));
71         if (fd != NULL) (void) Fclose(fd);
72         fd = NULL;
73     }
74     fn = _free(fn);
75
76     return fd;
77 }
78
79 /**
80  * Load manifest into iterator arg list.
81  * @param gi            generalized iterator
82  * @param path          file path
83  * @return              RPMRC_OK on success
84  */
85 static rpmRC rpmgiLoadManifest(rpmgi gi, const char * path)
86         /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
87         /*@modifies gi, rpmGlobalMacroContext, h_errno, internalState @*/
88 {
89     FD_t fd = rpmgiOpen(path, "r.ufdio");
90     rpmRC rpmrc = RPMRC_FAIL;
91
92     if (fd != NULL) {
93         rpmrc = rpmReadPackageManifest(fd, &gi->argc, &gi->argv);
94         (void) Fclose(fd);
95     }
96     return rpmrc;
97 }
98
99 /**
100  * Return header from package.
101  * @param gi            generalized iterator
102  * @param path          file path
103  * @return              header (NULL on failure)
104  */
105 /*@null@*/
106 static Header rpmgiReadHeader(rpmgi gi, const char * path)
107         /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
108         /*@modifies gi, rpmGlobalMacroContext, h_errno, internalState @*/
109 {
110     FD_t fd = rpmgiOpen(path, "r.ufdio");
111     Header h = NULL;
112
113     if (fd != NULL) {
114         /* XXX what if path needs expansion? */
115         rpmRC rpmrc = rpmReadPackageFile(gi->ts, fd, path, &h);
116
117         (void) Fclose(fd);
118
119         switch (rpmrc) {
120         case RPMRC_NOTFOUND:
121             /* XXX Read a package manifest. Restart ftswalk on success. */
122         case RPMRC_FAIL:
123         default:
124             h = headerFree(h);
125             break;
126         case RPMRC_NOTTRUSTED:
127         case RPMRC_NOKEY:
128         case RPMRC_OK:
129             break;
130         }
131     }
132
133     return h;
134 }
135
136 /**
137  * Read next header from package, lazily expanding manifests as found.
138  * @param gi            generalized iterator
139  * @return              RPMRC_OK on success
140  */
141 /*@null@*/
142 static rpmRC rpmgiLoadReadHeader(rpmgi gi)
143         /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
144         /*@modifies gi, rpmGlobalMacroContext, h_errno, internalState @*/
145 {
146     rpmRC rpmrc = RPMRC_NOTFOUND;
147     Header h = NULL;
148
149     do {
150         const char * fn;        /* XXX gi->hdrPath? */
151
152         fn = gi->argv[gi->i];
153         h = rpmgiReadHeader(gi, fn);
154         if (h != NULL) {
155             rpmrc = RPMRC_OK;
156             break;
157         }
158
159         /* Not a header, so try for a manifest. */
160         gi->argv[gi->i] = NULL;         /* HACK */
161         rpmrc = rpmgiLoadManifest(gi, fn);
162         if (rpmrc != RPMRC_OK) {
163             gi->argv[gi->i] = fn;       /* HACK */
164             break;
165         }
166         fn = _free(fn);
167     } while (1);
168
169     if (rpmrc == RPMRC_OK && h != NULL)
170         gi->h = headerLink(h);
171     h = headerFree(h);
172
173     return rpmrc;
174 }
175
176 /**
177  * Read next header from package, lazily walking file tree.
178  * @param gi            generalized iterator
179  * @return              RPMRC_OK on success
180  */
181 /*@null@*/
182 static rpmRC rpmgiWalkReadHeader(rpmgi gi)
183         /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
184         /*@modifies gi, rpmGlobalMacroContext, h_errno, internalState @*/
185 {
186     rpmRC rpmrc = RPMRC_NOTFOUND;
187     Header h = NULL;
188
189     if (gi->ftsp != NULL)
190     while ((gi->fts = Fts_read(gi->ftsp)) != NULL) {
191         FTSENT * fts = gi->fts;
192
193 if (_rpmgi_debug < 0)
194 fprintf(stderr, "FTS_%s\t%*s %s\n", ftsInfoStr(fts->fts_info),
195                 indent * (fts->fts_level < 0 ? 0 : fts->fts_level), "",
196                 fts->fts_name);
197
198 /*@=branchstate@*/
199         switch (fts->fts_info) {
200         case FTS_F:
201         case FTS_SL:
202 if (_rpmgi_debug  < 0)
203 fprintf(stderr, "*** gi %p\t%p[%d]: %s\n", gi, gi->ftsp, gi->i, fts->fts_path);
204             h = rpmgiReadHeader(gi, fts->fts_path);
205             /*@switchbreak@*/ break;
206         default:
207             /*@switchbreak@*/ break;
208         }
209 /*@=branchstate@*/
210         if (h != NULL) {
211             rpmrc = RPMRC_OK;
212             break;
213         }
214     }
215
216     if (rpmrc == RPMRC_OK && h != NULL)
217         gi->h = headerLink(h);
218     h = headerFree(h);
219
220     return rpmrc;
221 }
222
223 /**
224  * Append globbed arg list to iterator.
225  * @param gi            generalized iterator
226  * @param argv          arg list to be globbed
227  * @returns             RPMRC_OK on success
228  */
229 static rpmRC rpmgiGlobArgv(rpmgi gi, ARGV_t argv)
230         /*@globals internalState @*/
231         /*@modifies gi, internalState @*/
232 {
233     const char * arg;
234     rpmRC rpmrc = RPMRC_OK;
235
236     if (argv != NULL)
237     while ((arg = *argv++) != NULL) {
238         ARGV_t av = NULL;
239         int ac = 0;
240         int xx;
241
242         xx = rpmGlob(arg, &ac, &av);
243         xx = argvAppend(&gi->argv, av);
244         gi->argc += ac;
245         av = argvFree(av);
246         ac = 0;
247     }
248     return rpmrc;
249 }
250
251 rpmgi XrpmgiUnlink(rpmgi gi, const char * msg, const char * fn, unsigned ln)
252 {
253     if (gi == NULL) return NULL;
254
255 if (_rpmgi_debug && msg != NULL)
256 fprintf(stderr, "--> gi %p -- %d %s at %s:%u\n", gi, gi->nrefs, msg, fn, ln);
257
258     gi->nrefs--;
259     return NULL;
260 }
261
262 rpmgi XrpmgiLink(rpmgi gi, const char * msg, const char * fn, unsigned ln)
263 {
264     if (gi == NULL) return NULL;
265     gi->nrefs++;
266
267 if (_rpmgi_debug && msg != NULL)
268 fprintf(stderr, "--> gi %p ++ %d %s at %s:%u\n", gi, gi->nrefs, msg, fn, ln);
269
270     /*@-refcounttrans@*/ return gi; /*@=refcounttrans@*/
271 }
272
273 rpmgi rpmgiFree(rpmgi gi)
274 {
275     if (gi == NULL)
276         return NULL;
277
278     if (gi->nrefs > 1)
279         return rpmgiUnlink(gi, NULL);
280
281 if (_rpmgi_debug < 0)
282 fprintf(stderr, "*** gi %p\t%p[%d]\n", gi, gi->argv, gi->argc);
283
284     (void) rpmgiUnlink(gi, NULL);
285
286 /*@-usereleased@*/
287
288     gi->hdrPath = _free(gi->hdrPath);
289     gi->h = headerFree(gi->h);
290
291     gi->argv = argvFree(gi->argv);
292
293     if (gi->ftsp != NULL) {
294         int xx;
295         xx = Fts_close(gi->ftsp);
296         gi->ftsp = NULL;
297         gi->fts = NULL;
298     }
299     if (gi->fd != NULL) {
300         (void) Fclose(gi->fd);
301         gi->fd = NULL;
302     }
303     gi->mi = rpmdbFreeIterator(gi->mi);
304     gi->ts = rpmtsFree(gi->ts);
305
306     memset(gi, 0, sizeof(*gi));         /* XXX trash and burn */
307 /*@-refcounttrans@*/
308     gi = _free(gi);
309 /*@=refcounttrans@*/
310 /*@=usereleased@*/
311     return NULL;
312 }
313
314 rpmgi rpmgiNew(rpmts ts, int tag, const void * keyp, size_t keylen)
315 {
316     rpmgi gi = xcalloc(1, sizeof(*gi));
317
318     if (gi == NULL)
319         return NULL;
320
321     gi->ts = rpmtsLink(ts, NULL);
322     gi->tag = tag;
323     gi->keyp = keyp;
324     gi->keylen = keylen;
325
326     gi->flags = 0;
327     gi->active = 0;
328     gi->i = -1;
329     gi->hdrPath = NULL;
330     gi->h = NULL;
331
332     gi->mi = NULL;
333     gi->fd = NULL;
334     gi->argv = xcalloc(1, sizeof(*gi->argv));
335     gi->argc = 0;
336     gi->ftsOpts = 0;
337     gi->ftsp = NULL;
338     gi->fts = NULL;
339
340     gi = rpmgiLink(gi, NULL);
341
342     return gi;
343 }
344
345 rpmRC rpmgiNext(/*@null@*/ rpmgi gi)
346 {
347     char hnum[32];
348     rpmRC rpmrc = RPMRC_NOTFOUND;
349     int xx;
350
351     if (gi == NULL)
352         return rpmrc;
353
354     /* Free header from previous iteration. */
355     gi->h = headerFree(gi->h);
356     gi->hdrPath = _free(gi->hdrPath);
357     hnum[0] = '\0';
358
359 /*@-branchstate@*/
360     if (++gi->i >= 0)
361     switch (gi->tag) {
362     default:
363     case RPMDBI_PACKAGES:
364         if (!gi->active) {
365             gi->mi = rpmtsInitIterator(gi->ts, gi->tag, gi->keyp, gi->keylen);
366 if (_rpmgi_debug < 0)
367 fprintf(stderr, "*** gi %p\t%p\n", gi, gi->mi);
368             gi->active = 1;
369         }
370         if (gi->mi != NULL) {   /* XXX unnecessary */
371             Header h = rpmdbNextIterator(gi->mi);
372             if (h != NULL) {
373                 gi->h = headerLink(h);
374                 sprintf(hnum, "%u", rpmdbGetIteratorOffset(gi->mi));
375             }
376         }
377         if (gi->h == NULL) {
378             gi->mi = rpmdbFreeIterator(gi->mi);
379             goto enditer;
380         }
381         gi->hdrPath = rpmExpand("rpmdb h# ", hnum, NULL);
382         break;
383     case RPMDBI_HDLIST:
384         if (!gi->active) {
385             const char * path = "/usr/share/comps/%{_arch}/hdlist";
386             gi->fd = rpmgiOpen(path, "r.ufdio");
387             gi->active = 1;
388         }
389         if (gi->fd != NULL) {
390             gi->h = headerRead(gi->fd, HEADER_MAGIC_YES);
391             sprintf(hnum, "%u", (unsigned)gi->i);
392         }
393
394         if (gi->h == NULL) {
395             if (gi->fd != NULL) (void) Fclose(gi->fd);
396             gi->fd = NULL;
397             goto enditer;
398         }
399         gi->hdrPath = rpmExpand("hdlist h# ", hnum, NULL);
400         break;
401     case RPMDBI_ARGLIST:
402         if (gi->argv == NULL || gi->argv[gi->i] == NULL)
403             goto enditer;
404
405 if (_rpmgi_debug  < 0)
406 fprintf(stderr, "*** gi %p\t%p[%d]: %s\n", gi, gi->argv, gi->i, gi->argv[gi->i]);
407         /* Read next header, lazily expanding manifests as found. */
408         rpmrc = rpmgiLoadReadHeader(gi);
409
410         if (rpmrc != RPMRC_OK)  /* XXX check this */
411             goto enditer;
412
413         gi->hdrPath = xstrdup(gi->argv[gi->i]);
414         break;
415     case RPMDBI_FTSWALK:
416         if (gi->argv == NULL)           /* HACK */
417             goto enditer;
418
419         if (!gi->active) {
420             gi->ftsp = Fts_open((char *const *)gi->argv, gi->ftsOpts, NULL);
421             /* XXX NULL with open(2)/malloc(3) errno set */
422             gi->active = 1;
423         }
424
425         /* Read next header, lazily walking file tree. */
426         rpmrc = rpmgiWalkReadHeader(gi);
427
428         if (gi->h == NULL || rpmrc != RPMRC_OK) {
429             xx = Fts_close(gi->ftsp);
430             gi->ftsp = NULL;
431             goto enditer;
432         }
433
434         if (gi->fts != NULL)
435             gi->hdrPath = xstrdup(gi->fts->fts_path);
436         break;
437     }
438 /*@=branchstate@*/
439
440     if (gi->flags & 0x1) {
441         xx = rpmtsAddInstallElement(gi->ts, gi->h, (fnpyKey)gi->hdrPath, 0, NULL);
442         /* TODO save header and path in rpmte. */
443     }
444
445     return RPMRC_OK;
446
447 enditer:
448     if (gi->flags & 0x2) {
449         xx = rpmtsCheck(gi->ts);
450         xx = rpmtsOrder(gi->ts);
451     }
452     gi->h = headerFree(gi->h);
453     gi->hdrPath = _free(gi->hdrPath);
454     gi->i = -1;
455     gi->active = 0;
456     return rpmrc;
457 }
458
459 const char * rpmgiHdrPath(rpmgi gi)
460 {
461     return (gi != NULL ? gi->hdrPath : NULL);
462 }
463
464 Header rpmgiHeader(rpmgi gi)
465 {
466 /*@-compdef -refcounttrans -retexpose -usereleased@*/
467     return (gi != NULL ? gi->h : NULL);
468 /*@=compdef =refcounttrans =retexpose =usereleased@*/
469 }
470
471 rpmts rpmgiTs(rpmgi gi)
472 {
473 /*@-compdef -refcounttrans -retexpose -usereleased@*/
474     return (gi != NULL ? gi->ts : NULL);
475 /*@=compdef =refcounttrans =retexpose =usereleased@*/
476 }
477
478 rpmRC rpmgiSetArgs(rpmgi gi, ARGV_t argv, int ftsOpts, int flags)
479 {
480     rpmRC rpmrc = rpmgiGlobArgv(gi, argv);
481     gi->ftsOpts = ftsOpts;
482     gi->flags = flags;
483     return rpmrc;
484 }
485
486 /*@=modfilesys@*/