// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*-

// 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/template_tree_node.cc,v 1.79 2007/02/16 22:47:25 pavlin Exp $"

#include "rtrmgr_module.h"

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

#ifdef HAVE_GLOB_H
#include <glob.h>
#elif defined(HOST_OS_WINDOWS)
#include "glob_win32.h"
#endif

#include <sstream>

#include "command_tree.hh"
#include "conf_tree.hh"
#include "module_command.hh"
#include "template_commands.hh"
#include "template_tree.hh"
#include "template_tree_node.hh"
#include "util.hh"


TemplateTreeNode::TemplateTreeNode(TemplateTree& template_tree,
				   TemplateTreeNode* parent,
				   const string& path,
				   const string& varname)
    : _parent(parent),
      _template_tree(template_tree),
      _module_name(""),
      _default_target_name(""),
      _segname(path),
      _varname(varname),
      _has_default(false),
      _is_tag(false),
      _order(ORDER_UNSORTED),
      _verbose(template_tree.verbose()),
      _is_deprecated(false),
      _is_user_hidden(false),
      _is_mandatory(false),
      _is_read_only(false),
      _is_permanent(false)
{
    if (_parent != NULL) {
	_parent->add_child(this);
	_module_name = _parent->module_name();
	_default_target_name = _parent->default_target_name();
	_child_number = _parent->children().size();
    } else {
	_child_number = 0;
    }
}

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

    map<string, BaseCommand *>::iterator iter1, iter2;
    iter1 = _cmd_map.begin();
    while (iter1 != _cmd_map.end()) {
	iter2 = iter1;
	++iter1;
	delete iter2->second;
	_cmd_map.erase(iter2);
    }
}

bool
TemplateTreeNode::expand_template_tree(string& error_msg)
{
    map<string, BaseCommand*>::iterator cmd_iter;

    //
    // Expand all %allow, %allow-operator and %allow-range filters.
    // XXX: currently the %allow-operator expanding is no-op.
    //
    cmd_iter = _cmd_map.find("%allow");
    if (cmd_iter != _cmd_map.end()) {
	BaseCommand* command = cmd_iter->second;
	AllowOptionsCommand* allow_options_command;
	allow_options_command = dynamic_cast<AllowOptionsCommand*>(command);
	XLOG_ASSERT(allow_options_command != NULL);
	if (allow_options_command->expand_actions(error_msg) != true)
	    return (false);
    }
    cmd_iter = _cmd_map.find("%allow-operator");
    if (cmd_iter != _cmd_map.end()) {
	BaseCommand* command = cmd_iter->second;
	AllowOperatorsCommand* allow_operators_command;
	allow_operators_command = dynamic_cast<AllowOperatorsCommand*>(command);
	XLOG_ASSERT(allow_operators_command != NULL);
	if (allow_operators_command->expand_actions(error_msg) != true)
	    return (false);
    }
    cmd_iter = _cmd_map.find("%allow-range");
    if (cmd_iter != _cmd_map.end()) {
	BaseCommand* command = cmd_iter->second;
	AllowRangeCommand* allow_range_command;
	allow_range_command = dynamic_cast<AllowRangeCommand*>(command);
	XLOG_ASSERT(allow_range_command != NULL);
	if (allow_range_command->expand_actions(error_msg) != true)
	    return (false);
    }

    //
    // Mark all referred mandatory variables
    //
    list<string>::const_iterator string_iter;
    for (string_iter = mandatory_config_nodes().begin();
	 string_iter != mandatory_config_nodes().end();
	 ++string_iter) {
	const string& mandatory_config_node = *string_iter;
	TemplateTreeNode* ttn;
	ttn = find_varname_node(mandatory_config_node);
	if (ttn == NULL) {
	    error_msg = c_format("Invalid template mandatory variable %s: "
				 "not found",
				 mandatory_config_node.c_str());
	    return (false);
	}
	bool is_multi_value = false;
	TemplateTreeNode* ttn_check_multi = ttn;
	do {
	    //
	    // Check if there is a multi-value node between the referred
	    // template node and this node (or the root of the template tree).
	    //
	    if (ttn_check_multi->is_tag()) {
		is_multi_value = true;
		break;
	    }
	    ttn_check_multi = ttn_check_multi->parent();
	    if (ttn_check_multi == this)
		break;
	    if (ttn_check_multi == NULL)
		break;
	} while (true);
	if (is_multi_value) {
	    error_msg = c_format("Invalid template mandatory variable %s: "
				 "cannot specify a multi-value node as "
				 "mandatory",
				 mandatory_config_node.c_str());
	    return (false);
	}

	ttn->set_mandatory(true);
    }

    //
    // Recursively expand all children nodes
    //
    list<TemplateTreeNode*>::iterator iter2;
    for (iter2 = _children.begin(); iter2 != _children.end(); ++iter2) {
	TemplateTreeNode* ttn = *iter2;
	if (ttn->expand_template_tree(error_msg) != true)
	    return (false);
    }

    return (true);
}

bool
TemplateTreeNode::check_template_tree(string& error_msg) const
{
    const BaseCommand *base_cmd = NULL;
    map<string, BaseCommand*>::const_iterator cmd_iter;

    //
    // Check all refered variables for this node
    //
    for (cmd_iter = _cmd_map.begin(); cmd_iter != _cmd_map.end(); ++cmd_iter) {
	const BaseCommand* command = cmd_iter->second;
	if (! command->check_referred_variables(error_msg))
	    return false;
    }

    //
    // Check all referred mandatory variables
    //
    list<string>::const_iterator string_iter;
    for (string_iter = mandatory_config_nodes().begin();
	 string_iter != mandatory_config_nodes().end();
	 ++string_iter) {
	const string& mandatory_config_node = *string_iter;
	const TemplateTreeNode* ttn;
	ttn = find_const_varname_node(mandatory_config_node);
	if (ttn == NULL) {
	    error_msg = c_format("Invalid template mandatory variable %s: "
				 "not found",
				 mandatory_config_node.c_str());
	    return false;
	}
	bool is_multi_value = false;
	do {
	    //
	    // Check if there is a multi-value node between the referred
	    // template node and this node (or the root of the template tree).
	    //
	    if (ttn->is_tag()) {
		is_multi_value = true;
		break;
	    }
	    ttn = ttn->parent();
	    if (ttn == this)
		break;
	    if (ttn == NULL)
		break;
	} while (true);
	if (is_multi_value) {
	    error_msg = c_format("Invalid template mandatory variable %s: "
				 "cannot specify a multi-value node as "
				 "mandatory",
				 mandatory_config_node.c_str());
	    return false;
	}
    }

    //
    // Check specific commands for this node
    //
    // XXX: only leaf nodes should have %set command
    base_cmd = const_command("%set");
    if (base_cmd != NULL) {
	if (! is_leaf_value()) {
	    error_msg = c_format("Found %%set command in node \"%s\" that "
				 "doesn't expect value",
			      path().c_str());
	    return false;
	}
    }

    //
    // Recursively check all children nodes
    //
    list<TemplateTreeNode*>::const_iterator iter2;
    for (iter2 = _children.begin(); iter2 != _children.end(); ++iter2) {
	const TemplateTreeNode* ttn = *iter2;
	if (ttn->check_template_tree(error_msg) != true)
	    return false;
    }

    return true;
}

