Radish alpha
H
HardenedBSD Package Manager
Radicle
Git (anonymous pull)
Log in to clone via SSH
First step to replace cJSON by jansson.
jlaffaye committed 15 years ago
commit 6fcc033a07d222c656d46337b3329651ce939d30
parent 5d61fbefb0714cdc9e89190d0318f2f66cab4d24
22 files changed +4057 -635
modified external/Makefile
@@ -1,2 +1,4 @@
-
SUBDIR= cjson tinycdb
+
SUBDIR=	jansson \
+
	tinycdb
+

.include <bsd.subdir.mk>
deleted external/cjson/Makefile
@@ -1,12 +0,0 @@
-
LIB=    cjson
-
INTERNALLIB=
-
SRCS=   cJSON.c
-

-
WARNS?= 6
-
WFORMAT?=   1
-

-
CFLAGS+= -fPIC
-
NO_MAN=true
-

-
.include <bsd.lib.mk>
-

deleted external/cjson/cJSON.c
@@ -1,490 +0,0 @@
-
/*
-
  Copyright (c) 2009 Dave Gamble
-

-
  Permission is hereby granted, free of charge, to any person obtaining a copy
-
  of this software and associated documentation files (the "Software"), to deal
-
  in the Software without restriction, including without limitation the rights
-
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-
  copies of the Software, and to permit persons to whom the Software is
-
  furnished to do so, subject to the following conditions:
-

-
  The above copyright notice and this permission notice shall be included in
-
  all copies or substantial portions of the Software.
-

-
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-
  THE SOFTWARE.
-
*/
-

-
// cJSON
-
// JSON parser in C.
-

-
#include <string.h>
-
#include <stdio.h>
-
#include <math.h>
-
#include <stdlib.h>
-
#include <float.h>
-
#include <limits.h>
-
#include <ctype.h>
-
#include "cJSON.h"
-

-
static int cJSON_strcasecmp(const char *s1,const char *s2)
-
{
-
	if (!s1) return (s1==s2)?0:1;if (!s2) return 1;
-
	for(; tolower(*s1) == tolower(*s2); ++s1, ++s2)	if(*s1 == 0)	return 0;
-
	return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2);
-
}
-

-
static void *(*cJSON_malloc)(size_t sz) = malloc;
-
static void (*cJSON_free)(void *ptr) = free;
-

-
static char* cJSON_strdup(const char* str)
-
{
-
      size_t len;
-
      char* copy;
-

-
      len = strlen(str) + 1;
-
      if (!(copy = (char*)cJSON_malloc(len))) return 0;
-
      memcpy(copy,str,len);
-
      return copy;
-
}
-

-
void cJSON_InitHooks(cJSON_Hooks* hooks)
-
{
-
    if (!hooks) { /* Reset hooks */
-
        cJSON_malloc = malloc;
-
        cJSON_free = free;
-
        return;
-
    }
-

-
	cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc;
-
	cJSON_free	 = (hooks->free_fn)?hooks->free_fn:free;
-
}
-

-
// Internal constructor.
-
static cJSON *cJSON_New_Item(void)
-
{
-
	cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON));
-
	if (node) memset(node,0,sizeof(cJSON));
-
	return node;
-
}
-

-
// Delete a cJSON structure.
-
void cJSON_Delete(cJSON *c)
-
{
-
	cJSON *next;
-
	while (c)
-
	{
-
		next=c->next;
-
		if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child);
-
		if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring);
-
		if (c->string) cJSON_free(c->string);
-
		cJSON_free(c);
-
		c=next;
-
	}
-
}
-

-
// Parse the input text to generate a number, and populate the result into item.
-
static const char *parse_number(cJSON *item,const char *num)
-
{
-
	double n=0,sign=1,scale=0;int subscale=0,signsubscale=1;
-

-
	// Could use sscanf for this?
-
	if (*num=='-') sign=-1,num++;	// Has sign?
-
	if (*num=='0') num++;			// is zero
-
	if (*num>='1' && *num<='9')	do	n=(n*10.0)+(*num++ -'0');	while (*num>='0' && *num<='9');	// Number?
-
	if (*num=='.') {num++;		do	n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');}	// Fractional part?
-
	if (*num=='e' || *num=='E')		// Exponent?
-
	{	num++;if (*num=='+') num++;	else if (*num=='-') signsubscale=-1,num++;		// With sign?
-
		while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0');	// Number?
-
	}
-

-
	n=sign*n*pow(10.0,(scale+subscale*signsubscale));	// number = +/- number.fraction * 10^+/- exponent
-
	
-
	item->valuedouble=n;
-
	item->valueint=(int)n;
-
	item->type=cJSON_Number;
-
	return num;
-
}
-

-
// Render the number nicely from the given item into a string.
-
static char *print_number(cJSON *item)
-
{
-
	char *str;
-
	double d=item->valuedouble;
-
	if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN)
-
	{
-
		str=(char*)cJSON_malloc(21);	// 2^64+1 can be represented in 21 chars.
-
		sprintf(str,"%d",item->valueint);
-
	}
-
	else
-
	{
-
		str=(char*)cJSON_malloc(64);	// This is a nice tradeoff.
-
		if (fabs(floor(d)-d)<=DBL_EPSILON)			sprintf(str,"%.0f",d);
-
		else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9)	sprintf(str,"%e",d);
-
		else										sprintf(str,"%f",d);
-
	}
-
	return str;
-
}
-

-
// Parse the input text into an unescaped cstring, and populate item.
-
static const char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
-
static const char *parse_string(cJSON *item,const char *str)
-
{
-
	const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc;
-
	if (*str!='\"') return 0;	// not a string!
-
	
-
	while (*ptr!='\"' && (unsigned char)*ptr>31 && ++len) if (*ptr++ == '\\') ptr++;	// Skip escaped quotes.
-
	
-
	out=(char*)cJSON_malloc(len+1);	// This is how long we need for the string, roughly.
-
	if (!out) return 0;
-
	
-
	ptr=str+1;ptr2=out;
-
	while (*ptr!='\"' && (unsigned char)*ptr>31)
-
	{
-
		if (*ptr!='\\') *ptr2++=*ptr++;
-
		else
-
		{
-
			ptr++;
-
			switch (*ptr)
-
			{
-
				case 'b': *ptr2++='\b';	break;
-
				case 'f': *ptr2++='\f';	break;
-
				case 'n': *ptr2++='\n';	break;
-
				case 'r': *ptr2++='\r';	break;
-
				case 't': *ptr2++='\t';	break;
-
				case 'u':	 // transcode utf16 to utf8. DOES NOT SUPPORT SURROGATE PAIRS CORRECTLY.
-
					sscanf(ptr+1,"%4x",&uc);	// get the unicode char.
-
					len=3;if (uc<0x80) len=1;else if (uc<0x800) len=2;ptr2+=len;
-
					
-
					switch (len) {
-
						case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
-
						case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
-
						case 1: *--ptr2 =(uc | firstByteMark[len]);
-
					}
-
					ptr2+=len;ptr+=4;
-
					break;
-
				default:  *ptr2++=*ptr; break;
-
			}
-
			ptr++;
-
		}
-
	}
-
	*ptr2=0;
-
	if (*ptr=='\"') ptr++;
-
	item->valuestring=out;
-
	item->type=cJSON_String;
-
	return ptr;
-
}
-

-
// Render the cstring provided to an escaped version that can be printed.
-
static char *print_string_ptr(const char *str)
-
{
-
	const char *ptr;char *ptr2,*out;int len=0;
-
	
-
	if (!str) return cJSON_strdup("");
-
	ptr=str;while (*ptr && ++len) {if ((unsigned char)*ptr<32 || *ptr=='\"' || *ptr=='\\') len++;ptr++;}
-
	
-
	out=(char*)cJSON_malloc(len+3);
-
	ptr2=out;ptr=str;
-
	*ptr2++='\"';
-
	while (*ptr)
-
	{
-
		if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++;
-
		else
-
		{
-
			*ptr2++='\\';
-
			switch (*ptr++)
-
			{
-
				case '\\':	*ptr2++='\\';	break;
-
				case '\"':	*ptr2++='\"';	break;
-
				case '\b':	*ptr2++='b';	break;
-
				case '\f':	*ptr2++='f';	break;
-
				case '\n':	*ptr2++='n';	break;
-
				case '\r':	*ptr2++='r';	break;
-
				case '\t':	*ptr2++='t';	break;
-
				default: ptr2--;	break;	// eviscerate with prejudice.
-
			}
-
		}
-
	}
-
	*ptr2++='\"';*ptr2++=0;
-
	return out;
-
}
-
// Invote print_string_ptr (which is useful) on an item.
-
static char *print_string(cJSON *item)	{return print_string_ptr(item->valuestring);}
-

-
// Predeclare these prototypes.
-
static const char *parse_value(cJSON *item,const char *value);
-
static char *print_value(cJSON *item,int depth,int fmt);
-
static const char *parse_array(cJSON *item,const char *value);
-
static char *print_array(cJSON *item,int depth,int fmt);
-
static const char *parse_object(cJSON *item,const char *value);
-
static char *print_object(cJSON *item,int depth,int fmt);
-

-
// Utility to jump whitespace and cr/lf
-
static const char *skip(const char *in) {while (in && (unsigned char)*in<=32) in++; return in;}
-

-
// Parse an object - create a new root, and populate.
-
cJSON *cJSON_Parse(const char *value)
-
{
-
	cJSON *c=cJSON_New_Item();
-
	if (!c) return 0;       /* memory fail */
-

-
	if (!parse_value(c,skip(value))) {cJSON_Delete(c);return 0;}
-
	return c;
-
}
-

-
// Render a cJSON item/entity/structure to text.
-
char *cJSON_Print(cJSON *item)				{return print_value(item,0,1);}
-
char *cJSON_PrintUnformatted(cJSON *item)	{return print_value(item,0,0);}
-

-
// Parser core - when encountering text, process appropriately.
-
static const char *parse_value(cJSON *item,const char *value)
-
{
-
	if (!value)						return 0;	// Fail on null.
-
	if (!strncmp(value,"null",4))	{ item->type=cJSON_NULL;  return value+4; }
-
	if (!strncmp(value,"false",5))	{ item->type=cJSON_False; return value+5; }
-
	if (!strncmp(value,"true",4))	{ item->type=cJSON_True; item->valueint=1;	return value+4; }
-
	if (*value=='\"')				{ return parse_string(item,value); }
-
	if (*value=='-' || (*value>='0' && *value<='9'))	{ return parse_number(item,value); }
-
	if (*value=='[')				{ return parse_array(item,value); }
-
	if (*value=='{')				{ return parse_object(item,value); }
-

-
	return 0;	// failure.
-
}
-

-
// Render a value to text.
-
static char *print_value(cJSON *item,int depth,int fmt)
-
{
-
	char *out=0;
-
	if (!item) return 0;
-
	switch ((item->type)&255)
-
	{
-
		case cJSON_NULL:	out=cJSON_strdup("null");	break;
-
		case cJSON_False:	out=cJSON_strdup("false");break;
-
		case cJSON_True:	out=cJSON_strdup("true"); break;
-
		case cJSON_Number:	out=print_number(item);break;
-
		case cJSON_String:	out=print_string(item);break;
-
		case cJSON_Array:	out=print_array(item,depth,fmt);break;
-
		case cJSON_Object:	out=print_object(item,depth,fmt);break;
-
	}
-
	return out;
-
}
-

-
// Build an array from input text.
-
static const char *parse_array(cJSON *item,const char *value)
-
{
-
	cJSON *child;
-
	if (*value!='[')	return 0;	// not an array!
-

-
	item->type=cJSON_Array;
-
	value=skip(value+1);
-
	if (*value==']') return value+1;	// empty array.
-

-
	item->child=child=cJSON_New_Item();
-
	if (!item->child) return 0;		 // memory fail
-
	value=skip(parse_value(child,skip(value)));	// skip any spacing, get the value.
-
	if (!value) return 0;
-

-
	while (*value==',')
-
	{
-
		cJSON *new_item;
-
		if (!(new_item=cJSON_New_Item())) return 0; 	// memory fail
-
		child->next=new_item;new_item->prev=child;child=new_item;
-
		value=skip(parse_value(child,skip(value+1)));
-
		if (!value) return 0;	// memory fail
-
	}
-

-
	if (*value==']') return value+1;	// end of array
-
	return 0;	// malformed.
-
}
-

-
// Render an array to text
-
static char *print_array(cJSON *item,int depth,int fmt)
-
{
-
	char **entries;
-
	char *out=0,*ptr,*ret;int len=5;
-
	cJSON *child=item->child;
-
	int numentries=0,i=0,fail=0;
-
	
-
	// How many entries in the array?
-
	while (child) numentries++,child=child->next;
-
	// Allocate an array to hold the values for each
-
	entries=(char**)cJSON_malloc(numentries*sizeof(char*));
-
	if (!entries) return 0;
-
	memset(entries,0,numentries*sizeof(char*));
-
	// Retrieve all the results:
-
	child=item->child;
-
	while (child && !fail)
-
	{
-
		ret=print_value(child,depth+1,fmt);
-
		entries[i++]=ret;
-
		if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1;
-
		child=child->next;
-
	}
-
	
-
	// If we didn't fail, try to malloc the output string
-
	if (!fail) out=cJSON_malloc(len);
-
	// If that fails, we fail.
-
	if (!out) fail=1;
-

-
	// Handle failure.
-
	if (fail)
-
	{
-
		for (i=0;i<numentries;i++) if (entries[i]) cJSON_free(entries[i]);
-
		cJSON_free(entries);
-
		return 0;
-
	}
-
	
-
	// Compose the output array.
-
	*out='[';
-
	ptr=out+1;*ptr=0;
-
	for (i=0;i<numentries;i++)
-
	{
-
		strcpy(ptr,entries[i]);ptr+=strlen(entries[i]);
-
		if (i!=numentries-1) {*ptr++=',';if(fmt)*ptr++=' ';*ptr=0;}
-
		cJSON_free(entries[i]);
-
	}
-
	cJSON_free(entries);
-
	*ptr++=']';*ptr++=0;
-
	return out;	
-
}
-

-
// Build an object from the text.
-
static const char *parse_object(cJSON *item,const char *value)
-
{
-
	cJSON *child;
-
	if (*value!='{')	return 0;	// not an object!
-
	
-
	item->type=cJSON_Object;
-
	value=skip(value+1);
-
	if (*value=='}') return value+1;	// empty array.
-
	
-
	item->child=child=cJSON_New_Item();
-
	value=skip(parse_string(child,skip(value)));
-
	if (!value) return 0;
-
	child->string=child->valuestring;child->valuestring=0;
-
	if (*value!=':') return 0;	// fail!
-
	value=skip(parse_value(child,skip(value+1)));	// skip any spacing, get the value.
-
	if (!value) return 0;
-
	
-
	while (*value==',')
-
	{
-
		cJSON *new_item;
-
		if (!(new_item=cJSON_New_Item()))	return 0; // memory fail
-
		child->next=new_item;new_item->prev=child;child=new_item;
-
		value=skip(parse_string(child,skip(value+1)));
-
		if (!value) return 0;
-
		child->string=child->valuestring;child->valuestring=0;
-
		if (*value!=':') return 0;	// fail!
-
		value=skip(parse_value(child,skip(value+1)));	// skip any spacing, get the value.		
-
		if (!value) return 0;
-
	}
-
	
-
	if (*value=='}') return value+1;	// end of array
-
	return 0;	// malformed.	
-
}
-

-
// Render an object to text.
-
static char *print_object(cJSON *item,int depth,int fmt)
-
{
-
	char **entries=0,**names=0;
-
	char *out=0,*ptr,*ret,*str;int len=7,i=0,j;
-
	cJSON *child=item->child;
-
	int numentries=0,fail=0;
-
	// Count the number of entries.
-
	while (child) numentries++,child=child->next;
-
	// Allocate space for the names and the objects
-
	entries=(char**)cJSON_malloc(numentries*sizeof(char*));
-
	if (!entries) return 0;
-
	names=(char**)cJSON_malloc(numentries*sizeof(char*));
-
	if (!names) {cJSON_free(entries);return 0;}
-
	memset(entries,0,sizeof(char*)*numentries);
-
	memset(names,0,sizeof(char*)*numentries);
-

-
	// Collect all the results into our arrays:
-
	child=item->child;depth++;if (fmt) len+=depth;
-
	while (child)
-
	{
-
		names[i]=str=print_string_ptr(child->string);
-
		entries[i++]=ret=print_value(child,depth,fmt);
-
		if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1;
-
		child=child->next;
-
	}
-
	
-
	// Try to allocate the output string
-
	if (!fail) out=(char*)cJSON_malloc(len);
-
	if (!out) fail=1;
-

-
	// Handle failure
-
	if (fail)
-
	{
-
		for (i=0;i<numentries;i++) {if (names[i]) free(names[i]);if (entries[i]) free(entries[i]);}
-
		free(names);free(entries);
-
		return 0;
-
	}
-
	
-
	// Compose the output:
-
	*out='{';ptr=out+1;if (fmt)*ptr++='\n';*ptr=0;
-
	for (i=0;i<numentries;i++)
-
	{
-
		if (fmt) for (j=0;j<depth;j++) *ptr++='\t';
-
		strcpy(ptr,names[i]);ptr+=strlen(names[i]);
-
		*ptr++=':';if (fmt) *ptr++='\t';
-
		strcpy(ptr,entries[i]);ptr+=strlen(entries[i]);
-
		if (i!=numentries-1) *ptr++=',';
-
		if (fmt) *ptr++='\n';*ptr=0;
-
		cJSON_free(names[i]);cJSON_free(entries[i]);
-
	}
-
	
-
	cJSON_free(names);cJSON_free(entries);
-
	if (fmt) for (i=0;i<depth-1;i++) *ptr++='\t';
-
	*ptr++='}';*ptr++=0;
-
	return out;	
-
}
-

