/** * Expand a #string into Buffer #out, returning the pointer to the string * itself, inside the Buffer #out. If #out is NULL then the buffer will be * created and destroyed internally. * * @retval NULL something went wrong */ char *ExpandScalar(const EvalContext *ctx, const char *ns, const char *scope, const char *string, Buffer *out) { bool out_belongs_to_us = false; if (out == NULL) { out = BufferNew(); out_belongs_to_us = true; } assert(string != NULL); assert(out != NULL); Buffer *current_item = BufferNew(); for (const char *sp = string; *sp != '\0'; sp++) { BufferClear(current_item); ExtractScalarPrefix(current_item, sp, strlen(sp)); BufferAppend(out, BufferData(current_item), BufferSize(current_item)); sp += BufferSize(current_item); if (*sp == '\0') { break; } BufferClear(current_item); char varstring = sp[1]; ExtractScalarReference(current_item, sp, strlen(sp), true); sp += BufferSize(current_item) + 2; if (IsCf3VarString(BufferData(current_item))) { Buffer *temp = BufferCopy(current_item); BufferClear(current_item); ExpandScalar(ctx, ns, scope, BufferData(temp), current_item); BufferDestroy(temp); } if (!IsExpandable(BufferData(current_item))) { VarRef *ref = VarRefParseFromNamespaceAndScope( BufferData(current_item), ns, scope, CF_NS, '.'); DataType value_type; const void *value = EvalContextVariableGet(ctx, ref, &value_type); VarRefDestroy(ref); switch (DataTypeToRvalType(value_type)) { case RVAL_TYPE_SCALAR: assert(value != NULL); BufferAppendString(out, value); continue; break; case RVAL_TYPE_CONTAINER: { assert(value != NULL); const JsonElement *jvalue = value; /* instead of casts */ if (JsonGetElementType(jvalue) == JSON_ELEMENT_TYPE_PRIMITIVE) { BufferAppendString(out, JsonPrimitiveGetAsString(jvalue)); continue; } break; } default: /* TODO Log() */ break; } } if (varstring == '{') { BufferAppendF(out, "${%s}", BufferData(current_item)); } else { BufferAppendF(out, "$(%s)", BufferData(current_item)); } } BufferDestroy(current_item); LogDebug(LOG_MOD_EXPAND, "ExpandScalar( %s : %s . %s ) => %s", SAFENULL(ns), SAFENULL(scope), string, BufferData(out)); return out_belongs_to_us ? BufferClose(out) : BufferGet(out); }
// If return_names is not set, only the captured data is returned (so // for N captures you can expect N elements in the Sequence). Seq *StringMatchCapturesWithPrecompiledRegex(const pcre *pattern, const char *str, const bool return_names) { int captures; int res = pcre_fullinfo(pattern, NULL, PCRE_INFO_CAPTURECOUNT, &captures); if (res != 0) { return NULL; } // Get the table of named captures. unsigned char *name_table = NULL; // Doesn't have to be freed as per docs. int namecount = 0; int name_entry_size = 0; unsigned char *tabptr; pcre_fullinfo(pattern, NULL, PCRE_INFO_NAMECOUNT, &namecount); const bool have_named_captures = (namecount > 0 && return_names); if (have_named_captures) { pcre_fullinfo(pattern, NULL, PCRE_INFO_NAMETABLE, &name_table); pcre_fullinfo(pattern, NULL, PCRE_INFO_NAMEENTRYSIZE, &name_entry_size); } int *ovector = xmalloc(sizeof(int) * (captures + 1) * 3); int result = pcre_exec(pattern, NULL, str, strlen(str), 0, 0, ovector, (captures + 1) * 3); if (result <= 0) { free(ovector); return NULL; } Seq *ret = SeqNew(captures + 1, BufferDestroy); for (int i = 0; i <= captures; ++i) { Buffer *capture = NULL; if (have_named_captures) { // The overhead of doing a nested name scan is negligible. tabptr = name_table; for (int namepos = 0; namepos < namecount; namepos++) { int n = (tabptr[0] << 8) | tabptr[1]; if (n == i) // We found the position { capture = BufferNewFrom(tabptr + 2, name_entry_size - 3); break; } tabptr += name_entry_size; } } if (return_names) { if (NULL == capture) { capture = BufferNew(); BufferAppendF(capture, "%zd", i); } SeqAppend(ret, capture); } Buffer *data = BufferNewFrom(str + ovector[2*i], ovector[2*i + 1] - ovector[2 * i]); Log(LOG_LEVEL_DEBUG, "StringMatchCaptures: return_names = %d, have_named_captures = %d, offset %d, name '%s', data '%s'", return_names, have_named_captures, i, capture == NULL ? "no_name" : BufferData(capture), BufferData(data)); SeqAppend(ret, data); } free(ovector); return ret; }
bool ExpandScalar(const EvalContext *ctx, const char *ns, const char *scope, const char *string, Buffer *out) { assert(string); if (strlen(string) == 0) { return true; } bool fully_expanded = true; Buffer *current_item = BufferNew(); for (const char *sp = string; *sp != '\0'; sp++) { BufferClear(current_item); ExtractScalarPrefix(current_item, sp, strlen(sp)); BufferAppend(out, BufferData(current_item), BufferSize(current_item)); sp += BufferSize(current_item); if (*sp == '\0') { break; } BufferClear(current_item); char varstring = sp[1]; ExtractScalarReference(current_item, sp, strlen(sp), true); sp += BufferSize(current_item) + 2; if (IsCf3VarString(BufferData(current_item))) { Buffer *temp = BufferCopy(current_item); BufferClear(current_item); ExpandScalar(ctx, ns, scope, BufferData(temp), current_item); BufferDestroy(temp); } if (!IsExpandable(BufferData(current_item))) { DataType type = CF_DATA_TYPE_NONE; const void *value = NULL; { VarRef *ref = VarRefParseFromNamespaceAndScope(BufferData(current_item), ns, scope, CF_NS, '.'); value = EvalContextVariableGet(ctx, ref, &type); VarRefDestroy(ref); } if (value) { switch (DataTypeToRvalType(type)) { case RVAL_TYPE_SCALAR: BufferAppendString(out, value); continue; case RVAL_TYPE_CONTAINER: if (JsonGetElementType((JsonElement*)value) == JSON_ELEMENT_TYPE_PRIMITIVE) { BufferAppendString(out, JsonPrimitiveGetAsString((JsonElement*)value)); continue; } break; default: break; } } } if (varstring == '{') { BufferAppendF(out, "${%s}", BufferData(current_item)); } else { BufferAppendF(out, "$(%s)", BufferData(current_item)); } } BufferDestroy(current_item); return fully_expanded; }