void
TemplateTreeNode::add_child(TemplateTreeNode* child)
{
    _children.push_back(child);
}

void
TemplateTreeNode::add_cmd(const string& cmd) throw (ParseError)
{
    string error_msg;
    BaseCommand* command;

    if (cmd == "%modinfo") {
	// only the MasterTemplateTree cares about this
    } else if (cmd == "%allow") {
	// If the command already exists, no need to create it again.
	// The command action will simply be added to the existing command.
	if (_cmd_map.find(cmd) == _cmd_map.end()) {
	    command = new AllowOptionsCommand(*this, cmd);
	    _cmd_map[cmd] = command;
	}
    } else if (cmd == "%allow-range") {
	// If the command already exists, no need to create it again.
	// The command action will simply be added to the existing command.
	if (_cmd_map.find(cmd) == _cmd_map.end()) {
	    command = new AllowRangeCommand(*this, cmd);
	    _cmd_map[cmd] = command;
	}
    } else if (cmd == "%allow-operator") {
	debug_msg("%%allow-operator\n");
	// If the command already exists, no need to create it again.
	// The command action will simply be added to the existing command.
	if (_cmd_map.find(cmd) == _cmd_map.end()) {
	    debug_msg("creating new AllowOperatorsCommand\n");
	    command = new AllowOperatorsCommand(*this, cmd);
	    _cmd_map[cmd] = command;
	}
    } else if (cmd == "%help") {
	// Nothing to do - the work is done by add_action
    } else if (cmd == "%deprecated") {
	// Nothing to do - the work is done by add_action
    } else if (cmd == "%user-hidden") {
	// Nothing to do - the work is done by add_action
    } else if (cmd == "%read-only") {
	// XXX: only leaf nodes should have %read-only command
	if (! is_leaf_value()) {
	    error_msg = c_format("Invalid command \"%s\".\n", cmd.c_str());
	    error_msg += "This command only applies to leaf nodes that have ";
	    error_msg += "values.\n";
	    xorp_throw(ParseError, error_msg);
	}
	_is_read_only = true;
	_is_permanent = true;	// XXX: read-only also implies permanent node
    } else if (cmd == "%permanent") {
	_is_permanent = true;
    } else if (cmd == "%order") {
	// Nothing to do - the work is done by add_action
    } else if ((cmd == "%create")
	       || (cmd == "%activate")
	       || (cmd == "%update")
	       || (cmd == "%list")
	       || (cmd == "%delete")
	       || (cmd == "%set")
	       || (cmd == "%unset")
	       || (cmd == "%get")
	       || (cmd == "%default")) {
	//
	// Check if we are allowed to add this command
	//
	if (cmd == "%set") {
	    // XXX: only leaf nodes should have %set command
	    if (! is_leaf_value()) {
		error_msg = c_format("Invalid command \"%s\".\n", cmd.c_str());
		error_msg += "This command only applies to leaf nodes that ";
		error_msg += "have values and only if the value is allowed ";
		error_msg += "to be changed.\n";
		xorp_throw(ParseError, error_msg);
	    }
	}

	//
	// XXX: We just create a placeholder here - if we were a master
	// template tree node we'd create a real command.
	//
	if (_cmd_map.find(cmd) == _cmd_map.end()) {
	    command = new DummyBaseCommand(*this, cmd);
	    _cmd_map[cmd] = command;
	}
    } else if (cmd == "%mandatory") {
	// Nothing to do
    } else {
	error_msg = c_format("Invalid command \"%s\".\n", cmd.c_str());
	error_msg += "Valid commands are %create, %delete, %set, %unset, ";
	error_msg += "%get, %default, %modinfo, %activate, %update, %allow, ";
	error_msg += "%allow-range, %mandatory, %deprecated, %user-hidden, ";
	error_msg += "%read-only, %permanent, %order\n";
	xorp_throw(ParseError, error_msg);
    }
}

set<string>
TemplateTreeNode::commands() const
{
    set<string> cmds;
    map<string, BaseCommand *>::const_iterator iter;

    for (iter = _cmd_map.begin(); iter != _cmd_map.end(); ++iter) {
	cmds.insert(iter->first);
    }
    return cmds;
}

void
TemplateTreeNode::add_action(const string& cmd,
			     const list<string>& action_list)
    throw (ParseError)
{
    BaseCommand* command;
    map<string, BaseCommand*>::iterator iter;
    string error_msg;

    if (cmd == "%modinfo") {
	// only the Master tree cares about this
    } else if (cmd == "%allow") {
	iter = _cmd_map.find("%allow");
	XLOG_ASSERT(iter != _cmd_map.end());
	command = iter->second;
	AllowCommand* allow_command = dynamic_cast<AllowCommand*>(command);
	XLOG_ASSERT(allow_command != NULL);
	allow_command->add_action(action_list);
    } else if (cmd == "%allow-range") {
	iter = _cmd_map.find("%allow-range");
	XLOG_ASSERT(iter != _cmd_map.end());
	command = iter->second;
	AllowCommand* allow_command = dynamic_cast<AllowCommand*>(command);
	XLOG_ASSERT(allow_command != NULL);
	allow_command->add_action(action_list);
    } else if (cmd == "%allow-operator") {
	iter = _cmd_map.find("%allow-operator");
	XLOG_ASSERT(iter != _cmd_map.end());
	command = iter->second;
	AllowCommand* allow_command = dynamic_cast<AllowCommand*>(command);
	XLOG_ASSERT(allow_command != NULL);
	allow_command->add_action(action_list);
    } else if (cmd == "%help") {
	if (action_list.size() == 2) {
	    list<string>::const_iterator li = action_list.begin();
	    li++;
	    // Trim off quotes if present
	    string help = unquote(*li);
	    if (action_list.front() == "short") {
		_help = help;
	    } else if (action_list.front() == "long") {
		_help_long = help;
	    } else {
		error_msg = c_format("Invalid %%help descriptor %s: "
				     "\"short\" or \"long\" expectted",
				     action_list.front().c_str());
		xorp_throw(ParseError, error_msg);
	    }
	} else {
	    error_msg = c_format("Invalid number of %%help arguments: "
				 "%u (expected 2)",
				 XORP_UINT_CAST(action_list.size()));
	    xorp_throw(ParseError, error_msg);
	}
    } else if (cmd == "%deprecated") {
	if (action_list.size() == 1) {
	    list<string>::const_iterator li = action_list.begin();
	    // Trim off quotes if present
	    string reason = unquote(*li);
	    _is_deprecated = true;
	    _deprecated_reason = reason;
	    if ((_parent != NULL) && (_parent->is_tag())) {
		_parent->set_deprecated(true);
		_parent->set_deprecated_reason(reason);
	    }
	} else {
	    error_msg = c_format("Invalid number of %%deprecated arguments: "
				 "%u (expected 1)",
				 XORP_UINT_CAST(action_list.size()));
	    xorp_throw(ParseError, error_msg);
	}
    } else if (cmd == "%user-hidden") {
	if (action_list.size() == 1) {
	    list<string>::const_iterator li = action_list.begin();
	    // Trim off quotes if present
	    string reason = unquote(*li);
	    _is_user_hidden = true;
	    _user_hidden_reason = reason;
	    if ((_parent != NULL) && (_parent->is_tag())) {
		_parent->set_user_hidden(true);
		_parent->set_user_hidden_reason(reason);
	    }
	} else {
	    error_msg = c_format("Invalid number of %%user-hidden arguments: "
				 "%u (expected 1)",
				 XORP_UINT_CAST(action_list.size()));
	    xorp_throw(ParseError, error_msg);
	}
    } else if (cmd == "%read-only") {
	if (action_list.size() == 1) {
	    list<string>::const_iterator li = action_list.begin();
	    // Trim off quotes if present
	    string reason = unquote(*li);
	    _is_read_only = true;
	    _read_only_reason = reason;
	    _is_permanent = true; // XXX: read-only also implies permanent node
	} else {
	    error_msg = c_format("Invalid number of %%user-hidden arguments: "
				 "%u (expected 1)",
				 XORP_UINT_CAST(action_list.size()));
	    xorp_throw(ParseError, error_msg);
	}
    } else if (cmd == "%permanent") {
	if (action_list.size() == 1) {
	    list<string>::const_iterator li = action_list.begin();
	    // Trim off quotes if present
	    string reason = unquote(*li);
	    _is_permanent = true;
	    _permanent_reason = reason;
	} else {
	    error_msg = c_format("Invalid number of %%permanent arguments: "
				 "%u (expected 1)",
				 XORP_UINT_CAST(action_list.size()));
	    xorp_throw(ParseError, error_msg);
	}
    } else if (cmd == "%order") {
	if (action_list.size() == 1) {
	    list<string>::const_iterator li = action_list.begin();
	    TTSortOrder order = ORDER_UNSORTED;
	    if (*li == "unsorted") {
		order = ORDER_UNSORTED;
	    } else if (*li == "sorted-numeric") {
		order = ORDER_SORTED_NUMERIC;
	    } else if (*li == "sorted-alphabetic") {
		order = ORDER_SORTED_ALPHABETIC;
	    } else {
		error_msg = c_format("Bad %%order specification in template "
				     "file ignored - should be unsorted, "
				     "sorted-numeric, or sorted-alphabetic");
		xorp_throw(ParseError, error_msg);
	    }
	    set_order(order);

	    if ((_parent != NULL) && (_parent->is_tag())) {
		_parent->set_order(order);
	    }
	} else {
	    error_msg = c_format("Invalid number of %%order arguments: "
				 "%u (expected 1)",
				 XORP_UINT_CAST(action_list.size()));
	    xorp_throw(ParseError, error_msg);
	}
    } else if (cmd == "%mandatory") {
	// Add all new mandatory variables
	list<string>::const_iterator li;
	for (li = action_list.begin(); li != action_list.end(); ++li) {
	    const string& varname = *li;
	    if (find(_mandatory_config_nodes.begin(),
		     _mandatory_config_nodes.end(),
		     varname)
		== _mandatory_config_nodes.end()) {
		_mandatory_config_nodes.push_back(varname);
	    }
	}
    } else {
	// the master tree will deal with these
    }
}

