/* This file is part of gjson
   Copyright (C) 2020,2025 Sergey Poznyakoff

   Gjson is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3, or (at your option)
   any later version.

   Gjson is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with gjson.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#if ENABLE_NLS
# include "gettext.h"
#else
# ifndef gettext
#  define gettext(msgid) msgid
# endif
#endif
#ifndef _
# define _(msgid) gettext(msgid)
#endif
#ifndef N_
# define N_(s) s
#endif
#include "json.h"

enum json_num_type json_num_type = JSON_NUM_DOUBLE;

void *
json_2nrealloc(void *p, size_t *pn, size_t s)
{
	size_t n = *pn;
	char *newp;

	if (!p) {
		if (!n) {
			/* The approximate size to use for initial small
			   allocation requests, when the invoking code
			   specifies an old size of zero.  64 bytes is
			   the largest "small" request for the
			   GNU C library malloc.  */
			enum { DEFAULT_MXFAST = 64 };

			n = DEFAULT_MXFAST / s;
			n += !n;
		}
	} else {
		/* Set N = ceil (1.5 * N) so that progress is made if N == 1.
		   Check for overflow, so that N * S stays in size_t range.
		   The check is slightly conservative, but an exact check isn't
		   worth the trouble.  */
		if ((size_t) -1 / 3 * 2 / s <= n) {
			errno = ENOMEM;
			return NULL;
		}
		n += (n + 1) / 2;
	}

	newp = realloc(p, n * s);
	if (!newp)
		return NULL;
	*pn = n;
	return newp;
}

static int
strexpand(char **pstr, size_t *pn, size_t len, size_t incr)
{
	while (len + incr > *pn) {
		char *p = json_2nrealloc(*pstr, pn, 1);
		if (!p)
			return -1;
		*pstr = p;
	}
	return 0;
}

char const *
json_strerror(int ec)
{
	static char *json_errstr[] = {
		[JSON_E_NOERR]     = N_("No error"),
		[JSON_E_NOMEM]     = N_("Not enough memory"),
		[JSON_E_BADTOK]    = N_("Unrecognized token"),
		[JSON_E_BADDELIM]  = N_("Bad delimiter"),
		[JSON_E_BADSTRING] = N_("Malformed string"),
		[JSON_E_RANGE]     = N_("Value out of range"),
		[JSON_E_EOF]       = N_("Unexpected end of input"),
		[JSON_E_USAGE]     = N_("Usage error")
	};
	ec &= ~JSON_E_MOREINPUT;
	if (ec < 0 || ec >= sizeof(json_errstr) / sizeof(json_errstr[0]))
		return gettext("Unknown error");
	return gettext(json_errstr[ec]);
}

static inline void
json_format_write(struct json_format *fmt, char const *str, size_t len)
{
	fmt->write(fmt->data, str, len);
}

static inline void
json_format_writez(struct json_format *fmt, char const *str)
{
	json_format_write(fmt, str, strlen(str));
}

static inline void
json_format_writec(struct json_format *fmt, char c)
{
	json_format_write(fmt, &c, 1);
}

static void
json_format_indent(struct json_format *fmt, size_t level)
{
	level *= fmt->indent;
	while (level--)
		json_format_writec(fmt, ' ');
}

static void
json_format_delim(struct json_format *fmt, size_t level)
{
	json_format_writec(fmt, ',');
	if (fmt->indent) {
		json_format_writec(fmt, '\n');
		json_format_indent(fmt, level);
	} else
		json_format_writec(fmt, ' ');
}

/* General-purpose */
struct json_value *
json_value_create(int type)
{
	struct json_value *obj = calloc(1, sizeof(*obj));
	if (obj)
		obj->type = type;
	return obj;
}

typedef void (*json_format_fun)(struct json_format *, struct json_value *, size_t);
typedef void (*json_free_fun)(struct json_value *);
typedef int (*json_copy_fun)(struct json_value *, struct json_value **);

static int json_copy_generic(struct json_value *, struct json_value **);
static int json_copy_string(struct json_value *, struct json_value **);
static int json_copy_array(struct json_value *, struct json_value **);
static int json_copy_object(struct json_value *, struct json_value **);

static void json_format_null(struct json_format *, struct json_value *,
			     size_t level);
static void json_format_bool(struct json_format *, struct json_value *,
			     size_t level);
