cJSON_Utils.c 38.1 KB
Newer Older
M
Max Bruckner 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/*
  Copyright (c) 2009-2017 Dave Gamble and cJSON contributors

  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.
*/

23
#pragma GCC visibility push(default)
24
#include <ctype.h>
25 26
#include <string.h>
#include <stdlib.h>
27
#include <stdio.h>
M
Max Bruckner 已提交
28
#include <limits.h>
29
#pragma GCC visibility pop
M
Max Bruckner 已提交
30

31 32
#include "cJSON_Utils.h"

M
Max Bruckner 已提交
33 34 35 36
/* define our own boolean type */
#define true ((cJSON_bool)1)
#define false ((cJSON_bool)0)

M
Max Bruckner 已提交
37
static unsigned char* cJSONUtils_strdup(const unsigned char* const string)
38
{
M
Max Bruckner 已提交
39
    size_t length = 0;
40
    unsigned char *copy = NULL;
41

M
Max Bruckner 已提交
42 43 44
    length = strlen((const char*)string) + sizeof("");
    copy = (unsigned char*) cJSON_malloc(length);
    if (copy == NULL)
45
    {
46
        return NULL;
47
    }
M
Max Bruckner 已提交
48
    memcpy(copy, string, length);
49 50 51 52

    return copy;
}

53 54
/* string comparison which doesn't consider NULL pointers equal */
static int compare_strings(const unsigned char *string1, const unsigned char *string2, const cJSON_bool case_sensitive)
55
{
M
Max Bruckner 已提交
56
    if ((string1 == NULL) || (string2 == NULL))
M
Max Bruckner 已提交
57
    {
M
Max Bruckner 已提交
58
        return 1;
M
Max Bruckner 已提交
59
    }
M
Max Bruckner 已提交
60 61

    if (string1 == string2)
M
Max Bruckner 已提交
62
    {
M
Max Bruckner 已提交
63
        return 0;
M
Max Bruckner 已提交
64
    }
M
Max Bruckner 已提交
65

66 67 68 69 70
    if (case_sensitive)
    {
        return strcmp((const char*)string1, (const char*)string2);
    }

M
Max Bruckner 已提交
71
    for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++)
M
Max Bruckner 已提交
72
    {
M
Max Bruckner 已提交
73
        if (*string1 == '\0')
M
Max Bruckner 已提交
74 75 76 77 78
        {
            return 0;
        }
    }

M
Max Bruckner 已提交
79
    return tolower(*string1) - tolower(*string2);
80 81
}

M
Max Bruckner 已提交
82
/* Compare the next path element of two JSON pointers, two NULL pointers are considered unequal: */
83
static cJSON_bool compare_pointers(const unsigned char *name, const unsigned char *pointer, const cJSON_bool case_sensitive)
84
{
M
Max Bruckner 已提交
85
    if ((name == NULL) || (pointer == NULL))
86
    {
87
        return false;
88
    }
M
Max Bruckner 已提交
89 90

    for (; (*name != '\0') && (*pointer != '\0') && (*pointer != '/'); (void)name++, pointer++) /* compare until next '/' */
91
    {
M
Max Bruckner 已提交
92
        if (*pointer == '~')
93 94
        {
            /* check for escaped '~' (~0) and '/' (~1) */
M
Max Bruckner 已提交
95
            if (((pointer[1] != '0') || (*name != '~')) && ((pointer[1] != '1') || (*name != '/')))
96
            {
M
Max Bruckner 已提交
97
                /* invalid escape sequence or wrong character in *name */
98
                return false;
99 100 101
            }
            else
            {
M
Max Bruckner 已提交
102
                pointer++;
103 104
            }
        }
105
        else if ((!case_sensitive && (tolower(*name) != tolower(*pointer))) || (case_sensitive && (*name != *pointer)))
106
        {
107
            return false;
108 109
        }
    }
M
Max Bruckner 已提交
110
    if (((*pointer != 0) && (*pointer != '/')) != (*name != 0))
111 112
    {
        /* one string has ended, the other not */
113
        return false;;
114 115
    }

116
    return true;
117 118
}

119
/* calculate the length of a string if encoded as JSON pointer with ~0 and ~1 escape sequences */
120
static size_t pointer_encoded_length(const unsigned char *string)
121
{
122 123
    size_t length;
    for (length = 0; *string != '\0'; (void)string++, length++)
124
    {
125 126
        /* character needs to be escaped? */
        if ((*string == '~') || (*string == '/'))
127
        {
128
            length++;
129 130 131
        }
    }

132
    return length;
133
}
134

135
/* copy a string while escaping '~' and '/' with ~0 and ~1 JSON pointer escape codes */
136
static void encode_string_as_pointer(unsigned char *destination, const unsigned char *source)
137
{
138
    for (; source[0] != '\0'; (void)source++, destination++)
139
    {
140
        if (source[0] == '/')
141
        {
142 143
            destination[1] = '1';
            destination++;
144
        }
145
        else if (source[0] == '~')
146
        {
147 148 149
            destination[0] = '~';
            destination[1] = '1';
            destination++;
150 151 152
        }
        else
        {
153
            destination[0] = source[0];
154 155 156
        }
    }

157
    destination[0] = '\0';
158 159
}

