diff options
author | An-Cheng Huang <ancheng@vyatta.com> | 2010-11-09 18:38:49 -0800 |
---|---|---|
committer | An-Cheng Huang <ancheng@vyatta.com> | 2010-11-09 18:38:49 -0800 |
commit | 1e615a4e3f3681f8bd02d49d688001d61aa8b815 (patch) | |
tree | ed5ce2367d540d5414a283e1e2b9c2e20d0239cd /src/cnode/cnode-algorithm.cpp | |
parent | b8f85d2744a42eb18ac78451c29dc6de7bab5fea (diff) | |
download | vyatta-cfg-1e615a4e3f3681f8bd02d49d688001d61aa8b815.tar.gz vyatta-cfg-1e615a4e3f3681f8bd02d49d688001d61aa8b815.zip |
initial rework of config output framework
* separate data from algorithm.
* prepare for unified input/output framework.
Diffstat (limited to 'src/cnode/cnode-algorithm.cpp')
-rw-r--r-- | src/cnode/cnode-algorithm.cpp | 411 |
1 files changed, 411 insertions, 0 deletions
diff --git a/src/cnode/cnode-algorithm.cpp b/src/cnode/cnode-algorithm.cpp new file mode 100644 index 0000000..9f67d7d --- /dev/null +++ b/src/cnode/cnode-algorithm.cpp @@ -0,0 +1,411 @@ +/* + * Copyright (C) 2010 Vyatta, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <cstdio> +#include <cstdlib> +#include <cstring> + +#include <cstore/cstore.hpp> +#include <cnode/cnode.hpp> +#include <cnode/cnode-algorithm.hpp> + +using namespace std; +using namespace cnode; + + +////// constants +static const string PFX_DIFF_ADD = "+"; // added +static const string PFX_DIFF_DEL = "-"; // deleted +static const string PFX_DIFF_UPD = ">"; // changed +static const string PFX_DIFF_NONE = " "; +static const string PFX_DIFF_NULL = ""; + +static const string PFX_DEACT_D = "!"; // deactivated +static const string PFX_DEACT_DP = "D"; // deactivate pending +static const string PFX_DEACT_AP = "A"; // activate pending +static const string PFX_DEACT_NONE = " "; + + +////// static (internal) functions +static void +_show_diff(CfgNode *cfg1, CfgNode *cfg2, int level, bool show_def, + bool hide_secret); + +static void +_print_value_str(const string& name, const char *vstr, bool hide_secret) +{ + // handle secret hiding first + if (hide_secret) { + static const char *sname[] = { "passphrase", "password", + "pre-shared-secret", "key", NULL }; + static size_t slen[] = { 10, 8, 17, 3, 0 }; + size_t nlen = name.length(); + for (size_t i = 0; sname[i]; i++) { + if (nlen < slen[i]) { + // can't match + continue; + } + if (name.find(sname[i], nlen - slen[i]) != name.npos) { + // found secret + printf("****************"); + return; + } + } + } + + const char *quote = ""; + size_t vlen = strlen(vstr); + if (*vstr == 0 || strcspn(vstr, "*}{;\011\012\013\014\015 ") < vlen) { + quote = "\""; + } + printf("%s%s%s", quote, vstr, quote); +} + +static void +_diff_print_indent(CfgNode *cfg1, CfgNode *cfg2, int level, + const char *pfx_diff) +{ + const char *pfx_deact = PFX_DEACT_NONE.c_str(); + if (cfg1 && cfg2) { + if (cfg1->isDeactivated()) { + if (cfg2->isDeactivated()) { + pfx_deact = PFX_DEACT_D.c_str(); + } else { + pfx_deact = PFX_DEACT_AP.c_str(); + } + } else { + if (cfg2->isDeactivated()) { + pfx_deact = PFX_DEACT_DP.c_str(); + } + // 4th case handled by default + } + } + + printf("%s %s", pfx_deact, pfx_diff); + for (int i = 0; i < level; i++) { + printf(" "); + } +} + +static void +_diff_print_comment(CfgNode *cfg1, CfgNode *cfg2, int level) +{ + const char *pfx_diff = PFX_DIFF_NONE.c_str(); + string comment = ""; + if (cfg1 != cfg2) { + string c1 = (cfg1 ? cfg1->getComment() : ""); + string c2 = (cfg2 ? cfg2->getComment() : ""); + if (c1 != "") { + if (c2 != "") { + // in both + comment = c2; + if (c1 != c2) { + pfx_diff = PFX_DIFF_UPD.c_str(); + } + } else { + // only in cfg1 + comment = c1; + pfx_diff = PFX_DIFF_DEL.c_str(); + } + } else { + if (c2 != "") { + // only in cfg2 + comment = c2; + pfx_diff = PFX_DIFF_ADD.c_str(); + } + // 4th case handled by default + } + } else { + // same node => no diff + pfx_diff = PFX_DIFF_NULL.c_str(); + } + if (comment == "") { + // no comment + return; + } + _diff_print_indent(cfg1, cfg2, level, pfx_diff); + printf("/* %s */\n", comment.c_str()); +} + +static bool +_diff_check_and_show_leaf(CfgNode *cfg1, CfgNode *cfg2, int level, + bool show_def, bool hide_secret) +{ + if ((cfg1 && !cfg1->isLeaf()) || (cfg2 && !cfg2->isLeaf())) { + // not a leaf node + return false; + } + + CfgNode *cfg = NULL; + const char *force_pfx_diff = NULL; + if (!cfg1) { + cfg = cfg2; + force_pfx_diff = PFX_DIFF_ADD.c_str(); + } else { + cfg = cfg1; + if (!cfg2) { + force_pfx_diff = PFX_DIFF_DEL.c_str(); + } else if (cfg1 == cfg2) { + force_pfx_diff = PFX_DIFF_NULL.c_str(); + } + } + + if (cfg->isMulti()) { + // multi-value node + _diff_print_comment(cfg1, cfg2, level); + if (force_pfx_diff) { + // simple case: just use the same diff prefix for all values + const vector<string>& vvec = cfg->getValues(); + for (size_t i = 0; i < vvec.size(); i++) { + _diff_print_indent(cfg1, cfg2, level, force_pfx_diff); + printf("%s ", cfg->getName().c_str()); + _print_value_str(cfg->getName(), vvec[i].c_str(), hide_secret); + printf("\n"); + } + } else { + // need to actually do a diff. + // this follows the original perl logic. + const vector<string>& ovec = cfg1->getValues(); + const vector<string>& nvec = cfg1->getValues(); + vector<string> values; + vector<const char *> pfxs; + Cstore::MapT<string, bool> nmap; + for (size_t i = 0; i < nvec.size(); i++) { + nmap[nvec[i]] = true; + } + Cstore::MapT<string, bool> omap; + for (size_t i = 0; i < ovec.size(); i++) { + omap[ovec[i]] = true; + if (nmap.find(ovec[i]) == nmap.end()) { + values.push_back(ovec[i]); + pfxs.push_back(PFX_DIFF_DEL.c_str()); + } + } + + for (size_t i = 0; i < nvec.size(); i++) { + values.push_back(nvec[i]); + if (omap.find(nvec[i]) == omap.end()) { + pfxs.push_back(PFX_DIFF_ADD.c_str()); + } else if (i < ovec.size() && nvec[i] == ovec[i]) { + pfxs.push_back(PFX_DIFF_NONE.c_str()); + } else { + pfxs.push_back(PFX_DIFF_UPD.c_str()); + } + } + + for (size_t i = 0; i < values.size(); i++) { + _diff_print_indent(cfg1, cfg2, level, pfxs[i]); + printf("%s ", cfg->getName().c_str()); + _print_value_str(cfg->getName(), values[i].c_str(), hide_secret); + printf("\n"); + } + } + } else { + // single-value node + if (show_def || !cfg->isDefault()) { + const string& val = cfg->getValue(); + if (!force_pfx_diff) { + const string& val1 = cfg1->getValue(); + if (val == val1) { + force_pfx_diff = PFX_DIFF_NONE.c_str(); + } else { + force_pfx_diff = PFX_DIFF_UPD.c_str(); + } + } + _diff_print_indent(cfg1, cfg2, level, force_pfx_diff); + printf("%s ", cfg->getName().c_str()); + _print_value_str(cfg->getName(), val.c_str(), hide_secret); + printf("\n"); + } + } + + return true; +} + +static bool +_diff_check_and_show_tag(CfgNode *cfg1, CfgNode *cfg2, int level, + bool show_def, bool hide_secret) +{ + if ((cfg1 && !cfg1->isTag()) || (cfg2 && !cfg2->isTag())) { + // not a tag node + return false; + } + + vector<CfgNode *> vals1, vals2; + if (cfg1) { + vals1 = cfg1->getTagValues(); + } + if (cfg2) { + vals2 = cfg2->getTagValues(); + } + + Cstore::MapT<string, bool> vmap; + Cstore::MapT<string, CfgNode *> vnmap1, vnmap2; + for (size_t i = 0; i < vals1.size(); i++) { + vmap[vals1[i]->getValue()] = true; + vnmap1[vals1[i]->getValue()] = vals1[i]; + } + for (size_t i = 0; i < vals2.size(); i++) { + vmap[vals2[i]->getValue()] = true; + vnmap2[vals2[i]->getValue()] = vals2[i]; + } + + vector<string> values; + Cstore::MapT<string, bool>::iterator it = vmap.begin(); + for (; it != vmap.end(); ++it) { + values.push_back((*it).first); + } + Cstore::sortNodes(values); + + for (size_t i = 0; i < values.size(); i++) { + bool in1 = (vnmap1.find(values[i]) != vnmap1.end()); + bool in2 = (vnmap2.find(values[i]) != vnmap2.end()); + CfgNode *c1 = (in1 ? vnmap1[values[i]] : NULL); + CfgNode *c2 = (in2 ? vnmap2[values[i]] : NULL); + /* note: if the root is a tag node (level == -1), then need to make + * level 0 when calling tag values' show(). + */ + _show_diff(c1, c2, ((level >= 0) ? level : 0), show_def, hide_secret); + } + return true; +} + +static void +_diff_show_other(CfgNode *cfg1, CfgNode *cfg2, int level, bool show_def, + bool hide_secret) +{ + CfgNode *cfg = NULL; + const char *pfx_diff = PFX_DIFF_NONE.c_str(); + if (!cfg1) { + cfg = cfg2; + pfx_diff = PFX_DIFF_ADD.c_str(); + } else { + cfg = cfg1; + if (!cfg2) { + pfx_diff = PFX_DIFF_DEL.c_str(); + } else if (cfg1 == cfg2) { + pfx_diff = PFX_DIFF_NULL.c_str(); + } + } + + const string& name = cfg->getName(); + bool print_this = (level >= 0 && name.size() > 0); + if (print_this) { + _diff_print_comment(cfg1, cfg2, level); + _diff_print_indent(cfg1, cfg2, level, pfx_diff); + if (cfg->isValue()) { + // at tag value + printf("%s %s", name.c_str(), cfg->getValue().c_str()); + } else { + // at intermediate node + printf("%s", name.c_str()); + } + printf("%s\n", (cfg->isLeafTypeless() ? "" : " {")); + } + + vector<CfgNode *> cnodes1, cnodes2; + if (cfg1) { + cnodes1 = cfg1->getChildNodes(); + } + if (cfg2) { + cnodes2 = cfg2->getChildNodes(); + } + + Cstore::MapT<string, bool> map; + Cstore::MapT<string, CfgNode *> nmap1, nmap2; + for (size_t i = 0; i < cnodes1.size(); i++) { + map[cnodes1[i]->getName()] = true; + nmap1[cnodes1[i]->getName()] = cnodes1[i]; + } + for (size_t i = 0; i < cnodes2.size(); i++) { + map[cnodes2[i]->getName()] = true; + nmap2[cnodes2[i]->getName()] = cnodes2[i]; + } + + vector<string> cnodes; + Cstore::MapT<string, bool>::iterator it = map.begin(); + for (; it != map.end(); ++it) { + cnodes.push_back((*it).first); + } + Cstore::sortNodes(cnodes); + + for (size_t i = 0; i < cnodes.size(); i++) { + bool in1 = (nmap1.find(cnodes[i]) != nmap1.end()); + bool in2 = (nmap2.find(cnodes[i]) != nmap2.end()); + CfgNode *c1 = (in1 ? nmap1[cnodes[i]] : NULL); + CfgNode *c2 = (in2 ? nmap2[cnodes[i]] : NULL); + _show_diff(c1, c2, level + 1, show_def, hide_secret); + } + + if (print_this && !cfg->isLeafTypeless()) { + _diff_print_indent(cfg1, cfg2, level, pfx_diff); + printf("}\n"); + } +} + +static void +_show_diff(CfgNode *cfg1, CfgNode *cfg2, int level, bool show_def, + bool hide_secret) +{ + /* cfg1 and cfg2 point to the same config node in two configs. a "diff" + * output is shown comparing the two configs recursively with this node + * as the root of the config tree. + * + * there are four possible scenarios: + * (1) (cfg1 && cfg2) && (cfg1 != cfg2): node exists in both config. + * (2) (cfg1 && cfg2) && (cfg1 == cfg2): the two point to the same config. + * this will be just a "show". + * (3) (!cfg1 && cfg2): node exists in cfg2 but not in cfg1 (added). + * (4) (cfg1 && !cfg2): node exists in cfg1 but not in cfg1 (deleted). + * + * calling this with both NULL is invalid. + */ + if (!cfg1 && !cfg2) { + fprintf(stderr, "_show_diff error (both config NULL)\n"); + exit(1); + } + + if (_diff_check_and_show_leaf(cfg1, cfg2, level, show_def, hide_secret)) { + // leaf node has been shown. done. + return; + } else if (_diff_check_and_show_tag(cfg1, cfg2, level, show_def, + hide_secret)) { + // tag node has been shown. done. + return; + } else { + // intermediate node or tag value + _diff_show_other(cfg1, cfg2, level, show_def, hide_secret); + } +} + + +////// algorithms +void +cnode::show_diff(const CfgNode& cfg1, const CfgNode& cfg2, bool show_def, + bool hide_secret) +{ + if (cfg1.isInvalid() || cfg2.isInvalid()) { + printf("Specified configuration path is not valid\n"); + return; + } + if (cfg1.isEmpty() && cfg2.isEmpty()) { + printf("Configuration under specified path is empty\n"); + return; + } + _show_diff(const_cast<CfgNode *>(&cfg1), const_cast<CfgNode *>(&cfg2), -1, + show_def, hide_secret); +} + |