static void json_format_double(struct json_format *, struct json_value *,
			       size_t level);
static void json_format_long(struct json_format *, struct json_value *,
			     size_t level);
static void json_format_string(struct json_format *, struct json_value *,
			       size_t level);
static void json_format_array(struct json_format *, struct json_value *,
			       size_t level);
static void json_format_object(struct json_format *, struct json_value *,
			       size_t level);


static void json_free_string(struct json_value *);
static void json_free_array(struct json_value *);
static void json_free_object(struct json_value *);

static struct json_value_meth {
	json_format_fun format_fun;
	json_free_fun free_fun;
	json_copy_fun copy_fun;
} json_value_meth[] = {
	[json_null]   = { json_format_null, NULL, json_copy_generic },
	[json_bool]   = { json_format_bool, NULL, json_copy_generic },
	[json_double] = { json_format_double, NULL, json_copy_generic },
	[json_long]   = { json_format_long, NULL, json_copy_generic },
	[json_string] = { json_format_string, json_free_string, json_copy_string },
	[json_array]  = { json_format_array, json_free_array, json_copy_array },
	[json_object] = { json_format_object, json_free_object, json_copy_object }
};

static inline int
is_valid_type(int type)
{
	return type >= 0
		&& type < sizeof(json_value_meth) / sizeof(json_value_meth[0]);
}

static inline int
is_valid_value(struct json_value *obj)
{
	return obj && is_valid_type(obj->type);
}

void
json_value_free(struct json_value *obj)
{
	if (is_valid_value(obj)) {
		if (json_value_meth[obj->type].free_fun)
			json_value_meth[obj->type].free_fun(obj);
		free(obj);
	}
}

int
json_value_copy(struct json_value *val, struct json_value **new_val)
{
	if (is_valid_value(val))
		return json_value_meth[val->type].copy_fun(val, new_val);
	else {
		errno = EINVAL;
		return -1;
	}
}

int
json_value_format(struct json_value *obj, struct json_format *fmt, size_t level)
{
	++level;
	if (!obj) {
		json_format_null(fmt, obj, level);
		return 0;
	}
	if (is_valid_value(obj)) {
		json_value_meth[obj->type].format_fun(fmt, obj, level);
		return 0;
	}
	errno = EINVAL;
	return -1;
}

static int
json_copy_generic(struct json_value *val, struct json_value **ret_val)
{
	struct json_value *p;

	if (!val) {
		p = json_new_null();
		if (!p)
			return -1;
	} else if (is_valid_type(val->type)) {
		p = malloc(sizeof(*p));
		if (p)
			memcpy(p, val, sizeof(*p));
	} else {
		errno = EINVAL;
	}

	if (!p)
		return -1;

	*ret_val = p;
	return 0;
}

/* Null value */
struct json_value *
json_new_null(void)
{
	return json_value_create(json_null);
}

static void
json_format_null(struct json_format *fmt, struct json_value *val, size_t level)
{
	json_format_writez(fmt, "null");
}

/* Bool value */
struct json_value *
json_new_bool(int b)
{
	struct json_value *j = json_value_create(json_bool);
	if (j)
		j->b = b;
	return j;
}

static void
json_format_bool(struct json_format *fmt, struct json_value *val, size_t level)
{
	json_format_writez(fmt, val->b ? "true" : "false");
}

/* Double-precision numeric value */
struct json_value *
json_new_double(double n)
{
	struct json_value *j = json_value_create(json_double);
	if (j)
		j->n = n;
	return j;
}

static void
json_format_double(struct json_format *fmt, struct json_value *val, size_t level)
{
	char buffer[128];//FIXME
	if (fmt->precision == -1)
		snprintf(buffer, sizeof buffer, "%e", val->n);
	else
		snprintf(buffer, sizeof buffer, "%.*f", fmt->precision,
			 val->n);
	json_format_writez(fmt, buffer);
}

/* Long integer value */
struct json_value *
json_new_long(long n)
{
	struct json_value *j = json_value_create(json_long);
	if (j)
		j->i = n;
	return j;
}

static void
json_format_long(struct json_format *fmt, struct json_value *val, size_t level)
{
	char buffer[128];//FIXME
	snprintf(buffer, sizeof buffer, "%ld", val->i);
	json_format_writez(fmt, buffer);
}

