| 834 | }; |
| 835 | |
| 836 | char* SplitStructuredLineInternal(char* line, |
| 837 | char delimiter, |
| 838 | const char* symbol_pairs, |
| 839 | vector<char*>* cols, |
| 840 | bool with_escapes) { |
| 841 | ClosingSymbolLookup lookup(symbol_pairs); |
| 842 | |
| 843 | // Stack of symbols expected to close the current opened expressions. |
| 844 | vector<char> expected_to_close; |
| 845 | bool in_escape = false; |
| 846 | |
| 847 | CHECK(cols); |
| 848 | cols->push_back(line); |
| 849 | char* current; |
| 850 | for (current = line; *current; ++current) { |
| 851 | char c = *current; |
| 852 | if (in_escape) { |
| 853 | in_escape = false; |
| 854 | } else if (with_escapes && c == '\\') { |
| 855 | // We are escaping the next character. Note the escape still appears |
| 856 | // in the output. |
| 857 | in_escape = true; |
| 858 | } else if (expected_to_close.empty() && c == delimiter) { |
| 859 | // We don't have any open expression, this is a valid separator. |
| 860 | *current = 0; |
| 861 | cols->push_back(current + 1); |
| 862 | } else if (!expected_to_close.empty() && c == expected_to_close.back()) { |
| 863 | // Can we close the currently open expression? |
| 864 | expected_to_close.pop_back(); |
| 865 | } else if (lookup.GetClosingChar(c)) { |
| 866 | // If this is an opening symbol, we open a new expression and push |
| 867 | // the expected closing symbol on the stack. |
| 868 | expected_to_close.push_back(lookup.GetClosingChar(c)); |
| 869 | } else if (lookup.IsClosing(c)) { |
| 870 | // Error: mismatched closing symbol. |
| 871 | return current; |
| 872 | } |
| 873 | } |
| 874 | if (!expected_to_close.empty()) { |
| 875 | return current; // Missing closing symbol(s) |
| 876 | } |
| 877 | return nullptr; // Success |
| 878 | } |
| 879 | |
| 880 | bool SplitStructuredLineInternal(StringPiece line, |
| 881 | char delimiter, |
no test coverage detected