blob: 5ca75c96620bb3f8984a6f0a6a1d1daeba8b8836 [file] [log] [blame] [raw]
/*- * Copyright (c) 2016 Adam Starak <starak.adam@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#define NEED_SOLARIS_BOOLEAN
#include <sys/types.h>
#include <libnvpair.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define DNDEBUG 1
#define CHECK(name, expect, effect) do { \
if (name == expect) \
effect; \
} while (0)
#define CHECK_INEQUAL(name, expect, effect) do { \
if (name != expect) \
effect; \
} while (0)
#define PERROR(text, pos) do { \
fprintf(stderr, "%s (%zu)\n", text, pos); \
} while (0)
#define DEBUG(func, text) do { \
if (DNDEBUG) \
fprintf(stderr, "[%s] %s\n", func, text); \
} while (0)
#define DEBUG_INSERT(key, type) do { \
if (DNDEBUG) \
fprintf(stderr, "Dodalem {\"%s\" : %s}\n", key, type); \
} while (0)
struct stack
{
char **key;
unsigned *level;
};
struct machine
{
int fd; // provided file_descriptor
const char *data_source;
size_t position; // letters read
char buffer; // last letter read
char *key; // key recognised by machine
int type; // NV_TYPE
unsigned level; // nest level
bool nv_array; // true if json is an array of nvlists
struct stack stack; // nvlist array name stack
size_t stack_size; // stack max size
size_t stack_position; // stack position
};
struct child_nvlist {
nvlist_t *list;
struct child_nvlist *parent;
char *name;
nvlist_t **in_array;
unsigned int in_array_count;
};
static int
nextc(struct machine *machine)
{
if(machine->fd == -1) {
machine->buffer = machine->data_source[machine->position];
if(!machine->buffer) return 0;
machine->position++;
return 1;
} else {
int s = read(machine->fd, &machine->buffer, 1);
if (s == -1) {
PERROR("Cannot read next byte", machine->position + 1);
} else if(s) {
machine->position++;
}
return s;
}
}
static bool
verifyc(struct machine *machine, char expected)
{
int value;
value = nextc(machine);
CHECK(value, -1, return false);
if (value == 0) {
PERROR("Expected character at", machine->position);
return false;
}
CHECK(machine->buffer, expected, return true);
PERROR("Expected other character at", machine->position);
return false;
}
static bool
verify_text(struct machine *machine, const char *text)
{
unsigned i;
for (i = 0; i < strlen(text); i++)
CHECK(verifyc(machine, text[i]), false, return false);
return true;
}
static int
end(struct machine *machine)
{
int value;
value = nextc(machine);
CHECK(value, -1, return -1);
return value ? 0 : 1;
}
static int
clear_white(struct machine *machine)
{
int value;
while(isspace(machine->buffer)) {
value = nextc(machine);
CHECK(value, -1, return -1);
CHECK(value, 0, return 0);
}
return 1;
}
static void
clear_machine(struct machine *machine)
{
unsigned i;
if (machine->stack.key != NULL) {
for (i = 0; i < machine->stack_position; i++)
if (machine->stack.key[i] != NULL)
free(machine->stack.key[i]);
free(machine->stack.key);
}
if (machine->stack.level != NULL)
free(machine->stack.level);
if (machine->key != NULL)
free(machine->key);
}
static bool
add_nvlist_array(struct machine *machine, struct child_nvlist **nvl)
{
nvlist_t **child;
fprintf(stderr, "function: add_nvlist_array(%p, %p)\n", machine, nvl);
if (machine->stack_position == machine->stack_size) {
machine->stack_size *= 2;
machine->stack.level =
realloc(machine->stack.level, sizeof(machine->stack)*machine->stack_size);
CHECK(machine->stack.level, NULL, return false);
machine->stack.key =
realloc(machine->stack.key, sizeof(machine->stack)*machine->stack_size);
CHECK(machine->stack.key, NULL, return false);
}
machine->stack.key[machine->stack_position] = strdup(machine->key);
CHECK(machine->stack.key[machine->stack_position], NULL, return false);
machine->stack.level[machine->stack_position] = machine->level;
machine->stack_position++;
child = malloc(sizeof(*child));
if (child == NULL) {
free(child);
return false;
}
int e = nvlist_alloc(child, NV_UNIQUE_NAME, 0);
if (e) {
free(child);
return false;
}
dump_nvlist((*nvl)->list, 1);
putchar('\n');
fprintf(stderr, "add_nvlist_array: calling nvlist_add_nvlist_array(%p, %p<%s>, %p, 1)\n", (*nvl)->list, machine->key, machine->key, child);
//nvlist_move_nvlist_array(*nvl, machine->key, child, 1);
e = nvlist_add_nvlist_array((*nvl)->list, machine->key, child, 1);
fprintf(stderr, "add_nvlist_array: e = %d\n", e);
dump_nvlist((*nvl)->list, 1);
putchar('\n');
struct child_nvlist *cnvl = malloc(sizeof(struct child_nvlist));
CHECK(cnvl, NULL, return false);
cnvl->list = child[0];
cnvl->parent = *nvl;
cnvl->name = strdup(machine->key);
cnvl->in_array = child;
cnvl->in_array_count = 1;
*nvl = cnvl;
machine->level++;
DEBUG("add_nvlist_array", "dodaje nvliste do arraya");
return true;
}
static int
get_array(struct machine *machine, struct child_nvlist **nvl)
{
switch(machine->buffer) {
case 'f':
machine->type = DATA_TYPE_BOOLEAN_ARRAY;
break;
case 't':
machine->type = DATA_TYPE_BOOLEAN_ARRAY;
break;
case '\"':
machine->type = DATA_TYPE_STRING_ARRAY;
break;
case '{':
if (machine->level == 0)
machine->level++;
CHECK(nextc(machine), -1, return -1);
CHECK(clear_white(machine), -1, return -1);
machine->type = DATA_TYPE_NVLIST_ARRAY;
add_nvlist_array(machine, nvl);
break;
default:
if (isdigit(machine->buffer)) {
machine->type = DATA_TYPE_INT64_ARRAY;
} else {
PERROR("Error checking array type", machine->position);
machine->type = -1;
}
break;
}
return machine->type;
}
static int
get_type(struct machine *machine, struct child_nvlist **nvl)
{
DEBUG("get_type", "sprawdzam typ");
switch(machine->buffer) {
case 'n':
machine->type = DATA_TYPE_UNKNOWN;
break;
case 'f':
machine->type = DATA_TYPE_BOOLEAN;
break;
case 't':
machine->type = DATA_TYPE_BOOLEAN;
break;
case '\"':
machine->type = DATA_TYPE_STRING;
break;
case '{':
CHECK(nextc(machine), -1, return -1);
CHECK(clear_white(machine), -1, return -1);
machine->type = DATA_TYPE_NVLIST;
machine->level++;
break;
case '[':
CHECK(nextc(machine), -1, return -1);
CHECK(clear_white(machine), -1, return -1);
CHECK(get_array(machine, nvl), -1, return -1);
break;
default:
if (isdigit(machine->buffer)) {
//machine->type = DATA_TYPE_INT64;
machine->type = DATA_TYPE_DOUBLE;
} else if (end(machine)) {
machine->type = DATA_TYPE_UNKNOWN;
} else {
PERROR("Error checking array type", machine->position);
return -1;
}
break;
}
//DEBUG("get_type", nvpair_type_string(machine->type));
return machine->type;
}
static bool
insert_null(struct machine *machine, nvlist_t *nvl)
{
CHECK(verify_text(machine, "ull"), false, return false);
//nvlist_add_null(nvl, machine->key);
DEBUG_INSERT(machine->key, "null");
return nextc(machine) > -1 ? true : false;
}
static int
fetch_bool(struct machine *machine)
{
if (machine->buffer == 'f') {
CHECK(verify_text(machine, "alse"), false, return -1);
CHECK(nextc(machine), -1, return -1);
return false;
}
else {
CHECK(verify_text(machine, "rue"), false, return -1);
CHECK(nextc(machine), -1, return -1);
return true;
}
}
static bool
insert_bool(struct machine *machine, nvlist_t *nvl)
{
int value;
value = fetch_bool(machine);
CHECK(value, -1, return 0);
nvlist_add_boolean_value(nvl, machine->key, value);
if (DNDEBUG && value) {
DEBUG_INSERT(machine->key, "true");
}
else if (DNDEBUG)
DEBUG_INSERT(machine->key, "false");
return true;
}
static char
insert_special(struct machine *machine)
{
if (nextc(machine) != 1) {
PERROR("Expected other character at", machine->position);
return (-1);
}
switch(machine->buffer) {
case '\"':
return ('\"');
case '\\':
return ('\\');
case 'b':
return ('\b');
case 'f':
return ('\f');
case 'n':
return ('\n');
case 'r':
return ('\r');
case 't':
return ('\t');
default:
PERROR("Expected other character at", machine->position);
}
return (-1);
}
static char *
fetch_string(struct machine *machine)
{
char *value;
size_t size;
unsigned i;
i = 0;
size = 100;
value = malloc(sizeof(*value)*size);
CHECK(value, NULL, goto failed);
CHECK_INEQUAL(nextc(machine), 1, goto failed);
while (machine->buffer != '\"') {
value[i] = machine->buffer;
if (value[i] == '\\') {
value[i] = insert_special(machine);
CHECK(value[i], -1, goto failed);
}
i++;
if (i+1 == size) {
value = realloc(&value, sizeof(*value)*size*2);
CHECK(value, NULL, goto failed);
size *= 2;
}
CHECK_INEQUAL(nextc(machine), 1, goto failed);
}
value[i] = '\0';
value = realloc(value, sizeof(*value)*(strlen(value)+1));
CHECK(value, NULL, goto failed);
CHECK(nextc(machine), -1, goto failed);
return (value);
failed:
PERROR("Error parsing string", machine->position);
if (value != NULL)
free(value);
return NULL;
}
static bool
insert_string(struct machine *machine, nvlist_t *nvl)
{
char *value;
fprintf(stderr, "function: insert_string(%p, %p)\n", machine, nvl);
value = fetch_string(machine);
CHECK(value, NULL, return false);
fprintf(stderr, "insert_string: calling nvlist_add_string(%p, %p<%s>, %p<%s>)\n", nvl, machine->key, machine->key, value, value);
nvlist_add_string(nvl, machine->key, value);
dump_nvlist(nvl, 1);
putchar('\n');
DEBUG_INSERT(machine->key, "string");
return true;
}
static bool
overflow(uint64_t value, char digit)
{
uint64_t max = ULLONG_MAX/10 - digit + '0';
return value <= max ? false : true;
}
static uint64_t
fetch_number(struct machine *machine, uint64_t *result)
{
uint64_t value;
value = 0;
if (machine->buffer == '0') {
CHECK(nextc(machine), -1, return false);
*result = 0;
return true;
}
value = machine->buffer - '0';
CHECK(nextc(machine), -1, return false);
while (isdigit(machine->buffer)) {
if (overflow(value, machine->buffer)) {
PERROR("Overflow at", machine->position);
return false;
}
value = value * 10 + machine->buffer - '0';
CHECK(nextc(machine), -1, return false);
}
*result = value;
return true;
}
static bool
insert_number(struct machine *machine, nvlist_t *nvl)
{
uint64_t value;
value = 0;
if (!fetch_number(machine, &value)) {
PERROR("Error fetching number", machine->position);
return false;
}
nvlist_add_uint64(nvl, machine->key, value);
DEBUG_INSERT(machine->key, "number");
return true;
}
static bool
insert_bool_array(struct machine *machine, struct child_nvlist *nvl)
{
boolean_t *value;
int tmp;
size_t size;
unsigned i;
i = 0;
size = 20;
value = malloc(sizeof(*value)*size);
CHECK(value, NULL, goto failed);
machine->type = DATA_TYPE_BOOLEAN;
while (machine->buffer != ']') {
if (machine->type != DATA_TYPE_BOOLEAN) {
PERROR("Wrong type in array, expected bool at", machine->position);
goto failed;
}
tmp = fetch_bool(machine);
CHECK(tmp, -1, goto failed);
value[i] = tmp;
i++;
if (i == size) {
value = realloc(value, sizeof(*value)*size*2);
CHECK(value, NULL, goto failed);
size *= 2;
}
CHECK(clear_white(machine), -1, goto failed);
if (machine->buffer == ',') {
CHECK(nextc(machine), -1, goto failed);
CHECK(clear_white(machine), -1, goto failed);
CHECK(get_type(machine, &nvl), -1, goto failed);
}
else if (machine->buffer != ']') {
PERROR("Wrong char at", machine->position);
}
}
value = realloc(value, sizeof(*value)*i);
CHECK(value, NULL, goto failed);
nvlist_add_boolean_array(nvl->list, machine->key, value, i);
DEBUG_INSERT(machine->key, "bool_array");
machine->type = DATA_TYPE_BOOLEAN_ARRAY;
CHECK(nextc(machine), -1, goto failed);
return true;
failed:
if (value != NULL)
free(value);
return false;
}
static bool
insert_string_array(struct machine *machine, struct child_nvlist *nvl)
{
char **value;
size_t size;
unsigned i, j;
i = 0;
size = 20;
value = malloc(sizeof(*value)*size);
CHECK(value, NULL, goto failed);
machine->type = DATA_TYPE_STRING;
while (machine->buffer != ']') {
if (machine->type != DATA_TYPE_STRING) {
PERROR("Wrong type in array, expected bool at", machine->position);
goto failed;
}
value[i] = fetch_string(machine);
CHECK(value[i], NULL, goto failed);
CHECK(clear_white(machine), -1, goto failed);
i++;
if (i == size) {
value = realloc(value, sizeof(*value)*size*2);
CHECK(value, NULL, goto failed);
size *= 2;
}
if (machine->buffer == ',') {
CHECK(nextc(machine), -1, goto failed);
CHECK(clear_white(machine), -1, goto failed);
CHECK(get_type(machine, &nvl), -1, goto failed);
}
else if (machine->buffer != ']') {
PERROR("Wrong character at", machine->position);
goto failed;
}
}
value = realloc(value, sizeof(value)*i);
CHECK(value, NULL, goto failed);
nvlist_add_string_array(nvl->list, machine->key, value, i);
DEBUG_INSERT(machine->key, "string_array");
CHECK(nextc(machine), -1, goto failed);
machine->type = DATA_TYPE_STRING_ARRAY;
return true;
failed:
j = 0;
if (value != NULL) {
for (j = 0; j <= i; j++)
if (value[j] != NULL)
free(value[j]);
free(value);
}
return false;
}
static bool
insert_number_array(struct machine *machine, struct child_nvlist *nvl)
{
uint64_t *value;
size_t size;
unsigned i;
i = 0;
size = 20;
value = malloc(sizeof(*value)*size);
CHECK(value, NULL, goto failed);
machine->type = DATA_TYPE_UINT64;
while (machine->buffer != ']') {
switch(machine->type) {
case DATA_TYPE_BYTE:
case DATA_TYPE_INT16:
case DATA_TYPE_UINT16:
case DATA_TYPE_INT32:
case DATA_TYPE_UINT32:
case DATA_TYPE_INT64:
case DATA_TYPE_UINT64:
case DATA_TYPE_DOUBLE:
break;
default:
PERROR("Wrong type in array, expected number at", machine->position);
goto failed;
}
CHECK(fetch_number(machine, &value[i]), false, goto failed);
CHECK(clear_white(machine), -1, goto failed);
i++;
if (i == size) {
value = realloc(value, sizeof(*value)*size*2);
CHECK(value, NULL, goto failed);
size *= 2;
}
if (machine->buffer == ',') {
CHECK(nextc(machine), -1, goto failed);
CHECK(clear_white(machine), -1, goto failed);
CHECK(get_type(machine, &nvl), -1, goto failed);
}
else if (machine->buffer != ']') {
PERROR("Wrong character at", machine->position);
goto failed;
}
}
value = realloc(value, sizeof(*value)*i);
CHECK(value, NULL, goto failed);
nvlist_add_uint64_array(nvl->list, machine->key, value, i);
machine->type = DATA_TYPE_INT64_ARRAY;
CHECK(nextc(machine), -1, goto failed);
DEBUG_INSERT(machine->key, "number_array");
return true;
failed:
if (value != NULL)
free(value);
return false;
}
static bool
insert_nvlist(struct machine *machine, struct child_nvlist **nvl)
{
nvlist_t *child;
CHECK_INEQUAL(nvlist_alloc(&child, NV_UNIQUE_NAME, 0), 0, return false);
nvlist_add_nvlist((*nvl)->list, machine->key, child);
dump_nvlist((*nvl)->list, 1);
putchar('\n');
struct child_nvlist *cnvl = malloc(sizeof(struct child_nvlist));
CHECK(cnvl, NULL, return false);
cnvl->list = child;
cnvl->parent = *nvl;
cnvl->name = strdup(machine->key);
cnvl->in_array = NULL;
cnvl->in_array_count = 0;
*nvl = cnvl;
DEBUG_INSERT(machine->key, "nvlist");
return true;
}
static bool
insert_type(struct machine *machine, struct child_nvlist **nvl)
{
switch(machine->type) {
case DATA_TYPE_UNKNOWN:
return insert_null(machine, (*nvl)->list) ? true : false;
break;
case DATA_TYPE_BOOLEAN:
return insert_bool(machine, (*nvl)->list) ? true : false;
break;
case DATA_TYPE_STRING:
return insert_string(machine, (*nvl)->list) ? true : false;
break;
case DATA_TYPE_BYTE:
case DATA_TYPE_INT16:
case DATA_TYPE_UINT16:
case DATA_TYPE_INT32:
case DATA_TYPE_UINT32:
case DATA_TYPE_INT64:
case DATA_TYPE_UINT64:
case DATA_TYPE_DOUBLE:
return insert_number(machine, (*nvl)->list) ? true : false;
break;
case DATA_TYPE_BOOLEAN_ARRAY:
return insert_bool_array(machine, *nvl) ? true : false;
break;
case DATA_TYPE_STRING_ARRAY:
return insert_string_array(machine, *nvl) ? true : false;
break;
case DATA_TYPE_INT64_ARRAY:
return insert_number_array(machine, *nvl) ? true : false;
break;
case DATA_TYPE_NVLIST:
return insert_nvlist(machine, nvl) ? true : false;
break;
}
return true;
}
static bool
get_key(struct machine *machine)
{
if (machine->key != NULL)
free(machine->key);
machine->key = fetch_string(machine);
if (machine->key == NULL)
return false;
return true;
}
static bool
update_nvlist_array(struct machine *machine, struct child_nvlist **nvl, struct child_nvlist *cnvl)
{
nvlist_t **children;
unsigned int nitems, i;
char *key;
fprintf(stderr, "function: update_nvlist_array(%p, %p)\n", machine, nvl);
if (!machine->stack_position ||
machine->level != machine->stack.level[machine->stack_position-1])
return true;
key = machine->stack.key[machine->stack_position-1];
#if 0
//children = nvlist_take_nvlist_array(*nvl, key, &nitems);
dump_nvlist((*nvl)->list, 1);
putchar('\n');
fprintf(stderr, "update_nvlist_array: calling nvlist_lookup_nvlist_array(%p, %p<%s>, %p, %p)\n", (*nvl)->list, key, key, &children, &nitems);
int e = nvlist_lookup_nvlist_array((*nvl)->list, key, &children, &nitems);
if(e) return false;
//fprintf(stderr, "update_nvlist_array: calling nvlist_remove\n");
// Calling nvlist_remove(3) will invalidate its data
//nvlist_remove((*nvl)->list, key, DATA_TYPE_NVLIST_ARRAY);
//dump_nvlist((*nvl)->list, 1);
//putchar('\n');
fputs("checking children[0]\n", stderr);
dump_nvlist(children[0], 1);
putchar('\n');
#else
children = cnvl->in_array;
nitems = cnvl->in_array_count;
if(!children || !nitems) return false;
#endif
children = realloc(children, sizeof(*children)*(nitems+1));
CHECK(children, NULL, goto failed);
CHECK_INEQUAL(nvlist_alloc(children + nitems, NV_UNIQUE_NAME, 0), 0, goto failed);
fprintf(stderr, "update_nvlist_array: nitems = %u\n", nitems);
//nvlist_move_nvlist_array(*nvl, key, children, nitems+1);
fprintf(stderr, "update_nvlist_array: calling nvlist_add_nvlist_array(%p, %p<%s>, %p, %u)\n", (*nvl)->list, key, key, children, nitems+1);
cnvl->in_array = children;
cnvl->in_array_count++;
int e = nvlist_add_nvlist_array((*nvl)->list, key, children, nitems+1);
fprintf(stderr, "update_nvlist_array: e = %d\n", e);
if(e) return false;
dump_nvlist((*nvl)->list, 1);
putchar('\n');
cnvl = malloc(sizeof(struct child_nvlist));
CHECK(cnvl, NULL, goto failed);
cnvl->list = children[nitems];
cnvl->parent = *nvl;
cnvl->name = strdup(key);
cnvl->in_array = children;
cnvl->in_array_count = nitems + 1;
*nvl = cnvl;
fprintf(stderr, "update_nvlist_array: cnvl->list = %p\n", cnvl->list);
dump_nvlist(children[0], 1);
putchar('\n');
CHECK_INEQUAL(nextc(machine), 1, return false);
CHECK(clear_white(machine), -1, return false);
machine->level++;
return true;
failed:
if (children != NULL) {
for (i = 0; i <= nitems; i++) {
if (children[i] != NULL)
nvlist_free(children[i]);
}
free(children);
}
return false;
}
static bool
change_level(struct machine *machine, struct child_nvlist **nvl)
{
machine->level--;
if (machine->level > 0) {
//*nvl = nvpair_nvlist(nvlist_get_nvpair_parent(*nvl));
struct child_nvlist *cnvl = *nvl;
*nvl = (*nvl)->parent;
if(cnvl->in_array) {
nvlist_add_nvlist_array((*nvl)->list, cnvl->name, cnvl->in_array, cnvl->in_array_count);
} else {
nvlist_add_nvlist((*nvl)->list, cnvl->name, cnvl->list);
}
CHECK_INEQUAL(nextc(machine), 1, return false);
CHECK(clear_white(machine), -1, return false);
if (machine->buffer == ']' && machine->stack_position) {
free(machine->stack.key[machine->stack_position-1]);
machine->stack_position--;
if (machine->nv_array && !machine->stack_position) {
machine->level--;
return false;
}
CHECK_INEQUAL(nextc(machine), 1, return false);
CHECK(clear_white(machine), 0, return false);
}
if ( machine->buffer != ',' && machine->buffer != '}') {
PERROR("Expected ',' or '}' at", machine->position);
return false;
}
else if (machine->buffer == ',') {
CHECK_INEQUAL(nextc(machine), 1, return false);
CHECK(clear_white(machine), -1, return false);
CHECK(update_nvlist_array(machine, nvl, cnvl), false, return false);
}
free(cnvl->name);
free(cnvl);
}
return true;
}
static bool
parse_nvlist(struct machine *machine, struct child_nvlist **nvl)
{
while (machine->level != 0) {
while (machine->buffer != '}') {
if (machine->buffer != '\"') {
PERROR("Error parsing key at", machine->position);
goto failed;
}
CHECK(get_key(machine), false, goto failed);
CHECK(clear_white(machine), -1, goto failed);
if (machine->buffer != ':') {
PERROR("Expected ':' at", machine->position);
}
CHECK_INEQUAL(nextc(machine), 1, goto failed);
CHECK(clear_white(machine), -1, goto failed);
CHECK(get_type(machine, nvl), -1, goto failed);
CHECK(insert_type(machine, nvl), false, goto failed);
if (machine->type == DATA_TYPE_NVLIST || machine->type == DATA_TYPE_NVLIST_ARRAY) {
continue;
}
CHECK(clear_white(machine), -1, goto failed);
if (machine->buffer != ',' && machine->buffer != '}') {
PERROR("Expected ',' or '}' at", machine->position);
goto failed;
}
if (machine->buffer != '}') {
CHECK_INEQUAL(nextc(machine), 1, goto failed);
CHECK(clear_white(machine), -1, goto failed);
}
}
change_level(machine, nvl);
}
CHECK(nextc(machine), -1, goto failed);
return true;
failed:
while((*nvl)->parent) *nvl = (*nvl)->parent;
return false;
}
static nvlist_t *json_to_nvlist(struct machine *machine) {
int tmp;
nvlist_t *nvl;
CHECK_INEQUAL(nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0), 0, goto failed);
machine->position = 0;
machine->level = 0;
machine->key = strdup("");
machine->nv_array = false;
machine->stack.key = malloc(sizeof(*machine->stack.level)*20);
machine->stack_size = 20;
CHECK(machine->stack.key, NULL, goto failed);
machine->stack.level = malloc(sizeof(*machine->stack.level)*20);
machine->stack_position = 0;
CHECK(machine->stack.level, NULL, goto failed);
struct child_nvlist *cnvl = malloc(sizeof(struct child_nvlist));
cnvl->list = nvl;
cnvl->parent = NULL;
cnvl->name = NULL;
cnvl->in_array = NULL;
cnvl->in_array_count = 0;
CHECK(nextc(machine), -1, goto failed);
tmp = clear_white(machine);
if (tmp == -1)
goto failed;
else if (tmp == 0) {
clear_machine(machine);
return nvl;
}
CHECK(get_type(machine, &cnvl), -1, goto failed);
switch(machine->type) {
case DATA_TYPE_UNKNOWN:
clear_machine(machine);
return nvl;
break;
case DATA_TYPE_NVLIST:
CHECK(parse_nvlist(machine, &cnvl), false, goto failed);
break;
case DATA_TYPE_NVLIST_ARRAY:
machine->nv_array = true;
parse_nvlist(machine, &cnvl);
break;
default:
CHECK(insert_type(machine, &cnvl), false, goto failed);
break;
}
clear_white(machine);
if (!end(machine)) {
PERROR("ERROR at the end of parsing", machine->position);
goto failed;
}
clear_machine(machine);
return nvl;
failed:
if (nvl != NULL)
nvlist_free(nvl);
clear_machine(machine);
errno = EINVAL;
return NULL;
}
nvlist_t *
nvlist_create_from_json_fd(int fd)
{
struct machine machine;
machine.fd = fd;
machine.data_source = NULL;
return json_to_nvlist(&machine);
}
nvlist_t *
nvlist_create_from_json(const char *buffer) {
struct machine machine;
machine.fd = -1;
machine.data_source = buffer;
return json_to_nvlist(&machine);
}