/* String value */
struct json_value *
json_new_string(char const *str)
{
	struct json_value *j = json_value_create(json_string);
	if (j) {
		j->s = strdup(str);
		if (!j->s) {
			free(j);
			j = NULL;
		}
	}
	return j;
}

static void
json_free_string(struct json_value *val)
{
	free(val->s);
}

static int
json_copy_string(struct json_value *val, struct json_value **ret_val)
{
	struct json_value *newval = json_value_create(json_string);
	if (!newval)
		return -1;
	if ((newval->s = strdup(val->s)) == NULL) {
		free(newval);
		return -1;
	}
	*ret_val = newval;
	return 0;
}

enum { ESCAPE, UNESCAPE };

static char transtab[][9] = {
	"\\\"/\b\f\n\r\t",
	"\\\"/bfnrt"
};

static inline int
escape(char c, int un)
{
	char *p = strchr(transtab[un], c);
	return p ? transtab[!un][p-transtab[un]] : 0;
}

static void
json_format_escape(struct json_format *fmt, char const *s)
{
	json_format_writec(fmt, '"');
	while (*s) {
		int c;
		size_t n = strcspn(s, transtab[ESCAPE]);
		if (n > 0)
			json_format_write(fmt, s, n);
		s += n;
		if (*s == 0)
			break;
		if ((c = escape(*s, ESCAPE)) != 0) {
			json_format_writec(fmt, '\\');
			json_format_writec(fmt, c);
		} else {
			json_format_writec(fmt, *s);
		}
		s++;
	}
	json_format_writec(fmt, '"');
}

static void
json_format_string(struct json_format *fmt, struct json_value *val, size_t level)
{
	json_format_escape(fmt, val->s);
}

/* Array value */
struct json_value *
json_new_array(void)
{
	struct json_value *j = json_value_create(json_array);
	if (j) {
		j->a = malloc(sizeof(*j->a));
		if (j->a) {
			j->a->oc = 0;
			j->a->on = 0;
			j->a->ov = NULL;
		} else {
			free(j);
			j = NULL;
		}
	}
	return j;
}

static void
json_free_array(struct json_value *val)
{
	size_t i;

	for (i = 0; i < val->a->oc; i++)
		json_value_free(val->a->ov[i]);
	free(val->a->ov);
	free(val->a);
}

static int
json_copy_array(struct json_value *val, struct json_value **ret_val)
{
	struct json_value *newval;
	size_t i;

	newval = json_new_array();
	if (!newval)
		return -1;
	if (!(newval->a->ov = calloc(val->a->oc, sizeof(val->a->ov[0])))) {
		free(newval);
		return -1;
	}
	newval->a->oc = newval->a->on = val->a->oc;
	for (i = 0; i < val->a->oc; i++) {
		if (json_value_copy(val->a->ov[i], &newval->a->ov[i])) {
			newval->a->oc = i;
			json_value_free(newval);
			return -1;
		}
	}
	*ret_val = newval;
	return 0;
}

int
json_array_expand(struct json_value *jv)
{
	size_t i;
	size_t n = jv->a->on;
	struct json_value **p = json_2nrealloc(jv->a->ov,
					       &jv->a->on,
					       sizeof(jv->a->ov[0]));
	if (!p)
		return -1;
	jv->a->ov = p;
	for (i = jv->a->on; i < n; i++)
		jv->a->ov[i] = NULL;
	return 0;
}

int
json_array_insert(struct json_value *jv, size_t idx, struct json_value *v)
{
	if (jv->type != json_array) {
		errno = EINVAL;
		return -1;
	}

	if (jv->a->oc <= idx) {
		while (jv->a->on <= idx) {
			if (json_array_expand(jv))
				return -1;
		}
		jv->a->oc = idx + 1;
	}
	jv->a->ov[idx] = v;
	return 0;
}

int
json_array_append(struct json_value *jv, struct json_value *v)
{
	if (jv->type != json_array) {
		errno = EINVAL;
		return -1;
	}
	return json_array_insert(jv, jv->a->oc, v);
}

int
json_array_set(struct json_value *jv, size_t idx, struct json_value *v)
{
	if (jv->type != json_array) {
		errno = EINVAL;
		return -1;
	}
	if (idx >= json_array_length(jv)) {
		errno = ENOENT;
		return -1;
	}
	jv->a->ov[idx] = v;
	return 0;
}