-
// Get Array size/item / object item.
-
int    cJSON_GetArraySize(cJSON *array)							{cJSON *c=array->child;int i=0;while(c)i++,c=c->next;return i;}
-
cJSON *cJSON_GetArrayItem(cJSON *array,int item)				{cJSON *c=array->child;  while (c && item>0) item--,c=c->next; return c;}
-
cJSON *cJSON_GetObjectItem(cJSON *object,const char *string)	{cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;}
-

-
// Utility for array list handling.
-
static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;}
-
// Utility for handling references.
-
static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;}
-

-
// Add item to array/object.
-
void   cJSON_AddItemToArray(cJSON *array, cJSON *item)						{cJSON *c=array->child;if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}}
-
void   cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item)	{if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);}
-
void	cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item)						{cJSON_AddItemToArray(array,create_reference(item));}
-
void	cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item)	{cJSON_AddItemToObject(object,string,create_reference(item));}
-

-
cJSON *cJSON_DetachItemFromArray(cJSON *array,int which)			{cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return 0;
-
	if (c->prev) c->prev->next=c->next;if (c->next) c->next->prev=c->prev;if (c==array->child) array->child=c->next;c->prev=c->next=0;return c;}
-
void   cJSON_DeleteItemFromArray(cJSON *array,int which)			{cJSON_Delete(cJSON_DetachItemFromArray(array,which));}
-
cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;}
-
void   cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));}
-

-
// Replace array/object items with new ones.
-
void   cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem)		{cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return;
-
	newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem;
-
	if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);}
-
void   cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;if(c){newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}}
-

-
// Create basic types:
-
cJSON *cJSON_CreateNull()						{cJSON *item=cJSON_New_Item();item->type=cJSON_NULL;return item;}
-
cJSON *cJSON_CreateTrue()						{cJSON *item=cJSON_New_Item();item->type=cJSON_True;return item;}
-
cJSON *cJSON_CreateFalse()						{cJSON *item=cJSON_New_Item();item->type=cJSON_False;return item;}
-
cJSON *cJSON_CreateNumber(double num)			{cJSON *item=cJSON_New_Item();item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;return item;}
-
cJSON *cJSON_CreateString(const char *string)	{cJSON *item=cJSON_New_Item();item->type=cJSON_String;item->valuestring=cJSON_strdup(string);return item;}
-
cJSON *cJSON_CreateArray()						{cJSON *item=cJSON_New_Item();item->type=cJSON_Array;return item;}
-
cJSON *cJSON_CreateObject()						{cJSON *item=cJSON_New_Item();item->type=cJSON_Object;return item;}
-

-
// Create Arrays:
-
cJSON *cJSON_CreateIntArray(int *numbers,int count)				{int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
-
cJSON *cJSON_CreateFloatArray(float *numbers,int count)			{int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
-
cJSON *cJSON_CreateDoubleArray(double *numbers,int count)		{int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
-
cJSON *cJSON_CreateStringArray(const char **strings,int count)	{int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;i<count;i++){n=cJSON_CreateString(strings[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
deleted external/cjson/cJSON.h
@@ -1,123 +0,0 @@
-
/*
-
  Copyright (c) 2009 Dave Gamble
-
 
-
  Permission is hereby granted, free of charge, to any person obtaining a copy
-
  of this software and associated documentation files (the "Software"), to deal
-
  in the Software without restriction, including without limitation the rights
-
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-
  copies of the Software, and to permit persons to whom the Software is
-
  furnished to do so, subject to the following conditions:
-
 
-
  The above copyright notice and this permission notice shall be included in
-
  all copies or substantial portions of the Software.
-
 
-
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-
  THE SOFTWARE.
-
*/
-

-
#ifndef cJSON__h
-
#define cJSON__h
-

-
#ifdef __cplusplus
-
extern "C"
-
{
-
#endif
-

-
// cJSON Types:
-
#define cJSON_False 0
-
#define cJSON_True 1
-
#define cJSON_NULL 2
-
#define cJSON_Number 3
-
#define cJSON_String 4
-
#define cJSON_Array 5
-
#define cJSON_Object 6
-
	
-
#define cJSON_IsReference 256
-

-
// The cJSON structure:
-
typedef struct cJSON {
-
	struct cJSON *next,*prev;	// next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem
-
	struct cJSON *child;		// An array or object item will have a child pointer pointing to a chain of the items in the array/object.
-

-
	int type;					// The type of the item, as above.
-

-
	char *valuestring;			// The item's string, if type==cJSON_String
-
	int valueint;				// The item's number, if type==cJSON_Number
-
	double valuedouble;			// The item's number, if type==cJSON_Number
-

-
	char *string;				// The item's name string, if this item is the child of, or is in the list of subitems of an object.
-
} cJSON;
-

-
typedef struct cJSON_Hooks {
-
      void *(*malloc_fn)(size_t sz);
-
      void (*free_fn)(void *ptr);
-
} cJSON_Hooks;
-

-
// Supply malloc, realloc and free functions to cJSON
-
extern void cJSON_InitHooks(cJSON_Hooks* hooks);
-

-

-
// Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished.
-
extern cJSON *cJSON_Parse(const char *value);
-
// Render a cJSON entity to text for transfer/storage. Free the char* when finished.
-
extern char  *cJSON_Print(cJSON *item);
-
// Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished.
-
extern char  *cJSON_PrintUnformatted(cJSON *item);
-
// Delete a cJSON entity and all subentities.
-
extern void   cJSON_Delete(cJSON *c);
-

-
// Returns the number of items in an array (or object).
-
extern int	  cJSON_GetArraySize(cJSON *array);
-
// Retrieve item number "item" from array "array". Returns NULL if unsuccessful.
-
extern cJSON *cJSON_GetArrayItem(cJSON *array,int item);
-
// Get item "string" from object. Case insensitive.
-
extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string);
-
	
-
// These calls create a cJSON item of the appropriate type.
-
extern cJSON *cJSON_CreateNull(void);
-
extern cJSON *cJSON_CreateTrue(void);
-
extern cJSON *cJSON_CreateFalse(void);
-
extern cJSON *cJSON_CreateNumber(double num);
-
extern cJSON *cJSON_CreateString(const char *string);
-
extern cJSON *cJSON_CreateArray(void);
-
extern cJSON *cJSON_CreateObject(void);
-

-
// These utilities create an Array of count items.
-
extern cJSON *cJSON_CreateIntArray(int *numbers,int count);
-
extern cJSON *cJSON_CreateFloatArray(float *numbers,int count);
-
extern cJSON *cJSON_CreateDoubleArray(double *numbers,int count);
-
extern cJSON *cJSON_CreateStringArray(const char **strings,int count);
-

-
// Append item to the specified array/object.
-
extern void cJSON_AddItemToArray(cJSON *array, cJSON *item);
-
extern void	cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item);
-
// Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON.
-
extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
-
extern void	cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item);
-

-
// Remove/Detatch items from Arrays/Objects.
-
extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which);
-
extern void   cJSON_DeleteItemFromArray(cJSON *array,int which);
-
extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string);
-
extern void   cJSON_DeleteItemFromObject(cJSON *object,const char *string);
-
	
-
// Update array items.
-
extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem);
-
extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
-

-
#define cJSON_AddNullToObject(object,name)	cJSON_AddItemToObject(object, name, cJSON_CreateNull())
-
#define cJSON_AddTrueToObject(object,name)	cJSON_AddItemToObject(object, name, cJSON_CreateTrue())
-
#define cJSON_AddFalseToObject(object,name)		cJSON_AddItemToObject(object, name, cJSON_CreateFalse())
-
#define cJSON_AddNumberToObject(object,name,n)	cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n))
-
#define cJSON_AddStringToObject(object,name,s)	cJSON_AddItemToObject(object, name, cJSON_CreateString(s))
-

-
#ifdef __cplusplus
-
}
-
#endif
-

-
#endif
added external/jansson/Makefile
@@ -0,0 +1,17 @@
+
LIB=		jansson
+
INTERNALLIB=
+
SRCS=		bench.c \
+
		dump.c \
+
		hashtable.c \
+
		load.c \
+
		strbuffer.c \
+
		utf.c \
+
		value.c
+

+
CFLAGS+=	-fPIC -I.
+
WARNS=		2
+
WFORMAT=	1
+

+
NO_MAN=		true
+

+
.include <bsd.lib.mk>
added external/jansson/bench.c
@@ -0,0 +1,23 @@
+
#include <stdlib.h>
+
#include <stdio.h>
+
#include "jansson.h"
+

+
int
+
main()
+
{
+
	void *m;
+
	json_t *j;
+
	json_t *n;
+
	json_error_t e;
+
m = malloc(1024);
+
	printf("Reading...");
+
	j = json_load_file("/var/db/pkg/ruby-1.8.7.248_4,1/+MANIFEST", &e);
+
	printf(" done!\n");
+
	if (j == NULL)
+
		printf("%s\n", e.text);	
+
	else {
+
		n = json_object_get(j, "name");
+
		printf("Name=%s\n", json_string_value(n));
+
		json_decref(j);
+
	}	
+
}
added external/jansson/config.h
@@ -0,0 +1,73 @@
+
/* config.h.  Generated from config.h.in by configure.  */
+
/* config.h.in.  Generated from configure.ac by autoheader.  */
+

+
/* Define to 1 if you have the <dlfcn.h> header file. */
+
#define HAVE_DLFCN_H 1
+

+
/* Define to 1 if you have the <inttypes.h> header file. */
+
#define HAVE_INTTYPES_H 1
+

+
/* Define to 1 if you have the <memory.h> header file. */
+
#define HAVE_MEMORY_H 1
+

+
/* Define to 1 if you have the <stdint.h> header file. */
+
#define HAVE_STDINT_H 1
+

+
/* Define to 1 if you have the <stdlib.h> header file. */
+
#define HAVE_STDLIB_H 1
+

+
/* Define to 1 if you have the <strings.h> header file. */
+
#define HAVE_STRINGS_H 1
+

+
/* Define to 1 if you have the <string.h> header file. */
+
#define HAVE_STRING_H 1
+

+
/* Define to 1 if you have the <sys/stat.h> header file. */
+
#define HAVE_SYS_STAT_H 1
+

+
/* Define to 1 if you have the <sys/types.h> header file. */
+
#define HAVE_SYS_TYPES_H 1
+

+
/* Define to 1 if you have the <unistd.h> header file. */
+
#define HAVE_UNISTD_H 1
+

+
/* Define to the sub-directory in which libtool stores uninstalled libraries.
+
   */
+
#define LT_OBJDIR ".libs/"
+

+
/* Name of package */
+
#define PACKAGE "jansson"
+

+
/* Define to the address where bug reports for this package should be sent. */
+
#define PACKAGE_BUGREPORT "petri@digip.org"
+

+
/* Define to the full name of this package. */
+
#define PACKAGE_NAME "jansson"
+

+
/* Define to the full name and version of this package. */
+
#define PACKAGE_STRING "jansson 1.3"
+

+
/* Define to the one symbol short name of this package. */
+
#define PACKAGE_TARNAME "jansson"
+

+
/* Define to the home page for this package. */
+
#define PACKAGE_URL ""
+

+
/* Define to the version of this package. */
+
#define PACKAGE_VERSION "1.3"
+

+
/* Define to 1 if you have the ANSI C header files. */
+
#define STDC_HEADERS 1
+

+
/* Version number of package */
+
#define VERSION "1.3"
+

+
/* Define to `__inline__' or `__inline' if that's what the C compiler
+
   calls it, or to nothing if 'inline' is not supported under any name.  */
+
#ifndef __cplusplus
+
/* #undef inline */
+
#endif
+

+
/* Define to the type of a signed integer type of width exactly 32 bits if
+
   such a type exists and the standard includes do not define it. */
+
/* #undef int32_t */
added external/jansson/dump.c
@@ -0,0 +1,460 @@
+
/*
+
 * Copyright (c) 2009, 2010 Petri Lehtinen <petri@digip.org>
+
 *
+
 * Jansson is free software; you can redistribute it and/or modify
+
 * it under the terms of the MIT license. See LICENSE for details.
+
 */
+

+
#define _GNU_SOURCE
+
#include <stdio.h>
+
#include <stdlib.h>
+
#include <string.h>
+
#include <assert.h>
+

+
#include <jansson.h>
+
#include "jansson_private.h"
+
#include "strbuffer.h"
+
#include "utf.h"
+

+
#define MAX_INTEGER_STR_LENGTH  100
+
#define MAX_REAL_STR_LENGTH     100
+

+
typedef int (*dump_func)(const char *buffer, int size, void *data);
+

+
struct string
+
{
+
    char *buffer;
+
    int length;
+
    int size;
+
};
+

+
static int dump_to_strbuffer(const char *buffer, int size, void *data)
+
{
+
    return strbuffer_append_bytes((strbuffer_t *)data, buffer, size);
+
}
+

+
static int dump_to_file(const char *buffer, int size, void *data)
+
{
+
    FILE *dest = (FILE *)data;
+
    if(fwrite(buffer, size, 1, dest) != 1)
+
        return -1;
+
    return 0;
+
}
+

+
/* 256 spaces (the maximum indentation size) */
+
static char whitespace[] = "                                                                                                                                                                                                                                                                ";
+

+
static int dump_indent(unsigned long flags, int depth, int space, dump_func dump, void *data)
+
{
+
    if(JSON_INDENT(flags) > 0)
+
    {
+
        int i, ws_count = JSON_INDENT(flags);
+

+
        if(dump("\n", 1, data))
+
            return -1;
+

+
        for(i = 0; i < depth; i++)
+
        {
+
            if(dump(whitespace, ws_count, data))
+
                return -1;
+
        }
+
    }
+
    else if(space && !(flags & JSON_COMPACT))
+
    {
+
        return dump(" ", 1, data);
+
    }
+
    return 0;
+
}
+

+
static int dump_string(const char *str, int ascii, dump_func dump, void *data)
+
{
+
    const char *pos, *end;
+
    int32_t codepoint;
+

+
    if(dump("\"", 1, data))
+
        return -1;
+

+
    end = pos = str;
+
    while(1)
+
    {
+
        const char *text;
+
        char seq[13];
+
        int length;
+

+
        while(*end)
+
        {
+
            end = utf8_iterate(pos, &codepoint);
+
            if(!end)
+
                return -1;
+

+
            /* mandatory escape or control char */
+
            if(codepoint == '\\' || codepoint == '"' || codepoint < 0x20)
+
                break;
+

+
            /* non-ASCII */
+
            if(ascii && codepoint > 0x7F)
+
                break;
+

+
            pos = end;
+
        }
+

+
        if(pos != str) {
+
            if(dump(str, pos - str, data))
+
                return -1;
+
        }
+

+
        if(end == pos)
+
            break;
+

+
        /* handle \, ", and control codes */
+
        length = 2;
+
        switch(codepoint)
+
        {
+
            case '\\': text = "\\\\"; break;
+
            case '\"': text = "\\\""; break;
+
            case '\b': text = "\\b"; break;
+
            case '\f': text = "\\f"; break;
+
            case '\n': text = "\\n"; break;
+
            case '\r': text = "\\r"; break;
+
            case '\t': text = "\\t"; break;
+
            default:
+
            {
+
                /* codepoint is in BMP */
+
                if(codepoint < 0x10000)
+
                {
+
                    sprintf(seq, "\\u%04x", codepoint);
+
                    length = 6;
+
                }
+

+
                /* not in BMP -> construct a UTF-16 surrogate pair */
+
                else
+
                {
+
                    int32_t first, last;
+

+
                    codepoint -= 0x10000;
+
                    first = 0xD800 | ((codepoint & 0xffc00) >> 10);
+
                    last = 0xDC00 | (codepoint & 0x003ff);
+

+
                    sprintf(seq, "\\u%04x\\u%04x", first, last);
+
                    length = 12;
+
                }
+

+
                text = seq;
+
                break;
+
            }
+
        }
+

+
        if(dump(text, length, data))
+
            return -1;
+

+
        str = pos = end;
+
    }
+

+
    return dump("\"", 1, data);
+
}
+

+
static int object_key_compare_keys(const void *key1, const void *key2)
+
{
+
    return strcmp((*(const object_key_t **)key1)->key,
+
                  (*(const object_key_t **)key2)->key);
+
}
+

+
static int object_key_compare_serials(const void *key1, const void *key2)
+
{
+
    return (*(const object_key_t **)key1)->serial -
+
           (*(const object_key_t **)key2)->serial;
+
}
+

+
static int do_dump(const json_t *json, unsigned long flags, int depth,
+
                   dump_func dump, void *data)
