// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*-
// vim:set sts=4 ts=8:

// Copyright (c) 2001-2007 International Computer Science Institute
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software")
// to deal in the Software without restriction, subject to the conditions
// listed in the XORP LICENSE file. These conditions include: you must
// preserve this copyright notice, and you cannot mention the copyright
// holders in advertising related to the Software without their permission.
// The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
// notice is a summary of the XORP LICENSE file; the license in that file is
// legally binding.

#ident "$XORP: xorp/rtrmgr/conf_tree_node.cc,v 1.114 2007/02/16 22:47:21 pavlin Exp $"

//#define DEBUG_LOGGING
#include "rtrmgr_module.h"

#include "libxorp/xorp.h"
#include "libxorp/xlog.h"
#include "libxorp/debug.h"
#include "libxorp/utils.hh"

#include "command_tree.hh"
#include "conf_tree_node.hh"
#include "module_command.hh"
#include "template_commands.hh"
#include "template_tree_node.hh"
#include "util.hh"


extern int booterror(const char *s) throw (ParseError);

bool
CTN_Compare::operator() (ConfigTreeNode* a, ConfigTreeNode *b)
{
    //
    // If the nodes have the same template, then we sort according to their
    // own sort order, otherwise we sort them into template tree order.
    //
    if (a->template_tree_node() == b->template_tree_node()) {
	TTSortOrder order = a->template_tree_node()->order();
	switch (order) {
	case ORDER_UNSORTED:
	    // Don't sort
	    return false;
	case ORDER_SORTED_NUMERIC:
	    if (a->is_tag()) {
		XLOG_ASSERT(!a->const_children().empty());
		XLOG_ASSERT(!b->const_children().empty());
		const ConfigTreeNode *child_a = *(a->const_children().begin());
		const ConfigTreeNode *child_b = *(b->const_children().begin());
		return (strtoll(child_a->segname().c_str(), (char **)NULL, 10)
			< strtoll(child_b->segname().c_str(), (char **)NULL, 10));
	    } else {
		return (strtoll(a->segname().c_str(), (char **)NULL, 10)
			< strtoll(b->segname().c_str(), (char **)NULL, 10));
	    }
	case ORDER_SORTED_ALPHABETIC:
	    if (a->is_tag()) {
		XLOG_ASSERT(!a->const_children().empty());
		XLOG_ASSERT(!b->const_children().empty());
		const ConfigTreeNode *child_a = *(a->const_children().begin());
		const ConfigTreeNode *child_b = *(b->const_children().begin());
		return (child_a->str() < child_b->str());
	    } else {
		return (a->str() < b->str());
	    }
	}
    }

    // Nodes have different templates, so sort by template file order
    return (a->child_number() < b->child_number());
}


ConfigTreeNode::ConfigTreeNode(bool verbose)
    : _template_tree_node(NULL),
      _has_value(false),
      _operator(OP_NONE),
      _committed_operator(OP_NONE),
      _parent(NULL),
      _node_id(ConfigNodeId::ZERO()),
      _node_id_generator(ConfigNodeId::ZERO()),
      _user_id(0),
      _committed_user_id(0),
      _clientid(0),
      _modification_time(TimeVal::ZERO()),
      _committed_modification_time(TimeVal::ZERO()),
      _existence_committed(false),
      _value_committed(false),
      _deleted(false),
      _on_parent_path(false),
      _verbose(verbose)
{
}

ConfigTreeNode::ConfigTreeNode(const string& nodename,
			       const string& path, 
			       const TemplateTreeNode* ttn,
			       ConfigTreeNode* parent,
			       const ConfigNodeId& node_id,
			       uid_t user_id,
			       uint32_t clientid,
			       bool verbose)
    : _template_tree_node(ttn),
      _has_value(false),
      _operator(OP_NONE),
      _committed_operator(OP_NONE),
      _segname(nodename),
      _path(path),
      _parent(parent),
      _node_id(node_id),
      _node_id_generator(ConfigNodeId::ZERO()),
      _user_id(user_id),
      _committed_user_id(0),
      _clientid(clientid),
      _modification_time(TimeVal::ZERO()),
      _committed_modification_time(TimeVal::ZERO()),
      _existence_committed(false),
      _value_committed(false),
      _deleted(false),
      _on_parent_path(false),
      _verbose(verbose)
{
    TimerList::system_gettimeofday(&_modification_time);
    _node_id_generator.set_instance_id(_clientid);
    parent->add_child(this);
    if (node_id.is_empty()) {
	allocate_unique_node_id();
    }
}

ConfigTreeNode::ConfigTreeNode(const ConfigTreeNode& ctn)
    : _template_tree_node(ctn._template_tree_node),
      _has_value(ctn._has_value),
      _value(ctn._value),
      _committed_value(ctn._committed_value),
      _operator(ctn._operator),
      _committed_operator(ctn._committed_operator),
      _segname(ctn._segname),
      _path(ctn._path),
      _parent(NULL),
      _node_id(ctn._node_id),
      _node_id_generator(ctn._node_id_generator),
      _user_id(ctn._user_id),
      _committed_user_id(ctn._committed_user_id),
      _clientid(ctn._clientid),
      _modification_time(ctn._modification_time),
      _committed_modification_time(ctn._committed_modification_time),
      _existence_committed(ctn._existence_committed),
      _value_committed(ctn._value_committed),
      _deleted(ctn._deleted),
      _on_parent_path(false),
      _verbose(ctn._verbose)
{
}

ConfigTreeNode::~ConfigTreeNode()
{
    while (!_children.empty()) {
	delete _children.front();
	_children.pop_front();
    }

    // Detect accidental reuse
    _template_tree_node = reinterpret_cast<TemplateTreeNode *>(0xbad);
    _parent = reinterpret_cast<ConfigTreeNode *>(0xbad);
}

bool
ConfigTreeNode::operator==(const ConfigTreeNode& them) const
{
    return (is_same(them, false));
}

bool
ConfigTreeNode::is_same(const ConfigTreeNode& them, bool ignore_node_id) const
{
    //
    // This comparison only cares about the declarative state of the node,
    // not about the ephemeral state such as when it was modified, deleted,
    // or who changed it last.
    //
    if (_segname != them.segname())
	return false;
    if (_parent != NULL && them.const_parent() != NULL) {
	if (is_tag() != them.is_tag())
	    return false;
	if (_has_value && (_value != them.value())) {
	    return false;
	}
	if (!is_tag() && !them.is_tag()) {
	    if (_operator != them.get_operator()) {
		return false;
	    }
	}
	if ((! ignore_node_id) && (_node_id != them.node_id())) {
	    return false;
	}
    }
    if (is_leaf_value() != them.is_leaf_value())
	return false;
    if (_template_tree_node != them.template_tree_node())
	return false;

    debug_msg("Equal nodes:\n");
    debug_msg("Node1: %s\n", str().c_str());
    debug_msg("Node2: %s\n\n", them.str().c_str());

    return true;
}


void
ConfigTreeNode::add_child(ConfigTreeNode* child)
{
    _children.push_back(child);
    list<ConfigTreeNode *> sorted_children = _children;
    sort_by_template(sorted_children);
    _children = sorted_children;
}

void
ConfigTreeNode::remove_child(ConfigTreeNode* child)
{
    //
    // It's faster to implement this ourselves than use _children.remove()
    // because _children has no duplicates.
    //
    list<ConfigTreeNode*>::iterator iter;
    for (iter = _children.begin(); iter != _children.end(); ++iter) {
	if ((*iter) == child) {
	    _children.erase(iter);
	    return;
	}
    }

    XLOG_UNREACHABLE();
}