map<string, string>
TemplateTreeNode::create_variable_map(const list<string>& segments) const
{
    map<string,string> varmap;
    const TemplateTreeNode* ttn = this;
    list<string>::const_reverse_iterator iter;

    for (iter = segments.rbegin(); iter != segments.rend(); ++iter) {
	if (ttn->name_is_variable())
	    varmap[ttn->segname()] = *iter;
	ttn = ttn->parent();
    }
    return varmap;
}

string
TemplateTreeNode::path() const
{
    string path;

    if (_parent != NULL) {
	string parent_path = _parent->path();
	if (parent_path == "")
	    path = _segname;
	else
	    path = parent_path + " " + _segname;
    } else {
	path = "";
    }
    return path;
}

bool
TemplateTreeNode::is_module_root_node() const
{
    string my_module_name, parent_module_name;

    my_module_name = module_name();
    if (my_module_name.empty())
	return (false);		// XXX: this node doesn't belong to any module

    if (parent() != NULL)
	parent_module_name = parent()->module_name();

    return (my_module_name != parent_module_name);
}

bool
TemplateTreeNode::is_leaf_value() const
{
    if ((type() != NODE_VOID) && (_parent != NULL) && (!_parent->is_tag()))
	return true;

    return false;
}

list<ConfigOperator>
TemplateTreeNode::allowed_operators() const
{
    list<ConfigOperator> result;

    const BaseCommand* c = const_command("%allow-operator");
    if (c == NULL)
	return result;

    const AllowOperatorsCommand* cmd;
    cmd = dynamic_cast<const AllowOperatorsCommand *>(c);
    if (cmd == NULL)
	return result;

    result = cmd->allowed_operators();
    return result;
}

string
TemplateTreeNode::str() const
{
    string tmp;

    tmp = path() + " Type:" + typestr();
    if (has_default()) {
	tmp += " = " + default_str();
    }
    if (!_cmd_map.empty()) {
	map<string, BaseCommand*>::const_iterator rpair;
	rpair = _cmd_map.begin();
	while (rpair != _cmd_map.end()) {
	    tmp += "\n    " + rpair->second->str();
	    ++rpair;
	}
    }
    return tmp;
}

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

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

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

    return s;
}

string
TemplateTreeNode::encoded_typestr() const
{
    return "<" + typestr() + ">";
}

bool
TemplateTreeNode::type_match(const string&, string& ) const
{
    return true;
}

BaseCommand*
TemplateTreeNode::command(const string& cmd_name)
{
    map<string, BaseCommand *>::iterator iter;

    iter = _cmd_map.find(cmd_name);
    if (iter == _cmd_map.end())
	return NULL;
    return iter->second;
}

const BaseCommand*
TemplateTreeNode::const_command(const string& cmd_name) const
{
    map<string, BaseCommand *>::const_iterator iter;

    iter = _cmd_map.find(cmd_name);
    if (iter == _cmd_map.end())
	return NULL;
    return iter->second;
}

string
TemplateTreeNode::strip_quotes(const string& s) const
{
    size_t len = s.length();

    if (len < 2)
	return s;
    if ((s[0] == '"') && (s[len - 1] == '"'))
	return s.substr(1, len - 2);
    else
	return s;
}

bool
TemplateTreeNode::name_is_variable() const
{
    if (_segname.size() < 4)
	return false;
    if (_segname[0] != '$' || _segname[1] != '(')
	return false;
    return true;
}

bool
TemplateTreeNode::expand_variable(const string& varname, string& value,
				  bool ignore_deleted_nodes) const
{
    const TemplateTreeNode* varname_node;

    UNUSED(ignore_deleted_nodes);

    varname_node = find_const_varname_node(varname);
    if (varname_node == NULL)
	return false;

    list<string> var_parts;
    if (split_up_varname(varname, var_parts) == false) {
	return false;
    }
    const string& nodename = var_parts.back();

    XLOG_ASSERT((nodename == varname_node->segname())
		|| (nodename == "DEFAULT"));
    if (! varname_node->has_default())
	return false;		// XXX: the variable has no default value
    value = varname_node->default_str();
    return true;
}

bool
TemplateTreeNode::expand_expression(const string& expression,
				    string& value) const
{
    // Expecting at least "`~" + a_name + "`"
    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;
}

void
TemplateTreeNode::set_subtree_module_name(const string& module_name)
{
    //
    // Recursively set the module name to the subtree below this node
    //
    _module_name = module_name;

    list<TemplateTreeNode *>::iterator iter;
    for (iter = _children.begin(); iter != _children.end(); ++iter) {
	TemplateTreeNode* child = *iter;
	child->set_subtree_module_name(module_name);
    }
}

