/*
* 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 .
*/
#include
#include
#include
#include
#include
#include
#include
#include
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 = "";
const string cnode::ACTIVE_CFG = "@ACTIVE";
const string cnode::WORKING_CFG = "@WORKING";
////// static (internal) functions
static void
_show_diff(const CfgNode *cfg1, const CfgNode *cfg2, int level,
Cpath& cur_path, Cpath& last_ctx, bool show_def,
bool hide_secret, bool context_diff);
static void
_get_cmds_diff(const CfgNode *cfg1, const CfgNode *cfg2,
Cpath& cur_path, vector& del_list,
vector& set_list, vector& com_list);
/* compare the values of a "multi" node in the two configs. the values and
* the "prefix" of each value are returned in "values" and "pfxs",
* respectively.
*
* return value indicates whether the node is different in the two configs.
*
* comparison follows the original perl logic.
*/
static bool
_cmp_multi_values(const CfgNode *cfg1, const CfgNode *cfg2,
vector& values, vector& pfxs)
{
const vector& ovec = cfg1->getValues();
const vector& nvec = cfg2->getValues();
Cstore::MapT nmap;
bool changed = false;
for (size_t i = 0; i < nvec.size(); i++) {
nmap[nvec[i]] = true;
}
Cstore::MapT 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());
changed = true;
}
}
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());
changed = true;
} 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());
changed = true;
}
}
return changed;
}
static void
_cmp_non_leaf_nodes(const CfgNode *cfg1, const CfgNode *cfg2,
vector& rcnodes1,
vector& rcnodes2, bool& not_tag_node,
bool& is_value, bool& is_leaf_typeless,
string& name, string& value)
{
const CfgNode *cfg = (cfg1 ? cfg1 : cfg2);
is_value = cfg->isValue();
not_tag_node = (!cfg->isTag() || is_value);
is_leaf_typeless = cfg->isLeafTypeless();
bool is_tag_node = !not_tag_node;
name = cfg->getName();
if (is_value) {
value = cfg->getValue();
}
// handle child nodes
vector cnodes1, cnodes2;
if (cfg1) {
cnodes1 = cfg1->getChildNodes();
}
if (cfg2) {
cnodes2 = cfg2->getChildNodes();
}
Cstore::MapT map;
Cstore::MapT nmap1, nmap2;
for (size_t i = 0; i < cnodes1.size(); i++) {
string key = (is_tag_node
? cnodes1[i]->getValue() : cnodes1[i]->getName());
map[key] = true;
nmap1[key] = cnodes1[i];
}
for (size_t i = 0; i < cnodes2.size(); i++) {
string key = (is_tag_node
? cnodes2[i]->getValue() : cnodes2[i]->getName());
map[key] = true;
nmap2[key] = cnodes2[i];
}
vector cnodes;
Cstore::MapT::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++) {
Cstore::MapT::iterator p1 = nmap1.find(cnodes[i]);
Cstore::MapT::iterator p2 = nmap2.find(cnodes[i]);
bool in1 = (p1 != nmap1.end());
bool in2 = (p2 != nmap2.end());
CfgNode *c1 = (in1 ? p1->second : NULL);
CfgNode *c2 = (in2 ? p2->second : NULL);
rcnodes1.push_back(c1);
rcnodes2.push_back(c2);
}
}
static void
_add_path_to_list(vector& list, Cpath& path, const string *nptr,
const string *vptr)
{
if (nptr) {
path.push(*nptr);
}
if (vptr) {
path.push(*vptr);
}
list.push_back(path);
if (vptr) {
path.pop();
}
if (nptr) {
path.pop();
}
}
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(const CfgNode *cfg1, const CfgNode *cfg2, int level,
const char *pfx_diff)
{
/* note: activate/deactivate state output was handled here. pending
* redesign, the output notation will be changed to "per-subtree"
* marking, so the output will be handled with the rest of the node.
*/
printf("%s", pfx_diff);
for (int i = 0; i < level; i++) {
printf(" ");
}
}
/* this is used by "context diff" to print the context in "edit notation"
* like in JUNOS "show | compare".
*/
static void
_diff_print_context(Cpath& cur_path, Cpath& last_ctx)
{
if (last_ctx == cur_path) {
// don't repeat the context if it's still the same as the last one
return;
}
last_ctx = cur_path;
printf("[edit");
for (size_t i = 0; i < cur_path.size(); i++) {
printf(" %s", cur_path[i]);
}
printf("]\n");
}
/* print the comment (if any) at the specified node, including "change
* prefix" (if any).
*
* since a node's comment is printed before the node itself, this will
* print the "context" first if doing context diff.
*
* cur_path: the current context (for context diff).
* context_diff: whether we're doing context diff.
*
* return whether anything is printed. this is only used for context diff
* (caller does different things according to return value).
*/
static bool
_diff_print_comment(const CfgNode *cfg1, const CfgNode *cfg2, int level,
Cpath& cur_path, Cpath& last_ctx, bool context_diff)
{
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();
comment = cfg1->getComment();
}
if (comment == "") {
// no comment
return false;
}
/* print comment if
* * not doing context diff
* OR
* * doing context diff and there is actually a difference
*/
if (!context_diff || (pfx_diff != PFX_DIFF_NONE.c_str()
&& pfx_diff != PFX_DIFF_NULL.c_str())) {
if (context_diff) {
// print context first
_diff_print_context(cur_path, last_ctx);
}
_diff_print_indent(cfg1, cfg2, level, pfx_diff);
printf("/* %s */\n", comment.c_str());
return true;
} else {
return false;
}
}
static bool
_diff_check_and_show_leaf(const CfgNode *cfg1, const CfgNode *cfg2, int level,
Cpath& cur_path, Cpath& last_ctx, bool show_def,
bool hide_secret, bool context_diff)
{
if ((cfg1 && !cfg1->isLeaf()) || (cfg2 && !cfg2->isLeaf())) {
// not a leaf node
return false;
}
const 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();
}
}
bool cprint = _diff_print_comment(cfg1, cfg2, level, cur_path, last_ctx,
context_diff);
if (cprint) {
/* when doing context diff, normally we only show the node if there is a
* difference. however, if something was printed for comment, the node
* should be printed even if there is no difference. so set context_diff
* to false to force the node to be displayed.
*/
context_diff = false;
}
if (cfg->isMulti()) {
// multi-value node
if (force_pfx_diff) {
// simple case: just use the same diff prefix for all values
if (!cprint && context_diff) {
/* if nothing was printed for comment and we're doing context diff,
* then context hasn't been displayed yet. so print it first.
*/
_diff_print_context(cur_path, last_ctx);
}
if (!context_diff || force_pfx_diff != PFX_DIFF_NULL.c_str()) {
// not context diff OR there is a difference => print the node
const vector& 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.
vector values;
vector pfxs;
bool changed = _cmp_multi_values(cfg1, cfg2, values, pfxs);
if (!context_diff || changed) {
// not context diff OR there is a difference => print the node
for (size_t i = 0; i < values.size(); i++) {
if (context_diff && pfxs[i] == PFX_DIFF_NONE.c_str()) {
// not printing unchanged values if doing context diff
continue;
}
if (!cprint && context_diff) {
/* if nothing was printed for comment and we're doing context
* diff, then context hasn't been displayed yet. so print it
* first. note that we only want to see the context once, so
* set cprint to true so that later iterations won't print it
* again.
*/
_diff_print_context(cur_path, last_ctx);
cprint = true;
}
_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()) {
string val = cfg->getValue();
if (!force_pfx_diff) {
const string& val1 = cfg1->getValue();
val = cfg2->getValue();
if (val == val1) {
force_pfx_diff = PFX_DIFF_NONE.c_str();
} else {
force_pfx_diff = PFX_DIFF_UPD.c_str();
}
}
bool changed = (force_pfx_diff != PFX_DIFF_NONE.c_str()
&& force_pfx_diff != PFX_DIFF_NULL.c_str());
if (!context_diff || changed) {
// not context diff OR there is a difference => print the node
if (!cprint && context_diff) {
/* if nothing was printed for comment and we're doing context diff,
* then context hasn't been displayed yet. so print it first.
*/
_diff_print_context(cur_path, last_ctx);
}
_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 void
_diff_show_other(const CfgNode *cfg1, const CfgNode *cfg2, int level,
Cpath& cur_path, Cpath& last_ctx, bool show_def,
bool hide_secret, bool context_diff)
{
bool orig_cdiff = context_diff;
const char *pfx_diff = PFX_DIFF_NONE.c_str();
if (!cfg1) {
pfx_diff = PFX_DIFF_ADD.c_str();
} else {
if (!cfg2) {
pfx_diff = PFX_DIFF_DEL.c_str();
} else if (cfg1 == cfg2) {
pfx_diff = PFX_DIFF_NULL.c_str();
}
}
string name, value;
bool not_tag_node, is_value, is_leaf_typeless;
vector rcnodes1, rcnodes2;
_cmp_non_leaf_nodes(cfg1, cfg2, rcnodes1, rcnodes2, not_tag_node, is_value,
is_leaf_typeless, name, value);
/* only print "this" node if it
* (1) is a tag value or an intermediate node,
* (2) is not "root", and
* (3) has a "name".
*/
bool print_this = (not_tag_node && level >= 0 && name.size() > 0);
int next_level = level + 1;
if (print_this) {
bool cprint = _diff_print_comment(cfg1, cfg2, level, cur_path, last_ctx,
orig_cdiff);
if (orig_cdiff && pfx_diff != PFX_DIFF_NONE.c_str()) {
/* note:
* orig_cdiff is the original value of context_diff.
* pfx_diff is set above.
* so the condition here means
* (1) when this function is called, we are doing a context diff
* AND
* (2) pfx_diff is either PFX_DIFF_ADD or PFX_DIFF_DEL, i.e., one
* and only one of cfg1 and cfg2 is NULL.
*
* note that pfx_diff cannot be PFX_DIFF_NULL since
* (cfg1 == cfg2) cannot be true if context_diff is true (see
* caller's logic).
*
* since one and only one of cfg1 and cfg2 is NULL, it means that the
* whole subtree rooted at the current node is "added" or "deleted".
* therefore, set context_diff to false for the recursion into this
* subtree so that it will be displayed normally.
*/
context_diff = false;
}
if (cprint || !orig_cdiff || pfx_diff != PFX_DIFF_NONE.c_str()) {
/* print this node if
* (1) something was printed for comment, in which case the node
* should be printed regardless of whether there is a difference.
* OR
* (2) not doing context diff.
* OR
* (3) there is a difference.
*/
if (!cprint && orig_cdiff) {
/* if nothing was printed for comment and we're doing context diff,
* then context hasn't been displayed yet. so print it first.
*/
_diff_print_context(cur_path, last_ctx);
}
_diff_print_indent(cfg1, cfg2, level, pfx_diff);
if (is_value) {
// at tag value
printf("%s %s", name.c_str(), value.c_str());
} else {
// at intermediate node
printf("%s", name.c_str());
}
if (cprint && orig_cdiff && pfx_diff == PFX_DIFF_NONE.c_str()) {
/* the condition means:
* (1) something was printed for comment.
* AND
* (2) doing context diff.
* AND
* (3) there is no difference for the node itself.
*
* this means we are only printing the node for the comment. so just
* print "{ ... }" to represent the subtree like JUNOS
* "show | compare". any difference in the subtree will be handled
* later by the recursion into it.
*
* in this case also set is_leaf_typeless to true to prevent a
* dangling "}\n" from being printed at the end of this function.
*/
printf(" { ... }\n");
is_leaf_typeless = true;
} else {
printf("%s\n", (is_leaf_typeless ? "" : " {"));
}
}
cur_path.push(name);
if (is_value) {
cur_path.push(value);
}
} else {
next_level = (level >= 0 ? level : 0);
}
for (size_t i = 0; i < rcnodes1.size(); i++) {
_show_diff(rcnodes1[i], rcnodes2[i], next_level, cur_path, last_ctx,
show_def, hide_secret, context_diff);
}
// finish printing "this" node if necessary
if (print_this) {
cur_path.pop();
if (is_value) {
cur_path.pop();
}
if (!orig_cdiff || pfx_diff != PFX_DIFF_NONE.c_str()) {
/* not context diff OR there is a difference => print closing '}'
* if necessary. also note the exception above where is_leaf_typeless
* is set to true to prevent this.
*/
if (!is_leaf_typeless) {
_diff_print_indent(cfg1, cfg2, level, pfx_diff);
printf("}\n");
}
}
}
}
static void
_show_diff(const CfgNode *cfg1, const CfgNode *cfg2, int level,
Cpath& cur_path, Cpath& last_ctx, bool show_def,
bool hide_secret, bool context_diff)
{
// if doesn't exist, treat as NULL
if (cfg1 && !cfg1->exists()) {
cfg1 = NULL;
}
if (cfg2 && !cfg2->exists()) {
cfg2 = NULL;
}
/* 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 (context_diff) {
if (cfg1 == cfg2) {
/* nothing to do for context diff so stop the recursion. this also
* means that during the recursion cfg1 and cfg2 must be different
* if doing context diff.
*/
return;
}
/* when doing context diff, the display indentation level always starts
* at 0.
*/
level = 0;
}
if (_diff_check_and_show_leaf(cfg1, cfg2, (level >= 0 ? level : 0),
cur_path, last_ctx, show_def, hide_secret,
context_diff)) {
// leaf node has been shown. done.
return;
} else {
// intermediate node, tag node, or tag value
_diff_show_other(cfg1, cfg2, level, cur_path, last_ctx, show_def,
hide_secret, context_diff);
}
}
static void
_get_comment_diff_cmd(const CfgNode *cfg1, const CfgNode *cfg2,
Cpath& cur_path, vector& com_list,
const string *val)
{
const string *comment = NULL;
const string *name = NULL;
string empty = "";
string c1, c2;
if (cfg1 != cfg2) {
c1 = (cfg1 ? cfg1->getComment() : "");
c2 = (cfg2 ? cfg2->getComment() : "");
if (c1 != "") {
name = &(cfg1->getName());
if (c2 != "") {
// in both
if (c1 != c2) {
// updated
comment = &c2;
}
} else {
// only in cfg1 => deleted
comment = ∅
}
} else {
if (c2 != "") {
// only in cfg2 => added
name = &(cfg2->getName());
comment = &c2;
}
}
} else {
// cfg1 == cfg2 => just getting all commands
c1 = cfg1->getComment();
if (c1 != "") {
name = &(cfg1->getName());
comment = &c1;
}
}
if (comment) {
if (val) {
cur_path.push(*name);
name = val;
}
_add_path_to_list(com_list, cur_path, name, comment);
if (val) {
cur_path.pop();
}
}
}
static bool
_get_cmds_diff_leaf(const CfgNode *cfg1, const CfgNode *cfg2,
Cpath& cur_path, vector& del_list,
vector& set_list, vector& com_list)
{
if ((cfg1 && !cfg1->isLeaf()) || (cfg2 && !cfg2->isLeaf())) {
// not a leaf node
return false;
}
const CfgNode *cfg = NULL;
vector *list = NULL;
if (cfg1) {
cfg = cfg1;
if (!cfg2) {
// exists in cfg1 but not in cfg2 => delete and stop recursion
_add_path_to_list(del_list, cur_path, &(cfg1->getName()), NULL);
return true;
} else if (cfg1 == cfg2) {
// same config => just translating config to set commands
list = &set_list;
}
} else {
// !cfg1 => cfg2 must not be NULL
cfg = cfg2;
list = &set_list;
}
_get_comment_diff_cmd(cfg1, cfg2, cur_path, com_list, NULL);
if (cfg->isMulti()) {
// multi-value node
if (list) {
const vector& vvec = cfg->getValues();
for (size_t i = 0; i < vvec.size(); i++) {
_add_path_to_list(*list, cur_path, &(cfg->getName()), &(vvec[i]));
}
} else {
// need to actually do a diff.
vector dummy_vals;
vector dummy_pfxs;
if (_cmp_multi_values(cfg1, cfg2, dummy_vals, dummy_pfxs)) {
/* something changed. to get the correct ordering for multi-node
* values, need to delete the node and then set the new values.
*/
const vector& nvec = cfg2->getValues();
_add_path_to_list(del_list, cur_path, &(cfg->getName()), NULL);
for (size_t i = 0; i < nvec.size(); i++) {
_add_path_to_list(set_list, cur_path, &(cfg->getName()), &(nvec[i]));
}
}
}
} else {
// single-value node
string val = cfg->getValue();
if (!list) {
const string& val1 = cfg1->getValue();
val = cfg2->getValue();
if (val != val1) {
// changed => need to set it
list = &set_list;
}
}
if (list) {
_add_path_to_list(*list, cur_path, &(cfg->getName()), &val);
}
}
return true;
}
static void
_get_cmds_diff_other(const CfgNode *cfg1, const CfgNode *cfg2,
Cpath& cur_path, vector& del_list,
vector& set_list, vector& com_list)
{
vector *list = NULL;
if (cfg1) {
if (!cfg2) {
// exists in cfg1 but not in cfg2 => delete and stop recursion
_add_path_to_list(del_list, cur_path, &(cfg1->getName()),
(cfg1->isValue() ? &(cfg1->getValue()) : NULL));
return;
} else if (cfg1 == cfg2) {
// same config => just translating config to set commands
list = &set_list;
}
} else {
// !cfg1 => cfg2 must not be NULL
list = &set_list;
}
string name, value;
bool not_tag_node, is_value, is_leaf_typeless;
vector rcnodes1, rcnodes2;
_cmp_non_leaf_nodes(cfg1, cfg2, rcnodes1, rcnodes2, not_tag_node, is_value,
is_leaf_typeless, name, value);
if (rcnodes1.size() < 1 && list) {
// subtree is empty
_add_path_to_list(*list, cur_path, &name, (is_value ? &value : NULL));
return;
}
bool add_this = (not_tag_node && name.size() > 0);
if (add_this) {
const string *val = (is_value ? &value : NULL);
_get_comment_diff_cmd(cfg1, cfg2, cur_path, com_list, val);
cur_path.push(name);
if (is_value) {
cur_path.push(value);
}
}
for (size_t i = 0; i < rcnodes1.size(); i++) {
_get_cmds_diff(rcnodes1[i], rcnodes2[i], cur_path, del_list, set_list,
com_list);
}
if (add_this) {
if (is_value) {
cur_path.pop();
}
cur_path.pop();
}
}
static void
_get_cmds_diff(const CfgNode *cfg1, const CfgNode *cfg2,
Cpath& cur_path, vector& del_list,
vector& set_list, vector& com_list)
{
// if doesn't exist, treat as NULL
if (cfg1 && !cfg1->exists()) {
cfg1 = NULL;
}
if (cfg2 && !cfg2->exists()) {
cfg2 = NULL;
}
if (!cfg1 && !cfg2) {
fprintf(stderr, "_get_cmds_diff error (both config NULL)\n");
exit(1);
}
if (_get_cmds_diff_leaf(cfg1, cfg2, cur_path, del_list, set_list,
com_list)) {
// leaf node has been shown. done.
return;
} else {
// intermediate node, tag node, or tag value
_get_cmds_diff_other(cfg1, cfg2, cur_path, del_list, set_list, com_list);
}
}
static void
_print_cmds_list(const char *op, vector& list)
{
for (size_t i = 0; i < list.size(); i++) {
printf("%s", op);
for (size_t j = 0; j < list[i].size(); j++) {
printf(" '%s'", list[i][j]);
}
printf("\n");
}
}
////// algorithms
void
cnode::show_cfg_diff(const CfgNode& cfg1, const CfgNode& cfg2,
Cpath& cur_path, bool show_def, bool hide_secret,
bool context_diff)
{
if (cfg1.isInvalid() || cfg2.isInvalid()) {
printf("Specified configuration path is not valid\n");
return;
}
if ((cfg1.isEmpty() && cfg2.isEmpty())
|| (!cfg1.exists() && !cfg2.exists())) {
printf("Configuration under specified path is empty\n");
return;
}
// use an invalid value for initial last_ctx
Cpath last_ctx;
_show_diff(&cfg1, &cfg2, -1, cur_path, last_ctx, show_def, hide_secret,
context_diff);
}
void
cnode::show_cfg(const CfgNode& cfg, bool show_def, bool hide_secret)
{
Cpath cur_path;
show_cfg_diff(cfg, cfg, cur_path, show_def, hide_secret);
}
void
cnode::show_cmds_diff(const CfgNode& cfg1, const CfgNode& cfg2)
{
Cpath cur_path;
vector del_list;
vector set_list;
vector com_list;
_get_cmds_diff(&cfg1, &cfg2, cur_path, del_list, set_list, com_list);
_print_cmds_list("delete", del_list);
_print_cmds_list("set", set_list);
_print_cmds_list("comment", com_list);
}
void
cnode::show_cmds(const CfgNode& cfg)
{
show_cmds_diff(cfg, cfg);
}
void
cnode::get_cmds_diff(const CfgNode& cfg1, const CfgNode& cfg2,
vector& del_list, vector& set_list,
vector& com_list)
{
Cpath cur_path;
_get_cmds_diff(&cfg1, &cfg2, cur_path, del_list, set_list, com_list);
}
void
cnode::get_cmds(const CfgNode& cfg, vector& set_list,
vector& com_list)
{
Cpath cur_path;
vector del_list;
_get_cmds_diff(&cfg, &cfg, cur_path, del_list, set_list, com_list);
}
void
cnode::showConfig(const string& cfg1, const string& cfg2,
const Cpath& path, bool show_def, bool hide_secret,
bool context_diff, bool show_cmds)
{
tr1::shared_ptr aroot, wroot, croot1, croot2;
tr1::shared_ptr cstore;
Cpath rpath(path);
Cpath cur_path;
if ((cfg1 == ACTIVE_CFG || cfg1 == WORKING_CFG)
&& (cfg2 == ACTIVE_CFG || cfg2 == WORKING_CFG)) {
// active/working config only => use edit level and path
cstore.reset(Cstore::createCstore(true));
cstore->getEditLevel(cur_path);
} else {
// at least one config file => don't use edit level and path
cstore.reset(Cstore::createCstore(false));
rpath.clear();
}
if (cfg1 == ACTIVE_CFG || cfg2 == ACTIVE_CFG) {
aroot.reset(new CfgNode(*cstore, rpath, true, true));
}
if (cfg1 == WORKING_CFG || cfg2 == WORKING_CFG) {
// note: if there is no config session, this will abort
wroot.reset(new CfgNode(*cstore, rpath, false, true));
}
if (cfg1 == ACTIVE_CFG) {
croot1 = aroot;
} else if (cfg1 == WORKING_CFG) {
croot1 = wroot;
} else {
croot1.reset(cparse::parse_file(cfg1.c_str(), *cstore));
}
if (cfg2 == ACTIVE_CFG) {
croot2 = aroot;
} else if (cfg2 == WORKING_CFG) {
croot2 = wroot;
} else {
croot2.reset(cparse::parse_file(cfg2.c_str(), *cstore));
}
if (!croot1.get() || !croot2.get()) {
printf("Cannot parse specified config file(s)\n");
return;
}
if (show_cmds) {
show_cmds_diff(*croot1, *croot2);
} else {
show_cfg_diff(*croot1, *croot2, cur_path, show_def, hide_secret,
context_diff);
}
}