void
ConfigTreeNode::add_default_children()
{
    if (_template_tree_node == NULL)
	return;

    // Find all the template nodes for our children
    set<const TemplateTreeNode*> childrens_templates;
    list<ConfigTreeNode *>::const_iterator iter;
    for (iter = _children.begin(); iter != _children.end(); ++iter) {
	childrens_templates.insert((*iter)->template_tree_node());
    }

    //
    // Now go through all the children of our template node, and find
    // any that are leaf nodes that have default values, and that we
    // don't already have an instance of among our children.
    //
    list<TemplateTreeNode*>::const_iterator tci;
    for (tci = _template_tree_node->children().begin();
	 tci != _template_tree_node->children().end();
	 ++tci) {
	TemplateTreeNode* ttn = *tci;
	if (ttn->has_default()) {
	    if (childrens_templates.find(ttn) == childrens_templates.end()) {
		string error_msg;
		string name = ttn->segname();
		string path = _path + " " + name;
		ConfigTreeNode *new_node = create_node(name, path, ttn,
						       this, 
						       ConfigNodeId::ZERO(),
						       _user_id,
						       _clientid,
						       _verbose);
		if (new_node->set_value(ttn->default_str(), _user_id,
					error_msg)
		    != true) {
		    XLOG_FATAL("Cannot set default value: %s",
			       error_msg.c_str());
		}
		if (new_node->set_operator(OP_ASSIGN, _user_id, error_msg)
		    != true) {
		    XLOG_FATAL("Cannot set default operator: %s",
			       error_msg.c_str());
		}
	    }
	}
    }
}

void
ConfigTreeNode::recursive_add_default_children()
{
    list<ConfigTreeNode *>::const_iterator iter;
    for (iter = _children.begin(); iter != _children.end(); ++iter) {
	(*iter)->recursive_add_default_children();
    }

    add_default_children();
}

bool
ConfigTreeNode::check_allowed_value(string& error_msg) const
{
    return (check_allowed_value(_value, error_msg));
}

bool 
ConfigTreeNode::check_allowed_value(const string& value,
				    string& error_msg) const
{
    if (_template_tree_node == NULL)
	return true;		// XXX: the root node has a NULL template

    return (_template_tree_node->check_allowed_value(value, error_msg));
}

bool 
ConfigTreeNode::check_allowed_operator(const string& value,
				       string& error_msg) const
{
    if (_template_tree_node == NULL)
	return true;		// XXX: the root node has a NULL template

    const BaseCommand* base_cmd;
    const AllowOperatorsCommand* cmd;
    base_cmd = _template_tree_node->const_command("%allow-operator");
    cmd = dynamic_cast<const AllowOperatorsCommand *>(base_cmd);
    if (cmd == NULL)
	return true;

    return (cmd->verify_variable_by_value(*this, value, error_msg));
}

bool
ConfigTreeNode::set_value(const string& value, uid_t user_id,
			  string& error_msg)
{
    if (check_allowed_value(value, error_msg) != true)
	return false;

    set_value_without_verification(value, user_id);

    return true;
}

void
ConfigTreeNode::set_value_without_verification(const string& value,
					       uid_t user_id)
{
    _value = value;
    _has_value = true;
    _value_committed = false;
    _user_id = user_id;
    TimerList::system_gettimeofday(&_modification_time);
}

bool
ConfigTreeNode::set_operator(ConfigOperator op, uid_t user_id,
			     string& error_msg)
{
    if (op != OP_NONE) {
	string value = operator_to_str(op);

	if (check_allowed_operator(value, error_msg) != true)
	    return false;
    }

    set_operator_without_verification(op, user_id);

    return true;
}

void
ConfigTreeNode::set_operator_without_verification(ConfigOperator op,
						  uid_t user_id)
{
    if (_operator != op) {
	_operator = op;
	_value_committed = false;
	_user_id = user_id;
	TimerList::system_gettimeofday(&_modification_time);
    }
}

bool
ConfigTreeNode::merge_deltas(uid_t user_id,
			     const ConfigTreeNode& delta_node, 
			     bool provisional_change,
			     bool preserve_node_id,
			     string& error_msg)
{
    XLOG_ASSERT(_segname == delta_node.segname());

    if (_template_tree_node != NULL) {
	XLOG_ASSERT(type() == delta_node.type());

	if (delta_node.is_leaf_value()) {
	    if (_value != delta_node.value() 
		|| _operator != delta_node.get_operator()) {
		_has_value = true;
		if (provisional_change) {
		    _committed_value = _value;
		    _value = delta_node.value();
		    _committed_operator = _operator;
		    _operator = delta_node.get_operator();
		    if (preserve_node_id)
			_node_id = delta_node.node_id();
		    else {
			_node_id = ConfigNodeId::ZERO();
			allocate_unique_node_id();
		    }
		    _committed_user_id = _user_id;
		    _user_id = user_id;
		    _clientid = delta_node.clientid();
		    _committed_modification_time = _modification_time;
		    TimerList::system_gettimeofday(&_modification_time);
		    _value_committed = false;
		} else {
		    _committed_value = delta_node.value();
		    _value = delta_node.value();
		    _committed_operator = delta_node.get_operator();
		    _operator = delta_node.get_operator();
		    if (preserve_node_id)
			_node_id = delta_node.node_id();
		    else
			allocate_unique_node_id();
		    _committed_user_id = delta_node.user_id();
		    _user_id = delta_node.user_id();
		    _clientid = delta_node.clientid();
		    _committed_modification_time =
			delta_node.modification_time();
		    _modification_time = delta_node.modification_time();
		    _value_committed = true;
		}
#if 0
XXXXXXX to be copied to MasterConfigTreeNode
		_actions_pending = 0;
		_actions_succeeded = true;
		_cmd_that_failed = NULL;
#endif
	    }
	}
    }

    list<ConfigTreeNode*>::const_iterator iter;
    for (iter = delta_node.const_children().begin();
	 iter != delta_node.const_children().end();
	 ++iter) {
	ConfigTreeNode *delta_child = *iter;
	
	bool delta_child_done = false;
	list<ConfigTreeNode*>::iterator ci;
	for (ci = _children.begin(); ci != _children.end(); ++ci) {
	    ConfigTreeNode *my_child = *ci;
	    if (my_child->segname() == delta_child->segname()) {
		delta_child_done = true;
		bool success = my_child->merge_deltas(user_id, *delta_child,
						      provisional_change,
						      preserve_node_id,
						      error_msg);
		if (success == false) {
		    // If something failed, abort the merge
		    return false;
		}
		break;
	    }
	}
	if (delta_child_done == false) {
	    ConfigTreeNode* new_node;
	    ConfigNodeId new_node_id = ConfigNodeId::ZERO();
	    if (preserve_node_id)
		new_node_id = delta_child->node_id();
	    new_node = create_node(delta_child->segname(),
				   delta_child->path(),
				   delta_child->template_tree_node(),
				   this,
				   new_node_id,
				   user_id,
				   delta_child->clientid(),
				   _verbose);
	    if (!provisional_change)
		new_node->set_existence_committed(true);
	    new_node->merge_deltas(user_id, *delta_child, provisional_change,
				   preserve_node_id, error_msg);
	}
    }
    return true;
}