void
TemplateTreeNode::set_subtree_default_target_name(const string& default_target_name)
{
    //
    // Recursively set the default target name to the subtree below this node
    //
    _default_target_name = default_target_name;

    list<TemplateTreeNode *>::iterator iter;
    for (iter = _children.begin(); iter != _children.end(); ++iter) {
	TemplateTreeNode* child = *iter;
	child->set_subtree_default_target_name(default_target_name);
    }
}

string
TemplateTreeNode::get_module_name_by_variable(const string& varname) const
{
    const TemplateTreeNode* ttn;

    ttn = find_const_varname_node(varname);
    //
    // Search all template tree nodes toward the root
    // to find the module name.
    //
    while (ttn != NULL) {
	string module_name = ttn->module_name();
	if (module_name.length() > 0)
	    return (module_name);
	ttn = ttn->parent();
    }

    return "";		// XXX: nothing found
}

string
TemplateTreeNode::get_default_target_name_by_variable(const string& varname) const
{
    const TemplateTreeNode* ttn;

    ttn = find_const_varname_node(varname);
    if (ttn != NULL) {
	//
	// Search all template tree nodes toward the root
	// to find the default target name.
	//
	for ( ; ttn != NULL; ttn = ttn->parent()) {
	    string default_target_name = ttn->default_target_name();
	    if (default_target_name.length() > 0)
		return (default_target_name);
	}
    }

    return "";		// XXX: nothing found
}

bool
TemplateTreeNode::split_up_varname(const string& varname,
				   list<string>& var_parts) const
{
    if (varname.size() < 4)
	return false;

    if (varname[0] != '$'
	|| varname[1] != '('
	|| varname[varname.size() - 1] != ')') {
	return false;
    }

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

    return true;
}

const TemplateTreeNode*
TemplateTreeNode::find_const_varname_node(const string& varname) 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<TemplateTreeNode*>(this))->find_varname_node(varname);
}

TemplateTreeNode*
TemplateTreeNode::find_varname_node(const string& varname)
{
    if (varname == "$(@)" /* current value of a node */
	|| (varname == "$(DEFAULT)") /* default value of a node */
	|| (varname == "$(<>)") /* operator for a terminal node */
	|| (varname == "$(#)") /* the node ID of a node */
	|| (varname == "$(" + _segname + ")") ) {
	XLOG_ASSERT(! is_tag());
	if (varname == "$(DEFAULT)") {
	    if (! has_default())
		return NULL;	// The template tree node has no default value
	}
	return this;
    }

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

    if (var_parts.front() == "@") {
	return find_child_varname_node(var_parts);
    }

    if (var_parts.size() > 1) {
	// It's a parent node, or a child of a parent node
	return find_parent_varname_node(var_parts);
    }

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

TemplateTreeNode*
TemplateTreeNode::find_parent_varname_node(const list<string>& var_parts)
{
    if (_parent == NULL) {
	//
	// We have reached the root node.
	// Search among the children.
	//
	list<TemplateTreeNode* >::iterator iter;
	for (iter = _children.begin(); iter != _children.end(); ++iter) {
	    TemplateTreeNode* found_child;
	    found_child = (*iter)->find_child_varname_node(var_parts);
	    if (found_child != NULL)
		return found_child;
	}
	return NULL;
    }
    if (is_tag() || (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);
	}
    }

    return _parent->find_parent_varname_node(var_parts);
}

TemplateTreeNode*
TemplateTreeNode::find_child_varname_node(const list<string>& var_parts)
{
    if ((var_parts.front() != "@") && (var_parts.front() != _segname)) {
	// varname doesn't match us
	return NULL;
    }

    // The name might refer to this node
    if (var_parts.size() == 1) {
	if ((var_parts.front() == "@") 
	    || (var_parts.front() == _segname)
	    || (var_parts.front() == "<>")
	    || (var_parts.front() == "#")) {
	    return this;
	}
    }

    // The name might refer to the default value of this node
    if ((var_parts.size() == 2) && (var_parts.back() == "DEFAULT")) {
	if ((var_parts.front() == "@") || (var_parts.front() == _segname)) {
	    // The name refers to the default value of this node
	    if (! has_default())
		return NULL;	// The template tree node has no default value
	    return this;
	}
    }

    // The name might refer to the operator value of this node
    if ((var_parts.size() == 2) && (var_parts.back() == "<>")) {
	if ((var_parts.front() == "@") || (var_parts.front() == _segname)) {
	    return this;
	}
    }

    // The name might refer to the node ID of this node
    if ((var_parts.size() == 2) && (var_parts.back() == "#")) {
	if ((var_parts.front() == "@") || (var_parts.front() == _segname)) {
	    return this;
	}
    }

    // The name might refer to a child of ours
    list<string> child_var_parts = var_parts;
    child_var_parts.pop_front();

    list<TemplateTreeNode* >::iterator iter;
    for (iter = _children.begin(); iter != _children.end(); ++iter) {
	TemplateTreeNode* found_child;
	found_child = (*iter)->find_child_varname_node(child_var_parts);
	if (found_child != NULL)
	    return found_child;
    }

    return NULL;
}

const TemplateTreeNode*
TemplateTreeNode::find_first_deprecated_ancestor() const
{
    const TemplateTreeNode* ttn = _parent;
    const TemplateTreeNode* deprecated_ttn = NULL;

    if (is_deprecated())
	deprecated_ttn = this;

    while (ttn != NULL) {
	if (ttn->is_deprecated())
	    deprecated_ttn = ttn;
	ttn = ttn->parent();
    }

    return (deprecated_ttn);
}

const TemplateTreeNode*
TemplateTreeNode::find_first_user_hidden_ancestor() const
{
    const TemplateTreeNode* ttn = _parent;
    const TemplateTreeNode* user_hidden_ttn = NULL;

    if (is_user_hidden())
	user_hidden_ttn = this;

    while (ttn != NULL) {
	if (ttn->is_user_hidden())
	    user_hidden_ttn = ttn;
	ttn = ttn->parent();
    }

    return (user_hidden_ttn);
}

void
TemplateTreeNode::add_allowed_value(const string& value, const string& help)
{
    // XXX: insert the new pair even if we overwrite an existing one
    _allowed_values.insert(make_pair(value, help));
}

void
TemplateTreeNode::add_allowed_range(int64_t lower_value, int64_t upper_value,
				    const string& help)
{
    pair<int64_t, int64_t> range(lower_value, upper_value);

    // XXX: insert the new pair even if we overwrite an existing one
    _allowed_ranges.insert(make_pair(range, help));
}