+
{
+
    int ascii = flags & JSON_ENSURE_ASCII ? 1 : 0;
+

+
    switch(json_typeof(json)) {
+
        case JSON_NULL:
+
            return dump("null", 4, data);
+

+
        case JSON_TRUE:
+
            return dump("true", 4, data);
+

+
        case JSON_FALSE:
+
            return dump("false", 5, data);
+

+
        case JSON_INTEGER:
+
        {
+
            char buffer[MAX_INTEGER_STR_LENGTH];
+
            int size;
+

+
            size = snprintf(buffer, MAX_INTEGER_STR_LENGTH, "%d", json_integer_value(json));
+
            if(size >= MAX_INTEGER_STR_LENGTH)
+
                return -1;
+

+
            return dump(buffer, size, data);
+
        }
+

+
        case JSON_REAL:
+
        {
+
            char buffer[MAX_REAL_STR_LENGTH];
+
            int size;
+

+
            size = snprintf(buffer, MAX_REAL_STR_LENGTH, "%.17g",
+
                            json_real_value(json));
+
            if(size >= MAX_REAL_STR_LENGTH)
+
                return -1;
+

+
            /* Make sure there's a dot or 'e' in the output. Otherwise
+
               a real is converted to an integer when decoding */
+
            if(strchr(buffer, '.') == NULL &&
+
               strchr(buffer, 'e') == NULL)
+
            {
+
                if(size + 2 >= MAX_REAL_STR_LENGTH) {
+
                    /* No space to append ".0" */
+
                    return -1;
+
                }
+
                buffer[size] = '.';
+
                buffer[size + 1] = '0';
+
                size += 2;
+
            }
+

+
            return dump(buffer, size, data);
+
        }
+

+
        case JSON_STRING:
+
            return dump_string(json_string_value(json), ascii, dump, data);
+

+
        case JSON_ARRAY:
+
        {
+
            int i;
+
            int n;
+
            json_array_t *array;
+

+
            /* detect circular references */
+
            array = json_to_array(json);
+
            if(array->visited)
+
                goto array_error;
+
            array->visited = 1;
+

+
            n = json_array_size(json);
+

+
            if(dump("[", 1, data))
+
                goto array_error;
+
            if(n == 0) {
+
                array->visited = 0;
+
                return dump("]", 1, data);
+
            }
+
            if(dump_indent(flags, depth + 1, 0, dump, data))
+
                goto array_error;
+

+
            for(i = 0; i < n; ++i) {
+
                if(do_dump(json_array_get(json, i), flags, depth + 1,
+
                           dump, data))
+
                    goto array_error;
+

+
                if(i < n - 1)
+
                {
+
                    if(dump(",", 1, data) ||
+
                       dump_indent(flags, depth + 1, 1, dump, data))
+
                        goto array_error;
+
                }
+
                else
+
                {
+
                    if(dump_indent(flags, depth, 0, dump, data))
+
                        goto array_error;
+
                }
+
            }
+

+
            array->visited = 0;
+
            return dump("]", 1, data);
+

+
        array_error:
+
            array->visited = 0;
+
            return -1;
+
        }
+

+
        case JSON_OBJECT:
+
        {
+
            json_object_t *object;
+
            void *iter;
+
            const char *separator;
+
            int separator_length;
+

+
            if(flags & JSON_COMPACT) {
+
                separator = ":";
+
                separator_length = 1;
+
            }
+
            else {
+
                separator = ": ";
+
                separator_length = 2;
+
            }
+

+
            /* detect circular references */
+
            object = json_to_object(json);
+
            if(object->visited)
+
                goto object_error;
+
            object->visited = 1;
+

+
            iter = json_object_iter((json_t *)json);
+

+
            if(dump("{", 1, data))
+
                goto object_error;
+
            if(!iter) {
+
                object->visited = 0;
+
                return dump("}", 1, data);
+
            }
+
            if(dump_indent(flags, depth + 1, 0, dump, data))
+
                goto object_error;
+

+
            if(flags & JSON_SORT_KEYS || flags & JSON_PRESERVE_ORDER)
+
            {
+
                const object_key_t **keys;
+
                unsigned int size;
+
                unsigned int i;
+
                int (*cmp_func)(const void *, const void *);
+

+
                size = json_object_size(json);
+
                keys = malloc(size * sizeof(object_key_t *));
+
                if(!keys)
+
                    goto object_error;
+

+
                i = 0;
+
                while(iter)
+
                {
+
                    keys[i] = jsonp_object_iter_fullkey(iter);
+
                    iter = json_object_iter_next((json_t *)json, iter);
+
                    i++;
+
                }
+
                assert(i == size);
+

+
                if(flags & JSON_SORT_KEYS)
+
                    cmp_func = object_key_compare_keys;
+
                else
+
                    cmp_func = object_key_compare_serials;
+

+
                qsort(keys, size, sizeof(object_key_t *), cmp_func);
+

+
                for(i = 0; i < size; i++)
+
                {
+
                    const char *key;
+
                    json_t *value;
+

+
                    key = keys[i]->key;
+
                    value = json_object_get(json, key);
+
                    assert(value);
+

+
                    dump_string(key, ascii, dump, data);
+
                    if(dump(separator, separator_length, data) ||
+
                       do_dump(value, flags, depth + 1, dump, data))
+
                    {
+
                        free(keys);
+
                        goto object_error;
+
                    }
+

+
                    if(i < size - 1)
+
                    {
+
                        if(dump(",", 1, data) ||
+
                           dump_indent(flags, depth + 1, 1, dump, data))
+
                        {
+
                            free(keys);
+
                            goto object_error;
+
                        }
+
                    }
+
                    else
+
                    {
+
                        if(dump_indent(flags, depth, 0, dump, data))
+
                        {
+
                            free(keys);
+
                            goto object_error;
+
                        }
+
                    }
+
                }
+

+
                free(keys);
+
            }
+
            else
+
            {
+
                /* Don't sort keys */
+

+
                while(iter)
+
                {
+
                    void *next = json_object_iter_next((json_t *)json, iter);
+

+
                    dump_string(json_object_iter_key(iter), ascii, dump, data);
+
                    if(dump(separator, separator_length, data) ||
+
                       do_dump(json_object_iter_value(iter), flags, depth + 1,
+
                               dump, data))
+
                        goto object_error;
+

+
                    if(next)
+
                    {
+
                        if(dump(",", 1, data) ||
+
                           dump_indent(flags, depth + 1, 1, dump, data))
+
                            goto object_error;
+
                    }
+
                    else
+
                    {
+
                        if(dump_indent(flags, depth, 0, dump, data))
+
                            goto object_error;
+
                    }
+

+
                    iter = next;
+
                }
+
            }
+

+
            object->visited = 0;
+
            return dump("}", 1, data);
+

+
        object_error:
+
            object->visited = 0;
+
            return -1;
+
        }
+

+
        default:
+
            /* not reached */
+
            return -1;
+
    }
+
}
+

+

+
char *json_dumps(const json_t *json, unsigned long flags)
+
{
+
    strbuffer_t strbuff;
+
    char *result;
+

+
    if(!json_is_array(json) && !json_is_object(json))
+
        return NULL;
+

+
    if(strbuffer_init(&strbuff))
+
        return NULL;
+

+
    if(do_dump(json, flags, 0, dump_to_strbuffer, (void *)&strbuff)) {
+
        strbuffer_close(&strbuff);
+
        return NULL;
+
    }
+

+
    result = strdup(strbuffer_value(&strbuff));
+
    strbuffer_close(&strbuff);
+

+
    return result;
+
}
+

+
int json_dumpf(const json_t *json, FILE *output, unsigned long flags)
+
{
+
    if(!json_is_array(json) && !json_is_object(json))
+
        return -1;
+

+
    return do_dump(json, flags, 0, dump_to_file, (void *)output);
+
}
+

+
int json_dump_file(const json_t *json, const char *path, unsigned long flags)
+
{
+
    int result;
+

+
    FILE *output = fopen(path, "w");
+
    if(!output)
+
        return -1;
+

+
    result = json_dumpf(json, output, flags);
+

+
    fclose(output);
+
    return result;
+
}
added external/jansson/hashtable.c
@@ -0,0 +1,375 @@
+
/*
+
 * Copyright (c) 2009, 2010 Petri Lehtinen <petri@digip.org>
+
 *
+
 * This library is free software; you can redistribute it and/or modify
+
 * it under the terms of the MIT license. See LICENSE for details.
+
 */
+

+
#include <config.h>
+

+
#include <stdlib.h>
+
#include "hashtable.h"
+

+
typedef struct hashtable_list list_t;
+
typedef struct hashtable_pair pair_t;
+
typedef struct hashtable_bucket bucket_t;
+

+
#define container_of(ptr_, type_, member_)                      \
+
    ((type_ *)((char *)ptr_ - (size_t)&((type_ *)0)->member_))
+

+
#define list_to_pair(list_)  container_of(list_, pair_t, list)
+

+
static inline void list_init(list_t *list)
+
{
+
    list->next = list;
+
    list->prev = list;
+
}
+

+
static inline void list_insert(list_t *list, list_t *node)
+
{
+
    node->next = list;
+
    node->prev = list->prev;
+
    list->prev->next = node;
+
    list->prev = node;
+
}
+

+
static inline void list_remove(list_t *list)
+
{
+
    list->prev->next = list->next;
+
    list->next->prev = list->prev;
+
}
+

+
static inline int bucket_is_empty(hashtable_t *hashtable, bucket_t *bucket)
+
{
+
    return bucket->first == &hashtable->list && bucket->first == bucket->last;
+
}
+

+
static void insert_to_bucket(hashtable_t *hashtable, bucket_t *bucket,
+
                             list_t *list)
+
{
+
    if(bucket_is_empty(hashtable, bucket))
+
    {
+
        list_insert(&hashtable->list, list);
+
        bucket->first = bucket->last = list;
+
    }
+
    else
+
    {
+
        list_insert(bucket->first, list);
+
        bucket->first = list;
+
    }
+
}
+

+
static unsigned int primes[] = {
+
    5, 13, 23, 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593,
+
    49157, 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469,
+
    12582917, 25165843, 50331653, 100663319, 201326611, 402653189,
+
    805306457, 1610612741
+
};
+
static const unsigned int num_primes = sizeof(primes) / sizeof(unsigned int);
+

+
static inline unsigned int num_buckets(hashtable_t *hashtable)
+
{
+
    return primes[hashtable->num_buckets];
+
}
+

+

+
static pair_t *hashtable_find_pair(hashtable_t *hashtable, bucket_t *bucket,
+
                                   const void *key, unsigned int hash)
+
{
+
    list_t *list;
+
    pair_t *pair;
+

+
    if(bucket_is_empty(hashtable, bucket))
+
        return NULL;
+

+
    list = bucket->first;
+
    while(1)
+
    {
+
        pair = list_to_pair(list);
+
        if(pair->hash == hash && hashtable->cmp_keys(pair->key, key))
+
            return pair;
+

+
        if(list == bucket->last)
+
            break;
+

+
        list = list->next;
+
    }
+

+
    return NULL;
+
}
+

+
/* returns 0 on success, -1 if key was not found */
+
static int hashtable_do_del(hashtable_t *hashtable,
+
                            const void *key, unsigned int hash)
+
{
+
    pair_t *pair;
+
    bucket_t *bucket;
+
    unsigned int index;
+

+
    index = hash % num_buckets(hashtable);
+
    bucket = &hashtable->buckets[index];
+

+
    pair = hashtable_find_pair(hashtable, bucket, key, hash);
+
    if(!pair)
+
        return -1;
+

+
    if(&pair->list == bucket->first && &pair->list == bucket->last)
+
        bucket->first = bucket->last = &hashtable->list;
+

+
    else if(&pair->list == bucket->first)
+
        bucket->first = pair->list.next;
+

+
    else if(&pair->list == bucket->last)
+
        bucket->last = pair->list.prev;
+

+
    list_remove(&pair->list);
+

+
    if(hashtable->free_key)
+
        hashtable->free_key(pair->key);
+
    if(hashtable->free_value)
+
        hashtable->free_value(pair->value);
+

+
    free(pair);
+
    hashtable->size--;
+

+
    return 0;
+
}
+

+
static void hashtable_do_clear(hashtable_t *hashtable)
+
{
+
    list_t *list, *next;
+
    pair_t *pair;
+

+
    for(list = hashtable->list.next; list != &hashtable->list; list = next)
+
    {
+
        next = list->next;
+
        pair = list_to_pair(list);
+
        if(hashtable->free_key)
+
            hashtable->free_key(pair->key);
+
        if(hashtable->free_value)
+
            hashtable->free_value(pair->value);
+
        free(pair);
+
    }
+
}
+

+
static int hashtable_do_rehash(hashtable_t *hashtable)
+
{
+
    list_t *list, *next;
+
    pair_t *pair;
+
    unsigned int i, index, new_size;
+

+
    free(hashtable->buckets);
+

+
    hashtable->num_buckets++;
+
    new_size = num_buckets(hashtable);
+

+
    hashtable->buckets = malloc(new_size * sizeof(bucket_t));
+
    if(!hashtable->buckets)
+
        return -1;
+

+
    for(i = 0; i < num_buckets(hashtable); i++)
+
    {
+
        hashtable->buckets[i].first = hashtable->buckets[i].last =
+
            &hashtable->list;
+
    }
+

+
    list = hashtable->list.next;
+
    list_init(&hashtable->list);
+

+
    for(; list != &hashtable->list; list = next) {
+
        next = list->next;
+
        pair = list_to_pair(list);
+
        index = pair->hash % new_size;
+
        insert_to_bucket(hashtable, &hashtable->buckets[index], &pair->list);
+
    }
+

+
    return 0;
+
}
+

+

+
hashtable_t *hashtable_create(key_hash_fn hash_key, key_cmp_fn cmp_keys,
+
                              free_fn free_key, free_fn free_value)
+
{
+
    hashtable_t *hashtable = malloc(sizeof(hashtable_t));
+
    if(!hashtable)
+
        return NULL;
+

+
    if(hashtable_init(hashtable, hash_key, cmp_keys, free_key, free_value))
+
    {
+
        free(hashtable);
+
        return NULL;
+
    }
+

+
    return hashtable;
+
}
+

+
void hashtable_destroy(hashtable_t *hashtable)
+
{
+
    hashtable_close(hashtable);
+
    free(hashtable);
+
}
+

+
int hashtable_init(hashtable_t *hashtable,
+
                   key_hash_fn hash_key, key_cmp_fn cmp_keys,
+
                   free_fn free_key, free_fn free_value)
+
{
+
    unsigned int i;
+

+
    hashtable->size = 0;
+
    hashtable->num_buckets = 0;  /* index to primes[] */
+
    hashtable->buckets = malloc(num_buckets(hashtable) * sizeof(bucket_t));
+
    if(!hashtable->buckets)
+
        return -1;
+

+
    list_init(&hashtable->list);
+

+
    hashtable->hash_key = hash_key;
+
    hashtable->cmp_keys = cmp_keys;
+
    hashtable->free_key = free_key;
+
    hashtable->free_value = free_value;
+

+
    for(i = 0; i < num_buckets(hashtable); i++)
+
    {
+
        hashtable->buckets[i].first = hashtable->buckets[i].last =
+
            &hashtable->list;
+
    }
+

+
    return 0;
+
}
+

+
void hashtable_close(hashtable_t *hashtable)
+
{
+
    hashtable_do_clear(hashtable);
+
    free(hashtable->buckets);
+
}
+

+
int hashtable_set(hashtable_t *hashtable, void *key, void *value)
+
{
+
    pair_t *pair;
+
    bucket_t *bucket;
+
    unsigned int hash, index;
+

+
    /* rehash if the load ratio exceeds 1 */
+
    if(hashtable->size >= num_buckets(hashtable))
+
        if(hashtable_do_rehash(hashtable))
+
            return -1;
+

+
    hash = hashtable->hash_key(key);
+
    index = hash % num_buckets(hashtable);
+
    bucket = &hashtable->buckets[index];
+
    pair = hashtable_find_pair(hashtable, bucket, key, hash);
+

+
    if(pair)
+
    {
+
        if(hashtable->free_key)
+
            hashtable->free_key(key);
+
        if(hashtable->free_value)
+
            hashtable->free_value(pair->value);
+
        pair->value = value;
+
    }
+
    else
+
    {
+
        pair = malloc(sizeof(pair_t));
+
        if(!pair)
+
            return -1;
+

+
        pair->key = key;
+
        pair->value = value;
+
        pair->hash = hash;
+
        list_init(&pair->list);
+

+
        insert_to_bucket(hashtable, bucket, &pair->list);
+

+
        hashtable->size++;
+
    }
+
    return 0;
+
}
+

+
void *hashtable_get(hashtable_t *hashtable, const void *key)
+
{
+
    pair_t *pair;
+
    unsigned int hash;
+
    bucket_t *bucket;
+

+
    hash = hashtable->hash_key(key);
+
    bucket = &hashtable->buckets[hash % num_buckets(hashtable)];
+

+
    pair = hashtable_find_pair(hashtable, bucket, key, hash);
+
    if(!pair)
+
        return NULL;
+

+
    return pair->value;
+
}
+

+
int hashtable_del(hashtable_t *hashtable, const void *key)
+
{
+
    unsigned int hash = hashtable->hash_key(key);
+
    return hashtable_do_del(hashtable, key, hash);
+
}
+

+
void hashtable_clear(hashtable_t *hashtable)
+
{
+
    unsigned int i;
+

+
    hashtable_do_clear(hashtable);
+

+
    for(i = 0; i < num_buckets(hashtable); i++)
+
    {
+
        hashtable->buckets[i].first = hashtable->buckets[i].last =
+
            &hashtable->list;
+
    }
+

+
    list_init(&hashtable->list);
+
    hashtable->size = 0;
+
}
+

+
void *hashtable_iter(hashtable_t *hashtable)
+
{
+
    return hashtable_iter_next(hashtable, &hashtable->list);
+
}
+

+
void *hashtable_iter_at(hashtable_t *hashtable, const void *key)
+
{
+
    pair_t *pair;
+
    unsigned int hash;
+
    bucket_t *bucket;
+

+
    hash = hashtable->hash_key(key);
+
    bucket = &hashtable->buckets[hash % num_buckets(hashtable)];
+

+
    pair = hashtable_find_pair(hashtable, bucket, key, hash);
+
    if(!pair)
+
        return NULL;
+

+
    return &pair->list;
+
}
+

+
void *hashtable_iter_next(hashtable_t *hashtable, void *iter)
+
{
+
    list_t *list = (list_t *)iter;
+
    if(list->next == &hashtable->list)
+
        return NULL;
+
    return list->next;
+
}
+

+
void *hashtable_iter_key(void *iter)
+
{
+
    pair_t *pair = list_to_pair((list_t *)iter);
+
    return pair->key;
+
}
+

+
void *hashtable_iter_value(void *iter)
+
{
+
    pair_t *pair = list_to_pair((list_t *)iter);
+
    return pair->value;
+
}
+