bool
ConfigTreeNode::merge_deletions(uid_t user_id,
				const ConfigTreeNode& deletion_node, 
				bool provisional_change,
				string& error_msg)
{
    XLOG_ASSERT(_segname == deletion_node.segname());

    if (_template_tree_node != NULL) {
	XLOG_ASSERT(type() == deletion_node.type());
	if (deletion_node.const_children().empty()) {
	    if (provisional_change) {
		_deleted = true;
		_value_committed = false;
		//
		// XXX: Mark the whole subtree for deletion so later
		// the scanning of the tree will invoke the appropriate
		// method for each node.
		//
		mark_subtree_for_deletion(user_id);
	    } else {
		delete_subtree_silently();
	    }
	    return true;
	}
    }
    
    list<ConfigTreeNode*>::const_iterator iter;
    for (iter = deletion_node.const_children().begin();
	 iter != deletion_node.const_children().end();
	 ++iter) {
	ConfigTreeNode *deletion_child = *iter;

	bool deletion_child_done = false;
	list<ConfigTreeNode*>::iterator ci;
	for (ci = _children.begin(); ci != _children.end(); ++ci) {
	    ConfigTreeNode *my_child = *ci;
	    if (my_child->segname() == deletion_child->segname()) {
		deletion_child_done = true;
		bool success = my_child->merge_deletions(user_id, 
							 *deletion_child,
							 provisional_change,
							 error_msg);
		if (success == false) {
		    // If something failed, abort the merge
		    return false;
		}
		break;
	    }
	}
	if (deletion_child_done == false) {
	    error_msg = c_format("Failed to delete node:\n"
				 "   %s\n"
				 "Node does not exist.\n",
				 deletion_node.path().c_str());
	    return false;
	}
    }
    return true;
}


ConfigTreeNode*
ConfigTreeNode::find_config_module(const string& module_name)
{
    if (module_name.empty())
	return (NULL);

    // Check if this node matches
    if (_template_tree_node != NULL) {
	if (_template_tree_node->module_name() == module_name)
	    return (this);
    }

    // Check all children nodes
    list<ConfigTreeNode*>::const_iterator iter;
    for (iter = _children.begin(); iter != _children.end(); ++iter) {
	ConfigTreeNode* ctn = (*iter)->find_config_module(module_name);
	if (ctn != NULL)
	    return (ctn);
    }

    return (NULL);
}

bool
ConfigTreeNode::check_config_tree(string& error_msg) const
{
    list<ConfigTreeNode *>::const_iterator iter;

    //
    // Check that the node is not deprecated or read-only
    //
    if (_template_tree_node != NULL) {
	if (_template_tree_node->is_deprecated()) {
	    error_msg = c_format("Node \"%s\" is deprecated: %s\n",
				 _path.c_str(),
				 _template_tree_node->deprecated_reason().c_str());
	    return false;
	}
	if (_template_tree_node->is_read_only()
	    && (is_leaf_value())
	    && (! is_default_value())) {
	    string reason = read_only_reason();
	    error_msg = c_format("Node \"%s\" is read-only", _path.c_str());
	    if (! reason.empty())
		error_msg += c_format(": %s", reason.c_str());
	    error_msg += "\n";
	    return false;
	}
    }
    //
    // An early check that the child nodes are not deprecated or read-only.
    // Note: we need to check the child nodes before the check for
    // mandatory configuration nodes so we will print a better error
    // message if, say, a mandatory node has been renamed and the old
    // one has been deprecated.
    //
    for (iter = _children.begin(); iter != _children.end(); ++iter) {
	const TemplateTreeNode* ttn = (*iter)->template_tree_node();
	if (ttn == NULL)
	    continue;
	if (ttn->is_deprecated()) {
	    error_msg = c_format("Node \"%s\" is deprecated: %s\n",
				 (*iter)->path().c_str(),
				 ttn->deprecated_reason().c_str());
	    return false;
	}
	if (ttn->is_read_only()
	    && (is_leaf_value())
	    && (! is_default_value())) {
	    string reason = ttn->read_only_reason();
	    error_msg = c_format("Node \"%s\" is read-only", _path.c_str());
	    if (! reason.empty())
		error_msg += c_format(": %s", reason.c_str());
	    error_msg += "\n";
	    return false;
	}
    }

    //
    // Check that all mandatory configuration nodes are configured in place
    //
    if ((! deleted()) && (_template_tree_node != NULL)) {
	list<string>::const_iterator li;
	for (li = _template_tree_node->mandatory_config_nodes().begin();
	     li != _template_tree_node->mandatory_config_nodes().end();
	     ++li) {
	    const string& mandatory_config_node = *li;
	    string value;
	    if (expand_variable(mandatory_config_node, value, true) != true) {
		error_msg = c_format("Missing mandatory configuration node "
				     "\"%s\" required by node \"%s\"\n",
				     mandatory_config_node.c_str(),
				     _path.c_str());
		return false;
	    }
	}
    }

    //
    // Verify the allowed configuration values
    //
    if (_template_tree_node != NULL) {
	if (has_value()) {
	    if (_template_tree_node->check_allowed_value(value(), error_msg)
		!= true) {
		return false;
	    }
	}
	if (_template_tree_node->verify_variables(*this, error_msg) != true)
	    return false;
    }

    //
    // Recursively check all child nodes
    //
    for (iter = _children.begin(); iter != _children.end(); ++iter) {
	ConfigTreeNode* ctn = *iter;
	if (ctn->check_config_tree(error_msg) != true)
	    return false;
    }

    return true;
}


string
ConfigTreeNode::discard_changes(int depth, int last_depth)
{
    string result;
    bool changes_made = false;

    debug_msg("DISCARD CHANGES node >%s<\n", _path.c_str());

    // The root node has a NULL template
    if (_template_tree_node != NULL) {
	if (_existence_committed == false) {
	    bool show_top = (depth > 0);
	    result = show_subtree(show_top, depth, /* XXX */ depth * 2, true, 
				  /* numbered */ false, false, false);
	    delete_subtree_silently();
	    return result;
	} else if (_value_committed == false) {
	    debug_msg("discarding changes from node %s\n",
		      _path.c_str());
	    _value_committed = true;
	    _deleted = false;
	    _value = _committed_value;
	    _operator = _committed_operator;
	    _user_id = _committed_user_id;
	    _modification_time = _committed_modification_time;
	    result = node_str();
	    if (is_leaf_value()) 
		result += "\n";
	    else
		result += " {\n";
	    changes_made = true;
	} 
    }

    list<ConfigTreeNode *>::iterator iter, prev_iter;
    iter = _children.begin();
    if (changes_made)
	last_depth = depth;
    while (iter != _children.end()) {
	// Be careful, or discard_changes can invalidate the iterator
	prev_iter = iter;
	++iter;
	debug_msg("  child: %s\n", (*prev_iter)->path().c_str());
	result += (*prev_iter)->discard_changes(depth + 1, last_depth);
    }

    return result;
}

int
ConfigTreeNode::type() const
{
    return _template_tree_node->type();
}

bool
ConfigTreeNode::is_tag() const
{
    //
    // Only the root node does not have a template tree node, and it is
    //
    if (_template_tree_node == NULL)
	return false;

    return _template_tree_node->is_tag();
}

bool
ConfigTreeNode::is_multi_value_node() const
{
    if (_parent == NULL)
	return false;
    return (_parent->is_tag());
}

bool
ConfigTreeNode::is_leaf_value() const
{
    if (_has_value)
	return true;
    if (_template_tree_node == NULL)
	return false;
    if (_template_tree_node->is_leaf_value())
	return true;
    return false;
}

bool
ConfigTreeNode::is_read_only() const
{
    if (_template_tree_node == NULL)
	return false;
    return (_template_tree_node->is_read_only());
}

const string&
ConfigTreeNode::read_only_reason() const
{
    if (_template_tree_node == NULL) {
	static const string empty_reason;
	return empty_reason;
    }
    return (_template_tree_node->read_only_reason());
}

bool
ConfigTreeNode::is_permanent() const
{
    if (_template_tree_node == NULL)
	return false;
    return (_template_tree_node->is_permanent());
}

const string&
ConfigTreeNode::permanent_reason() const
{
    if (_template_tree_node == NULL) {
	static const string empty_reason;
	return empty_reason;
    }
    return (_template_tree_node->permanent_reason());
}