160
CJSON_PUBLIC(char *) cJSONUtils_FindPointerFromObjectTo(const cJSON * const object, const cJSON * const target)
161
{
162 163
    size_t child_index = 0;
    cJSON *current_child = 0;
164

165 166 167 168 169
    if ((object == NULL) || (target == NULL))
    {
        return NULL;
    }

170 171 172
    if (object == target)
    {
        /* found */
173
        return (char*)cJSONUtils_strdup((const unsigned char*)"");
174
    }
175

176 177
    /* 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++)
178
    {
179 180 181
        unsigned char *target_pointer = (unsigned char*)cJSONUtils_FindPointerFromObjectTo(current_child, target);
        /* found the target? */
        if (target_pointer != NULL)
182
        {
183
            if (cJSON_IsArray(object))
184 185
            {
                /* reserve enough memory for a 64 bit integer + '/' and '\0' */
186
                unsigned char *full_pointer = (unsigned char*)cJSON_malloc(strlen((char*)target_pointer) + 20 + sizeof("/"));
187 188 189
                /* 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 */
190
                if (child_index > ULONG_MAX)
191
                {
192
                    cJSON_free(target_pointer);
193 194
                    return NULL;
                }
195 196
                sprintf((char*)full_pointer, "/%lu%s", (unsigned long)child_index, target_pointer); /* /<array_index><path> */
                cJSON_free(target_pointer);
197

198
                return (char*)full_pointer;
199
            }
200 201

            if (cJSON_IsObject(object))
202
            {
203
                unsigned char *full_pointer = (unsigned char*)cJSON_malloc(strlen((char*)target_pointer) + pointer_encoded_length((unsigned char*)current_child->string) + 2);
204
                full_pointer[0] = '/';
205
                encode_string_as_pointer(full_pointer + 1, (unsigned char*)current_child->string);
206 207
                strcat((char*)full_pointer, (char*)target_pointer);
                cJSON_free(target_pointer);
208

209
                return (char*)full_pointer;
210 211 212
            }

            /* reached leaf of the tree, found nothing */
213
            cJSON_free(target_pointer);
214
            return NULL;
215 216 217 218
        }
    }

    /* not found */
219
    return NULL;
220 221
}

222 223 224 225 226 227 228 229 230 231 232 233 234
/* non broken version of cJSON_GetArrayItem */
static cJSON *get_array_item(const cJSON *array, size_t item)
{
    cJSON *child = array ? array->child : NULL;
    while ((child != NULL) && (item > 0))
    {
        item--;
        child = child->next;
    }

    return child;
}

235 236 237 238 239 240 241 242 243 244 245
static cJSON_bool decode_array_index_from_pointer(const unsigned char * const pointer, size_t * const index)
{
    size_t parsed_index = 0;
    size_t position = 0;

    if ((pointer[0] == '0') && ((pointer[1] != '\0') && (pointer[1] != '/')))
    {
        /* leading zeroes are not permitted */
        return 0;
    }

246
    for (position = 0; (pointer[position] >= '0') && (pointer[0] <= '9'); position++)
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
    {
        parsed_index = (10 * parsed_index) + (size_t)(pointer[position] - '0');

    }

    if ((pointer[position] != '\0') && (pointer[position] != '/'))
    {
        return 0;
    }

    *index = parsed_index;

    return 1;
}

262
static cJSON *get_item_from_pointer(cJSON * const object, const char * pointer, const cJSON_bool case_sensitive)
263
{
M
Max Bruckner 已提交
264
    cJSON *current_element = object;
265
    /* follow path of the pointer */
M
Max Bruckner 已提交
266
    while ((pointer[0] == '/') && (current_element != NULL))
267
    {
M
Max Bruckner 已提交
268 269
        pointer++;
        if (cJSON_IsArray(current_element))
270
        {
271
            size_t index = 0;
272
            if (!decode_array_index_from_pointer((const unsigned char*)pointer, &index))
273
            {
274
                return NULL;
275
            }
276

M
Max Bruckner 已提交
277
            current_element = get_array_item(current_element, index);
278
        }
M
Max Bruckner 已提交
279
        else if (cJSON_IsObject(current_element))
280
        {
M
Max Bruckner 已提交
281
            current_element = current_element->child;
282
            /* GetObjectItem. */
283
            while ((current_element != NULL) && !compare_pointers((unsigned char*)current_element->string, (const unsigned char*)pointer, case_sensitive))
284
            {
M
Max Bruckner 已提交
285
                current_element = current_element->next;
286 287 288 289
            }
        }
        else
        {
290
            return NULL;
291
        }
292 293 294 295 296 297

        /* skip to the next path token or end of string */
        while ((pointer[0] != '\0') && (pointer[0] != '/'))
        {
            pointer++;
        }
298 299
    }

M
Max Bruckner 已提交
300
    return current_element;
301 302
}

303 304 305 306 307
CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointer(cJSON * const object, const char *pointer)
{
    return get_item_from_pointer(object, pointer, false);
}

308 309 310 311 312
CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointerCaseSensitive(cJSON * const object, const char *pointer)
{
    return get_item_from_pointer(object, pointer, true);
}

313
/* JSON Patch implementation. */
314
static void decode_pointer_inplace(unsigned char *string)
315
{
316
    unsigned char *decoded_string = string;
317 318 319 320 321

    if (string == NULL) {
        return;
    }

322
    for (; *string; (void)decoded_string++, string++)
323
    {
324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
        if (string[0] == '~')
        {
            if (string[1] == '0')
            {
                decoded_string[0] = '~';
            }
            else if (string[1] == '1')
            {
                decoded_string[1] = '/';
            }
            else
            {
                /* invalid escape sequence */
                return;
            }

            string++;
        }
342 343
    }

344
    decoded_string[0] = '\0';
345 346
}

347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379
/* non-broken cJSON_DetachItemFromArray */
static cJSON *detach_item_from_array(cJSON *array, size_t which)
{
    cJSON *c = array->child;
    while (c && (which > 0))
    {
        c = c->next;
        which--;
    }
    if (!c)
    {
        /* item doesn't exist */
        return NULL;
    }
    if (c->prev)
    {
        /* not the first element */
        c->prev->next = c->next;
    }
    if (c->next)
    {
        c->next->prev = c->prev;
    }
    if (c==array->child)
    {
        array->child = c->next;
    }
    /* make sure the detached item doesn't point anywhere anymore */
    c->prev = c->next = NULL;

    return c;
}