int
json_array_get(struct json_value *jv, size_t idx, struct json_value **retval)
{
	if (jv->type != json_array) {
		errno = EINVAL;
		return -1;
	}
	if (idx >= json_array_length(jv)) {
		errno = ENOENT;
		return -1;
	}
	*retval = jv->a->ov[idx];
	return 0;
}

static void
json_format_array(struct json_format *fmt, struct json_value *obj,
		  size_t level)
{
	size_t i;

	json_format_writec(fmt, '[');
	if (obj->a->oc) {
		if (fmt->indent)
			json_format_writec(fmt, '\n');
		for (i = 0; i < obj->a->oc; i++) {
			(i ? json_format_delim
			   : json_format_indent)(fmt, level);
			json_value_format(obj->a->ov[i], fmt, level);
		}
		if (fmt->indent) {
			json_format_writec(fmt, '\n');
			json_format_indent(fmt, level-1);
		}
	}
	json_format_writec(fmt, ']');
}

/* Object value */
struct json_value *
json_new_object(void)
{
	struct json_value *jv = json_value_create(json_object);
	if (jv && !(jv->o = calloc(1, sizeof(*jv->o)))) {
		free(jv);
		return NULL;
	}
	return jv;
}

static void
json_free_object(struct json_value *val)
{
	struct json_pair *p;

	p = val->o->head;
	while (p) {
		struct json_pair *next = p->next;
		free(p->k);
		json_value_free(p->v);
		free(p);
		p = next;
	}
	free(val->o);
}

static int
json_copy_object(struct json_value *val, struct json_value **ret_val)
{
	struct json_value *newval;
	struct json_pair *p;

	if (!(newval = json_new_object()))
		return -1;
	for (p = val->o->head; p; p = p->next) {
		struct json_value *elt = NULL;

		if (json_value_copy(p->v, &elt)
		    || json_object_set(newval, p->k, elt)) {
			json_value_free(elt);
			json_value_free(newval);
			return -1;
		}
	}
	*ret_val = newval;
	return 0;
}

static int
json_object_lookup_or_install(struct json_object *obj, char const *name,
			      int install,
			      struct json_pair **retval)
{
	struct json_pair *l, *m;
	size_t i, n, count;

	if (obj->count == 0)
		l = NULL;
	else {
		l = obj->head;

		if (strcmp(l->k, name) > 0) {
			l = NULL;
		} else if (strcmp(obj->tail->k, name) < 0) {
			l = obj->tail;
		} else {
			count = obj->count;
			while (count) {
				int c;

				n = count / 2;
				for (m = l, i = 0; i < n; m = m->next, i++)
					;

				c = strcmp(m->k, name);
				if (c == 0) {
					*retval = m;
					return 0;
				} else if (n == 0) {
					break;
				} else if (c < 0) {
					l = m;
					count -= n;
				} else {
					count = n;
				}
			}
		}
	}

	if (!install) {
		errno = ENOENT;
		return -1;
	}

	m = malloc(sizeof(*m));
	if (!m)
		return -1;
	m->next = NULL;
	m->k = strdup(name);
	if (!m->k) {
		free(m);
		return -1;
	}
	m->v = NULL;

	if (!l) {
		if (obj->head == NULL)
			obj->head = obj->tail = m;
		else {
			m->next = obj->head;
			obj->head = m;
		}
	} else {
		while (l->next && strcmp(l->next->k, name) < 0)
			l = l->next;

		m->next = l->next;
		l->next = m;
		if (!m->next)
			obj->tail = m;
	}
	obj->count++;

	*retval = m;
	return 0;
}

int
json_object_set(struct json_value *obj, char const *name,
		struct json_value *val)
{
	struct json_pair *p;
	int res;

	if (obj->type != json_object) {
		errno = EINVAL;
		return -1;
	}

	res = json_object_lookup_or_install(obj->o, name, 1, &p);
	if (res)
		return res;

	json_value_free(p->v);
	p->v = val;
	return 0;
}

int
json_object_get(struct json_value *obj, char const *name,
		struct json_value **retval)
{
	struct json_pair *p;
	int res;

	if (obj->type != json_object) {
		errno = EINVAL;
		return -1;
	}

	res = json_object_lookup_or_install(obj->o, name, 0, &p);
	if (res)
		return res;
	*retval = p->v;
	return 0;
}