bool
ConfigTreeNode::is_user_hidden() const
{
    if (_template_tree_node == NULL)
	return false;
    return (_template_tree_node->is_user_hidden());
}

const string&
ConfigTreeNode::user_hidden_reason() const
{
    if (_template_tree_node == NULL) {
	static const string empty_reason;
	return empty_reason;
    }
    return (_template_tree_node->user_hidden_reason());
}

unsigned int
ConfigTreeNode::depth() const
{
    if (is_root_node())
	return 0;
    else
	return (1 + _parent->depth());
}

const string&
ConfigTreeNode::value() const
{
    if (_has_value)
	return _value;

    if (is_tag()) {
	// We should never ask a tag for its value
	XLOG_UNREACHABLE();
    }

    return _segname;
}

ConfigOperator
ConfigTreeNode::get_operator() const
{
    if (is_tag()) {
	// We should never ask a tag for its operator
	XLOG_UNREACHABLE();
    }

    return _operator;
}

void
ConfigTreeNode::undelete_node_and_ancestors()
{
    ConfigTreeNode* ctn;

    //
    // Undelete this node and all its ancestors
    //
    for (ctn = this; ctn != NULL; ctn = ctn->parent()) {
	ctn->undelete();
    }
}

void
ConfigTreeNode::undelete_subtree()
{
    list<ConfigTreeNode*>::iterator iter;

    undelete();

    for (iter = _children.begin(); iter != _children.end(); ++iter) {
	ConfigTreeNode* ctn = *iter;
	ctn->undelete_subtree();
    }
}

string
ConfigTreeNode::show_subtree(bool show_top, int depth, int indent,
			     bool do_indent, bool numbered, bool annotate,
			     bool suppress_default_values) const
{
    string s;
    string my_in;
    bool is_a_tag = false;
    int new_indent;

    if (suppress_default_values && is_default_value())
	return string("");

    if (_template_tree_node != NULL) {
	// XXX: ignore deprecated subtrees
	if (_template_tree_node->is_deprecated())
	    return string("");
	// XXX: ignore user-hidden subtrees
	if (_template_tree_node->is_user_hidden())
	    return string("");
    }

    if (_template_tree_node != NULL)
	is_a_tag = is_tag();

    for (int i = 0; i < indent; i++) 
	my_in += "  ";

    if (is_a_tag && show_top) {
	new_indent = indent;
	list<ConfigTreeNode*>::const_iterator iter;
	for (iter = _children.begin(); iter != _children.end(); ++iter) {
	    const ConfigTreeNode* child_ctn = *iter;

	    if (suppress_default_values && child_ctn->is_default_value()) {
		//
		// Skip children with default values if we don't care about
		// them.
		//
		continue;
	    }

	    //
	    // Add the annotation prefix
	    //
	    if (annotate) {
		do {
		    if (child_ctn->deleted()) {
			s += "-   ";
			break;
		    }
		    if (! child_ctn->existence_committed()) {
			s += ">   ";
			break;
		    }
		    s += "    ";
		    break;
		} while (false);
	    }
	    s += my_in + show_node_id(numbered, child_ctn->node_id()) 
		+ _segname + " " +
		child_ctn->show_subtree(show_top, depth, indent, false, 
					numbered, annotate,
					suppress_default_values);
	}
    } else if (is_a_tag && (show_top == false)) {
	s = "ERROR";
    } else if ((is_a_tag == false) && show_top) {
	string s2;

	new_indent = indent + 2;

	//
	// Add the annotation prefix
	//
	if (annotate) {
	    do {
		if (deleted()) {
		    s2 = "-   ";
		    break;
		}
		if (is_uncommitted()) {
		    s2 = ">   ";
		    break;
		}
		s2 = "    ";
		break;
	    } while (false);
	}

	if (do_indent) {
	    s = s2 + my_in + show_node_id(numbered, node_id()) + _segname;
	} else {
	    // no need to show the node ID here
	    XLOG_ASSERT(_parent->is_tag());
	    if ((type() == NODE_TEXT) && is_quotable_string(_segname)) {
		s += quoted_value(_segname);
	    } else {
		s += _segname; 
	    }
	}
	if ((type() != NODE_VOID) && (_has_value)) {
	    string value;
	    if ((type() == NODE_TEXT) /* && annotate */ ) {
		value = quoted_value(_value);
	    } else {
		value = _value;
	    }
	    if (_parent != NULL && _parent->is_tag()) {
		s += " " + value;
	    } else {
		if (_operator == OP_NONE) {
		    // no-op
		} else if (_operator == OP_ASSIGN) {
		    s += show_operator() + " " + value;
		} else {
		    s += " " + show_operator() + " " + value;
		}
	    }
	}
	if (_children.empty() && (type() != NODE_VOID || _has_value)) {
	    //
	    // Normally if a node has no children, we don't want the
	    // braces, but certain grouping nodes, with type NODE_VOID
	    // (e.g. "protocols") won't parse correctly if we print
	    // them without the braces.
	    //
	    s += "\n";
	    return s;
	}
	s += " {\n";
	list<ConfigTreeNode*>::const_iterator iter;
	for (iter = _children.begin(); iter != _children.end(); ++iter) {
	    s += (*iter)->show_subtree(true, depth + 1, new_indent, true, 
				       numbered, annotate,
				       suppress_default_values);
	}
	s += s2 + my_in + "}\n";
    } else if ((is_a_tag == false) && (show_top == false)) {
	new_indent = indent;
	list<ConfigTreeNode*>::const_iterator iter;
	for (iter = _children.begin(); iter != _children.end(); ++iter) {
	    s += (*iter)->show_subtree(true, depth + 1, new_indent, true, 
				       numbered, annotate,
				       suppress_default_values);
	}
    }
    return s;
}

void
ConfigTreeNode::mark_subtree_as_committed()
{
    _existence_committed = true;
    _value_committed = true;
    _deleted = false;
    _committed_value = _value;
    _committed_operator = _operator;
    _committed_user_id = _user_id;
    _committed_modification_time = _modification_time;

    list<ConfigTreeNode*>::iterator iter;
    iter = _children.begin();
    while (iter != _children.end()) {
	(*iter)->mark_subtree_as_committed();
	++iter;
    }
}

void
ConfigTreeNode::mark_subtree_as_uncommitted()
{
    _existence_committed = false;
    _value_committed = false;

    list<ConfigTreeNode*>::iterator iter;
    iter = _children.begin();
    while (iter != _children.end()) {
	(*iter)->mark_subtree_as_uncommitted();
	++iter;
    }
}

void
ConfigTreeNode::mark_subtree_for_deletion(uid_t user_id)
{
    // We delete all the children of this node, then delete the node itself
    debug_msg("Mark subtree for deletion: %s\n", _segname.c_str());

    if (_existence_committed == false) {
	// this node was never committed
	delete_subtree_silently();
	return;
    }

    //
    // Delete_subtree calls remove_child, so we just iterate until no
    // children are left.
    //
    debug_msg("Node has %u children\n", XORP_UINT_CAST(_children.size()));
    list<ConfigTreeNode*>::iterator iter, prev_iter;
    iter = _children.begin();
    while (iter != _children.end()) {
	//
	// Be careful to avoid the iterator becoming invalid when
	// calling mark_subtree_for_deletion on an uncommitted node.
	//
	prev_iter = iter;
	++iter;
	(*prev_iter)->mark_subtree_for_deletion(user_id);
    }

    _user_id = user_id;
    TimerList::system_gettimeofday(&_modification_time);
    _deleted = true;
    _value_committed = false;
#if 0
XXXXX to be copied to MasterConfigTreeNode??
    _actions_succeeded = true;
#endif
}