380
/* detach an item at the given path */
381
static cJSON *detach_path(cJSON *object, const unsigned char *path, const cJSON_bool case_sensitive)
382
{
M
Max Bruckner 已提交
383 384
    unsigned char *parent_pointer = NULL;
    unsigned char *child_pointer = NULL;
385
    cJSON *parent = NULL;
M
Max Bruckner 已提交
386
    cJSON *detached_item = NULL;
387 388

    /* copy path and split it in parent and child */
M
Max Bruckner 已提交
389 390 391
    parent_pointer = cJSONUtils_strdup(path);
    if (parent_pointer == NULL) {
        goto cleanup;
392 393
    }

M
Max Bruckner 已提交
394 395
    child_pointer = (unsigned char*)strrchr((char*)parent_pointer, '/'); /* last '/' */
    if (child_pointer == NULL)
396
    {
M
Max Bruckner 已提交
397
        goto cleanup;
398
    }
399
    /* split strings */
M
Max Bruckner 已提交
400 401
    child_pointer[0] = '\0';
    child_pointer++;
402

403
    parent = get_item_from_pointer(object, (char*)parent_pointer, case_sensitive);
404
    decode_pointer_inplace(child_pointer);
405

M
Max Bruckner 已提交
406
    if (cJSON_IsArray(parent))
407
    {
408
        size_t index = 0;
M
Max Bruckner 已提交
409
        if (!decode_array_index_from_pointer(child_pointer, &index))
410
        {
M
Max Bruckner 已提交
411
            goto cleanup;
412
        }
M
Max Bruckner 已提交
413
        detached_item = detach_item_from_array(parent, index);
414
    }
415
    else if (cJSON_IsObject(parent))
416
    {
M
Max Bruckner 已提交
417 418 419 420 421 422 423 424 425 426 427 428
        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);
429
    }
430

M
Max Bruckner 已提交
431
    return detached_item;
432 433
}

434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549
/* sort lists using mergesort */
static cJSON *sort_list(cJSON *list, const cJSON_bool case_sensitive)
{
    cJSON *first = list;
    cJSON *second = list;
    cJSON *current_item = list;
    cJSON *result = list;
    cJSON *result_tail = NULL;

    if ((list == NULL) || (list->next == NULL))
    {
        /* One entry is sorted already. */
        return result;
    }

    while ((current_item != NULL) && (current_item->next != NULL) && (compare_strings((unsigned char*)current_item->string, (unsigned char*)current_item->next->string, case_sensitive) < 0))
    {
        /* Test for list sorted. */
        current_item = current_item->next;
    }
    if ((current_item == NULL) || (current_item->next == NULL))
    {
        /* Leave sorted lists unmodified. */
        return result;
    }

    /* reset pointer to the beginning */
    current_item = list;
    while (current_item != NULL)
    {
        /* Walk two pointers to find the middle. */
        second = second->next;
        current_item = current_item->next;
        /* advances current_item two steps at a time */
        if (current_item != NULL)
        {
            current_item = current_item->next;
        }
    }
    if ((second != NULL) && (second->prev != NULL))
    {
        /* Split the lists */
        second->prev->next = NULL;
    }

    /* Recursively sort the sub-lists. */
    first = sort_list(first, case_sensitive);
    second = sort_list(second, case_sensitive);
    result = NULL;

    /* Merge the sub-lists */
    while ((first != NULL) && (second != NULL))
    {
        cJSON *smaller = NULL;
        if (compare_strings((unsigned char*)first->string, (unsigned char*)second->string, false) < 0)
        {
            smaller = first;
        }
        else
        {
            smaller = second;
        }

        if (result == NULL)
        {
            /* start merged list with the smaller element */
            result_tail = smaller;
            result = smaller;
        }
        else
        {
            /* add smaller element to the list */
            result_tail->next = smaller;
            smaller->prev = result_tail;
            result_tail = smaller;
        }

        if (first == smaller)
        {
            first = first->next;
        }
        else
        {
            second = second->next;
        }
    }

    if (first != NULL)
    {
        /* Append rest of first list. */
        if (result == NULL)
        {
            return first;
        }
        result_tail->next = first;
        first->prev = result_tail;
    }
    if (second != NULL)
    {
        /* Append rest of second list */
        if (result == NULL)
        {
            return second;
        }
        result_tail->next = second;
        second->prev = result_tail;
    }

    return result;
}

static void sort_object(cJSON * const object, const cJSON_bool case_sensitive)
{
    object->child = sort_list(object->child, case_sensitive);
}

550
static cJSON_bool compare_json(cJSON *a, cJSON *b, const cJSON_bool case_sensitive)
551
{
552
    if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)))
M
Max Bruckner 已提交
553 554
    {
        /* mismatched type. */
555
        return false;
M
Max Bruckner 已提交
556
    }
557
    switch (a->type & 0xFF)
M
Max Bruckner 已提交
558 559 560
    {
        case cJSON_Number:
            /* numeric mismatch. */
M
Max Bruckner 已提交
561 562
            if ((a->valueint != b->valueint) || (a->valuedouble != b->valuedouble))
            {
563
                return false;
M
Max Bruckner 已提交
564 565 566
            }
            else
            {
567
                return true;
M
Max Bruckner 已提交
568 569
            }

M
Max Bruckner 已提交
570 571
        case cJSON_String:
            /* string mismatch. */
M
Max Bruckner 已提交
572
            if (strcmp(a->valuestring, b->valuestring) != 0)
M
Max Bruckner 已提交
573
            {
574
                return false;
M
Max Bruckner 已提交
575 576 577
            }
            else
            {
578
                return true;
M
Max Bruckner 已提交
579 580
            }

M
Max Bruckner 已提交
581
        case cJSON_Array:
M
Max Bruckner 已提交
582
            for ((void)(a = a->child), b = b->child; (a != NULL) && (b != NULL); (void)(a = a->next), b = b->next)
M
Max Bruckner 已提交
583
            {
584
                cJSON_bool identical = compare_json(a, b, case_sensitive);
585
                if (!identical)
M
Max Bruckner 已提交
586
                {
587
                    return false;
M
Max Bruckner 已提交
588 589
                }
            }
M
Max Bruckner 已提交
590

M
Max Bruckner 已提交
591
            /* array size mismatch? (one of both children is not NULL) */
M
Max Bruckner 已提交
592 593
            if ((a != NULL) || (b != NULL))
            {
594
                return false;
M
Max Bruckner 已提交
595 596 597
            }
            else
            {
598
                return true;
M
Max Bruckner 已提交
599 600
            }

M
Max Bruckner 已提交
601
        case cJSON_Object:
602 603
            sort_object(a, case_sensitive);
            sort_object(b, case_sensitive);
M
Max Bruckner 已提交
604
            for ((void)(a = a->child), b = b->child; (a != NULL) && (b != NULL); (void)(a = a->next), b = b->next)
M
Max Bruckner 已提交
605
            {
606
                cJSON_bool identical = false;
M
Max Bruckner 已提交
607
                /* compare object keys */
608
                if (compare_strings((unsigned char*)a->string, (unsigned char*)b->string, case_sensitive))
M
Max Bruckner 已提交
609 610
                {
                    /* missing member */
611
                    return false;
M
Max Bruckner 已提交
612
                }
613
                identical = compare_json(a, b, case_sensitive);
614
                if (!identical)
M
Max Bruckner 已提交
615
                {
616
                    return false;
M
Max Bruckner 已提交
617 618
                }
            }
M
Max Bruckner 已提交
619

M
Max Bruckner 已提交
620
            /* object length mismatch (one of both children is not null) */
M
Max Bruckner 已提交
621 622
            if ((a != NULL) || (b != NULL))
            {
623
                return false;
M
Max Bruckner 已提交
624 625 626
            }
            else
            {
627
                return true;
M
Max Bruckner 已提交
628
            }
629

M
Max Bruckner 已提交
630 631 632
        default:
            break;
    }