+
void hashtable_iter_set(hashtable_t *hashtable, void *iter, void *value)
+
{
+
    pair_t *pair = list_to_pair((list_t *)iter);
+

+
    if(hashtable->free_value)
+
        hashtable->free_value(pair->value);
+

+
    pair->value = value;
+
}
added external/jansson/hashtable.h
@@ -0,0 +1,207 @@
+
/*
+
 * Copyright (c) 2009, 2010 Petri Lehtinen <petri@digip.org>
+
 *
+
 * This library is free software; you can redistribute it and/or modify
+
 * it under the terms of the MIT license. See LICENSE for details.
+
 */
+

+
#ifndef HASHTABLE_H
+
#define HASHTABLE_H
+

+
typedef unsigned int (*key_hash_fn)(const void *key);
+
typedef int (*key_cmp_fn)(const void *key1, const void *key2);
+
typedef void (*free_fn)(void *key);
+

+
struct hashtable_list {
+
    struct hashtable_list *prev;
+
    struct hashtable_list *next;
+
};
+

+
struct hashtable_pair {
+
    void *key;
+
    void *value;
+
    unsigned int hash;
+
    struct hashtable_list list;
+
};
+

+
struct hashtable_bucket {
+
    struct hashtable_list *first;
+
    struct hashtable_list *last;
+
};
+

+
typedef struct hashtable {
+
    unsigned int size;
+
    struct hashtable_bucket *buckets;
+
    unsigned int num_buckets;  /* index to primes[] */
+
    struct hashtable_list list;
+

+
    key_hash_fn hash_key;
+
    key_cmp_fn cmp_keys;  /* returns non-zero for equal keys */
+
    free_fn free_key;
+
    free_fn free_value;
+
} hashtable_t;
+

+
/**
+
 * hashtable_create - Create a hashtable object
+
 *
+
 * @hash_key: The key hashing function
+
 * @cmp_keys: The key compare function. Returns non-zero for equal and
+
 *     zero for unequal unequal keys
+
 * @free_key: If non-NULL, called for a key that is no longer referenced.
+
 * @free_value: If non-NULL, called for a value that is no longer referenced.
+
 *
+
 * Returns a new hashtable object that should be freed with
+
 * hashtable_destroy when it's no longer used, or NULL on failure (out
+
 * of memory).
+
 */
+
hashtable_t *hashtable_create(key_hash_fn hash_key, key_cmp_fn cmp_keys,
+
                              free_fn free_key, free_fn free_value);
+

+
/**
+
 * hashtable_destroy - Destroy a hashtable object
+
 *
+
 * @hashtable: The hashtable
+
 *
+
 * Destroys a hashtable created with hashtable_create().
+
 */
+
void hashtable_destroy(hashtable_t *hashtable);
+

+
/**
+
 * hashtable_init - Initialize a hashtable object
+
 *
+
 * @hashtable: The (statically allocated) hashtable object
+
 * @hash_key: The key hashing function
+
 * @cmp_keys: The key compare function. Returns non-zero for equal and
+
 *     zero for unequal unequal keys
+
 * @free_key: If non-NULL, called for a key that is no longer referenced.
+
 * @free_value: If non-NULL, called for a value that is no longer referenced.
+
 *
+
 * Initializes a statically allocated hashtable object. The object
+
 * should be cleared with hashtable_close when it's no longer used.
+
 *
+
 * Returns 0 on success, -1 on error (out of memory).
+
 */
+
int hashtable_init(hashtable_t *hashtable,
+
                   key_hash_fn hash_key, key_cmp_fn cmp_keys,
+
                   free_fn free_key, free_fn free_value);
+

+
/**
+
 * hashtable_close - Release all resources used by a hashtable object
+
 *
+
 * @hashtable: The hashtable
+
 *
+
 * Destroys a statically allocated hashtable object.
+
 */
+
void hashtable_close(hashtable_t *hashtable);
+

+
/**
+
 * hashtable_set - Add/modify value in hashtable
+
 *
+
 * @hashtable: The hashtable object
+
 * @key: The key
+
 * @value: The value
+
 *
+
 * If a value with the given key already exists, its value is replaced
+
 * with the new value.
+
 *
+
 * Key and value are "stealed" in the sense that hashtable frees them
+
 * automatically when they are no longer used. The freeing is
+
 * accomplished by calling free_key and free_value functions that were
+
 * supplied to hashtable_new. In case one or both of the free
+
 * functions is NULL, the corresponding item is not "stealed".
+
 *
+
 * Returns 0 on success, -1 on failure (out of memory).
+
 */
+
int hashtable_set(hashtable_t *hashtable, void *key, void *value);
+

+
/**
+
 * hashtable_get - Get a value associated with a key
+
 *
+
 * @hashtable: The hashtable object
+
 * @key: The key
+
 *
+
 * Returns value if it is found, or NULL otherwise.
+
 */
+
void *hashtable_get(hashtable_t *hashtable, const void *key);
+

+
/**
+
 * hashtable_del - Remove a value from the hashtable
+
 *
+
 * @hashtable: The hashtable object
+
 * @key: The key
+
 *
+
 * Returns 0 on success, or -1 if the key was not found.
+
 */
+
int hashtable_del(hashtable_t *hashtable, const void *key);
+

+
/**
+
 * hashtable_clear - Clear hashtable
+
 *
+
 * @hashtable: The hashtable object
+
 *
+
 * Removes all items from the hashtable.
+
 */
+
void hashtable_clear(hashtable_t *hashtable);
+

+
/**
+
 * hashtable_iter - Iterate over hashtable
+
 *
+
 * @hashtable: The hashtable object
+
 *
+
 * Returns an opaque iterator to the first element in the hashtable.
+
 * The iterator should be passed to hashtable_iter_* functions.
+
 * The hashtable items are not iterated over in any particular order.
+
 *
+
 * There's no need to free the iterator in any way. The iterator is
+
 * valid as long as the item that is referenced by the iterator is not
+
 * deleted. Other values may be added or deleted. In particular,
+
 * hashtable_iter_next() may be called on an iterator, and after that
+
 * the key/value pair pointed by the old iterator may be deleted.
+
 */
+
void *hashtable_iter(hashtable_t *hashtable);
+

+
/**
+
 * hashtable_iter_at - Return an iterator at a specific key
+
 *
+
 * @hashtable: The hashtable object
+
 * @key: The key that the iterator should point to
+
 *
+
 * Like hashtable_iter() but returns an iterator pointing to a
+
 * specific key.
+
 */
+
void *hashtable_iter_at(hashtable_t *hashtable, const void *key);
+

+
/**
+
 * hashtable_iter_next - Advance an iterator
+
 *
+
 * @hashtable: The hashtable object
+
 * @iter: The iterator
+
 *
+
 * Returns a new iterator pointing to the next element in the
+
 * hashtable or NULL if the whole hastable has been iterated over.
+
 */
+
void *hashtable_iter_next(hashtable_t *hashtable, void *iter);
+

+
/**
+
 * hashtable_iter_key - Retrieve the key pointed by an iterator
+
 *
+
 * @iter: The iterator
+
 */
+
void *hashtable_iter_key(void *iter);
+

+
/**
+
 * hashtable_iter_value - Retrieve the value pointed by an iterator
+
 *
+
 * @iter: The iterator
+
 */
+
void *hashtable_iter_value(void *iter);
+

+
/**
+
 * hashtable_iter_set - Set the value pointed by an iterator
+
 *
+
 * @iter: The iterator
+
 * @value: The value to set
+
 */
+
void hashtable_iter_set(hashtable_t *hashtable, void *iter, void *value);
+

+
#endif
added external/jansson/jansson.h
@@ -0,0 +1,191 @@
+
/*
+
 * Copyright (c) 2009, 2010 Petri Lehtinen <petri@digip.org>
+
 *
+
 * Jansson is free software; you can redistribute it and/or modify
+
 * it under the terms of the MIT license. See LICENSE for details.
+
 */
+

+
#ifndef JANSSON_H
+
#define JANSSON_H
+

+
#include <stdio.h>
+

+
#ifndef __cplusplus
+
#define JSON_INLINE inline
+
#else
+
#define JSON_INLINE inline
+
extern "C" {
+
#endif
+

+
/* types */
+

+
typedef enum {
+
    JSON_OBJECT,
+
    JSON_ARRAY,
+
    JSON_STRING,
+
    JSON_INTEGER,
+
    JSON_REAL,
+
    JSON_TRUE,
+
    JSON_FALSE,
+
    JSON_NULL
+
} json_type;
+

+
typedef struct {
+
    json_type type;
+
    unsigned long refcount;
+
} json_t;
+

+
#define json_typeof(json)      ((json)->type)
+
#define json_is_object(json)   (json && json_typeof(json) == JSON_OBJECT)
+
#define json_is_array(json)    (json && json_typeof(json) == JSON_ARRAY)
+
#define json_is_string(json)   (json && json_typeof(json) == JSON_STRING)
+
#define json_is_integer(json)  (json && json_typeof(json) == JSON_INTEGER)
+
#define json_is_real(json)     (json && json_typeof(json) == JSON_REAL)
+
#define json_is_number(json)   (json_is_integer(json) || json_is_real(json))
+
#define json_is_true(json)     (json && json_typeof(json) == JSON_TRUE)
+
#define json_is_false(json)    (json && json_typeof(json) == JSON_FALSE)
+
#define json_is_boolean(json)  (json_is_true(json) || json_is_false(json))
+
#define json_is_null(json)     (json && json_typeof(json) == JSON_NULL)
+

+
/* construction, destruction, reference counting */
+

+
json_t *json_object(void);
+
json_t *json_array(void);
+
json_t *json_string(const char *value);
+
json_t *json_string_nocheck(const char *value);
+
json_t *json_integer(int value);
+
json_t *json_real(double value);
+
json_t *json_true(void);
+
json_t *json_false(void);
+
json_t *json_null(void);
+

+
static JSON_INLINE
+
json_t *json_incref(json_t *json)
+
{
+
    if(json && json->refcount != (unsigned int)-1)
+
        ++json->refcount;
+
    return json;
+
}
+

+
/* do not call json_delete directly */
+
void json_delete(json_t *json);
+

+
static JSON_INLINE
+
void json_decref(json_t *json)
+
{
+
    if(json && json->refcount != (unsigned int)-1 && --json->refcount == 0)
+
        json_delete(json);
+
}
+

+

+
/* getters, setters, manipulation */
+

+
unsigned int json_object_size(const json_t *object);
+
json_t *json_object_get(const json_t *object, const char *key);
+
int json_object_set_new(json_t *object, const char *key, json_t *value);
+
int json_object_set_new_nocheck(json_t *object, const char *key, json_t *value);
+
int json_object_del(json_t *object, const char *key);
+
int json_object_clear(json_t *object);
+
int json_object_update(json_t *object, json_t *other);
+
void *json_object_iter(json_t *object);
+
void *json_object_iter_at(json_t *object, const char *key);
+
void *json_object_iter_next(json_t *object, void *iter);
+
const char *json_object_iter_key(void *iter);
+
json_t *json_object_iter_value(void *iter);
+
int json_object_iter_set_new(json_t *object, void *iter, json_t *value);
+

+
static JSON_INLINE
+
int json_object_set(json_t *object, const char *key, json_t *value)
+
{
+
    return json_object_set_new(object, key, json_incref(value));
+
}
+

+
static JSON_INLINE
+
int json_object_set_nocheck(json_t *object, const char *key, json_t *value)
+
{
+
    return json_object_set_new_nocheck(object, key, json_incref(value));
+
}
+

+
static inline
+
int json_object_iter_set(json_t *object, void *iter, json_t *value)
+
{
+
    return json_object_iter_set_new(object, iter, json_incref(value));
+
}
+

+
unsigned int json_array_size(const json_t *array);
+
json_t *json_array_get(const json_t *array, unsigned int index);
+
int json_array_set_new(json_t *array, unsigned int index, json_t *value);
+
int json_array_append_new(json_t *array, json_t *value);
+
int json_array_insert_new(json_t *array, unsigned int index, json_t *value);
+
int json_array_remove(json_t *array, unsigned int index);
+
int json_array_clear(json_t *array);
+
int json_array_extend(json_t *array, json_t *other);
+

+
static JSON_INLINE
+
int json_array_set(json_t *array, unsigned int index, json_t *value)
+
{
+
    return json_array_set_new(array, index, json_incref(value));
+
}
+

+
static JSON_INLINE
+
int json_array_append(json_t *array, json_t *value)
+
{
+
    return json_array_append_new(array, json_incref(value));
+
}
+

+
static JSON_INLINE
+
int json_array_insert(json_t *array, unsigned int index, json_t *value)
+
{
+
    return json_array_insert_new(array, index, json_incref(value));
+
}
+

+
const char *json_string_value(const json_t *string);
+
int json_integer_value(const json_t *integer);
+
double json_real_value(const json_t *real);
+
double json_number_value(const json_t *json);
+

+
int json_string_set(json_t *string, const char *value);
+
int json_string_set_nocheck(json_t *string, const char *value);
+
int json_integer_set(json_t *integer, int value);
+
int json_real_set(json_t *real, double value);
+

+

+
/* equality */
+

+
int json_equal(json_t *value1, json_t *value2);
+

+

+
/* copying */
+

+
json_t *json_copy(json_t *value);
+
json_t *json_deep_copy(json_t *value);
+

+

+
/* loading, printing */
+

+
#define JSON_ERROR_TEXT_LENGTH  160
+

+
typedef struct {
+
    char text[JSON_ERROR_TEXT_LENGTH];
+
    int line;
+
} json_error_t;
+

+
json_t *json_loads(const char *input, json_error_t *error);
+
json_t *json_loadf(FILE *input, json_error_t *error);
+
json_t *json_load_file(const char *path, json_error_t *error);
+

+
#define JSON_INDENT(n)      (n & 0xFF)
+
#define JSON_COMPACT        0x100
+
#define JSON_ENSURE_ASCII   0x200
+
#define JSON_SORT_KEYS      0x400
+
#define JSON_PRESERVE_ORDER 0x800
+

+
char *json_dumps(const json_t *json, unsigned long flags);
+
int json_dumpf(const json_t *json, FILE *output, unsigned long flags);
+
int json_dump_file(const json_t *json, const char *path, unsigned long flags);
+

+
#ifdef __cplusplus
+
}
+
#endif
+

+
#endif
added external/jansson/jansson_private.h
@@ -0,0 +1,60 @@
+
/*
+
 * Copyright (c) 2009, 2010 Petri Lehtinen <petri@digip.org>
+
 *
+
 * Jansson is free software; you can redistribute it and/or modify
+
 * it under the terms of the MIT license. See LICENSE for details.
+
 */
+

+
#ifndef JANSSON_PRIVATE_H
+
#define JANSSON_PRIVATE_H
+

+
#include "jansson.h"
+
#include "hashtable.h"
+

+
#define container_of(ptr_, type_, member_)  \
+
    ((type_ *)((char *)ptr_ - (size_t)&((type_ *)0)->member_))
+

+
typedef struct {
+
    json_t json;
+
    hashtable_t hashtable;
+
    unsigned long serial;
+
    int visited;
+
} json_object_t;
+

+
typedef struct {
+
    json_t json;
+
    unsigned int size;
+
    unsigned int entries;
+
    json_t **table;
+
    int visited;
+
} json_array_t;
+

+
typedef struct {
+
    json_t json;
+
    char *value;
+
} json_string_t;
+

+
typedef struct {
+
    json_t json;
+
    double value;
+
} json_real_t;
+

+
typedef struct {
+
    json_t json;
+
    int value;
+
} json_integer_t;
+

+
#define json_to_object(json_)  container_of(json_, json_object_t, json)
+
#define json_to_array(json_)   container_of(json_, json_array_t, json)
+
#define json_to_string(json_)  container_of(json_, json_string_t, json)
+
#define json_to_real(json_)   container_of(json_, json_real_t, json)
+
#define json_to_integer(json_) container_of(json_, json_integer_t, json)
+

+
typedef struct {
+
    unsigned long serial;
+
    char key[];
+
} object_key_t;
+

+
const object_key_t *jsonp_object_iter_fullkey(void *iter);
+

+
#endif
added external/jansson/load.c
@@ -0,0 +1,879 @@
+
/*
+
 * Copyright (c) 2009, 2010 Petri Lehtinen <petri@digip.org>
+
 *
+
 * Jansson is free software; you can redistribute it and/or modify
+
 * it under the terms of the MIT license. See LICENSE for details.
+
 */
+

+
#define _GNU_SOURCE
+
#include <ctype.h>
+
#include <errno.h>
+
#include <limits.h>
+
#include <stdio.h>
+
#include <stdlib.h>
+
#include <string.h>
+
#include <stdarg.h>
+
#include <assert.h>
+

+
#include <jansson.h>
+
#include "jansson_private.h"
+
#include "strbuffer.h"
+
#include "utf.h"
+

+
#define TOKEN_INVALID         -1
+
#define TOKEN_EOF              0
+
#define TOKEN_STRING         256
+
#define TOKEN_INTEGER        257
+
#define TOKEN_REAL           258
+
#define TOKEN_TRUE           259
+
#define TOKEN_FALSE          260
+
#define TOKEN_NULL           261
+

+
/* read one byte from stream, return EOF on end of file */
+
typedef int (*get_func)(void *data);
+

+
/* return non-zero if end of file has been reached */
+
typedef int (*eof_func)(void *data);
+

+
typedef struct {
+
    get_func get;
+
    eof_func eof;
+
    void *data;
+
    int stream_pos;
+
    char buffer[5];
+
    int buffer_pos;
+
} stream_t;
+

+

+
typedef struct {
+
    stream_t stream;
+
    strbuffer_t saved_text;
+
    int token;
+
    int line, column;
+
    union {
+
        char *string;
+
        int integer;
+
        double real;
+
    } value;
+
} lex_t;
+

+

+
/*** error reporting ***/
+

+
static void error_init(json_error_t *error)
+
{
+
    if(error)
+
    {
+
        error->text[0] = '\0';
+
        error->line = -1;
+
    }
+
}
+

