Add ENABLE_COMPLEX_DEPS flag
[platform/upstream/libsolv.git] / ext / repo_comps.c
1 /*
2  * repo_comps.c
3  *
4  * Parses RedHat comps format
5  *
6  * Copyright (c) 2012, Novell Inc.
7  *
8  * This program is licensed under the BSD license, read LICENSE.BSD
9  * for further information
10  */
11
12 #include <sys/types.h>
13 #include <sys/stat.h>
14 #include <unistd.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <assert.h>
19
20 #include "pool.h"
21 #include "repo.h"
22 #include "util.h"
23 #include "solv_xmlparser.h"
24 #define DISABLE_SPLIT
25 #include "tools_util.h"
26 #include "repo_comps.h"
27
28 /*
29  * TODO:
30  *
31  * what's the difference between group/category?
32  *
33  * maybe handle REL_COND in solver recommends handling?
34  */
35
36 enum state {
37   STATE_START,
38   STATE_COMPS,
39   STATE_GROUP,
40   STATE_ID,
41   STATE_NAME,
42   STATE_DESCRIPTION,
43   STATE_DISPLAY_ORDER,
44   STATE_DEFAULT,
45   STATE_LANGONLY,
46   STATE_LANG_ONLY,
47   STATE_USERVISIBLE,
48   STATE_PACKAGELIST,
49   STATE_PACKAGEREQ,
50   STATE_CATEGORY,
51   STATE_CID,
52   STATE_CNAME,
53   STATE_CDESCRIPTION,
54   STATE_CDISPLAY_ORDER,
55   STATE_GROUPLIST,
56   STATE_GROUPID,
57   STATE_ENVIRONMENT,
58   STATE_OPTIONLIST,
59   NUMSTATES
60 };
61
62 static struct solv_xmlparser_element stateswitches[] = {
63   { STATE_START,       "comps",         STATE_COMPS,         0 },
64   { STATE_COMPS,       "group",         STATE_GROUP,         0 },
65   { STATE_COMPS,       "category",      STATE_CATEGORY,      0 },
66   { STATE_COMPS,       "environment",   STATE_ENVIRONMENT,   0 },
67   { STATE_GROUP,       "id",            STATE_ID,            1 },
68   { STATE_GROUP,       "name",          STATE_NAME,          1 },
69   { STATE_GROUP,       "description",   STATE_DESCRIPTION,   1 },
70   { STATE_GROUP,       "uservisible",   STATE_USERVISIBLE,   1 },
71   { STATE_GROUP,       "display_order", STATE_DISPLAY_ORDER, 1 },
72   { STATE_GROUP,       "default",       STATE_DEFAULT,       1 },
73   { STATE_GROUP,       "langonly",      STATE_LANGONLY,      1 },
74   { STATE_GROUP,       "lang_only",     STATE_LANG_ONLY,     1 },
75   { STATE_GROUP,       "packagelist",   STATE_PACKAGELIST,   0 },
76   { STATE_PACKAGELIST, "packagereq",    STATE_PACKAGEREQ,    1 },
77   { STATE_CATEGORY,    "id",            STATE_ID,            1 },
78   { STATE_CATEGORY,    "name",          STATE_NAME,          1 },
79   { STATE_CATEGORY,    "description",   STATE_DESCRIPTION,   1 },
80   { STATE_CATEGORY ,   "grouplist",     STATE_GROUPLIST,     0 },
81   { STATE_CATEGORY ,   "display_order", STATE_DISPLAY_ORDER, 1 },
82   { STATE_GROUPLIST,   "groupid",       STATE_GROUPID,       1 },
83   { STATE_ENVIRONMENT, "id",            STATE_ID,            1 },
84   { STATE_ENVIRONMENT, "name",          STATE_NAME,          1 },
85   { STATE_ENVIRONMENT, "description",   STATE_DESCRIPTION,   1 },
86   { STATE_ENVIRONMENT, "grouplist",     STATE_GROUPLIST,     0 },
87   { STATE_ENVIRONMENT, "optionlist",    STATE_OPTIONLIST,    0 },
88   { STATE_ENVIRONMENT, "display_order", STATE_DISPLAY_ORDER, 1 },
89   { STATE_OPTIONLIST,  "groupid",       STATE_GROUPID,       1 },
90   { NUMSTATES }
91 };
92
93 struct parsedata {
94   Pool *pool;
95   Repo *repo;
96   Repodata *data;
97   const char *filename;
98   const char *basename;
99
100   struct solv_xmlparser xmlp;
101   struct joindata jd;
102
103   const char *tmplang;
104   Id reqtype;
105   Id condreq;
106
107   Solvable *solvable;
108   const char *kind;
109   int isdefault;
110   int isvisible;
111   Id handle;
112 };
113
114
115 #define COMPS_DEFAULT_ISVISIBLE 1
116 #define COMPS_DEFAULT_ISDEFAULT 0
117
118 /* Return true if "true", false if "false", default_value otherwise */
119 static int
120 parse_boolean(char *content, int default_value)
121 {
122   if (!strcmp(content, "true"))
123     return 1;
124   if (!strcmp(content, "false"))
125     return 0;
126   return default_value;
127 }
128
129
130 static void
131 startElement(struct solv_xmlparser *xmlp, int state, const char *name, const char **atts)
132 {
133   struct parsedata *pd = xmlp->userdata;
134   Pool *pool = pd->pool;
135   Solvable *s;
136
137   switch(state)
138     {
139     case STATE_GROUP:
140     case STATE_CATEGORY:
141     case STATE_ENVIRONMENT:
142       s = pd->solvable = pool_id2solvable(pool, repo_add_solvable(pd->repo));
143       pd->handle = s - pool->solvables;
144       if (state == STATE_GROUP)
145         pd->kind = "group";
146       else if (state == STATE_CATEGORY)
147         pd->kind = "category";
148       else
149         pd->kind = "environment";
150       pd->isvisible = COMPS_DEFAULT_ISVISIBLE;
151       pd->isdefault = COMPS_DEFAULT_ISDEFAULT;
152       break;
153
154     case STATE_NAME:
155     case STATE_CNAME:
156     case STATE_DESCRIPTION:
157     case STATE_CDESCRIPTION:
158       pd->tmplang = join_dup(&pd->jd, solv_xmlparser_find_attr("xml:lang", atts));
159       break;
160
161     case STATE_PACKAGEREQ:
162       {
163         const char *type = solv_xmlparser_find_attr("type", atts);
164         pd->condreq = 0;
165         pd->reqtype = SOLVABLE_RECOMMENDS;
166         if (type && !strcmp(type, "conditional"))
167           {
168             const char *requires = solv_xmlparser_find_attr("requires", atts);
169             if (requires && *requires)
170               pd->condreq = pool_str2id(pool, requires, 1);
171           }
172         else if (type && !strcmp(type, "mandatory"))
173           pd->reqtype = SOLVABLE_REQUIRES;
174         else if (type && !strcmp(type, "optional"))
175           pd->reqtype = SOLVABLE_SUGGESTS;
176         break;
177       }
178
179     case STATE_GROUPLIST:
180       {
181         pd->reqtype = SOLVABLE_REQUIRES;
182         break;
183       }
184
185     case STATE_OPTIONLIST:
186       {
187         pd->reqtype = SOLVABLE_SUGGESTS;
188         break;
189       }
190
191     default:
192       break;
193     }
194 }
195
196
197 static void
198 endElement(struct solv_xmlparser *xmlp, int state, char *content)
199 {
200   struct parsedata *pd = xmlp->userdata;
201   Solvable *s = pd->solvable;
202   Id id;
203
204   switch (state)
205     {
206     case STATE_GROUP:
207     case STATE_CATEGORY:
208     case STATE_ENVIRONMENT:
209       if (!s->arch)
210         s->arch = ARCH_NOARCH;
211       if (!s->evr)
212         s->evr = ID_EMPTY;
213       if (s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
214         s->provides = repo_addid_dep(pd->repo, s->provides, pool_rel2id(pd->pool, s->name, s->evr, REL_EQ, 1), 0);
215       if (pd->isvisible)
216         repodata_set_void(pd->data, pd->handle, SOLVABLE_ISVISIBLE);
217       if (pd->isdefault)
218         repodata_set_void(pd->data, pd->handle, SOLVABLE_ISDEFAULT);
219       pd->solvable = 0;
220       break;
221
222     case STATE_ID:
223       s->name = pool_str2id(pd->pool, join2(&pd->jd, pd->kind, ":", content), 1);
224       break;
225
226     case STATE_NAME:
227       repodata_set_str(pd->data, pd->handle, pool_id2langid(pd->pool, SOLVABLE_SUMMARY, pd->tmplang, 1), content);
228       break;
229
230     case STATE_DESCRIPTION:
231       repodata_set_str(pd->data, pd->handle, pool_id2langid(pd->pool, SOLVABLE_DESCRIPTION, pd->tmplang, 1), content);
232       break;
233
234     case STATE_PACKAGEREQ:
235       id = pool_str2id(pd->pool, content, 1);
236       if (pd->condreq)
237         id = pool_rel2id(pd->pool, id, pd->condreq, REL_COND, 1);
238       repo_add_idarray(pd->repo, pd->handle, pd->reqtype, id);
239       break;
240
241     case STATE_GROUPID:
242       id = pool_str2id(pd->pool, join2(&pd->jd, "group", ":", content), 1);
243       repo_add_idarray(pd->repo, pd->handle, pd->reqtype, id);
244       break;
245
246     case STATE_USERVISIBLE:
247       pd->isvisible = parse_boolean(content, COMPS_DEFAULT_ISVISIBLE);
248       break;
249
250     case STATE_DEFAULT:
251       pd->isdefault = parse_boolean(content, COMPS_DEFAULT_ISDEFAULT);
252       break;
253
254     case STATE_LANG_ONLY:
255       repodata_set_str(pd->data, pd->handle, SOLVABLE_LANGONLY, content);
256       break;
257
258     case STATE_LANGONLY:
259       repodata_set_str(pd->data, pd->handle, SOLVABLE_LANGONLY, content);
260       break;
261
262     case STATE_DISPLAY_ORDER:
263       repodata_set_str(pd->data, pd->handle, SOLVABLE_ORDER, content);
264       break;
265
266     default:
267       break;
268     }
269 }
270
271 int
272 repo_add_comps(Repo *repo, FILE *fp, int flags)
273 {
274   Repodata *data;
275   struct parsedata pd;
276
277   data = repo_add_repodata(repo, flags);
278
279   memset(&pd, 0, sizeof(pd));
280   pd.repo = repo;
281   pd.pool = repo->pool;
282   pd.data = data;
283   solv_xmlparser_init(&pd.xmlp, stateswitches, &pd, startElement, endElement);
284   if (solv_xmlparser_parse(&pd.xmlp, fp) != SOLV_XMLPARSER_OK)
285     pool_debug(pd.pool, SOLV_ERROR, "repo_comps: %s at line %u:%u\n", pd.xmlp.errstr, pd.xmlp.line, pd.xmlp.column);
286   solv_xmlparser_free(&pd.xmlp);
287   join_freemem(&pd.jd);
288
289   if (!(flags & REPO_NO_INTERNALIZE))
290     repodata_internalize(data);
291   return 0;
292 }
293
294 /* EOF */