static void
json_format_object(struct json_format *fmt, struct json_value *obj,
		   size_t level)
{
	struct json_object *op = obj->o;
	struct json_pair *p;
	json_format_writec(fmt, '{');
	if (op->count) {
		if (fmt->indent)
			json_format_writec(fmt, '\n');
		for (p = op->head; p; p = p->next) {
			(p == op->head
			  ? json_format_indent
			  : json_format_delim)(fmt, level);
			json_format_escape(fmt, p->k);
			json_format_writec(fmt, ':');
			if (fmt->indent)
				json_format_writec(fmt, ' ');
			json_value_format(p->v, fmt, level);
		}
		if (fmt->indent) {
			json_format_writec(fmt, '\n');
			json_format_indent(fmt, level-1);
		}
	}
	json_format_writec(fmt, '}');
}

/* Removes from OBJ all pairs for which the predicate function PRED
   returns 1.
 */
int
json_object_filter(struct json_value *obj,
		   int (*pred)(char const *, struct json_value *, void *),
		   void *data)
{
	struct json_object *op;
	struct json_pair *p, *prev;

	if (obj->type != json_object) {
		errno = EINVAL;
		return -1;
	}
	op = obj->o;
	if (!op->head)
		return 0;

	prev = NULL;
	for (p = op->head; p; ) {
		struct json_pair *next = p->next;
		if (pred(p->k, p->v, data)) {
			if (prev)
				prev->next = next;
			else
				op->head = next;
			if (!next)
				op->tail = prev;
			free(p->k);
			json_value_free(p->v);
		} else
			prev = p;
		p = next;
	}
	return 0;
}

/* Parser */
#define ISSPACE(c) ((c)==' '||(c)=='\t'||(c)=='\n'||(c)=='\r')
#define ISHEX(c) (strchr("0123456789abcdefABCDEF", c) != NULL)
#define SKIPWS(s, e) while ((s) != (e) && ISSPACE(*(s))) (s)++;

enum state {
	ST_INIT,
	ST_KEY,    /* Object: key read. */
	ST_COLON,  /* Object: key and colon obtained. */
	ST_VALUE,  /* Object, Array: value read. */
	ST_COMMA,  /* Object, Array: comma read. */
};

struct j_context {
	struct j_context *next;
	struct json_value *obj;
	char *key;
	enum state state;
};

static inline int
j_context_type(struct j_context **ctx)
{
	return *ctx ? (*ctx)->obj->type : json_null;
}

static inline enum state
j_context_state(struct j_context **ctx)
{
	return *ctx ? (*ctx)->state : ST_INIT;
}

static inline void
j_context_set_state(struct j_context **ctx, enum state state)
{
	(*ctx)->state = state;
}

static int
j_context_push(struct j_context **ctx, int type)
{
	struct j_context *cur = malloc(sizeof(*cur));
	if (!cur)
		return JSON_E_NOMEM;
	switch (type) {
	case json_array:
		cur->obj = json_new_array();
		break;
	case json_object:
		cur->obj = json_new_object();
		break;
	default:
		abort();
	}
	if (!cur->obj) {
		free(cur);
		return JSON_E_NOMEM;
	}
	cur->key = NULL;
	cur->state = ST_INIT;
	cur->next = *ctx;
	*ctx = cur;
	return JSON_E_NOERR;
}

static struct json_value *
j_context_pop(struct j_context **ctx)
{
	struct json_value *val;
	struct j_context *next;

	if (!*ctx)
		return NULL;
	val = (*ctx)->obj;
	if (val->type == json_object)
		free((*ctx)->key);
	next = (*ctx)->next;
	free(*ctx);
	*ctx = next;

	return val;
}

static inline int
fromhex(int c)
{
	switch (c) {
	case '0': case '1': case '2': case '3': case '4':
	case '5': case '6': case '7': case '8': case '9':
		return c - '0';
	case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
		return c - 'a' + 10;
	case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
		return c - 'A' + 10;
	}
	return -1;
}