+
static void error_set(json_error_t *error, const lex_t *lex,
+
                      const char *msg, ...)
+
{
+
    va_list ap;
+
    char text[JSON_ERROR_TEXT_LENGTH];
+

+
    if(!error || error->text[0] != '\0') {
+
        /* error already set */
+
        return;
+
    }
+

+
    va_start(ap, msg);
+
    vsnprintf(text, JSON_ERROR_TEXT_LENGTH, msg, ap);
+
    va_end(ap);
+

+
    if(lex)
+
    {
+
        const char *saved_text = strbuffer_value(&lex->saved_text);
+
        error->line = lex->line;
+
        if(saved_text && saved_text[0])
+
        {
+
            if(lex->saved_text.length <= 20) {
+
                snprintf(error->text, JSON_ERROR_TEXT_LENGTH,
+
                         "%s near '%s'", text, saved_text);
+
            }
+
            else
+
                snprintf(error->text, JSON_ERROR_TEXT_LENGTH, "%s", text);
+
        }
+
        else
+
        {
+
            snprintf(error->text, JSON_ERROR_TEXT_LENGTH,
+
                     "%s near end of file", text);
+
        }
+
    }
+
    else
+
    {
+
        error->line = -1;
+
        snprintf(error->text, JSON_ERROR_TEXT_LENGTH, "%s", text);
+
    }
+
}
+

+

+
/*** lexical analyzer ***/
+

+
static void
+
stream_init(stream_t *stream, get_func get, eof_func eof, void *data)
+
{
+
    stream->get = get;
+
    stream->eof = eof;
+
    stream->data = data;
+
    stream->stream_pos = 0;
+
    stream->buffer[0] = '\0';
+
    stream->buffer_pos = 0;
+
}
+

+
static char stream_get(stream_t *stream, json_error_t *error)
+
{
+
    char c;
+

+
    if(!stream->buffer[stream->buffer_pos])
+
    {
+
        stream->buffer[0] = stream->get(stream->data);
+
        stream->buffer_pos = 0;
+

+
        c = stream->buffer[0];
+

+
        if((unsigned char)c >= 0x80 && c != (char)EOF)
+
        {
+
            /* multi-byte UTF-8 sequence */
+
            int i, count;
+

+
            count = utf8_check_first(c);
+
            if(!count)
+
                goto out;
+

+
            assert(count >= 2);
+

+
            for(i = 1; i < count; i++)
+
                stream->buffer[i] = stream->get(stream->data);
+

+
            if(!utf8_check_full(stream->buffer, count, NULL))
+
                goto out;
+

+
            stream->stream_pos += count;
+
            stream->buffer[count] = '\0';
+
        }
+
        else {
+
            stream->buffer[1] = '\0';
+
            stream->stream_pos++;
+
        }
+
    }
+

+
    return stream->buffer[stream->buffer_pos++];
+

+
out:
+
    error_set(error, NULL, "unable to decode byte 0x%x at position %d",
+
              (unsigned char)c, stream->stream_pos);
+

+
    stream->buffer[0] = EOF;
+
    stream->buffer[1] = '\0';
+
    stream->buffer_pos = 1;
+

+
    return EOF;
+
}
+

+
static void stream_unget(stream_t *stream, char c)
+
{
+
    assert(stream->buffer_pos > 0);
+
    stream->buffer_pos--;
+
    assert(stream->buffer[stream->buffer_pos] == c);
+
}
+

+

+
static int lex_get(lex_t *lex, json_error_t *error)
+
{
+
    return stream_get(&lex->stream, error);
+
}
+

+
static int lex_eof(lex_t *lex)
+
{
+
    return lex->stream.eof(lex->stream.data);
+
}
+

+
static void lex_save(lex_t *lex, char c)
+
{
+
    strbuffer_append_byte(&lex->saved_text, c);
+
}
+

+
static int lex_get_save(lex_t *lex, json_error_t *error)
+
{
+
    char c = stream_get(&lex->stream, error);
+
    lex_save(lex, c);
+
    return c;
+
}
+

+
static void lex_unget_unsave(lex_t *lex, char c)
+
{
+
    char d;
+
    stream_unget(&lex->stream, c);
+
    d = strbuffer_pop(&lex->saved_text);
+
    assert(c == d);
+
}
+

+
static void lex_save_cached(lex_t *lex)
+
{
+
    while(lex->stream.buffer[lex->stream.buffer_pos] != '\0')
+
    {
+
        lex_save(lex, lex->stream.buffer[lex->stream.buffer_pos]);
+
        lex->stream.buffer_pos++;
+
    }
+
}
+

+
/* assumes that str points to 'u' plus at least 4 valid hex digits */
+
static int32_t decode_unicode_escape(const char *str)
+
{
+
    int i;
+
    int32_t value = 0;
+

+
    assert(str[0] == 'u');
+

+
    for(i = 1; i <= 4; i++) {
+
        char c = str[i];
+
        value <<= 4;
+
        if(isdigit(c))
+
            value += c - '0';
+
        else if(islower(c))
+
            value += c - 'a' + 10;
+
        else if(isupper(c))
+
            value += c - 'A' + 10;
+
        else
+
            assert(0);
+
    }
+

+
    return value;
+
}
+

+
static void lex_scan_string(lex_t *lex, json_error_t *error)
+
{
+
    char c;
+
    const char *p;
+
    char *t;
+
    int i;
+

+
    lex->value.string = NULL;
+
    lex->token = TOKEN_INVALID;
+

+
    c = lex_get_save(lex, error);
+

+
    while(c != '"') {
+
        if(c == (char)EOF) {
+
            lex_unget_unsave(lex, c);
+
            if(lex_eof(lex))
+
                error_set(error, lex, "premature end of input");
+
            goto out;
+
        }
+

+
        else if((unsigned char)c <= 0x1F) {
+
            /* control character */
+
            lex_unget_unsave(lex, c);
+
            if(c == '\n')
+
                error_set(error, lex, "unexpected newline", c);
+
            else
+
                error_set(error, lex, "control character 0x%x", c);
+
            goto out;
+
        }
+

+
        else if(c == '\\') {
+
            c = lex_get_save(lex, error);
+
            if(c == 'u') {
+
                c = lex_get_save(lex, error);
+
                for(i = 0; i < 4; i++) {
+
                    if(!isxdigit(c)) {
+
                        lex_unget_unsave(lex, c);
+
                        error_set(error, lex, "invalid escape");
+
                        goto out;
+
                    }
+
                    c = lex_get_save(lex, error);
+
                }
+
            }
+
            else if(c == '"' || c == '\\' || c == '/' || c == 'b' ||
+
                    c == 'f' || c == 'n' || c == 'r' || c == 't')
+
                c = lex_get_save(lex, error);
+
            else {
+
                lex_unget_unsave(lex, c);
+
                error_set(error, lex, "invalid escape");
+
                goto out;
+
            }
+
        }
+
        else
+
            c = lex_get_save(lex, error);
+
    }
+

+
    /* the actual value is at most of the same length as the source
+
       string, because:
+
         - shortcut escapes (e.g. "\t") (length 2) are converted to 1 byte
+
         - a single \uXXXX escape (length 6) is converted to at most 3 bytes
+
         - two \uXXXX escapes (length 12) forming an UTF-16 surrogate pair
+
           are converted to 4 bytes
+
    */
+
    lex->value.string = malloc(lex->saved_text.length + 1);
+
    if(!lex->value.string) {
+
        /* this is not very nice, since TOKEN_INVALID is returned */
+
        goto out;
+
    }
+

+
    /* the target */
+
    t = lex->value.string;
+

+
    /* + 1 to skip the " */
+
    p = strbuffer_value(&lex->saved_text) + 1;
+

+
    while(*p != '"') {
+
        if(*p == '\\') {
+
            p++;
+
            if(*p == 'u') {
+
                char buffer[4];
+
                int length;
+
                int32_t value;
+

+
                value = decode_unicode_escape(p);
+
                p += 5;
+

+
                if(0xD800 <= value && value <= 0xDBFF) {
+
                    /* surrogate pair */
+
                    if(*p == '\\' && *(p + 1) == 'u') {
+
                        int32_t value2 = decode_unicode_escape(++p);
+
                        p += 5;
+

+
                        if(0xDC00 <= value2 && value2 <= 0xDFFF) {
+
                            /* valid second surrogate */
+
                            value =
+
                                ((value - 0xD800) << 10) +
+
                                (value2 - 0xDC00) +
+
                                0x10000;
+
                        }
+
                        else {
+
                            /* invalid second surrogate */
+
                            error_set(error, lex,
+
                                      "invalid Unicode '\\u%04X\\u%04X'",
+
                                      value, value2);
+
                            goto out;
+
                        }
+
                    }
+
                    else {
+
                        /* no second surrogate */
+
                        error_set(error, lex, "invalid Unicode '\\u%04X'",
+
                                  value);
+
                        goto out;
+
                    }
+
                }
+
                else if(0xDC00 <= value && value <= 0xDFFF) {
+
                    error_set(error, lex, "invalid Unicode '\\u%04X'", value);
+
                    goto out;
+
                }
+
                else if(value == 0)
+
                {
+
                    error_set(error, lex, "\\u0000 is not allowed");
+
                    goto out;
+
                }
+

+
                if(utf8_encode(value, buffer, &length))
+
                    assert(0);
+

+
                memcpy(t, buffer, length);
+
                t += length;
+
            }
+
            else {
+
                switch(*p) {
+
                    case '"': case '\\': case '/':
+
                        *t = *p; break;
+
                    case 'b': *t = '\b'; break;
+
                    case 'f': *t = '\f'; break;
+
                    case 'n': *t = '\n'; break;
+
                    case 'r': *t = '\r'; break;
+
                    case 't': *t = '\t'; break;
+
                    default: assert(0);
+
                }
+
                t++;
+
                p++;
+
            }
+
        }
+
        else
+
            *(t++) = *(p++);
+
    }
+
    *t = '\0';
+
    lex->token = TOKEN_STRING;
+
    return;
+

+
out:
+
    free(lex->value.string);
+
}
+

+
static int lex_scan_number(lex_t *lex, char c, json_error_t *error)
+
{
+
    const char *saved_text;
+
    char *end;
+
    double value;
+

+
    lex->token = TOKEN_INVALID;
+

+
    if(c == '-')
+
        c = lex_get_save(lex, error);
+

+
    if(c == '0') {
+
        c = lex_get_save(lex, error);
+
        if(isdigit(c)) {
+
            lex_unget_unsave(lex, c);
+
            goto out;
+
        }
+
    }
+
    else if(isdigit(c)) {
+
        c = lex_get_save(lex, error);
+
        while(isdigit(c))
+
            c = lex_get_save(lex, error);
+
    }
+
    else {
+
      lex_unget_unsave(lex, c);
+
      goto out;
+
    }
+

+
    if(c != '.' && c != 'E' && c != 'e') {
+
        long value;
+

+
        lex_unget_unsave(lex, c);
+

+
        saved_text = strbuffer_value(&lex->saved_text);
+
        value = strtol(saved_text, &end, 10);
+
        assert(end == saved_text + lex->saved_text.length);
+

+
        if((value == LONG_MAX && errno == ERANGE) || value > INT_MAX) {
+
            error_set(error, lex, "too big integer");
+
            goto out;
+
        }
+
        else if((value == LONG_MIN && errno == ERANGE) || value < INT_MIN) {
+
            error_set(error, lex, "too big negative integer");
+
            goto out;
+
        }
+

+
        lex->token = TOKEN_INTEGER;
+
        lex->value.integer = (int)value;
+
        return 0;
+
    }
+

+
    if(c == '.') {
+
        c = lex_get(lex, error);
+
        if(!isdigit(c))
+
            goto out;
+
        lex_save(lex, c);
+

+
        c = lex_get_save(lex, error);
+
        while(isdigit(c))
+
            c = lex_get_save(lex, error);
+
    }
+

+
    if(c == 'E' || c == 'e') {
+
        c = lex_get_save(lex, error);
+
        if(c == '+' || c == '-')
+
            c = lex_get_save(lex, error);
+

+
        if(!isdigit(c)) {
+
            lex_unget_unsave(lex, c);
+
            goto out;
+
        }
+

+
        c = lex_get_save(lex, error);
+
        while(isdigit(c))
+
            c = lex_get_save(lex, error);
+
    }
+

+
    lex_unget_unsave(lex, c);
+

+
    saved_text = strbuffer_value(&lex->saved_text);
+
    value = strtod(saved_text, &end);
+
    assert(end == saved_text + lex->saved_text.length);
+

+
    if(errno == ERANGE && value != 0) {
+
        error_set(error, lex, "real number overflow");
+
        goto out;
+
    }
+

+
    lex->token = TOKEN_REAL;
+
    lex->value.real = value;
+
    return 0;
+

+
out:
+
    return -1;
+
}
+

+
static int lex_scan(lex_t *lex, json_error_t *error)
+
{
+
    char c;
+

+
    strbuffer_clear(&lex->saved_text);
+

+
    if(lex->token == TOKEN_STRING) {
+
        free(lex->value.string);
+
        lex->value.string = NULL;
+
    }
+

+
    c = lex_get(lex, error);
+
    while(c == ' ' || c == '\t' || c == '\n' || c == '\r')
+
    {
+
        if(c == '\n')
+
            lex->line++;
+

+
        c = lex_get(lex, error);
+
    }
+

+
    if(c == (char)EOF) {
+
        if(lex_eof(lex))
+
            lex->token = TOKEN_EOF;
+
        else
+
            lex->token = TOKEN_INVALID;
+
        goto out;
+
    }
+

+
    lex_save(lex, c);
+

+
    if(c == '{' || c == '}' || c == '[' || c == ']' || c == ':' || c == ',')
+
        lex->token = c;
+

+
    else if(c == '"')
+
        lex_scan_string(lex, error);
+

+
    else if(isdigit(c) || c == '-') {
+
        if(lex_scan_number(lex, c, error))
+
            goto out;
+
    }
+

+
    else if(isupper(c) || islower(c)) {
+
        /* eat up the whole identifier for clearer error messages */
+
        const char *saved_text;
+

+
        c = lex_get_save(lex, error);
+
        while(isupper(c) || islower(c))
+
            c = lex_get_save(lex, error);
+
        lex_unget_unsave(lex, c);
+

+
        saved_text = strbuffer_value(&lex->saved_text);
+

+
        if(strcmp(saved_text, "true") == 0)
+
            lex->token = TOKEN_TRUE;
+
        else if(strcmp(saved_text, "false") == 0)
+
            lex->token = TOKEN_FALSE;
+
        else if(strcmp(saved_text, "null") == 0)
+
            lex->token = TOKEN_NULL;
+
        else
+
            lex->token = TOKEN_INVALID;
+
    }
+

+
    else {
+
        /* save the rest of the input UTF-8 sequence to get an error
+
           message of valid UTF-8 */
+
        lex_save_cached(lex);
+
        lex->token = TOKEN_INVALID;
+
    }
+

+
out:
+
    return lex->token;
+
}
+

+
static char *lex_steal_string(lex_t *lex)
+
{
+
    char *result = NULL;
+
    if(lex->token == TOKEN_STRING)
+
    {
+
        result = lex->value.string;
+
        lex->value.string = NULL;
+
    }
+
    return result;
+
}
+

+
static int lex_init(lex_t *lex, get_func get, eof_func eof, void *data)
+
{
+
    stream_init(&lex->stream, get, eof, data);
+
    if(strbuffer_init(&lex->saved_text))
+
        return -1;
+

+
    lex->token = TOKEN_INVALID;
+
    lex->line = 1;
+

+
    return 0;
+
}
+

+
static void lex_close(lex_t *lex)
+
{
+
    if(lex->token == TOKEN_STRING)
+
        free(lex->value.string);
+
    strbuffer_close(&lex->saved_text);
+
}
+

+

+
/*** parser ***/
+

+
static json_t *parse_value(lex_t *lex, json_error_t *error);
+

+
static json_t *parse_object(lex_t *lex, json_error_t *error)
+
{
+
    json_t *object = json_object();
+
    if(!object)
+
        return NULL;
+

+
    lex_scan(lex, error);
+
    if(lex->token == '}')
+
        return object;
+

+
    while(1) {
+
        char *key;
+
        json_t *value;
+

+
        if(lex->token != TOKEN_STRING) {
+
            error_set(error, lex, "string or '}' expected");
+
            goto error;
+
        }
+

+
        key = lex_steal_string(lex);
+
        if(!key)
+
            return NULL;
+

+
        lex_scan(lex, error);
+
        if(lex->token != ':') {
+
            free(key);
+
            error_set(error, lex, "':' expected");
+
            goto error;
+
        }
+

+
        lex_scan(lex, error);
+
        value = parse_value(lex, error);
+
        if(!value) {
+
            free(key);
+
            goto error;
+
        }
+

+
        if(json_object_set_nocheck(object, key, value)) {
+
            free(key);
+
            json_decref(value);
+
            goto error;
+
        }
+

+
        json_decref(value);
+
        free(key);
+

+
        lex_scan(lex, error);
+
        if(lex->token != ',')
+
            break;
+

+
        lex_scan(lex, error);
+
    }
+

+
    if(lex->token != '}') {
+
        error_set(error, lex, "'}' expected");
+
        goto error;
+
    }
+

+
    return object;
+

+
error:
+
    json_decref(object);
+
    return NULL;
+
}
+

+
static json_t *parse_array(lex_t *lex, json_error_t *error)
+
{
+
    json_t *array = json_array();
+
    if(!array)
+
        return NULL;
+

+
    lex_scan(lex, error);
+
    if(lex->token == ']')
+
        return array;
+

+
    while(lex->token) {
+
        json_t *elem = parse_value(lex, error);
+
        if(!elem)
+
            goto error;
+

+
        if(json_array_append(array, elem)) {
+
            json_decref(elem);
+
            goto error;
+
        }
+
        json_decref(elem);
+

+
        lex_scan(lex, error);
+
        if(lex->token != ',')
+
            break;
+

+
        lex_scan(lex, error);
+
    }
+

+
    if(lex->token != ']') {
+
        error_set(error, lex, "']' expected");
+
        goto error;
+
    }
+

+
    return array;
+

+
error:
+
    json_decref(array);
+
    return NULL;
+
}
+

