Imported Upstream version 0.7.12
[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(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 swap_solvables(Repo *repo, Repodata *data, Id pa, Id pb)
92 {
93   Pool *pool = repo->pool;
94   Solvable tmp; 
95
96   tmp = pool->solvables[pa];
97   pool->solvables[pa] = pool->solvables[pb];
98   pool->solvables[pb] = tmp; 
99   repodata_swap_attrs(data, pa, pb); 
100 }
101
102 static Id *
103 fn2data(struct parsedata *pd, const char *fn, Id *fntypep, int create)
104 {
105   size_t l = strlen(fn), extl = 0;
106   Id fnid;
107   if (l > 6 && !strcmp(fn + l - 6, ".conda"))
108     extl = 6;
109   else if (l > 8 && !strcmp(fn + l - 8, ".tar.bz2"))
110     extl = 8;
111   else
112     return 0;
113   fnid = stringpool_strn2id(&pd->fnpool, fn, l - extl, create);
114   if (!fnid)
115     return 0;
116   if (fnid * 2 + 2 > pd->fndata.count)
117     queue_insertn(&pd->fndata, pd->fndata.count, fnid * 2 + 2 - pd->fndata.count, 0);
118   if (fntypep)
119     *fntypep = extl == 8 ? 1 : 2;       /* 1: legacy .tar.bz2  2: .conda */
120   return pd->fndata.elements + 2 * fnid;
121 }
122
123 static int
124 parse_package(struct parsedata *pd, struct solv_jsonparser *jp, char *kfn)
125 {
126   int type = JP_OBJECT;
127   Pool *pool= pd->pool;
128   Repodata *data = pd->data;
129   Solvable *s;
130   Id handle;
131   char *fn = 0;
132   char *subdir = 0;
133   Id *fndata = 0, fntype = 0;
134
135   handle = repo_add_solvable(pd->repo);
136   s = pool_id2solvable(pool, handle);
137   while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_OBJECT_END)
138     {
139       if (type == JP_STRING && !strcmp(jp->key, "build"))
140         repodata_add_poolstr_array(data, handle, SOLVABLE_BUILDFLAVOR, jp->value);
141       else if (type == JP_NUMBER && !strcmp(jp->key, "build_number"))
142         repodata_set_str(data, handle, SOLVABLE_BUILDVERSION, jp->value);
143       else if (type == JP_ARRAY && !strcmp(jp->key, "depends"))
144         type = parse_deps(pd, jp, &s->requires);
145       else if (type == JP_ARRAY && !strcmp(jp->key, "requires"))
146         type = parse_deps(pd, jp, &s->requires);
147       else if (type == JP_ARRAY && !strcmp(jp->key, "constrains"))
148         type = parse_otherdeps(pd, jp, handle, SOLVABLE_CONSTRAINS);
149       else if (type == JP_STRING && !strcmp(jp->key, "license"))
150         repodata_add_poolstr_array(data, handle, SOLVABLE_LICENSE, jp->value);
151       else if (type == JP_STRING && !strcmp(jp->key, "md5"))
152         repodata_set_checksum(data, handle, SOLVABLE_PKGID, REPOKEY_TYPE_MD5, jp->value);
153       else if (type == JP_STRING && !strcmp(jp->key, "sha256"))
154         repodata_set_checksum(data, handle, SOLVABLE_CHECKSUM, REPOKEY_TYPE_SHA256, jp->value);
155       else if (type == JP_STRING && !strcmp(jp->key, "name"))
156         s->name = pool_str2id(pool, jp->value, 1);
157       else if (type == JP_STRING && !strcmp(jp->key, "version"))
158         s->evr= pool_str2id(pool, jp->value, 1);
159       else if (type == JP_STRING && !strcmp(jp->key, "fn") && !fn)
160         fn = solv_strdup(jp->value);
161       else if (type == JP_STRING && !strcmp(jp->key, "subdir") && !subdir)
162         subdir = solv_strdup(jp->value);
163       else if (type == JP_NUMBER && !strcmp(jp->key, "size"))
164         repodata_set_num(data, handle, SOLVABLE_DOWNLOADSIZE, strtoull(jp->value, 0, 10));
165       else if (type == JP_NUMBER && !strcmp(jp->key, "timestamp"))
166         {
167           unsigned long long ts = strtoull(jp->value, 0, 10);
168           if (ts > 253402300799ULL)
169             ts /= 1000;
170           repodata_set_num(data, handle, SOLVABLE_BUILDTIME, ts);
171         }
172       else if (type == JP_STRING && !strcmp(jp->key, "track_features"))
173         {
174           char *p = jp->value, *pe;
175           for (; *p; p++)
176             {
177               if (*p == ' ' || *p == '\t' || *p == ',')
178                 continue;
179               pe = p + 1;
180               while (*pe && *pe != ' ' && *pe != '\t' && *pe != ',')
181                 pe++;
182               repodata_add_idarray(data, handle, SOLVABLE_TRACK_FEATURES, pool_strn2id(pool, p, pe - p, 1));
183               p = pe - 1;
184             }
185         }
186       else if (type == JP_ARRAY && !strcmp(jp->key, "track_features"))
187         type = parse_trackfeatures(pd, jp, handle);
188       else
189         type = jsonparser_skip(jp, type);
190     }
191   if (fn || kfn)
192     {
193       repodata_set_location(data, handle, 0, subdir, fn ? fn : kfn);
194       fndata = fn2data(pd, fn ? fn : kfn, &fntype, 1);
195     }
196   solv_free(fn);
197   solv_free(subdir);
198   if (!s->evr)
199     s->evr = 1;
200   if (s->name)
201     s->provides = repo_addid_dep(pd->repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
202
203   if (fndata)
204     {
205       /* deal with legacy package entries */
206       if (fndata[0] && fndata[0] > fntype)
207         {
208           /* ignore this package */
209           repo_free_solvable(pd->repo, handle, 1);
210           return type;
211         }
212       if (fndata[0] && fndata[0] < fntype)
213         {
214           /* replace old package */
215           swap_solvables(pd->repo, data, handle, fndata[1]);
216           repo_free_solvable(pd->repo, handle, 1);
217           handle = fndata[1];
218         }
219       fndata[0] = fntype;
220       fndata[1] = handle;
221     }
222   return type;
223 }
224
225 static int
226 parse_packages(struct parsedata *pd, struct solv_jsonparser *jp)
227 {
228   int type = JP_OBJECT;
229   while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_OBJECT_END)
230     {
231       if (type == JP_OBJECT)
232         {
233           char *fn = solv_strdup(jp->key);
234           type = parse_package(pd, jp, fn);
235           solv_free(fn);
236         }
237       else
238         type = jsonparser_skip(jp, type);
239     }
240   return type;
241 }
242
243 static int
244 parse_packages2(struct parsedata *pd, struct solv_jsonparser *jp)
245 {
246   int type = JP_ARRAY;
247   while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_ARRAY_END)
248     {
249       if (type == JP_OBJECT)
250         type = parse_package(pd, jp, 0);
251       else
252         type = jsonparser_skip(jp, type);
253     }
254   return type;
255 }
256
257 static int
258 parse_main(struct parsedata *pd, struct solv_jsonparser *jp)
259 {
260   int type = JP_OBJECT;
261   while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_OBJECT_END)
262     {
263       if (type == JP_OBJECT && !strcmp("packages", jp->key))
264         type = parse_packages(pd, jp);
265       else if (type == JP_ARRAY && !strcmp("packages", jp->key))
266         type = parse_packages2(pd, jp);
267       else if (type == JP_OBJECT && !strcmp("packages.conda", jp->key))
268         type = parse_packages(pd, jp);
269       else if (type == JP_ARRAY && !strcmp("packages.conda", jp->key))
270         type = parse_packages2(pd, jp);
271       else
272         type = jsonparser_skip(jp, type);
273     }
274   return type;
275 }
276
277 int
278 repo_add_conda(Repo *repo, FILE *fp, int flags)
279 {
280   Pool *pool = repo->pool;
281   struct solv_jsonparser jp;
282   struct parsedata pd;
283   Repodata *data;
284   int type, ret = 0;
285
286   data = repo_add_repodata(repo, flags);
287
288   memset(&pd, 0, sizeof(pd));
289   pd.pool = pool;
290   pd.repo = repo;
291   pd.data = data;
292   stringpool_init_empty(&pd.fnpool);
293   queue_init(&pd.fndata);
294
295   jsonparser_init(&jp, fp);
296   if ((type = jsonparser_parse(&jp)) != JP_OBJECT)
297     ret = pool_error(pool, -1, "repository does not start with an object");
298   else if ((type = parse_main(&pd, &jp)) != JP_OBJECT_END)
299     ret = pool_error(pool, -1, "parse error line %d", jp.line);
300   jsonparser_free(&jp);
301
302   queue_free(&pd.fndata);
303   stringpool_free(&pd.fnpool);
304   if (!(flags & REPO_NO_INTERNALIZE))
305     repodata_internalize(data);
306
307   return ret;
308 }
309