Imported Upstream version 0.7.18
[platform/upstream/libsolv.git] / ext / repo_conda.c
1 /*
2  * Copyright (c) 2019, SUSE LLC.
3  *
4  * This program is licensed under the BSD license, read LICENSE.BSD
5  * for further information
6  */
7
8 #define _GNU_SOURCE
9 #include <sys/types.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13
14 #include "pool.h"
15 #include "repo.h"
16 #include "chksum.h"
17 #include "solv_jsonparser.h"
18 #include "conda.h"
19 #include "repo_conda.h"
20
21 struct parsedata {
22   Pool *pool;
23   Repo *repo;
24   Repodata *data;
25
26   Stringpool fnpool;
27   Queue fndata;
28 };
29
30 static int
31 parse_deps(struct parsedata *pd, struct solv_jsonparser *jp, Offset *depp)
32 {
33   int type = JP_ARRAY;
34   while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_ARRAY_END)
35     {
36       if (type == JP_STRING)
37         {
38           Id id = pool_conda_matchspec(pd->pool, jp->value);
39           if (id)
40             *depp = repo_addid_dep(pd->repo, *depp, id, 0);
41         }
42       else
43         type = jsonparser_skip(jp, type);
44     }
45   return type;
46 }
47
48 static int
49 parse_otherdeps(struct parsedata *pd, struct solv_jsonparser *jp, Id handle, Id keyname)
50 {
51   int type = JP_ARRAY;
52   while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_ARRAY_END)
53     {
54       if (type == JP_STRING)
55         {
56           Id id = pool_conda_matchspec(pd->pool, jp->value);
57           if (id)
58             repodata_add_idarray(pd->data, handle, keyname, id);
59         }
60       else
61         type = jsonparser_skip(jp, type);
62     }
63   return type;
64 }
65
66 static int
67 parse_trackfeatures_array(struct parsedata *pd, struct solv_jsonparser *jp, Id handle)
68 {
69   int type = JP_ARRAY;
70   while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_ARRAY_END)
71     {
72       if (type == JP_STRING)
73         {
74           char *p = jp->value, *pe;
75           while (*p == ' ' || *p == '\t')
76             p++;
77           if (!*p)
78             continue;
79           for (pe = p + strlen(p) - 1; pe > p; pe--)
80             if (*pe != ' ' && *pe != '\t')
81               break;
82           repodata_add_idarray(pd->data, handle, SOLVABLE_TRACK_FEATURES, pool_strn2id(pd->pool, p, pe - p + 1, 1));
83         }
84       else
85         type = jsonparser_skip(jp, type);
86     }
87   return type;
88 }
89
90 static void
91 parse_trackfeatures_string(struct parsedata *pd, const char *p, Id handle)
92 {
93   const char *pe;
94   for (; *p; p++)
95     {
96       if (*p == ' ' || *p == '\t' || *p == ',')
97         continue;
98       pe = p + 1;
99       while (*pe && *pe != ' ' && *pe != '\t' && *pe != ',')
100         pe++;
101       repodata_add_idarray(pd->data, handle, SOLVABLE_TRACK_FEATURES, pool_strn2id(pd->pool, p, pe - p, 1));
102       p = pe - 1;
103     }
104 }
105
106 static void 
107 swap_solvables(Pool *pool, Repodata *data, Id pa, Id pb)
108 {
109   Solvable tmp; 
110
111   tmp = pool->solvables[pa];
112   pool->solvables[pa] = pool->solvables[pb];
113   pool->solvables[pb] = tmp; 
114   repodata_swap_attrs(data, pa, pb); 
115 }
116
117 static Id *
118 fn2data(struct parsedata *pd, const char *fn, Id *fntypep, int create)
119 {
120   size_t l = strlen(fn), extl = 0;
121   Id fnid;
122   if (l > 6 && !strcmp(fn + l - 6, ".conda"))
123     extl = 6;
124   else if (l > 8 && !strcmp(fn + l - 8, ".tar.bz2"))
125     extl = 8;
126   else
127     return 0;
128   fnid = stringpool_strn2id(&pd->fnpool, fn, l - extl, create);
129   if (!fnid)
130     return 0;
131   if (fnid * 2 + 2 > pd->fndata.count)
132     queue_insertn(&pd->fndata, pd->fndata.count, fnid * 2 + 2 - pd->fndata.count, 0);
133   if (fntypep)
134     *fntypep = extl == 8 ? 1 : 2;       /* 1: legacy .tar.bz2  2: .conda */
135   return pd->fndata.elements + 2 * fnid;
136 }
137
138 static int
139 parse_package(struct parsedata *pd, struct solv_jsonparser *jp, char *kfn)
140 {
141   int type = JP_OBJECT;
142   Pool *pool= pd->pool;
143   Repodata *data = pd->data;
144   Solvable *s;
145   Id handle;
146   char *fn = 0;
147   char *subdir = 0;
148   Id *fndata = 0, fntype = 0;
149
150   handle = repo_add_solvable(pd->repo);
151   s = pool_id2solvable(pool, handle);
152   while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_OBJECT_END)
153     {
154       if (type == JP_STRING && !strcmp(jp->key, "build"))
155         repodata_add_poolstr_array(data, handle, SOLVABLE_BUILDFLAVOR, jp->value);
156       else if (type == JP_NUMBER && !strcmp(jp->key, "build_number"))
157         repodata_set_str(data, handle, SOLVABLE_BUILDVERSION, jp->value);
158       else if (type == JP_ARRAY && !strcmp(jp->key, "depends"))
159         type = parse_deps(pd, jp, &s->requires);
160       else if (type == JP_ARRAY && !strcmp(jp->key, "requires"))
161         type = parse_deps(pd, jp, &s->requires);
162       else if (type == JP_ARRAY && !strcmp(jp->key, "constrains"))
163         type = parse_otherdeps(pd, jp, handle, SOLVABLE_CONSTRAINS);
164       else if (type == JP_STRING && !strcmp(jp->key, "license"))
165         repodata_add_poolstr_array(data, handle, SOLVABLE_LICENSE, jp->value);
166       else if (type == JP_STRING && !strcmp(jp->key, "md5"))
167         repodata_set_checksum(data, handle, SOLVABLE_PKGID, REPOKEY_TYPE_MD5, jp->value);
168       else if (type == JP_STRING && !strcmp(jp->key, "sha256"))
169         repodata_set_checksum(data, handle, SOLVABLE_CHECKSUM, REPOKEY_TYPE_SHA256, jp->value);
170       else if (type == JP_STRING && !strcmp(jp->key, "name"))
171         s->name = pool_str2id(pool, jp->value, 1);
172       else if (type == JP_STRING && !strcmp(jp->key, "version"))
173         s->evr= pool_str2id(pool, jp->value, 1);
174       else if (type == JP_STRING && !strcmp(jp->key, "fn") && !fn)
175         fn = solv_strdup(jp->value);
176       else if (type == JP_STRING && !strcmp(jp->key, "subdir") && !subdir)
177         subdir = solv_strdup(jp->value);
178       else if (type == JP_NUMBER && !strcmp(jp->key, "size"))
179         repodata_set_num(data, handle, SOLVABLE_DOWNLOADSIZE, strtoull(jp->value, 0, 10));
180       else if (type == JP_NUMBER && !strcmp(jp->key, "timestamp"))
181         {
182           unsigned long long ts = strtoull(jp->value, 0, 10);
183           if (ts > 253402300799ULL)
184             ts /= 1000;
185           repodata_set_num(data, handle, SOLVABLE_BUILDTIME, ts);
186         }
187       else if (type == JP_STRING && !strcmp(jp->key, "track_features"))
188         parse_trackfeatures_string(pd, jp->value, handle);
189       else if (type == JP_ARRAY && !strcmp(jp->key, "track_features"))
190         type = parse_trackfeatures_array(pd, jp, handle);
191       else
192         type = jsonparser_skip(jp, type);
193     }
194   if (fn || kfn)
195     {
196       repodata_set_location(data, handle, 0, subdir, fn ? fn : kfn);
197       fndata = fn2data(pd, fn ? fn : kfn, &fntype, 1);
198     }
199   solv_free(fn);
200   solv_free(subdir);
201   if (!s->evr)
202     s->evr = 1;
203   if (s->name)
204     s->provides = repo_addid_dep(pd->repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
205
206   if (fndata)
207     {
208       /* deal with legacy package entries */
209       if (fndata[0] && fndata[0] > fntype)
210         {
211           /* ignore this package */
212           repo_free_solvable(pd->repo, handle, 1);
213           return type;
214         }
215       if (fndata[0] && fndata[0] < fntype)
216         {
217           /* replace old package */
218           swap_solvables(pool, data, handle, fndata[1]);
219           repo_free_solvable(pd->repo, handle, 1);
220           handle = fndata[1];
221         }
222       fndata[0] = fntype;
223       fndata[1] = handle;
224     }
225   return type;
226 }
227
228 static int
229 parse_packages(struct parsedata *pd, struct solv_jsonparser *jp)
230 {
231   int type = JP_OBJECT;
232   while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_OBJECT_END)
233     {
234       if (type == JP_OBJECT)
235         {
236           char *fn = solv_strdup(jp->key);
237           type = parse_package(pd, jp, fn);
238           solv_free(fn);
239         }
240       else
241         type = jsonparser_skip(jp, type);
242     }
243   return type;
244 }
245
246 static int
247 parse_packages2(struct parsedata *pd, struct solv_jsonparser *jp)
248 {
249   int type = JP_ARRAY;
250   while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_ARRAY_END)
251     {
252       if (type == JP_OBJECT)
253         type = parse_package(pd, jp, 0);
254       else
255         type = jsonparser_skip(jp, type);
256     }
257   return type;
258 }
259
260 static int
261 parse_main(struct parsedata *pd, struct solv_jsonparser *jp, int flags)
262 {
263   int type = JP_OBJECT;
264   while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_OBJECT_END)
265     {
266       if (type == JP_OBJECT && !strcmp("packages", jp->key))
267         type = parse_packages(pd, jp);
268       else if (type == JP_ARRAY && !strcmp("packages", jp->key))
269         type = parse_packages2(pd, jp);
270       else if (type == JP_OBJECT && !strcmp("packages.conda", jp->key) && !(flags & CONDA_ADD_USE_ONLY_TAR_BZ2))
271         type = parse_packages(pd, jp);
272       else if (type == JP_ARRAY && !strcmp("packages.conda", jp->key) && !(flags & CONDA_ADD_USE_ONLY_TAR_BZ2))
273         type = parse_packages2(pd, jp);
274       else
275         type = jsonparser_skip(jp, type);
276     }
277   return type;
278 }
279
280 int
281 repo_add_conda(Repo *repo, FILE *fp, int flags)
282 {
283   Pool *pool = repo->pool;
284   struct solv_jsonparser jp;
285   struct parsedata pd;
286   Repodata *data;
287   int type, ret = 0;
288
289   data = repo_add_repodata(repo, flags);
290
291   memset(&pd, 0, sizeof(pd));
292   pd.pool = pool;
293   pd.repo = repo;
294   pd.data = data;
295   stringpool_init_empty(&pd.fnpool);
296   queue_init(&pd.fndata);
297
298   jsonparser_init(&jp, fp);
299   if ((type = jsonparser_parse(&jp)) != JP_OBJECT)
300     ret = pool_error(pool, -1, "repository does not start with an object");
301   else if ((type = parse_main(&pd, &jp, flags)) != JP_OBJECT_END)
302     ret = pool_error(pool, -1, "parse error line %d", jp.line);
303   jsonparser_free(&jp);
304
305   queue_free(&pd.fndata);
306   stringpool_free(&pd.fnpool);
307   if (!(flags & REPO_NO_INTERNALIZE))
308     repodata_internalize(data);
309
310   return ret;
311 }
312