+
static json_t *parse_value(lex_t *lex, json_error_t *error)
+
{
+
    json_t *json;
+

+
    switch(lex->token) {
+
        case TOKEN_STRING: {
+
            json = json_string_nocheck(lex->value.string);
+
            break;
+
        }
+

+
        case TOKEN_INTEGER: {
+
            json = json_integer(lex->value.integer);
+
            break;
+
        }
+

+
        case TOKEN_REAL: {
+
            json = json_real(lex->value.real);
+
            break;
+
        }
+

+
        case TOKEN_TRUE:
+
            json = json_true();
+
            break;
+

+
        case TOKEN_FALSE:
+
            json = json_false();
+
            break;
+

+
        case TOKEN_NULL:
+
            json = json_null();
+
            break;
+

+
        case '{':
+
            json = parse_object(lex, error);
+
            break;
+

+
        case '[':
+
            json = parse_array(lex, error);
+
            break;
+

+
        case TOKEN_INVALID:
+
            error_set(error, lex, "invalid token");
+
            return NULL;
+

+
        default:
+
            error_set(error, lex, "unexpected token");
+
            return NULL;
+
    }
+

+
    if(!json)
+
        return NULL;
+

+
    return json;
+
}
+

+
static json_t *parse_json(lex_t *lex, json_error_t *error)
+
{
+
    error_init(error);
+

+
    lex_scan(lex, error);
+
    if(lex->token != '[' && lex->token != '{') {
+
        error_set(error, lex, "'[' or '{' expected");
+
        return NULL;
+
    }
+

+
    return parse_value(lex, error);
+
}
+

+
typedef struct
+
{
+
    const char *data;
+
    int pos;
+
} string_data_t;
+

+
static int string_get(void *data)
+
{
+
    char c;
+
    string_data_t *stream = (string_data_t *)data;
+
    c = stream->data[stream->pos];
+
    if(c == '\0')
+
        return EOF;
+
    else
+
    {
+
        stream->pos++;
+
        return c;
+
    }
+
}
+

+
static int string_eof(void *data)
+
{
+
    string_data_t *stream = (string_data_t *)data;
+
    return (stream->data[stream->pos] == '\0');
+
}
+

+
json_t *json_loads(const char *string, json_error_t *error)
+
{
+
    lex_t lex;
+
    json_t *result;
+

+
    string_data_t stream_data = {
+
        .data = string,
+
        .pos = 0
+
    };
+

+
    if(lex_init(&lex, string_get, string_eof, (void *)&stream_data))
+
        return NULL;
+

+
    result = parse_json(&lex, error);
+
    if(!result)
+
        goto out;
+

+
    lex_scan(&lex, error);
+
    if(lex.token != TOKEN_EOF) {
+
        error_set(error, &lex, "end of file expected");
+
        json_decref(result);
+
        result = NULL;
+
    }
+

+
out:
+
    lex_close(&lex);
+
    return result;
+
}
+

+
json_t *json_loadf(FILE *input, json_error_t *error)
+
{
+
    lex_t lex;
+
    json_t *result;
+

+
    if(lex_init(&lex, (get_func)fgetc, (eof_func)feof, input))
+
        return NULL;
+

+
    result = parse_json(&lex, error);
+
    if(!result)
+
        goto out;
+

+
    lex_scan(&lex, error);
+
    if(lex.token != TOKEN_EOF) {
+
        error_set(error, &lex, "end of file expected");
+
        json_decref(result);
+
        result = NULL;
+
    }
+

+
out:
+
    lex_close(&lex);
+
    return result;
+
}
+

+
json_t *json_load_file(const char *path, json_error_t *error)
+
{
+
    json_t *result;
+
    FILE *fp;
+

+
    error_init(error);
+

+
    fp = fopen(path, "r");
+
    if(!fp)
+
    {
+
        error_set(error, NULL, "unable to open %s: %s",
+
                  path, strerror(errno));
+
        return NULL;
+
    }
+

+
    result = json_loadf(fp, error);
+

+
    fclose(fp);
+
    return result;
+
}
added external/jansson/strbuffer.c
@@ -0,0 +1,95 @@
+
/*
+
 * Copyright (c) 2009, 2010 Petri Lehtinen <petri@digip.org>
+
 *
+
 * Jansson is free software; you can redistribute it and/or modify
+
 * it under the terms of the MIT license. See LICENSE for details.
+
 */
+

+
#define _GNU_SOURCE
+
#include <stdlib.h>
+
#include <string.h>
+
#include "strbuffer.h"
+
#include "util.h"
+

+
#define STRBUFFER_MIN_SIZE  16
+
#define STRBUFFER_FACTOR    2
+

+
int strbuffer_init(strbuffer_t *strbuff)
+
{
+
    strbuff->size = STRBUFFER_MIN_SIZE;
+
    strbuff->length = 0;
+

+
    strbuff->value = malloc(strbuff->size);
+
    if(!strbuff->value)
+
        return -1;
+

+
    /* initialize to empty */
+
    strbuff->value[0] = '\0';
+
    return 0;
+
}
+

+
void strbuffer_close(strbuffer_t *strbuff)
+
{
+
    free(strbuff->value);
+
    strbuff->size = 0;
+
    strbuff->length = 0;
+
    strbuff->value = NULL;
+
}
+

+
void strbuffer_clear(strbuffer_t *strbuff)
+
{
+
    strbuff->length = 0;
+
    strbuff->value[0] = '\0';
+
}
+

+
const char *strbuffer_value(const strbuffer_t *strbuff)
+
{
+
    return strbuff->value;
+
}
+

+
char *strbuffer_steal_value(strbuffer_t *strbuff)
+
{
+
    char *result = strbuff->value;
+
    strbuffer_init(strbuff);
+
    return result;
+
}
+

+
int strbuffer_append(strbuffer_t *strbuff, const char *string)
+
{
+
    return strbuffer_append_bytes(strbuff, string, strlen(string));
+
}
+

+
int strbuffer_append_byte(strbuffer_t *strbuff, char byte)
+
{
+
    return strbuffer_append_bytes(strbuff, &byte, 1);
+
}
+

+
int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, int size)
+
{
+
    if(strbuff->length + size >= strbuff->size)
+
    {
+
        strbuff->size = max(strbuff->size * STRBUFFER_FACTOR,
+
                            strbuff->length + size + 1);
+

+
        strbuff->value = realloc(strbuff->value, strbuff->size);
+
        if(!strbuff->value)
+
            return -1;
+
    }
+

+
    memcpy(strbuff->value + strbuff->length, data, size);
+
    strbuff->length += size;
+
    strbuff->value[strbuff->length] = '\0';
+

+
    return 0;
+
}
+

+
char strbuffer_pop(strbuffer_t *strbuff)
+
{
+
    if(strbuff->length > 0) {
+
        char c = strbuff->value[--strbuff->length];
+
        strbuff->value[strbuff->length] = '\0';
+
        return c;
+
    }
+
    else
+
        return '\0';
+
}
added external/jansson/strbuffer.h
@@ -0,0 +1,31 @@
+
/*
+
 * Copyright (c) 2009, 2010 Petri Lehtinen <petri@digip.org>
+
 *
+
 * Jansson is free software; you can redistribute it and/or modify
+
 * it under the terms of the MIT license. See LICENSE for details.
+
 */
+

+
#ifndef STRBUFFER_H
+
#define STRBUFFER_H
+

+
typedef struct {
+
    char *value;
+
    int length;   /* bytes used */
+
    int size;     /* bytes allocated */
+
} strbuffer_t;
+

+
int strbuffer_init(strbuffer_t *strbuff);
+
void strbuffer_close(strbuffer_t *strbuff);
+

+
void strbuffer_clear(strbuffer_t *strbuff);
+

+
const char *strbuffer_value(const strbuffer_t *strbuff);
+
char *strbuffer_steal_value(strbuffer_t *strbuff);
+

+
int strbuffer_append(strbuffer_t *strbuff, const char *string);
+
int strbuffer_append_byte(strbuffer_t *strbuff, char byte);
+
int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, int size);
+

+
char strbuffer_pop(strbuffer_t *strbuff);
+

+
#endif
added external/jansson/utf.c
@@ -0,0 +1,190 @@
+
/*
+
 * Copyright (c) 2009, 2010 Petri Lehtinen <petri@digip.org>
+
 *
+
 * Jansson is free software; you can redistribute it and/or modify
+
 * it under the terms of the MIT license. See LICENSE for details.
+
 */
+

+
#include <string.h>
+
#include "utf.h"
+

+
int utf8_encode(int32_t codepoint, char *buffer, int *size)
+
{
+
    if(codepoint < 0)
+
        return -1;
+
    else if(codepoint < 0x80)
+
    {
+
        buffer[0] = (char)codepoint;
+
        *size = 1;
+
    }
+
    else if(codepoint < 0x800)
+
    {
+
        buffer[0] = 0xC0 + ((codepoint & 0x7C0) >> 6);
+
        buffer[1] = 0x80 + ((codepoint & 0x03F));
+
        *size = 2;
+
    }
+
    else if(codepoint < 0x10000)
+
    {
+
        buffer[0] = 0xE0 + ((codepoint & 0xF000) >> 12);
+
        buffer[1] = 0x80 + ((codepoint & 0x0FC0) >> 6);
+
        buffer[2] = 0x80 + ((codepoint & 0x003F));
+
        *size = 3;
+
    }
+
    else if(codepoint <= 0x10FFFF)
+
    {
+
        buffer[0] = 0xF0 + ((codepoint & 0x1C0000) >> 18);
+
        buffer[1] = 0x80 + ((codepoint & 0x03F000) >> 12);
+
        buffer[2] = 0x80 + ((codepoint & 0x000FC0) >> 6);
+
        buffer[3] = 0x80 + ((codepoint & 0x00003F));
+
        *size = 4;
+
    }
+
    else
+
        return -1;
+

+
    return 0;
+
}
+

+
int utf8_check_first(char byte)
+
{
+
    unsigned char u = (unsigned char)byte;
+

+
    if(u < 0x80)
+
        return 1;
+

+
    if(0x80 <= u && u <= 0xBF) {
+
        /* second, third or fourth byte of a multi-byte
+
           sequence, i.e. a "continuation byte" */
+
        return 0;
+
    }
+
    else if(u == 0xC0 || u == 0xC1) {
+
        /* overlong encoding of an ASCII byte */
+
        return 0;
+
    }
+
    else if(0xC2 <= u && u <= 0xDF) {
+
        /* 2-byte sequence */
+
        return 2;
+
    }
+

+
    else if(0xE0 <= u && u <= 0xEF) {
+
        /* 3-byte sequence */
+
        return 3;
+
    }
+
    else if(0xF0 <= u && u <= 0xF4) {
+
        /* 4-byte sequence */
+
        return 4;
+
    }
+
    else { /* u >= 0xF5 */
+
        /* Restricted (start of 4-, 5- or 6-byte sequence) or invalid
+
           UTF-8 */
+
        return 0;
+
    }
+
}
+

+
int utf8_check_full(const char *buffer, int size, int32_t *codepoint)
+
{
+
    int i;
+
    int32_t value = 0;
+
    unsigned char u = (unsigned char)buffer[0];
+

+
    if(size == 2)
+
    {
+
        value = u & 0x1F;
+
    }
+
    else if(size == 3)
+
    {
+
        value = u & 0xF;
+
    }
+
    else if(size == 4)
+
    {
+
        value = u & 0x7;
+
    }
+
    else
+
        return 0;
+

+
    for(i = 1; i < size; i++)
+
    {
+
        u = (unsigned char)buffer[i];
+

+
        if(u < 0x80 || u > 0xBF) {
+
            /* not a continuation byte */
+
            return 0;
+
        }
+

+
        value = (value << 6) + (u & 0x3F);
+
    }
+

+
    if(value > 0x10FFFF) {
+
        /* not in Unicode range */
+
        return 0;
+
    }
+

+
    else if(0xD800 <= value && value <= 0xDFFF) {
+
        /* invalid code point (UTF-16 surrogate halves) */
+
        return 0;
+
    }
+

+
    else if((size == 2 && value < 0x80) ||
+
            (size == 3 && value < 0x800) ||
+
            (size == 4 && value < 0x10000)) {
+
        /* overlong encoding */
+
        return 0;
+
    }
+

+
    if(codepoint)
+
        *codepoint = value;
+

+
    return 1;
+
}
+

+
const char *utf8_iterate(const char *buffer, int32_t *codepoint)
+
{
+
    int count;
+
    int32_t value;
+

+
    if(!*buffer)
+
        return buffer;
+

+
    count = utf8_check_first(buffer[0]);
+
    if(count <= 0)
+
        return NULL;
+

+
    if(count == 1)
+
        value = (unsigned char)buffer[0];
+
    else
+
    {
+
        if(!utf8_check_full(buffer, count, &value))
+
            return NULL;
+
    }
+

+
    if(codepoint)
+
        *codepoint = value;
+

+
    return buffer + count;
+
}
+

+
int utf8_check_string(const char *string, int length)
+
{
+
    int i;
+

+
    if(length == -1)
+
        length = strlen(string);
+

+
    for(i = 0; i < length; i++)
+
    {
+
        int count = utf8_check_first(string[i]);
+
        if(count == 0)
+
            return 0;
+
        else if(count > 1)
+
        {
+
            if(i + count > length)
+
                return 0;
+

+
            if(!utf8_check_full(&string[i], count, NULL))
+
                return 0;
+

+
            i += count - 1;
+
        }
+
    }
+

+
    return 1;
+
}
added external/jansson/utf.h
@@ -0,0 +1,28 @@
+
/*
+
 * Copyright (c) 2009, 2010 Petri Lehtinen <petri@digip.org>
+
 *
+
 * Jansson is free software; you can redistribute it and/or modify
+
 * it under the terms of the MIT license. See LICENSE for details.
+
 */
+

+
#ifndef UTF_H
+
#define UTF_H
+

+
#include <config.h>
+

+
#ifdef HAVE_INTTYPES_H
+
/* inttypes.h includes stdint.h in a standard environment, so there's
+
no need to include stdint.h separately. If inttypes.h doesn't define
+
int32_t, it's defined in config.h. */
+
#include <inttypes.h>
+
#endif
+

+
int utf8_encode(int codepoint, char *buffer, int *size);
+

+
int utf8_check_first(char byte);
+
int utf8_check_full(const char *buffer, int size, int32_t *codepoint);
+
const char *utf8_iterate(const char *buffer, int32_t *codepoint);
+

+
int utf8_check_string(const char *string, int length);
+

+
#endif
added external/jansson/util.h
@@ -0,0 +1,13 @@
+
/*
+
 * Copyright (c) 2009, 2010 Petri Lehtinen <petri@digip.org>
+
 *
+
 * Jansson is free software; you can redistribute it and/or modify
+
 * it under the terms of the MIT license. See LICENSE for details.
+
 */
+

+
#ifndef UTIL_H
+
#define UTIL_H
+

+
#define max(a, b)  ((a) > (b) ? (a) : (b))
+

+
#endif
added external/jansson/value.c
@@ -0,0 +1,976 @@
+
/*
+
 * Copyright (c) 2009, 2010 Petri Lehtinen <petri@digip.org>
+
 *
+
 * Jansson is free software; you can redistribute it and/or modify
+
 * it under the terms of the MIT license. See LICENSE for details.
+
 */
+

+
#define _GNU_SOURCE
+

+
#include <config.h>
+

+
#include <stdlib.h>
+
#include <string.h>
+

+
#include <jansson.h>
+
#include "hashtable.h"
+
#include "jansson_private.h"
+
#include "utf.h"
+
#include "util.h"
+

+

+
static inline void json_init(json_t *json, json_type type)
+
{
+
    json->type = type;
+
    json->refcount = 1;
+
}
+

+

+
/*** object ***/
+

+
/* This macro just returns a pointer that's a few bytes backwards from
+
   string. This makes it possible to pass a pointer to object_key_t
+
   when only the string inside it is used, without actually creating
+
   an object_key_t instance. */
+
#define string_to_key(string)  container_of(string, object_key_t, key)
+

+
static unsigned int hash_key(const void *ptr)
+
{
+
    const char *str = ((const object_key_t *)ptr)->key;
+

+
    unsigned int hash = 5381;
+
    unsigned int c;
+

+
    while((c = (unsigned int)*str))
+
    {
+
        hash = ((hash << 5) + hash) + c;
+
        str++;
+
    }
+

+
    return hash;
+
}
+

+
static int key_equal(const void *ptr1, const void *ptr2)
+
{
+
    return strcmp(((const object_key_t *)ptr1)->key,
+
                  ((const object_key_t *)ptr2)->key) == 0;
+
}
+

+
static void value_decref(void *value)
+
{
+
    json_decref((json_t *)value);
+
}
+

+
json_t *json_object(void)
+
{
+
    json_object_t *object = malloc(sizeof(json_object_t));
+
    if(!object)
+
        return NULL;
+
    json_init(&object->json, JSON_OBJECT);
+

+
    if(hashtable_init(&object->hashtable, hash_key, key_equal,
+
                      free, value_decref))
+
    {
+
        free(object);
+
        return NULL;
+
    }
+

+
    object->serial = 0;
+
    object->visited = 0;
+

+
    return &object->json;
+
}
+

+
static void json_delete_object(json_object_t *object)
+
{
+
    hashtable_close(&object->hashtable);
+
    free(object);
+
}
+