static int
utf8_wctomb(char const *u, char r[6], char **endp)
{
	unsigned int wc = 0;
	int count;
	int n;
	int i;

	for (i = 0; i < 4; i++) {
		if ((n = fromhex(u[i])) == -1) {
			*endp = (char*)u + i;
			return -1;
		}
		wc = (wc << 4) + n;
	}

	if (wc < 0x80)
		count = 1;
	else if (wc < 0x800)
		count = 2;
	else if (wc < 0x10000)
		count = 3;
	else if (wc < 0x200000)
		count = 4;
	else if (wc < 0x4000000)
		count = 5;
	else if (wc <= 0x7fffffff)
		count = 6;
	else
		return -1;

	switch (count) {
		/* Note: code falls through cases! */
	case 6:
		r[5] = 0x80 | (wc & 0x3f);
		wc = wc >> 6;
		wc |= 0x4000000;
	case 5:
		r[4] = 0x80 | (wc & 0x3f);
		wc = wc >> 6;
		wc |= 0x200000;
	case 4:
		r[3] = 0x80 | (wc & 0x3f);
		wc = wc >> 6;
		wc |= 0x10000;
	case 3:
		r[2] = 0x80 | (wc & 0x3f);
		wc = wc >> 6;
		wc |= 0x800;
	case 2:
		r[1] = 0x80 | (wc & 0x3f);
		wc = wc >> 6;
		wc |= 0xc0;
	case 1:
		r[0] = wc;
	}
	return count;
}

static int
memspn(char const *p, char const *delim, char const *stop)
{
	int i;
	for (i = 0; p != stop; i++, p++)
		if (!strchr(delim, *p))
			break;
	return i;
}

static int
memcspn(char const *p, char const *delim, char const *stop)
{
	int i;
	for (i = 0; p != stop; i++, p++)
		if (strchr(delim, *p))
			break;
	return i;
}

static int
j_get_text(char const *input, char const *stop, char **retval,
	   char const **endp)
{
	char *str = NULL;
	size_t len = 0;
	size_t cap = 0;
	int ec = JSON_E_BADSTRING|JSON_E_MOREINPUT;
	int tl = 0; /* Length of the tail segment of input. */

	input++;
	while (input < stop) {
		int n;

		if (*input == '"') {
			ec = JSON_E_NOERR;
			break;
		}

		n = memcspn(input, "\\\"", stop);

		if (input + n == stop) {
			ec = JSON_E_BADSTRING|JSON_E_MOREINPUT;
			break;
		}

		/* Optimize memory allocations for the common case: */
		if (input[n] == '"') {
			tl = n;
			ec = JSON_E_NOERR;
			break;
		}
		if (strexpand(&str, &cap, len, n)) {
			ec = JSON_E_NOMEM;
			break;
		}
		memcpy(str + len, input, n);
		input += n;
		len += n;

		if (*input == '\\') {
			char r[6];
			if (++input == stop) {
				ec = JSON_E_BADSTRING|JSON_E_MOREINPUT;
				break;
			}
			if ((r[0] = escape(*input, UNESCAPE)) != 0) {
				n = 1;
				input++;
			} else if (*input == 'u') {
				if (input + 5 < stop) {
					char *errp;
					n = utf8_wctomb(input + 1, r, &errp);
					if (n == -1) {
						ec = JSON_E_BADSTRING;
						input = errp;
						break;
					}
					input += 5;
				} else {
					ec = JSON_E_BADSTRING|JSON_E_MOREINPUT;
					break;
				}
			} else {
				ec = JSON_E_BADSTRING;
				break;
			}

			if (strexpand(&str, &cap, len, n)) {
				ec = JSON_E_NOMEM;
				break;
			}
			memcpy(str + len, r, n);
			len += n;
		}
	}
	if (ec == JSON_E_NOERR) {
		char *q = realloc(str, len + tl + 1);
		if (q == NULL) {
			ec = JSON_E_NOMEM;
			free(str);
		} else {
			str = q;
			memcpy(str + len, input, tl);
			str[len + tl] = 0;
			*retval = str;
			input += tl;
		}
	} else
		free(str);
	*endp = input;
	return ec;
}

static int
mem_to_double(char const *ptr, char const *stop,
	      struct json_value **valp, char **endp)
{
	size_t n = 0;
	char *store, *p;
	double d;

	switch (ptr[n]) {
	case '-':
	case '+':
		n++;
	}

	n += memspn(ptr+n, ".0123456789", stop);
	if (ptr[n] == 'e' || ptr[n] == 'E') {
		n++;
		if (ptr[n] == '-')
			n++;
		n += memspn(ptr+n, "0123456789", stop);
	}