void
ConfigTreeNode::delete_subtree_silently()
{
    //
    // Delete_subtree calls remove_child, so we just iterate until no
    // children are left.
    //
    while (!_children.empty()) {
	_children.front()->delete_subtree_silently();
    }

    if (_parent != NULL)
	_parent->remove_child(this);

    if (! is_root_node())
	delete this;
}

void
ConfigTreeNode::clone_subtree(const ConfigTreeNode& orig_node)
{
    list<ConfigTreeNode*>::const_iterator iter;
    for (iter = orig_node.const_children().begin();
	 iter != orig_node.const_children().end();
	 ++iter) {
	ConfigTreeNode* new_node = create_node(**iter);
	new_node->set_parent(this);
	add_child(new_node);
	new_node->clone_subtree(**iter);
    }
}

string
ConfigTreeNode::typestr() const
{
    return _template_tree_node->typestr();
}

string
ConfigTreeNode::node_str() const
{
    string tmp;

    if (_template_tree_node == NULL) {
	tmp = "ROOT";
    } else {
	tmp = _path + " " + typestr();
	if (!_value.empty()) {
	    tmp += " = " + _value;
	}
    }
    return tmp;
}

//
// TODO: this method is not used. What is the difference between
// str() and node_str() ??
// 
string
ConfigTreeNode::str() const
{
    string s = _path;

    if (_has_value) {
	s+= ": " + _value;
    }
    return s;
}

string
ConfigTreeNode::subtree_str() const
{
    string s;

    s = c_format("%s\n", node_str().c_str());

    list<ConfigTreeNode*>::const_iterator iter;
    for (iter = _children.begin(); iter != _children.end(); ++iter) {
	s += (*iter)->subtree_str();
    }

    return s;
}

const ConfigTreeNode*
ConfigTreeNode::find_const_node(const list<string>& path) const
{
    //
    // We need both const and non-const versions of find_node,
    // but don't want to write it all twice.
    //
    return (const_cast<ConfigTreeNode*>(this))->find_node(path);
}

ConfigTreeNode*
ConfigTreeNode::find_node(const list<string>& path)
{
    list<string> path_copy = path;

    // Are we looking for the root node?
    if (path_copy.empty())
	return this;

    if (_template_tree_node != NULL) {
	// XXX: not a root node
	if (path_copy.front() == _segname) {
	    if (path_copy.size() == 1) {
		return this;
	    } else {
		path_copy.pop_front();
	    }
	} else {
	    // We must have screwed up
	    XLOG_UNREACHABLE();
	}
    }

    list<ConfigTreeNode *>::const_iterator iter;
    for (iter = _children.begin(); iter != _children.end(); ++iter) {
	if ((*iter)->segname() == path_copy.front()) {
	    return ((*iter)->find_node(path_copy));
	}
    }

    // None of the nodes match the path
    return NULL;
}


bool
ConfigTreeNode::retain_different_nodes(const ConfigTreeNode& them,
				       bool retain_value_changed)
{
    list<ConfigTreeNode*>::iterator my_iter;
    list<ConfigTreeNode*>::const_iterator their_iter;
    bool retained_children = false;

    XLOG_ASSERT(_segname == them.segname());

    for (my_iter = _children.begin(); my_iter != _children.end(); ) {
	bool retain_child = true;
	ConfigTreeNode *my_child = *my_iter;

	// Be careful not to invalidate the iterator when we remove children
	++my_iter;

	for (their_iter = them.const_children().begin();
	     their_iter != them.const_children().end();
	     ++their_iter) {
	    ConfigTreeNode* their_child = *their_iter;

	    if (their_child->deleted())
		continue;	// XXX: ignore deleted nodes

	    // Are the nodes the same?
	    if (my_child->is_same(*their_child, true)) {
		if (!my_child->retain_different_nodes(*their_child,
						      retain_value_changed)) {
		    retain_child = false;
		}
		break;
	    }
	    // Are the nodes the same leaf node, but with a changed value
	    if (!retain_value_changed
		&& my_child->is_leaf_value() && their_child->is_leaf_value()
		&& (my_child->segname() == their_child->segname())) {
		retain_child = false;
		break;
	    }
	}

	if (retain_child == false) {
	    my_child->delete_subtree_silently();
	} else {
	    retained_children = true;
	}
    }
    return retained_children;
}

bool
ConfigTreeNode::retain_deletion_nodes(const ConfigTreeNode& them,
				      bool retain_value_changed)
{
    list<ConfigTreeNode*>::iterator my_iter;
    list<ConfigTreeNode*>::const_iterator their_iter;
    bool found_deletion_children = false;

    XLOG_ASSERT(_segname == them.segname());

    for (my_iter = _children.begin(); my_iter != _children.end(); ) {
	bool retain_child = true;
	bool node_found = false;
	bool found_deletion_child = false;
	ConfigTreeNode *my_child = *my_iter;

	// Be careful not to invalidate the iterator when we remove children
	++my_iter;

	for (their_iter = them.const_children().begin();
	     their_iter != them.const_children().end();
	     ++their_iter) {
	    ConfigTreeNode* their_child = *their_iter;

	    if (their_child->deleted())
		continue;	// XXX: ignore deleted nodes

	    // Are the nodes the same?
	    if (my_child->is_same(*their_child, true)) {
		if (my_child->retain_deletion_nodes(*their_child,
						    retain_value_changed)) {
		    found_deletion_child = true;
		}
		node_found = true;
		break;
	    }
	    // Are the nodes the same leaf node, but with a changed value
	    if (!retain_value_changed
		&& my_child->is_leaf_value() && their_child->is_leaf_value()
		&& (my_child->segname() == their_child->segname())) {
		// retain_child = false;
		node_found = true;
		break;
	    }
	}

	//
	// Remove the children of my_child
	//
	if (! node_found) {
	    for (their_iter = my_child->children().begin();
		 their_iter != my_child->children().end(); ) {
		ConfigTreeNode* del_node = *their_iter;
		++their_iter;
		del_node->delete_subtree_silently();
		found_deletion_child = true;
	    }
	}

	if (retain_child == false) {
	    my_child->delete_subtree_silently();
	    found_deletion_child = true;
	}
	if ((! found_deletion_child)
	    && node_found
	    && (! my_child->has_undeleted_children())) {
	    // XXX: common child among both trees, hence just prune it
	    my_child->delete_subtree_silently();
	}

	if (found_deletion_child)
	    found_deletion_children = true;
    }

    return (found_deletion_children);
}

void
ConfigTreeNode::retain_common_nodes(const ConfigTreeNode& them)
{
    list<ConfigTreeNode*>::iterator my_iter;
    list<ConfigTreeNode*>::const_iterator their_iter;
    bool retained_children = false;

    XLOG_ASSERT(_segname == them.segname());

    for (my_iter = _children.begin(); my_iter != _children.end(); ) {
	bool retain_child = false;
	ConfigTreeNode *my_child = *my_iter;

	// Be careful not to invalidate the iterator when we remove children
	++my_iter;

	for (their_iter = them.const_children().begin();
	     their_iter != them.const_children().end();
	     ++their_iter) {
	    ConfigTreeNode* their_child = *their_iter;
	    if ((*my_child) == (*their_child)) {
		my_child->retain_common_nodes(*their_child);
		retain_child = true;
		retained_children = true;
		break;
	    }
	}
	if (retain_child == false) {
	    my_child->delete_subtree_silently();
	}
    }
    if (_parent != NULL && retained_children == false && is_tag()) {
	delete_subtree_silently();
	return;
    }
}