M
Max Bruckner 已提交
633

M
Max Bruckner 已提交
634
    /* null, true or false */
635
    return true;
636 637
}

638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675
/* non broken version of cJSON_InsertItemInArray */
static cJSON_bool insert_item_in_array(cJSON *array, size_t which, cJSON *newitem)
{
    cJSON *child = array->child;
    while (child && (which > 0))
    {
        child = child->next;
        which--;
    }
    if (which > 0)
    {
        /* item is after the end of the array */
        return 0;
    }
    if (child == NULL)
    {
        cJSON_AddItemToArray(array, newitem);
        return 1;
    }

    /* insert into the linked list */
    newitem->next = child;
    newitem->prev = child->prev;
    child->prev = newitem;

    /* was it at the beginning */
    if (child == array->child)
    {
        array->child = newitem;
    }
    else
    {
        newitem->prev->next = newitem;
    }

    return 1;
}

676 677 678 679 680 681 682 683 684 685
static cJSON *get_object_item(const cJSON * const object, const char* name, const cJSON_bool case_sensitive)
{
    if (case_sensitive)
    {
        return cJSON_GetObjectItemCaseSensitive(object, name);
    }

    return cJSON_GetObjectItem(object, name);
}

M
Max Bruckner 已提交
686 687
enum patch_operation { INVALID, ADD, REMOVE, REPLACE, MOVE, COPY, TEST };

688
static enum patch_operation decode_patch_operation(const cJSON * const patch, const cJSON_bool case_sensitive)
689
{
690
    cJSON *operation = get_object_item(patch, "op", case_sensitive);
M
Max Bruckner 已提交
691 692 693 694
    if (!cJSON_IsString(operation))
    {
        return INVALID;
    }
695

M
Max Bruckner 已提交
696
    if (strcmp(operation->valuestring, "add") == 0)
M
Max Bruckner 已提交
697
    {
M
Max Bruckner 已提交
698
        return ADD;
M
Max Bruckner 已提交
699
    }
700

M
Max Bruckner 已提交
701
    if (strcmp(operation->valuestring, "remove") == 0)
M
Max Bruckner 已提交
702
    {
M
Max Bruckner 已提交
703
        return REMOVE;
M
Max Bruckner 已提交
704
    }
M
Max Bruckner 已提交
705 706

    if (strcmp(operation->valuestring, "replace") == 0)
M
Max Bruckner 已提交
707
    {
M
Max Bruckner 已提交
708
        return REPLACE;
M
Max Bruckner 已提交
709
    }
M
Max Bruckner 已提交
710 711

    if (strcmp(operation->valuestring, "move") == 0)
M
Max Bruckner 已提交
712
    {
M
Max Bruckner 已提交
713
        return MOVE;
M
Max Bruckner 已提交
714
    }
M
Max Bruckner 已提交
715 716

    if (strcmp(operation->valuestring, "copy") == 0)
M
Max Bruckner 已提交
717
    {
M
Max Bruckner 已提交
718
        return COPY;
M
Max Bruckner 已提交
719
    }
M
Max Bruckner 已提交
720 721

    if (strcmp(operation->valuestring, "test") == 0)
M
Max Bruckner 已提交
722
    {
M
Max Bruckner 已提交
723
        return TEST;
M
Max Bruckner 已提交
724
    }
M
Max Bruckner 已提交
725 726 727 728 729 730 731 732

    return INVALID;
}

/* overwrite and existing item with another one and free resources on the way */
static void overwrite_item(cJSON * const root, const cJSON replacement)
{
    if (root == NULL)
M
Max Bruckner 已提交
733
    {
M
Max Bruckner 已提交
734
        return;
M
Max Bruckner 已提交
735
    }
M
Max Bruckner 已提交
736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752

    if (root->string != NULL)
    {
        cJSON_free(root->string);
    }
    if (root->valuestring != NULL)
    {
        cJSON_free(root->valuestring);
    }
    if (root->child != NULL)
    {
        cJSON_Delete(root->child);
    }

    memcpy(root, &replacement, sizeof(cJSON));
}

