Parse the XPath segment between /body/DocFragment[N]/body/ and the terminal position into an ordered sequence of steps. Returns step count, 0 on failure. Example input: "/body/DocFragment[1]/body/div[1]/ul/li[4]/text()[1].51" Fills steps with: {div,1}, {ul,1}, {li,4}
| 132 | // Example input: "/body/DocFragment[1]/body/div[1]/ul/li[4]/text()[1].51" |
| 133 | // Fills steps with: {div,1}, {ul,1}, {li,4} |
| 134 | int parseXPathSteps(const std::string& xpath, XPathStep steps[MAX_XPATH_DEPTH]) { |
| 135 | static const char kBodyFrag[] = "/body/DocFragment["; |
| 136 | const size_t fragPos = xpath.find(kBodyFrag); |
| 137 | if (fragPos == std::string::npos) return 0; |
| 138 | const size_t afterBracket = xpath.find(']', fragPos + strlen(kBodyFrag)); |
| 139 | if (afterBracket == std::string::npos) return 0; |
| 140 | static const char kBody[] = "/body/"; |
| 141 | if (xpath.compare(afterBracket + 1, strlen(kBody), kBody) != 0) return 0; |
| 142 | size_t pos = afterBracket + 1 + strlen(kBody); |
| 143 | |
| 144 | size_t stepsEnd = xpath.rfind("/text()"); |
| 145 | if (stepsEnd == std::string::npos) { |
| 146 | stepsEnd = xpath.rfind('.'); |
| 147 | if (stepsEnd == std::string::npos || stepsEnd <= pos || stepsEnd + 1 >= xpath.size()) return 0; |
| 148 | for (size_t i = stepsEnd + 1; i < xpath.size(); i++) { |
| 149 | if (xpath[i] < '0' || xpath[i] > '9') return 0; |
| 150 | } |
| 151 | } |
| 152 | if (stepsEnd <= pos) return 0; |
| 153 | |
| 154 | int count = 0; |
| 155 | while (pos < stepsEnd && count < MAX_XPATH_DEPTH) { |
| 156 | const size_t slash = xpath.find('/', pos); |
| 157 | const size_t segEnd = (slash < stepsEnd) ? slash : stepsEnd; |
| 158 | |
| 159 | XPathStep& step = steps[count]; |
| 160 | const size_t bracket = xpath.find('[', pos); |
| 161 | const size_t nameEnd = (bracket != std::string::npos && bracket < segEnd) ? bracket : segEnd; |
| 162 | const size_t nameLen = nameEnd - pos; |
| 163 | if (nameLen == 0 || nameLen >= sizeof(step.tag)) return 0; |
| 164 | memcpy(step.tag, xpath.c_str() + pos, nameLen); |
| 165 | step.tag[nameLen] = '\0'; |
| 166 | |
| 167 | if (bracket != std::string::npos && bracket < segEnd) { |
| 168 | const size_t closeBracket = xpath.find(']', bracket + 1); |
| 169 | if (closeBracket == std::string::npos || closeBracket > segEnd) return 0; |
| 170 | int idx = 0; |
| 171 | for (size_t i = bracket + 1; i < closeBracket; i++) { |
| 172 | if (xpath[i] < '0' || xpath[i] > '9') return 0; |
| 173 | idx = idx * 10 + (xpath[i] - '0'); |
| 174 | } |
| 175 | step.siblingIndex = idx; |
| 176 | } else { |
| 177 | step.siblingIndex = 1; |
| 178 | } |
| 179 | |
| 180 | count++; |
| 181 | pos = (slash < stepsEnd) ? slash + 1 : stepsEnd; |
| 182 | } |
| 183 | return count; |
| 184 | } |
| 185 | |
| 186 | class ParagraphStreamer final : public Print { |
| 187 | size_t bytesWritten = 0; |