bool
ConfigTreeNode::expand_variable(const string& varname, string& value,
				bool ignore_deleted_nodes) const
{

    VarType type = NONE;
    const ConfigTreeNode *varname_node;

    debug_msg("ConfigTreeNode::expand_variable at %s: >%s<\n",
	      _segname.c_str(), varname.c_str());

    varname_node = find_const_varname_node(varname, type);

    if (ignore_deleted_nodes && (varname_node != NULL)) {
	if (varname_node->deleted())
	    return false;
    }

    switch (type) {
    case NONE:
	return false;
    case NODE_VALUE:
	XLOG_ASSERT(varname_node != NULL);
	value = varname_node->value();
	return true;
    case NODE_OPERATOR:
	XLOG_ASSERT(varname_node != NULL);
	value = varname_node->show_operator();
	debug_msg("variable %s at %s, value is \"%s\"\n",
		  varname.c_str(), _segname.c_str(), value.c_str());
	return true;
    case NODE_ID:
	XLOG_ASSERT(varname_node != NULL);
	value = varname_node->node_id().str();
	debug_msg("variable %s at %s, value is \"%s\"\n",
		  varname.c_str(), _segname.c_str(), value.c_str());
	return true;
    case NAMED:
    {
	list<string> var_parts;

	XLOG_ASSERT(varname_node != NULL);
	if (split_up_varname(varname, var_parts) == false) {
	    return false;
	}
	value = varname_node->named_value(var_parts.back());
	return true;
    }
    case TEMPLATE_DEFAULT:
    {
	const TemplateTreeNode* ttn;
	ttn = _template_tree_node->find_const_varname_node(varname);
	XLOG_ASSERT(ttn != NULL);
	XLOG_ASSERT(ttn->has_default());
	value = ttn->default_str();
	return true;
    }
    }
    XLOG_UNREACHABLE();
}

bool
ConfigTreeNode::expand_variable_to_full_varname(const string& varname,
						string& full_varname) const
{

    VarType type = NONE;
    const ConfigTreeNode *varname_node;

    debug_msg("ConfigTreeNode::expand_variable_to_full_varname at %s: >%s<\n",
	      _segname.c_str(), varname.c_str());

    varname_node = find_const_varname_node(varname, type);

    switch (type) {
    case NONE:
	return false;
    case NODE_VALUE:
    case NODE_OPERATOR:
    case NODE_ID:
	XLOG_ASSERT(varname_node != NULL);
	full_varname = varname_node->path();
	return true;
    case NAMED:
    {
	list<string> var_parts;

	XLOG_ASSERT(varname_node != NULL);
	if (split_up_varname(varname, var_parts) == false) {
	    return false;
	}
	full_varname = varname_node->path() + " " + var_parts.back();
	return true;
    }
    case TEMPLATE_DEFAULT:
    {
	const TemplateTreeNode* ttn;
	ttn = _template_tree_node->find_const_varname_node(varname);
	XLOG_ASSERT(ttn != NULL);
	full_varname = ttn->path();
	return true;
    }
    }
    XLOG_UNREACHABLE();
}

bool
ConfigTreeNode::expand_expression(const string& expression,
				  string& value) const
{
    // Expect string of form: "`" opchar + name + "`"
    // and the only definition of opchar supported is "~".
    //
    if (expression.size() < 4) {
	return false;
    }
    if (expression[0] != '`' ||
	expression[expression.size() - 1] != '`') {
	return false;
    }

    // Trim the back-quotes
    string tmp_expr = expression.substr(1, expression.size() - 2);

    //
    // XXX: for now the only expression we can expand is the "~" boolean
    // negation operator.
    //
    if (tmp_expr[0] != '~')
	return false;

    // Trim the operator
    tmp_expr = tmp_expr.substr(1, expression.size() - 1);

    // Expand the variable
    string tmp_value;
    if (expand_variable(tmp_expr, tmp_value, true) != true)
	return false;

    if (tmp_value == "false")
	value = "true";
    else if (tmp_value == "true")
	value = "false";
    else
	return false;

    return true;
}

const ConfigTreeNode*
ConfigTreeNode::find_const_varname_node(const string& varname,
					VarType& type) const
{
    //
    // We need both const and non-const versions of find_varname_node,
    // but don't want to write it all twice.
    //
    return (const_cast<ConfigTreeNode*>(this))
	->find_varname_node(varname, type);
}

ConfigTreeNode*
ConfigTreeNode::find_varname_node(const string& varname, VarType& type)
{
    debug_msg("ConfigTreeNode::find_varname_node at %s: >%s<\n",
	      _segname.c_str(), varname.c_str());

    if (varname == "$(@)" || (varname == "$(" + _segname + ")") ) {
	XLOG_ASSERT(!_template_tree_node->is_tag());
	type = NODE_VALUE;
	debug_msg("varname node is >%s<\n", _segname.c_str());
	return this;
    }

    if (varname == "$(<>)") {
	XLOG_ASSERT(!_template_tree_node->is_tag());
	type = NODE_OPERATOR;
	debug_msg("varname node is >%s<\n", _segname.c_str());
	return this;
    }

    if (varname == "$(#)") {
	XLOG_ASSERT(!_template_tree_node->is_tag());
	type = NODE_ID;
	debug_msg("varname node is >%s<\n", _segname.c_str());
	return this;
    }

    list<string> var_parts;
    if (split_up_varname(varname, var_parts) == false) {
	return NULL;
    }

    if (var_parts.back() == "DEFAULT") {
	// XXX: use the template tree to get the default value
	const TemplateTreeNode* ttn = NULL;
	if ((var_parts.size() > 1) && (_template_tree_node != NULL)) {
	    ttn = _template_tree_node->find_const_varname_node(varname);
	} else {
	    ttn = _template_tree_node;
	}
	if ((ttn != NULL) && ttn->has_default()) {
	    type = TEMPLATE_DEFAULT;
	    return NULL;
	}
	type = NONE;
	return NULL;
    }

    ConfigTreeNode* found_node = NULL;
    if ((var_parts.front() == "@") || (_parent == NULL)) {
	_on_parent_path = true;
	found_node = find_child_varname_node(var_parts, type);
	_on_parent_path = false;
    } else if (var_parts.size() > 1) {
	// It's a parent node, or a child of a parent node
	found_node = find_parent_varname_node(var_parts, type);
    }
    if (found_node != NULL)
	return (found_node);

    // Test if we can match a template node
    if (_template_tree_node != NULL) {
	const TemplateTreeNode* ttn;
	ttn = _template_tree_node->find_const_varname_node(varname);
	if ((ttn != NULL) && ttn->has_default()) {
	    type = TEMPLATE_DEFAULT;
	    return NULL;
	}
    }

    //
    // XXX: if not referring to this node, then size of 0 or 1
    // is not valid syntax.
    //
    type = NONE;
    return NULL;
}

ConfigTreeNode*
ConfigTreeNode::find_parent_varname_node(const list<string>& var_parts, 
					 VarType& type)
{
    debug_msg("find parent at node %s\n", _segname.c_str());

    if (_parent == NULL) {
	debug_msg("reached root node\n");
	type = NONE;
	return NULL;
    }

    if (is_tag() || (this->type() == NODE_VOID)) {
	// When naming a parent node variable, you must start with a tag
	if (_segname == var_parts.front()) {
	    // We've found the right place to start
	    return find_child_varname_node(var_parts, type);
	}
    }
    _on_parent_path = true;
    ConfigTreeNode *parent, *found;
    parent = (ConfigTreeNode*)_parent;
    found = parent->find_parent_varname_node(var_parts, type);
    _on_parent_path = false;
    return found;
}

