/*
* Copyright (c) 2019, SUSE LLC.
*
* This program is licensed under the BSD license, read LICENSE.BSD
* for further information
*/
#define _GNU_SOURCE
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "pool.h"
#include "repo.h"
#include "chksum.h"
#include "solv_jsonparser.h"
#include "conda.h"
#include "repo_conda.h"
struct parsedata {
Pool *pool;
Repo *repo;
Repodata *data;
};
static int
parse_deps(struct parsedata *pd, struct solv_jsonparser *jp, Offset *depp)
{
int type = JP_ARRAY;
while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_ARRAY_END)
{
if (type == JP_STRING)
{
Id id = pool_conda_matchspec(pd->pool, jp->value);
if (id)
*depp = repo_addid_dep(pd->repo, *depp, id, 0);
}
else
type = jsonparser_skip(jp, type);
}
return type;
}
static int
parse_otherdeps(struct parsedata *pd, struct solv_jsonparser *jp, Id handle, Id keyname)
{
int type = JP_ARRAY;
while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_ARRAY_END)
{
if (type == JP_STRING)
{
Id id = pool_conda_matchspec(pd->pool, jp->value);
if (id)
repodata_add_idarray(pd->data, handle, keyname, id);
}
else
type = jsonparser_skip(jp, type);
}
return type;
}
static int
parse_package(struct parsedata *pd, struct solv_jsonparser *jp, char *kfn)
{
int type = JP_OBJECT;
Pool *pool= pd->pool;
Repodata *data = pd->data;
Solvable *s;
Id handle = repo_add_solvable(pd->repo);
s = pool_id2solvable(pool, handle);
char *fn = 0;
char *subdir = 0;
while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_OBJECT_END)
{
if (type == JP_STRING && !strcmp(jp->key, "build"))
repodata_add_poolstr_array(data, handle, SOLVABLE_BUILDFLAVOR, jp->value);
else if (type == JP_NUMBER && !strcmp(jp->key, "build_number"))
repodata_set_str(data, handle, SOLVABLE_BUILDVERSION, jp->value);
else if (type == JP_ARRAY && !strcmp(jp->key, "depends"))
type = parse_deps(pd, jp, &s->requires);
else if (type == JP_ARRAY && !strcmp(jp->key, "requires"))
type = parse_deps(pd, jp, &s->requires);
else if (type == JP_ARRAY && !strcmp(jp->key, "constrains"))
type = parse_otherdeps(pd, jp, handle, SOLVABLE_CONSTRAINS);
else if (type == JP_STRING && !strcmp(jp->key, "license"))
repodata_add_poolstr_array(data, handle, SOLVABLE_LICENSE, jp->value);
else if (type == JP_STRING && !strcmp(jp->key, "md5"))
repodata_set_checksum(data, handle, SOLVABLE_PKGID, REPOKEY_TYPE_MD5, jp->value);
else if (type == JP_STRING && !strcmp(jp->key, "sha256"))
repodata_set_checksum(data, handle, SOLVABLE_CHECKSUM, REPOKEY_TYPE_SHA256, jp->value);
else if (type == JP_STRING && !strcmp(jp->key, "name"))
s->name = pool_str2id(pool, jp->value, 1);
else if (type == JP_STRING && !strcmp(jp->key, "version"))
s->evr= pool_str2id(pool, jp->value, 1);
else if (type == JP_STRING && !strcmp(jp->key, "fn") && !fn)
fn = solv_strdup(jp->value);
else if (type == JP_STRING && !strcmp(jp->key, "subdir") && !subdir)
subdir = solv_strdup(jp->value);
else if (type == JP_NUMBER && !strcmp(jp->key, "size"))
repodata_set_num(data, handle, SOLVABLE_DOWNLOADSIZE, strtoull(jp->value, 0, 10));
else if (type == JP_NUMBER && !strcmp(jp->key, "timestamp"))
{
unsigned long long ts = strtoull(jp->value, 0, 10);
if (ts > 253402300799ULL)
ts /= 1000;
repodata_set_num(data, handle, SOLVABLE_BUILDTIME, ts);
}
else
type = jsonparser_skip(jp, type);
}
if (fn || kfn)
repodata_set_location(data, handle, 0, subdir, fn ? fn : kfn);
solv_free(fn);
solv_free(subdir);
if (!s->evr)
s->evr = 1;
if (s->name)
s->provides = repo_addid_dep(pd->repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
return type;
}
static int
parse_packages(struct parsedata *pd, struct solv_jsonparser *jp)
{
int type = JP_OBJECT;
while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_OBJECT_END)
{
if (type == JP_OBJECT)
{
char *fn = solv_strdup(jp->key);
type = parse_package(pd, jp, fn);
solv_free(fn);
}
else
type = jsonparser_skip(jp, type);
}
return type;
}
static int
parse_packages2(struct parsedata *pd, struct solv_jsonparser *jp)
{
int type = JP_ARRAY;
while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_ARRAY_END)
{
if (type == JP_OBJECT)
type = parse_package(pd, jp, 0);
else
type = jsonparser_skip(jp, type);
}
return type;
}
static int
parse_main(struct parsedata *pd, struct solv_jsonparser *jp)
{
int type = JP_OBJECT;
while (type > 0 && (type = jsonparser_parse(jp)) > 0 && type != JP_OBJECT_END)
{
if (type == JP_OBJECT && !strcmp("packages", jp->key))
type = parse_packages(pd, jp);
if (type == JP_ARRAY && !strcmp("packages", jp->key))
type = parse_packages2(pd, jp);
else
type = jsonparser_skip(jp, type);
}
return type;
}
int
repo_add_conda(Repo *repo, FILE *fp, int flags)
{
Pool *pool = repo->pool;
struct solv_jsonparser jp;
struct parsedata pd;
Repodata *data;
int type, ret = 0;
data = repo_add_repodata(repo, flags);
memset(&pd, 0, sizeof(pd));
pd.pool = pool;
pd.repo = repo;
pd.data = data;
jsonparser_init(&jp, fp);
if ((type = jsonparser_parse(&jp)) != JP_OBJECT)
ret = pool_error(pool, -1, "repository does not start with an object");
else if ((type = parse_main(&pd, &jp)) != JP_OBJECT_END)
ret = pool_error(pool, -1, "parse error line %d", jp.line);
jsonparser_free(&jp);
if (!(flags & REPO_NO_INTERNALIZE))
repodata_internalize(data);
return ret;
}