bool
TemplateTreeNode::check_allowed_value(const string& value,
				      string& error_msg) const
{
    //
    // Check the allowed values
    //
    if ((! _allowed_values.empty())
	&& (_allowed_values.find(value) == _allowed_values.end())) {
	map<string, string> values = _allowed_values;

	if (values.size() == 1) {
	    error_msg = c_format("The only value allowed is %s.",
				 values.begin()->first.c_str());
	} else {
	    error_msg = "Allowed values are: ";
	    bool is_first = true;
	    while (! values.empty()) {
		if (is_first) {
		    is_first = false;
		} else {
		    if (values.size() == 1)
			error_msg += " and ";
		    else
			error_msg += ", ";
		}
		map<string, string>::iterator iter = values.begin();
		error_msg += iter->first;
		values.erase(iter);
	    }
	    error_msg += ".";
	}
	return (false);
    }

    //
    // Check the allowed ranges
    //
    bool is_accepted = true;
    int64_t lower_value = 0;
    int64_t upper_value = 0;
    //
    // XXX: it is sufficient for the variable's value to belong to any
    // of the allowed ranges.
    //
    if (! _allowed_ranges.empty()) {
	is_accepted = false;
	map<pair<int64_t, int64_t>, string>::const_iterator iter;
	int64_t ival = strtoll(value.c_str(), (char **)NULL, 10);
	for (iter = _allowed_ranges.begin();
	     iter != _allowed_ranges.end();
	     ++iter) {
	    const pair<int64_t, int64_t>& range = iter->first;
	    lower_value = range.first;
	    upper_value = range.second;
	    if ((ival >= lower_value) && (ival <= upper_value)) {
		is_accepted = true;
		break;
	    }
	}
    }

    if (! is_accepted) {
	map<pair<int64_t, int64_t>, string> ranges = _allowed_ranges;

	if (ranges.size() == 1) {
	    const pair<int64_t, int64_t>& range = ranges.begin()->first;
	    ostringstream ost;
	    ost << "[" << range.first << ".." << range.second << "]";
	    error_msg = c_format("The only range allowed is %s.",
				 ost.str().c_str());
	} else {
	    error_msg = "Allowed ranges are: ";
	    bool is_first = true;
	    while (! ranges.empty()) {
		if (is_first) {
		    is_first = false;
		} else {
		    if (ranges.size() == 1)
			error_msg += " and ";
		    else
			error_msg += ", ";
		}
		map<pair<int64_t, int64_t>, string>::iterator iter;
		iter = ranges.begin();
		const pair<int64_t, int64_t>& range = iter->first;
		ostringstream ost;
		ost << "[" << range.first << ".." << range.second << "]";
		error_msg += c_format("%s", ost.str().c_str());
		ranges.erase(iter);
	    }
	    error_msg += ".";
	}
	return (false);
    }

    return (true);
}

bool
TemplateTreeNode::verify_variables(const ConfigTreeNode& ctn,
				   string& error_msg) const
{
    map<string, BaseCommand *>::const_iterator iter;

    for (iter = _cmd_map.begin(); iter != _cmd_map.end(); ++iter) {
	const BaseCommand* command = iter->second;
	const AllowCommand* allow_command;
	allow_command = dynamic_cast<const AllowCommand *>(command);
	if (allow_command == NULL)
	    continue;
	if (allow_command->verify_variables(ctn, error_msg) != true)
	    return (false);
    }

    return (true);
}

const string&
TemplateTreeNode::help() const
{
    // If the node is a tag, the help is held on the child
    if (is_tag())
	return children().front()->help();
    return _help;
}

const string&
TemplateTreeNode::help_long() const
{
    if (is_tag())
	return children().front()->help_long();
    if (_help_long == "")
	return help();
    return _help_long;
}

#if 0
bool
TemplateTreeNode::check_template_tree(string& error_msg) const
{
    //
    // First check this node, then check recursively all children nodes
    //

    //
    // Check whether all referred variable names are valid
    //
    map<string, BaseCommand *>::const_iterator iter1;
    for (iter1 = _cmd_map.begin(); iter1 != _cmd_map.end(); ++iter1) {
	const Command* command;
	command = dynamic_cast<Command*>(iter1->second);
	if (command) {
	    if (! command->check_referred_variables(error_msg))
		return false;
	}
    }

    //
    // Recursively check all children nodes
    //
    list<TemplateTreeNode*>::const_iterator iter2;
    for (iter2 = _children.begin(); iter2 != _children.end(); ++iter2) {
	const TemplateTreeNode* ttn = *iter2;
	if (ttn->check_template_tree(error_msg) != true)
	    return false;
    }

    return true;
}
#endif // 0

bool
TemplateTreeNode::check_command_tree(const list<string>& cmd_names,
				     bool include_intermediate_nodes,
				     bool include_read_only_nodes,
				     bool include_permanent_nodes,
				     bool include_user_hidden_nodes,
				     size_t depth) const
{
    bool instantiated = false;

    // XXX: ignore deprecated subtrees
    if (is_deprecated())
	return false;

    // XXX: ignore read-only nodes, permanent nodes and user-hidden nodes
    if (is_read_only() && (! include_read_only_nodes))
	return false;
    if (is_permanent() && (! include_permanent_nodes))
	return false;
    if (is_user_hidden() && (! include_user_hidden_nodes))
	return false;

    debug_msg("TTN:check_command_tree %s type %s depth %u\n",
	      _segname.c_str(), typestr().c_str(), XORP_UINT_CAST(depth));

    if (_parent != NULL && _parent->is_tag() && (depth == 0)) {
	//
	// If the parent is a tag, then this node must be a pure
	// variable. We don't want to instantiate any pure variable
	// nodes in the command tree.
	//
	debug_msg("pure variable\n");
	return false;
    }

    list<string>::const_iterator iter;
    for (iter = cmd_names.begin(); iter != cmd_names.end(); ++iter) {
	if (const_command(*iter) != NULL) {
	    debug_msg("node has command %s\n", (*iter).c_str());
	    instantiated = true;
	    break;
	} else {
	    debug_msg("node doesn't have command %s\n", (*iter).c_str());
	}
    }

    //
    // When we're building a command tree we only want to go one level
    // into the template tree beyond the part of the tree that is
    // instantiated by the config tree.  The exception is if the first
    // level node is a tag or a VOID node, then we need to check
    // further to find a real node.
    //
    if (_is_tag || ((type() == NODE_VOID) && include_intermediate_nodes)) {
	list<TemplateTreeNode*>::const_iterator tti;
	for (tti = _children.begin(); tti != _children.end(); ++tti) {
	    if ((*tti)->check_command_tree(cmd_names,
					   include_intermediate_nodes,
					   include_read_only_nodes,
					   include_permanent_nodes,
					   include_user_hidden_nodes,
					   depth + 1)) {
		instantiated = true;
		break;
	    }
	}
    }
    return instantiated;
}

bool
TemplateTreeNode::check_variable_name(const vector<string>& parts,
				      size_t part) const
{
    bool ok = false;

    debug_msg("check_variable_name: us>%s< match>%s<, %d\n",
	      _segname.c_str(), parts[part].c_str(), static_cast<int>(part));

    do {
	if (_parent == NULL) {
	    // Root node
	    ok = true;
	    part--; // Prevent increment of part later
	    break;
	}

	if (_parent->is_tag()) {
	    // We're the varname after a tag
	    if (parts[part] == "*") {
		ok = true;
	    } else {
		debug_msg("--varname but not wildcarded\n");
	    }
	    break;
	}

	if (parts[part] != _segname) {
	    debug_msg("--mismatch\n");
	    return false;
	}

	ok = true;
	break;
    } while (false);

    if (! ok)
	return false;

    if (part == parts.size() - 1) {
	// Everything that we were looking for matched
	// Final check that we haven't finished at a tag
	if (is_tag())
	    return (false);
	debug_msg("**match successful**\n");
	return true;
    }

    if (_children.empty()) {
	// No more children, but the search string still has components
	debug_msg("--no children\n");
	return false;
    }

    list<TemplateTreeNode*>::const_iterator tti;
    for (tti = _children.begin(); tti != _children.end(); ++tti) {
	if ((*tti)->check_variable_name(parts, part + 1)) {
	    return true;
	}
    }
    debug_msg("--no successful children, %d\n", static_cast<int>(part));

    return false;
}

