#include "configparser.h" #include #include #include #include #include using namespace std; const int OPERATIONSET = 0; const int OPERATIONADD = 1; const int OPERATION_MERGE = 2; // конструктор ConfigParam ConfigParam::ConfigParam() { name = ""; this->val.clear(); } // создаем ConfigParam из строки ConfigParam::ConfigParam(string s) { name = s; this->val.clear(); } // создаем ConfigParam по имени и значению ConfigParam::ConfigParam(string name, string value) { this->name = name; this->val.clear(); ConfigValue v; v.type = CONFIGVALUETYPESTRING; v.sval = value; this->val.push_back(v); } bool ConfigParser::IsDelim(char c) { if (IsWS(c)) return true; if ((c == '{') || (c == '}')) return true; if ((c == '[') || (c == ']')) return true; if (c == ',') return true; if (c == ';') return true; if (c == '#') return true; return false; } bool operator<(const ConfigParam &a, const ConfigParam &b) { return a.name < b.name; } string ConfigParser::i2s(int a) { ostringstream oss; oss << a; return oss.str(); } // Является ли символ whitespace-ом bool ConfigParser::IsWS(char c) { return ((c == ' ') || (c == '\n') || (c == '\r') || (c == '\t')); } // Можно ли такие символы использовать в названии параметров bool ConfigParser::GoodForName(char c) { if (IsWS(c)) return false; if ((c == '[') || (c == ']')) return false; if ((c == '{') || (c == '}')) return false; if ((c == '(') || (c == ')')) return false; if (c == '"') return false; if (c == '\\') return false; if (c == '+') return false; if (c == '=') return false; return true; } // возвращает символ, соответствующий escape-последовательности "\c" char ConfigParser::UnSlash(char c) { if (c == 'n') return '\n'; if (c == '\\') return '\\'; if (c == '\"') return '"'; return c; } // разбирает s с l по r, считая, что она должна быть ConfigParam - ом // возвращает, какая операция была применена int ConfigParser::ParseConfigParam(int &l, int &r, ConfigParam &p) { while ((l <= r) && (IsWS(s[l]))) { if (s[l] == '\n') cline++; l++; } if (l > r) throw "Expected parameter at line " + i2s(cline); if (!GoodForName(s[l])) { throw "Parameter name expected at line " + i2s(cline); } p.name = ""; while (GoodForName(s[l])) { if (l == r) throw "Unfinished param at line " + i2s(cline); p.name += s[l]; l++; } while ((l <= r) && (IsWS(s[l]))) { if (s[l] == '\n') cline++; l++; } if (l > r) throw "Unfinished param at line " + i2s(cline); int op; if (s[l] == '+') { if ((l == r) || (s[l + 1] != '=')) throw "Unrecognized operation: + or += expected at line " + i2s(cline); op = OPERATIONADD; l += 2; } else if (s[l] == '=') { op = OPERATIONSET; l++; } else if (s[l] == '(') { // this is a function call l++; string args = ""; while (l <= r && s[l] != ')') { args += s[l]; if (s[l] == '\n') cline++; l++; } if (l > r) throw (string)("Unfinished function args at line " + i2s(cline)); l++; if (p.name == "include") { // пока так костыльно, годный парсинг аргументов надо вместе с глобальным // рефакторингом парсера делать TrimSpaces(args); TrimQuotes(args); ConfigParser t; t.Load(args); p.val.push_back(t.cfg); // в этом случае значение имеет тип "множество" p.val[0].type = CONFIGVALUETYPESET; return OPERATION_MERGE; } else { throw "Unknown function " + p.name; } } else throw "Unrecognized operation: + or += expected at line " + i2s(cline); while ((l <= r) && (IsWS(s[l]))) { if (s[l] == '\n') cline++; l++; } if (l > r) throw "Expected parameter value " + i2s(cline); if (s[l] == '{') { ConfigValue a; p.val.push_back(a); l++; // в этом случае значение имеет тип "множество" p.val[0].type = CONFIGVALUETYPESET; ParseConfigValue(l, r, p.val[0]); } else if (s[l] == '[') { l++; ParseConfigParamValues(l, r, p); } else { ConfigValue a; p.val.push_back(a); // в этом случае значение имеет тип "строка" p.val[0].type = CONFIGVALUETYPESTRING; ParseConfigValue(l, r, p.val[0]); } return op; } // Разбирает несколько ConfigValues, заданных через запятую void ConfigParser::ParseConfigParamValues(int &l, int &r, ConfigParam &b) { while(1) { while ((l <= r) && (IsWS(s[l]))) { if (s[l] == '\n') cline++; l++; } if (l > r) throw "Unfinished array at line " + i2s(cline); if (s[l] == ']') { l++; break; } ConfigValue a; b.val.push_back(a); if (s[l] == '{') { l++; b.val[b.val.size() - 1].type = CONFIGVALUETYPESET; } else { b.val[b.val.size() - 1].type = CONFIGVALUETYPESTRING; } ParseConfigValue(l, r, b.val[b.val.size() - 1]); if (l > r) throw "Unfinished array at line " + i2s(cline); if (s[l] == ']') { l++; break; } l++; } } // разбирает строку s с l по r, считая, что она - корректный ConfigValue void ConfigParser::ParseConfigValue(int &l, int &r, ConfigValue &vl) { while ((l <= r) && (IsWS(s[l]))) { if (s[l] == '\n') cline++; l++; } if (l > r) return; // отсечем комментарии if ((s[l] == ';') || (s[l] == '#')) { while (s[l] != '\n') l++; ParseConfigValue(l, r, vl); return; } // если в точке l наличествует символ-разделитель, значит // наше Value закончилось if ((s[l] == ',') || (s[l] == ']')) { return; } if (vl.type == CONFIGVALUETYPESET) { // нашли скобочку - set закрылся if (s[l] == '}') { l++; return; } if (!GoodForName(s[l])) { throw "Parameter name expected at line " + i2s(cline); } ConfigParam p; int oper = ParseConfigParam(l, r, p); if (oper == OPERATION_MERGE) { for (set::iterator t = p.val[0].pval.begin(); t != p.val[0].pval.end(); t++) { vl.pval.insert(*t); } } else { set::iterator it = vl.pval.find(p); if (it == vl.pval.end()) { vl.pval.insert(p); } else { int i; if (oper == OPERATIONADD) { reverse(p.val.begin(), p.val.end()); for (i = ((int)it->val.size() - 1); i >= 0; i--) { p.val.push_back(it->val[i]); } reverse(p.val.begin(), p.val.end()); vl.pval.erase(it); vl.pval.insert(p); } else if (oper == OPERATIONSET) { vl.pval.erase(it); vl.pval.insert(p); } else { throw (string)("Internal error (wrong operation returned) at line " + i2s(cline)); } } } ParseConfigValue(l, r, vl); } else { vl.sval = ""; int isslashed = 0; int isquoted = 0; while (1) { if (l > r) throw "Unfinished value at line " + i2s(cline); if (isslashed) { vl.sval += UnSlash(s[l]); isslashed = 0; } else { if (s[l] == '\\') isslashed = 1; else if (s[l] == '"') isquoted = 1 - isquoted; else { if (IsDelim(s[l])) { if (isquoted == 0) break; if (s[l] == '\n') throw "Unexpected end of line at line " + i2s(cline); } vl.sval += s[l]; } } l++; } } } // убирает двойные кавычки в начале/конце строки void ConfigParser::TrimQuotes(string &s) { if (((int)s.length()) < 2) return; if ((s[0] == '\"') && (s[((int)s.length()) - 1] == '\"')) { s = s.substr(1, s.length() - 2); } } void ConfigParser::Load(string fname) { // Загружаем весь конфиг в одну строку, теперь будем ее разбирать FILE *fin = fopen(fname.c_str(), "rt"); if (!fin) throw "Config file " + fname + " not found!"; int c = getc(fin); slen = 0; while (c != EOF) { slen++; c = getc(fin); } s = new char[slen + 1]; int i; fclose(fin); fin = fopen(fname.c_str(), "rt"); for (i = 0; i < slen; i++) s[i] = getc(fin); fclose(fin); s[slen] = '\n'; slen++; // Теперь считаем всю строку конфиг-значением (так оно и должно быть) cline = 1; cfg.type = CONFIGVALUETYPESET; cfg.sval = ""; cfg.pval.clear(); try{ int l = 0; int r = slen - 1; ParseConfigValue(l, r, cfg); delete []s; } catch(string ss) { delete []s; throw "Error parsing " + fname + ": " + ss; } catch(...) { delete []s; throw "Unrecognized error parsing " + fname; } } // возвращает значение параметра (в старом стиле) string ConfigParser::GetParam(string name) { set::iterator p; p = this->cfg.pval.find(ConfigParam(name)); if (p != this->cfg.pval.end()) { if (p->val.size() == 0) throw "There are no values for parameter " + name; return p->val[0].sval; } throw ((string)"No param \"" + name + "\" found!"); } // возвращает значение параметра (в старом стиле) string ConfigParser::GetParamSilent(string name) { set::iterator p; p = this->cfg.pval.find(ConfigParam(name)); if (p != this->cfg.pval.end()) { if (p->val.size() == 0) return ""; return p->val[0].sval; } return ""; } // возвращает какое-то одно значение параметра // генерирует исключение при ошибке string ConfigParam::GetSingleVal() const { if (val.size() == 0) throw ((string)"No value available"); if (val[0].type == CONFIGVALUETYPESTRING) return val[0].sval; else { throw "Wrong parameter value type (string expected)"; } } // убивает в строке пробелы в начале и в конце void ConfigParser::TrimSpaces(string &s) { string tmp = s; s = ""; int wf = 0, i; for (i = 0; i < ((int)tmp.length()); i++) { if (! IsWS(tmp[i]) ) { wf = 1; } if (wf == 1) s += tmp[i]; } while ((s.length() > 0) && (IsWS(s[s.length() - 1]))) { s.erase(--s.end()); } } // Ищет в этом value заданный параметр. Возвращает пустую строку, если такого нет. string ConfigValue::GetParamVal(string pname) const { if (this->type == CONFIGVALUETYPESTRING) throw ((string)"Attempted to get param from value, which is not a set"); set::iterator p = this->pval.find((ConfigParam)(pname)); if (p == pval.end()) return ""; return p->GetSingleVal(); }