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); +} + | 