/**************************************************************************
 * TextTemplate
 **************************************************************************/

TextTemplate::TextTemplate(TemplateTree& template_tree,
			   TemplateTreeNode* parent,
			   const string& path, const string& varname,
			   const string& initializer) throw (ParseError)
    : TemplateTreeNode(template_tree, parent, path, varname),
      _default("")
{
    string error_msg;

    if (initializer.empty())
	return;

    if (! type_match(initializer, error_msg)) {
	error_msg = c_format("Bad Text type value \"%s\": %s.",
			     initializer.c_str(), error_msg.c_str());
	xorp_throw(ParseError, error_msg);
    }

    string s = strip_quotes(initializer);
    _default = s;
    set_has_default();
}

bool
TextTemplate::type_match(const string&, string&) const
{
    //
    // If the lexical analyser passed it to us, we can assume its a
    // text value.
    //
    return true;
}


/**************************************************************************
 * ArithTemplate
 **************************************************************************/

ArithTemplate::ArithTemplate(TemplateTree& template_tree,
			     TemplateTreeNode* parent,
			     const string& path, const string& varname,
			     const string& initializer) throw (ParseError)
    : TemplateTreeNode(template_tree, parent, path, varname),
      _default("")
{
    string error_msg;

    if (initializer.empty())
	return;

    if (! type_match(initializer, error_msg)) {
	error_msg = c_format("Bad arith type value \"%s\": %s.",
			     initializer.c_str(), error_msg.c_str());
	xorp_throw(ParseError, error_msg);
    }

    string s = strip_quotes(initializer);
    _default = s;
    set_has_default();
}

bool
ArithTemplate::type_match(const string&, string&) const
{
    //
    // If the lexical analyser passed it to us, we can assume its a
    // arith value.
    //
    return true;
}


/**************************************************************************
 * UIntTemplate
 **************************************************************************/

UIntTemplate::UIntTemplate(TemplateTree& template_tree,
			   TemplateTreeNode* parent,
			   const string& path, const string& varname,
			   const string& initializer) throw (ParseError)
    : TemplateTreeNode(template_tree, parent, path, varname)
{
    string error_msg;

    if (initializer.empty())
	return;

    string s = strip_quotes(initializer);
    if (! type_match(s, error_msg)) {
	error_msg = c_format("Bad UInt type value \"%s\": %s.",
			     initializer.c_str(), error_msg.c_str());
	xorp_throw(ParseError, error_msg);
    }
    _default = strtoll(s.c_str(), (char **)NULL, 10);
    set_has_default();
}

bool
UIntTemplate::type_match(const string& orig, string& error_msg) const
{
    string s = strip_quotes(orig);

    for (size_t i = 0; i < s.length(); i++) {
	if (s[i] < '0' || s[i] > '9') {
	    if (s[i]=='-') {
		error_msg = "value cannot be negative";
	    } else if (s[i]=='.') {
		error_msg = "value must be an integer";
	    } else {
		error_msg = "value must be numeric";
	    }
	    return false;
	}
    }
    return true;
}

string
UIntTemplate::default_str() const
{
    return c_format("%u", _default);
}


/**************************************************************************
 * UIntRangeTemplate
 **************************************************************************/

UIntRangeTemplate::UIntRangeTemplate(TemplateTree& template_tree,
			   TemplateTreeNode* parent,
			   const string& path, const string& varname,
			   const string& initializer) throw (ParseError)
    : TemplateTreeNode(template_tree, parent, path, varname),
      _default(NULL)
{
    string error_msg;

    if (initializer.empty())
	return;

    try {
	_default = new U32Range(initializer.c_str());
    } catch (InvalidString) {
	error_msg = c_format("Bad U32Range type value \"%s\".",
			     initializer.c_str());
	xorp_throw(ParseError, error_msg);
    }
    set_has_default();
}

UIntRangeTemplate::~UIntRangeTemplate()
{
    if (_default != NULL)
	delete _default;
}

string
UIntRangeTemplate::default_str() const
{
    if (_default != NULL)
	return _default->str();

    return "";
}

bool
UIntRangeTemplate::type_match(const string& s, string& error_msg) const
{
    string tmp = strip_quotes(s);

    if (tmp.empty()) {
	error_msg = "value must be a valid range of unsigned 32-bit integers";
	return false;
    }

    try {
	U32Range* u32range = new U32Range(tmp.c_str());
	delete u32range;
    } catch (InvalidString) {
	error_msg = "value must be a valid range of unsigned 32-bit integers";
	return false;
    }
    return true;
}


/**************************************************************************
 * IntTemplate
 **************************************************************************/

IntTemplate::IntTemplate(TemplateTree& template_tree,
			 TemplateTreeNode* parent,
			 const string& path, const string& varname,
			 const string& initializer) throw (ParseError)
    : TemplateTreeNode(template_tree, parent, path, varname)
{
    string error_msg;

    if (initializer.empty())
	return;

    string s = strip_quotes(initializer);
    if (! type_match(s, error_msg)) {
	error_msg = c_format("Bad Int type value \"%s\": %s.",
			     initializer.c_str(), error_msg.c_str());
	xorp_throw(ParseError, error_msg);
    }
    _default = strtoll(s.c_str(), (char **)NULL, 10);
    set_has_default();
}

bool
IntTemplate::type_match(const string& orig, string& error_msg) const
{
    string s = strip_quotes(orig);
    size_t start = 0;
    if (s[0] == '-') {
	if (s.length() == 1) {
	    error_msg = "value must be an integer";
	    return false;
	}
	start = 1;
    }
    for (size_t i = start; i < s.length(); i++)
	if (s[i] < '0' || s[i] > '9') {
	    if (s[i]=='.') {
		error_msg = "value must be an integer";
	    } else {
		error_msg = "value must be numeric";
	    }	    
	    return false;
	}
    return true;
}

string
IntTemplate::default_str() const
{
    return c_format("%d", _default);
}


/**************************************************************************
 * BoolTemplate
 **************************************************************************/

BoolTemplate::BoolTemplate(TemplateTree& template_tree,
			   TemplateTreeNode* parent,
			   const string& path, const string& varname,
			   const string& initializer) throw (ParseError)
    : TemplateTreeNode(template_tree, parent, path, varname)
{
    string error_msg;

    if (initializer.empty())
	return;
    
    if (! type_match(initializer, error_msg)) {
	error_msg = c_format("Bad Bool type value \"%s\": %s.",
			     initializer.c_str(), error_msg.c_str());
	xorp_throw(ParseError, error_msg);
    }
    if (initializer == string("false"))
	_default = false;
    else
	_default = true;
    set_has_default();
}

bool
BoolTemplate::type_match(const string& s, string& error_msg) const
{
    if (s == "true")
	return true;
    else if (s == "false")
	return true;
    error_msg = "value must be \"true\" or \"false\"";
    return false;
}

string
BoolTemplate::default_str() const
{
    if (_default)
	return "true";
    else
	return "false";
}


/**************************************************************************
 * IPv4Template
 **************************************************************************/