753
static int apply_patch(cJSON *object, const cJSON *patch, const cJSON_bool case_sensitive)
M
Max Bruckner 已提交
754 755 756 757 758 759 760 761 762
{
    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;

763
    path = get_object_item(patch, "path", case_sensitive);
M
Max Bruckner 已提交
764
    if (!cJSON_IsString(path))
M
Max Bruckner 已提交
765
    {
M
Max Bruckner 已提交
766 767 768 769 770
        /* malformed patch. */
        status = 2;
        goto cleanup;
    }

771
    opcode = decode_patch_operation(patch, case_sensitive);
M
Max Bruckner 已提交
772 773 774 775 776 777 778 779
    if (opcode == INVALID)
    {
        status = 3;
        goto cleanup;
    }
    else if (opcode == TEST)
    {
        /* compare value: {...} with the given path */
780
        status = !compare_json(get_item_from_pointer(object, path->valuestring, case_sensitive), get_object_item(patch, "value", case_sensitive), case_sensitive);
M
Max Bruckner 已提交
781
        goto cleanup;
M
Max Bruckner 已提交
782
    }
783

784 785 786 787 788
    /* special case for replacing the root */
    if (path->valuestring[0] == '\0')
    {
        if (opcode == REMOVE)
        {
M
Max Bruckner 已提交
789
            static const cJSON invalid = { NULL, NULL, NULL, cJSON_Invalid, NULL, 0, 0, NULL};
790

M
Max Bruckner 已提交
791
            overwrite_item(object, invalid);
792

M
Max Bruckner 已提交
793 794
            status = 0;
            goto cleanup;
795 796 797 798
        }

        if ((opcode == REPLACE) || (opcode == ADD))
        {
799
            value = get_object_item(patch, "value", case_sensitive);
800 801 802
            if (value == NULL)
            {
                /* missing "value" for add/replace. */
M
Max Bruckner 已提交
803 804
                status = 7;
                goto cleanup;
805 806 807 808 809 810
            }

            value = cJSON_Duplicate(value, 1);
            if (value == NULL)
            {
                /* out of memory for add/replace. */
M
Max Bruckner 已提交
811 812
                status = 8;
                goto cleanup;
813 814
            }

M
Max Bruckner 已提交
815
            overwrite_item(object, *value);
816 817 818

            /* delete the duplicated value */
            cJSON_free(value);
M
Max Bruckner 已提交
819
            value = NULL;
820

M
Max Bruckner 已提交
821 822 823 824 825 826 827 828 829
            /* the string "value" isn't needed */
            if (object->string != NULL)
            {
                cJSON_free(object->string);
                object->string = NULL;
            }

            status = 0;
            goto cleanup;
830 831 832
        }
    }

M
Max Bruckner 已提交
833
    if ((opcode == REMOVE) || (opcode == REPLACE))
M
Max Bruckner 已提交
834 835
    {
        /* Get rid of old. */
836
        cJSON *old_item = detach_path(object, (unsigned char*)path->valuestring, case_sensitive);
837 838
        if (old_item == NULL)
        {
M
Max Bruckner 已提交
839 840
            status = 13;
            goto cleanup;
841 842
        }
        cJSON_Delete(old_item);
M
Max Bruckner 已提交
843
        if (opcode == REMOVE)
M
Max Bruckner 已提交
844
        {
845
            /* For Remove, this job is done. */
M
Max Bruckner 已提交
846 847
            status = 0;
            goto cleanup;
M
Max Bruckner 已提交
848 849
        }
    }
850

M
Max Bruckner 已提交
851
    /* Copy/Move uses "from". */
M
Max Bruckner 已提交
852
    if ((opcode == MOVE) || (opcode == COPY))
M
Max Bruckner 已提交
853
    {
854
        cJSON *from = get_object_item(patch, "from", case_sensitive);
M
Max Bruckner 已提交
855
        if (from == NULL)
M
Max Bruckner 已提交
856 857
        {
            /* missing "from" for copy/move. */
M
Max Bruckner 已提交
858 859
            status = 4;
            goto cleanup;
M
Max Bruckner 已提交
860
        }
861

M
Max Bruckner 已提交
862
        if (opcode == MOVE)
M
Max Bruckner 已提交
863
        {
864
            value = detach_path(object, (unsigned char*)from->valuestring, case_sensitive);
M
Max Bruckner 已提交
865
        }
M
Max Bruckner 已提交
866
        if (opcode == COPY)
M
Max Bruckner 已提交
867
        {
868
            value = get_item_from_pointer(object, from->valuestring, case_sensitive);
M
Max Bruckner 已提交
869
        }
M
Max Bruckner 已提交
870
        if (value == NULL)
M
Max Bruckner 已提交
871 872
        {
            /* missing "from" for copy/move. */
M
Max Bruckner 已提交
873 874
            status = 5;
            goto cleanup;
M
Max Bruckner 已提交
875
        }
M
Max Bruckner 已提交
876
        if (opcode == COPY)
M
Max Bruckner 已提交
877 878 879
        {
            value = cJSON_Duplicate(value, 1);
        }
M
Max Bruckner 已提交
880
        if (value == NULL)
M
Max Bruckner 已提交
881 882
        {
            /* out of memory for copy/move. */
M
Max Bruckner 已提交
883 884
            status = 6;
            goto cleanup;
M
Max Bruckner 已提交
885 886 887 888
        }
    }
    else /* Add/Replace uses "value". */
    {
889
        value = get_object_item(patch, "value", case_sensitive);
M
Max Bruckner 已提交
890
        if (value == NULL)
M
Max Bruckner 已提交
891 892
        {
            /* missing "value" for add/replace. */
M
Max Bruckner 已提交
893 894
            status = 7;
            goto cleanup;
M
Max Bruckner 已提交
895 896
        }
        value = cJSON_Duplicate(value, 1);
M
Max Bruckner 已提交
897
        if (value == NULL)
M
Max Bruckner 已提交
898 899
        {
            /* out of memory for add/replace. */
M
Max Bruckner 已提交
900 901
            status = 8;
            goto cleanup;
M
Max Bruckner 已提交
902 903
        }
    }
904

M
Max Bruckner 已提交
905
    /* Now, just add "value" to "path". */
906

M
Max Bruckner 已提交
907
    /* split pointer in parent and child */
M
Max Bruckner 已提交
908 909 910
    parent_pointer = cJSONUtils_strdup((unsigned char*)path->valuestring);
    child_pointer = (unsigned char*)strrchr((char*)parent_pointer, '/');
    if (child_pointer != NULL)
M
Max Bruckner 已提交
911
    {
M
Max Bruckner 已提交
912 913
        child_pointer[0] = '\0';
        child_pointer++;
M
Max Bruckner 已提交
914
    }
915
    parent = get_item_from_pointer(object, (char*)parent_pointer, case_sensitive);
916
    decode_pointer_inplace(child_pointer);
917

M
Max Bruckner 已提交
918
    /* add, remove, replace, move, copy, test. */
M
Max Bruckner 已提交
919
    if ((parent == NULL) || (child_pointer == NULL))
M
Max Bruckner 已提交
920 921
    {
        /* Couldn't find object to add to. */
M
Max Bruckner 已提交
922 923
        status = 9;
        goto cleanup;
M
Max Bruckner 已提交
924
    }
925
    else if (cJSON_IsArray(parent))
M
Max Bruckner 已提交
926
    {
M
Max Bruckner 已提交
927
        if (strcmp((char*)child_pointer, "-") == 0)
M
Max Bruckner 已提交
928 929
        {
            cJSON_AddItemToArray(parent, value);
M
Max Bruckner 已提交
930
            value = NULL;
M
Max Bruckner 已提交
931 932 933
        }
        else
        {
934
            size_t index = 0;
M
Max Bruckner 已提交
935
            if (!decode_array_index_from_pointer(child_pointer, &index))
936
            {
M
Max Bruckner 已提交
937 938
                status = 11;
                goto cleanup;
939 940
            }

941
            if (!insert_item_in_array(parent, index, value))
942
            {
M
Max Bruckner 已提交
943 944
                status = 10;
                goto cleanup;
945
            }
M
Max Bruckner 已提交
946
            value = NULL;
M
Max Bruckner 已提交
947 948
        }
    }
949
    else if (cJSON_IsObject(parent))
M
Max Bruckner 已提交
950
    {
951 952 953 954 955 956 957 958
        if (case_sensitive)
        {
            cJSON_DeleteItemFromObjectCaseSensitive(parent, (char*)child_pointer);
        }
        else
        {
            cJSON_DeleteItemFromObject(parent, (char*)child_pointer);
        }
M
Max Bruckner 已提交
959 960
        cJSON_AddItemToObject(parent, (char*)child_pointer, value);
        value = NULL;
M
Max Bruckner 已提交
961
    }
M
Max Bruckner 已提交
962 963 964

cleanup:
    if (value != NULL)
M
Max Bruckner 已提交
965 966 967
    {
        cJSON_Delete(value);
    }
M
Max Bruckner 已提交
968 969 970 971
    if (parent_pointer != NULL)
    {
        cJSON_free(parent_pointer);
    }
M
Max Bruckner 已提交
972

M
Max Bruckner 已提交
973
    return status;
M
Max Bruckner 已提交
974
}
975

