static bool RenderVariable(Writer *out, const char *content, size_t content_len, bool escaped, Seq *hash_stack) { JsonElement *var = NULL; if (strncmp(content, ".", content_len) == 0) { var = SeqAt(hash_stack, SeqLength(hash_stack) - 1); } else { var = LookupVariable(hash_stack, content, content_len); } if (!var) { return true; } switch (JsonGetElementType(var)) { case JSON_ELEMENT_TYPE_PRIMITIVE: return RenderVariablePrimitive(out, var, escaped); case JSON_ELEMENT_TYPE_CONTAINER: assert(false); return false; } assert(false); return false; }
static bool RenderVariable(Buffer *out, const char *content, size_t content_len, TagType conversion, Seq *hash_stack) { JsonElement *var = NULL; bool escape = conversion == TAG_TYPE_VAR; bool serialize = conversion == TAG_TYPE_VAR_SERIALIZED; bool serialize_compact = conversion == TAG_TYPE_VAR_SERIALIZED_COMPACT; const bool item_mode = strncmp(content, ".", content_len) == 0; const bool key_mode = strncmp(content, "@", content_len) == 0; if (item_mode || key_mode) { var = SeqAt(hash_stack, SeqLength(hash_stack) - 1); } else { var = LookupVariable(hash_stack, content, content_len); } if (!var) { return true; } switch (JsonGetElementType(var)) { case JSON_ELEMENT_TYPE_PRIMITIVE: // note that this also covers 'serialize' on primitives return RenderVariablePrimitive(out, var, escape, key_mode); case JSON_ELEMENT_TYPE_CONTAINER: if (serialize || serialize_compact) { return RenderVariableContainer(out, var, serialize_compact); } else { assert(false); return false; } } assert(false); return false; }
static bool Render(Buffer *out, const char *start, const char *input, Seq *hash_stack, char *delim_start, size_t *delim_start_len, char *delim_end, size_t *delim_end_len, bool skip_content, const char *section, const char **section_end) { while (true) { if (!input) { Log(LOG_LEVEL_ERR, "Unexpected end to Mustache template"); return false; } Mustache tag = NextTag(input, delim_start, *delim_start_len, delim_end, *delim_end_len); { const char *line_begin = NULL; const char *line_end = NULL; if (!IsTagTypeRenderable(tag.type) && IsTagStandalone(start, tag.begin, tag.end, &line_begin, &line_end)) { RenderContent(out, input, line_begin - input, false, skip_content); input = line_end; } else { RenderContent(out, input, tag.begin - input, false, skip_content); input = tag.end; } } switch (tag.type) { case TAG_TYPE_ERR: return false; case TAG_TYPE_DELIM: if (!SetDelimiters(tag.content, tag.content_len, delim_start, delim_start_len, delim_end, delim_end_len)) { return false; } continue; case TAG_TYPE_COMMENT: continue; case TAG_TYPE_NONE: return true; case TAG_TYPE_VAR_SERIALIZED: case TAG_TYPE_VAR_SERIALIZED_COMPACT: case TAG_TYPE_VAR_UNESCAPED: case TAG_TYPE_VAR: if (!skip_content) { if (tag.content_len > 0) { if (!RenderVariable(out, tag.content, tag.content_len, tag.type, hash_stack)) { return false; } } else { RenderContent(out, delim_start, *delim_start_len, false, false); RenderContent(out, delim_end, *delim_end_len, false, false); } } continue; case TAG_TYPE_INVERTED: case TAG_TYPE_SECTION: { char *section = xstrndup(tag.content, tag.content_len); JsonElement *var = LookupVariable(hash_stack, tag.content, tag.content_len); SeqAppend(hash_stack, var); if (!var) { const char *cur_section_end = NULL; if (!Render(out, start, input, hash_stack, delim_start, delim_start_len, delim_end, delim_end_len, skip_content || tag.type != TAG_TYPE_INVERTED, section, &cur_section_end)) { free(section); return false; } free(section); input = cur_section_end; continue; } switch (JsonGetElementType(var)) { case JSON_ELEMENT_TYPE_PRIMITIVE: switch (JsonGetPrimitiveType(var)) { case JSON_PRIMITIVE_TYPE_BOOL: { bool skip = skip_content || (!JsonPrimitiveGetAsBool(var) ^ (tag.type == TAG_TYPE_INVERTED)); const char *cur_section_end = NULL; if (!Render(out, start, input, hash_stack, delim_start, delim_start_len, delim_end, delim_end_len, skip, section, &cur_section_end)) { free(section); return false; } free(section); input = cur_section_end; } continue; default: Log(LOG_LEVEL_WARNING, "Mustache sections can only take a boolean or a container (array or map) value, but section '%s' isn't getting one of those.", section); return false; } break; case JSON_ELEMENT_TYPE_CONTAINER: switch (JsonGetContainerType(var)) { case JSON_CONTAINER_TYPE_OBJECT: case JSON_CONTAINER_TYPE_ARRAY: if (JsonLength(var) > 0) { const char *cur_section_end = NULL; for (size_t i = 0; i < JsonLength(var); i++) { JsonElement *child_hash = JsonAt(var, i); SeqAppend(hash_stack, child_hash); if (!Render(out, start, input, hash_stack, delim_start, delim_start_len, delim_end, delim_end_len, skip_content || tag.type == TAG_TYPE_INVERTED, section, &cur_section_end)) { free(section); return false; } } input = cur_section_end; free(section); } else { const char *cur_section_end = NULL; if (!Render(out, start, input, hash_stack, delim_start, delim_start_len, delim_end, delim_end_len, tag.type != TAG_TYPE_INVERTED, section, &cur_section_end)) { free(section); return false; } free(section); input = cur_section_end; } break; } break; } } continue; case TAG_TYPE_SECTION_END: if (!section) { char *varname = xstrndup(tag.content, tag.content_len); Log(LOG_LEVEL_WARNING, "Unknown section close in mustache template '%s'", varname); free(varname); return false; } else { SeqRemove(hash_stack, SeqLength(hash_stack) - 1); *section_end = input; return true; } break; default: assert(false); return false; } } assert(false); }
bool cGenreParseInfo::DoVarSubstitution( TSTRING &rval ) //throw( eParserHelper ) { cDebug d("cConfigFile::DoVarSubst()"); d.TraceDebug("ORIG: %s\n", rval.c_str()); // walk through string // look for $( // find matching ) // pick out subset // look up in symbol table // substitute subset // continue iterating // step through string // iterate to (slen-1), because we are looking for a two-character sentinel "$(" bool fEscaping = false; for (TSTRING::size_type i = 0; i < rval.size(); i++) { TCHAR c = rval[i]; // is it the "$(" sentinel? (an escaped '$' is not a variable) if (c == '$' && ! fEscaping ) { c = rval[i+1]; if (c == '(') { // ooh, wow! it's a variable! find the end bool found = false; TSTRING::size_type j; for (j = i+1; j < rval.size(); j++) { if (rval[j] == ')') { found = true; break; } } // did we find it? if (!found) { // TODO: throw error return false; } // otherwise, cut out the variable name TSTRING::size_type begin = i + 2; TSTRING::size_type size = j - i - 2; TSTRING varname; varname = rval.substr(begin, size); d.TraceDebug("symbol = %s\n", varname.c_str()); // look up in symbol table TSTRING varvalue; if ( ! LookupVariable( varname, varvalue ) ) throw eParserUseUndefVar( varname ); // replace varname with varvalue rval.replace(begin-2, size+3, varvalue); d.TraceDebug("POST: %s\n", rval.c_str()); // no no no // we should exit function, and get called again // update counters // we should bump the cursor over by the length of the // varvalue (minus one, to compensate for post-increment of index) i += varvalue.size() - 1; goto nextchar; } } else if (c == '\\') { fEscaping = ! fEscaping; } else { fEscaping = false; } nextchar: ; } d.TraceDebug("DONE: %s\n", rval.c_str()); // switch around return true; }