+
unsigned int json_object_size(const json_t *json)
+
{
+
    json_object_t *object;
+

+
    if(!json_is_object(json))
+
        return -1;
+

+
    object = json_to_object(json);
+
    return object->hashtable.size;
+
}
+

+
json_t *json_object_get(const json_t *json, const char *key)
+
{
+
    json_object_t *object;
+

+
    if(!json_is_object(json))
+
        return NULL;
+

+
    object = json_to_object(json);
+
    return hashtable_get(&object->hashtable, string_to_key(key));
+
}
+

+
int json_object_set_new_nocheck(json_t *json, const char *key, json_t *value)
+
{
+
    json_object_t *object;
+
    object_key_t *k;
+

+
    if(!key || !value)
+
        return -1;
+

+
    if(!json_is_object(json) || json == value)
+
    {
+
        json_decref(value);
+
        return -1;
+
    }
+
    object = json_to_object(json);
+

+
    k = malloc(sizeof(object_key_t) + strlen(key) + 1);
+
    if(!k)
+
        return -1;
+

+
    k->serial = object->serial++;
+
    strcpy(k->key, key);
+

+
    if(hashtable_set(&object->hashtable, k, value))
+
    {
+
        json_decref(value);
+
        return -1;
+
    }
+

+
    return 0;
+
}
+

+
int json_object_set_new(json_t *json, const char *key, json_t *value)
+
{
+
    if(!key || !utf8_check_string(key, -1))
+
    {
+
        json_decref(value);
+
        return -1;
+
    }
+

+
    return json_object_set_new_nocheck(json, key, value);
+
}
+

+
int json_object_del(json_t *json, const char *key)
+
{
+
    json_object_t *object;
+

+
    if(!json_is_object(json))
+
        return -1;
+

+
    object = json_to_object(json);
+
    return hashtable_del(&object->hashtable, string_to_key(key));
+
}
+

+
int json_object_clear(json_t *json)
+
{
+
    json_object_t *object;
+

+
    if(!json_is_object(json))
+
        return -1;
+

+
    object = json_to_object(json);
+
    hashtable_clear(&object->hashtable);
+

+
    return 0;
+
}
+

+
int json_object_update(json_t *object, json_t *other)
+
{
+
    void *iter;
+

+
    if(!json_is_object(object) || !json_is_object(other))
+
        return -1;
+

+
    iter = json_object_iter(other);
+
    while(iter) {
+
        const char *key;
+
        json_t *value;
+

+
        key = json_object_iter_key(iter);
+
        value = json_object_iter_value(iter);
+

+
        if(json_object_set_nocheck(object, key, value))
+
            return -1;
+

+
        iter = json_object_iter_next(other, iter);
+
    }
+

+
    return 0;
+
}
+

+
void *json_object_iter(json_t *json)
+
{
+
    json_object_t *object;
+

+
    if(!json_is_object(json))
+
        return NULL;
+

+
    object = json_to_object(json);
+
    return hashtable_iter(&object->hashtable);
+
}
+

+
void *json_object_iter_at(json_t *json, const char *key)
+
{
+
    json_object_t *object;
+

+
    if(!key || !json_is_object(json))
+
        return NULL;
+

+
    object = json_to_object(json);
+
    return hashtable_iter_at(&object->hashtable, string_to_key(key));
+
}
+

+
void *json_object_iter_next(json_t *json, void *iter)
+
{
+
    json_object_t *object;
+

+
    if(!json_is_object(json) || iter == NULL)
+
        return NULL;
+

+
    object = json_to_object(json);
+
    return hashtable_iter_next(&object->hashtable, iter);
+
}
+

+
const object_key_t *jsonp_object_iter_fullkey(void *iter)
+
{
+
    if(!iter)
+
        return NULL;
+

+
    return hashtable_iter_key(iter);
+
}
+

+
const char *json_object_iter_key(void *iter)
+
{
+
    if(!iter)
+
        return NULL;
+

+
    return jsonp_object_iter_fullkey(iter)->key;
+
}
+

+
json_t *json_object_iter_value(void *iter)
+
{
+
    if(!iter)
+
        return NULL;
+

+
    return (json_t *)hashtable_iter_value(iter);
+
}
+

+
int json_object_iter_set_new(json_t *json, void *iter, json_t *value)
+
{
+
    json_object_t *object;
+

+
    if(!json_is_object(json) || !iter || !value)
+
        return -1;
+

+
    object = json_to_object(json);
+
    hashtable_iter_set(&object->hashtable, iter, value);
+

+
    return 0;
+
}
+

+
static int json_object_equal(json_t *object1, json_t *object2)
+
{
+
    void *iter;
+

+
    if(json_object_size(object1) != json_object_size(object2))
+
        return 0;
+

+
    iter = json_object_iter(object1);
+
    while(iter)
+
    {
+
        const char *key;
+
        json_t *value1, *value2;
+

+
        key = json_object_iter_key(iter);
+
        value1 = json_object_iter_value(iter);
+
        value2 = json_object_get(object2, key);
+

+
        if(!json_equal(value1, value2))
+
            return 0;
+

+
        iter = json_object_iter_next(object1, iter);
+
    }
+

+
    return 1;
+
}
+

+
static json_t *json_object_copy(json_t *object)
+
{
+
    json_t *result;
+
    void *iter;
+

+
    result = json_object();
+
    if(!result)
+
        return NULL;
+

+
    iter = json_object_iter(object);
+
    while(iter)
+
    {
+
        const char *key;
+
        json_t *value;
+

+
        key = json_object_iter_key(iter);
+
        value = json_object_iter_value(iter);
+
        json_object_set_nocheck(result, key, value);
+

+
        iter = json_object_iter_next(object, iter);
+
    }
+

+
    return result;
+
}
+

+
static json_t *json_object_deep_copy(json_t *object)
+
{
+
    json_t *result;
+
    void *iter;
+

+
    result = json_object();
+
    if(!result)
+
        return NULL;
+

+
    iter = json_object_iter(object);
+
    while(iter)
+
    {
+
        const char *key;
+
        json_t *value;
+

+
        key = json_object_iter_key(iter);
+
        value = json_object_iter_value(iter);
+
        json_object_set_new_nocheck(result, key, json_deep_copy(value));
+

+
        iter = json_object_iter_next(object, iter);
+
    }
+

+
    return result;
+
}
+

+

+
/*** array ***/
+

+
json_t *json_array(void)
+
{
+
    json_array_t *array = malloc(sizeof(json_array_t));
+
    if(!array)
+
        return NULL;
+
    json_init(&array->json, JSON_ARRAY);
+

+
    array->entries = 0;
+
    array->size = 8;
+

+
    array->table = malloc(array->size * sizeof(json_t *));
+
    if(!array->table) {
+
        free(array);
+
        return NULL;
+
    }
+

+
    array->visited = 0;
+

+
    return &array->json;
+
}
+

+
static void json_delete_array(json_array_t *array)
+
{
+
    unsigned int i;
+

+
    for(i = 0; i < array->entries; i++)
+
        json_decref(array->table[i]);
+

+
    free(array->table);
+
    free(array);
+
}
+

+
unsigned int json_array_size(const json_t *json)
+
{
+
    if(!json_is_array(json))
+
        return 0;
+

+
    return json_to_array(json)->entries;
+
}
+

+
json_t *json_array_get(const json_t *json, unsigned int index)
+
{
+
    json_array_t *array;
+
    if(!json_is_array(json))
+
        return NULL;
+
    array = json_to_array(json);
+

+
    if(index >= array->entries)
+
        return NULL;
+

+
    return array->table[index];
+
}
+

+
int json_array_set_new(json_t *json, unsigned int index, json_t *value)
+
{
+
    json_array_t *array;
+

+
    if(!value)
+
        return -1;
+

+
    if(!json_is_array(json) || json == value)
+
    {
+
        json_decref(value);
+
        return -1;
+
    }
+
    array = json_to_array(json);
+

+
    if(index >= array->entries)
+
    {
+
        json_decref(value);
+
        return -1;
+
    }
+

+
    json_decref(array->table[index]);
+
    array->table[index] = value;
+

+
    return 0;
+
}
+

+
static void array_move(json_array_t *array, unsigned int dest,
+
                       unsigned int src, unsigned int count)
+
{
+
    memmove(&array->table[dest], &array->table[src], count * sizeof(json_t *));
+
}
+

+
static void array_copy(json_t **dest, unsigned int dpos,
+
                       json_t **src, unsigned int spos,
+
                       unsigned int count)
+
{
+
    memcpy(&dest[dpos], &src[spos], count * sizeof(json_t *));
+
}
+

+
static json_t **json_array_grow(json_array_t *array,
+
                                unsigned int amount,
+
                                int copy)
+
{
+
    unsigned int new_size;
+
    json_t **old_table, **new_table;
+

+
    if(array->entries + amount <= array->size)
+
        return array->table;
+

+
    old_table = array->table;
+

+
    new_size = max(array->size + amount, array->size * 2);
+
    new_table = malloc(new_size * sizeof(json_t *));
+
    if(!new_table)
+
        return NULL;
+

+
    array->size = new_size;
+
    array->table = new_table;
+

+
    if(copy) {
+
        array_copy(array->table, 0, old_table, 0, array->entries);
+
        free(old_table);
+
        return array->table;
+
    }
+

+
    return old_table;
+
}
+

+
int json_array_append_new(json_t *json, json_t *value)
+
{
+
    json_array_t *array;
+

+
    if(!value)
+
        return -1;
+

+
    if(!json_is_array(json) || json == value)
+
    {
+
        json_decref(value);
+
        return -1;
+
    }
+
    array = json_to_array(json);
+

+
    if(!json_array_grow(array, 1, 1)) {
+
        json_decref(value);
+
        return -1;
+
    }
+

+
    array->table[array->entries] = value;
+
    array->entries++;
+

+
    return 0;
+
}
+

+
int json_array_insert_new(json_t *json, unsigned int index, json_t *value)
+
{
+
    json_array_t *array;
+
    json_t **old_table;
+

+
    if(!value)
+
        return -1;
+

+
    if(!json_is_array(json) || json == value) {
+
        json_decref(value);
+
        return -1;
+
    }
+
    array = json_to_array(json);
+

+
    if(index > array->entries) {
+
        json_decref(value);
+
        return -1;
+
    }
+

+
    old_table = json_array_grow(array, 1, 0);
+
    if(!old_table) {
+
        json_decref(value);
+
        return -1;
+
    }
+

+
    if(old_table != array->table) {
+
        array_copy(array->table, 0, old_table, 0, index);
+
        array_copy(array->table, index + 1, old_table, index,
+
                   array->entries - index);
+
        free(old_table);
+
    }
+
    else
+
        array_move(array, index + 1, index, array->entries - index);
+

+
    array->table[index] = value;
+
    array->entries++;
+

+
    return 0;
+
}
+

+
int json_array_remove(json_t *json, unsigned int index)
+
{
+
    json_array_t *array;
+

+
    if(!json_is_array(json))
+
        return -1;
+
    array = json_to_array(json);
+

+
    if(index >= array->entries)
+
        return -1;
+

+
    json_decref(array->table[index]);
+

+
    array_move(array, index, index + 1, array->entries - index);
+
    array->entries--;
+

+
    return 0;
+
}
+

+
int json_array_clear(json_t *json)
+
{
+
    json_array_t *array;
+
    unsigned int i;
+

+
    if(!json_is_array(json))
+
        return -1;
+
    array = json_to_array(json);
+

+
    for(i = 0; i < array->entries; i++)
+
        json_decref(array->table[i]);
+

+
    array->entries = 0;
+
    return 0;
+
}
+

+
int json_array_extend(json_t *json, json_t *other_json)
+
{
+
    json_array_t *array, *other;
+
    unsigned int i;
+

+
    if(!json_is_array(json) || !json_is_array(other_json))
+
        return -1;
+
    array = json_to_array(json);
+
    other = json_to_array(other_json);
+

+
    if(!json_array_grow(array, other->entries, 1))
+
        return -1;
+

+
    for(i = 0; i < other->entries; i++)
+
        json_incref(other->table[i]);
+

+
    array_copy(array->table, array->entries, other->table, 0, other->entries);
+

+
    array->entries += other->entries;
+
    return 0;
+
}
+

+
static int json_array_equal(json_t *array1, json_t *array2)
+
{
+
    unsigned int i, size;
+

+
    size = json_array_size(array1);
+
    if(size != json_array_size(array2))
+
        return 0;
+

+
    for(i = 0; i < size; i++)
+
    {
+
        json_t *value1, *value2;
+

+
        value1 = json_array_get(array1, i);
+
        value2 = json_array_get(array2, i);
+

+
        if(!json_equal(value1, value2))
+
            return 0;
+
    }
+

+
    return 1;
+
}
+

+
static json_t *json_array_copy(json_t *array)
+
{
+
    json_t *result;
+
    unsigned int i;
+

+
    result = json_array();
+
    if(!result)
+
        return NULL;
+

+
    for(i = 0; i < json_array_size(array); i++)
+
        json_array_append(result, json_array_get(array, i));
+

+
    return result;
+
}
+

+
static json_t *json_array_deep_copy(json_t *array)
+
{
+
    json_t *result;
+
    unsigned int i;
+

+
    result = json_array();
+
    if(!result)
+
        return NULL;
+

+
    for(i = 0; i < json_array_size(array); i++)
+
        json_array_append_new(result, json_deep_copy(json_array_get(array, i)));
+

+
    return result;
+
}
+

+
/*** string ***/
+

+
json_t *json_string_nocheck(const char *value)
+
{
+
    json_string_t *string;
+

+
    if(!value)
+
        return NULL;
+

+
    string = malloc(sizeof(json_string_t));
+
    if(!string)
+
        return NULL;
+
    json_init(&string->json, JSON_STRING);
+

+
    string->value = strdup(value);
+
    if(!string->value) {
+
        free(string);
+
        return NULL;
+
    }
+

+
    return &string->json;
+
}
+

+
json_t *json_string(const char *value)
+
{
+
    if(!value || !utf8_check_string(value, -1))
+
        return NULL;
+

+
    return json_string_nocheck(value);
+
}
+

+
const char *json_string_value(const json_t *json)
+
{
+
    if(!json_is_string(json))
+
        return NULL;
+

+
    return json_to_string(json)->value;
+
}
+

+
int json_string_set_nocheck(json_t *json, const char *value)
+
{
+
    char *dup;
+
    json_string_t *string;
+

+
    dup = strdup(value);
+
    if(!dup)
+
        return -1;
+

+
    string = json_to_string(json);
+
    free(string->value);
+
    string->value = dup;
+

+
    return 0;
+
}
+

+
int json_string_set(json_t *json, const char *value)
+
{
+
    if(!value || !utf8_check_string(value, -1))
+
        return -1;
+

+
    return json_string_set_nocheck(json, value);
+
}
+

+
static void json_delete_string(json_string_t *string)
+
{
+
    free(string->value);
+
    free(string);
+
}
+

+
static int json_string_equal(json_t *string1, json_t *string2)
+
{
+
    return strcmp(json_string_value(string1), json_string_value(string2)) == 0;
+
}
+

+
static json_t *json_string_copy(json_t *string)
+
{
+
    return json_string_nocheck(json_string_value(string));
+
}
+

+

+
/*** integer ***/
+

+
json_t *json_integer(int value)
+
{
+
    json_integer_t *integer = malloc(sizeof(json_integer_t));
+
    if(!integer)
+
        return NULL;
+
    json_init(&integer->json, JSON_INTEGER);
+

+
    integer->value = value;
+
    return &integer->json;
+
}
+

+
int json_integer_value(const json_t *json)
+
{
+
    if(!json_is_integer(json))
+
        return 0;
+

+
    return json_to_integer(json)->value;
+
}
+

+
int json_integer_set(json_t *json, int value)
+
{
+
    if(!json_is_integer(json))
+
        return -1;
+

+
    json_to_integer(json)->value = value;
+

+
    return 0;
+
}
+

+
static void json_delete_integer(json_integer_t *integer)
+
{
+
    free(integer);
+
}
+

+
static int json_integer_equal(json_t *integer1, json_t *integer2)
+
{
+
    return json_integer_value(integer1) == json_integer_value(integer2);
+
}
+

+
static json_t *json_integer_copy(json_t *integer)
+
{
+
    return json_integer(json_integer_value(integer));
+
}
+

+

+
/*** real ***/
+

+
json_t *json_real(double value)
+
{
+
    json_real_t *real = malloc(sizeof(json_real_t));
+
    if(!real)
+
        return NULL;
+
    json_init(&real->json, JSON_REAL);
+

+
    real->value = value;
+
    return &real->json;
+
}
+

+
double json_real_value(const json_t *json)
+
{
+
    if(!json_is_real(json))
+
        return 0;
+

+
    return json_to_real(json)->value;
+
}
+

+
int json_real_set(json_t *json, double value)
+
{
+
    if(!json_is_real(json))
+
        return 0;
+

+
    json_to_real(json)->value = value;
+

+
    return 0;
+
}
+

+
static void json_delete_real(json_real_t *real)
+
{
+
    free(real);
+
}
+

+
static int json_real_equal(json_t *real1, json_t *real2)
+
{
+
    return json_real_value(real1) == json_real_value(real2);
+
}
+

+
static json_t *json_real_copy(json_t *real)
+
{
+
    return json_real(json_real_value(real));
+
}
+

+

+
/*** number ***/
+

+
double json_number_value(const json_t *json)
+
{
+
    if(json_is_integer(json))
+
        return json_integer_value(json);
+
    else if(json_is_real(json))
+
        return json_real_value(json);
+
    else
+
        return 0.0;
+
}
+

+

+
/*** simple values ***/
+

+
json_t *json_true(void)
+
{
+
    static json_t the_true = {
+
        .type = JSON_TRUE,
+
        .refcount = (unsigned int)-1
+
    };
+
    return &the_true;
+
}
+

+