IPv4Template::IPv4Template(TemplateTree& template_tree,
			   TemplateTreeNode* parent,
			   const string& path, const string& varname,
			   const string& initializer) throw (ParseError)
    : TemplateTreeNode(template_tree, parent, path, varname),
      _default(NULL)
{
    string error_msg;

    if (initializer.empty())
	return;

    try {
	_default = new IPv4(initializer.c_str());
    } catch (InvalidString) {
	error_msg = c_format("Bad IPv4 type value \"%s\".",
			     initializer.c_str());
	xorp_throw(ParseError, error_msg);
    }
    set_has_default();
}

IPv4Template::~IPv4Template()
{
    if (_default != NULL)
	delete _default;
}

string
IPv4Template::default_str() const
{
    if (_default != NULL)
	return _default->str();

    return "";
}

bool
IPv4Template::type_match(const string& s, string& error_msg) const
{
    string tmp = strip_quotes(s);

    if (tmp.empty()) {
	error_msg = "value must be an IP address in dotted decimal form";
	return false;
    }

    try {
	IPv4* ipv4 = new IPv4(tmp.c_str());
	delete ipv4;
    } catch (InvalidString) {
	error_msg = "value must be an IP address in dotted decimal form";
	return false;
    }
    return true;
}


/**************************************************************************
 * IPv4NetTemplate
 **************************************************************************/

IPv4NetTemplate::IPv4NetTemplate(TemplateTree& template_tree,
				 TemplateTreeNode* parent,
				 const string& path, const string& varname,
				 const string& initializer) throw (ParseError)
    : TemplateTreeNode(template_tree, parent, path, varname),
      _default(NULL)
{
    string error_msg;

    if (initializer.empty())
	return;

    try {
	_default = new IPv4Net(initializer.c_str());
    } catch (InvalidString) {
	error_msg = c_format("Bad IPv4Net type value \"%s\".",
			     initializer.c_str());
	xorp_throw(ParseError, error_msg);
    } catch (InvalidNetmaskLength) {
	error_msg = c_format("Illegal IPv4 prefix length in subnet \"%s\".",
			     initializer.c_str());
	xorp_throw(ParseError, error_msg);
    }
    set_has_default();
}

IPv4NetTemplate::~IPv4NetTemplate()
{
    if (_default != NULL)
	delete _default;
}

string
IPv4NetTemplate::default_str() const
{
    if (_default != NULL)
	return _default->str();

    return "";
}

bool
IPv4NetTemplate::type_match(const string& s, string& error_msg) const
{
    string tmp = strip_quotes(s);

    if (tmp.empty()) {
	error_msg = "value must be an IPv4 subnet in address/prefix-length form";
	return false;
    }

    try {
	IPv4Net ipv4net = IPv4Net(tmp.c_str());
	string::size_type slash = tmp.find('/');
	XLOG_ASSERT(slash != string::npos);
	IPv4 ipv4(tmp.substr(0, slash).c_str());
	if (ipv4 != ipv4net.masked_addr()) {
	    error_msg = "there is a mismatch between the masked address value and the prefix length";
	    return false;
	}
    } catch (InvalidString) {
	error_msg = "value must be an IPv4 subnet in address/prefix-length form";
	return false;
    } catch (InvalidNetmaskLength) {
	error_msg = c_format("prefix length must be an integer between "
			     "0 and %u",
			     IPv4::addr_bitlen());
	return false;
    }
    return true;
}


/**************************************************************************
 * IPv4RangeTemplate
 **************************************************************************/

IPv4RangeTemplate::IPv4RangeTemplate(TemplateTree& template_tree,
				 TemplateTreeNode* parent,
				 const string& path, const string& varname,
				 const string& initializer) throw (ParseError)
    : TemplateTreeNode(template_tree, parent, path, varname),
      _default(NULL)
{
    string error_msg;

    if (initializer.empty())
	return;

    try {
	_default = new IPv4Range(initializer.c_str());
    } catch (InvalidString) {
	error_msg = c_format("Bad IPv4Range type value \"%s\".",
			     initializer.c_str());
	xorp_throw(ParseError, error_msg);
    }
    set_has_default();
}

IPv4RangeTemplate::~IPv4RangeTemplate()
{
    if (_default != NULL)
	delete _default;
}

string
IPv4RangeTemplate::default_str() const
{
    if (_default != NULL)
	return _default->str();

    return "";
}

bool
IPv4RangeTemplate::type_match(const string& s, string& error_msg) const
{
    string tmp = strip_quotes(s);

    if (tmp.empty()) {
	error_msg = "invalid format";
	return false;
    }

    try {
	IPv4Range* ipv4range = new IPv4Range(tmp.c_str());
	delete ipv4range;
    } catch (InvalidString) {
	error_msg = "invalid format";
	return false;
    }
    return true;
}


/**************************************************************************
 * IPv6Template
 **************************************************************************/

IPv6Template::IPv6Template(TemplateTree& template_tree,
			   TemplateTreeNode* parent,
			   const string& path, const string& varname,
			   const string& initializer) throw (ParseError)
    : TemplateTreeNode(template_tree, parent, path, varname),
      _default(NULL)
{
    string error_msg;

    if (initializer.empty())
	return;

    try {
	_default = new IPv6(initializer.c_str());
    } catch (InvalidString) {
	error_msg = c_format("Bad IPv6 type value \"%s\".",
			     initializer.c_str());
	xorp_throw(ParseError, error_msg);
    }
    set_has_default();
}

IPv6Template::~IPv6Template()
{
    if (_default != NULL)
	delete _default;
}

string
IPv6Template::default_str() const
{
    if (_default != NULL)
	return _default->str();

    return "";
}

bool
IPv6Template::type_match(const string& s, string& error_msg) const
{
    string tmp = strip_quotes(s);

    if (tmp.empty()) {
	error_msg = "value must be an IPv6 address";
	return false;
    }

    try {
	IPv6* ipv6 = new IPv6(tmp.c_str());
	delete ipv6;
    } catch (InvalidString) {
	error_msg = "value must be an IPv6 address";
	return false;
    }
    return true;
}


/**************************************************************************
 * IPv6NetTemplate
 **************************************************************************/

IPv6NetTemplate::IPv6NetTemplate(TemplateTree& template_tree,
				 TemplateTreeNode* parent,
				 const string& path, const string& varname,
				 const string& initializer) throw (ParseError)
    : TemplateTreeNode(template_tree, parent, path, varname),
      _default(NULL)
{
    string error_msg;

    if (initializer.empty())
	return;

    try {
	_default = new IPv6Net(initializer.c_str());
    } catch (InvalidString) {
	error_msg = c_format("Bad IPv6Net type value \"%s\".",
			     initializer.c_str());
	xorp_throw(ParseError, error_msg);
    } catch (InvalidNetmaskLength) {
	error_msg = c_format("Illegal IPv6 prefix length in subnet \"%s\".",
			     initializer.c_str());
	xorp_throw(ParseError, error_msg);
    }
    set_has_default();
}

IPv6NetTemplate::~IPv6NetTemplate()
{
    if (_default != NULL)
	delete _default;
}

string
IPv6NetTemplate::default_str() const
{
    if (_default != NULL)
	return _default->str();

    return "";
}