M
Max Bruckner 已提交
976
CJSON_PUBLIC(int) cJSONUtils_ApplyPatches(cJSON * const object, const cJSON * const patches)
977
{
M
Max Bruckner 已提交
978 979
    const cJSON *current_patch = NULL;
    int status = 0;
980

981
    if (!cJSON_IsArray(patches))
982 983 984 985
    {
        /* malformed patches. */
        return 1;
    }
M
Max Bruckner 已提交
986 987

    if (patches != NULL)
988
    {
M
Max Bruckner 已提交
989
        current_patch = patches->child;
990
    }
M
Max Bruckner 已提交
991 992

    while (current_patch != NULL)
993
    {
994
        status = apply_patch(object, current_patch, false);
M
Max Bruckner 已提交
995
        if (status != 0)
996
        {
M
Max Bruckner 已提交
997
            return status;
998
        }
M
Max Bruckner 已提交
999
        current_patch = current_patch->next;
1000 1001 1002
    }

    return 0;
1003
}
1004

1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033
CJSON_PUBLIC(int) cJSONUtils_ApplyPatchesCaseSensitive(cJSON * const object, const cJSON * const patches)
{
    const cJSON *current_patch = NULL;
    int status = 0;

    if (!cJSON_IsArray(patches))
    {
        /* malformed patches. */
        return 1;
    }

    if (patches != NULL)
    {
        current_patch = patches->child;
    }

    while (current_patch != NULL)
    {
        status = apply_patch(object, current_patch, true);
        if (status != 0)
        {
            return status;
        }
        current_patch = current_patch->next;
    }

    return 0;
}

1034
static void compose_patch(cJSON * const patches, const unsigned char * const operation, const unsigned char * const path, const unsigned char *suffix, const cJSON * const value)
1035
{
1036
    cJSON *patch = cJSON_CreateObject();
M
Max Bruckner 已提交
1037
    if (patch == NULL)
1038
    {
M
Max Bruckner 已提交
1039
        return;
1040
    }
M
Max Bruckner 已提交
1041 1042 1043
    cJSON_AddItemToObject(patch, "op", cJSON_CreateString((const char*)operation));

    if (suffix == NULL)
1044
    {
1045
        cJSON_AddItemToObject(patch, "path", cJSON_CreateString((const char*)path));
1046
    }
M
Max Bruckner 已提交
1047 1048
    else
    {
1049
        size_t suffix_length = pointer_encoded_length(suffix);
M
Max Bruckner 已提交
1050 1051 1052 1053
        size_t path_length = strlen((const char*)path);
        unsigned char *full_path = (unsigned char*)cJSON_malloc(path_length + suffix_length + sizeof("/"));

        sprintf((char*)full_path, "%s/", (const char*)path);
1054
        encode_string_as_pointer(full_path + path_length + 1, suffix);
M
Max Bruckner 已提交
1055 1056

        cJSON_AddItemToObject(patch, "path", cJSON_CreateString((const char*)full_path));
C
crhackos 已提交
1057
        cJSON_free(full_path);
M
Max Bruckner 已提交
1058 1059 1060
    }

    if (value != NULL)
1061
    {
M
Max Bruckner 已提交
1062
        cJSON_AddItemToObject(patch, "value", cJSON_Duplicate(value, 1));
1063 1064
    }
    cJSON_AddItemToArray(patches, patch);
1065 1066
}