	store = strndup(ptr, n);
	if (!store)
		return JSON_E_NOMEM;
	errno = 0;
	d = strtod(store, &p);
	n = p - store;
	free(store);
	*endp = (char*)ptr + n;
	if (errno == ERANGE)
		return JSON_E_RANGE;
	if ((*valp = json_new_double(d)) == NULL)
		return JSON_E_NOMEM;
	return JSON_E_NOERR;
}

static int
mem_to_long(char const *ptr, char const *stop,
	    struct json_value **valp, char **endp)
{
	int neg;
	long n = 0;

	switch (*ptr) {
	case '-':
		neg = 1;
		ptr++;
		break;
	case '+':
		neg = 0;
		ptr++;
		break;
	default:
		neg = 0;
	}

	for (;ptr < stop; ptr++) {
		int d = *ptr - '0';
		if (d < 0 || d > 9) {
			*endp = (char*)ptr;
			break;
		}
		if (neg ? (LONG_MIN / 10 > -n) : (LONG_MAX / 10 < n)) {
			*endp = (char*)ptr;
			return JSON_E_RANGE;
		}
		n *= 10;
		if (neg ? (LONG_MIN + d > -n) : (LONG_MAX - d < n)) {
			*endp = (char*)ptr;
			return JSON_E_RANGE;
		}
		n += d;
	}

	if (neg)
		n = - n;

	*endp = (char*)ptr;

	if ((*valp = json_new_long(n)) == NULL)
		return JSON_E_NOMEM;

	return JSON_E_NOERR;
}

static int
memton(char const *ptr, char const *stop,
       struct json_value **valp, char **endp)
{
	int rc;
	struct json_value *v;
	char *ep;

	switch (json_num_type) {
	case JSON_NUM_DOUBLE:
		rc = mem_to_double(ptr, stop, valp, endp);
		break;
	case JSON_NUM_LONG:
	case JSON_NUM_AUTO:
		if ((rc = mem_to_long(ptr, stop, &v, &ep)) == JSON_E_NOERR) {
			if (strchr(".eE", *ep)) {
				json_value_free(v);
				rc = mem_to_double(ptr, stop, &v, &ep);
			}
		} else if (rc == JSON_E_RANGE)
			rc = mem_to_double(ptr, stop, &v, &ep);
		if (rc == JSON_E_NOERR && json_is_double(v)) {
			long i;
			if (LONG_MIN <= v->n && v->n <= LONG_MAX &&
			    (i = v->n) == v->n) {
				/* Convert to long */
				v->i = i;
				v->type = json_long;
				*valp = v;
				*endp = ep;
			} else if (json_num_type == JSON_NUM_LONG) {
				json_value_free(v);
				rc = JSON_E_RANGE;
				*endp = (char*)ptr;
			} else {
				*valp = v;
				*endp = ep;
			}
		} else {
			*valp = v;
			*endp = ep;
		}
		break;
	}
	return rc;
}

