diff options
author | An-Cheng Huang <ancheng@vyatta.com> | 2011-02-28 18:25:01 -0800 |
---|---|---|
committer | An-Cheng Huang <ancheng@vyatta.com> | 2011-02-28 18:25:01 -0800 |
commit | 24b3de8987f622b349cbe14dca99594f2c279902 (patch) | |
tree | 32d2b36144872943726b00ea63c274c0f7107933 | |
parent | 2d0d7bc61e12779a56272f82bc66044a5580e778 (diff) | |
download | vyatta-cfg-24b3de8987f622b349cbe14dca99594f2c279902.tar.gz vyatta-cfg-24b3de8987f622b349cbe14dca99594f2c279902.zip |
add config template abstraction
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | src/cli_bin.cpp | 12 | ||||
-rw-r--r-- | src/cli_cstore.h | 2 | ||||
-rw-r--r-- | src/cli_new.c | 14 | ||||
-rw-r--r-- | src/cli_val.h | 2 | ||||
-rw-r--r-- | src/cnode/cnode.cpp | 25 | ||||
-rw-r--r-- | src/cstore/cstore-c.cpp | 13 | ||||
-rw-r--r-- | src/cstore/cstore-c.h | 3 | ||||
-rw-r--r-- | src/cstore/cstore-varref.cpp | 32 | ||||
-rw-r--r-- | src/cstore/cstore.cpp | 503 | ||||
-rw-r--r-- | src/cstore/cstore.hpp | 36 | ||||
-rw-r--r-- | src/cstore/ctemplate.hpp | 126 | ||||
-rw-r--r-- | src/cstore/unionfs/cstore-unionfs.cpp | 30 | ||||
-rw-r--r-- | src/cstore/unionfs/cstore-unionfs.hpp | 6 |
14 files changed, 446 insertions, 360 deletions
diff --git a/Makefile.am b/Makefile.am index ff384cb..3cd5fde 100644 --- a/Makefile.am +++ b/Makefile.am @@ -57,6 +57,8 @@ vinclude_HEADERS = src/cli_cstore.h vcincdir = $(vincludedir)/cstore vcinc_HEADERS = src/cstore/cstore-c.h vcinc_HEADERS += src/cstore/cstore.hpp +vcinc_HEADERS += src/cstore/cstore-varref.hpp +vcinc_HEADERS += src/cstore/ctemplate.hpp vcuincdir = $(vcincdir)/unionfs vcuinc_HEADERS = src/cstore/unionfs/cstore-unionfs.hpp diff --git a/src/cli_bin.cpp b/src/cli_bin.cpp index 11c63a1..20da924 100644 --- a/src/cli_bin.cpp +++ b/src/cli_bin.cpp @@ -93,11 +93,7 @@ doSet(Cstore& cstore, const vector<string>& path_comps) static void doDelete(Cstore& cstore, const vector<string>& path_comps) { - vtw_def def; - if (!cstore.validateDeletePath(path_comps, def)) { - bye("invalid delete path"); - } - if (!cstore.deleteCfgPath(path_comps, def)) { + if (!cstore.deleteCfgPath(path_comps)) { bye("delete failed\n"); } } @@ -149,11 +145,7 @@ doCopy(Cstore& cstore, const vector<string>& path_comps) static void doComment(Cstore& cstore, const vector<string>& path_comps) { - vtw_def def; - if (!cstore.validateCommentArgs(path_comps, def)) { - bye("invalid comment args\n"); - } - if (!cstore.commentCfgPath(path_comps, def)) { + if (!cstore.commentCfgPath(path_comps)) { bye("comment cfg path failed\n"); } } diff --git a/src/cli_cstore.h b/src/cli_cstore.h index 54da657..5f0b061 100644 --- a/src/cli_cstore.h +++ b/src/cli_cstore.h @@ -133,7 +133,7 @@ const valstruct *get_syntax_self_in_valstruct(vtw_node *vnode); int get_shell_command_output(const char *cmd, char *buf, unsigned int buf_size); int parse_def(vtw_def *defp, const char *path, boolean type_only); -boolean validate_value(vtw_def *def, char *value); +boolean validate_value(const vtw_def *def, char *value); const char *type_to_name(vtw_type_e type); int initialize_output(const char *op); void bye(const char *msg, ...) __attribute__((format(printf, 1, 2), noreturn)); diff --git a/src/cli_new.c b/src/cli_new.c index a5dc082..950eb79 100644 --- a/src/cli_new.c +++ b/src/cli_new.c @@ -502,15 +502,16 @@ valstruct str2val(char *cp) /**************************************************** STATIC FUNCTIONS ****************************************************/ -int char2val_notext(vtw_def *def, int my_type, int my_type2, char *value, valstruct **valp, char *buf); -int char2val_text(vtw_def *def, char *value, valstruct **valp); +int char2val_notext(const vtw_def *def, int my_type, int my_type2, + char *value, valstruct **valp, char *buf); +int char2val_text(const vtw_def *def, char *value, valstruct **valp); /************************************************** char2val: convert string into valstruct verifying the type according to def ****************************************************/ -int char2val(vtw_def *def, char *value, valstruct *valp) +int char2val(const vtw_def *def, char *value, valstruct *valp) { int my_type = def->def_type; int my_type2 = def->def_type2; @@ -549,7 +550,8 @@ int char2val(vtw_def *def, char *value, valstruct *valp) //non-text type processing block -int char2val_notext(vtw_def *def, int my_type, int my_type2, char *value, valstruct **valpp, char *err_buf) +int char2val_notext(const vtw_def *def, int my_type, int my_type2, + char *value, valstruct **valpp, char *err_buf) { valstruct *valp = *valpp; int token; @@ -641,7 +643,7 @@ int char2val_notext(vtw_def *def, int my_type, int my_type2, char *value, valstr return 0; } -int char2val_text(vtw_def *def, char *value, valstruct **valpp) +int char2val_text(const vtw_def *def, char *value, valstruct **valpp) { valstruct *valp = *valpp; char *endp, *cp; @@ -1867,7 +1869,7 @@ void switch_path(first_seg *segp) validates value against type and syntax return TRUE if OK, FALSE otherwise **************************************************/ -boolean validate_value(vtw_def *def, char *cp) +boolean validate_value(const vtw_def *def, char *cp) { int status; boolean ret=TRUE; diff --git a/src/cli_val.h b/src/cli_val.h index cd19471..a3eb201 100644 --- a/src/cli_val.h +++ b/src/cli_val.h @@ -59,7 +59,7 @@ typedef struct { int print_offset; /* for additional optional output information */ } vtw_path; /* vyatta tree walk */ -extern int char2val(vtw_def *def, char *value, valstruct *valp); +extern int char2val(const vtw_def *def, char *value, valstruct *valp); extern int get_value(char **valpp, vtw_path *pathp); extern vtw_node * make_node(vtw_oper_e oper, vtw_node *left, vtw_node *right); diff --git a/src/cnode/cnode.cpp b/src/cnode/cnode.cpp index c88a495..f484fb1 100644 --- a/src/cnode/cnode.cpp +++ b/src/cnode/cnode.cpp @@ -19,6 +19,7 @@ #include <vector> #include <string> #include <algorithm> +#include <memory> #include <cli_cstore.h> #include <cnode/cnode.hpp> @@ -49,15 +50,15 @@ CfgNode::CfgNode(vector<string>& path_comps, char *name, char *val, break; } - vtw_def def; - if (cstore->validateTmplPath(path_comps, false, def)) { + auto_ptr<Ctemplate> def(cstore->parseTmpl(path_comps, false)); + if (def.get()) { // got the def - _is_tag = def.tag; - _is_leaf = (!def.tag && def.def_type != ERROR_TYPE); + _is_tag = def->isTag(); + _is_leaf = (!_is_tag && !def->isTypeless()); // match constructor from cstore (leaf node never _is_value) - _is_value = (def.is_value && !_is_leaf); - _is_multi = def.multi; + _is_value = (def->isValue() && !_is_leaf); + _is_multi = def->isMulti(); /* XXX given the current definition of "default", the concept of * "default" doesn't really apply to config files. @@ -123,8 +124,8 @@ CfgNode::CfgNode(Cstore& cstore, vector<string>& path_comps, * "root", treat it as an intermediate node. */ if (path_comps.size() > 0) { - vtw_def def; - if (cstore.validateTmplPath(path_comps, false, def)) { + auto_ptr<Ctemplate> def(cstore.parseTmpl(path_comps, false)); + if (def.get()) { // got the def if (!cstore.cfgPathExists(path_comps, active)) { // path doesn't exist @@ -132,10 +133,10 @@ CfgNode::CfgNode(Cstore& cstore, vector<string>& path_comps, return; } - _is_value = def.is_value; - _is_tag = def.tag; - _is_leaf = (!def.tag && def.def_type != ERROR_TYPE); - _is_multi = def.multi; + _is_value = def->isValue(); + _is_tag = def->isTag(); + _is_leaf = (!_is_tag && !def->isTypeless()); + _is_multi = def->isMulti(); _is_default = cstore.cfgPathDefault(path_comps, active); _is_deactivated = cstore.cfgPathDeactivated(path_comps, active); cstore.cfgPathGetComment(path_comps, _comment, active); diff --git a/src/cstore/cstore-c.cpp b/src/cstore/cstore-c.cpp index 50828c4..06f0067 100644 --- a/src/cstore/cstore-c.cpp +++ b/src/cstore/cstore-c.cpp @@ -60,19 +60,6 @@ cstore_validate_tmpl_path(void *handle, const char *path_comps[], } int -cstore_validate_tmpl_path_d(void *handle, const char *path_comps[], - int num_comps, int validate_tags, vtw_def *def) -{ - if (handle) { - vector<string> vs; - _get_str_vec(vs, path_comps, num_comps); - Cstore *cs = (Cstore *) handle; - return (cs->validateTmplPath(vs, validate_tags, *def) ? 1 : 0); - } - return 0; -} - -int cstore_cfg_path_exists(void *handle, const char *path_comps[], int num_comps) { if (handle) { diff --git a/src/cstore/cstore-c.h b/src/cstore/cstore-c.h index 4f33672..44f1efd 100644 --- a/src/cstore/cstore-c.h +++ b/src/cstore/cstore-c.h @@ -26,9 +26,6 @@ void *cstore_init(void); void cstore_free(void *handle); int cstore_validate_tmpl_path(void *handle, const char *path_comps[], int num_comps, int validate_tags); -int cstore_validate_tmpl_path_d(void *handle, const char *path_comps[], - int num_comps, int validate_tags, - vtw_def *def); int cstore_cfg_path_exists(void *handle, const char *path_comps[], int num_comps); int cstore_cfg_path_deactivated(void *handle, const char *path_comps[], diff --git a/src/cstore/cstore-varref.cpp b/src/cstore/cstore-varref.cpp index 79ab56f..2878dd0 100644 --- a/src/cstore/cstore-varref.cpp +++ b/src/cstore/cstore-varref.cpp @@ -93,15 +93,15 @@ Cstore::VarRef::process_ref(const vector<string>& ref_comps, string cr_comp= rcomps.front(); rcomps.erase(rcomps.begin()); - vtw_def def; - bool got_tmpl = _cstore->get_parsed_tmpl(pcomps, false, def); + auto_ptr<Ctemplate> def(_cstore->parseTmpl(pcomps, false)); + bool got_tmpl = (def.get() != 0); bool handle_leaf = false; if (cr_comp == "@") { if (!got_tmpl) { // invalid path return; } - if (def.def_type == ERROR_TYPE) { + if (def->isTypeless()) { // no value for typeless node return; } @@ -112,17 +112,17 @@ Cstore::VarRef::process_ref(const vector<string>& ref_comps, * $VAR(@), so use the "at string". */ pcomps.push_back(_at_string); - process_ref(rcomps, pcomps, def.def_type); + process_ref(rcomps, pcomps, def->getType(1)); return; } } if (pcomps.size() < _orig_path_comps.size()) { // within the original path. @ translates to the path comp. pcomps.push_back(_orig_path_comps[pcomps.size()]); - process_ref(rcomps, pcomps, def.def_type); + process_ref(rcomps, pcomps, def->getType(1)); return; } - if (def.is_value || def.tag) { + if (def->isValue() || def->isTag()) { // invalid ref return; } @@ -136,11 +136,12 @@ Cstore::VarRef::process_ref(const vector<string>& ref_comps, return; } pcomps.pop_back(); - if (!_cstore->get_parsed_tmpl(pcomps, false, def)) { + def.reset(_cstore->parseTmpl(pcomps, false)); + if (!def.get()) { // invalid tmpl path return; } - if (def.is_value && def.tag) { + if (def->isTagValue()) { // at "tag value", need to pop one more. if (pcomps.size() == 0) { // invalid path @@ -154,21 +155,21 @@ Cstore::VarRef::process_ref(const vector<string>& ref_comps, // invalid path return; } - if (def.def_type == ERROR_TYPE) { + if (def->isTypeless()) { // no value for typeless node return; } - if (def.is_value) { + if (def->isValue()) { // invalid ref return; } - if (def.tag) { + if (def->isTag()) { // tag node vector<string> cnodes; _cstore->cfgPathGetChildNodes(pcomps, cnodes, _active); for (size_t i = 0; i < cnodes.size(); i++) { pcomps.push_back(cnodes[i]); - process_ref(rcomps, pcomps, def.def_type); + process_ref(rcomps, pcomps, def->getType(1)); pcomps.pop_back(); } } else { @@ -177,7 +178,7 @@ Cstore::VarRef::process_ref(const vector<string>& ref_comps, } } else { // just text. go down 1 level. - if (got_tmpl && def.tag && !def.is_value) { + if (got_tmpl && def->isTagNode()) { // at "tag node". need to go down 1 more level. if (pcomps.size() >= _orig_path_comps.size()) { // already under the original node. invalid ref. @@ -191,7 +192,7 @@ Cstore::VarRef::process_ref(const vector<string>& ref_comps, } if (handle_leaf) { - if (def.multi) { + if (def->isMulti()) { // multi-value node vector<string> vals; if (!_cstore->cfgPathGetValues(pcomps, vals, _active)) { @@ -215,7 +216,8 @@ Cstore::VarRef::process_ref(const vector<string>& ref_comps, return; } pcomps.push_back(val); - _paths.push_back(pair<vector<string>, vtw_type_e>(pcomps, def.def_type)); + _paths.push_back(pair<vector<string>, vtw_type_e>(pcomps, + def->getType(1))); // at leaf. stop recursion. } } diff --git a/src/cstore/cstore.cpp b/src/cstore/cstore.cpp index 39c57cd..f32e6ec 100644 --- a/src/cstore/cstore.cpp +++ b/src/cstore/cstore.cpp @@ -23,6 +23,7 @@ #include <fcntl.h> #include <algorithm> #include <sstream> +#include <memory> // for debian's version comparison algorithm #define APT_COMPATIBILITY 986 @@ -121,22 +122,19 @@ Cstore::createCstore(const string& session_id, string& env) bool Cstore::validateTmplPath(const vector<string>& path_comps, bool validate_vals) { - vtw_def def; // if we can get parsed tmpl, path is valid - return get_parsed_tmpl(path_comps, validate_vals, def); + auto_ptr<Ctemplate> def(get_parsed_tmpl(path_comps, validate_vals)); + return (def.get() != 0); } -/* same as above but return parsed template. - * def: (output) parsed template. +/* same as above but return parsed template. return 0 if invalid/failed. * note: if last path component is "value" (i.e., def.is_value), parsed * template is actually at "full path - 1". see get_parsed_tmpl() for details. */ -bool -Cstore::validateTmplPath(const vector<string>& path_comps, bool validate_vals, - vtw_def& def) +Ctemplate * +Cstore::parseTmpl(const vector<string>& path_comps, bool validate_vals) { - // if we can get parsed tmpl, path is valid - return get_parsed_tmpl(path_comps, validate_vals, def); + return get_parsed_tmpl(path_comps, validate_vals); } /* get parsed template of specified path as a string-string map @@ -147,7 +145,6 @@ bool Cstore::getParsedTmpl(const vector<string>& path_comps, Cstore::MapT<string, string>& tmap, bool allow_val) { - vtw_def def; /* currently this function is used outside actual CLI operations, mainly * from the perl API. since value validation is from the original CLI * implementation, it doesn't seem to behave correctly in such cases, @@ -155,54 +152,55 @@ Cstore::getParsedTmpl(const vector<string>& path_comps, * * anyway, not validating values in the following call. */ - if (!get_parsed_tmpl(path_comps, false, def)) { + auto_ptr<Ctemplate> def(get_parsed_tmpl(path_comps, false)); + if (!def.get()) { return false; } - if (!allow_val && def.is_value) { + if (!allow_val && def->isValue()) { /* note: !allow_val means specified path must terminate at an actual * "node", not a "value". so this fails since path ends in value. * this emulates the original perl API behavior. */ return false; } - if (def.is_value) { + if (def->isValue()) { tmap["is_value"] = "1"; } // make the map - if (def.def_type != ERROR_TYPE) { - tmap["type"] = type_to_name(def.def_type); + if (!def->isTypeless(1)) { + tmap["type"] = def->getTypeName(1); } - if (def.def_type2 != ERROR_TYPE) { - tmap["type2"] = type_to_name(def.def_type2); + if (!def->isTypeless(2)) { + tmap["type2"] = def->getTypeName(2); } - if (def.def_node_help) { - tmap["help"] = def.def_node_help; + if (def->getNodeHelp()) { + tmap["help"] = def->getNodeHelp(); } - if (def.multi) { + if (def->isMulti()) { tmap["multi"] = "1"; - if (def.def_multi > 0) { + if (def->getMultiLimit() > 0) { ostringstream s; - s << def.def_multi; + s << def->getMultiLimit(); tmap["limit"] = s.str(); } - } else if (def.tag) { + } else if (def->isTag()) { tmap["tag"] = "1"; - if (def.def_tag > 0) { + if (def->getTagLimit() > 0) { ostringstream s; - s << def.def_tag; + s << def->getTagLimit(); tmap["limit"] = s.str(); } - } else if (def.def_default) { - tmap["default"] = def.def_default; + } else if (def->getDefault()) { + tmap["default"] = def->getDefault(); } - if (def.def_enumeration) { - tmap["enum"] = def.def_enumeration; + if (def->getEnumeration()) { + tmap["enum"] = def->getEnumeration(); } - if (def.def_allowed) { - tmap["allowed"] = def.def_allowed; + if (def->getAllowed()) { + tmap["allowed"] = def->getAllowed(); } - if (def.def_val_help) { - tmap["val_help"] = def.def_val_help; + if (def->getValHelp()) { + tmap["val_help"] = def->getValHelp(); } return true; } @@ -223,19 +221,23 @@ Cstore::tmplGetChildNodes(const vector<string>& path_comps, } /* delete specified "logical path" from "working config". - * def: parsed template corresponding to logical path path_comps. * return true if successful. otherwise return false. - * note: assume specified path has been validated - * (i.e., validateDeletePath()). */ bool -Cstore::deleteCfgPath(const vector<string>& path_comps, const vtw_def& def) +Cstore::deleteCfgPath(const vector<string>& path_comps) { ASSERT_IN_SESSION; + string terr; + auto_ptr<Ctemplate> def(get_parsed_tmpl(path_comps, false, terr)); + if (!def.get()) { + output_user("%s\n", terr.c_str()); + return false; + } + if (!cfg_path_exists(path_comps, false, true)) { output_user("Nothing to delete (the specified %s does not exist)\n", - (!def.is_value || def.tag) ? "node" : "value"); + (!def->isValue() || def->isTag()) ? "node" : "value"); // treat as success return true; } @@ -247,11 +249,11 @@ Cstore::deleteCfgPath(const vector<string>& path_comps, const vtw_def& def) * 2. no default value * => remove config path */ - if (def.def_default) { + if (def->getDefault()) { // case 1. construct path for value file. SAVE_PATHS; append_cfg_path(path_comps); - if (def.is_value) { + if (def->isValue()) { // last comp is "value". need to go up 1 level. pop_cfg_path(); } @@ -261,7 +263,7 @@ Cstore::deleteCfgPath(const vector<string>& path_comps, const vtw_def& def) * also deactivated. note that unmark_deactivated() succeeds if it's * not marked deactivated. also mark "changed". */ - bool ret = (write_value(def.def_default) && mark_display_default() + bool ret = (write_value(def->getDefault()) && mark_display_default() && unmark_deactivated() && mark_changed_with_ancestors()); if (!ret) { output_user("Failed to set default value during delete\n"); @@ -287,15 +289,15 @@ Cstore::deleteCfgPath(const vector<string>& path_comps, const vtw_def& def) bool ret = false; SAVE_PATHS; append_cfg_path(path_comps); - if (!def.is_value) { + if (!def->isValue()) { // sub-case (2) ret = remove_node(); } else { // last comp is value - if (def.tag) { + if (def->isTag()) { // sub-case (1c) ret = remove_tag(); - } else if (def.multi) { + } else if (def->isMulti()) { // sub-case (1b) pop_cfg_path(); ret = remove_value_from_multi(path_comps[path_comps.size() - 1]); @@ -325,17 +327,17 @@ Cstore::validateSetPath(const vector<string>& path_comps) ASSERT_IN_SESSION; // if we can get parsed tmpl, path is valid - vtw_def def; string terr; - if (!get_parsed_tmpl(path_comps, true, def, terr)) { + auto_ptr<Ctemplate> def(get_parsed_tmpl(path_comps, true, terr)); + if (!def.get()) { output_user("%s\n", terr.c_str()); return false; } bool ret = true; SAVE_PATHS; - if (!def.is_value) { - if (def.def_type != ERROR_TYPE) { + if (!def->isValue()) { + if (!def->isTypeless()) { /* disallow setting value node without value * note: different from old behavior, which only disallow setting a * single-value node without value. now all value nodes @@ -353,7 +355,7 @@ Cstore::validateSetPath(const vector<string>& path_comps) */ append_cfg_path(path_comps); append_tmpl_path(path_comps); - if (!validate_val(&def, "")) { + if (!validate_val(def.get(), "")) { ret = false; } } @@ -362,22 +364,6 @@ Cstore::validateSetPath(const vector<string>& path_comps) return ret; } -/* check if specified "logical path" is valid for "delete" operation - * return true if valid. otherwise return false. - */ -bool -Cstore::validateDeletePath(const vector<string>& path_comps, vtw_def& def) -{ - ASSERT_IN_SESSION; - - string terr; - if (!get_parsed_tmpl(path_comps, false, def, terr)) { - output_user("%s\n", terr.c_str()); - return false; - } - return true; -} - /* check if specified "logical path" is valid for "activate" operation * return true if valid. otherwise return false. */ @@ -386,8 +372,8 @@ Cstore::validateActivatePath(const vector<string>& path_comps) { ASSERT_IN_SESSION; - vtw_def def; - if (!validate_act_deact(path_comps, "activate", def)) { + auto_ptr<Ctemplate> def(validate_act_deact(path_comps, "activate")); + if (!def.get()) { return false; } if (!cfgPathMarkedDeactivated(path_comps)) { @@ -396,7 +382,7 @@ Cstore::validateActivatePath(const vector<string>& path_comps) return false; } bool ret = true; - if (def.is_value && def.tag && def.def_tag > 0) { + if (def->isTagValue() && def->getTagLimit() > 0) { // we are activating a tag, and there is a limit on number of tags. vector<string> cnodes; SAVE_PATHS; @@ -404,10 +390,10 @@ Cstore::validateActivatePath(const vector<string>& path_comps) string t = pop_cfg_path(); // get child nodes, excluding deactivated ones. get_all_child_node_names(cnodes, false, false); - if (def.def_tag <= cnodes.size()) { + if (def->getTagLimit() <= cnodes.size()) { // limit exceeded output_user("Cannot activate \"%s\": number of values exceeds limit " - "(%d allowed)\n", t.c_str(), def.def_tag); + "(%d allowed)\n", t.c_str(), def->getTagLimit()); ret = false; } RESTORE_PATHS; @@ -423,8 +409,8 @@ Cstore::validateDeactivatePath(const vector<string>& path_comps) { ASSERT_IN_SESSION; - vtw_def def; - return validate_act_deact(path_comps, "deactivate", def); + auto_ptr<Ctemplate> def(validate_act_deact(path_comps, "deactivate")); + return (def.get() != 0); } /* check if specified "logical path" is valid for "edit" operation. @@ -437,9 +423,9 @@ Cstore::getEditEnv(const vector<string>& path_comps, string& env) { ASSERT_IN_SESSION; - vtw_def def; string terr; - if (!get_parsed_tmpl(path_comps, false, def, terr)) { + auto_ptr<Ctemplate> def(get_parsed_tmpl(path_comps, false, terr)); + if (!def.get()) { output_user("%s\n", terr.c_str()); return false; } @@ -448,8 +434,7 @@ Cstore::getEditEnv(const vector<string>& path_comps, string& env) * OR * (2) "typeless node" */ - if (!(def.is_value && def.tag) - && !(!def.is_value && def.def_type == ERROR_TYPE)) { + if (!def->isTagValue() && !def->isTypeless()) { // neither "tag value" nor "typeless node" output_user("The \"edit\" command cannot be issued " "at the specified level\n"); @@ -502,19 +487,16 @@ Cstore::getEditUpEnv(string& env) return false; } - /* get_parsed_tmpl() does not allow empty path, so use one component - * from current paths. - */ - vtw_def def; string terr; vector<string> path_comps; - if (!get_parsed_tmpl(path_comps, false, def, terr)) { + auto_ptr<Ctemplate> def(get_parsed_tmpl(path_comps, false, terr)); + if (!def.get()) { // this should not happen since it's using existing levels output_user("%s\n", terr.c_str()); return false; } SAVE_PATHS; - if (def.is_value && def.tag) { + if (def->isTagValue()) { // edit level is at "tag value". go up 1 extra level. pop_cfg_path(); pop_tmpl_path(); @@ -574,9 +556,13 @@ Cstore::getCompletionEnv(const vector<string>& comps, string& env) bool ret = false; SAVE_PATHS; do { - vtw_def def; + bool is_typeless = true; + bool is_leaf_value = false; + bool is_value = false; + auto_ptr<Ctemplate> def; if (pcomps.size() > 0) { - if (!get_parsed_tmpl(pcomps, false, def)) { + def.reset(get_parsed_tmpl(pcomps, false)); + if (!def.get()) { // invalid path break; } @@ -586,16 +572,20 @@ Cstore::getCompletionEnv(const vector<string>& comps, string& env) } append_cfg_path(pcomps); append_tmpl_path(pcomps); + is_typeless = def->isTypeless(); + is_leaf_value = def->isLeafValue(); + is_value = def->isValue(); } else { - // we are at root. simulate a typeless node. - def.def_type = ERROR_TYPE; - def.tag = def.multi = def.is_value = 0; + /* we are at root. default values simulate a typeless node so nop. + * note that in this case def is "empty", so must ensure that it's + * not used. + */ } /* at this point, cfg and tmpl paths are constructed up to the comp * before last_comp, and def is parsed. */ - if (def.is_value && !def.tag) { + if (is_leaf_value) { // invalid path (this means the comp before last_comp is a leaf value) break; } @@ -605,7 +595,7 @@ Cstore::getCompletionEnv(const vector<string>& comps, string& env) string comp_help; vector<pair<string, string> > help_pairs; bool last_comp_val = true; - if (def.def_type == ERROR_TYPE || def.is_value) { + if (is_typeless || is_value) { /* path so far is at a typeless node OR a tag value (tag already * checked above): * completions: from tmpl children. @@ -615,6 +605,9 @@ Cstore::getCompletionEnv(const vector<string>& comps, string& env) * * note: for such completions, we filter non-existent nodes if * necessary. + * + * also, the "root" node case above will reach this block, so + * must not use def in this block. */ vector<string> ufvec; if (exists_only) { @@ -638,9 +631,9 @@ Cstore::getCompletionEnv(const vector<string>& comps, string& env) for (size_t i = 0; i < comp_vals.size(); i++) { pair<string, string> hpair(comp_vals[i], ""); push_tmpl_path(hpair.first); - vtw_def cdef; - if (tmpl_parse(cdef) && cdef.def_node_help) { - hpair.second = cdef.def_node_help; + auto_ptr<Ctemplate> cdef(tmpl_parse()); + if (cdef.get() && cdef->getNodeHelp()) { + hpair.second = cdef->getNodeHelp(); } else { hpair.second = "<No help text available>"; } @@ -653,9 +646,11 @@ Cstore::getCompletionEnv(const vector<string>& comps, string& env) /* path so far is at a "value node". * note: follow the original implementation and don't filter * non-existent values for such completions + * + * also, cannot be "root" node if we reach here, so def can be used. */ // first, handle completions. - if (def.tag) { + if (def->isTag()) { // it's a "tag node". get completions from tag values. get_all_child_node_names(comp_vals, false, true); } else { @@ -667,7 +662,7 @@ Cstore::getCompletionEnv(const vector<string>& comps, string& env) * "enumeration" * "$VAR(@) in ..." */ - if (def.def_enumeration || def.def_allowed) { + if (def->getEnumeration() || def->getAllowed()) { /* do "enumeration" or "allowed". * note: emulate original implementation and set up COMP_WORDS and * COMP_CWORD environment variables. these are needed by some @@ -682,10 +677,10 @@ Cstore::getCompletionEnv(const vector<string>& comps, string& env) cmd_str += (" '" + comps[i] + "'"); } cmd_str += "); "; - if (def.def_enumeration) { - cmd_str += (C_ENUM_SCRIPT_DIR + "/" + def.def_enumeration); + if (def->getEnumeration()) { + cmd_str += (C_ENUM_SCRIPT_DIR + "/" + def->getEnumeration()); } else { - string astr = def.def_allowed; + string astr = def->getAllowed(); shell_escape_squotes(astr); cmd_str += "_cstore_internal_allowed () { eval '"; cmd_str += astr; @@ -711,10 +706,10 @@ Cstore::getCompletionEnv(const vector<string>& comps, string& env) * shell into an array of values. */ free(buf); - } else if (def.actions[syntax_act].vtw_list_head) { + } else if (def->getActions(syntax_act)->vtw_list_head) { // look for "self ref in values" from syntax const valstruct *vals = get_syntax_self_in_valstruct( - def.actions[syntax_act].vtw_list_head); + def->getActions(syntax_act)->vtw_list_head); if (vals) { if (vals->cnt == 0 && vals->val) { comp_vals.push_back(vals->val); @@ -729,23 +724,23 @@ Cstore::getCompletionEnv(const vector<string>& comps, string& env) } // now handle help. - if (def.def_comp_help) { + if (def->getCompHelp()) { // "comp_help" exists. - comp_help = def.def_comp_help; + comp_help = def->getCompHelp(); shell_escape_squotes(comp_help); } - if (def.def_val_help) { + if (def->getValHelp()) { // has val_help. first separate individual lines. size_t start = 0, i = 0; vector<string> vhelps; - for (i = 0; def.def_val_help[i]; i++) { - if (def.def_val_help[i] == '\n') { - vhelps.push_back(string(&(def.def_val_help[start]), i - start)); + for (i = 0; (def->getValHelp())[i]; i++) { + if ((def->getValHelp())[i] == '\n') { + vhelps.push_back(string(&((def->getValHelp())[start]), i - start)); start = i + 1; } } if (start < i) { - vhelps.push_back(string(&(def.def_val_help[start]), i - start)); + vhelps.push_back(string(&((def->getValHelp())[start]), i - start)); } // process each line @@ -753,15 +748,15 @@ Cstore::getCompletionEnv(const vector<string>& comps, string& env) size_t sc; if ((sc = vhelps[i].find(';')) == vhelps[i].npos) { // no ';' - if (i == 0 && def.def_type != ERROR_TYPE) { + if (i == 0 && !def->isTypeless(1)) { // first val_help. pair with "type". help_pairs.push_back(pair<string, string>( - type_to_name(def.def_type), vhelps[i])); + def->getTypeName(1), vhelps[i])); } - if (i == 1 && def.def_type2 != ERROR_TYPE) { - // second val_help. pair with "type2". + if (i == 1 && !def->isTypeless(2)) { + // second val_help. pair with second "type". help_pairs.push_back(pair<string, string>( - type_to_name(def.def_type2), vhelps[i])); + def->getTypeName(2), vhelps[i])); } } else { // ';' at index sc @@ -770,13 +765,17 @@ Cstore::getCompletionEnv(const vector<string>& comps, string& env) vhelps[i].substr(sc + 1))); } } - } else if (def.def_type && def.def_node_help) { + } else if (!def->isTypeless(1) && def->getNodeHelp()) { // simple case. just use "type" and "help" - help_pairs.push_back(pair<string, string>(type_to_name(def.def_type), - def.def_node_help)); + help_pairs.push_back(pair<string, string>(def->getTypeName(1), + def->getNodeHelp())); } } + /* from this point on cannot use def (since the "root" node case + * can reach here). + */ + // this var is the array of possible completions env = (C_ENV_SHAPI_COMP_VALS + "=("); for (size_t i = 0; i < comp_vals.size(); i++) { @@ -896,61 +895,6 @@ Cstore::validateMoveArgs(const vector<string>& args) return ret; } -/* check if specified "arguments" is valid for "comment" operation - * return true if valid. otherwise return false. - */ -bool -Cstore::validateCommentArgs(const vector<string>& args, vtw_def& def) -{ - ASSERT_IN_SESSION; - - /* separate path from comment. - * follow the original implementation: the last arg is the comment, and - * everything else is part of the path. - */ - vector<string> path_comps(args); - string comment = args.back(); - path_comps.pop_back(); - - // check the path - string terr; - if (!get_parsed_tmpl(path_comps, false, def, terr)) { - output_user("%s\n", terr.c_str()); - return false; - } - // here we want to include deactivated nodes - if (!cfg_path_exists(path_comps, false, true)) { - output_user("The specified config node does not exist\n"); - return false; - } - if (def.is_value && !def.tag) { - /* XXX differ from the original implementation, which allows commenting - * on a "value" BUT silently "promote" the comment to the parent - * "node". this will probably create confusion for the user. - * - * just disallow such cases here. - */ - output_user("Cannot comment on config values\n"); - return false; - } - if (def.tag && !def.is_value) { - /* XXX follow original implementation and disallow comment on a - * "tag node". this is because "show" does not display such - * comments (see bug 5794). - */ - output_user("Cannot add comment at this level\n"); - return false; - } - if (comment.find_first_of('*') != string::npos) { - // don't allow '*'. this is due to config files using C-style /**/ - // comments. this probably belongs to lower-level, but we are enforcing - // it here. - output_user("Cannot use the '*' character in a comment\n"); - return false; - } - return true; -} - /* perform rename in "working config" according to specified args. * return true if successful. otherwise return false. * note: assume args are already validated (i.e., validateRenameArgs()). @@ -1000,15 +944,56 @@ Cstore::copyCfgPath(const vector<string>& args) * return true if valid. otherwise return false. */ bool -Cstore::commentCfgPath(const vector<string>& args, const vtw_def& def) +Cstore::commentCfgPath(const vector<string>& args) { ASSERT_IN_SESSION; - // separate path from comment + /* separate path from comment. + * follow the original implementation: the last arg is the comment, and + * everything else is part of the path. + */ vector<string> path_comps(args); string comment = args.back(); path_comps.pop_back(); + // check the path + string terr; + auto_ptr<Ctemplate> def(get_parsed_tmpl(path_comps, false, terr)); + if (!def.get()) { + output_user("%s\n", terr.c_str()); + return false; + } + // here we want to include deactivated nodes + if (!cfg_path_exists(path_comps, false, true)) { + output_user("The specified config node does not exist\n"); + return false; + } + if (def->isLeafValue()) { + /* XXX differ from the original implementation, which allows commenting + * on a "value" BUT silently "promote" the comment to the parent + * "node". this will probably create confusion for the user. + * + * just disallow such cases here. + */ + output_user("Cannot comment on config values\n"); + return false; + } + if (def->isTagNode()) { + /* XXX follow original implementation and disallow comment on a + * "tag node". this is because "show" does not display such + * comments (see bug 5794). + */ + output_user("Cannot add comment at this level\n"); + return false; + } + if (comment.find_first_of('*') != string::npos) { + // don't allow '*'. this is due to config files using C-style /**/ + // comments. this probably belongs to lower-level, but we are enforcing + // it here. + output_user("Cannot use the '*' character in a comment\n"); + return false; + } + SAVE_PATHS; append_cfg_path(path_comps); bool ret; @@ -1344,8 +1329,8 @@ Cstore::cfgPathGetValueDA(const vector<string>& path_comps, string& value, ASSERT_IN_SESSION; } - vtw_def def; - if (!get_parsed_tmpl(path_comps, false, def)) { + auto_ptr<Ctemplate> def(get_parsed_tmpl(path_comps, false)); + if (!def.get()) { // invalid node return false; } @@ -1355,7 +1340,7 @@ Cstore::cfgPathGetValueDA(const vector<string>& path_comps, string& value, * original API will return a single string that includes all values. * this new function will return failure in such cases. */ - if (def.is_value || def.multi || def.tag || def.def_type == ERROR_TYPE) { + if (!def->isSingleLeafNode()) { // specified path is not a single-value node return false; } @@ -1405,8 +1390,8 @@ Cstore::cfgPathGetValuesDA(const vector<string>& path_comps, ASSERT_IN_SESSION; } - vtw_def def; - if (!get_parsed_tmpl(path_comps, false, def)) { + auto_ptr<Ctemplate> def(get_parsed_tmpl(path_comps, false)); + if (!def.get()) { // invalid node return false; } @@ -1416,7 +1401,7 @@ Cstore::cfgPathGetValuesDA(const vector<string>& path_comps, * original API will return the node's value. this new function * will return failure in such cases. */ - if (def.is_value || !def.multi || def.tag || def.def_type == ERROR_TYPE) { + if (!def->isMultiLeafNode()) { // specified path is not a multi-value node return false; } @@ -1551,8 +1536,8 @@ Cstore::cfgPathDefault(const vector<string>& path_comps, bool active_cfg) bool Cstore::cfgPathEffective(const vector<string>& path_comps) { - vtw_def def; - if (!validateTmplPath(path_comps, false, def)) { + auto_ptr<Ctemplate> def(parseTmpl(path_comps, false)); + if (!def.get()) { // invalid path return false; } @@ -1573,10 +1558,10 @@ Cstore::cfgPathEffective(const vector<string>& path_comps) append_cfg_path(path_comps); if (!in_active && in_work) { // check if case (2) - ret = marked_committed(def, true); + ret = marked_committed(def.get(), true); } else if (in_active && !in_work) { // check if case (3) - ret = !marked_committed(def, false); + ret = !marked_committed(def.get(), false); } RESTORE_PATHS; @@ -1765,15 +1750,12 @@ Cstore::setVarRef(const string& ref_str, const string& value, bool to_active) bool ret = false; if (vref.getSetPath(pcomps)) { reset_paths(); - vtw_def def; - if (get_parsed_tmpl(pcomps, false, def)) { - if (!def.is_value && !def.tag && !def.multi - && def.def_type != ERROR_TYPE) { - // currently only support single-value node - append_cfg_path(pcomps); - if (write_value(value, to_active)) { - ret = true; - } + auto_ptr<Ctemplate> def(get_parsed_tmpl(pcomps, false)); + if (def.get() && def->isSingleLeafNode()) { + // currently only support single-value node + append_cfg_path(pcomps); + if (write_value(value, to_active)) { + ret = true; } } } @@ -1859,9 +1841,7 @@ Cstore::loadFile(const char *filename) // "apply" the changes to the working config for (size_t i = 0; i < del_list.size(); i++) { - vtw_def def; - if (!validateDeletePath(del_list[i], def) - || !deleteCfgPath(del_list[i], def)) { + if (!deleteCfgPath(del_list[i])) { print_str_vec("Delete [", "] failed\n", del_list[i], "'"); } } @@ -1871,9 +1851,7 @@ Cstore::loadFile(const char *filename) } } for (size_t i = 0; i < com_list.size(); i++) { - vtw_def def; - if (!validateCommentArgs(com_list[i], def) - || !commentCfgPath(com_list[i], def)) { + if (!commentCfgPath(com_list[i])) { print_str_vec("Comment [", "] failed\n", com_list[i], "'"); } } @@ -2043,23 +2021,23 @@ Cstore::append_tmpl_path(const vector<string>& path_comps, bool& is_tag) * then template at the path is parsed. * path_comps: vector of path components. * validate_vals: whether to validate all "values" along specified path. - * def: (output) parsed template. * error: (output) error message if failed. - * return false if invalid template path. otherwise return true. + * return parsed template if successful. otherwise return 0. * note: - * also, if last path component is value (i.e., def.is_value), the template + * also, if last path component is value (i.e., isValue()), the template * parsed is actually at "full path - 1". */ -bool +Ctemplate * Cstore::get_parsed_tmpl(const vector<string>& path_comps, bool validate_vals, - vtw_def& def, string& error) + string& error) { + Ctemplate *rtmpl = 0; // default error message error = "The specified configuration node is not valid"; if (tmpl_path_at_root() && path_comps.size() == 0) { // empty path not valid - return false; + return rtmpl; } /* note: this function may be invoked recursively (depth 1) when @@ -2088,7 +2066,6 @@ Cstore::get_parsed_tmpl(const vector<string>& path_comps, bool validate_vals, } } } - bool ret = false; do { /* cases for template path: * (1) valid path ending in "actual node", i.e., typeless node, tag node, @@ -2125,7 +2102,7 @@ Cstore::get_parsed_tmpl(const vector<string>& path_comps, bool validate_vals, * pop it. */ pop_tmpl_path(); - if (!validate_val(NULL, (*pcomps)[i])) { + if (!validate_val(0, (*pcomps)[i])) { // invalid value error = "Value validation failed"; valid = false; @@ -2154,19 +2131,20 @@ Cstore::get_parsed_tmpl(const vector<string>& path_comps, bool validate_vals, * we haven't done anything yet. */ if (pcomps->size() > 1) { - if (tmpl_parse(def)) { - if (def.tag || def.multi || def.def_type != ERROR_TYPE) { + auto_ptr<Ctemplate> ttmpl(tmpl_parse()); + if (ttmpl.get()) { + if (ttmpl->isTag() || ttmpl->isMulti() || !ttmpl->isTypeless()) { // case (2). last component is "value". if (validate_vals) { // validate value - if (!validate_val(&def, (*pcomps)[pcomps->size() - 1])) { + if (!validate_val(ttmpl.get(), (*pcomps)[pcomps->size() - 1])) { // invalid value error = "Value validation failed"; break; } } - def.is_value = 1; - ret = true; + rtmpl = ttmpl.release(); + rtmpl->setIsValue(true); break; } } @@ -2181,12 +2159,11 @@ Cstore::get_parsed_tmpl(const vector<string>& path_comps, bool validate_vals, // no need to push cfg path (only needed for validate_val()) if (tmpl_node_exists()) { // case (1). last component is "node". - if (!tmpl_parse(def)) { + if (!(rtmpl = tmpl_parse())) { exit_internal("failed to parse tmpl [%s]\n", tmpl_path_to_str().c_str()); } - def.is_value = 0; - ret = true; + rtmpl->setIsValue(false); break; } // case (3) (fall through) @@ -2196,21 +2173,21 @@ Cstore::get_parsed_tmpl(const vector<string>& path_comps, bool validate_vals, } else { restore_paths(not_validating); } - return ret; + return rtmpl; } /* check if specified "logical path" is valid for "activate" or * "deactivate" operation. - * return true if valid. otherwise return false. + * return parsed template if valid. otherwise return 0. */ -bool -Cstore::validate_act_deact(const vector<string>& path_comps, const string& op, - vtw_def& def) +Ctemplate * +Cstore::validate_act_deact(const vector<string>& path_comps, const string& op) { string terr; - if (!get_parsed_tmpl(path_comps, false, def, terr)) { + auto_ptr<Ctemplate> def(get_parsed_tmpl(path_comps, false, terr)); + if (!def.get()) { output_user("%s\n", terr.c_str()); - return false; + return 0; } { /* XXX this is a temporary workaround for bug 5708, which should be @@ -2218,24 +2195,25 @@ Cstore::validate_act_deact(const vector<string>& path_comps, const string& op, * resolved (see bug for more details). once those are resolved, * this workaround should be removed and the bug fixed properly. */ - if (!def.tag && def.def_type != ERROR_TYPE) { + if (!def->isTag() && !def->isTypeless()) { output_user("Cannot %s a leaf configuration node\n", op.c_str()); - return false; + return 0; } } - if (def.is_value && !def.tag) { + if (def->isLeafValue()) { /* last component is a value of a single- or multi-value node (i.e., * a leaf value) => not allowed */ output_user("Cannot %s a leaf configuration value\n", op.c_str()); - return false; + return 0; } if (!cfg_path_exists(path_comps, false, true)) { output_user("Nothing to %s (the specified %s does not exist)\n", - op.c_str(), (!def.is_value || def.tag) ? "node" : "value"); - return false; + op.c_str(), + (!def->isValue() || def->isTag()) ? "node" : "value"); + return 0; } - return true; + return def.release(); } /* check if specified args is valid for "rename" or "copy" operation. @@ -2262,13 +2240,13 @@ Cstore::validate_rename_copy(const vector<string>& args, const string& op) vector<string> ppath; ppath.push_back(otagnode); ppath.push_back(otagval); - vtw_def def; string terr; - if (!get_parsed_tmpl(ppath, false, def, terr)) { + auto_ptr<Ctemplate> def(get_parsed_tmpl(ppath, false, terr)); + if (!def.get()) { output_user("%s\n", terr.c_str()); return false; } - if (!def.is_value || !def.tag) { + if (!def->isTagValue()) { // can only rename "tagnode tagvalue" output_user("Cannot %s under \"%s\"\n", op.c_str(), otagnode.c_str()); return false; @@ -2287,7 +2265,8 @@ Cstore::validate_rename_copy(const vector<string>& args, const string& op) ntagnode.c_str(), ntagval.c_str()); return false; } - if (!get_parsed_tmpl(ppath, true, def, terr)) { + def.reset(get_parsed_tmpl(ppath, true, terr)); + if (!def.get()) { output_user("%s\n", terr.c_str()); return false; } @@ -2359,7 +2338,7 @@ bool Cstore::set_cfg_path(const vector<string>& path_comps, bool output) { vector<string> ppath; - vtw_def def; + auto_ptr<Ctemplate> def; bool ret = true; bool path_exists = true; // do the set from the top down @@ -2369,7 +2348,8 @@ Cstore::set_cfg_path(const vector<string>& path_comps, bool output) ppath.push_back(path_comps[i]); // get template at this level - if (!get_parsed_tmpl(ppath, false, def)) { + def.reset(get_parsed_tmpl(ppath, false)); + if (!def.get()) { output_internal("paths[%s,%s]\n", cfg_path_to_str().c_str(), tmpl_path_to_str().c_str()); for (size_t i = 0; i < ppath.size(); i++) { @@ -2388,16 +2368,16 @@ Cstore::set_cfg_path(const vector<string>& path_comps, bool output) append_cfg_path(ppath); append_tmpl_path(ppath); - if (!def.is_value) { + if (!def->isValue()) { // this level is a "node" if (!add_node() || !create_default_children()) { ret = false; break; } - } else if (def.tag) { + } else if (def->isTag()) { // this level is a "tag value". // add the tag, taking the max tag limit into consideration. - if (!add_tag(def) || !create_default_children()) { + if (!add_tag(def->getTagLimit()) || !create_default_children()) { ret = false; break; } @@ -2405,10 +2385,10 @@ Cstore::set_cfg_path(const vector<string>& path_comps, bool output) // this level is a "value" of a single-/multi-value node. // go up 1 level to get the node. pop_cfg_path(); - if (def.multi) { + if (def->isMulti()) { // value of multi-value node. // add the value, taking the max multi limit into consideration. - if (!add_value_to_multi(def, ppath.back())) { + if (!add_value_to_multi(def->getMultiLimit(), ppath.back())) { ret = false; break; } @@ -2428,7 +2408,7 @@ Cstore::set_cfg_path(const vector<string>& path_comps, bool output) } RESTORE_PATHS; // if "break" was hit - if (ret && def.is_value && def.def_default) { + if (ret && def->isValue() && def->getDefault()) { /* a node with default has been explicitly set. needs to be marked * as non-default for display purposes. * @@ -2681,15 +2661,15 @@ Cstore::cfg_value_exists(const string& value, bool active_cfg) * not the value. */ bool -Cstore::validate_val(const vtw_def *def, const string& value) +Cstore::validate_val(const Ctemplate *def, const string& value) { - vtw_def ndef; + auto_ptr<Ctemplate> ndef; if (!def) { - if (!tmpl_parse(ndef)) { + ndef.reset(tmpl_parse()); + if (!(def = ndef.get())) { exit_internal("failed to parse tmpl [%s]\n", tmpl_path_to_str().c_str()); } - def = &ndef; - if (def->def_type == ERROR_TYPE) { + if (def->isTypeless()) { // not a value node exit_internal("validating non-value node [%s]\n", tmpl_path_to_str().c_str()); @@ -2701,7 +2681,7 @@ Cstore::validate_val(const vtw_def *def, const string& value) char *vbuf = (char *) malloc(vlen + 1); strncpy(vbuf, value.c_str(), vlen + 1); vbuf[vlen] = 0; - bool ret = validate_val_impl((vtw_def *) def, vbuf); + bool ret = validate_val_impl(def, vbuf); free(vbuf); return ret; } @@ -2712,7 +2692,7 @@ Cstore::validate_val(const vtw_def *def, const string& value) * already exists. */ bool -Cstore::add_tag(const vtw_def& def) +Cstore::add_tag(unsigned int tlimit) { string t = pop_cfg_path(); vector<string> cnodes; @@ -2720,23 +2700,18 @@ Cstore::add_tag(const vtw_def& def) get_all_child_node_names(cnodes, false, false); bool ret = false; do { - if (def.def_tag > 0 && def.def_tag <= cnodes.size()) { + if (tlimit > 0 && tlimit <= cnodes.size()) { // limit exceeded output_user("Cannot set node \"%s\": number of values exceeds limit" - "(%d allowed)\n", t.c_str(), def.def_tag); + "(%d allowed)\n", t.c_str(), tlimit); break; } - /* XXX the following is the original logic, which is wrong since def_tag - * is unsigned. + /* XXX the original implementation contains special case where the + * previous tag should be replaced. this is probably unnecessary since + * "rename" can be used for tag node anyway. also the implementation + * used -1 as the limit for the special case, which can't work since + * the limit is unsigned. ignore the special case for now. */ - if (def.def_tag < 0 && cnodes.size() == 1) { - /* XXX special case in the original implementation where the previous - * tag should be replaced. this is probably unnecessary since - * "rename" can be used for tag node anyway. - */ - ret = rename_child_node(cnodes[0], t); - break; - } // neither of the above. just add the tag. ret = add_child_node(t); } while (0); @@ -2750,7 +2725,7 @@ Cstore::add_tag(const vtw_def& def) * not configured for the node. */ bool -Cstore::add_value_to_multi(const vtw_def& def, const string& value) +Cstore::add_value_to_multi(unsigned int mlimit, const string& value) { // get current values vector<string> vvec; @@ -2770,10 +2745,10 @@ Cstore::add_value_to_multi(const vtw_def& def, const string& value) * * for now just apply the limit for anything >= 1. */ - if (def.def_multi >= 1 && vvec.size() >= def.def_multi) { + if (mlimit >= 1 && vvec.size() >= mlimit) { // limit exceeded output_user("Cannot set value \"%s\": number of values exceeded " - "(%d allowed)\n", value.c_str(), def.def_multi); + "(%d allowed)\n", value.c_str(), mlimit); return false; } @@ -2817,12 +2792,12 @@ Cstore::create_default_children() bool ret = true; for (size_t i = 0; i < tcnodes.size(); i++) { push_tmpl_path(tcnodes[i]); - vtw_def def; - if (tmpl_node_exists() && tmpl_parse(def)) { - if (def.def_default) { + if (tmpl_node_exists()) { + auto_ptr<Ctemplate> def(tmpl_parse()); + if (def.get() && def->getDefault()) { // has default value. set it. push_cfg_path(tcnodes[i]); - if (!add_node() || !write_value(def.def_default) + if (!add_node() || !write_value(def->getDefault()) || !mark_display_default()) { ret = false; } diff --git a/src/cstore/cstore.hpp b/src/cstore/cstore.hpp index c76cb8f..6a76f31 100644 --- a/src/cstore/cstore.hpp +++ b/src/cstore/cstore.hpp @@ -22,6 +22,7 @@ #include <tr1/unordered_map> #include <cli_cstore.h> +#include <cstore/ctemplate.hpp> /* declare perl internal functions. just need these two so don't include * all the perl headers. @@ -100,8 +101,7 @@ public: //// functions implemented in this base class // these operate on template path bool validateTmplPath(const vector<string>& path_comps, bool validate_vals); - bool validateTmplPath(const vector<string>& path_comps, bool validate_vals, - vtw_def& def); + Ctemplate *parseTmpl(const vector<string>& path_comps, bool validate_vals); bool getParsedTmpl(const vector<string>& path_comps, MapT<string, string>& tmap, bool allow_val = true); void tmplGetChildNodes(const vector<string>& path_comps, @@ -130,8 +130,7 @@ public: bool validateSetPath(const vector<string>& path_comps); bool setCfgPath(const vector<string>& path_comps); // delete - bool validateDeletePath(const vector<string>& path_comps, vtw_def& def); - bool deleteCfgPath(const vector<string>& path_comps, const vtw_def& def); + bool deleteCfgPath(const vector<string>& path_comps); // activate (actually "unmark deactivated" since it is 2-state, not 3) bool validateActivatePath(const vector<string>& path_comps); bool unmarkCfgPathDeactivated(const vector<string>& path_comps); @@ -145,8 +144,7 @@ public: bool validateCopyArgs(const vector<string>& args); bool copyCfgPath(const vector<string>& args); // comment - bool validateCommentArgs(const vector<string>& args, vtw_def& def); - bool commentCfgPath(const vector<string>& args, const vtw_def& def); + bool commentCfgPath(const vector<string>& args); // discard bool discardChanges(); // move @@ -340,7 +338,7 @@ private: // these operate on current tmpl path virtual bool tmpl_node_exists() = 0; - virtual bool tmpl_parse(vtw_def& def) = 0; + virtual Ctemplate *tmpl_parse() = 0; // these operate on current work path (or active with "active_cfg") virtual bool remove_node() = 0; @@ -374,10 +372,10 @@ private: virtual bool marked_display_default(bool active_cfg) = 0; // observers during commit operation - virtual bool marked_committed(const vtw_def& def, bool is_set) = 0; + virtual bool marked_committed(const Ctemplate *def, bool is_set) = 0; // these operate on both current tmpl and work paths - virtual bool validate_val_impl(vtw_def *def, char *value) = 0; + virtual bool validate_val_impl(const Ctemplate *def, char *value) = 0; // observers for "edit/tmpl levels" (for "edit"-related operations) /* note that these should be handled in the base class since they @@ -435,15 +433,15 @@ private: // these require full path // (note: get_parsed_tmpl also uses current tmpl path) - bool get_parsed_tmpl(const vector<string>& path_comps, bool validate_vals, - vtw_def& def, string& error); - bool get_parsed_tmpl(const vector<string>& path_comps, bool validate_vals, - vtw_def& def) { + Ctemplate *get_parsed_tmpl(const vector<string>& path_comps, + bool validate_vals, string& error); + Ctemplate *get_parsed_tmpl(const vector<string>& path_comps, + bool validate_vals) { string dummy; - return get_parsed_tmpl(path_comps, validate_vals, def, dummy); + return get_parsed_tmpl(path_comps, validate_vals, dummy); }; - bool validate_act_deact(const vector<string>& path_comps, const string& op, - vtw_def& def); + Ctemplate *validate_act_deact(const vector<string>& path_comps, + const string& op); bool validate_rename_copy(const vector<string>& args, const string& op); bool conv_move_args_for_rename(const vector<string>& args, vector<string>& edit_path_comps, @@ -465,8 +463,8 @@ private: vector<string> vvec(1, value); return write_value_vec(vvec, active_cfg); }; - bool add_tag(const vtw_def& def); - bool add_value_to_multi(const vtw_def& def, const string& value); + bool add_tag(unsigned int tlimit); + bool add_value_to_multi(unsigned int mlimit, const string& value); bool add_child_node(const string& name) { push_cfg_path(name); bool ret = add_node(); @@ -480,7 +478,7 @@ private: bool cfg_value_exists(const string& value, bool active_cfg); // these operate on both current tmpl and work paths - bool validate_val(const vtw_def *def, const string& value); + bool validate_val(const Ctemplate *def, const string& value); bool create_default_children(); void get_edit_env(string& env); diff --git a/src/cstore/ctemplate.hpp b/src/cstore/ctemplate.hpp new file mode 100644 index 0000000..b9d1d8c --- /dev/null +++ b/src/cstore/ctemplate.hpp @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2010 Vyatta, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _CTEMPLATE_H_ +#define _CTEMPLATE_H_ + +#include <tr1/memory> + +#include <cli_cstore.h> +#include <cstore/cstore.hpp> + +namespace cstore { // begin namespace cstore + +using namespace std; + +class Ctemplate { +public: + Ctemplate(tr1::shared_ptr<vtw_def> def) : _def(def) {}; + ~Ctemplate() {}; + + bool isValue() const { return _def->is_value; }; + bool isMulti() const { return _def->multi; }; + bool isTag() const { return _def->tag; }; + bool isTagNode() const { return (isTag() && !isValue()); }; + bool isTagValue() const { return (isTag() && isValue()); }; + bool isLeafValue() const { return (!isTag() && isValue()); }; + bool isTypeless(size_t tnum = 1) const { + /* note: the current "multi-type" implementation only supports two types. + * the interface here is generalized so it can support more in the + * future. + * + * isTypeless(i) implies isTypeless(j) for all (j > i). + * therefore, isTypeless() means the node has no types at all + * and is equivalent to isTypeless(1). + * + * originally, some "users" of vtw_def checks both is_value and + * def_type for typeless nodes. this should not be necessary so + * here we only check def_type. + */ + return ((tnum == 1) ? (_def->def_type == ERROR_TYPE) + : (_def->def_type2 == ERROR_TYPE)); + }; + bool isSingleLeafNode() const { + return (!isValue() && !isMulti() && !isTag() && !isTypeless()); + }; + bool isSingleLeafValue() const { + // assume isValue implies !isTypeless + return (isValue() && !isMulti() && !isTag()); + }; + bool isMultiLeafNode() const { + // assume isMulti implies !isTag && !isTypeless + return (!isValue() && isMulti()); + }; + bool isMultiLeafValue() const { + // assume isMulti implies !isTag && !isTypeless + return (isValue() && isMulti()); + }; + + size_t getNumTypes() const { + return (isTypeless(1) ? 0 : (isTypeless(2) ? 1 : 2)); + }; + vtw_type_e getType(size_t tnum = 1) const { + return ((tnum == 1) ? _def->def_type : _def->def_type2); + }; + const char *getTypeName(size_t tnum = 1) const { + return type_to_name(getType(tnum)); + }; + const char *getDefault() const { return _def->def_default; }; + const char *getNodeHelp() const { return _def->def_node_help; }; + const char *getEnumeration() const { return _def->def_enumeration; }; + const char *getAllowed() const { return _def->def_allowed; }; + const vtw_list *getActions(vtw_act_type act) const { + return &(_def->actions[act]); + }; + const char *getCompHelp() const { return _def->def_comp_help; }; + const char *getValHelp() const { return _def->def_val_help; }; + unsigned int getTagLimit() const { return _def->def_tag; }; + unsigned int getMultiLimit() const { return _def->def_multi; }; + + void setIsValue(bool is_val) { _def->is_value = is_val; }; + + const vtw_def *getDef() const { + /* XXX this is a hack for code that has not been converted and is still + * using vtw_def directly. this should go away once the transition + * is completed. + */ + return _def.get(); + }; + +private: + /* XXX ideally, template should be parsed directly into this class instead + * of wrapping the vtw_def struct in here. however, the legacy code + * (e.g., commit and code used by commit) still requires vtw_def, so + * need to keep it around for now until the transition is completed. + * + * note that the use of shared_ptr deals with the memory of the vtw_def + * struct *itself*. however, various members of vtw_def are allocated + * dynamically by the parser and were never freed before, i.e., they + * have always been leaked since the beginning. such leaks are not going + * to be fixed by the shared_ptr use here. + * + * once the transition is completed, vtw_def can be eliminated, and + * template data should be stored directly in this class using more + * suitable containers so that memory allocation/deallocation can be + * handled properly. + */ + tr1::shared_ptr<vtw_def> _def; +}; + +} // end namespace cstore + +#endif /* _CTEMPLATE_H_ */ + diff --git a/src/cstore/unionfs/cstore-unionfs.cpp b/src/cstore/unionfs/cstore-unionfs.cpp index 59b5582..b42c8f8 100644 --- a/src/cstore/unionfs/cstore-unionfs.cpp +++ b/src/cstore/unionfs/cstore-unionfs.cpp @@ -415,17 +415,21 @@ UnionfsCstore::tmpl_node_exists() return (b_fs_exists(tmpl_path) && b_fs_is_directory(tmpl_path)); } -/* parse template at current tmpl_path. - * def: for storing parsed template. - * return true if successful. otherwise return false. +/* parse template at current tmpl_path and return an allocated Ctemplate + * pointer if successful. otherwise return 0. */ -bool -UnionfsCstore::tmpl_parse(vtw_def& def) +Ctemplate * +UnionfsCstore::tmpl_parse() { + tr1::shared_ptr<vtw_def> def(new vtw_def); + vtw_def *_def = def.get(); b_fs::path tp = tmpl_path / C_DEF_NAME; - bool ret = (b_fs_exists(tp) && b_fs_is_regular(tp) - && parse_def(&def, tp.file_string().c_str(), 0) == 0); - return ret; + if (_def && b_fs_exists(tp) && b_fs_is_regular(tp) + && parse_def(_def, tp.file_string().c_str(), 0) == 0) { + // succes + return (new Ctemplate(def)); + } + return 0; } bool @@ -905,11 +909,11 @@ UnionfsCstore::cfg_node_changed() * be only one operation on the path). */ bool -UnionfsCstore::marked_committed(const vtw_def& def, bool is_set) +UnionfsCstore::marked_committed(const Ctemplate *def, bool is_set) { b_fs::path cpath = mutable_cfg_path; string com_str = cpath.file_string() + "/"; - if (def.is_value && !def.tag) { + if (def->isLeafValue()) { // path includes leaf value. construct the right string. string val = _unescape_path_name(cpath.filename()); cpath = cpath.parent_path(); @@ -917,7 +921,7 @@ UnionfsCstore::marked_committed(const vtw_def& def, bool is_set) * single-value nodes but not for multi-value nodes for some * reason. the following match current behavior. */ - if (!def.multi) { + if (!def->isMulti()) { val = _escape_path_name(val); } com_str = cpath.file_string() + "/value:" + val; @@ -927,7 +931,7 @@ UnionfsCstore::marked_committed(const vtw_def& def, bool is_set) } bool -UnionfsCstore::validate_val_impl(vtw_def *def, char *value) +UnionfsCstore::validate_val_impl(const Ctemplate *def, char *value) { /* XXX filesystem paths/accesses are completely embedded in var ref lib. * for now, treat the lib as a unionfs-specific implementation. @@ -936,7 +940,7 @@ UnionfsCstore::validate_val_impl(vtw_def *def, char *value) * processing. this is a global var in cli_new.c. */ var_ref_handle = (void *) this; - bool ret = validate_value(def, value); + bool ret = validate_value(def->getDef(), value); var_ref_handle = NULL; return ret; } diff --git a/src/cstore/unionfs/cstore-unionfs.hpp b/src/cstore/unionfs/cstore-unionfs.hpp index 29f6822..82bda2a 100644 --- a/src/cstore/unionfs/cstore-unionfs.hpp +++ b/src/cstore/unionfs/cstore-unionfs.hpp @@ -150,7 +150,7 @@ private: // these operate on current tmpl path bool tmpl_node_exists(); - bool tmpl_parse(vtw_def& def); + Ctemplate *tmpl_parse(); // these operate on current work path bool add_node(); @@ -184,10 +184,10 @@ private: bool marked_display_default(bool active_cfg); // observers during commit operation - bool marked_committed(const vtw_def& def, bool is_set); + bool marked_committed(const Ctemplate *def, bool is_set); // these operate on both current tmpl and work paths - bool validate_val_impl(vtw_def *def, char *value); + bool validate_val_impl(const Ctemplate *def, char *value); // observers for "edit/tmpl levels" (for "edit"-related operations). // note that these should be moved to base class in the future. |