1067
CJSON_PUBLIC(void) cJSONUtils_AddPatchToArray(cJSON * const array, const char * const operation, const char * const path, const cJSON * const value)
M
Max Bruckner 已提交
1068
{
1069
    compose_patch(array, (const unsigned char*)operation, (const unsigned char*)path, NULL, value);
M
Max Bruckner 已提交
1070
}
1071

1072
static void create_patches(cJSON * const patches, const unsigned char * const path, cJSON * const from, cJSON * const to, const cJSON_bool case_sensitive)
1073
{
1074 1075 1076 1077 1078
    if ((from == NULL) || (to == NULL))
    {
        return;
    }

1079
    if ((from->type & 0xFF) != (to->type & 0xFF))
1080
    {
1081
        compose_patch(patches, (const unsigned char*)"replace", path, 0, to);
1082 1083 1084
        return;
    }

1085
    switch (from->type & 0xFF)
1086 1087 1088 1089
    {
        case cJSON_Number:
            if ((from->valueint != to->valueint) || (from->valuedouble != to->valuedouble))
            {
1090
                compose_patch(patches, (const unsigned char*)"replace", path, NULL, to);
1091 1092 1093 1094
            }
            return;

        case cJSON_String:
M
Max Bruckner 已提交
1095
            if (strcmp(from->valuestring, to->valuestring) != 0)
1096
            {
1097
                compose_patch(patches, (const unsigned char*)"replace", path, NULL, to);
1098 1099
            }
            return;
1100

1101 1102
        case cJSON_Array:
        {
1103 1104 1105 1106 1107 1108 1109
            size_t index = 0;
            cJSON *from_child = from->child;
            cJSON *to_child = to->child;
            unsigned char *new_path = (unsigned char*)cJSON_malloc(strlen((const char*)path) + 20 + sizeof("/")); /* Allow space for 64bit int. log10(2^64) = 20 */

            /* generate patches for all array elements that exist in both "from" and "to" */
            for (index = 0; (from_child != NULL) && (to_child != NULL); (void)(from_child = from_child->next), (void)(to_child = to_child->next), index++)
1110
            {
1111 1112 1113
                /* 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 */
1114
                if (index > ULONG_MAX)
1115
                {
C
crhackos 已提交
1116
                    cJSON_free(new_path);
1117 1118
                    return;
                }
1119
                sprintf((char*)new_path, "%s/%lu", path, (unsigned long)index); /* path of the current array element */
1120
                create_patches(patches, new_path, from_child, to_child, case_sensitive);
1121
            }
1122

1123
            /* remove leftover elements from 'from' that are not in 'to' */
1124
            for (; (from_child != NULL); (void)(from_child = from_child->next))
1125
            {
1126 1127 1128
                /* 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 */
1129
                if (index > ULONG_MAX)
1130
                {
C
crhackos 已提交
1131
                    cJSON_free(new_path);
1132 1133
                    return;
                }
1134
                sprintf((char*)new_path, "%lu", (unsigned long)index);
1135
                compose_patch(patches, (const unsigned char*)"remove", path, new_path, NULL);
1136 1137
            }
            /* add new elements in 'to' that were not in 'from' */
1138
            for (; (to_child != NULL); (void)(to_child = to_child->next), index++)
1139
            {
1140
                compose_patch(patches, (const unsigned char*)"add", path, (const unsigned char*)"-", to_child);
1141
            }
C
crhackos 已提交
1142
            cJSON_free(new_path);
1143 1144 1145 1146 1147
            return;
        }

        case cJSON_Object:
        {
1148 1149
            cJSON *from_child = NULL;
            cJSON *to_child = NULL;
1150 1151
            sort_object(from, case_sensitive);
            sort_object(to, case_sensitive);
1152

1153 1154
            from_child = from->child;
            to_child = to->child;
1155
            /* for all object values in the object with more of them */
1156
            while ((from_child != NULL) || (to_child != NULL))
1157
            {
1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168
                int diff;
                if (from_child == NULL)
                {
                    diff = 1;
                }
                else if (to_child == NULL)
                {
                    diff = -1;
                }
                else
                {
1169
                    diff = compare_strings((unsigned char*)from_child->string, (unsigned char*)to_child->string, case_sensitive);
1170 1171 1172
                }

                if (diff == 0)
1173 1174
                {
                    /* both object keys are the same */
1175
                    size_t path_length = strlen((const char*)path);
1176
                    size_t from_child_name_length = pointer_encoded_length((unsigned char*)from_child->string);
1177 1178 1179
                    unsigned char *new_path = (unsigned char*)cJSON_malloc(path_length + from_child_name_length + sizeof("/"));

                    sprintf((char*)new_path, "%s/", path);
1180
                    encode_string_as_pointer(new_path + path_length + 1, (unsigned char*)from_child->string);
1181

1182
                    /* create a patch for the element */
1183
                    create_patches(patches, new_path, from_child, to_child, case_sensitive);
C
crhackos 已提交
1184
                    cJSON_free(new_path);
1185 1186 1187

                    from_child = from_child->next;
                    to_child = to_child->next;
1188 1189 1190 1191
                }
                else if (diff < 0)
                {
                    /* object element doesn't exist in 'to' --> remove it */
1192
                    compose_patch(patches, (const unsigned char*)"remove", path, (unsigned char*)from_child->string, NULL);
1193 1194

                    from_child = from_child->next;
1195 1196 1197 1198
                }
                else
                {
                    /* object element doesn't exist in 'from' --> add it */
1199
                    compose_patch(patches, (const unsigned char*)"add", path, (unsigned char*)to_child->string, to_child);
1200 1201

                    to_child = to_child->next;
1202 1203 1204 1205 1206 1207 1208 1209 1210
                }
            }
            return;
        }

        default:
            break;
    }
}
1211

1212
CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatches(cJSON * const from, cJSON * const to)
1213
{
1214
    cJSON *patches = cJSON_CreateArray();
1215
    create_patches(patches, (const unsigned char*)"", from, to, false);
1216

1217 1218
    return patches;
}
1219

