const CssSelector *CssPullParser::NextSelector() { if (!currSel) return nullptr; SkipWsAndComments(currSel, selEnd); if (currSel == selEnd) return nullptr; sel.s = currSel; // skip single selector const char *sEnd = currSel; while (currSel < selEnd && *currSel != ',') { if (*currSel == '"' || *currSel == '\'') { bool ok = SkipQuotedString(currSel, selEnd); CrashIf(!ok); sEnd = currSel; } else if (*currSel == '\\' && currSel < selEnd - 1) { currSel += 2; sEnd = currSel; } else if (!SkipWsAndComments(currSel, selEnd)) { sEnd = ++currSel; } } if (currSel < selEnd) currSel++; sel.sLen = sEnd - sel.s; sel.tag = Tag_NotFound; sel.clazz = nullptr; sel.clazzLen = 0; // parse "*", "el", ".class" and "el.class" const char *c = sEnd; for (; c > sel.s && (isalnum((unsigned char)*(c - 1)) || *(c - 1) == '-'); c--); if (c > sel.s && *(c - 1) == '.') { sel.clazz = c; sel.clazzLen = sEnd - c; c--; } for (; c > sel.s && (isalnum((unsigned char)*(c - 1)) || *(c - 1) == '-'); c--); if (sel.clazz - 1 == sel.s) { sel.tag = Tag_Any; } else if (c == (sel.clazz ? sel.clazz - 1 : sEnd) && c == sel.s + 1 && *sel.s == '*') { sel.tag = Tag_Any; } else if (c == sel.s) { sel.tag = FindHtmlTag(sel.s, sel.clazz ? sel.clazz - sel.s - 1 : sel.sLen); } return &sel; }
bool CssPullParser::NextRule() { if (inProps) while (NextProperty()); CrashIf(inProps && currPos < end); if (inlineStyle || currPos == end) return false; if (currPos == s) { SkipWsAndComments(currPos, end); if (currPos + 4 < end && str::StartsWith(currPos, "<!--")) currPos += 4; } SkipWsAndComments(currPos, end); currSel = currPos; // skip selectors while (currPos < end && *currPos != '{') { if (*currPos == '"' || *currPos == '\'') { if (!SkipQuotedString(currPos, end)) break; } else if (*currPos == ';') { currPos++; SkipWsAndComments(currPos, end); currSel = currPos; } else if (!SkipWsAndComments(currPos, end)) { currPos++; } } if (currPos == end) { currSel = nullptr; return false; } selEnd = currPos++; inProps = true; return true; }
static bool SkipBlock(const char*& s, const char *end) { CrashIf(s >= end || *s != '{'); s++; while (s < end && *s != '}') { if (*s == '"' || *s == '\'') { if (!SkipQuotedString(s, end)) return false; } else if (*s == '{') { if (!SkipBlock(s, end)) return false; } else if (*s == '\\' && s < end - 1) s += 2; else if (!SkipWsAndComments(s, end)) s++; } if (s == end) return false; s++; return true; }
static SquareTreeNode *ParseSquareTreeRec(char *& data, bool isTopLevel=false) { SquareTreeNode *node = new SquareTreeNode(); while (*(data = SkipWsAndComments(data))) { // all non-empty non-comment lines contain a key-value pair // where the value is either a string (separated by '=' or ':') // or a list of child nodes (if the key is followed by '[' alone) char *key = data; for (data = key; *data && *data != '=' && *data != ':' && *data != '[' && *data != ']' && *data != '\n'; data++); if (!*data || '\n' == *data) { // use first whitespace as a fallback separator for (data = key; *data && !str::IsWs(*data); data++); } char *separator = data; if (*data && *data != '\n') { // skip to the first non-whitespace character on the same line (value) data = SkipWs(data + 1, true); } char *value = data; // skip to the end of the line for (; *data && *data != '\n'; data++); if (IsBracketLine(separator) || // also tolerate "key \n [ \n ... \n ]" (else the key // gets an empty value and the child node an empty key) str::IsWs(*separator) && '\n' == *value && IsBracketLine(SkipWsAndComments(data))) { // parse child node(s) data = SkipWsAndComments(separator) + 1; *SkipWsRev(key, separator) = '\0'; node->data.Append(SquareTreeNode::DataItem(key, ParseSquareTreeRec(data))); // arrays are created by either reusing the same key for a different child // or by concatenating multiple children ("[ \n ] [ \n ] [ \n ]") while (IsBracketLine((data = SkipWsAndComments(data)))) { data++; node->data.Append(SquareTreeNode::DataItem(key, ParseSquareTreeRec(data))); } } else if (']' == *key) { // finish parsing child node data = key + 1; if (!isTopLevel) return node; // ignore superfluous closing square brackets instead of // ignoring all content following them } else if ('[' == *key && ']' == SkipWsRev(value, data)[-1]) { // treat INI section headers as top-level node names // (else "[Section]" would be ignored) if (!isTopLevel) { data = key; return node; } // trim whitespace around section name (for consistency with GetPrivateProfileString) key = SkipWs(key + 1); *SkipWsRev(key, SkipWsRev(value, data) - 1) = '\0'; node->data.Append(SquareTreeNode::DataItem(key, ParseSquareTreeRec(data))); } else if ('[' == *separator || ']' == *separator) { // invalid line (ignored) } else { // string value (decoding is left to the consumer) bool hasMoreLines = '\n' == *data; *SkipWsRev(key, separator) = '\0'; *SkipWsRev(value, data) = '\0'; node->data.Append(SquareTreeNode::DataItem(key, value)); if (hasMoreLines) data++; } } // assume that all square brackets have been properly balanced return node; }
const CssProperty *CssPullParser::NextProperty() { if (currPos == s) inlineStyle = inProps = true; else if (!inProps) return nullptr; GetNextProperty: SkipWsAndComments(currPos, end); if (currPos == end) return nullptr; if (*currPos == '}') { currPos++; inProps = false; return nullptr; } if (*currPos == '{') { if (!SkipBlock(currPos, end)) return nullptr; goto GetNextProperty; } if (*currPos == ';') { currPos++; goto GetNextProperty; } const char *name = currPos; // skip identifier while (currPos < end && !str::IsWs(*currPos) && *currPos != ':' && *currPos != ';' && *currPos != '{' && *currPos != '}') { currPos++; } SkipWsAndComments(currPos, end); if (currPos == end || *currPos != ':') goto GetNextProperty; prop.type = FindCssProp(name, currPos - name); currPos++; SkipWsAndComments(currPos, end); prop.s = currPos; // skip value const char *valEnd = currPos; while (currPos < end && *currPos != ';' && *currPos != '}') { if (*currPos == '"' || *currPos == '\'') { if (!SkipQuotedString(currPos, end)) return nullptr; valEnd = currPos; } else if (*currPos == '{') { if (!SkipBlock(currPos, end)) return nullptr; valEnd = currPos; } else if (*currPos == '\\' && currPos < end - 1) { currPos += 2; valEnd = currPos; } else if (!SkipWsAndComments(currPos, end)) { valEnd = ++currPos; } } prop.sLen = valEnd - prop.s; return ∝ }