int
json__parse(struct j_context **pctx, char const *input, char const *stop,
	    struct json_value **retval, char **endp, char **tok)
{
	struct json_value *val;
	int ecode;
	char *str;
	char const *p;
# define RETERR(c) do { if (endp) *endp = (char*)input; return c; } while(0)
# define SAVETOK() do { if (tok) *tok = (char*)input; } while (0)
	while (1) {
		val = NULL;

		SKIPWS(input, stop);
		SAVETOK();
		if (input == stop) {
			RETERR(JSON_E_EOF|JSON_E_MOREINPUT);
		}

		switch (j_context_state(pctx)) {
		case ST_VALUE:
			if (*input == ',') {
				j_context_set_state(pctx, ST_COMMA);
				input++;
				continue;
			}
			/* fall through */
		case ST_INIT:
			switch (*input) {
			case ']':
				if (j_context_type(pctx) == json_array) {
					val = j_context_pop(pctx);
					if (*pctx)
						j_context_set_state(pctx,
								    ST_VALUE);
					++input;
					goto getval;
				} else
					RETERR(JSON_E_BADTOK);
				break;

			case '}':
				if (j_context_type(pctx) == json_object) {
					val = j_context_pop(pctx);
					if (*pctx)
						j_context_set_state(pctx,
								    ST_VALUE);
					++input;
					goto getval;
				} else
					RETERR(JSON_E_BADTOK);
				break;
			}
			/* fall through */

		case ST_COLON:
		case ST_COMMA:
			switch (*input) {
			case '[':
				j_context_push(pctx, json_array);
				++input;
				continue;

			case '{':
				j_context_push(pctx, json_object);
				++input;
				continue;

			case '"':
				SAVETOK();
				ecode = j_get_text(input, stop, &str, &p);
				if (ecode != JSON_E_NOERR)
					RETERR(ecode);
				input = p + 1;
				if (j_context_type(pctx) == json_object &&
				    (j_context_state(pctx) == ST_INIT ||
				     j_context_state(pctx) == ST_COMMA)) {
					(*pctx)->key = str;
					j_context_set_state(pctx, ST_KEY);
					continue;
				} else {
					val = json_value_create(json_string);
					if (!val)
						RETERR(JSON_E_NOMEM);
					val->s = str;
				}
				break;

			case '-':
			case '+':
			case '.':
			case '0':
			case '1':
			case '2':
			case '3':
			case '4':
			case '5':
			case '6':
			case '7':
			case '8':
			case '9':{
				char *p;

				SAVETOK();
				ecode = memton(input, stop, &val, &p);
				if (ecode != JSON_E_NOERR)
					RETERR(ecode);
				input = p;
				if (p == stop) {
					if (*pctx) {
						json_value_free(val);
						RETERR(JSON_E_EOF|JSON_E_MOREINPUT);
					}
				}
				break;
			}

			default:{
				size_t n = stop - input;
				if (n >= 4 && memcmp(input, "null", 4) == 0) {
					val = json_new_null();
					input += 4;
				} else if (n >= 4 && memcmp(input, "true", 4) == 0) {
					val = json_new_bool(1);
					input += 4;
				} else if (n >= 5 && memcmp(input, "false", 5) == 0) {
					val = json_new_bool(0);
					input += 5;
				} else
					RETERR(JSON_E_BADTOK|JSON_E_MOREINPUT);//FIXME
				if (!val)
					RETERR(JSON_E_NOMEM);
			  }
			}
			break;

		case ST_KEY:
			if (*input == ':') {
				j_context_set_state(pctx, ST_COLON);
				input++;
				continue;
			}
			RETERR(JSON_E_BADDELIM);

		default:
			abort();
		}
	getval:
		if (val) {
			if (*pctx) {
				int rc;

				switch (j_context_type(pctx)) {
				case json_array:
					rc = json_array_append((*pctx)->obj,
							       val);
					break;

				case json_object:
					if (!(*pctx)->key) {
						json_value_free(val);
						RETERR(JSON_E_BADTOK);
					}
					rc = json_object_set((*pctx)->obj,
							     (*pctx)->key,
							     val);
					free((*pctx)->key);
					(*pctx)->key = NULL;
					break;

				default:
					abort();
				}
				if (rc) {
					json_value_free(val);
					RETERR(JSON_E_NOMEM);
				}
				j_context_set_state(pctx, ST_VALUE);
			} else {
				SKIPWS(input, stop);
				if (input < stop) {
					json_value_free(val);
					RETERR(JSON_E_BADTOK);
				}
				*retval = val;
				break;
			}
		}
	}

	if (endp)
		*endp = (char*)input;
	return JSON_E_NOERR;
}

void
json_context_free(json_context *ctx)
{
	struct json_value *v;
	while ((v = j_context_pop(ctx)) != NULL)
		json_value_free(v);
}

int
json_parse(char const *input, size_t len,
	   struct json_value **retval, char **endp)
{
	struct j_context *ctx = NULL;
	int rc;

	if (input == NULL || len == 0 || retval == NULL)
		return JSON_E_USAGE;

	rc = json__parse(&ctx, input, input + len, retval, endp,
			 NULL);
	json_context_free(&ctx);
	return rc & ~JSON_E_MOREINPUT;
}

int
json_parse_string(char const *input, struct json_value **retval, char **endp)
{
	return json_parse(input, strlen(input), retval, endp);
}

int
json_parse_incr(char const *input, size_t len, json_context *ctx,
		struct json_value **retval, size_t *consumed, char **endp)
{
	char *tok, *end;
	int rc;

	if (retval == NULL || consumed == NULL)
		return JSON_E_USAGE;

	rc = json__parse(ctx, input, input + len, retval, &end, &tok);
	if (rc & JSON_E_MOREINPUT)
		*consumed = tok - input;
	else
		*consumed = end - input;
	if (endp)
		*endp = end;
	return rc;
}