bool
IPv6NetTemplate::type_match(const string& s, string& error_msg) const
{
    string tmp = strip_quotes(s);

    if (tmp.empty()) {
	error_msg = "value must be an IPv6 subnet in address/prefix-length "
	    "form";
	return false;
    }

    try {
	IPv6Net ipv6net = IPv6Net(tmp.c_str());
	string::size_type slash = tmp.find('/');
	XLOG_ASSERT(slash != string::npos);
	IPv6 ipv6(tmp.substr(0, slash).c_str());
	if (ipv6 != ipv6net.masked_addr()) {
	    error_msg = "there is a mismatch between the masked address value and the prefix length";
	    return false;
	}
    } catch (InvalidString) {
	error_msg = "value must be an IPv6 subnet in address/prefix-length "
	    "form";
	return false;
    } catch (InvalidNetmaskLength) {
	error_msg = c_format("prefix length must be an integer between "
			     "0 and %u",
			     IPv6::addr_bitlen());
	return false;
    }
    return true;
}


/**************************************************************************
 * IPv6RangeTemplate
 **************************************************************************/

IPv6RangeTemplate::IPv6RangeTemplate(TemplateTree& template_tree,
				 TemplateTreeNode* parent,
				 const string& path, const string& varname,
				 const string& initializer) throw (ParseError)
    : TemplateTreeNode(template_tree, parent, path, varname),
      _default(NULL)
{
    string error_msg;

    if (initializer.empty())
	return;

    try {
	_default = new IPv6Range(initializer.c_str());
    } catch (InvalidString) {
	error_msg = c_format("Bad IPv6Range type value \"%s\".",
			     initializer.c_str());
	xorp_throw(ParseError, error_msg);
    }
    set_has_default();
}

IPv6RangeTemplate::~IPv6RangeTemplate()
{
    if (_default != NULL)
	delete _default;
}

string
IPv6RangeTemplate::default_str() const
{
    if (_default != NULL)
	return _default->str();

    return "";
}

bool
IPv6RangeTemplate::type_match(const string& s, string& error_msg) const
{
    string tmp = strip_quotes(s);

    if (tmp.empty()) {
	error_msg = "invalid format";
	return false;
    }

    try {
	IPv6Range* ipv6range = new IPv6Range(tmp.c_str());
	delete ipv6range;
    } catch (InvalidString) {
	error_msg = "invalid format";
	return false;
    }
    return true;
}


/**************************************************************************
 * MacaddrTemplate
 **************************************************************************/

MacaddrTemplate::MacaddrTemplate(TemplateTree& template_tree,
				 TemplateTreeNode* parent,
				 const string& path, const string& varname,
				 const string& initializer) throw (ParseError)
    : TemplateTreeNode(template_tree, parent, path, varname),
      _default(NULL)
{
    string error_msg;

    if (initializer.empty())
	return;

    try {
	_default = new EtherMac(initializer.c_str());
    } catch (InvalidString) {
	error_msg = c_format("Bad MacAddr type value \"%s\".",
			     initializer.c_str());
	xorp_throw(ParseError, error_msg);
    }
    set_has_default();
}

MacaddrTemplate::~MacaddrTemplate()
{
    if (_default != NULL)
	delete _default;
}

string
MacaddrTemplate::default_str() const
{
    if (_default != NULL)
	return _default->str();

    return "";
}

bool
MacaddrTemplate::type_match(const string& s, string& error_msg) const
{
    string tmp = strip_quotes(s);

    if (tmp.empty()) {
	error_msg = "value must be an MAC address (six hex digits separated "
	    "by colons)";
	return false;
    }

    try {
	EtherMac* mac = new EtherMac(tmp.c_str());
	delete mac;
    } catch (InvalidString) {
	error_msg = "value must be an MAC address (six hex digits separated "
	    "by colons)";
	return false;
    }
    return true;
}


/**************************************************************************
 * UrlFileTemplate
 **************************************************************************/

UrlFileTemplate::UrlFileTemplate(TemplateTree& template_tree,
				 TemplateTreeNode* parent,
				 const string& path, const string& varname,
				 const string& initializer) throw (ParseError)
    : TemplateTreeNode(template_tree, parent, path, varname),
      _default("")
{
    string error_msg;

    if (initializer.empty())
	return;

    if (! type_match(initializer, error_msg)) {
	error_msg = c_format("Bad UrlFile type value \"%s\": %s.",
			     initializer.c_str(), error_msg.c_str());
	xorp_throw(ParseError, error_msg);
    }

    string s = strip_quotes(initializer);
    _default = s;
    set_has_default();
}

bool
UrlFileTemplate::type_match(const string&, string&) const
{
    //
    // If the lexical analyser passed it to us, we can assume its a
    // text value.
    //
    return true;
}


/**************************************************************************
 * UrlFtpTemplate
 **************************************************************************/

UrlFtpTemplate::UrlFtpTemplate(TemplateTree& template_tree,
			       TemplateTreeNode* parent,
			       const string& path, const string& varname,
			       const string& initializer) throw (ParseError)
    : TemplateTreeNode(template_tree, parent, path, varname),
      _default("")
{
    string error_msg;

    if (initializer.empty())
	return;

    if (! type_match(initializer, error_msg)) {
	error_msg = c_format("Bad UrlFtp type value \"%s\": %s.",
			     initializer.c_str(), error_msg.c_str());
	xorp_throw(ParseError, error_msg);
    }

    string s = strip_quotes(initializer);
    _default = s;
    set_has_default();
}

bool
UrlFtpTemplate::type_match(const string&, string&) const
{
    //
    // If the lexical analyser passed it to us, we can assume its a
    // text value.
    //
    return true;
}


/**************************************************************************
 * UrlHttpTemplate
 **************************************************************************/

UrlHttpTemplate::UrlHttpTemplate(TemplateTree& template_tree,
				 TemplateTreeNode* parent,
				 const string& path, const string& varname,
				 const string& initializer) throw (ParseError)
    : TemplateTreeNode(template_tree, parent, path, varname),
      _default("")
{
    string error_msg;

    if (initializer.empty())
	return;

    if (! type_match(initializer, error_msg)) {
	error_msg = c_format("Bad UrlHttp type value \"%s\": %s.",
			     initializer.c_str(), error_msg.c_str());
	xorp_throw(ParseError, error_msg);
    }

    string s = strip_quotes(initializer);
    _default = s;
    set_has_default();
}

bool
UrlHttpTemplate::type_match(const string&, string&) const
{
    //
    // If the lexical analyser passed it to us, we can assume its a
    // text value.
    //
    return true;
}


/**************************************************************************
 * UrlTftpTemplate
 **************************************************************************/

UrlTftpTemplate::UrlTftpTemplate(TemplateTree& template_tree,
				 TemplateTreeNode* parent,
				 const string& path, const string& varname,
				 const string& initializer) throw (ParseError)
    : TemplateTreeNode(template_tree, parent, path, varname),
      _default("")
{
    string error_msg;

    if (initializer.empty())
	return;

    if (! type_match(initializer, error_msg)) {
	error_msg = c_format("Bad UrlTftp type value \"%s\": %s.",
			     initializer.c_str(), error_msg.c_str());
	xorp_throw(ParseError, error_msg);
    }

    string s = strip_quotes(initializer);
    _default = s;
    set_has_default();
}

bool
UrlTftpTemplate::type_match(const string&, string&) const
{
    //
    // If the lexical analyser passed it to us, we can assume its a
    // text value.
    //
    return true;
}


syntax highlighted by Code2HTML, v. 0.9.1