Imported Upstream version 4.14.1
[platform/upstream/rpm.git] / lib / rpmgi.c
1 /** \ingroup rpmio
2  * \file lib/rpmgi.c
3  */
4 #include "system.h"
5
6 #include <errno.h>
7
8 #include <rpm/rpmtypes.h>
9 #include <rpm/rpmlib.h>         /* rpmReadPackageFile */
10 #include <rpm/rpmts.h>
11 #include <rpm/rpmmacro.h>               /* XXX rpmExpand */
12 #include <rpm/rpmfileutil.h>
13 #include <rpm/rpmlog.h>
14
15 #include "lib/rpmgi.h"
16 #include "lib/manifest.h"
17
18 #include "debug.h"
19
20 #define MANIFEST_RECURSIONS 1000 /* Max. number of allowed manifest recursions */
21
22 RPM_GNUC_INTERNAL
23 rpmgiFlags giFlags = RPMGI_NONE;
24
25 /** \ingroup rpmgi
26  */
27 struct rpmgi_s {
28     rpmts ts;                   /*!< Iterator transaction set. */
29
30     rpmgiFlags flags;           /*!< Iterator control bits. */
31     int i;                      /*!< Element index. */
32     int errors;
33
34     ARGV_t argv;
35     int argc;
36
37     int curLvl;                 /*!< Current recursion level */
38     int recLvls[MANIFEST_RECURSIONS]; /*!< Reversed end index for given level */
39
40 };
41
42 /**
43  * Open a file after macro expanding path.
44  * @todo There are two error messages printed on header, then manifest failures.
45  * @param path          file path
46  * @param fmode         open mode
47  * @return              file handle
48  */
49 static FD_t rpmgiOpen(const char * path, const char * fmode)
50 {
51     char * fn = rpmExpand(path, NULL);
52     FD_t fd = Fopen(fn, fmode);
53
54     if (fd == NULL || Ferror(fd)) {
55         rpmlog(RPMLOG_ERR, _("open of %s failed: %s\n"), fn, Fstrerror(fd));
56         if (fd != NULL) (void) Fclose(fd);
57         fd = NULL;
58     }
59     free(fn);
60
61     return fd;
62 }
63
64 /**
65  * Load manifest into iterator arg list.
66  * @param gi            generalized iterator
67  * @param path          file path
68  * @return              RPMRC_OK on success
69  */
70 static rpmRC rpmgiLoadManifest(rpmgi gi, const char * path)
71 {
72     FD_t fd = rpmgiOpen(path, "r.ufdio");
73     rpmRC rpmrc = RPMRC_FAIL;
74
75     if (fd != NULL) {
76         rpmrc = rpmReadPackageManifest(fd, &gi->argc, &gi->argv);
77         (void) Fclose(fd);
78     }
79     return rpmrc;
80 }
81
82 /**
83  * Return header from package.
84  * @param gi            generalized iterator
85  * @param path          file path
86  * @retval hdrp         header (NULL on failure)
87  * @return              1 if path could be opened, 0 if not
88  */
89 static int rpmgiReadHeader(rpmgi gi, const char * path, Header * hdrp)
90 {
91     FD_t fd = rpmgiOpen(path, "r.ufdio");
92     Header h = NULL;
93
94     if (fd != NULL) {
95         /* XXX what if path needs expansion? */
96         rpmRC rpmrc = rpmReadPackageFile(gi->ts, fd, path, &h);
97
98         (void) Fclose(fd);
99
100         switch (rpmrc) {
101         case RPMRC_NOTFOUND:
102             /* XXX Read a package manifest. Restart ftswalk on success. */
103         case RPMRC_FAIL:
104         default:
105             h = headerFree(h);
106             break;
107         case RPMRC_NOTTRUSTED:
108         case RPMRC_NOKEY:
109         case RPMRC_OK:
110             break;
111         }
112     }
113
114     *hdrp = h;
115     return (fd != NULL);
116 }
117
118 /**
119  * Read next header from package, lazily expanding manifests as found.
120  * @todo An empty file read as manifest truncates argv returning RPMRC_NOTFOUND.
121  * @todo Chained manifests lose an arg someplace.
122  * @param gi            generalized iterator
123  * @return              header on success
124  */
125 static Header rpmgiLoadReadHeader(rpmgi gi)
126 {
127     Header h = NULL;
128
129     if (gi->argv != NULL && gi->argv[gi->i] != NULL)
130     do {
131         char * fn = gi->argv[gi->i];
132         int rc;
133
134         while (gi->recLvls[gi->curLvl] > gi->argc - gi->i)
135             gi->curLvl--;
136
137         rc = rpmgiReadHeader(gi, fn, &h);
138
139         if (h != NULL || (gi->flags & RPMGI_NOMANIFEST) || rc == 0)
140             break;
141
142         if (gi->curLvl == MANIFEST_RECURSIONS - 1) {
143             rpmlog(RPMLOG_ERR,
144                    _("Max level of manifest recursion exceeded: %s\n"), fn);
145             break;
146         }
147         gi->curLvl++;
148         gi->recLvls[gi->curLvl] = gi->argc - gi->i;
149
150         /* Not a header, so try for a manifest. */
151         gi->argv[gi->i] = NULL;         /* Mark the insertion point */
152         if (rpmgiLoadManifest(gi, fn) != RPMRC_OK) {
153             gi->argv[gi->i] = fn;       /* Manifest failed, restore fn */
154             rpmlog(RPMLOG_ERR, 
155                    _("%s: not an rpm package (or package manifest)\n"), fn);
156             break;
157         }
158         fn = _free(fn);
159     } while (1);
160
161     return h;
162 }
163
164
165 /**
166  * Append globbed arg list to iterator.
167  * @param gi            generalized iterator
168  * @param argv          arg list to be globbed (or NULL)
169  */
170 static void rpmgiGlobArgv(rpmgi gi, ARGV_const_t argv)
171 {
172     if (argv == NULL) return;
173
174     /* XXX Expand globs only if requested */
175     if ((gi->flags & RPMGI_NOGLOB)) {
176         argvAppend(&gi->argv, argv);
177     } else {
178         const char * arg;
179         while ((arg = *argv++) != NULL) {
180             char * t = rpmEscapeSpaces(arg);
181             char ** av = NULL;
182
183             if (rpmGlob(t, NULL, &av) == 0) {
184                 argvAppend(&gi->argv, av);
185                 argvFree(av);
186             }
187             free(t);
188         }
189     }
190     gi->argc = argvCount(gi->argv);
191
192     return;
193 }
194
195 rpmgi rpmgiFree(rpmgi gi)
196 {
197     if (gi != NULL) {
198         rpmtsFree(gi->ts);
199         argvFree(gi->argv);
200
201         memset(gi, 0, sizeof(*gi)); /* XXX trash and burn */
202         free(gi);
203     }
204     return NULL;
205 }
206
207 rpmgi rpmgiNew(rpmts ts, rpmgiFlags flags, ARGV_const_t argv)
208 {
209     rpmgi gi = xcalloc(1, sizeof(*gi));
210
211     gi->ts = rpmtsLink(ts);
212
213     gi->flags = flags;
214     gi->i = -1;
215     gi->errors = 0;
216
217     gi->argv = argvNew();
218     gi->argc = 0;
219     rpmgiGlobArgv(gi, argv);
220
221     gi->curLvl = 0;
222     gi->recLvls[gi->curLvl] = 1;
223
224     return gi;
225 }
226
227 Header rpmgiNext(rpmgi gi)
228 {
229     Header h = NULL;
230
231     if (gi != NULL && ++gi->i >= 0) {
232         /* 
233          * Read next header, lazily expanding manifests as found,
234          * count + skip errors.
235          */
236         while (gi->i < gi->argc) {
237             if ((h = rpmgiLoadReadHeader(gi)) != NULL) 
238                 break;
239             gi->errors++;
240             gi->i++;
241         }
242
243         /* Out of things to try, end of iteration */
244         if (h == NULL)
245             gi->i = -1;
246     }
247
248     return h;
249 }
250
251 int rpmgiNumErrors(rpmgi gi)
252 {
253     return (gi != NULL ? gi->errors : -1);
254 }