CJSON_PUBLIC(char *) cJSONUtils_FindPointerFromObjectTo(const cJSON * const object, const cJSON * const target) { size_t child_index = 0; cJSON *current_child = 0; if (object == target) { /* found */ return (char*)cJSONUtils_strdup((const unsigned char*)""); } /* recursively search all children of the object or array */ for (current_child = object->child; current_child != NULL; (void)(current_child = current_child->next), child_index++) { unsigned char *target_pointer = (unsigned char*)cJSONUtils_FindPointerFromObjectTo(current_child, target); /* found the target? */ if (target_pointer != NULL) { if (cJSON_IsArray(object)) { /* reserve enough memory for a 64 bit integer + '/' and '\0' */ unsigned char *full_pointer = (unsigned char*)cJSON_malloc(strlen((char*)target_pointer) + 20 + sizeof("/")); /* check if conversion to unsigned long is valid * This should be eliminated at compile time by dead code elimination * if size_t is an alias of unsigned long, or if it is bigger */ if (child_index > ULONG_MAX) { cJSON_free(target_pointer); return NULL; } sprintf((char*)full_pointer, "/%lu%s", (unsigned long)child_index, target_pointer); /* /<array_index><path> */ cJSON_free(target_pointer); return (char*)full_pointer; } if (cJSON_IsObject(object)) { unsigned char *full_pointer = (unsigned char*)cJSON_malloc(strlen((char*)target_pointer) + pointer_encoded_length((unsigned char*)current_child->string) + 2); full_pointer[0] = '/'; encode_string_as_pointer(full_pointer + 1, (unsigned char*)current_child->string); strcat((char*)full_pointer, (char*)target_pointer); cJSON_free(target_pointer); return (char*)full_pointer; } /* reached leaf of the tree, found nothing */ cJSON_free(target_pointer); return NULL; } } /* not found */ return NULL; }
CJSON_PUBLIC(char *) cJSONUtils_FindPointerFromObjectTo(cJSON *object, cJSON *target) { size_t c = 0; cJSON *obj = 0; if (object == target) { /* found */ return (char*)cJSONUtils_strdup((const unsigned char*)""); } /* recursively search all children of the object */ for (obj = object->child; obj; (void)(obj = obj->next), c++) { unsigned char *found = (unsigned char*)cJSONUtils_FindPointerFromObjectTo(obj, target); if (found) { if (cJSON_IsArray(object)) { /* reserve enough memory for a 64 bit integer + '/' and '\0' */ unsigned char *ret = (unsigned char*)malloc(strlen((char*)found) + 23); /* check if conversion to unsigned long is valid * This should be eliminated at compile time by dead code elimination * if size_t is an alias of unsigned long, or if it is bigger */ if (c > ULONG_MAX) { free(found); return NULL; } sprintf((char*)ret, "/%lu%s", (unsigned long)c, found); /* /<array_index><path> */ free(found); return (char*)ret; } else if (cJSON_IsObject(object)) { unsigned char *ret = (unsigned char*)malloc(strlen((char*)found) + cJSONUtils_PointerEncodedstrlen((unsigned char*)obj->string) + 2); *ret = '/'; cJSONUtils_PointerEncodedstrcpy(ret + 1, (unsigned char*)obj->string); strcat((char*)ret, (char*)found); free(found); return (char*)ret; } /* reached leaf of the tree, found nothing */ free(found); return NULL; } } /* not found */ return NULL; }
/* detach an item at the given path */ static cJSON *detach_path(cJSON *object, const unsigned char *path, const cJSON_bool case_sensitive) { unsigned char *parent_pointer = NULL; unsigned char *child_pointer = NULL; cJSON *parent = NULL; cJSON *detached_item = NULL; /* copy path and split it in parent and child */ parent_pointer = cJSONUtils_strdup(path); if (parent_pointer == NULL) { goto cleanup; } child_pointer = (unsigned char*)strrchr((char*)parent_pointer, '/'); /* last '/' */ if (child_pointer == NULL) { goto cleanup; } /* split strings */ child_pointer[0] = '\0'; child_pointer++; parent = get_item_from_pointer(object, (char*)parent_pointer, case_sensitive); decode_pointer_inplace(child_pointer); if (cJSON_IsArray(parent)) { size_t index = 0; if (!decode_array_index_from_pointer(child_pointer, &index)) { goto cleanup; } detached_item = detach_item_from_array(parent, index); } else if (cJSON_IsObject(parent)) { detached_item = cJSON_DetachItemFromObject(parent, (char*)child_pointer); } else { /* Couldn't find object to remove child from. */ goto cleanup; } cleanup: if (parent_pointer != NULL) { cJSON_free(parent_pointer); } return detached_item; }
char *cJSONUtils_FindPointerFromObjectTo(cJSON *object, cJSON *target) { int type = object->type; int c = 0; cJSON *obj = 0; if (object == target) { /* found */ return cJSONUtils_strdup(""); } /* recursively search all children of the object */ for (obj = object->child; obj; obj = obj->next, c++) { char *found = cJSONUtils_FindPointerFromObjectTo(obj, target); if (found) { if (type == cJSON_Array) { /* reserve enough memory for a 64 bit integer + '/' and '\0' */ char *ret = (char*)malloc(strlen(found) + 23); sprintf(ret, "/%d%s", c, found); /* /<array_index><path> */ free(found); return ret; } else if (type == cJSON_Object) { char *ret = (char*)malloc(strlen(found) + cJSONUtils_PointerEncodedstrlen(obj->string) + 2); *ret = '/'; cJSONUtils_PointerEncodedstrcpy(ret + 1, obj->string); strcat(ret, found); free(found); return ret; } /* reached leaf of the tree, found nothing */ free(found); return 0; } } /* not found */ return 0; }
static cJSON *cJSONUtils_PatchDetach(cJSON *object, const unsigned char *path) { unsigned char *parentptr = NULL; unsigned char *childptr = NULL; cJSON *parent = NULL; cJSON *ret = NULL; /* copy path and split it in parent and child */ parentptr = cJSONUtils_strdup(path); if (parentptr == NULL) { return NULL; } childptr = (unsigned char*)strrchr((char*)parentptr, '/'); /* last '/' */ if (childptr == NULL) { free(parentptr); return NULL; } /* split strings */ *childptr++ = '\0'; parent = cJSONUtils_GetPointer(object, (char*)parentptr); cJSONUtils_InplaceDecodePointerString(childptr); if (!parent) { /* Couldn't find object to remove child from. */ ret = NULL; } else if (cJSON_IsArray(parent)) { ret = cJSON_DetachItemFromArray(parent, atoi((char*)childptr)); } else if (cJSON_IsObject(parent)) { ret = cJSON_DetachItemFromObject(parent, (char*)childptr); } free(parentptr); /* return the detachted item */ return ret; }
static cJSON *cJSONUtils_PatchDetach(cJSON *object, const char *path) { char *parentptr = 0; char *childptr = 0; cJSON *parent = 0; cJSON *ret = 0; /* copy path and split it in parent and child */ parentptr = cJSONUtils_strdup(path); childptr = strrchr(parentptr, '/'); /* last '/' */ if (childptr) { /* split strings */ *childptr++ = '\0'; } parent = cJSONUtils_GetPointer(object, parentptr); cJSONUtils_InplaceDecodePointerString(childptr); if (!parent) { /* Couldn't find object to remove child from. */ ret = 0; } 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 the detachted item */ return ret; }
static int cJSONUtils_ApplyPatch(cJSON *object, cJSON *patch) { cJSON *op = 0; cJSON *path = 0; cJSON *value = 0; cJSON *parent = 0; int opcode = 0; char *parentptr = 0; char *childptr = 0; op = cJSON_GetObjectItem(patch, "op"); path = cJSON_GetObjectItem(patch, "path"); if (!op || !path) { /* malformed patch. */ return 2; } /* decode operation */ 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")) { /* compare value: {...} with the given path */ return cJSONUtils_Compare(cJSONUtils_GetPointer(object, path->valuestring), cJSON_GetObjectItem(patch, "value")); } else { /* unknown opcode. */ return 3; } /* Remove/Replace */ if ((opcode == 1) || (opcode == 2)) { /* Get rid of old. */ cJSON_Delete(cJSONUtils_PatchDetach(object, path->valuestring)); if (opcode == 1) { /* For Remove, this is job done. */ return 0; } } /* Copy/Move uses "from". */ if ((opcode == 3) || (opcode == 4)) { cJSON *from = cJSON_GetObjectItem(patch, "from"); if (!from) { /* missing "from" for copy/move. */ return 4; } if (opcode == 3) { /* move */ value = cJSONUtils_PatchDetach(object, from->valuestring); } if (opcode == 4) { /* copy */ value = cJSONUtils_GetPointer(object, from->valuestring); } if (!value) { /* missing "from" for copy/move. */ return 5; } if (opcode == 4) { value = cJSON_Duplicate(value, 1); } if (!value) { /* out of memory for copy/move. */ return 6; } } else /* Add/Replace uses "value". */ { value = cJSON_GetObjectItem(patch, "value"); if (!value) { /* missing "value" for add/replace. */ return 7; } value = cJSON_Duplicate(value, 1); if (!value) { /* out of memory for add/replace. */ return 8; } } /* Now, just add "value" to "path". */ /* split pointer in parent and child */ parentptr = cJSONUtils_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) { /* Couldn't find object to add to. */ free(parentptr); cJSON_Delete(value); return 9; } 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); } else { cJSON_Delete(value); } free(parentptr); return 0; }
static int apply_patch(cJSON *object, const cJSON *patch, const cJSON_bool case_sensitive) { cJSON *path = NULL; cJSON *value = NULL; cJSON *parent = NULL; enum patch_operation opcode = INVALID; unsigned char *parent_pointer = NULL; unsigned char *child_pointer = NULL; int status = 0; path = get_object_item(patch, "path", case_sensitive); if (!cJSON_IsString(path)) { /* malformed patch. */ status = 2; goto cleanup; } opcode = decode_patch_operation(patch, case_sensitive); if (opcode == INVALID) { status = 3; goto cleanup; } else if (opcode == TEST) { /* compare value: {...} with the given path */ status = !compare_json(get_item_from_pointer(object, path->valuestring, case_sensitive), get_object_item(patch, "value", case_sensitive), case_sensitive); goto cleanup; } /* special case for replacing the root */ if (path->valuestring[0] == '\0') { if (opcode == REMOVE) { static const cJSON invalid = { NULL, NULL, NULL, cJSON_Invalid, NULL, 0, 0, NULL}; overwrite_item(object, invalid); status = 0; goto cleanup; } if ((opcode == REPLACE) || (opcode == ADD)) { value = get_object_item(patch, "value", case_sensitive); if (value == NULL) { /* missing "value" for add/replace. */ status = 7; goto cleanup; } value = cJSON_Duplicate(value, 1); if (value == NULL) { /* out of memory for add/replace. */ status = 8; goto cleanup; } overwrite_item(object, *value); /* delete the duplicated value */ cJSON_free(value); value = NULL; /* the string "value" isn't needed */ if (object->string != NULL) { cJSON_free(object->string); object->string = NULL; } status = 0; goto cleanup; } } if ((opcode == REMOVE) || (opcode == REPLACE)) { /* Get rid of old. */ cJSON *old_item = detach_path(object, (unsigned char*)path->valuestring, case_sensitive); if (old_item == NULL) { status = 13; goto cleanup; } cJSON_Delete(old_item); if (opcode == REMOVE) { /* For Remove, this job is done. */ status = 0; goto cleanup; } } /* Copy/Move uses "from". */ if ((opcode == MOVE) || (opcode == COPY)) { cJSON *from = get_object_item(patch, "from", case_sensitive); if (from == NULL) { /* missing "from" for copy/move. */ status = 4; goto cleanup; } if (opcode == MOVE) { value = detach_path(object, (unsigned char*)from->valuestring, case_sensitive); } if (opcode == COPY) { value = get_item_from_pointer(object, from->valuestring, case_sensitive); } if (value == NULL) { /* missing "from" for copy/move. */ status = 5; goto cleanup; } if (opcode == COPY) { value = cJSON_Duplicate(value, 1); } if (value == NULL) { /* out of memory for copy/move. */ status = 6; goto cleanup; } } else /* Add/Replace uses "value". */ { value = get_object_item(patch, "value", case_sensitive); if (value == NULL) { /* missing "value" for add/replace. */ status = 7; goto cleanup; } value = cJSON_Duplicate(value, 1); if (value == NULL) { /* out of memory for add/replace. */ status = 8; goto cleanup; } } /* Now, just add "value" to "path". */ /* split pointer in parent and child */ parent_pointer = cJSONUtils_strdup((unsigned char*)path->valuestring); child_pointer = (unsigned char*)strrchr((char*)parent_pointer, '/'); if (child_pointer != NULL) { child_pointer[0] = '\0'; child_pointer++; } parent = get_item_from_pointer(object, (char*)parent_pointer, case_sensitive); decode_pointer_inplace(child_pointer); /* add, remove, replace, move, copy, test. */ if ((parent == NULL) || (child_pointer == NULL)) { /* Couldn't find object to add to. */ status = 9; goto cleanup; } else if (cJSON_IsArray(parent)) { if (strcmp((char*)child_pointer, "-") == 0) { cJSON_AddItemToArray(parent, value); value = NULL; } else { size_t index = 0; if (!decode_array_index_from_pointer(child_pointer, &index)) { status = 11; goto cleanup; } if (!insert_item_in_array(parent, index, value)) { status = 10; goto cleanup; } value = NULL; } } else if (cJSON_IsObject(parent)) { cJSON_DeleteItemFromObject(parent, (char*)child_pointer); cJSON_AddItemToObject(parent, (char*)child_pointer, value); value = NULL; } cleanup: if (value != NULL) { cJSON_Delete(value); } if (parent_pointer != NULL) { cJSON_free(parent_pointer); } return status; }