static void test_copy_compare(void) { JsonElement *bench = LoadTestFile("benchmark.json"); assert_true(bench != NULL); JsonElement *copy = JsonCopy(bench); assert_true(copy != NULL); assert_int_equal(0, JsonCompare(copy, bench)); JsonDestroy(bench); JsonDestroy(copy); }
JsonElement *RvalToJson(Rval rval) { assert(rval.item); switch (rval.type) { case RVAL_TYPE_SCALAR: return JsonStringCreate(RvalScalarValue(rval)); case RVAL_TYPE_LIST: return RlistToJson(RvalRlistValue(rval)); case RVAL_TYPE_FNCALL: return FnCallToJson(RvalFnCallValue(rval)); case RVAL_TYPE_CONTAINER: return JsonCopy(RvalContainerValue(rval)); case RVAL_TYPE_NOPROMISEE: assert(false); return JsonObjectCreate(1); } assert(false); return NULL; }
Rval RvalNew(const void *item, RvalType type) { switch (type) { case RVAL_TYPE_SCALAR: return (Rval) { xstrdup(item), RVAL_TYPE_SCALAR }; case RVAL_TYPE_FNCALL: return (Rval) { FnCallCopy(item), RVAL_TYPE_FNCALL }; case RVAL_TYPE_LIST: return (Rval) { RlistCopy(item), RVAL_TYPE_LIST }; case RVAL_TYPE_CONTAINER: return (Rval) { JsonCopy(item), RVAL_TYPE_CONTAINER }; case RVAL_TYPE_NOPROMISEE: return ((Rval) {NULL, type}); } assert(false); return ((Rval) { NULL, RVAL_TYPE_NOPROMISEE }); }
Rval ExpandPrivateRval(EvalContext *ctx, const char *ns, const char *scope, const void *rval_item, RvalType rval_type) { Rval returnval; returnval.item = NULL; returnval.type = RVAL_TYPE_NOPROMISEE; switch (rval_type) { case RVAL_TYPE_SCALAR: { Buffer *buffer = BufferNew(); ExpandScalar(ctx, ns, scope, rval_item, buffer); returnval = (Rval) { BufferClose(buffer), RVAL_TYPE_SCALAR }; } break; case RVAL_TYPE_LIST: returnval.item = ExpandList(ctx, ns, scope, rval_item, true); returnval.type = RVAL_TYPE_LIST; break; case RVAL_TYPE_FNCALL: returnval.item = ExpandFnCall(ctx, ns, scope, rval_item); returnval.type = RVAL_TYPE_FNCALL; break; case RVAL_TYPE_CONTAINER: returnval = RvalNew(JsonCopy(rval_item), RVAL_TYPE_CONTAINER); break; case RVAL_TYPE_NOPROMISEE: break; } return returnval; }
Rval RvalNewRewriter(const void *item, RvalType type, JsonElement *map) { switch (type) { case RVAL_TYPE_SCALAR: if (NULL != map && JsonLength(map) > 0 && // do we have a rewrite map? (strstr(item, "$(") || strstr(item, "${"))) // are there unresolved variable references? { // TODO: replace with BufferSearchAndReplace when the // string_replace code is merged. // Sorry about the CF_BUFSIZE ugliness. int max_size = 10*CF_BUFSIZE+1; char *buffer_from = xmalloc(max_size); char *buffer_to = xmalloc(max_size); Buffer *format = BufferNew(); strncpy(buffer_from, item, max_size); for (int iteration = 0; iteration < 10; iteration++) { bool replacement_made = false; int var_start = -1; char closing_brace = 0; for (int c = 0; c < buffer_from[c]; c++) { printf("In %s at %i: '%s'\n", __func__, __LINE__, buffer_from); if (buffer_from[c] == '$') { if (buffer_from[c+1] == '(') { closing_brace = ')'; } else if (buffer_from[c+1] == '{') { closing_brace = '}'; } if (closing_brace) { c++; var_start = c-1; } } else if (var_start >= 0 && buffer_from[c] == closing_brace) { char saved = buffer_from[c]; buffer_from[c] = '\0'; const char *repl = JsonObjectGetAsString(map, buffer_from + var_start + 2); buffer_from[c] = saved; if (repl) { // Before the replacement. memcpy(buffer_to, buffer_from, var_start); // The actual replacement. int repl_len = strlen(repl); memcpy(buffer_to + var_start, repl, repl_len); // The text after. strlcpy(buffer_to + var_start + repl_len, buffer_from + c + 1, max_size - var_start - repl_len); // Reset location to immediately after the replacement. c = var_start + repl_len - 1; var_start = -1; strcpy(buffer_from, buffer_to); closing_brace = 0; replacement_made = true; } } } if (!replacement_made) { break; } } char *ret = xstrdup(buffer_to); BufferDestroy(format); free(buffer_to); free(buffer_from); return (Rval) { ret, RVAL_TYPE_SCALAR }; } else { return (Rval) { xstrdup(item), RVAL_TYPE_SCALAR }; } case RVAL_TYPE_FNCALL: return (Rval) { FnCallCopyRewriter(item, map), RVAL_TYPE_FNCALL }; case RVAL_TYPE_LIST: return (Rval) { RlistCopyRewriter(item, map), RVAL_TYPE_LIST }; case RVAL_TYPE_CONTAINER: return (Rval) { JsonCopy(item), RVAL_TYPE_CONTAINER }; case RVAL_TYPE_NOPROMISEE: return ((Rval) {NULL, type}); } assert(false); return ((Rval) { NULL, RVAL_TYPE_NOPROMISEE }); }
// See fncall.c for the usage of allow_all_types. Rlist *RlistAppendAllTypes(Rlist **start, const void *item, RvalType type, bool allow_all_types) { Rlist *lp = *start; switch (type) { case RVAL_TYPE_SCALAR: return RlistAppendScalar(start, item); case RVAL_TYPE_FNCALL: break; case RVAL_TYPE_LIST: if (allow_all_types) { JsonElement* store = JsonArrayCreate(RlistLen(item)); for (const Rlist *rp = item; rp; rp = rp->next) { JsonArrayAppendElement(store, RvalToJson(rp->val)); } return RlistAppendRval(start, (Rval) { store, RVAL_TYPE_CONTAINER }); } for (const Rlist *rp = item; rp; rp = rp->next) { lp = RlistAppendRval(start, RvalCopy(rp->val)); } return lp; case RVAL_TYPE_CONTAINER: if (allow_all_types) { return RlistAppendRval(start, (Rval) { JsonCopy((JsonElement*) item), RVAL_TYPE_CONTAINER }); } // note falls through! default: Log(LOG_LEVEL_DEBUG, "Cannot append %c to rval-list '%s'", type, (char *) item); return NULL; } Rlist *rp = xmalloc(sizeof(Rlist)); rp->val = RvalNew(item, type); rp->next = NULL; if (*start == NULL) { *start = rp; } else { for (lp = *start; lp->next != NULL; lp = lp->next) { } lp->next = rp; } return rp; }
/* Inserts an Rlist node with value #rval, right after the rlist node #node. */ void RlistInsertAfter(Rlist *node, Rval rval) { assert(node != NULL); Rlist new_node = { .val = rval, .next = node->next }; node->next = xmemdup(&new_node, sizeof(new_node)); } Rval RvalNewRewriter(const void *item, RvalType type, JsonElement *map) { switch (type) { case RVAL_TYPE_SCALAR: if (map != NULL && JsonLength(map) > 0 && // do we have a rewrite map? (strstr(item, "$(") || strstr(item, "${"))) // are there unresolved variable references? { // TODO: replace with BufferSearchAndReplace when the // string_replace code is merged. // Sorry about the CF_BUFSIZE ugliness. int max_size = 10*CF_BUFSIZE+1; char *buffer_from = xmalloc(max_size); char *buffer_to = xmalloc(max_size); Buffer *format = BufferNew(); StringCopy(item, buffer_from, max_size); for (int iteration = 0; iteration < 10; iteration++) { bool replacement_made = false; int var_start = -1; char closing_brace = 0; for (int c = 0; c < buffer_from[c]; c++) { if (buffer_from[c] == '$') { if (buffer_from[c+1] == '(') { closing_brace = ')'; } else if (buffer_from[c+1] == '{') { closing_brace = '}'; } if (closing_brace) { c++; var_start = c-1; } } else if (var_start >= 0 && buffer_from[c] == closing_brace) { char saved = buffer_from[c]; buffer_from[c] = '\0'; const char *repl = JsonObjectGetAsString(map, buffer_from + var_start + 2); buffer_from[c] = saved; if (repl) { // Before the replacement. memcpy(buffer_to, buffer_from, var_start); // The actual replacement. int repl_len = strlen(repl); memcpy(buffer_to + var_start, repl, repl_len); // The text after. strlcpy(buffer_to + var_start + repl_len, buffer_from + c + 1, max_size - var_start - repl_len); // Reset location to immediately after the replacement. c = var_start + repl_len - 1; var_start = -1; StringCopy(buffer_to, buffer_from, max_size); closing_brace = 0; replacement_made = true; } } } if (!replacement_made) { break; } } char *ret = xstrdup(buffer_to); BufferDestroy(format); free(buffer_to); free(buffer_from); return (Rval) { ret, RVAL_TYPE_SCALAR }; } else { return (Rval) { xstrdup(item), RVAL_TYPE_SCALAR }; } case RVAL_TYPE_FNCALL: return (Rval) { FnCallCopyRewriter(item, map), RVAL_TYPE_FNCALL }; case RVAL_TYPE_LIST: return (Rval) { RlistCopyRewriter(item, map), RVAL_TYPE_LIST }; case RVAL_TYPE_CONTAINER: return (Rval) { JsonCopy(item), RVAL_TYPE_CONTAINER }; case RVAL_TYPE_NOPROMISEE: return ((Rval) {NULL, type}); } assert(false); return ((Rval) { NULL, RVAL_TYPE_NOPROMISEE }); } Rval RvalNew(const void *item, RvalType type) { return RvalNewRewriter(item, type, NULL); } Rval RvalCopyRewriter(Rval rval, JsonElement *map) { return RvalNewRewriter(rval.item, rval.type, map); } Rval RvalCopy(Rval rval) { return RvalNew(rval.item, rval.type); } /*******************************************************************/ Rlist *RlistCopyRewriter(const Rlist *rp, JsonElement *map) { Rlist *start = NULL; while (rp != NULL) { RlistAppendRval(&start, RvalCopyRewriter(rp->val, map)); rp = rp->next; } return start; }