1220 1221 1222 1223 1224 1225 1226 1227
CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatchesCaseSensitive(cJSON * const from, cJSON * const to)
{
    cJSON *patches = cJSON_CreateArray();
    create_patches(patches, (const unsigned char*)"", from, to, true);

    return patches;
}

1228
CJSON_PUBLIC(void) cJSONUtils_SortObject(cJSON * const object)
1229
{
1230
    sort_object(object, false);
1231 1232
}

1233
CJSON_PUBLIC(void) cJSONUtils_SortObjectCaseSensitive(cJSON * const object)
M
Max Bruckner 已提交
1234
{
1235
    sort_object(object, true);
M
Max Bruckner 已提交
1236
}
1237

1238
static cJSON *merge_patch(cJSON *target, const cJSON * const patch, const cJSON_bool case_sensitive)
1239
{
M
Max Bruckner 已提交
1240 1241
    cJSON *patch_child = NULL;

1242
    if (!cJSON_IsObject(patch))
M
Max Bruckner 已提交
1243 1244 1245 1246 1247
    {
        /* scalar value, array or NULL, just duplicate */
        cJSON_Delete(target);
        return cJSON_Duplicate(patch, 1);
    }
1248

1249
    if (!cJSON_IsObject(target))
M
Max Bruckner 已提交
1250 1251 1252 1253 1254
    {
        cJSON_Delete(target);
        target = cJSON_CreateObject();
    }

M
Max Bruckner 已提交
1255 1256
    patch_child = patch->child;
    while (patch_child != NULL)
M
Max Bruckner 已提交
1257
    {
M
Max Bruckner 已提交
1258
        if (cJSON_IsNull(patch_child))
M
Max Bruckner 已提交
1259 1260
        {
            /* NULL is the indicator to remove a value, see RFC7396 */
1261 1262 1263 1264 1265 1266 1267 1268
            if (case_sensitive)
            {
                cJSON_DeleteItemFromObjectCaseSensitive(target, patch_child->string);
            }
            else
            {
                cJSON_DeleteItemFromObject(target, patch_child->string);
            }
M
Max Bruckner 已提交
1269 1270 1271
        }
        else
        {
1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290
            cJSON *replace_me = NULL;
            cJSON *replacement = NULL;

            if (case_sensitive)
            {
                replace_me = cJSON_DetachItemFromObjectCaseSensitive(target, patch_child->string);
            }
            else
            {
                replace_me = cJSON_DetachItemFromObject(target, patch_child->string);
            }

            replacement = merge_patch(replace_me, patch_child, case_sensitive);
            if (replacement == NULL)
            {
                return NULL;
            }

            cJSON_AddItemToObject(target, patch_child->string, replacement);
M
Max Bruckner 已提交
1291
        }
M
Max Bruckner 已提交
1292
        patch_child = patch_child->next;
M
Max Bruckner 已提交
1293 1294
    }
    return target;
1295
}
1296

1297 1298 1299 1300 1301 1302 1303 1304 1305 1306
CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatch(cJSON *target, const cJSON * const patch)
{
    return merge_patch(target, patch, false);
}

CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatchCaseSensitive(cJSON *target, const cJSON * const patch)
{
    return merge_patch(target, patch, true);
}

1307
static cJSON *generate_merge_patch(cJSON * const from, cJSON * const to, const cJSON_bool case_sensitive)
1308
{
1309 1310
    cJSON *from_child = NULL;
    cJSON *to_child = NULL;
1311
    cJSON *patch = NULL;
1312
    if (to == NULL)
1313 1314 1315 1316
    {
        /* patch to delete everything */
        return cJSON_CreateNull();
    }
1317
    if (!cJSON_IsObject(to) || !cJSON_IsObject(from))
1318 1319 1320 1321
    {
        return cJSON_Duplicate(to, 1);
    }

1322 1323
    sort_object(from, case_sensitive);
    sort_object(to, case_sensitive);
1324

1325 1326
    from_child = from->child;
    to_child = to->child;
1327
    patch = cJSON_CreateObject();
1328
    while (from_child || to_child)
1329
    {
1330 1331 1332 1333 1334
        int diff;
        if (from_child != NULL)
        {
            if (to_child != NULL)
            {
M
Max Bruckner 已提交
1335
                diff = strcmp(from_child->string, to_child->string);
1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347
            }
            else
            {
                diff = -1;
            }
        }
        else
        {
            diff = 1;
        }

        if (diff < 0)
1348 1349
        {
            /* from has a value that to doesn't have -> remove */
1350 1351 1352
            cJSON_AddItemToObject(patch, from_child->string, cJSON_CreateNull());

            from_child = from_child->next;
1353
        }
1354
        else if (diff > 0)
1355 1356
        {
            /* to has a value that from doesn't have -> add to patch */
1357 1358 1359
            cJSON_AddItemToObject(patch, to_child->string, cJSON_Duplicate(to_child, 1));

            to_child = to_child->next;
1360 1361 1362 1363
        }
        else
        {
            /* object key exists in both objects */
1364
            if (!compare_json(from_child, to_child, case_sensitive))
1365 1366
            {
                /* not identical --> generate a patch */
1367
                cJSON_AddItemToObject(patch, to_child->string, cJSONUtils_GenerateMergePatch(from_child, to_child));
1368
            }
1369

1370
            /* next key in the object */
1371 1372
            from_child = from_child->next;
            to_child = to_child->next;
1373 1374
        }
    }
1375
    if (patch->child == NULL)
1376
    {
1377
        /* no patch generated */
1378
        cJSON_Delete(patch);
1379
        return NULL;
1380 1381 1382
    }

    return patch;
M
Max Bruckner 已提交
1383
}
1384 1385 1386 1387 1388 1389 1390 1391 1392 1393

CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatch(cJSON * const from, cJSON * const to)
{
    return generate_merge_patch(from, to, false);
}

CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatchCaseSensitive(cJSON * const from, cJSON * const to)
{
    return generate_merge_patch(from, to, true);
}