+
json_t *json_false(void)
+
{
+
    static json_t the_false = {
+
        .type = JSON_FALSE,
+
        .refcount = (unsigned int)-1
+
    };
+
    return &the_false;
+
}
+

+

+
json_t *json_null(void)
+
{
+
    static json_t the_null = {
+
        .type = JSON_NULL,
+
        .refcount = (unsigned int)-1
+
    };
+
    return &the_null;
+
}
+

+

+
/*** deletion ***/
+

+
void json_delete(json_t *json)
+
{
+
    if(json_is_object(json))
+
        json_delete_object(json_to_object(json));
+

+
    else if(json_is_array(json))
+
        json_delete_array(json_to_array(json));
+

+
    else if(json_is_string(json))
+
        json_delete_string(json_to_string(json));
+

+
    else if(json_is_integer(json))
+
        json_delete_integer(json_to_integer(json));
+

+
    else if(json_is_real(json))
+
        json_delete_real(json_to_real(json));
+

+
    /* json_delete is not called for true, false or null */
+
}
+

+

+
/*** equality ***/
+

+
int json_equal(json_t *json1, json_t *json2)
+
{
+
    if(!json1 || !json2)
+
        return 0;
+

+
    if(json_typeof(json1) != json_typeof(json2))
+
        return 0;
+

+
    /* this covers true, false and null as they are singletons */
+
    if(json1 == json2)
+
        return 1;
+

+
    if(json_is_object(json1))
+
        return json_object_equal(json1, json2);
+

+
    if(json_is_array(json1))
+
        return json_array_equal(json1, json2);
+

+
    if(json_is_string(json1))
+
        return json_string_equal(json1, json2);
+

+
    if(json_is_integer(json1))
+
        return json_integer_equal(json1, json2);
+

+
    if(json_is_real(json1))
+
        return json_real_equal(json1, json2);
+

+
    return 0;
+
}
+

+

+
/*** copying ***/
+

+
json_t *json_copy(json_t *json)
+
{
+
    if(!json)
+
        return NULL;
+

+
    if(json_is_object(json))
+
        return json_object_copy(json);
+

+
    if(json_is_array(json))
+
        return json_array_copy(json);
+

+
    if(json_is_string(json))
+
        return json_string_copy(json);
+

+
    if(json_is_integer(json))
+
        return json_integer_copy(json);
+

+
    if(json_is_real(json))
+
        return json_real_copy(json);
+

+
    if(json_is_true(json) || json_is_false(json) || json_is_null(json))
+
        return json;
+

+
    return NULL;
+
}
+

+
json_t *json_deep_copy(json_t *json)
+
{
+
    if(!json)
+
        return NULL;
+

+
    if(json_is_object(json))
+
        return json_object_deep_copy(json);
+

+
    if(json_is_array(json))
+
        return json_array_deep_copy(json);
+

+
    /* for the rest of the types, deep copying doesn't differ from
+
       shallow copying */
+

+
    if(json_is_string(json))
+
        return json_string_copy(json);
+

+
    if(json_is_integer(json))
+
        return json_integer_copy(json);
+

+
    if(json_is_real(json))
+
        return json_real_copy(json);
+

+
    if(json_is_true(json) || json_is_false(json) || json_is_null(json))
+
        return json;
+

+
    return NULL;
+
}
modified libpkg/Makefile
@@ -1,22 +1,39 @@
.include <bsd.own.mk>

-
LIB=	pkg
-
INCS=	pkg.h pkgdb.h
-
WARNS=	6
+
LIB=		pkg
+
INCS=		pkg.h pkgdb.h
+
WARNS=		6
SHLIBDIR?=	/usr/lib
SHLIB_MAJOR=	0

-
SRCS=	pkg.c pkgdb.c pkgdb_cache.c pkg_compat.c util.c
+
SRCS=pkg_manifest.c
+
#SRCS=		pkg.c \
+
#		pkgdb.c \
+
#		pkgdb_cache.c \
+
#		pkg_compat.c \
+
#		pkg_manifest.c \
+
#		util.c

-
CFLAGS+=  -std=c99
-
CFLAGS+=  -I${.CURDIR} -I${.CURDIR}/../external/tinycdb -I${.CURDIR}/../external/cjson/
-
LDADD+=   -L${.CURDIR}/../external/tinycdb -L${.CURDIR}/../external/cjson -L/usr/local/lib -lcdb -lcjson -lm -larchive -lz -lbz2 -llzma
+
CFLAGS+=	-std=c99
+
CFLAGS+=	-I${.CURDIR} \
+
		-I${.CURDIR}/../external/jansson \
+
		-I${.CURDIR}/../external/tinycdb/
+
LDADD+=		-L${.CURDIR}/../external/jansson \
+
		-L${.CURDIR}/../external/tinycdb \
+
		-L/usr/local/lib \
+
		-lcdb \
+
		-ljansson \
+
		-lm \
+
		-larchive \
+
		-lz \
+
		-lbz2 \
+
		-llzma

DEBUG_FLAGS+=  -g
.if defined(PROFILE_BUILD)
-
DEBUG_FLAGS+=    -pg
+
DEBUG_FLAGS+=	-pg
.endif
-
NO_MAN=   yes
+
NO_MAN=		yes

.include <bsd.lib.mk>

added libpkg/pkg_manifest.c
@@ -0,0 +1,344 @@
+
#include <assert.h>
+
#include <err.h>
+
#include <stdbool.h>
+
#include <stdlib.h>
+

+
#include <jansson.h>
+

+
#include "pkg.h"
+
#include "pkg_manifest.h"
+

+
const char *
+
pkg_manifest_keystr(json_t *root, const char *key)
+
{
+
	json_t *node;
+

+
	if ((node = json_object_get(root, key)) == NULL)
+
		return (NULL);
+
	else
+
		return json_string_value(node);
+
}
+

+
void
+
pkg_manifest_array_init(struct pkg_manifest *m, enum array_type t)
+
{
+
	m->array.node = NULL;
+
	m->array.elm = NULL;
+
	m->array.size = 0;
+
	m->array.idx = 0;
+
	m->array.type = t;
+
}
+

+
/*
+
 * Simple accesser for string value at the root of the JSON node
+
 */
+
const char *
+
pkg_manifest_value(struct pkg_manifest *manifest, const char *key)
+
{
+
	const char *str;
+

+
	if ((str = pkg_manifest_keystr(manifest->json, key)) == NULL)
+
		warnx("Can not find '%s' in the manifest", key);
+

+
	return (str);
+
}
+

+
/*
+
 * Return true if the package was built with option named `name'
+
 */
+
bool
+
pkg_manifest_with_option(struct pkg_manifest *manifest, const char *name)
+
{
+
	json_t *node;
+
	json_t *elm;
+

+
	if ((node = json_object_get(manifest->json, "options")) == NULL)
+
		return (false);
+

+
	if ((elm = json_object_get(node, name)) == NULL)
+
		return (false);
+

+
	return json_equal(elm, json_true());
+
}
+

+
void
+
pkg_manifest_file_init(struct pkg_manifest *m)
+
{
+
	pkg_manifest_array_init(m, ARRAY_FILES);
+
}
+

+
/*
+
 * Return 0 if there is another file, 1 if we are done, -1 if an error occured.
+
 */
+
int
+
pkg_manifest_file_next(struct pkg_manifest *m)
+
{
+
	assert(m->array.type == ARRAY_FILES);
+

+
	/* First call */
+
	if (m->array.node == NULL) {
+
		if ((m->array.node = json_object_get(m->json, "files")) == NULL)
+
			return (-1);
+
		m->array.size = json_array_size(m->array.node);
+
	}
+

+
	if (m->array.idx >= m->array.size)
+
		return (1);
+

+
	if ((m->array.elm = json_array_get(m->array.node, m->array.idx)) == NULL)
+
		return (-1);
+

+
	m->array.idx++;
+

+
	return (0);
+
}
+

+
const char *
+
pkg_manifest_file_path(struct pkg_manifest *m)
+
{
+
	assert(m->array.type == ARRAY_FILES);
+
	assert(m->array.elm != NULL);
+

+
	return pkg_manifest_keystr(m->array.elm, "path");
+
}
+

+
const char *
+
pkg_manifest_file_md5(struct pkg_manifest *m)
+
{
+
	assert(m->array.type == ARRAY_FILES);
+
	assert(m->array.elm != NULL);
+

+
	return pkg_manifest_keystr(m->array.elm, "md5");
+
}
+

+
void
+
pkg_manifest_dep_init(struct pkg_manifest *m)
+
{
+
	pkg_manifest_array_init(m, ARRAY_DEPS);
+
}
+

+
int
+
pkg_manifest_dep_next(struct pkg_manifest *m)
+
{
+
	assert(m->array.type == ARRAY_DEPS);
+

+
	/* First call */
+
	if (m->array.node == NULL) {
+
		if ((m->array.node = json_object_get(m->json, "deps")) == NULL)
+
			return (-1);
+
		m->array.size = json_array_size(m->array.node);
+
	}
+

+
	if (m->array.idx >= m->array.size)
+
		return (1);
+

+
	if ((m->array.elm = json_array_get(m->array.node, m->array.idx)) == NULL)
+
		return (-1);
+

+
	m->array.idx++;
+

+
	return (0);
+

+
}
+

+
const char *
+
pkg_manifest_dep_name(struct pkg_manifest *m)
+
{
+
	assert(m->array.type == ARRAY_DEPS);
+
	assert(m->array.elm != NULL);
+

+
	return pkg_manifest_keystr(m->array.elm, "name");
+
}
+

+
const char *
+
pkg_manifest_dep_origin(struct pkg_manifest *m)
+
{
+
	assert(m->array.type == ARRAY_DEPS);
+
	assert(m->array.elm != NULL);
+

+
	return pkg_manifest_keystr(m->array.elm, "origin");
+
}
+

+
const char *
+
pkg_manifest_dep_version(struct pkg_manifest *m)
+
{
+
	assert(m->array.type == ARRAY_DEPS);
+
	assert(m->array.elm != NULL);
+

+
	return pkg_manifest_keystr(m->array.elm, "version");
+
}
+

+
void
+
pkg_manifest_exec_init(struct pkg_manifest *m)
+
{
+
	pkg_manifest_array_init(m, ARRAY_EXEC);
+
}
+

+
const char *
+
pkg_manifest_exec_next(struct pkg_manifest *m)
+
{
+
	assert(m->array.type == ARRAY_EXEC);
+

+
	/* First call */
+
	if (m->array.node == NULL) {
+
		if ((m->array.node = json_object_get(m->json, "exec")) == NULL)
+
			return (NULL);
+
		m->array.size = json_array_size(m->array.node);
+
	}
+

+
	if (m->array.idx >= m->array.size)
+
		return (NULL);
+

+
	if ((m->array.elm = json_array_get(m->array.node, m->array.idx)) == NULL)
+
		return (NULL);
+

+
	m->array.idx++;
+

+
	return json_string_value(m->array.elm);
+
}
+

+
void
+
pkg_manifest_unexec_init(struct pkg_manifest *m)
+
{
+
	pkg_manifest_array_init(m, ARRAY_UNEXEC);
+
}
+

+
const char *
+
pkg_manifest_unexec_next(struct pkg_manifest *m)
+
{
+
	assert(m->array.type == ARRAY_UNEXEC);
+

+
	/* First call */
+
	if (m->array.node == NULL) {
+
		if ((m->array.node = json_object_get(m->json, "unexec")) == NULL)
+
			return (NULL);
+
		m->array.size = json_array_size(m->array.node);
+
	}
+

+
	if (m->array.idx >= m->array.size)
+
		return (NULL);
+

+
	if ((m->array.elm = json_array_get(m->array.node, m->array.idx)) == NULL)
+
		return (NULL);
+

+
	m->array.idx++;
+

+
	return json_string_value(m->array.elm);
+
}
+

+
/* Setters */
+

+
void
+
pkg_manifest_add_value(struct pkg_manifest *m, const char *key, const char *value)
+
{
+
	json_object_set_new(m->json, key, json_string(value));
+
}
+

+
void
+
pkg_manifest_add_file(struct pkg_manifest *m, const char *path, const char *md5)
+
{
+
	json_t *files;
+
	json_t *file;
+

+
	if ((files = json_object_get(m->json, "files")) == NULL) {
+
		files = json_array();
+
		json_object_set_new(m->json, "files", files);
+
	}
+

+
	file = json_object();
+
	json_object_set_new(file, "path", json_string(path));
+
	json_object_set_new(file, "md5", json_string(md5));
+

+
	json_array_append_new(files, file);
+
}
+

+
void
+
pkg_manifest_add_dep(struct pkg_manifest *m, const char *name, const char *origin,
+
					 const char *version)
+
{
+
	json_t *deps;
+
	json_t *dep;
+

+
	if ((deps = json_object_get(m->json, "deps")) == NULL) {
+
		deps = json_array();
+
		json_object_set_new(m->json, "deps", deps);
+
	}
+

+
	dep = json_object();
+
	json_object_set_new(dep, "name", json_string(name));
+
	json_object_set_new(dep, "origin", json_string(origin));
+
	json_object_set_new(dep, "version", json_string(version));
+

+
	json_array_append_new(deps, dep);
+
}
+

+
void
+
pkg_manifest_add_exec(struct pkg_manifest *m, const char *value)
+
{
+
	json_t *array;
+

+
	if ((array = json_object_get(m->json, "exec")) == NULL) {
+
		array = json_array();
+
		json_object_set_new(m->json, "exec", array);
+
	}
+

+
	json_array_append_new(array, json_string(value));
+
}
+

+
void
+
pkg_manifest_add_unexec(struct pkg_manifest *m, const char *value)
+
{
+
	json_t *array;
+

+
	if ((array = json_object_get(m->json, "unexec")) == NULL) {
+
		array = json_array();
+
		json_object_set_new(m->json, "unexec", array);
+
	}
+

+
	json_array_append_new(array, json_string(value));
+
}
+

+
int
+
pkg_manifest_loadb(struct pkg_manifest *m, const char *buffer)
+
{
+
	json_error_t error;
+

+
	if ((m->json = json_loads(buffer, &error)) == NULL) {
+
		warnx("Can not parse buffer as JSON: %s", error.text);
+
		return (-1);
+
	}
+

+
	return (0);
+
}
+

+
int
+
pkg_manifest_loadp(struct pkg_manifest *m, const char *path)
+
{
+
	json_error_t error;
+

+
	if ((m->json = json_load_file(path, &error)) == NULL) {
+
		warnx("Can not parse %s: %s", path, error.text);
+
		return (-1);
+
	}
+

+
	return (0);
+
}
+

+
char *
+
pkg_manifest_dumpb(struct pkg_manifest *m)
+
{
+
	return json_dumps(m->json, JSON_COMPACT);
+
}
+

+
int
+
pkg_manifest_dumpp(struct pkg_manifest *m, const char *path)
+
{
+
	return json_dump_file(m->json, path, JSON_INDENT(2));
+
}
+

+
void
+
pkg_manifest_free(struct pkg_manifest *m)
+
{
+
	json_decref(m->json);
+
	free(m);
+
}
added libpkg/pkg_manifest.h
@@ -0,0 +1,66 @@
+
#ifndef _PKG_MANIFEST_H
+
#define _PKG_MANIFEST_H
+

+
#include <stdbool.h>
+

+
#include <jansson.h>
+

+
enum array_type {
+
	ARRAY_FILES,
+
	ARRAY_DEPS,
+
	ARRAY_EXEC,
+
	ARRAY_UNEXEC
+
};
+

+
struct pkg_manifest_array {
+
	json_t *node;
+
	json_t *elm;
+
	unsigned int size;
+
	unsigned int idx;
+
	enum array_type type;
+
};
+

+
struct pkg_manifest {
+
	json_t *json;
+
	struct pkg_manifest_array array;
+
};
+

+
/* Parser/Emitter */
+
int pkg_manifest_loadb(struct pkg_manifest *, const char *);
+
int pkg_manifest_loadp(struct pkg_manifest *, const char *);
+
char * pkg_manifest_dumpb(struct pkg_manifest *);
+
int pkg_manifest_dumpp(struct pkg_manifest *, const char *);
+
void pkg_manifest_free(struct pkg_manifest *);
+

+
/* Getter */
+
const char * pkg_manifest_value(struct pkg_manifest *, const char *);
+
bool pkg_manifest_with_option(struct pkg_manifest *, const char *);
+

+
void pkg_manifest_file_init(struct pkg_manifest *);
+
int pkg_manifest_file_next(struct pkg_manifest *);
+
const char * pkg_manifest_file_path(struct pkg_manifest *);
+
const char * pkg_manifest_file_md5(struct pkg_manifest *);
+

+
void pkg_manifest_dep_init(struct pkg_manifest *);
+
int pkg_manifest_dep_next(struct pkg_manifest *);
+
const char * pkg_manifest_dep_name(struct pkg_manifest *);
+
const char * pkg_manifest_dep_origin(struct pkg_manifest *);
+
const char * pkg_manifest_dep_version(struct pkg_manifest *);
+

+
void pkg_manifest_exec_init(struct pkg_manifest *);
+
const char * pkg_manifest_exec_next(struct pkg_manifest *);
+

+
void pkg_manifest_unexec_init(struct pkg_manifest *);
+
const char * pkg_manifest_unexec_next(struct pkg_manifest *);
+

+
/* Setter */
+
void pkg_manifest_add_value(struct pkg_manifest *, const char *, const char *);
+
void pkg_manifest_add_file(struct pkg_manifest *, const char *, const char *);
+
void pkg_manifest_add_dep(struct pkg_manifest *, const char *, const char *, const char *);
+
void pkg_manifest_add_exec(struct pkg_manifest *, const char *);
+
void pkg_manifest_add_unexec(struct pkg_manifest *, const char *);
+

+
/* Helper */
+
const char * pkg_manifest_keystr(json_t *, const char *);
+
void pkg_manifest_array_init(struct pkg_manifest *, enum array_type);
+
#endif