|
|
|
/**
|
|
|
|
* Copyright (c) 2007-2012, Timothy Stack
|
|
|
|
*
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions are met:
|
|
|
|
*
|
|
|
|
* * Redistributions of source code must retain the above copyright notice, this
|
|
|
|
* list of conditions and the following disclaimer.
|
|
|
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
|
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
|
|
* and/or other materials provided with the distribution.
|
|
|
|
* * Neither the name of Timothy Stack nor the names of its contributors
|
|
|
|
* may be used to endorse or promote products derived from this software
|
|
|
|
* without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
|
|
|
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
|
|
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
|
|
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
|
|
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
|
|
|
#include "spookyhash/SpookyV2.h"
|
|
|
|
#include "data_parser.hh"
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
data_format data_parser::FORMAT_SEMI("semi", DT_COMMA, DT_SEMI);
|
|
|
|
data_format data_parser::FORMAT_COMMA("comma", DT_INVALID, DT_COMMA);
|
|
|
|
data_format data_parser::FORMAT_PLAIN("plain", DT_INVALID, DT_INVALID);
|
|
|
|
|
|
|
|
data_parser::data_parser(data_scanner *ds)
|
|
|
|
: dp_errors("dp_errors", __FILE__, __LINE__),
|
|
|
|
dp_pairs("dp_pairs", __FILE__, __LINE__),
|
|
|
|
dp_msg_format(nullptr),
|
|
|
|
dp_msg_format_begin(ds->get_input().pi_offset),
|
|
|
|
dp_scanner(ds)
|
|
|
|
{
|
|
|
|
if (TRACE_FILE != nullptr) {
|
|
|
|
fprintf(TRACE_FILE, "input %s\n", ds->get_input().get_string());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void data_parser::pairup(data_parser::schema_id_t *schema,
|
|
|
|
data_parser::element_list_t &pairs_out,
|
|
|
|
data_parser::element_list_t &in_list, int group_depth)
|
|
|
|
{
|
|
|
|
element_list_t ELEMENT_LIST_T(el_stack), ELEMENT_LIST_T(free_row),
|
|
|
|
ELEMENT_LIST_T(key_comps), ELEMENT_LIST_T(value),
|
|
|
|
ELEMENT_LIST_T(prefix);
|
|
|
|
SpookyHash context;
|
|
|
|
|
|
|
|
require(in_list.el_format.df_name != nullptr);
|
|
|
|
|
|
|
|
POINT_TRACE("pairup_start");
|
|
|
|
|
|
|
|
FORMAT_TRACE(in_list);
|
|
|
|
|
|
|
|
for (auto iter = in_list.begin();
|
|
|
|
iter != in_list.end();
|
|
|
|
++iter) {
|
|
|
|
if (iter->e_token == DNT_GROUP) {
|
|
|
|
element_list_t ELEMENT_LIST_T(group_pairs);
|
|
|
|
|
|
|
|
this->pairup(nullptr, group_pairs, *iter->e_sub_elements,
|
|
|
|
group_depth + 1);
|
|
|
|
if (!group_pairs.empty()) {
|
|
|
|
iter->assign_elements(group_pairs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (in_list.el_format.df_prefix_terminator != DT_INVALID) {
|
|
|
|
if (iter->e_token == in_list.el_format.df_prefix_terminator) {
|
|
|
|
in_list.el_format.df_prefix_terminator = DT_INVALID;
|
|
|
|
} else {
|
|
|
|
el_stack.PUSH_BACK(*iter);
|
|
|
|
}
|
|
|
|
} else if (iter->e_token == in_list.el_format.df_terminator) {
|
|
|
|
this->end_of_value(el_stack, key_comps, value, in_list,
|
|
|
|
group_depth);
|
|
|
|
|
|
|
|
key_comps.PUSH_BACK(*iter);
|
|
|
|
} else if (iter->e_token == in_list.el_format.df_qualifier) {
|
|
|
|
value.SPLICE(value.end(),
|
|
|
|
key_comps,
|
|
|
|
key_comps.begin(),
|
|
|
|
key_comps.end());
|
|
|
|
strip(value, element_if(DT_WHITE));
|
|
|
|
if (!value.empty()) {
|
|
|
|
el_stack.PUSH_BACK(element(value, DNT_VALUE));
|
|
|
|
}
|
|
|
|
} else if (iter->e_token == in_list.el_format.df_separator) {
|
|
|
|
auto key_iter = key_comps.end();
|
|
|
|
bool found = false, key_is_values = true;
|
|
|
|
|
|
|
|
if (!key_comps.empty()) {
|
|
|
|
do {
|
|
|
|
--key_iter;
|
|
|
|
if (key_iter->e_token ==
|
|
|
|
in_list.el_format.df_appender) {
|
|
|
|
++key_iter;
|
|
|
|
value.SPLICE(value.end(),
|
|
|
|
key_comps,
|
|
|
|
key_comps.begin(),
|
|
|
|
key_iter);
|
|
|
|
key_comps.POP_FRONT();
|
|
|
|
found = true;
|
|
|
|
} else if (key_iter->e_token ==
|
|
|
|
in_list.el_format.df_terminator) {
|
|
|
|
std::vector<element> key_copy;
|
|
|
|
|
|
|
|
value.SPLICE(value.end(),
|
|
|
|
key_comps,
|
|
|
|
key_comps.begin(),
|
|
|
|
key_iter);
|
|
|
|
key_comps.POP_FRONT();
|
|
|
|
strip(key_comps, element_if(DT_WHITE));
|
|
|
|
if (key_comps.empty()) {
|
|
|
|
key_iter = key_comps.end();
|
|
|
|
} else {
|
|
|
|
key_iter = key_comps.begin();
|
|
|
|
}
|
|
|
|
found = true;
|
|
|
|
}
|
|
|
|
if (key_iter != key_comps.end()) {
|
|
|
|
switch (key_iter->e_token) {
|
|
|
|
case DT_WORD:
|
|
|
|
case DT_SYMBOL:
|
|
|
|
key_is_values = false;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} while (key_iter != key_comps.begin() && !found);
|
|
|
|
}
|
|
|
|
if (!found && !el_stack.empty() && !key_comps.empty()) {
|
|
|
|
element_list_t::iterator value_iter;
|
|
|
|
|
|
|
|
if (el_stack.size() > 1 &&
|
|
|
|
in_list.el_format.df_appender != DT_INVALID &&
|
|
|
|
in_list.el_format.df_terminator != DT_INVALID) {
|
|
|
|
/* If we're expecting a terminator and haven't found it */
|
|
|
|
/* then this is part of the value. */
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
value.SPLICE(value.end(),
|
|
|
|
key_comps,
|
|
|
|
key_comps.begin(),
|
|
|
|
key_comps.end());
|
|
|
|
value_iter = value.end();
|
|
|
|
std::advance(value_iter, -1);
|
|
|
|
key_comps.SPLICE(key_comps.begin(),
|
|
|
|
value,
|
|
|
|
value_iter,
|
|
|
|
value.end());
|
|
|
|
key_comps.resize(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
strip(value, element_if(DT_WHITE));
|
|
|
|
value.remove_if(element_if(DT_COMMA));
|
|
|
|
if (!value.empty()) {
|
|
|
|
el_stack.PUSH_BACK(element(value, DNT_VALUE));
|
|
|
|
}
|
|
|
|
strip(key_comps, element_if(DT_WHITE));
|
|
|
|
if (!key_comps.empty()) {
|
|
|
|
if (key_is_values) {
|
|
|
|
el_stack.PUSH_BACK(element(key_comps, DNT_VALUE));
|
|
|
|
} else {
|
|
|
|
el_stack.PUSH_BACK(element(key_comps, DNT_KEY, false));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
key_comps.CLEAR();
|
|
|
|
value.CLEAR();
|
|
|
|
} else {
|
|
|
|
key_comps.PUSH_BACK(*iter);
|
|
|
|
}
|
|
|
|
|
|
|
|
POINT_TRACE("pairup_loop");
|
|
|
|
}
|
|
|
|
|
|
|
|
POINT_TRACE("pairup_eol");
|
|
|
|
|
|
|
|
CONSUMED_TRACE(in_list);
|
|
|
|
|
|
|
|
// Only perform the free-row logic at the top level, if we're in a group
|
|
|
|
// assume it is a list.
|
|
|
|
if (group_depth < 1 && el_stack.empty()) {
|
|
|
|
free_row.SPLICE(free_row.begin(),
|
|
|
|
key_comps, key_comps.begin(), key_comps.end());
|
|
|
|
} else {
|
|
|
|
this->end_of_value(el_stack, key_comps, value, in_list, group_depth);
|
|
|
|
}
|
|
|
|
|
|
|
|
POINT_TRACE("pairup_stack");
|
|
|
|
|
|
|
|
context.Init(0, 0);
|
|
|
|
while (!el_stack.empty()) {
|
|
|
|
auto kv_iter = el_stack.begin();
|
|
|
|
if (kv_iter->e_token == DNT_VALUE) {
|
|
|
|
if (pairs_out.empty()) {
|
|
|
|
free_row.PUSH_BACK(el_stack.front());
|
|
|
|
} else {
|
|
|
|
element_list_t ELEMENT_LIST_T(free_pair_subs);
|
|
|
|
struct element blank;
|
|
|
|
|
|
|
|
blank.e_capture.c_begin = blank.e_capture.c_end =
|
|
|
|
el_stack.front().e_capture.
|
|
|
|
c_begin;
|
|
|
|
blank.e_token = DNT_KEY;
|
|
|
|
free_pair_subs.PUSH_BACK(blank);
|
|
|
|
free_pair_subs.PUSH_BACK(el_stack.front());
|
|
|
|
pairs_out.PUSH_BACK(element(free_pair_subs, DNT_PAIR));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (kv_iter->e_token != DNT_KEY) {
|
|
|
|
el_stack.POP_FRONT();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
++kv_iter;
|
|
|
|
if (kv_iter == el_stack.end()) {
|
|
|
|
el_stack.POP_FRONT();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
element_list_t ELEMENT_LIST_T(pair_subs);
|
|
|
|
|
|
|
|
if (schema != nullptr) {
|
|
|
|
size_t key_len;
|
|
|
|
const char *key_val =
|
|
|
|
this->get_element_string(el_stack.front(), key_len);
|
|
|
|
context.Update(key_val, key_len);
|
|
|
|
}
|
|
|
|
|
|
|
|
while (!free_row.empty()) {
|
|
|
|
element_list_t ELEMENT_LIST_T(free_pair_subs);
|
|
|
|
struct element blank;
|
|
|
|
|
|
|
|
blank.e_capture.c_begin = blank.e_capture.c_end =
|
|
|
|
free_row.front().e_capture.
|
|
|
|
c_begin;
|
|
|
|
blank.e_token = DNT_KEY;
|
|
|
|
free_pair_subs.PUSH_BACK(blank);
|
|
|
|
free_pair_subs.PUSH_BACK(free_row.front());
|
|
|
|
pairs_out.PUSH_BACK(element(free_pair_subs, DNT_PAIR));
|
|
|
|
free_row.POP_FRONT();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool has_value = false;
|
|
|
|
|
|
|
|
if (kv_iter->e_token == DNT_VALUE) {
|
|
|
|
++kv_iter;
|
|
|
|
has_value = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
pair_subs.SPLICE(pair_subs.begin(),
|
|
|
|
el_stack,
|
|
|
|
el_stack.begin(),
|
|
|
|
kv_iter);
|
|
|
|
|
|
|
|
if (!has_value) {
|
|
|
|
element_list_t ELEMENT_LIST_T(blank_value);
|
|
|
|
pcre_input &pi = this->dp_scanner->get_input();
|
|
|
|
const char *str = pi.get_string();
|
|
|
|
struct element blank;
|
|
|
|
|
|
|
|
blank.e_token = DT_QUOTED_STRING;
|
|
|
|
blank.e_capture.c_begin = blank.e_capture.c_end =
|
|
|
|
pair_subs.front().e_capture.c_end;
|
|
|
|
if ((blank.e_capture.c_begin >= 0) &&
|
|
|
|
((size_t) blank.e_capture.c_begin < pi.pi_length)) {
|
|
|
|
switch (str[blank.e_capture.c_begin]) {
|
|
|
|
case '=':
|
|
|
|
case ':':
|
|
|
|
blank.e_capture.c_begin += 1;
|
|
|
|
blank.e_capture.c_end += 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
blank_value.PUSH_BACK(blank);
|
|
|
|
pair_subs.PUSH_BACK(element(blank_value, DNT_VALUE));
|
|
|
|
}
|
|
|
|
|
|
|
|
pairs_out.PUSH_BACK(element(pair_subs, DNT_PAIR));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pairs_out.size() == 1) {
|
|
|
|
element &pair = pairs_out.front();
|
|
|
|
element &evalue = pair.e_sub_elements->back();
|
|
|
|
|
|
|
|
if (evalue.e_token == DNT_VALUE &&
|
|
|
|
evalue.e_sub_elements != nullptr &&
|
|
|
|
evalue.e_sub_elements->size() > 1) {
|
|
|
|
element_list_t::iterator next_sub;
|
|
|
|
|
|
|
|
next_sub = pair.e_sub_elements->begin();
|
|
|
|
++next_sub;
|
|
|
|
prefix.SPLICE(prefix.begin(),
|
|
|
|
*pair.e_sub_elements,
|
|
|
|
pair.e_sub_elements->begin(),
|
|
|
|
next_sub);
|
|
|
|
free_row.CLEAR();
|
|
|
|
free_row.SPLICE(free_row.begin(),
|
|
|
|
*evalue.e_sub_elements,
|
|
|
|
evalue.e_sub_elements->begin(),
|
|
|
|
evalue.e_sub_elements->end());
|
|
|
|
pairs_out.CLEAR();
|
|
|
|
context.Init(0, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (group_depth >= 1 && pairs_out.empty() && !free_row.empty()) {
|
|
|
|
pairs_out.SWAP(free_row);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pairs_out.empty() && !free_row.empty()) {
|
|
|
|
while (!free_row.empty()) {
|
|
|
|
switch (free_row.front().e_token) {
|
|
|
|
case DNT_GROUP:
|
|
|
|
case DNT_VALUE:
|
|
|
|
case DT_EMAIL:
|
|
|
|
case DT_CONSTANT:
|
|
|
|
case DT_NUMBER:
|
|
|
|
case DT_SYMBOL:
|
|
|
|
case DT_HEX_NUMBER:
|
|
|
|
case DT_OCTAL_NUMBER:
|
|
|
|
case DT_VERSION_NUMBER:
|
|
|
|
case DT_QUOTED_STRING:
|
|
|
|
case DT_IPV4_ADDRESS:
|
|
|
|
case DT_IPV6_ADDRESS:
|
|
|
|
case DT_MAC_ADDRESS:
|
|
|
|
case DT_HEX_DUMP:
|
|
|
|
case DT_XML_OPEN_TAG:
|
|
|
|
case DT_XML_CLOSE_TAG:
|
|
|
|
case DT_XML_EMPTY_TAG:
|
|
|
|
case DT_UUID:
|
|
|
|
case DT_URL:
|
|
|
|
case DT_PATH:
|
|
|
|
case DT_DATE:
|
|
|
|
case DT_TIME:
|
|
|
|
case DT_PERCENTAGE: {
|
|
|
|
element_list_t ELEMENT_LIST_T(pair_subs);
|
|
|
|
struct element blank;
|
|
|
|
|
|
|
|
blank.e_capture.c_begin = blank.e_capture.c_end =
|
|
|
|
free_row.front().e_capture.c_begin;
|
|
|
|
blank.e_token = DNT_KEY;
|
|
|
|
pair_subs.PUSH_BACK(blank);
|
|
|
|
pair_subs.PUSH_BACK(free_row.front());
|
|
|
|
pairs_out.PUSH_BACK(element(pair_subs, DNT_PAIR));
|
|
|
|
|
|
|
|
// Throw something into the hash so that the number of
|
|
|
|
// columns is significant. I don't think we want to
|
|
|
|
// use the token ID since some columns values might vary
|
|
|
|
// between rows.
|
|
|
|
context.Update(" ", 1);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default: {
|
|
|
|
size_t key_len;
|
|
|
|
const char *key_val = this->get_element_string(
|
|
|
|
free_row.front(), key_len);
|
|
|
|
|
|
|
|
context.Update(key_val, key_len);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
free_row.POP_FRONT();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!prefix.empty()) {
|
|
|
|
element_list_t ELEMENT_LIST_T(pair_subs);
|
|
|
|
struct element blank;
|
|
|
|
|
|
|
|
blank.e_capture.c_begin = blank.e_capture.c_end =
|
|
|
|
prefix.front().e_capture.c_begin;
|
|
|
|
blank.e_token = DNT_KEY;
|
|
|
|
pair_subs.PUSH_BACK(blank);
|
|
|
|
pair_subs.PUSH_BACK(prefix.front());
|
|
|
|
pairs_out.PUSH_FRONT(element(pair_subs, DNT_PAIR));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (schema != nullptr) {
|
|
|
|
context.Final(schema->out(0), schema->out(1));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (schema != nullptr && this->dp_msg_format != nullptr) {
|
|
|
|
pcre_input &pi = this->dp_scanner->get_input();
|
|
|
|
for (auto &fiter : pairs_out) {
|
|
|
|
*(this->dp_msg_format) += this->get_string_up_to_value(fiter);
|
|
|
|
this->dp_msg_format->append("#");
|
|
|
|
}
|
|
|
|
if ((size_t) this->dp_msg_format_begin < pi.pi_length) {
|
|
|
|
const char *str = pi.get_string();
|
|
|
|
pcre_context::capture_t last(this->dp_msg_format_begin,
|
|
|
|
pi.pi_length);
|
|
|
|
|
|
|
|
switch (str[last.c_begin]) {
|
|
|
|
case '\'':
|
|
|
|
case '"':
|
|
|
|
last.c_begin += 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
*(this->dp_msg_format) += pi.get_substr(&last);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pairs_out.size() > 1000) {
|
|
|
|
pairs_out.resize(1000);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void data_parser::discover_format()
|
|
|
|
{
|
|
|
|
pcre_context_static<30> pc;
|
|
|
|
std::stack<discover_format_state> state_stack;
|
|
|
|
struct element elem;
|
|
|
|
|
|
|
|
this->dp_group_token.push_back(DT_INVALID);
|
|
|
|
this->dp_group_stack.resize(1);
|
|
|
|
|
|
|
|
state_stack.push(discover_format_state());
|
|
|
|
while (this->dp_scanner->tokenize2(pc, elem.e_token)) {
|
|
|
|
pcre_context::iterator pc_iter;
|
|
|
|
|
|
|
|
pc_iter = std::find_if(pc.begin(), pc.end(), capture_if_not(-1));
|
|
|
|
require(pc_iter != pc.end());
|
|
|
|
|
|
|
|
elem.e_capture = *pc_iter;
|
|
|
|
|
|
|
|
require(elem.e_capture.c_begin >= 0);
|
|
|
|
require(elem.e_capture.c_end >= 0);
|
|
|
|
|
|
|
|
state_stack.top().update_for_element(elem);
|
|
|
|
switch (elem.e_token) {
|
|
|
|
case DT_LPAREN:
|
|
|
|
case DT_LANGLE:
|
|
|
|
case DT_LCURLY:
|
|
|
|
case DT_LSQUARE:
|
|
|
|
this->dp_group_token.push_back(elem.e_token);
|
|
|
|
this->dp_group_stack.emplace_back("_anon_", __FILE__, __LINE__);
|
|
|
|
state_stack.push(discover_format_state());
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DT_EMPTY_CONTAINER: {
|
|
|
|
auto &curr_group = this->dp_group_stack.back();
|
|
|
|
auto empty_list = element_list_t("_anon_", __FILE__, __LINE__);
|
|
|
|
discover_format_state dfs;
|
|
|
|
|
|
|
|
dfs.finalize();
|
|
|
|
|
|
|
|
empty_list.el_format = dfs.dfs_format;
|
|
|
|
curr_group.PUSH_BACK(element());
|
|
|
|
|
|
|
|
auto &empty = curr_group.back();
|
|
|
|
empty.e_capture.c_begin = elem.e_capture.c_begin + 1;
|
|
|
|
empty.e_capture.c_end = elem.e_capture.c_begin + 1;
|
|
|
|
empty.e_token = DNT_GROUP;
|
|
|
|
empty.assign_elements(empty_list);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DT_RPAREN:
|
|
|
|
case DT_RANGLE:
|
|
|
|
case DT_RCURLY:
|
|
|
|
case DT_RSQUARE:
|
|
|
|
if (this->dp_group_token.back() == (elem.e_token - 1)) {
|
|
|
|
this->dp_group_token.pop_back();
|
|
|
|
|
|
|
|
auto riter = this->dp_group_stack.rbegin();
|
|
|
|
++riter;
|
|
|
|
state_stack.top().finalize();
|
|
|
|
this->dp_group_stack.back().el_format =
|
|
|
|
state_stack.top().dfs_format;
|
|
|
|
state_stack.pop();
|
|
|
|
if (!this->dp_group_stack.back().empty()) {
|
|
|
|
(*riter).PUSH_BACK(element(this->dp_group_stack.back(),
|
|
|
|
DNT_GROUP));
|
|
|
|
} else {
|
|
|
|
(*riter).PUSH_BACK(element());
|
|
|
|
riter->back().e_capture.c_begin = elem.e_capture.c_begin;
|
|
|
|
riter->back().e_capture.c_end = elem.e_capture.c_begin;
|
|
|
|
riter->back().e_token = DNT_GROUP;
|
|
|
|
riter->back().assign_elements(
|
|
|
|
this->dp_group_stack.back());
|
|
|
|
}
|
|
|
|
this->dp_group_stack.pop_back();
|
|
|
|
} else {
|
|
|
|
this->dp_group_stack.back().PUSH_BACK(elem);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
this->dp_group_stack.back().PUSH_BACK(elem);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
while (this->dp_group_stack.size() > 1) {
|
|
|
|
this->dp_group_token.pop_back();
|
|
|
|
|
|
|
|
auto riter = this->dp_group_stack.rbegin();
|
|
|
|
++riter;
|
|
|
|
if (!this->dp_group_stack.back().empty()) {
|
|
|
|
state_stack.top().finalize();
|
|
|
|
this->dp_group_stack.back().el_format = state_stack.top().dfs_format;
|
|
|
|
state_stack.pop();
|
|
|
|
(*riter).PUSH_BACK(element(this->dp_group_stack.back(),
|
|
|
|
DNT_GROUP));
|
|
|
|
}
|
|
|
|
this->dp_group_stack.pop_back();
|
|
|
|
}
|
|
|
|
|
|
|
|
state_stack.top().finalize();
|
|
|
|
this->dp_group_stack.back().el_format = state_stack.top().dfs_format;
|
|
|
|
}
|
|
|
|
|
|
|
|
void data_parser::end_of_value(data_parser::element_list_t &el_stack,
|
|
|
|
data_parser::element_list_t &key_comps,
|
|
|
|
data_parser::element_list_t &value,
|
|
|
|
const data_parser::element_list_t &in_list,
|
|
|
|
int group_depth)
|
|
|
|
{
|
|
|
|
key_comps.remove_if(element_if(in_list.el_format.df_terminator));
|
|
|
|
key_comps.remove_if(element_if(DT_COMMA));
|
|
|
|
value.remove_if(element_if(in_list.el_format.df_terminator));
|
|
|
|
value.remove_if(element_if(DT_COMMA));
|
|
|
|
strip(key_comps, element_if(DT_WHITE));
|
|
|
|
strip(value, element_if(DT_WHITE));
|
|
|
|
if ((el_stack.empty() || el_stack.back().e_token != DNT_KEY) &&
|
|
|
|
value.empty() && key_comps.size() > 1 &&
|
|
|
|
(key_comps.front().e_token == DT_WORD ||
|
|
|
|
key_comps.front().e_token == DT_SYMBOL)) {
|
|
|
|
element_list_t::iterator key_iter, key_end;
|
|
|
|
bool found_value = false;
|
|
|
|
int word_count = 0;
|
|
|
|
key_iter = key_comps.begin();
|
|
|
|
key_end = key_comps.begin();
|
|
|
|
for (; key_iter != key_comps.end(); ++key_iter) {
|
|
|
|
if (key_iter->e_token == DT_WORD ||
|
|
|
|
key_iter->e_token == DT_SYMBOL) {
|
|
|
|
word_count += 1;
|
|
|
|
if (found_value) {
|
|
|
|
key_end = key_comps.begin();
|
|
|
|
}
|
|
|
|
} else if (key_iter->e_token == DT_WHITE) {
|
|
|
|
|
|
|
|
} else {
|
|
|
|
if (!found_value) {
|
|
|
|
key_end = key_iter;
|
|
|
|
}
|
|
|
|
found_value = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (word_count != 1) {
|
|
|
|
key_end = key_comps.begin();
|
|
|
|
}
|
|
|
|
value.SPLICE(value.end(),
|
|
|
|
key_comps,
|
|
|
|
key_end,
|
|
|
|
key_comps.end());
|
|
|
|
strip(key_comps, element_if(DT_WHITE));
|
|
|
|
if (!key_comps.empty()) {
|
|
|
|
el_stack.PUSH_BACK(element(key_comps, DNT_KEY, false));
|
|
|
|
}
|
|
|
|
key_comps.CLEAR();
|
|
|
|
} else {
|
|
|
|
value.SPLICE(value.end(),
|
|
|
|
key_comps,
|
|
|
|
key_comps.begin(),
|
|
|
|
key_comps.end());
|
|
|
|
}
|
|
|
|
strip(value, element_if(DT_WHITE));
|
|
|
|
strip(value, element_if(DT_COLON));
|
|
|
|
strip(value, element_if(DT_WHITE));
|
|
|
|
if (!value.empty()) {
|
|
|
|
if (value.size() == 2 && value.back().e_token == DNT_GROUP) {
|
|
|
|
element_list_t ELEMENT_LIST_T(group_pair);
|
|
|
|
|
|
|
|
group_pair.PUSH_BACK(element(value, DNT_PAIR));
|
|
|
|
el_stack.PUSH_BACK(element(group_pair, DNT_VALUE));
|
|
|
|
} else {
|
|
|
|
el_stack.PUSH_BACK(element(value, DNT_VALUE));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
value.CLEAR();
|
|
|
|
}
|
|
|
|
|
|
|
|
void data_parser::parse()
|
|
|
|
{
|
|
|
|
this->discover_format();
|
|
|
|
|
|
|
|
this->pairup(&this->dp_schema_id,
|
|
|
|
this->dp_pairs,
|
|
|
|
this->dp_group_stack.front());
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string
|
|
|
|
data_parser::get_element_string(const data_parser::element &elem) const
|
|
|
|
{
|
|
|
|
pcre_input &pi = this->dp_scanner->get_input();
|
|
|
|
|
|
|
|
return pi.get_substr(&elem.e_capture);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string
|
|
|
|
data_parser::get_string_up_to_value(const data_parser::element &elem)
|
|
|
|
{
|
|
|
|
pcre_input &pi = this->dp_scanner->get_input();
|
|
|
|
const element &val_elem = elem.e_token == DNT_PAIR ?
|
|
|
|
elem.e_sub_elements->back() : elem;
|
|
|
|
|
|
|
|
if (this->dp_msg_format_begin <= val_elem.e_capture.c_begin) {
|
|
|
|
pcre_context::capture_t leading_and_key = pcre_context::capture_t(
|
|
|
|
this->dp_msg_format_begin, val_elem.e_capture.c_begin);
|
|
|
|
const char *str = pi.get_string();
|
|
|
|
if (leading_and_key.length() >= 2) {
|
|
|
|
switch (str[leading_and_key.c_end - 1]) {
|
|
|
|
case '\'':
|
|
|
|
case '"':
|
|
|
|
leading_and_key.c_end -= 1;
|
|
|
|
switch (str[leading_and_key.c_end - 1]) {
|
|
|
|
case 'r':
|
|
|
|
case 'u':
|
|
|
|
leading_and_key.c_end -= 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
switch (str[leading_and_key.c_begin]) {
|
|
|
|
case '\'':
|
|
|
|
case '"':
|
|
|
|
leading_and_key.c_begin += 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this->dp_msg_format_begin = val_elem.e_capture.c_end;
|
|
|
|
return pi.get_substr(&leading_and_key);
|
|
|
|
} else {
|
|
|
|
this->dp_msg_format_begin = val_elem.e_capture.c_end;
|
|
|
|
}
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *data_parser::get_element_string(const data_parser::element &elem,
|
|
|
|
size_t &len_out)
|
|
|
|
{
|
|
|
|
pcre_input &pi = this->dp_scanner->get_input();
|
|
|
|
|
|
|
|
len_out = elem.e_capture.length();
|
|
|
|
return pi.get_substr_start(&elem.e_capture);
|
|
|
|
}
|
|
|
|
|
|
|
|
void data_parser::print(FILE *out, data_parser::element_list_t &el)
|
|
|
|
{
|
|
|
|
fprintf(out, " %s\n",
|
|
|
|
this->dp_scanner->get_input().get_string());
|
|
|
|
for (auto &iter : el) {
|
|
|
|
iter.print(out, this->dp_scanner->get_input());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
FILE *data_parser::TRACE_FILE;
|
|
|
|
|
|
|
|
data_format_state_t dfs_prefix_next(data_format_state_t state,
|
|
|
|
data_token_t next_token)
|
|
|
|
{
|
|
|
|
data_format_state_t retval = state;
|
|
|
|
|
|
|
|
switch (state) {
|
|
|
|
case DFS_INIT:
|
|
|
|
switch (next_token) {
|
|
|
|
case DT_PATH:
|
|
|
|
case DT_COLON:
|
|
|
|
case DT_EQUALS:
|
|
|
|
case DT_CONSTANT:
|
|
|
|
case DT_EMAIL:
|
|
|
|
case DT_WORD:
|
|
|
|
case DT_SYMBOL:
|
|
|
|
case DT_OCTAL_NUMBER:
|
|
|
|
case DT_HEX_NUMBER:
|
|
|
|
case DT_NUMBER:
|
|
|
|
case DT_WHITE:
|
|
|
|
case DT_LSQUARE:
|
|
|
|
case DT_RSQUARE:
|
|
|
|
case DT_LANGLE:
|
|
|
|
case DT_RANGLE:
|
|
|
|
case DT_EMPTY_CONTAINER:
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
retval = DFS_ERROR;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DFS_EXPECTING_SEP:
|
|
|
|
case DFS_ERROR:
|
|
|
|
retval = DFS_ERROR;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
data_format_state_t dfs_semi_next(data_format_state_t state,
|
|
|
|
data_token_t next_token)
|
|
|
|
{
|
|
|
|
data_format_state_t retval = state;
|
|
|
|
|
|
|
|
switch (state) {
|
|
|
|
case DFS_INIT:
|
|
|
|
switch (next_token) {
|
|
|
|
case DT_COMMA:
|
|
|
|
case DT_SEMI:
|
|
|
|
retval = DFS_ERROR;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
retval = DFS_KEY;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DFS_KEY:
|
|
|
|
switch (next_token) {
|
|
|
|
case DT_COLON:
|
|
|
|
case DT_EQUALS:
|
|
|
|
retval = DFS_VALUE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DT_SEMI:
|
|
|
|
retval = DFS_ERROR;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DFS_VALUE:
|
|
|
|
switch (next_token) {
|
|
|
|
case DT_SEMI:
|
|
|
|
retval = DFS_INIT;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DFS_EXPECTING_SEP:
|
|
|
|
case DFS_ERROR:
|
|
|
|
retval = DFS_ERROR;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
data_format_state_t dfs_comma_next(data_format_state_t state,
|
|
|
|
data_token_t next_token)
|
|
|
|
{
|
|
|
|
data_format_state_t retval = state;
|
|
|
|
|
|
|
|
switch (state) {
|
|
|
|
case DFS_INIT:
|
|
|
|
switch (next_token) {
|
|
|
|
case DT_COMMA:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DT_SEMI:
|
|
|
|
retval = DFS_ERROR;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
retval = DFS_KEY;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DFS_KEY:
|
|
|
|
switch (next_token) {
|
|
|
|
case DT_COLON:
|
|
|
|
case DT_EQUALS:
|
|
|
|
retval = DFS_VALUE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DT_COMMA:
|
|
|
|
retval = DFS_INIT;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DT_WORD:
|
|
|
|
retval = DFS_EXPECTING_SEP;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DT_SEMI:
|
|
|
|
retval = DFS_ERROR;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DFS_EXPECTING_SEP:
|
|
|
|
switch (next_token) {
|
|
|
|
case DT_COLON:
|
|
|
|
case DT_EQUALS:
|
|
|
|
case DT_LPAREN:
|
|
|
|
case DT_LCURLY:
|
|
|
|
case DT_LSQUARE:
|
|
|
|
case DT_LANGLE:
|
|
|
|
retval = DFS_VALUE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DT_EMPTY_CONTAINER:
|
|
|
|
retval = DFS_INIT;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DT_COMMA:
|
|
|
|
case DT_SEMI:
|
|
|
|
retval = DFS_ERROR;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DFS_VALUE:
|
|
|
|
switch (next_token) {
|
|
|
|
case DT_COMMA:
|
|
|
|
retval = DFS_INIT;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DT_COLON:
|
|
|
|
case DT_EQUALS:
|
|
|
|
retval = DFS_ERROR;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DFS_ERROR:
|
|
|
|
retval = DFS_ERROR;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
data_parser::element::element()
|
|
|
|
: e_capture(-1, -1),
|
|
|
|
e_token(DT_INVALID),
|
|
|
|
e_sub_elements(nullptr)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
data_parser::element::element(data_parser::element_list_t &subs,
|
|
|
|
data_token_t token, bool assign_subs_elements)
|
|
|
|
: e_capture(subs.front().e_capture.c_begin,
|
|
|
|
subs.back().e_capture.c_end),
|
|
|
|
e_token(token),
|
|
|
|
e_sub_elements(nullptr)
|
|
|
|
{
|
|
|
|
if (assign_subs_elements) {
|
|
|
|
this->assign_elements(subs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
data_parser::element::element(const data_parser::element &other)
|
|
|
|
{
|
|
|
|
/* require(other.e_sub_elements == nullptr); */
|
|
|
|
|
|
|
|
this->e_capture = other.e_capture;
|
|
|
|
this->e_token = other.e_token;
|
|
|
|
this->e_sub_elements = nullptr;
|
|
|
|
if (other.e_sub_elements != nullptr) {
|
|
|
|
this->assign_elements(*other.e_sub_elements);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
data_parser::element::~element()
|
|
|
|
{
|
|
|
|
delete this->e_sub_elements;
|
|
|
|
this->e_sub_elements = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
data_parser::element &
|
|
|
|
data_parser::element::operator=(const data_parser::element &other)
|
|
|
|
{
|
|
|
|
this->e_capture = other.e_capture;
|
|
|
|
this->e_token = other.e_token;
|
|
|
|
this->e_sub_elements = nullptr;
|
|
|
|
if (other.e_sub_elements != nullptr) {
|
|
|
|
this->assign_elements(*other.e_sub_elements);
|
|
|
|
}
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
void data_parser::element::assign_elements(data_parser::element_list_t &subs)
|
|
|
|
{
|
|
|
|
if (this->e_sub_elements == nullptr) {
|
|
|
|
this->e_sub_elements = new element_list_t("_sub_", __FILE__,
|
|
|
|
__LINE__);
|
|
|
|
this->e_sub_elements->el_format = subs.el_format;
|
|
|
|
}
|
|
|
|
this->e_sub_elements->SWAP(subs);
|
|
|
|
this->update_capture();
|
|
|
|
}
|
|
|
|
|
|
|
|
void data_parser::element::update_capture()
|
|
|
|
{
|
|
|
|
if (this->e_sub_elements != nullptr && !this->e_sub_elements->empty()) {
|
|
|
|
this->e_capture.c_begin =
|
|
|
|
this->e_sub_elements->front().e_capture.c_begin;
|
|
|
|
this->e_capture.c_end =
|
|
|
|
this->e_sub_elements->back().e_capture.c_end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const data_parser::element &data_parser::element::get_pair_value() const
|
|
|
|
{
|
|
|
|
require(this->e_token == DNT_PAIR);
|
|
|
|
|
|
|
|
return this->e_sub_elements->back();
|
|
|
|
}
|
|
|
|
|
|
|
|
data_token_t data_parser::element::value_token() const
|
|
|
|
{
|
|
|
|
data_token_t retval = DT_INVALID;
|
|
|
|
|
|
|
|
if (this->e_token == DNT_VALUE) {
|
|
|
|
if (this->e_sub_elements != nullptr &&
|
|
|
|
this->e_sub_elements->size() == 1) {
|
|
|
|
retval = this->e_sub_elements->front().e_token;
|
|
|
|
} else {
|
|
|
|
retval = DT_SYMBOL;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
retval = this->e_token;
|
|
|
|
}
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
const data_parser::element &data_parser::element::get_value_elem() const
|
|
|
|
{
|
|
|
|
if (this->e_token == DNT_VALUE) {
|
|
|
|
if (this->e_sub_elements != nullptr &&
|
|
|
|
this->e_sub_elements->size() == 1) {
|
|
|
|
return this->e_sub_elements->front();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
const data_parser::element &data_parser::element::get_pair_elem() const
|
|
|
|
{
|
|
|
|
if (this->e_token == DNT_VALUE) {
|
|
|
|
return this->e_sub_elements->front();
|
|
|
|
}
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
void data_parser::element::print(FILE *out, pcre_input &pi, int offset) const
|
|
|
|
{
|
|
|
|
int lpc;
|
|
|
|
|
|
|
|
if (this->e_sub_elements != nullptr) {
|
|
|
|
for (auto &e_sub_element : *this->e_sub_elements) {
|
|
|
|
e_sub_element.print(out, pi, offset + 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(out, "%4s %3d:%-3d ",
|
|
|
|
data_scanner::token2name(this->e_token),
|
|
|
|
this->e_capture.c_begin,
|
|
|
|
this->e_capture.c_end);
|
|
|
|
for (lpc = 0; lpc < this->e_capture.c_end; lpc++) {
|
|
|
|
if (lpc == this->e_capture.c_begin) {
|
|
|
|
fputc('^', out);
|
|
|
|
} else if (lpc == (this->e_capture.c_end - 1)) {
|
|
|
|
fputc('^', out);
|
|
|
|
} else if (lpc > this->e_capture.c_begin) {
|
|
|
|
fputc('-', out);
|
|
|
|
} else {
|
|
|
|
fputc(' ', out);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (; lpc < (int) pi.pi_length; lpc++) {
|
|
|
|
fputc(' ', out);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string sub = pi.get_substr(&this->e_capture);
|
|
|
|
fprintf(out, " %s\n", sub.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
data_parser::discover_format_state::discover_format_state()
|
|
|
|
: dfs_prefix_state(DFS_INIT),
|
|
|
|
dfs_semi_state(DFS_INIT),
|
|
|
|
dfs_comma_state(DFS_INIT)
|
|
|
|
{
|
|
|
|
memset(this->dfs_hist, 0, sizeof(this->dfs_hist));
|
|
|
|
}
|
|
|
|
|
|
|
|
void data_parser::discover_format_state::update_for_element(
|
|
|
|
const data_parser::element &elem)
|
|
|
|
{
|
|
|
|
this->dfs_prefix_state = dfs_prefix_next(this->dfs_prefix_state,
|
|
|
|
elem.e_token);
|
|
|
|
this->dfs_semi_state = dfs_semi_next(this->dfs_semi_state, elem.e_token);
|
|
|
|
this->dfs_comma_state = dfs_comma_next(this->dfs_comma_state, elem.e_token);
|
|
|
|
if (this->dfs_prefix_state != DFS_ERROR) {
|
|
|
|
if (this->dfs_semi_state == DFS_ERROR) {
|
|
|
|
this->dfs_semi_state = DFS_INIT;
|
|
|
|
}
|
|
|
|
if (this->dfs_comma_state == DFS_ERROR) {
|
|
|
|
this->dfs_comma_state = DFS_INIT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this->dfs_hist[elem.e_token] += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void data_parser::discover_format_state::finalize()
|
|
|
|
{
|
|
|
|
data_token_t qualifier = this->dfs_format.df_qualifier;
|
|
|
|
data_token_t separator = this->dfs_format.df_separator;
|
|
|
|
data_token_t prefix_term = this->dfs_format.df_prefix_terminator;
|
|
|
|
|
|
|
|
this->dfs_format = FORMAT_PLAIN;
|
|
|
|
if (this->dfs_hist[DT_EQUALS]) {
|
|
|
|
qualifier = DT_COLON;
|
|
|
|
separator = DT_EQUALS;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this->dfs_semi_state != DFS_ERROR && this->dfs_hist[DT_SEMI]) {
|
|
|
|
this->dfs_format = FORMAT_SEMI;
|
|
|
|
} else if (this->dfs_comma_state != DFS_ERROR) {
|
|
|
|
this->dfs_format = FORMAT_COMMA;
|
|
|
|
if (separator == DT_COLON && this->dfs_hist[DT_COMMA] > 0) {
|
|
|
|
if (!((this->dfs_hist[DT_COLON] == this->dfs_hist[DT_COMMA]) ||
|
|
|
|
((this->dfs_hist[DT_COLON] - 1) ==
|
|
|
|
this->dfs_hist[DT_COMMA]))) {
|
|
|
|
separator = DT_INVALID;
|
|
|
|
if (this->dfs_hist[DT_COLON] == 1) {
|
|
|
|
prefix_term = DT_COLON;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this->dfs_format.df_qualifier = qualifier;
|
|
|
|
this->dfs_format.df_separator = separator;
|
|
|
|
this->dfs_format.df_prefix_terminator = prefix_term;
|
|
|
|
}
|