|  | @@ -1,6 +1,9 @@
 | 
	
		
			
				|  |  |  #include <ctype.h>
 | 
	
		
			
				|  |  | +#include <string.h>
 | 
	
		
			
				|  |  | +#include <stdlib.h>
 | 
	
		
			
				|  |  |  #include "cJSON_Utils.h"
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +// JSON Pointer implementation:
 | 
	
		
			
				|  |  |  static int cJSONUtils_Pstrcasecmp(const char *a,const char *e)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |  	if (!a || !e) return (a==e)?0:1;
 | 
	
	
		
			
				|  | @@ -12,7 +15,6 @@ static int cJSONUtils_Pstrcasecmp(const char *a,const char *e)
 | 
	
		
			
				|  |  |  	return 0;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  cJSON *cJSONUtils_GetPointer(cJSON *object,const char *pointer)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |  	cJSON *target=object;int which=0;const char *element=0;
 | 
	
	
		
			
				|  | @@ -36,4 +38,107 @@ cJSON *cJSONUtils_GetPointer(cJSON *object,const char *pointer)
 | 
	
		
			
				|  |  |  	return object;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +// JSON Patch implementation.
 | 
	
		
			
				|  |  | +static void cJSONUtils_InplaceDecodePointerString(char *string)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	char *s2=string;
 | 
	
		
			
				|  |  | +	for (;*string;s2++,string++)
 | 
	
		
			
				|  |  | +		*s2=(*string!='~')?(*string):((*(++string)=='0')?'~':'/');
 | 
	
		
			
				|  |  | +	*s2=0;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static cJSON *cJSONUtils_PatchDetach(cJSON *object,const char *path)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	char *parentptr=0,*childptr=0;cJSON *parent=0;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	parentptr=strdup(path);	childptr=strrchr(parentptr,'/');	if (childptr) *childptr++=0;
 | 
	
		
			
				|  |  | +	parent=cJSONUtils_GetPointer(object,parentptr);
 | 
	
		
			
				|  |  | +	cJSONUtils_InplaceDecodePointerString(childptr);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	cJSON *ret=0;
 | 
	
		
			
				|  |  | +	if (!parent) ret=0;	// Couldn't find object to remove child from.
 | 
	
		
			
				|  |  | +	else if (parent->type==cJSON_Array)		ret=cJSON_DetachItemFromArray(parent,atoi(childptr));
 | 
	
		
			
				|  |  | +	else if (parent->type==cJSON_Object)	ret=cJSON_DetachItemFromObject(parent,childptr);
 | 
	
		
			
				|  |  | +	free(parentptr);
 | 
	
		
			
				|  |  | +	return ret;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static int cJSONUtils_ApplyPatch(cJSON *object,cJSON *patch)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	cJSON *op=0,*path=0,*value=0;int opcode=0;
 | 
	
		
			
				|  |  | +	
 | 
	
		
			
				|  |  | +	op=cJSON_GetObjectItem(patch,"op");
 | 
	
		
			
				|  |  | +	path=cJSON_GetObjectItem(patch,"path");
 | 
	
		
			
				|  |  | +	if (!op || !path) return 2;	// malformed patch.
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if		(!strcmp(op->valuestring,"add"))	opcode=0;
 | 
	
		
			
				|  |  | +	else if (!strcmp(op->valuestring,"remove")) opcode=1;
 | 
	
		
			
				|  |  | +	else if (!strcmp(op->valuestring,"replace"))opcode=2;
 | 
	
		
			
				|  |  | +	else if (!strcmp(op->valuestring,"move"))	opcode=3;
 | 
	
		
			
				|  |  | +	else if (!strcmp(op->valuestring,"copy"))	opcode=4;
 | 
	
		
			
				|  |  | +	else if (!strcmp(op->valuestring,"test"))	opcode=5;
 | 
	
		
			
				|  |  | +	else return 3; // unknown opcode.
 | 
	
		
			
				|  |  | +	
 | 
	
		
			
				|  |  | +	if (opcode==5) return 10; // TEST IS CURRENTLY UNIMPLEMENTED.
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if (opcode==1 || opcode==2)	// Remove/Replace
 | 
	
		
			
				|  |  | +	{
 | 
	
		
			
				|  |  | +		cJSON_Delete(cJSONUtils_PatchDetach(object,path->valuestring));	// Get rid of old.
 | 
	
		
			
				|  |  | +		if (opcode==1) return 0;	// For Remove, this is job done.
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if (opcode==3 || opcode==4)	// Copy/Move uses "from".
 | 
	
		
			
				|  |  | +	{
 | 
	
		
			
				|  |  | +		cJSON *from=cJSON_GetObjectItem(patch,"from");	if (!from) return 4; // missing "from" for copy/move.
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		if (opcode==3) value=cJSONUtils_PatchDetach(object,from->valuestring);
 | 
	
		
			
				|  |  | +		if (opcode==4) value=cJSONUtils_GetPointer(object,from->valuestring);
 | 
	
		
			
				|  |  | +		if (!value) return 5; // missing "from" for copy/move.
 | 
	
		
			
				|  |  | +		if (opcode==4) value=cJSON_Duplicate(value,1);
 | 
	
		
			
				|  |  | +		if (!value) return 6; // out of memory for copy/move.
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	else	// Add/Replace uses "value".
 | 
	
		
			
				|  |  | +	{
 | 
	
		
			
				|  |  | +		value=cJSON_GetObjectItem(patch,"value");
 | 
	
		
			
				|  |  | +		if (!value) return 7; // missing "value" for add/replace.
 | 
	
		
			
				|  |  | +		value=cJSON_Duplicate(value,1);
 | 
	
		
			
				|  |  | +		if (!value) return 8; // out of memory for add/replace.
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +		
 | 
	
		
			
				|  |  | +	// Now, just add "value" to "path".
 | 
	
		
			
				|  |  | +	char *parentptr=0,*childptr=0;cJSON *parent=0;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	parentptr=strdup(path->valuestring);	childptr=strrchr(parentptr,'/');	if (childptr) *childptr++=0;
 | 
	
		
			
				|  |  | +	parent=cJSONUtils_GetPointer(object,parentptr);
 | 
	
		
			
				|  |  | +	cJSONUtils_InplaceDecodePointerString(childptr);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	// add, remove, replace, move, copy, test.
 | 
	
		
			
				|  |  | +	if (!parent) {free(parentptr); return 9;}	// Couldn't find object to add to.
 | 
	
		
			
				|  |  | +	else if (parent->type==cJSON_Array)
 | 
	
		
			
				|  |  | +	{
 | 
	
		
			
				|  |  | +		if (!strcmp(childptr,"-"))	cJSON_AddItemToArray(parent,value);
 | 
	
		
			
				|  |  | +		else						cJSON_InsertItemInArray(parent,atoi(childptr),value);
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	else if (parent->type==cJSON_Object)
 | 
	
		
			
				|  |  | +	{
 | 
	
		
			
				|  |  | +		cJSON_DeleteItemFromObject(parent,childptr);
 | 
	
		
			
				|  |  | +		cJSON_AddItemToObject(parent,childptr,value);
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	free(parentptr);
 | 
	
		
			
				|  |  | +	return 0;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +int cJSONUtils_ApplyPatches(cJSON *object,cJSON *patches)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	int err;
 | 
	
		
			
				|  |  | +	if (!patches->type==cJSON_Array) return 1;	// malformed patches.
 | 
	
		
			
				|  |  | +	if (patches) patches=patches->child;
 | 
	
		
			
				|  |  | +	while (patches)
 | 
	
		
			
				|  |  | +	{
 | 
	
		
			
				|  |  | +		if ((err=cJSONUtils_ApplyPatch(object,patches))) return err;
 | 
	
		
			
				|  |  | +		patches=patches->next;
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	return 0;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  
 |