ConfigTreeNode*
ConfigTreeNode::find_child_varname_node(const list<string>& var_parts, 
					VarType& type)
{
    string s;

    s = "find child at node " + _segname + " varname: ";

    list<string>::const_iterator iter;
    for (iter = var_parts.begin(); iter != var_parts.end(); ++iter) {
	s += ">" + (*iter) + "< ";
    }
    s += "\n";
    debug_msg("%s", s.c_str());

    //
    // @ can only expand when we've already gone through this node
    // heading towards the parent.
    //
    if ((var_parts.front() == "@") && _on_parent_path == false) {
	type = NONE;
	debug_msg("no on parent path\n");
	return NULL;
    }

    if (_parent == NULL) {
	// We have reached the root node
	// The name should refer to a child of ours
	ConfigTreeNode *found_child, *child;
	list<ConfigTreeNode *>::iterator ci;
	for (ci = _children.begin(); ci != _children.end(); ++ci) {
	    child = (ConfigTreeNode*)(*ci);
	    found_child = child->find_child_varname_node(var_parts, type);
	    if (found_child != NULL)
		return found_child;
	}
	type = NONE;
	return NULL;
    }

    if ((var_parts.front() != "@")
	&& (var_parts.front() != _segname) 
	&& (var_parts.front() != "<>") 
	&& (var_parts.front() != "#") 
	&& ((!_has_value) || (var_parts.front() != _value))) {
	// varname doesn't match us.
	type = NONE;
	debug_msg("varname doesn't match\n");
	return NULL;
    }

    // The name might refer to this node
    if (var_parts.size() == 1) {
	if ((var_parts.front() == "@") || (var_parts.front() == _segname)) {
	    type = NODE_VALUE;
	    debug_msg("varname V node is >%s<\n", _segname.c_str());
	    return this;
	}
    }

    // The name might refer to this node for an operator
    if (var_parts.size() == 1) {
	if ((var_parts.front() == "<>")) {
	    type = NODE_OPERATOR;
	    debug_msg("varname O node is >%s<\n", _segname.c_str());
	    return this;
	}
    }

    // The name might refer to this node for a node ID
    if (var_parts.size() == 1) {
	if ((var_parts.front() == "#")) {
	    type = NODE_ID;
	    debug_msg("varname # node is >%s<\n", _segname.c_str());
	    return this;
	}
    }

    if (var_parts.size() == 2) {
	// The name might refer to a named variable on this node
	if (_variables.find(var_parts.back()) != _variables.end()) {
	    type = NAMED;
	    debug_msg("varname N node is >%s<\n", _segname.c_str());
	    return this;
	}
    }

    // The name might refer to a child of ours
    list<string> child_var_parts = var_parts;
    child_var_parts.pop_front();
    ConfigTreeNode *found_child, *child;
    list<ConfigTreeNode *>::iterator ci;
    for (ci = _children.begin(); ci != _children.end(); ++ci) {
	child = (ConfigTreeNode*)(*ci);
	found_child = child->find_child_varname_node(child_var_parts, type);
	if (found_child != NULL)
	    return found_child;
    }

    type = NONE;
    return NULL;
}

bool
ConfigTreeNode::split_up_varname(const string& varname, 
				       list<string>& var_parts) const
{
    debug_msg("split up varname >%s<\n", varname.c_str());

    if (varname.size() < 4 || varname[0] != '$'
	|| varname[1] != '(' || varname[varname.size() - 1] != ')') {
	XLOG_ERROR("Bad variable name: %s", varname.c_str());
	return false;
    }

    string trimmed = varname.substr(2, varname.size() - 3);
    var_parts = split(trimmed, '.');

    if (_verbose) {
	string debug_output;
	list<string>::iterator iter;
	for (iter = var_parts.begin(); iter != var_parts.end(); ++iter) {
	    debug_output += c_format(">%s<, ", iter->c_str());
	}
	debug_output += "\n";
	debug_msg("%s", debug_output.c_str());
    }
    return true;
}

string
ConfigTreeNode::join_up_varname(const list<string>& var_parts) const
{
    string s;

    s = "$(";
    list<string>::const_iterator iter;
    for (iter = var_parts.begin(); iter != var_parts.end(); ++iter) {
	if (iter != var_parts.begin()) {
	    s += ".";
	}
	s += *iter;
    }
    s += ")";
    return s;
}

void
ConfigTreeNode::expand_varname_to_matchlist(const vector<string>& parts,
						  size_t part,
						  list<string>& matches) const
{
    bool ok = false;

    // Don't expand unless the node has been committed
    if (! _existence_committed)
	return;

    if (is_root_node()) {
	// The root node
	ok = true;
	//
	// XXX: note that even if "part" is 0, this is compensated later by
	// the usage of "part + 1" later.
	//
	part--;		// Prevent increment of part later
    } else if ((!_parent->is_root_node()) && _parent->is_tag()) {
	// We're the varname after a tag
	if (parts[part] == "*") {
	    ok = true;
	} else {
	}
    } else {
	if (parts[part] != _segname) {
	    return;
	}
	ok = true;
    }

    if (! ok)
	return;

    if (part == parts.size() - 1) {
	// Everything that we were looking for matched
	XLOG_ASSERT(parts[part] == "*");
	matches.push_back(_segname);
	return;
    } 

    //
    // Search the children. If no more children, return the result so far
    //
    list<ConfigTreeNode*>::const_iterator iter;
    for (iter = _children.begin(); iter != _children.end(); ++iter) {
	ConfigTreeNode *child = (ConfigTreeNode*)(*iter);
	child->expand_varname_to_matchlist(parts, part + 1, matches);
    }
}

bool
ConfigTreeNode::set_variable(const string& varname, const string& value)
{
    //
    // The variable name should be in the form "nodename.varname".
    // Nodename itself can be complex, such as "vif.@" to indicate the
    // current vif.  The general idea is that we're setting a variable
    // on a config tree node. However the config tree node that we're
    // setting the variable on doesn't have to be "this" node - this
    // node only scopes the variable name.
    //

    // First search for an existing variable to set
    VarType type;
    ConfigTreeNode* node = find_varname_node(varname, type);
    string errmsg;
    if (node != NULL) {
	switch (type) {
	case NONE:
	    // This can't happen
	    XLOG_UNREACHABLE();
	    break;
	case NODE_VALUE: 
	    if (node->is_read_only()) {
		string reason = node->read_only_reason();
		errmsg = c_format("Attempt to set node \"%s\" "
				  "which is read-only",
				  varname.c_str());
		if (! reason.empty())
		    errmsg += c_format(": %s", reason.c_str());
		XLOG_ERROR("%s", errmsg.c_str());
		return false;
	    }
	    if (node->set_value(value, _user_id, errmsg) != true) {
		errmsg = c_format("Error setting the value of node \"%s\": %s",
				  varname.c_str(), errmsg.c_str());
		XLOG_ERROR("%s", errmsg.c_str());
		return false;
	    }
	    return true;
	case NODE_OPERATOR: 
	    errmsg = c_format("Attempt to set variable operator \"%s\" "
			      "which is the name of a configuration node",
			      varname.c_str());
	    XLOG_ERROR("%s", errmsg.c_str());
	    return false;
	case NODE_ID: 
	    errmsg = c_format("Attempt to set variable node ID \"%s\" "
			      "which is not user-settable",
			      varname.c_str());
	    XLOG_ERROR("%s", errmsg.c_str());
	    return false;
	case NAMED:
	{
	    list<string> var_parts;

	    if (split_up_varname(varname, var_parts) == false) {
		return false;
	    }
	    node->set_named_value(var_parts.back(), value);
	    return true;
	}
	case TEMPLATE_DEFAULT: 
	    // XXX: Ignore, and attempt to set a named variable below.
	    break;
	}
    }

    //
    // We found no existing variable, see if there's a node on which we
    // can set a named variable.
    //
    list<string> var_parts;
    if (split_up_varname(varname, var_parts) == false) {
	return false;
    }
    list<string>::iterator iter;
    for (iter = var_parts.begin(); iter != var_parts.end(); ++iter) {
	debug_msg("VP: %s\n", iter->c_str());
    }
    if (var_parts.size() < 2) {
	errmsg = c_format("Attempt to set incomplete variable \"%s\"",
			  varname.c_str());
	XLOG_ERROR("%s", errmsg.c_str());
	return false;
    }
    var_parts.pop_back();
    if ((var_parts.front() == "@") || (_parent == NULL)) {
        _on_parent_path = true;
        node = find_child_varname_node(var_parts, type);
        _on_parent_path = false;
    } else {
        // It's a parent node, or a child of a parent node
        node = find_parent_varname_node(var_parts, type);
    }
    if (node != NULL) {
	switch (type) {
	case NONE:
	    // This can't happen
	    XLOG_UNREACHABLE();
	    break;
	case NODE_VALUE:
	    if (split_up_varname(varname, var_parts) == false) {
		return false;
	    }
	    node->set_named_value(var_parts.back(), value);
	    return true;
	case NODE_OPERATOR:
	    errmsg = c_format("Attempt to set operator \"%s\" "
			      "which is the child of a named variable",
			      varname.c_str());
	    XLOG_ERROR("%s", errmsg.c_str());
	    return false;
	case NODE_ID:
	    errmsg = c_format("Attempt to set node ID \"%s\" "
			      "which is not user-settable",
			      varname.c_str());
	    XLOG_ERROR("%s", errmsg.c_str());
	    return false;
	case NAMED:
	    errmsg = c_format("Attempt to set variable \"%s\" "
			      "which is the child of a named variable",
			      varname.c_str());
	    XLOG_ERROR("%s", errmsg.c_str());
	    return false;
	case TEMPLATE_DEFAULT:
	    errmsg = c_format("Attempt to set variable \"%s\" "
			      "which is on a non-existent node",
			      varname.c_str());
	    XLOG_ERROR("%s", errmsg.c_str());
	    return false;
	}
    }

    errmsg = c_format("Attempt to set unknown variable \"%s\"",
		      varname.c_str());
    XLOG_ERROR("%s", errmsg.c_str());
    return false;
}

