|
Packit Service |
ff689b |
/*
|
|
Packit Service |
ff689b |
* solv_jsonparser.c
|
|
Packit Service |
ff689b |
*
|
|
Packit Service |
ff689b |
* Simple JSON stream parser
|
|
Packit Service |
ff689b |
*
|
|
Packit Service |
ff689b |
* Copyright (c) 2018, SUSE LLC
|
|
Packit Service |
ff689b |
*
|
|
Packit Service |
ff689b |
* This program is licensed under the BSD license, read LICENSE.BSD
|
|
Packit Service |
ff689b |
* for further information
|
|
Packit Service |
ff689b |
*/
|
|
Packit Service |
ff689b |
|
|
Packit Service |
ff689b |
#include <stdio.h>
|
|
Packit Service |
ff689b |
#include <stdlib.h>
|
|
Packit Service |
ff689b |
|
|
Packit Service |
ff689b |
#include "util.h"
|
|
Packit Service |
ff689b |
#include "solv_jsonparser.h"
|
|
Packit Service |
ff689b |
|
|
Packit Service |
ff689b |
void
|
|
Packit Service |
ff689b |
jsonparser_init(struct solv_jsonparser *jp, FILE *fp)
|
|
Packit Service |
ff689b |
{
|
|
Packit Service |
ff689b |
memset(jp, 0, sizeof(*jp));
|
|
Packit Service |
ff689b |
jp->fp = fp;
|
|
Packit Service |
ff689b |
jp->state = JP_START;
|
|
Packit Service |
ff689b |
jp->line = jp->nextline = 1;
|
|
Packit Service |
ff689b |
jp->nextc = ' ';
|
|
Packit Service |
ff689b |
queue_init(&jp->stateq);
|
|
Packit Service |
ff689b |
}
|
|
Packit Service |
ff689b |
|
|
Packit Service |
ff689b |
void
|
|
Packit Service |
ff689b |
jsonparser_free(struct solv_jsonparser *jp)
|
|
Packit Service |
ff689b |
{
|
|
Packit Service |
ff689b |
solv_free(jp->space);
|
|
Packit Service |
ff689b |
queue_free(&jp->stateq);
|
|
Packit Service |
ff689b |
}
|
|
Packit Service |
ff689b |
|
|
Packit Service |
ff689b |
static void
|
|
Packit Service |
ff689b |
savec(struct solv_jsonparser *jp, char c)
|
|
Packit Service |
ff689b |
{
|
|
Packit Service |
ff689b |
if (jp->nspace == jp->aspace)
|
|
Packit Service |
ff689b |
{
|
|
Packit Service |
ff689b |
jp->aspace += 256;
|
|
Packit Service |
ff689b |
jp->space = solv_realloc(jp->space, jp->aspace);
|
|
Packit Service |
ff689b |
}
|
|
Packit Service |
ff689b |
jp->space[jp->nspace++] = c;
|
|
Packit Service |
ff689b |
}
|
|
Packit Service |
ff689b |
|
|
Packit Service |
ff689b |
static void
|
|
Packit Service |
ff689b |
saveutf8(struct solv_jsonparser *jp, int c)
|
|
Packit Service |
ff689b |
{
|
|
Packit Service |
ff689b |
int i;
|
|
Packit Service |
ff689b |
if (c < 0x80)
|
|
Packit Service |
ff689b |
{
|
|
Packit Service |
ff689b |
savec(jp, c);
|
|
Packit Service |
ff689b |
return;
|
|
Packit Service |
ff689b |
}
|
|
Packit Service |
ff689b |
i = c < 0x800 ? 1 : c < 0x10000 ? 2 : 3;
|
|
Packit Service |
ff689b |
savec(jp, (0x1f80 >> i) | (c >> (6 * i)));
|
|
Packit Service |
ff689b |
while (--i >= 0)
|
|
Packit Service |
ff689b |
savec(jp, 0x80 | ((c >> (6 * i)) & 0x3f));
|
|
Packit Service |
ff689b |
}
|
|
Packit Service |
ff689b |
|
|
Packit Service |
ff689b |
static inline int
|
|
Packit Service |
ff689b |
nextc(struct solv_jsonparser *jp)
|
|
Packit Service |
ff689b |
{
|
|
Packit Service |
ff689b |
int c = getc(jp->fp);
|
|
Packit Service |
ff689b |
if (c == '\n')
|
|
Packit Service |
ff689b |
jp->nextline++;
|
|
Packit Service |
ff689b |
return c;
|
|
Packit Service |
ff689b |
}
|
|
Packit Service |
ff689b |
|
|
Packit Service |
ff689b |
static int
|
|
Packit Service |
ff689b |
skipspace(struct solv_jsonparser *jp)
|
|
Packit Service |
ff689b |
{
|
|
Packit Service |
ff689b |
int c = jp->nextc;
|
|
Packit Service |
ff689b |
jp->nextc = ' ';
|
|
Packit Service |
ff689b |
while (c == ' ' || c == '\t' || c == '\r' || c == '\n')
|
|
Packit Service |
ff689b |
c = nextc(jp);
|
|
Packit Service |
ff689b |
jp->line = jp->nextline;
|
|
Packit Service |
ff689b |
return c;
|
|
Packit Service |
ff689b |
}
|
|
Packit Service |
ff689b |
|
|
Packit Service |
ff689b |
static int
|
|
Packit Service |
ff689b |
parseliteral(struct solv_jsonparser *jp, int c)
|
|
Packit Service |
ff689b |
{
|
|
Packit Service |
ff689b |
size_t nspace = jp->nspace;
|
|
Packit Service |
ff689b |
savec(jp, c);
|
|
Packit Service |
ff689b |
for (;;)
|
|
Packit Service |
ff689b |
{
|
|
Packit Service |
ff689b |
c = nextc(jp);
|
|
Packit Service |
ff689b |
if (c < 'a' || c > 'z')
|
|
Packit Service |
ff689b |
break;
|
|
Packit Service |
ff689b |
savec(jp, c);
|
|
Packit Service |
ff689b |
}
|
|
Packit Service |
ff689b |
jp->nextc = c;
|
|
Packit Service |
ff689b |
savec(jp, 0);
|
|
Packit Service |
ff689b |
if (!strcmp(jp->space + nspace, "true"))
|
|
Packit Service |
ff689b |
return JP_BOOL;
|
|
Packit Service |
ff689b |
if (!strcmp(jp->space + nspace, "false"))
|
|
Packit Service |
ff689b |
return JP_BOOL;
|
|
Packit Service |
ff689b |
if (!strcmp(jp->space + nspace, "null"))
|
|
Packit Service |
ff689b |
return JP_NULL;
|
|
Packit Service |
ff689b |
return JP_ERROR;
|
|
Packit Service |
ff689b |
}
|
|
Packit Service |
ff689b |
|
|
Packit Service |
ff689b |
static int
|
|
Packit Service |
ff689b |
parsenumber(struct solv_jsonparser *jp, int c)
|
|
Packit Service |
ff689b |
{
|
|
Packit Service |
ff689b |
savec(jp, c);
|
|
Packit Service |
ff689b |
for (;;)
|
|
Packit Service |
ff689b |
{
|
|
Packit Service |
ff689b |
c = nextc(jp);
|
|
Packit Service |
ff689b |
if ((c < '0' || c > '9') && c != '+' && c != '-' && c != '.' && c != 'e' && c != 'E')
|
|
Packit Service |
ff689b |
break;
|
|
Packit Service |
ff689b |
savec(jp, c);
|
|
Packit Service |
ff689b |
}
|
|
Packit Service |
ff689b |
jp->nextc = c;
|
|
Packit Service |
ff689b |
savec(jp, 0);
|
|
Packit Service |
ff689b |
return JP_NUMBER;
|
|
Packit Service |
ff689b |
}
|
|
Packit Service |
ff689b |
|
|
Packit Service |
ff689b |
static int
|
|
Packit Service |
ff689b |
parseutf8(struct solv_jsonparser *jp, int surrogate)
|
|
Packit Service |
ff689b |
{
|
|
Packit Service |
ff689b |
int c, i, r = 0;
|
|
Packit Service |
ff689b |
/* parse 4-digit hex */
|
|
Packit Service |
ff689b |
for (i = 0; i < 4; i++)
|
|
Packit Service |
ff689b |
{
|
|
Packit Service |
ff689b |
c = nextc(jp);
|
|
Packit Service |
ff689b |
if (c >= '0' && c <= '9')
|
|
Packit Service |
ff689b |
c -= '0';
|
|
Packit Service |
ff689b |
else if (c >= 'a' && c <= 'f')
|
|
Packit Service |
ff689b |
c -= 'a' - 10;
|
|
Packit Service |
ff689b |
else if (c >= 'A' && c <= 'F')
|
|
Packit Service |
ff689b |
c -= 'A' - 10;
|
|
Packit Service |
ff689b |
else
|
|
Packit Service |
ff689b |
return -1;
|
|
Packit Service |
ff689b |
r = (r << 4) | c;
|
|
Packit Service |
ff689b |
}
|
|
Packit Service |
ff689b |
if (!surrogate && r >= 0xd800 && r < 0xdc00)
|
|
Packit Service |
ff689b |
{
|
|
Packit Service |
ff689b |
/* utf16 surrogate pair encodes 0x10000 - 0x10ffff */
|
|
Packit Service |
ff689b |
int r2;
|
|
Packit Service |
ff689b |
if (nextc(jp) != '\\' || nextc(jp) != 'u' || (r2 = parseutf8(jp, 1)) < 0xdc00 || r2 >= 0xe000)
|
|
Packit Service |
ff689b |
return -1;
|
|
Packit Service |
ff689b |
r = 0x10000 + ((r & 0x3ff) << 10 | (r2 & 0x3ff));
|
|
Packit Service |
ff689b |
}
|
|
Packit Service |
ff689b |
return r;
|
|
Packit Service |
ff689b |
}
|
|
Packit Service |
ff689b |
|
|
Packit Service |
ff689b |
static int
|
|
Packit Service |
ff689b |
parsestring(struct solv_jsonparser *jp)
|
|
Packit Service |
ff689b |
{
|
|
Packit Service |
ff689b |
int c;
|
|
Packit Service |
ff689b |
for (;;)
|
|
Packit Service |
ff689b |
{
|
|
Packit Service |
ff689b |
if ((c = nextc(jp)) < 32)
|
|
Packit Service |
ff689b |
return JP_ERROR;
|
|
Packit Service |
ff689b |
if (c == '"')
|
|
Packit Service |
ff689b |
break;
|
|
Packit Service |
ff689b |
if (c == '\\')
|
|
Packit Service |
ff689b |
{
|
|
Packit Service |
ff689b |
switch (c = nextc(jp))
|
|
Packit Service |
ff689b |
{
|
|
Packit Service |
ff689b |
case '"':
|
|
Packit Service |
ff689b |
case '\\':
|
|
Packit Service |
ff689b |
case '/':
|
|
Packit Service |
ff689b |
case '\n':
|
|
Packit Service |
ff689b |
break;
|
|
Packit Service |
ff689b |
case 'b':
|
|
Packit Service |
ff689b |
c = '\b';
|
|
Packit Service |
ff689b |
break;
|
|
Packit Service |
ff689b |
case 'f':
|
|
Packit Service |
ff689b |
c = '\f';
|
|
Packit Service |
ff689b |
break;
|
|
Packit Service |
ff689b |
case 'n':
|
|
Packit Service |
ff689b |
c = '\n';
|
|
Packit Service |
ff689b |
break;
|
|
Packit Service |
ff689b |
case 'r':
|
|
Packit Service |
ff689b |
c = '\r';
|
|
Packit Service |
ff689b |
break;
|
|
Packit Service |
ff689b |
case 't':
|
|
Packit Service |
ff689b |
c = '\t';
|
|
Packit Service |
ff689b |
break;
|
|
Packit Service |
ff689b |
case 'u':
|
|
Packit Service |
ff689b |
if ((c = parseutf8(jp, 0)) < 0)
|
|
Packit Service |
ff689b |
return JP_ERROR;
|
|
Packit Service |
ff689b |
saveutf8(jp, c);
|
|
Packit Service |
ff689b |
continue;
|
|
Packit Service |
ff689b |
default:
|
|
Packit Service |
ff689b |
return JP_ERROR;
|
|
Packit Service |
ff689b |
}
|
|
Packit Service |
ff689b |
}
|
|
Packit Service |
ff689b |
savec(jp, c);
|
|
Packit Service |
ff689b |
}
|
|
Packit Service |
ff689b |
savec(jp, 0);
|
|
Packit Service |
ff689b |
return JP_STRING;
|
|
Packit Service |
ff689b |
}
|
|
Packit Service |
ff689b |
|
|
Packit Service |
ff689b |
static int
|
|
Packit Service |
ff689b |
parsevalue(struct solv_jsonparser *jp)
|
|
Packit Service |
ff689b |
{
|
|
Packit Service |
ff689b |
int c = skipspace(jp);
|
|
Packit Service |
ff689b |
if (c == '"')
|
|
Packit Service |
ff689b |
return parsestring(jp);
|
|
Packit Service |
ff689b |
if ((c >= '0' && c <= '9') || c == '+' || c == '-' || c == '.')
|
|
Packit Service |
ff689b |
return parsenumber(jp, c);
|
|
Packit Service |
ff689b |
if ((c >= 'a' && c <= 'z'))
|
|
Packit Service |
ff689b |
return parseliteral(jp, c);
|
|
Packit Service |
ff689b |
if (c == '[')
|
|
Packit Service |
ff689b |
return JP_ARRAY;
|
|
Packit Service |
ff689b |
if (c == '{')
|
|
Packit Service |
ff689b |
return JP_OBJECT;
|
|
Packit Service |
ff689b |
if (c == ']')
|
|
Packit Service |
ff689b |
return JP_ARRAY_END;
|
|
Packit Service |
ff689b |
if (c == '}')
|
|
Packit Service |
ff689b |
return JP_OBJECT_END;
|
|
Packit Service |
ff689b |
return JP_ERROR;
|
|
Packit Service |
ff689b |
}
|
|
Packit Service |
ff689b |
|
|
Packit Service |
ff689b |
int
|
|
Packit Service |
ff689b |
jsonparser_parse(struct solv_jsonparser *jp)
|
|
Packit Service |
ff689b |
{
|
|
Packit Service |
ff689b |
int type;
|
|
Packit Service |
ff689b |
size_t nspace;
|
|
Packit Service |
ff689b |
|
|
Packit Service |
ff689b |
jp->depth = jp->stateq.count;
|
|
Packit Service |
ff689b |
jp->key = jp->value = 0;
|
|
Packit Service |
ff689b |
jp->keylen = jp->valuelen = 0;
|
|
Packit Service |
ff689b |
nspace = jp->nspace = 0;
|
|
Packit Service |
ff689b |
|
|
Packit Service |
ff689b |
if (jp->state == JP_END)
|
|
Packit Service |
ff689b |
return JP_END;
|
|
Packit Service |
ff689b |
if (jp->state == JP_START)
|
|
Packit Service |
ff689b |
jp->state = JP_END;
|
|
Packit Service |
ff689b |
type = parsevalue(jp);
|
|
Packit Service |
ff689b |
if (type <= 0)
|
|
Packit Service |
ff689b |
return JP_ERROR;
|
|
Packit Service |
ff689b |
if (type == JP_OBJECT_END || type == JP_ARRAY_END)
|
|
Packit Service |
ff689b |
{
|
|
Packit Service |
ff689b |
if (jp->state != type - 1)
|
|
Packit Service |
ff689b |
return JP_ERROR;
|
|
Packit Service |
ff689b |
jp->state = queue_pop(&jp->stateq);
|
|
Packit Service |
ff689b |
}
|
|
Packit Service |
ff689b |
else if (jp->state == JP_OBJECT)
|
|
Packit Service |
ff689b |
{
|
|
Packit Service |
ff689b |
nspace = jp->nspace;
|
|
Packit Service |
ff689b |
if (type != JP_STRING)
|
|
Packit Service |
ff689b |
return JP_ERROR;
|
|
Packit Service |
ff689b |
if (skipspace(jp) != ':')
|
|
Packit Service |
ff689b |
return JP_ERROR;
|
|
Packit Service |
ff689b |
type = parsevalue(jp);
|
|
Packit Service |
ff689b |
if (type == JP_OBJECT_END || type == JP_ARRAY_END)
|
|
Packit Service |
ff689b |
return JP_ERROR;
|
|
Packit Service |
ff689b |
jp->key = jp->space;
|
|
Packit Service |
ff689b |
jp->keylen = nspace - 1;
|
|
Packit Service |
ff689b |
}
|
|
Packit Service |
ff689b |
if (type == JP_STRING || type == JP_NUMBER || type == JP_BOOL || type == JP_NULL)
|
|
Packit Service |
ff689b |
{
|
|
Packit Service |
ff689b |
jp->value = jp->space + nspace;
|
|
Packit Service |
ff689b |
jp->valuelen = jp->nspace - nspace - 1;
|
|
Packit Service |
ff689b |
}
|
|
Packit Service |
ff689b |
if (type == JP_OBJECT || type == JP_ARRAY)
|
|
Packit Service |
ff689b |
{
|
|
Packit Service |
ff689b |
queue_push(&jp->stateq, jp->state);
|
|
Packit Service |
ff689b |
jp->state = type;
|
|
Packit Service |
ff689b |
}
|
|
Packit Service |
ff689b |
else if (jp->state == JP_OBJECT || jp->state == JP_ARRAY)
|
|
Packit Service |
ff689b |
{
|
|
Packit Service |
ff689b |
int c = skipspace(jp);
|
|
Packit Service |
ff689b |
if (c == (jp->state == JP_OBJECT ? '}' : ']'))
|
|
Packit Service |
ff689b |
jp->nextc = c;
|
|
Packit Service |
ff689b |
else if (c != ',')
|
|
Packit Service |
ff689b |
return JP_ERROR;
|
|
Packit Service |
ff689b |
}
|
|
Packit Service |
ff689b |
return type;
|
|
Packit Service |
ff689b |
}
|
|
Packit Service |
ff689b |
|
|
Packit Service |
ff689b |
int
|
|
Packit Service |
ff689b |
jsonparser_skip(struct solv_jsonparser *jp, int type)
|
|
Packit Service |
ff689b |
{
|
|
Packit Service |
ff689b |
if (type == JP_ARRAY || type == JP_OBJECT)
|
|
Packit Service |
ff689b |
{
|
|
Packit Service |
ff689b |
int depth = jp->depth + 1, endtype = type + 1;
|
|
Packit Service |
ff689b |
while (type > 0 && (type != endtype || jp->depth != depth))
|
|
Packit Service |
ff689b |
type = jsonparser_parse(jp);
|
|
Packit Service |
ff689b |
}
|
|
Packit Service |
ff689b |
return type;
|
|
Packit Service |
ff689b |
}
|
|
Packit Service |
ff689b |
|