void
ConfigTreeNode::set_named_value(const string& varname, const string& value)
{
    debug_msg("set_named_value %s=%s\n", varname.c_str(), value.c_str());

    _variables[varname] = value;
}


const string&
ConfigTreeNode::named_value(const string& varname) const
{
    map<string, string>::const_iterator iter;

    iter = _variables.find(varname);
    XLOG_ASSERT(iter != _variables.end());
    return iter->second;
}

int
ConfigTreeNode::child_number() const
{
    if (_template_tree_node) {
	return _template_tree_node->child_number();
    } else {
	return 0;
    }
}

void
ConfigTreeNode::sort_by_template(list<ConfigTreeNode*>& children) const
{
    children.sort(CTN_Compare());
} 

string
ConfigTreeNode::show_node_id(bool numbered, const ConfigNodeId& node_id) const 
{
    string s;
    if (numbered) {
	s = c_format("%%%s%% ", node_id.str().c_str());
    }
    return s;
}

void
ConfigTreeNode::allocate_unique_node_id() 
{
    ConfigTreeNode *prev = NULL, *next = NULL;
    ConfigTreeNode *effective_parent = _parent;

    XLOG_ASSERT(_node_id.is_empty());

    if (_parent == NULL)
	return;

    //
    // Node IDs are guaranteed to be unique among the children of
    // the same parent node.  If the parent node is a tag, they're
    // guaranteed to be unique among the children of the tag's parent
    // node.
    //
    debug_msg("finding order (phase 1)...\n");
    list<ConfigTreeNode *> sorted_children = _parent->children();
    sort_by_template(sorted_children);
    bool found_this = false;
    list<ConfigTreeNode*>::iterator iter;
    for (iter = sorted_children.begin(); 
	 iter != sorted_children.end();
	 ++iter) {
	if ((*iter) == this) {
	    // We found this node
	    found_this = true;
	    debug_msg("found this: %s\n", _segname.c_str());
	    continue;
	}
	if (found_this) {
	    if (next == NULL)
		next = *iter;
	    debug_msg("next: %s %s\n", next->segname().c_str(),
		      next->node_id().str().c_str());
	} else {
	    prev = *iter;
	    debug_msg("prev: %s %s\n", prev->segname().c_str(),
		      prev->node_id().str().c_str());
	}
    }

    //
    // Found the previous and the next (ordered) sibling
    //
    bool found_prev = false;
    if (prev != NULL)
	found_prev = true;
    if (_parent->is_tag() && (!is_tag()) && (_parent->parent() != NULL)) {
	effective_parent = _parent->parent();
	XLOG_ASSERT(effective_parent != NULL);

	debug_msg("node: %s effective parent: %s\n", _segname.c_str(),
		  effective_parent->segname().c_str());
	found_this = false;
	debug_msg("finding order (phase 2)...\n");
	list<ConfigTreeNode *> sorted_children = effective_parent->children();
	sort_by_template(sorted_children);
	for (iter = sorted_children.begin(); 
	     iter != sorted_children.end();
	     ++iter) {
	    if ((*iter) == _parent) {
		// We found the tag parent
		found_this = true;
		debug_msg("found this: %s\n", _segname.c_str());
		if (next != NULL)
		    break;
		continue;
	    }
	    if (found_this) {
		next = *iter;
		debug_msg("next: %s %s\n", next->segname().c_str(),
			  next->node_id().str().c_str());
		break;
	    } else {
		if (!found_prev) {
		    prev = *iter;
		    debug_msg("prev: %s %s\n", prev->segname().c_str(),
			      prev->node_id().str().c_str());
		}
	    }
	}
    }

    //
    // Calculate the unique part of the node ID
    //
    _node_id = effective_parent->node_id_generator().generate_unique_node_id();
    if (prev == NULL) {
	_node_id.set_position(0);	// XXX: the first node
    } else {
	_node_id.set_position(prev->node_id().unique_node_id());
    }
    debug_msg("node %s node_id %s", _segname.c_str(), _node_id.str().c_str());

    XLOG_ASSERT(! _node_id.is_empty());
}

string
ConfigTreeNode::show_operator() const
{
    return operator_to_str(_operator);
}

bool
ConfigTreeNode::is_committed() const
{
    return (! is_uncommitted());
}

bool
ConfigTreeNode::is_uncommitted() const
{
    return ((_has_value && !_value_committed) || (!_existence_committed));
}

bool
ConfigTreeNode::is_default_value() const
{
    if (! _has_value)
	return (false);

    if (_template_tree_node == NULL)
	return (false);

    if (! _template_tree_node->has_default())
	return (false);

    return (value() == _template_tree_node->default_str());
}

bool
ConfigTreeNode::is_default_value(const string& test_value) const
{
    if (test_value.empty())
	return (false);

    if (_template_tree_node == NULL)
	return (false);

    if (! _template_tree_node->has_default())
	return (false);

    return (test_value == _template_tree_node->default_str());
}

bool
ConfigTreeNode::has_undeleted_children() const
{
    list<ConfigTreeNode *>::const_iterator iter;

    for (iter = _children.begin(); iter != _children.end(); ++iter) {
	const ConfigTreeNode* child = *iter;
	if ((child != NULL) && (! child->deleted()))
	    return (true);
    }

    return (false);
}

string
ConfigTreeNode::quoted_value(const string& value) const
{
    string result;

    // XXX: re-escape quotes
    string escaped("");

    for (string::const_iterator i = value.begin(); i != value.end(); ++i) {
	const char x = *i;
	if (x == '"')
	    escaped += "\\\"";
	else
	    escaped += x;
    }

    result = "\"" + escaped + "\"";

    return result;
}


syntax highlighted by Code2HTML, v. 0.9.1