summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAn-Cheng Huang <ancheng@vyatta.com>2007-09-25 15:55:26 -0700
committerAn-Cheng Huang <ancheng@vyatta.com>2007-09-25 15:55:26 -0700
commite9a79a249cec69fc178098d2f75db9389068510a (patch)
tree0e366094b7fecd3988c243fbbb574015e0c900c8 /src
downloadvyatta-cfg-e9a79a249cec69fc178098d2f75db9389068510a.tar.gz
vyatta-cfg-e9a79a249cec69fc178098d2f75db9389068510a.zip
initial import (from eureka /cli) plus new build system.upstream
Diffstat (limited to 'src')
-rw-r--r--src/.gitignore9
-rw-r--r--src/cli_def.l424
-rw-r--r--src/cli_new.c1938
-rw-r--r--src/cli_objects.c343
-rw-r--r--src/cli_objects.h65
-rw-r--r--src/cli_parse.y206
-rw-r--r--src/cli_path_utils.c535
-rw-r--r--src/cli_path_utils.h72
-rw-r--r--src/cli_val.h206
-rw-r--r--src/cli_val.l293
-rw-r--r--src/cli_val_engine.c881
-rw-r--r--src/cli_val_engine.h86
-rw-r--r--src/commit.c1364
-rw-r--r--src/delete.c258
-rw-r--r--src/set.c310
15 files changed, 6990 insertions, 0 deletions
diff --git a/src/.gitignore b/src/.gitignore
new file mode 100644
index 0000000..a2b4c94
--- /dev/null
+++ b/src/.gitignore
@@ -0,0 +1,9 @@
+cli_parse.tab.c
+cli_parse.tab.h
+cli_def.lex.c
+cli_def.tab.c
+cli_def.tab.h
+cli_val.lex.c
+delete
+my_*
+show
diff --git a/src/cli_def.l b/src/cli_def.l
new file mode 100644
index 0000000..41994d6
--- /dev/null
+++ b/src/cli_def.l
@@ -0,0 +1,424 @@
+%{
+#include "cli_val.h"
+#include "cli_parse.h"
+FILE *yy_cli_def_in;
+static void make_def_value(vtw_type_e type);
+static int cli_last_nl_returned=0;
+#define STR_DELTA 2
+%}
+%x str
+%option noyywrap
+%option nounput
+%option never-interactive
+
+/*
+ * Regular expressions of IP and MAC addresses, URLs, etc.
+ */
+
+/*
+ * IPv4 address representation.
+ */
+RE_IPV4_BYTE 25[0-5]|2[0-4][0-9]|[01][0-9][0-9]|([0-9]{1,2})
+RE_IPV4 {RE_IPV4_BYTE}(\.{RE_IPV4_BYTE}){3}
+RE_IPV4_PREFIXLEN (3[012]|[12][0-9]|[0-9])
+RE_IPV4NET {RE_IPV4}"/"{RE_IPV4_PREFIXLEN}
+
+/*
+ * IPv6 address representation in Augmented Backus-Naur Form (ABNF)
+ * as defined in RFC-2234.
+ * IPv6 address representation taken from RFC-3986:
+ *
+ * IPv6address = 6( h16 ":" ) ls32
+ * / "::" 5( h16 ":" ) ls32
+ * / [ h16 ] "::" 4( h16 ":" ) ls32
+ * / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32
+ * / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32
+ * / [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32
+ * / [ *4( h16 ":" ) h16 ] "::" ls32
+ * / [ *5( h16 ":" ) h16 ] "::" h16
+ * / [ *6( h16 ":" ) h16 ] "::"
+ *
+ * h16 = 1*4HEXDIG
+ * ls32 = ( h16 ":" h16 ) / IPv4address
+ * IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet
+ * dec-octet = DIGIT ; 0-9
+ * / %x31-39 DIGIT ; 10-99
+ * / "1" 2DIGIT ; 100-199
+ * / "2" %x30-34 DIGIT ; 200-249
+ * / "25" %x30-35 ; 250-255
+ */
+
+RE_H16 [a-fA-F0-9]{1,4}
+RE_H16_COLON {RE_H16}":"
+RE_LS32 (({RE_H16}":"{RE_H16})|{RE_IPV4})
+RE_IPV6_P1 {RE_H16_COLON}{6}{RE_LS32}
+RE_IPV6_P2 "::"{RE_H16_COLON}{5}{RE_LS32}
+RE_IPV6_P3 ({RE_H16})?"::"{RE_H16_COLON}{4}{RE_LS32}
+RE_IPV6_P4 ({RE_H16_COLON}{0,1}{RE_H16})?"::"{RE_H16_COLON}{3}{RE_LS32}
+RE_IPV6_P5 ({RE_H16_COLON}{0,2}{RE_H16})?"::"{RE_H16_COLON}{2}{RE_LS32}
+RE_IPV6_P6 ({RE_H16_COLON}{0,3}{RE_H16})?"::"{RE_H16_COLON}{1}{RE_LS32}
+RE_IPV6_P7 ({RE_H16_COLON}{0,4}{RE_H16})?"::"{RE_LS32}
+RE_IPV6_P8 ({RE_H16_COLON}{0,5}{RE_H16})?"::"{RE_H16}
+RE_IPV6_P9 ({RE_H16_COLON}{0,6}{RE_H16})?"::"
+RE_IPV6 {RE_IPV6_P1}|{RE_IPV6_P2}|{RE_IPV6_P3}|{RE_IPV6_P4}|{RE_IPV6_P5}|{RE_IPV6_P6}|{RE_IPV6_P7}|{RE_IPV6_P8}|{RE_IPV6_P9}
+RE_IPV6_PREFIXLEN 12[0-8]|1[01][0-9]|[0-9][0-9]?
+RE_IPV6NET {RE_IPV6}"/"{RE_IPV6_PREFIXLEN}
+
+/*
+ * Ethernet MAC address representation.
+ */
+RE_MACADDR [a-fA-F0-9]{1,2}(:[a-fA-F0-9]{1,2}){5}
+
+/*
+ * URL-related regular expressions.
+ *
+ * Implementation is based on the BNF-like specification from:
+ * - RFC-1738: HTTP, FTP, FILE
+ * - RFC-3617: TFTP
+ * - RFC-3986: update of RFC-1738
+ */
+RE_URL {RE_URL_FILE}|{RE_URL_FTP}|{RE_URL_HTTP}|{RE_URL_TFTP}
+
+/*
+ * URL schemeparts for IP based protocols.
+ * Representation taken from RFC-1738, and some of it is updated by RFC-3986.
+ *
+ * login = [ user [ ":" password ] "@" ] hostport
+ * hostport = host [ ":" port ]
+ * host = hostname | hostnumber
+ * hostname = *[ domainlabel "." ] toplabel
+ * domainlabel = alphadigit | alphadigit *[ alphadigit | "-" ] alphadigit
+ * toplabel = alpha | alpha *[ alphadigit | "-" ] alphadigit
+ * alphadigit = alpha | digit
+ * hostnumber = digits "." digits "." digits "." digits
+ * port = digits
+ * user = *[ uchar | ";" | "?" | "&" | "=" ]
+ * password = *[ uchar | ";" | "?" | "&" | "=" ]
+ */
+RE_URL_LOGIN ({RE_URL_USER}(":"{RE_URL_PASSWORD})?"@")?{RE_URL_HOSTPORT}
+RE_URL_HOSTPORT {RE_URL_HOST}(":"{RE_URL_PORT})?
+RE_URL_HOST {RE_URL_HOSTNAME}|{RE_IPV4}|{RE_URL_IP_LITERAL}
+RE_URL_IP_LITERAL "["({RE_IPV6}|{RE_URL_IPV_FUTURE})"]"
+RE_URL_IPV_FUTURE "v"({RE_URL_HEXDIG})+"."({RE_URL_UNRESERVED}|{RE_URL_SUBDELIMS}|":")+
+RE_URL_HOSTNAME ({RE_URL_DOMAINLABEL}".")*{RE_URL_TOPLABEL}
+RE_URL_DOMAINLABEL {RE_URL_ALPHADIGIT}|{RE_URL_ALPHADIGIT}({RE_URL_ALPHADIGIT}|"-")*{RE_URL_ALPHADIGIT}
+RE_URL_TOPLABEL {RE_URL_ALPHA}|{RE_URL_ALPHA}({RE_URL_ALPHADIGIT}|"-")*{RE_URL_ALPHADIGIT}
+RE_URL_ALPHADIGIT {RE_URL_ALPHA}|{RE_URL_DIGIT}
+RE_URL_HOSTNUMBER {RE_URL_DIGITS}"."{RE_URL_DIGITS}"."{RE_URL_DIGITS}"."{RE_URL_DIGITS}
+RE_URL_PORT {RE_URL_DIGITS}
+RE_URL_USER ({RE_URL_UCHAR}|";"|"?"|"&"|"=")*
+RE_URL_PASSWORD ({RE_URL_UCHAR}|";"|"?"|"&"|"=")*
+
+/*
+ * FILE URL regular expression.
+ * Representation taken from RFC-1738.
+ *
+ * fileurl = "file://" [ host | "localhost" ] "/" fpath
+ */
+RE_URL_FILE "file://"({RE_URL_HOST}|"localhost")?"/"{RE_URL_FPATH}
+
+/*
+ * FTP URL regular expression.
+ * Representation taken from RFC-1738.
+ *
+ * ftpurl = "ftp://" login [ "/" fpath [ ";type=" ftptype ] ]
+ * fpath = fsegment *[ "/" fsegment ]
+ * fsegment = *[ uchar | "?" | ":" | "@" | "&" | "=" ]
+ * ftptype = "A" | "I" | "D" | "a" | "i" | "d"
+ */
+RE_URL_FTP "ftp://"{RE_URL_LOGIN}("/"{RE_URL_FPATH}(";type="{RE_URL_FTPTYPE})?)?
+RE_URL_FPATH {RE_URL_FSEGMENT}("/"{RE_URL_FSEGMENT})*
+RE_URL_FSEGMENT ({RE_URL_UCHAR}|"?"|":"|"@"|"&"|"=")*
+RE_URL_FTPTYPE "A"|"I"|"D"|"a"|"i"|"d"
+
+/*
+ * HTTP URL regular expression.
+ * Representation taken from RFC-1738.
+ *
+ * httpurl = "http://" hostport [ "/" hpath [ "?" search ] ]
+ * hpath = hsegment *[ "/" hsegment ]
+ * hsegment = *[ uchar | ";" | ":" | "@" | "&" | "=" ]
+ * search = *[ uchar | ";" | ":" | "@" | "&" | "=" ]
+ */
+RE_URL_HTTP "http://"{RE_URL_HOSTPORT}("/"{RE_URL_HPATH}("?"{RE_URL_SEARCH})?)?
+RE_URL_HPATH {RE_URL_HSEGMENT}("/"{RE_URL_HSEGMENT})*
+RE_URL_HSEGMENT ({RE_URL_UCHAR}|";"|":"|"@"|"&"|"=")*
+RE_URL_SEARCH ({RE_URL_UCHAR}|";"|":"|"@"|"&"|"=")*
+
+/*
+ * TFTP URL regular expression.
+ * Representation taken from RFC-3617.
+ *
+ * tftpURI = "tftp://" host "/" file [ mode ]
+ * mode = ";" "mode=" ( "netascii" / "octet" )
+ * file = *( unreserved / escaped )
+ * host = <as specified by RFC 2732 [3]>
+ * unreserved = <as specified in RFC 2396 [4]>
+ * escaped = <as specified in RFC 2396>
+ */
+RE_URL_TFTP "tftp://"{RE_URL_HOST}"/"{RE_URL_TFTP_FILE}({RE_URL_TFTP_MODE})?
+RE_URL_TFTP_MODE ";""mode="("netascii"|"octet")
+RE_URL_TFTP_FILE ({RE_URL_UNRESERVED}|{RE_URL_ESCAPE})*
+
+/*
+ * URL-related miscellaneous definitions.
+ * Representation taken from RFC-1738 and from RFC-3986.
+ *
+ * lowalpha = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" |
+ * "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" |
+ * "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" |
+ * "y" | "z"
+ * hialpha = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" |
+ * "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" |
+ * "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z"
+ * alpha = lowalpha | hialpha
+ * digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" |
+ * "8" | "9"
+ * safe = "$" | "-" | "_" | "." | "+"
+ * extra = "!" | "*" | "'" | "(" | ")" | ","
+ * national = "{" | "}" | "|" | "\" | "^" | "~" | "[" | "]" | "`"
+ * punctuation = "<" | ">" | "#" | "%" | <">
+ *
+ *
+ * reserved = ";" | "/" | "?" | ":" | "@" | "&" | "="
+ * hex = digit | "A" | "B" | "C" | "D" | "E" | "F" |
+ * "a" | "b" | "c" | "d" | "e" | "f"
+ * escape = "%" hex hex
+ *
+ * unreserved = alpha | digit | safe | extra
+ * uchar = unreserved | escape
+ * xchar = unreserved | reserved | escape
+ * digits = 1*digit
+ *
+ * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
+ * / "*" / "+" / "," / ";" / "="
+ */
+RE_URL_LOWALPHA [a-z]
+RE_URL_HIALPHA [A-Z]
+RE_URL_ALPHA {RE_URL_LOWALPHA}|{RE_URL_HIALPHA}
+RE_URL_DIGIT [0-9]
+RE_URL_SAFE "$"|"-"|"_"|"."|"+"
+RE_URL_EXTRA "!"|"*"|"'"|"("|")"|","
+RE_URL_NATIONAL "{"|"}"|"|"|"\"|"^"|"~"|"["|"]"|"`"
+RE_URL_PUNCTUATION "<"|">"|"#"|"%"|<">
+RE_URL_RESERVED ";"|"/"|"?"|":"|"@"|"&"|"="
+RE_URL_HEXDIG {RE_URL_DIGIT}|[A-F]|[a-f]
+RE_URL_ESCAPE "%"{RE_URL_HEXDIG}{RE_URL_HEXDIG}
+RE_URL_UNRESERVED {RE_URL_ALPHA}|{RE_URL_DIGIT}|{RE_URL_SAFE}|{RE_URL_EXTRA}
+RE_URL_UCHAR {RE_URL_UNRESERVED}|{RE_URL_ESCAPE}
+RE_URL_XCHAR {RE_URL_UNRESERVED}|{RE_URL_RESERVED}|{RE_URL_ESCAPE}
+RE_URL_DIGITS {RE_URL_DIGIT}{1,}
+RE_URL_SUBDELIMS "!"|"$"|"&"|"'"|"("|")"|"*"|"+"|","|";"|"="
+
+
+%%
+ char *string_buf=NULL;
+ char *string_buf_ptr=NULL;
+ int string_len=0, string_buf_len=0;
+ char str_delim=0;
+
+#[^\n]*(\\[ \t]*\n[^\n]*)*\n { /* comment */ }
+\n {yy_cli_def_lineno++;return EOL;}
+<<EOF>> { if(!cli_last_nl_returned) {cli_last_nl_returned=1;return EOL;} yyterminate(); }
+[\`\"] {
+ BEGIN(str);
+ str_delim = yy_cli_def_text[0];
+ string_buf_ptr = string_buf = my_malloc(STR_DELTA + 1, "");
+ string_len = STR_DELTA;
+ string_buf_len = STR_DELTA;
+ }
+<str>[\"\`] {
+ /* maybe a closing quote - all done */
+ if(str_delim == yy_cli_def_text[0]) {
+ BEGIN(INITIAL);
+ *string_buf_ptr = 0;
+ /* return string constant token type and
+ * value to parser
+ */
+ yy_cli_parse_lval.strp = string_buf;
+ return str_delim == '"'?STRING:EX_STRING;
+ }else{
+ *string_buf_ptr++ = yy_cli_def_text[0];
+ goto string_too_long;
+ }
+ }
+
+<str>\n {
+ /* error - unterminated string constant */
+ yy_cli_def_lineno++;
+ }
+
+<str>\\n {*string_buf_ptr++ = '\n';goto string_too_long;}
+<str>\\t {*string_buf_ptr++ = '\t';goto string_too_long;}
+<str>\\r {*string_buf_ptr++ = '\r';goto string_too_long;}
+<str>\\b {*string_buf_ptr++ = '\b';goto string_too_long;}
+<str>\\f {*string_buf_ptr++ = '\f';goto string_too_long;}
+<str>\\\n { yy_cli_def_lineno++; /* continuation => ignore */ }
+
+<str>\\. {
+ *string_buf_ptr++ = yy_cli_def_text[1];
+string_too_long:
+ *string_buf_ptr = 0;
+ /* printf("Cur string |%s|\n", string_buf); */
+ if (!--string_len) {
+ string_buf = my_realloc(string_buf,
+ string_buf_len + STR_DELTA + 1,
+ "cli_def STRING");
+ string_buf_ptr = string_buf + string_buf_len;
+ string_buf_len += STR_DELTA;
+ string_len = STR_DELTA;
+ }
+ }
+<str>[^\\\n\"\`]+ {
+ char *yptr = yy_cli_def_text;
+
+ while ( *yptr ){
+ *string_buf_ptr++ = *yptr++;
+ if (!--string_len) {
+ string_buf = my_realloc(string_buf,
+ string_buf_len + STR_DELTA + 1,
+ "cli_def STRING");
+ string_buf_ptr = string_buf + string_buf_len;
+ string_buf_len += STR_DELTA;
+ string_len = STR_DELTA;
+ }
+ }
+ *string_buf_ptr = 0;
+ }
+
+default: return DEFAULT;
+tag: return TAG;
+type: return TYPE;
+help: return HELP;
+syntax: return SYNTAX;
+commit: return COMMIT;
+check: yy_cli_parse_lval.action = syntax_act; return ACTION;
+delete: yy_cli_parse_lval.action = delete_act; return ACTION;
+update: yy_cli_parse_lval.action = update_act; return ACTION;
+activate: yy_cli_parse_lval.action = activate_act; return ACTION;
+create: yy_cli_parse_lval.action = create_act; return ACTION;
+begin: yy_cli_parse_lval.action = begin_act; return ACTION;
+end: yy_cli_parse_lval.action = end_act; return ACTION;
+multi: return MULTI;
+
+:: {
+ make_def_value(IPV6_TYPE);
+ return VALUE;
+ }
+txt {
+ yy_cli_parse_lval.type = TEXT_TYPE;
+ return TYPE_DEF;
+ }
+
+pattern return PATTERN;
+
+exec return EXEC;
+
+, return COMMA;
+\|\| return OR;
+\&\& return AND;
+\= return ASSIGN;
+\=\= {yy_cli_parse_lval.cond = EQ_COND; return COND;}
+\!\= {yy_cli_parse_lval.cond = NE_COND; return COND;}
+\< {yy_cli_parse_lval.cond = LT_COND; return COND;}
+\> {yy_cli_parse_lval.cond = GT_COND; return COND;}
+\<\= {yy_cli_parse_lval.cond = LE_COND; return COND;}
+\>\= {yy_cli_parse_lval.cond = GE_COND; return COND;}
+in {yy_cli_parse_lval.cond = IN_COND; return COND;}
+\! { return NOT; }
+\$\([^)]+\) {
+ yy_cli_parse_lval.strp = my_strdup(yy_cli_def_text, "TEXT");
+ return VAR;
+ }
+true {
+ make_def_value(BOOL_TYPE);
+ return VALUE;
+ }
+
+false {
+ make_def_value(BOOL_TYPE);
+ return VALUE;
+ }
+
+[0-9]+ {
+ make_def_value(INT_TYPE);
+ return VALUE;
+ }
+
+{RE_IPV4} {
+ make_def_value(IPV4_TYPE);
+ return VALUE;
+ }
+
+{RE_IPV4NET} {
+ make_def_value(IPV4NET_TYPE);
+ return VALUE;
+ }
+
+{RE_IPV6} {
+ make_def_value(IPV6_TYPE);
+ return VALUE;
+ }
+
+{RE_IPV6NET} {
+ make_def_value(IPV6NET_TYPE);
+ return VALUE;
+ }
+
+{RE_MACADDR} {
+ make_def_value(MACADDR_TYPE);
+ return VALUE;
+ }
+u32 {
+ yy_cli_parse_lval.type = INT_TYPE;
+ return TYPE_DEF;
+ }
+
+ipv4 {
+ yy_cli_parse_lval.type = IPV4_TYPE;
+ return TYPE_DEF;
+ }
+
+ipv4net {
+ yy_cli_parse_lval.type = IPV4NET_TYPE;
+ return TYPE_DEF;
+ }
+ipv6 {
+ yy_cli_parse_lval.type = IPV6_TYPE;
+ return TYPE_DEF;
+ }
+
+ipv6net {
+ yy_cli_parse_lval.type = IPV6NET_TYPE;
+ return TYPE_DEF;
+ }
+bool {
+ yy_cli_parse_lval.type = BOOL_TYPE;
+ return TYPE_DEF;
+ }
+macaddr {
+ yy_cli_parse_lval.type = MACADDR_TYPE;
+ return TYPE_DEF;
+ }
+\( return LP;
+\) return RP;
+; return SEMI;
+
+\\\n { yy_cli_def_lineno++; /*whitespace -- continuation => ignore */ }
+
+[ \t]+ /* whitespace */
+
+. {
+ /* everything else is a syntax error */
+ return SYNTAX_ERROR;
+ }
+
+%%
+
+static void make_def_value(vtw_type_e type)
+{
+ memset(&yy_cli_parse_lval.val.val, 0, sizeof(yy_cli_parse_lval.val.val));
+ yy_cli_parse_lval.val.free_me = TRUE;
+ yy_cli_parse_lval.val.val = my_strdup(yy_cli_def_text, "cli_parse.l");
+ yy_cli_parse_lval.val.val_type = type;
+}
diff --git a/src/cli_new.c b/src/cli_new.c
new file mode 100644
index 0000000..2f2801d
--- /dev/null
+++ b/src/cli_new.c
@@ -0,0 +1,1938 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <limits.h>
+#include <stdarg.h>
+#include "cli_val.h"
+#include "cli_parse.h"
+#include <regex.h>
+
+#include "cli_objects.h"
+#include "cli_val_engine.h"
+
+/* Defines: */
+
+#define EXE_STRING_DELTA 512
+#define PATH_DELTA 1000
+#define ENDS_ALLOC 20
+#define PATH_CM_LOCATION 25 /* mcd vs. ccd location
+ change when m_root changed */
+
+/* Global vars: */
+vtw_path m_path, t_path;
+
+/* Loval vars: */
+static vtw_node *vtw_free_nodes; /* linked via left */
+static char val_name[] = VAL_NAME;
+static int cond1[TOP_COND] ={5, 0,-1,-1, 0, 1, 0, 0};
+static int cond2[TOP_COND] ={5, 0, 1,-1,-1, 1, 1, 0};
+static char const *cond_formats[DOMAIN_TYPE] =
+ {
+ 0,
+ "%u", /* INT_TYPE */
+ "%u.%u.%u.%u", /*IPV4_TYPE*/
+ "%u.%u.%u.%u/%u", /*IPV4NET_TYPE*/
+ 0,
+ 0,
+ "%x:%x:%x:%x:%x:%x" /* MACADDR_TYPE */
+ };
+
+static int cond_format_lens[DOMAIN_TYPE] =
+ {
+ 0,
+ 1, /* INT_TYPE */
+ 4, /*IPV4_TYPE*/
+ 5, /*IPV4NET_TYPE*/
+ 0,
+ 0,
+ 6 /* MACADDR_TYPE */
+ };
+
+static int cli_val_len;
+static char *cli_val_alloc;
+static char *cli_val_ptr;
+
+static char *exe_string;
+static int exe_string_len;
+static int node_cnt;
+static int free_node_cnt;
+static boolean in_validate_val;
+static valstruct validate_value_val; /* value being validated
+ to be used as $(@) */
+
+/* Local function declarations: */
+
+static int check_comp(vtw_node *cur);
+static boolean check_syn_func(vtw_node *cur,const char* func,int line);
+#define check_syn(cur) check_syn_func((cur),__FUNCTION__,__LINE__)
+static void copy_path(vtw_path *to, vtw_path *from);
+static int eval_va(valstruct *res, vtw_node *node);
+static int expand_string(char *p);
+static void free_node(vtw_node *node);
+static void free_node_tree(vtw_node *node);
+static void free_reuse_list(void);
+static void free_path(vtw_path *path);
+static void free_string(char *str);
+static vtw_node * get_node(void);
+
+static void scan_ipv6(char *val, unsigned int *parts);
+
+static int set_reference_environment(const char* var_reference,
+ clind_path_ref *n_cfg_path,
+ clind_path_ref *n_tmpl_path,
+ clind_path_ref *n_cmd_path,
+ int active);
+
+/*************************************************
+ GLOBAL FUNCTIONS
+***************************************************/
+/* it is executed as "eval `my_set` in order to be able to
+ modify BASH env
+ therefore, all error will be reported as
+ printf("echo \"bla-bla-bla%s\";", sptr)
+ note very important ';' as the end of the format
+*/
+void bye(char *msg, ...)
+{
+ va_list ap;
+
+ if (is_silent_msg())
+ exit(0);
+ va_start(ap, msg);
+ if (is_echo())
+ printf("echo \"");
+ vprintf(msg, ap);
+ printf(is_echo()? "\";":"\n");
+ va_end(ap);
+
+ exit(0);
+}
+
+/* msg:
+ print message, preceeded by "echo " if global
+ flag echo set. This flag is used by program
+ which are executed as eval `command` in order to
+ modify BASH env
+*/
+void print_msg(char *msg, ...)
+{
+ va_list ap;
+
+ if (is_silent_msg())
+ return;
+ va_start(ap, msg);
+ if (is_echo())
+ printf("echo \"");
+ vprintf(msg, ap);
+ printf(is_echo()? "\";":"\n");
+ va_end(ap);
+}
+
+void touch_dir(const char *dp)
+{
+ struct stat statbuf;
+ if (lstat(dp, &statbuf) < 0) {
+ char *command;
+ command = my_malloc(strlen(dp) + 10, "set");
+ sprintf(command, "mkdir -p %s", dp);
+ system(command);
+ free(command);
+ return;
+ }
+ if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
+ bye("directory %s expected, found regular file", dp);
+ }
+ return;
+}
+
+/*****************************************************
+ add_val:
+ verify that the types are the same;
+ if first valstruct is single value, convert it
+ into multivalue;
+ add the value of second to the list of first;
+*****************************************************/
+void add_val(valstruct *first, valstruct *second)
+{
+ assert (first->free_me && second->free_me);
+ assert(second->cnt == 0);
+ if (first->val_type != second->val_type) {
+ printf("Different types\n\n");
+ } else {
+ if (first->cnt%MULTI_ALLOC == 0) {
+ /* convert into multivalue */
+ first->vals = my_realloc(first->vals, (first->cnt + MULTI_ALLOC) *
+ sizeof(char *), "add_value");
+ if (first->cnt == 0) { /* single value - convert */
+ first->vals[0] = first->val;
+ first->cnt = 1;
+ first->val = NULL;
+ }
+ }
+ second->free_me = FALSE; /* we took its string */
+ first->vals[first->cnt] = second->val;
+ ++first->cnt;
+ }
+}
+/*****************************************************
+ append - append node to the tail of list
+*****************************************************/
+void append(vtw_list *l, vtw_node *n, int aux)
+{
+ vtw_node *lnode;
+ lnode = make_node(LIST_OP, n, NULL);
+ lnode->vtw_node_aux = aux;
+ if(l->vtw_list_tail) {
+ assert(l->vtw_list_tail->vtw_node_right == NULL);
+ l->vtw_list_tail->vtw_node_right = lnode;
+ } else {
+ assert(l->vtw_list_head == NULL);
+ l->vtw_list_head = lnode;
+ }
+ l->vtw_list_tail = lnode;
+}
+
+void dt(vtw_sorted *srtp)
+{
+ int i;
+ for (i=0; i<srtp->num; ++i)
+ printf("%d %s\n", i, (char *)(srtp->ptrs[i]));
+}
+
+
+void di(vtw_sorted *srtp)
+{
+ int i;
+ for (i=0; i<srtp->num; ++i)
+ printf("%u %u\n", i, *(unsigned int *)(srtp->ptrs[i]));
+}
+
+static char _lock_file_[1025] = {0};
+static int _lock_fd_=-1;
+
+static void clean_lock_file(void) {
+ if(_lock_file_[0]) {
+ unlink(_lock_file_);
+ _lock_file_[0]=0;
+ }
+ if(_lock_fd_!=-1) {
+ close(_lock_fd_);
+ _lock_fd_=-1;
+ }
+}
+
+boolean get_config_lock(const char* adirp, const char* lock_name) {
+
+ boolean ret = TRUE;
+
+ sprintf(_lock_file_, "%s/%s", adirp, lock_name);
+
+ _lock_fd_ = open(_lock_file_, O_WRONLY | O_CREAT | O_EXCL, 0644);
+
+ ret = (_lock_fd_!=-1);
+
+ if(ret) {
+ atexit(clean_lock_file);
+ }
+
+ return ret;
+}
+
+void internal_error(int line, char *file)
+{
+ printf("\n\nInternal Error at line %d in %s\n", line, file);
+ exit (-1);
+}
+
+/*************************************************
+ vtw_sort:
+ create sorted structure for the value,
+ allocates ptrs and parts in this structure
+*/
+void vtw_sort(valstruct *valp, vtw_sorted *sortp)
+{
+ int i, unsorted, left, child, right;
+ void *leftp, *rightp, *childp;
+ const char * format;
+ unsigned int *parts;
+ vtw_type_e type = valp->val_type;
+ char *cp;
+ int cur=0, par=0, partnum=0, res=0;
+ void *curp, *parp;
+
+ sortp->num = valp->cnt?valp->cnt : 1;
+ sortp->ptrs = my_malloc(sortp->num * sizeof(void *), "sort_ptrs");
+ sortp->partnum = cond_format_lens[type];
+ if (sortp->partnum) {
+ sortp->parts = my_malloc(sortp->partnum * sortp->num * sizeof(void *),
+ "sort_parts");
+ }else{
+ sortp->parts = NULL;
+ }
+ switch (type){
+ case IPV6_TYPE:
+ case IPV6NET_TYPE:
+ for (i = 0; i < sortp->num; ++i) {
+ parts = sortp->parts + i * sortp->partnum;
+ scan_ipv6(valp->cnt?valp->vals[i]:valp->val, parts);
+ sortp->ptrs[i] = parts;
+ }
+ break;
+ case IPV4_TYPE:
+ case IPV4NET_TYPE:
+ case MACADDR_TYPE:
+ case INT_TYPE:
+ format = cond_formats[valp->val_type];
+ for (i = 0; i < sortp->num; ++i) {
+ cp = valp->cnt?valp->vals[i]:valp->val;
+ parts = sortp->parts + i * sortp->partnum;
+ switch (sortp->partnum) {
+ case 1:
+ (void) sscanf(cp, format, parts);
+ break;
+ case 2:
+ (void) sscanf(cp, format, parts, parts+1);
+ break;
+ case 3:
+ (void) sscanf(cp, format, parts, parts+1, parts+2);
+ break;
+ case 4:
+ (void) sscanf(cp, format, parts, parts+1, parts+2,
+ parts+3);
+ break;
+ case 5:
+ (void) sscanf(cp, format, parts, parts+1, parts+2,
+ parts+3, parts+4);
+ break;
+ case 6:
+ (void) sscanf(cp, format, parts, parts+1, parts+2,
+ parts+3, parts+4, parts+5);
+ break;
+ }
+ sortp->ptrs[i] = parts;
+ }
+ break;
+ case TEXT_TYPE:
+ case BOOL_TYPE:
+ for (i = 0; i < sortp->num; ++i) {
+ sortp->ptrs[i] = valp->cnt?valp->vals[i]:valp->val;
+ }
+ break;
+ default:
+ printf("Unknown value in switch on line %d\n", __LINE__);
+ exit(-5);
+ }
+ if (sortp->num < 2)
+ return;
+ /* now do a heap sort */
+ /* build heap */
+ /* from left to right, we start with the heap of only one (first) element*/
+ for (i = 2; i <= sortp->num; ++i)
+ {
+ cur = i;
+ do {
+ curp = sortp->ptrs[cur - 1];
+ par = cur >> 1;
+ parp = sortp->ptrs[par - 1];
+ if (sortp->partnum){
+ for(partnum = 0; partnum < sortp->partnum; ++partnum) {
+ if (*((unsigned int *)curp + partnum)>
+ *((unsigned int *)parp + partnum)){
+ res = 1;
+ break;
+ }
+ if (*((unsigned int *)curp + partnum)<
+ *((unsigned int *)parp + partnum)){
+ res = -1;
+ break;
+ }
+ res = 0;
+ }
+ }else{
+ res = strcmp((char *)curp, (char *) parp);
+ }
+ if (res <= 0)
+ break;
+ /* swap them */
+ sortp->ptrs[cur - 1] = parp;
+ sortp->ptrs[par - 1] = curp;
+
+ } while ((cur = par) != 1);
+ }
+ /* convert heap into sorted array */
+ unsorted = sortp->num; /* sortp->num must be >= 2 */
+ while (TRUE) {
+ void *tp;
+ /* root to the sorted part */
+ tp = sortp->ptrs[0];
+ sortp->ptrs[0] = sortp->ptrs[--unsorted];
+ sortp->ptrs[unsorted] = tp;
+ if (unsorted == 1)
+ break;
+ /* push down the new root */
+ par = 1;
+ while(TRUE) {
+ left = par << 1; /* left child */
+ if (left > unsorted)
+ break; /* no children */
+ else {
+ if (left == unsorted) {
+ /* only left child */
+ child = left;
+ } else {
+ /* both children */
+ right = left+1;
+ leftp = sortp->ptrs[left - 1];
+ rightp = sortp->ptrs[right - 1];
+ /* find larger child */
+ if (sortp->partnum){
+ for(partnum = 0; partnum < sortp->partnum; ++partnum) {
+ if (*((unsigned int *)leftp + partnum) >
+ *((unsigned int *)rightp + partnum)) {
+ res = 1;
+ break;
+ }
+ if (*((unsigned int *)leftp + partnum) <
+ *((unsigned int *)rightp + partnum)) {
+ res = -1;
+ break;
+ }
+ res = 0;
+ }
+ }else{
+ res = strcmp((char *)leftp, (char *) rightp);
+ }
+ if (res >= 0) {
+ child = left; /* left is larger or same*/
+ } else {
+ child = right;
+ }
+ }
+ /* compare parent and larger child */
+ parp = sortp->ptrs[par - 1];
+ childp = sortp->ptrs[child - 1];
+ if (sortp->partnum){
+ for(partnum = 0; partnum < sortp->partnum; ++partnum) {
+ if (*((unsigned int *)parp + partnum) >
+ *((unsigned int *)childp + partnum)){
+ res = 1;
+ break;
+ }
+ if (*((unsigned int *)parp + partnum) <
+ *((unsigned int *)childp + partnum)){
+ res = -1;
+ break;
+ }
+ res = 0;
+ }
+ }else{
+ res = strcmp((char *)parp, (char *) childp);
+ }
+ if (res >= 0) {
+ /* done with percolating down, parent larger than child */
+ break;
+ }
+ /* child greater, exchage and continue */
+ sortp->ptrs[par - 1] = childp;
+ sortp->ptrs[child - 1] = parp;
+ par = child;
+ }
+ }
+ }
+}
+
+/* returns FALSE if execution returns non-null,
+ returns TRUE if every excution returns NULL
+*/
+boolean execute_list(vtw_node *cur, vtw_def *def)
+{
+ boolean ret;
+ int status;
+ set_in_exec(TRUE);
+ status = char2val(def, get_at_string(), &validate_value_val);
+ if (status) return FALSE;
+ ret = check_syn(cur);
+ free_val(&validate_value_val);
+ set_in_exec(FALSE);
+ return ret;
+}
+
+
+/*****************************************************
+ make_node - create a node with oper, left, and right
+*****************************************************/
+vtw_node * make_node(vtw_oper_e oper, vtw_node *left,
+ vtw_node *right)
+{
+ vtw_node *ret ;
+ ret = get_node();
+ ret->vtw_node_oper = oper;
+ ret->vtw_node_left = left;
+ ret->vtw_node_right = right;
+ ret->vtw_node_string = NULL;
+ ret->vtw_node_aux = 0;
+ return ret;
+}
+vtw_node *make_str_node0(char *str, vtw_oper_e op)
+{
+ vtw_node *ret;
+ ret = make_node(op, NULL, NULL);
+ ret->vtw_node_string = str;
+ ret->vtw_node_type = TEXT_TYPE;
+ return ret;
+}
+/*****************************************************
+ make_str_node - create a VAL_OP node with str
+*****************************************************/
+vtw_node *make_str_node(char *str)
+{
+ return make_str_node0(str, VAL_OP);
+}
+/*****************************************************
+ make_var_node - create a VAR_OP node with str
+*****************************************************/
+vtw_node *make_var_node(char *str)
+{
+ return make_str_node0(str, VAR_OP);
+}
+/*****************************************************
+ make_val_node - create a VAl_OP node with str
+*****************************************************/
+vtw_node *make_val_node(valstruct *val)
+{
+ vtw_node *ret;
+ assert(val->free_me);
+ ret = make_node(VAL_OP, NULL, NULL);
+ ret->vtw_node_val = *val;
+ val->free_me = FALSE;
+ return ret;
+}
+valstruct str2val(char *cp)
+{
+ valstruct ret;
+ memset(&ret, 0, sizeof(ret));
+ ret.val_type = TEXT_TYPE;
+ ret.val = cp;
+ ret.free_me = TRUE;
+ return ret;
+}
+/****************************************************
+ STATIC FUNCTIONS
+****************************************************/
+
+/**************************************************
+ char2val:
+ convert string into valstruct verifying the type
+ according to def
+****************************************************/
+int char2val(vtw_def *def, char *value, valstruct *valp)
+{
+ int token;
+ char *endp, *cp;
+ int linecnt, cnt;
+ int my_type = def->def_type;
+ boolean first = TRUE;
+
+ memset(valp, 0, sizeof (*valp));
+
+ if (my_type == ERROR_TYPE) {
+ my_type = TEXT_TYPE;
+ }
+
+ if (my_type != TEXT_TYPE && my_type != ERROR_TYPE) {
+ cli_val_len = strlen(value);
+ cli_val_ptr = value;
+ while(1) {
+ token = yy_cli_val_lex();
+ if (token != VALUE) {
+ if (first || token){
+ if (def->def_type_help){
+ set_at_string(value);
+ (void)expand_string(def->def_type_help);
+ printf("%s\n", exe_string);
+ fprintf(stderr, "%s\n", exe_string);
+ } else {
+ print_msg("Wrong type of value in %s, "
+ "need %s\n",
+ m_path.path_buf + m_path.print_offset,
+ type_to_name(my_type));
+ }
+ return -1;
+ }
+ return 0;
+ }
+ if (my_type != get_cli_value_ptr()->val_type) {
+ if (def->def_type_help){
+ set_at_string(value);
+ (void)expand_string(def->def_type_help);
+ printf("%s\n", exe_string);
+ fprintf(stderr, "%s\n", exe_string);
+ } else {
+ print_msg("Wrong type of value in %s, "
+ "need %s\n",
+ m_path.path_buf + m_path.print_offset,
+ type_to_name(my_type));
+ }
+ my_free(get_cli_value_ptr()->val);
+ if (first)
+ return -1;
+ return 0;
+ }
+ if (first) {
+ *valp = *get_cli_value_ptr();
+ get_cli_value_ptr()->free_me = FALSE;
+ first = FALSE;
+ } else {
+ if (def->multi)
+ add_val(valp, get_cli_value_ptr());
+ else {
+ print_msg("Unexpected multivalue in %s\n", m_path.path);
+ free_val(get_cli_value_ptr());
+ }
+ }
+ token = yy_cli_val_lex();
+ if (!token)
+ return 0;
+ if (token != EOL) {
+ print_msg("Badly formed value in %s\n",
+ m_path.path + m_path.print_offset);
+ if (token == VALUE)
+ my_free(get_cli_value_ptr()->val);
+ return 0;
+ }
+ }
+ return 0;
+ }
+ valp->val_type = TEXT_TYPE;
+ valp->free_me = TRUE;
+ /* count lines */
+ linecnt = 0;
+ for (cp = value; *cp; ++cp)
+ if (*cp == '\n')
+ ++linecnt;
+ if (cp != value && cp[-1] != '\n')
+ ++linecnt; /* last non empty non \n terminated string */
+ if (linecnt == 0) /* one empty non terminated string */
+ linecnt = 1;
+ if (linecnt == 1) {
+ valp->val=my_strdup(value, "char2val 1");
+ /*truncate '\n' etc */
+ endp = strchr(valp->val, '\n');
+ if (endp)
+ *endp = 0;
+ } else {
+ valp->cnt = linecnt;
+ cnt = (linecnt + MULTI_ALLOC - 1) / MULTI_ALLOC;
+ cnt *= MULTI_ALLOC;
+ valp->vals = my_malloc(cnt * sizeof(char *), "char2val 2");
+ for(cp = value, cnt = 0; cnt < linecnt; ++cnt) {
+ endp = strchr(cp, '\n');
+ if (endp)
+ *endp = 0;
+ valp->vals[cnt]=my_strdup(cp, "char2val 3");
+ if (endp) {
+ *endp = '\n';
+ cp = endp + 1;
+ } else {
+ /* non '\n' terinated string, must be last line, we are done */
+ ++cnt;
+ assert(cnt == linecnt);
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+
+/****************************************************
+ val_comp:
+ compare two values per cond
+ returns result of comparison
+****************************************************/
+boolean val_cmp(valstruct *left, valstruct *right, vtw_cond_e cond)
+{
+ unsigned int left_parts[9], right_parts[9];
+ vtw_type_e val_type;
+ int parts_num, lstop, rstop, lcur, rcur;
+ char const *format;
+ char *lval, *rval;
+ int ret=0, step=0, res=0;
+
+ val_type = left->val_type;
+ if (left->cnt)
+ lstop = left->cnt;
+ else
+ lstop = 1;
+ if (right->cnt)
+ rstop = right->cnt;
+ else
+ rstop = 1;
+
+ for(lcur = 0; lcur < lstop; ++lcur) {
+ if (!lcur && !left->cnt)
+ lval = left->val;
+ else
+ lval = left->vals[lcur];
+ for(rcur = 0; rcur < rstop; ++rcur) {
+ if (!rcur && !right->cnt)
+ rval = right->val;
+ else
+ rval = right->vals[rcur];
+ switch (val_type) {
+ case IPV6_TYPE:
+ parts_num = 8;
+ goto ipv6_common;
+ case IPV6NET_TYPE:
+ parts_num = 9;
+ ipv6_common:
+ scan_ipv6(lval,left_parts);
+ scan_ipv6(rval,right_parts);
+ break;
+ case IPV4_TYPE:
+ case IPV4NET_TYPE:
+ case MACADDR_TYPE:
+ case INT_TYPE:
+ format = cond_formats[val_type];
+ parts_num = cond_format_lens[val_type];
+ (void) sscanf(lval, format, left_parts, left_parts+1,
+ left_parts+2, left_parts+3, left_parts+4,
+ left_parts+5);
+ (void) sscanf(rval, format, right_parts, right_parts+1,
+ right_parts+2, right_parts+3, right_parts+4,
+ right_parts+5);
+ break;
+ case TEXT_TYPE:
+ case BOOL_TYPE:
+ res = strcmp(lval, rval);
+ goto done_comp;
+ default:
+ printf("Unknown value in switch on line %d\n", __LINE__);
+ exit(-5);
+ }
+ /* here to do a multistep int compare */
+ for (step = 0; step < parts_num; ++ step) {
+ if (left_parts[step] > right_parts[step]) {
+ res = 1;
+ break; /* no reason to continue checking other steps */
+ }
+ if (left_parts[step] < right_parts[step]) {
+ res = -1;
+ break; /* no reason to continue checking other steps */
+ }
+ res = 0;
+ }
+ done_comp:
+ if(res > 0) res = 1;
+ else if(res < 0) res = -1;
+ ret = ((res == cond1[cond]) ||
+ (res == cond2[cond]));
+ if (ret && cond == IN_COND) {
+ set_in_cond_tik(rcur); /* for delete */
+ /* one success is enough for right cycle
+ in case of IN_COND, continue left cycle */
+ break;
+ }
+ if (!ret && cond != IN_COND)
+ /* one failure is enough in cases
+ other than IN_COND - go out */
+ return ret;
+ /* in all other cases:
+ (fail & IN_COND) or (success & !IN_COND)
+ contniue checking; */
+ }
+ }
+ return ret;
+}
+
+
+
+/****************************************************
+ check_comp:
+ evaluate comparison node.
+ returns boolean value of result
+****************************************************/
+static boolean check_comp(vtw_node *cur)
+{
+ int ret;
+ int status;
+ valstruct left, right;
+
+ memset(&left, 0 , sizeof(left));
+ memset(&right, 0 , sizeof(right));
+ ret = FALSE; /* in case of status */
+ status = eval_va(&left, cur->vtw_node_left);
+ if (status)
+ goto free_and_return;
+ status = eval_va(&right, cur->vtw_node_right);
+ if (status)
+ goto free_and_return;
+ if(left.val_type != right.val_type) {
+ printf("Different types in comparison\n");
+ goto free_and_return;
+ }
+ ret = val_cmp(&left, &right,cur->vtw_node_aux);
+ free_and_return:
+ if (left.free_me)
+ free_val(&left);
+ if (right.free_me)
+ free_val(&right);
+ return ret;
+}
+
+/******************
+ Change value of var in the file
+
+*****************/
+
+static int write_value_to_file(const char* var_path,const char* value) {
+
+ if(var_path && value) {
+
+ {
+ /*Build directory, if necessary:*/
+ clind_path_ref var_dir_path=clind_path_construct(var_path);
+
+ if(!var_dir_path) bye("Can not construct path %s", var_path);
+ else {
+
+ char* end = clind_path_pop_string(var_dir_path);
+
+ if(!end || strcmp(end,VAL_NAME)) {
+ bye("Wrong end of path: %s (%s)", end,var_path);
+ }
+
+ free(end);end=NULL;
+
+ touch();
+ touch_dir(clind_path_get_path_string(var_dir_path));
+
+ clind_path_destruct(&var_dir_path);
+ }
+ }
+
+ {
+ /*Write to file*/
+ FILE* fp = fopen(var_path, "w");
+ if(!fp) bye("Can not open value file %s", var_path);
+
+ if (fputs(value, fp) < 0 || fputc('\n',fp) < 0)
+ bye("Error writing file %s", var_path);
+
+ fclose(fp);
+
+ }
+ }
+
+ return 0;
+}
+
+static int change_var_value(const char* var_reference,const char* value, int active_dir) {
+
+ int ret=-1;
+
+ if(var_reference && value) {
+
+ char* var_path=NULL;
+
+ clind_path_ref n_cfg_path=NULL;
+ clind_path_ref n_tmpl_path=NULL;
+ clind_path_ref n_cmd_path=NULL;
+
+ if(set_reference_environment(var_reference,
+ &n_cfg_path,
+ &n_tmpl_path,
+ &n_cmd_path,
+ active_dir)==0) {
+
+ clind_val cv;
+
+ memset(&cv,0,sizeof(cv));
+
+ if(clind_config_engine_apply_command_path(n_cfg_path,
+ n_tmpl_path,
+ n_cmd_path,
+ FALSE,
+ &cv,
+ get_cdirp(),
+ get_tdirp(),
+ TRUE)==0) {
+ var_path=cv.value;
+
+ }
+
+ }
+
+ if(n_cfg_path) clind_path_destruct(&n_cfg_path);
+ if(n_tmpl_path) clind_path_destruct(&n_tmpl_path);
+ if(n_cmd_path) clind_path_destruct(&n_cmd_path);
+
+ if(var_path) {
+ ret=write_value_to_file(var_path,value);
+ free(var_path);
+ }
+ }
+
+ return ret;
+}
+
+/****************************************************
+ check_syn:
+ evaluate syntax tree;
+ returns TRUE if all checks are OK,
+ returns FALSE if check fails.
+****************************************************/
+static boolean check_syn_func(vtw_node *cur,const char* func,int line)
+{
+ int status;
+ int ret;
+ int ii;
+
+ switch(cur->vtw_node_oper) {
+ case LIST_OP:
+ ret = check_syn(cur->vtw_node_left);
+ if (!is_in_commit() && cur->vtw_node_aux)
+ ret = TRUE;
+ if (!ret || !cur->vtw_node_right) /* or no right operand */
+ return ret;
+ return check_syn(cur->vtw_node_right);
+ case HELP_OP:
+ ret = check_syn(cur->vtw_node_left);
+ if (ret <= 0){
+ if (expand_string(cur->vtw_node_right->vtw_node_string) == VTWERR_OK) {
+ fprintf(stderr, exe_string);
+ fprintf(stderr, "\n");
+ printf(exe_string);
+ printf("\n");
+ }
+ }
+ return ret;
+
+ case ASSIGN_OP:
+
+ if (is_in_exec()) {
+
+ valstruct right;
+
+ char* var_reference = NULL;
+
+ memset(&right, 0, sizeof(right));
+ status = eval_va(&right, cur->vtw_node_right);
+
+ if (status || right.cnt) { /* bad or multi */
+ if (right.free_me) free_val(&right);
+ return FALSE;
+ }
+
+ var_reference = strdup(cur->vtw_node_left->vtw_node_string+2);
+
+ {
+ int i=0;
+ while(var_reference[i]) {
+ if(var_reference[i]==')') {
+ var_reference[i]=0;
+ break;
+ }
+ i++;
+ }
+ }
+
+ change_var_value(var_reference,right.val,FALSE);
+ change_var_value(var_reference,right.val,TRUE);
+
+ if (right.free_me) free_val(&right);
+
+ if(var_reference) free(var_reference);
+ }
+
+ return TRUE;
+
+ case EXEC_OP:
+ /* for every value */
+ if (in_validate_val) {
+ char *save_at = get_at_string();
+ for(ii = 0; ii < validate_value_val.cnt || ii == 0; ++ii) {
+ set_at_string(validate_value_val.cnt?
+ validate_value_val.vals[ii]:validate_value_val.val);
+ status = expand_string(cur->vtw_node_left->vtw_node_string);
+ if (status != VTWERR_OK) {
+ set_at_string(save_at);
+ return FALSE;
+ }
+ ret = system(exe_string);
+ if (ret) {
+ set_at_string(save_at);
+ return FALSE;
+ }
+ }
+ set_at_string(save_at);
+ return TRUE;
+ }
+ /* else */
+ status = expand_string(cur->vtw_node_left->vtw_node_string);
+ if (status != VTWERR_OK) {
+ return FALSE;
+ }
+ ret = system(exe_string);
+ return !ret;
+
+ case PATTERN_OP: /* left to var, right to pattern */
+ {
+ valstruct left;
+ regex_t myreg;
+ boolean ret;
+ int ii;
+
+ ret = TRUE;
+ status = eval_va(&left, cur->vtw_node_left);
+ if (status) {
+ ret = FALSE;
+ goto free_and_return;
+ }
+ status = regcomp(&myreg, cur->vtw_node_right->vtw_node_string,
+ REG_EXTENDED);
+ if (status)
+ bye("Can not compile regex |%s|, result %d\n",
+ cur->vtw_node_right->vtw_node_string, status);
+ /* for every value */
+ for(ii = 0; ii < left.cnt || ii == 0; ++ii) {
+ status = regexec(&myreg, left.cnt?
+ left.vals[ii]:left.val,
+ 0, 0, 0);
+ if(status) {
+ ret = FALSE;
+ break;
+ }
+ }
+ free_and_return:
+ if (left.free_me)
+ free_val(&left);
+ return ret;
+ }
+
+ case OR_OP:
+ ret = check_syn(cur->vtw_node_left) ||
+ check_syn(cur->vtw_node_right);
+ return ret;
+ case AND_OP:
+ ret = check_syn(cur->vtw_node_left) &&
+ check_syn(cur->vtw_node_right);
+ return ret;
+ case NOT_OP:
+ ret = check_syn(cur->vtw_node_left);
+ return !ret;
+
+ case COND_OP: /* aux field specifies cond type (GT, GE, etc.)*/
+ ret = check_comp(cur);
+ return ret;
+
+ case VAL_OP:
+ printf("VAL op in check_syn\n");
+ exit(-4);
+ case VAR_OP:
+ printf("VAR op in check_syn\n");
+ exit(-4);
+ default:
+ printf("unknown op %d in check_syn\n", cur->vtw_node_oper);
+ exit(-4);
+ }
+}
+
+/*************************************************
+ copy_path:
+ copy path
+ if destination path owns memory, free it
+**************************************************/
+static void copy_path(vtw_path *to, vtw_path *from)
+{
+ if (to->path_buf)
+ my_free(to->path_buf);
+ if (to->path_ends)
+ my_free(to->path_ends);
+ *to = *from;
+ to->path_buf = (char *) my_malloc(from->path_alloc+2, "copy_path1");
+ memcpy(to->path_buf, from->path_buf, to->path_alloc + 1);
+ to->path = to->path_buf + (from->path-from->path_buf);
+ to->path_ends = (int *) my_malloc(to->path_ends_alloc * sizeof(int),
+ "copy_path2");
+ memcpy(to->path_ends, from->path_ends,
+ to->path_ends_alloc * sizeof(int));
+}
+
+/*****************************************************
+ eval_va:
+ converts VAR_OP or VAL_OP node into valstruct
+ in case of VAR_OP we need to find corresponding
+ template node to obtain type.
+
+*****************************************************/
+static int eval_va(valstruct *res, vtw_node *node)
+{
+ char *cp=NULL;
+ char *pathp=NULL;
+ int status=0;
+
+ switch (node->vtw_node_oper) {
+ case VAR_OP:
+
+ {
+ char *endp = 0;
+ clind_path_ref n_cfg_path=NULL;
+ clind_path_ref n_tmpl_path=NULL;
+ clind_path_ref n_cmd_path=NULL;
+
+ pathp = node->vtw_node_string;
+
+ assert(pathp[0]=='$' && pathp[1]=='(');
+ pathp += 2;
+
+ if(pathp[0] == '@' && pathp[1]!='@'){
+ /* this is why we passed at_val all around */
+ *res = validate_value_val;
+ res->free_me = FALSE;
+ return 0;
+ }
+
+ memset(res,0,sizeof(*res));
+
+ if ((endp = strchr(pathp, ')')) == NULL) {
+ printf("invalid VAR_OP [%s]\n", node->vtw_node_string);
+ return VTWERR_BADPATH;
+ }
+
+ *endp = 0;
+
+ if(set_reference_environment(pathp,
+ &n_cfg_path,
+ &n_tmpl_path,
+ &n_cmd_path,
+ is_in_delete_action())==0) {
+
+ clind_val cv;
+
+ memset(&cv,0,sizeof(cv));
+
+ status=clind_config_engine_apply_command_path(n_cfg_path,
+ n_tmpl_path,
+ n_cmd_path,
+ FALSE,
+ &cv,
+ get_cdirp(),
+ get_tdirp(),
+ FALSE);
+
+ if(status==0) {
+ if(cv.value) {
+ res->val_type = cv.val_type;
+ res->free_me = TRUE;
+ res->val = cv.value;
+ }
+ }
+ }
+
+ if(n_cfg_path) clind_path_destruct(&n_cfg_path);
+ if(n_tmpl_path) clind_path_destruct(&n_tmpl_path);
+ if(n_cmd_path) clind_path_destruct(&n_cmd_path);
+
+ *endp = ')';
+
+ return status;
+ }
+
+ case VAL_OP:
+ *res = node->vtw_node_val;
+ res->free_me = FALSE;
+ return 0;
+ case B_QUOTE_OP:
+ {
+ FILE *f;
+ int a_len, len, rd;
+
+ status = expand_string(node->vtw_node_string);
+ if (status != VTWERR_OK) {
+ return FALSE;
+ }
+ f = popen(exe_string, "r");
+ if (!f)
+ return -1;
+#define LEN 24
+ len = 0;
+ cp = my_malloc(LEN,"");
+ a_len = LEN;
+ for(;;){
+ rd = fread(cp + len, 1, a_len - len , f);
+ len += rd;
+ if (len < a_len)
+ break;
+ cp = my_realloc(cp, a_len+LEN, "");
+ a_len += LEN;
+ }
+ cp[len] = 0;
+ pclose(f);
+ memset(res, 0, sizeof (*res));
+ res->val_type = TEXT_TYPE;
+ res->free_me = TRUE;
+ res->val = cp;
+ }
+ return 0;
+ default:
+ return 0;
+ }
+}
+
+/**********************************************************
+ expand_string:
+ expand string replacing var references with the appropriate
+ values, the formed string is collected in the buffer pointed
+ at by the global exe_string. The buffer dynamically allocated
+ and reallocated.
+***********************************************************/
+static int expand_string(char *stringp)
+{
+ char *scanp;
+ char *resp = exe_string;
+ int left = exe_string_len;
+ int my_len;
+ int len;
+
+ scanp = stringp; /* save stringp for printf */
+
+ do{
+
+ if (left <= 1){
+ my_len = resp - exe_string;
+ exe_string_len += EXE_STRING_DELTA;
+ exe_string = my_realloc(exe_string, exe_string_len, "expand_string 1");
+ left += EXE_STRING_DELTA;
+ resp = exe_string + my_len;
+ /* back in business */
+ }
+ if (*scanp != '$') {
+ if(*scanp == '\\' && scanp[1] == '$') {
+ /* escaped $, treat as regular char */
+ ++scanp;
+ }
+ *resp++ = *scanp++;
+ --left;
+ } else {
+
+ char *cp=NULL;
+ boolean my_cp=FALSE;
+
+ if (scanp[1] != '('){
+
+ printf("Badly formed var reference in %s\n", stringp);
+ exit (VTWERR_BADPATH);
+
+ } else if(scanp[2] == '@' && scanp[3] == ')') {
+
+ cp = get_at_string();
+ my_cp = FALSE;
+ scanp += 4;
+
+ } else {
+
+ clind_path_ref n_cfg_path=NULL;
+ clind_path_ref n_tmpl_path=NULL;
+ clind_path_ref n_cmd_path=NULL;
+
+ char *endp;
+
+ endp = strchr(scanp, ')');
+ if (!endp ){
+ return -1;
+ }
+
+ scanp += 2;
+ /* path reference */
+ *endp = 0;
+ if (endp == scanp)
+ bye("Empty path");
+
+ if(set_reference_environment(scanp,
+ &n_cfg_path,
+ &n_tmpl_path,
+ &n_cmd_path,
+ is_in_delete_action())==0) {
+
+ clind_val cv;
+
+ memset(&cv,0,sizeof(cv));
+
+ if(clind_config_engine_apply_command_path(n_cfg_path,
+ n_tmpl_path,
+ n_cmd_path,
+ FALSE,
+ &cv,
+ get_cdirp(),
+ get_tdirp(),
+ FALSE)==0) {
+ cp=cv.value;
+
+ }
+
+ }
+
+ if(n_cfg_path) clind_path_destruct(&n_cfg_path);
+ if(n_tmpl_path) clind_path_destruct(&n_tmpl_path);
+ if(n_cmd_path) clind_path_destruct(&n_cmd_path);
+
+ if(!cp) {
+ cp="";
+ } else {
+ my_cp=TRUE;
+ }
+
+ *endp = ')';
+
+ scanp = strchr(scanp, ')') + 1;
+ }
+ len = strlen(cp);
+ while(len + 1 > left) { /* 1 for termination */
+ my_len = resp - exe_string;
+ exe_string_len += EXE_STRING_DELTA;
+ exe_string = my_realloc(exe_string, exe_string_len, "expand_string 2");
+ left += EXE_STRING_DELTA;
+ resp = exe_string + my_len;
+ /* back in business */
+ }
+
+ strcpy(resp, cp);
+ if(my_cp && cp) free(cp);
+ resp += len;
+ left -= len;
+ }
+
+ } while(*scanp);
+
+ *resp = 0;
+
+ return VTWERR_OK;
+}
+
+/*****************************************************
+ free_sorted:
+ free all memory allocated to sorted
+*****************************************************/
+void free_sorted(vtw_sorted *sortp)
+{
+ if(sortp->ptrs)
+ my_free(sortp->ptrs);
+ if (sortp->parts)
+ my_free(sortp->parts);
+}
+
+
+/*****************************************************
+ free_def:
+ free all memory allocated to def
+*****************************************************/
+void free_def(vtw_def *defp)
+{
+ vtw_act_type act;
+ for(act=0; act<top_act; ++ act)
+ if (defp->actions[act].vtw_list_head)
+ free_node_tree(defp->actions[act].vtw_list_head);
+ if (defp->def_type_help)
+ my_free(defp->def_type_help);
+ if (defp->def_default)
+ my_free(defp->def_default);
+}
+
+/*****************************************************
+ free_node - add node to free list
+*****************************************************/
+static void free_node(vtw_node *node)
+{
+ --node_cnt;
+ ++free_node_cnt;
+ node->vtw_node_left = vtw_free_nodes;
+ vtw_free_nodes = node;
+}
+
+/*****************************************************
+ free_node_tree - add all nodes of the tree to free list
+*****************************************************/
+static void free_node_tree(vtw_node *node)
+{
+ if (node->vtw_node_left)
+ free_node_tree(node->vtw_node_left);
+ if (node->vtw_node_right)
+ free_node_tree(node->vtw_node_right);
+ if (node->vtw_node_string)
+ free_string(node->vtw_node_string);
+ if (node->vtw_node_val.free_me)
+ free_val(&(node->vtw_node_val));
+ free_node(node);
+}
+
+static void free_path(vtw_path *path)
+{
+ if (path->path_ends)
+ my_free(path->path_ends);
+ if (path->path_buf) {
+ my_free(path->path_buf);
+ }
+}
+
+static void free_reuse_list()
+{
+ vtw_node *next;
+ int cnt = 0;
+
+ while (vtw_free_nodes) {
+ next = vtw_free_nodes->vtw_node_left;
+ my_free(vtw_free_nodes);
+ ++cnt;
+ vtw_free_nodes = next;
+ --free_node_cnt;
+ }
+#if DEBUG
+ printf("%d nodes used\n", cnt);
+#endif
+}
+
+/*****************************************************
+ free_val - dealloc allocated memory of valstruct
+*****************************************************/
+void free_val(valstruct *val)
+{
+ int cnt;
+
+ assert(val->free_me);
+ if (val->val)
+ my_free(val->val);
+ for (cnt = 0; cnt < val->cnt; ++ cnt)
+ my_free(val->vals[cnt]);
+ if(val->vals)
+ my_free(val->vals);
+}
+/*****************************************************
+ free_string - dealloc string
+ just free for now, we might do something else later
+*****************************************************/
+static void free_string(char *str)
+{
+ my_free(str);
+}
+
+
+/*****************************************************
+ get_node - take node from free list or allocate
+*****************************************************/
+static vtw_node * get_node(void)
+{
+ vtw_node *ret;
+ if (vtw_free_nodes){
+ ret = vtw_free_nodes;
+ vtw_free_nodes = vtw_free_nodes->vtw_node_left;
+ --free_node_cnt;
+ } else {
+ ret = my_malloc(sizeof(vtw_node), "New node");
+ }
+ ++node_cnt;
+ memset(ret, 0, sizeof(vtw_node));
+ return ret;
+}
+
+/****************************************************
+ get_value:
+ for a given path (*path) verify that value exists,
+ open it and read it. The pointer to allocated
+ memory is returned in valpp. It is responsibility
+ of a caller to release memory.
+ Returns:
+******************************************************/
+int get_value(char **valpp, vtw_path *pathp)
+{
+ struct stat statbuf;
+ int status = VTWERR_OK;
+ char const *err = NULL;
+ FILE *in = NULL;
+ char *valp;
+ int readcnt;
+
+
+ /* find value */
+ *valpp = 0;
+ push_path(pathp, val_name);
+
+ if (lstat(pathp->path, &statbuf) < 0) {
+ err = "no value file in [%s]\n";
+ goto bad_path;
+ }
+ if ((statbuf.st_mode & S_IFMT) != S_IFREG) {
+ err = "no value file in [%s]\n";
+ goto bad_path;
+ }
+ in = fopen(pathp->path, "r");
+ if (!in) {
+ err = "Can not open value file in [%s]\n";
+ goto bad_path;
+ }
+ valp = my_malloc(statbuf.st_size + 1, "get_value");
+ readcnt = fread(valp, 1, statbuf.st_size, in);
+ if (readcnt != statbuf.st_size) {
+ my_free(valp);
+ cli_val_alloc = 0;
+ err = "Error reading value file in [%s]\n";
+ goto bad_path;
+ }
+ valp[statbuf.st_size] = 0;
+ /* remove \n at the line end */
+ if (valp[statbuf.st_size - 1] == '\n')
+ valp[statbuf.st_size - 1] = 0;
+ *valpp = valp;
+ status = 0;
+pop:
+ if (in)
+ fclose(in);
+ pop_path(pathp);
+ if (err) {
+ fprintf(stderr, err, pathp->path_buf + m_path.print_offset);
+ printf(err, pathp->path_buf + m_path.print_offset);
+ }
+ return status;
+ bad_path:
+ status = VTWERR_BADPATH;
+ goto pop;
+}
+
+int get_value_to_at_string(vtw_path *pathp) {
+
+ char* at=NULL;
+ int status = get_value(&at,pathp);
+
+ if(status==0) {
+ set_at_string(at);
+ } else {
+ set_at_string(NULL);
+ }
+
+ return status;
+}
+
+/*****************************************************
+ out_of_memeory
+ print out of memory message and exit
+*****************************************************/
+void out_of_memory()
+{
+ printf("\n\t!!! OUT OF MEMORY !!!\n");
+ exit(-1);
+}
+
+/*************************************************
+ init_path:
+ init path, exit if not able (out_of_memory)
+**************************************************/
+void init_path(vtw_path *path, const char *root)
+{
+ long path_len;
+ memset(path, 0, sizeof(vtw_path));
+ path_len = pathconf(root, _PC_PATH_MAX);
+ if (path_len < 0)
+ path_len = PATH_DELTA;
+ path->path_alloc = path_len - 2;
+ /* 1 byte for null termination, and 1 byte for '/' */
+ path->path_buf = path->path =
+ (char *)my_malloc(path_len, "init_path 1");
+ strcpy(path->path, root);
+ path->path_len = strlen(root);
+ path->path_ends = (int *)my_malloc(ENDS_ALLOC * sizeof(int *),
+ "init_path 2");
+ path->path_lev = 1;
+ path->path_ends[0] = path->path_len;
+ path->path_ends_alloc = ENDS_ALLOC;
+}
+
+/*****************************************************
+ pop_path - shorten path by one segment
+*****************************************************/
+
+void pop_path(vtw_path *path)
+{
+ if (--path->path_lev < 1) {
+ INTERNAL;
+ }
+ path->path_len = path->path_ends[path->path_lev - 1];
+ path->path_buf[path->path_len] = 0;
+}
+void warrant_path(vtw_path *path, int len)
+{
+ int delta = path->path - path->path_buf;
+
+ while(path->path_alloc - path->path_len < len + 1){
+ path->path_buf = (char *)my_realloc(path->path_buf,
+ path->path_alloc +
+ PATH_DELTA, "push_path 1");
+ path->path_alloc += PATH_DELTA;
+ }
+ path->path = path->path_buf + delta;
+}
+/*****************************************************
+ push_path - extend path by '/' and one new segment
+*****************************************************/
+void push_path(vtw_path *path, char *segm)
+{
+ int len;
+ char *cp;
+ char *pp;
+
+ for(cp=segm, len=0;*cp;++cp, ++len)
+ if(*cp=='%' || *cp=='/')
+ len +=2;
+ warrant_path(path, len + 1);
+ path->path_buf[path->path_len] = '/';
+ path->path_buf[++path->path_len] = 0;
+ for(pp=path->path_buf + path->path_len,cp=segm;
+ *cp;++cp, ++pp)
+ if(*cp=='%') {
+ pp[0] = '%';
+ pp[1] = '2';
+ pp[2] = '5';
+ pp += 2;
+ }else if (*cp == '/') {
+ pp[0] = '%';
+ pp[1] = '2';
+ pp[2] = 'F';
+ pp += 2;
+ }else
+ *pp = *cp;
+ *pp = 0;
+
+ path->path_len += len;
+ if (path->path_lev == path->path_ends_alloc){
+ path->path_ends_alloc += ENDS_ALLOC;
+ path->path_ends = (int *)my_realloc(path->path_ends,
+ sizeof(int *)*path->path_ends_alloc, "puhs_path 2");
+ }
+ path->path_ends[path->path_lev++] = path->path_len;
+}
+
+/****************************************************
+ scan_ipv6:
+ scans ipv6 or ipv6net pointed by val
+ and returns as array of integers pointed
+ by parts
+***************************************************/
+static void scan_ipv6(char *val, unsigned int *parts)
+{
+ int num = 0;
+ int num_cnt = 0;
+ int dot_dot_pos = -1;
+ int dot_cnt = 0;
+ char c;
+ char *p;
+ int base = 16;
+ int total = 8;
+ int gap;
+
+ for (p = val;TRUE; ++p) {
+ switch ((c = *p)) {
+ case '.':
+ if (dot_cnt == 0) {
+ /* turn out it was decimal, convert our wrong
+ hex interpretation;
+ decimal may not have more than 3 digits */
+ num = (num/256)*100 + (num%256)/16*10 + num%16;
+ base = 10;
+ }
+ ++dot_cnt;
+ break;
+ case ':':
+ if (p[1] == ':'){
+ ++p;
+ dot_dot_pos = num_cnt + 1;
+ }
+ break;
+ case '/':
+ base = 10;
+ total = 9;
+ break;
+ case 0:
+ break;
+ default:
+ /* must be a digit */
+ c = tolower(*p);
+ if (isdigit(c))
+ num = num * base + c - '0';
+ else
+ num = num * base + c - 'a' + 10;
+ continue;
+ }
+ /* close the number */
+ /* the case of "::234: etc "
+ handled automatically as 0::234
+ with allowing :: to represent 0 or more
+ groups instead of 1 or more */
+
+ parts[num_cnt] = num;
+ num = 0;
+ ++num_cnt;
+ /* combine two decimal if needed */
+ if (dot_cnt == 2 || (dot_cnt == 3 && (c == 0 || c == '/'))) {
+ --num_cnt;
+ parts[num_cnt - 1] = parts[num_cnt - 1] * 256 + parts[num_cnt];
+ }
+ if (*p == 0)
+ break;
+ }
+ /* replace '::' with 0s */
+ if (dot_dot_pos != -1 && total != num_cnt) {
+ int i;
+ gap = total - num_cnt;
+ if (dot_dot_pos != num_cnt)
+ memmove(parts+dot_dot_pos+gap, parts+dot_dot_pos,
+ (num_cnt-dot_dot_pos)*sizeof(int));
+ for (i = 0; i<gap; ++i)
+ parts[dot_dot_pos+i] = 0;
+ }
+}
+
+/***************************************************
+ switch_path:
+ switch m_path between mcd and ccd directories -
+ modified (UNIONFS) configuration and changed
+ (real) configuration
+****************************************************/
+void switch_path(first_seg *segp)
+{
+ memcpy(m_path.path_buf + segp->f_segoff, segp->f_segp,
+ segp->f_seglen);
+ m_path.path = m_path.path_buf + segp->f_segoff;
+}
+
+/*************************************************
+ validate_value:
+ validates value against type and syntax
+ return TRUE if OK, FALSE otherwise
+**************************************************/
+boolean validate_value(vtw_def *def, char *cp)
+{
+ int status;
+ boolean ret=TRUE;
+
+ /* prepare cur_value */
+ set_at_string(cp);
+ status = char2val(def, cp, &validate_value_val);
+ if (status != VTWERR_OK)
+ return FALSE;
+ if ((def->def_type!=ERROR_TYPE) &&
+ (validate_value_val.val_type != def->def_type)) {
+ if (def->def_type_help){
+ (void)expand_string(def->def_type_help);
+ printf("%s\n", exe_string);
+ fprintf(stderr, "%s\n", exe_string);
+ } else {
+ printf("Incorrect type of %s, need %s\n",
+ cp, type_to_name(def->def_type));
+ }
+ ret = FALSE;
+ goto validate_value_free_and_return;
+ }
+ ret = TRUE;
+ if (def->actions && def->actions[syntax_act].vtw_list_head){
+ in_validate_val = TRUE;
+ ret = check_syn(def->actions[syntax_act].vtw_list_head);
+ in_validate_val = FALSE;
+ }
+ validate_value_free_and_return:
+ free_val(&validate_value_val);
+ return ret;
+}
+
+
+int cli_val_read(char *buf, int max_size)
+{
+ int len;
+
+ if (cli_val_len > max_size)
+ len = max_size;
+ else
+ len = cli_val_len;
+ if (len) {
+ (void)memcpy(buf, cli_val_ptr, len);
+ cli_val_len -= len;
+ cli_val_ptr += len;
+ }
+ return len;
+}
+/*==========================================================*/
+/* MEMORY */
+/*==========================================================*/
+
+void *my_malloc(size_t size, const char *name)
+{
+ return malloc(size);
+}
+void *my_realloc(void *ptr, size_t size, const char *name)
+{
+ return realloc(ptr, size);
+}
+
+void my_free(void *ptr)
+{
+ free(ptr);
+}
+
+/*************************************************
+ my_strdup:
+ do a strdup,
+ exit on no memory
+**************************************************/
+char *my_strdup(const char *s, const char *name)
+{
+ return strdup(s);
+}
+
+void done()
+{
+ free_reuse_list();
+ free_path(&t_path);
+ free_path(&m_path);
+ if (exe_string)
+ my_free(exe_string);
+}
+void mark_paths(vtw_mark *markp)
+{
+ markp->m_lev = m_path.path_lev;
+ markp->t_lev = t_path.path_lev;
+}
+void restore_paths(vtw_mark *markp)
+{
+ while(markp->m_lev < m_path.path_lev)
+ pop_path(&m_path);
+ while(markp->t_lev < t_path.path_lev)
+ pop_path(&t_path);
+}
+
+void touch()
+{
+ char *command;
+ command = my_malloc(strlen(get_mdirp()) + 20, "delete");
+ sprintf(command, "touch %s/%s", get_mdirp(), MOD_NAME);
+ system(command);
+ free(command);
+}
+
+char *type_to_name(vtw_type_e type) {
+ switch(type) {
+ case INT_TYPE: return("u32");
+ case IPV4_TYPE: return("ipv4");
+ case IPV4NET_TYPE: return("ipv4net");
+ case IPV6_TYPE: return("ipv6");
+ case IPV6NET_TYPE: return("ipv6net");
+ case MACADDR_TYPE: return("macaddr");
+ case DOMAIN_TYPE: return("domain");
+ case TEXT_TYPE: return("text");
+ case BOOL_TYPE: return("bool");
+ default: return("unknown");
+ }
+}
+
+#if 1
+void
+dump_log(int argc, char **argv)
+{
+}
+#else
+void dump_log(int argc, char **argv)
+{
+ int i;
+ int len;
+ char *cp;
+
+ len = 0;
+ for (i=0; i<argc;++i)
+ len += strlen(argv[i]);
+ len += argc;
+ cp = my_malloc(len, "dump_log");
+ len = 0;
+ for (i=0; i<argc;++i){
+ strcpy(cp+len, argv[i]);
+ len += strlen(argv[i]);
+ cp[len]= ' ';
+ ++len;
+ }
+ cp[len-1]=0;
+ printf("Command: %s\n",cp);
+ my_free(cp);
+}
+#endif
+
+/********************* New Dir ****************************/
+
+static int set_reference_environment(const char* var_reference,
+ clind_path_ref *n_cfg_path,
+ clind_path_ref *n_tmpl_path,
+ clind_path_ref *n_cmd_path,
+ int active) {
+
+ if(var_reference && n_cfg_path && n_tmpl_path && n_cmd_path) {
+
+ if(*var_reference=='/') {
+
+ if(is_in_delete_action()) {
+ *n_cfg_path=clind_path_construct(get_adirp());
+ } else {
+ *n_cfg_path=clind_path_construct(get_mdirp());
+ }
+ *n_tmpl_path=clind_path_construct(get_tdirp());
+ *n_cmd_path=clind_path_construct(var_reference);
+
+ } else {
+
+ vtw_path n_vt_path;
+
+ memset(&n_vt_path,0,sizeof(n_vt_path));
+
+ copy_path(&n_vt_path, &m_path);
+ /* switch to MPATH */
+ memcpy(n_vt_path.path_buf + get_f_seg_m_ptr()->f_segoff, get_f_seg_m_ptr()->f_segp,
+ get_f_seg_m_ptr()->f_seglen);
+ n_vt_path.path = n_vt_path.path_buf + get_f_seg_m_ptr()->f_segoff;
+
+ if(active) {
+
+ vtw_path active_path;
+
+ memset(&active_path,0,sizeof(active_path));
+
+ copy_path(&active_path, &n_vt_path);
+
+ memcpy(active_path.path_buf + get_f_seg_a_ptr()->f_segoff, get_f_seg_a_ptr()->f_segp,
+ get_f_seg_a_ptr()->f_seglen);
+ active_path.path = active_path.path_buf + get_f_seg_a_ptr()->f_segoff;
+
+ *n_cfg_path=clind_path_construct(active_path.path);
+
+ } else {
+
+ *n_cfg_path=clind_path_construct(n_vt_path.path);
+
+ }
+
+ *n_tmpl_path=clind_path_construct(t_path.path);
+ *n_cmd_path=clind_path_construct(var_reference);
+
+ }
+
+ return 0;
+
+ } else {
+
+ return -1;
+
+ }
+}
+
+/**********************************************************/
diff --git a/src/cli_objects.c b/src/cli_objects.c
new file mode 100644
index 0000000..9bb36f0
--- /dev/null
+++ b/src/cli_objects.c
@@ -0,0 +1,343 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <limits.h>
+#include <stdarg.h>
+#include "cli_val.h"
+#include "cli_parse.h"
+#include <regex.h>
+
+#include "cli_val_engine.h"
+
+#include "cli_objects.h"
+
+/************************ Storage area: *****************/
+
+static char *at_string=NULL;
+static boolean in_delete_action=FALSE;
+static valstruct cli_value;
+static boolean in_commit=FALSE; /* TRUE if in commit program*/
+static boolean in_exec=FALSE; /* TRUE if in exec */
+static boolean _is_echo=FALSE;
+static boolean _is_silent_msg=FALSE;
+static first_seg f_seg_a;
+static first_seg f_seg_c;
+static first_seg f_seg_m;
+static int in_cond_tik=0;
+
+/******************** Accessors: ************************/
+
+static char at_buffer[1024]={0};
+
+/* the string to use as $(@), must be set
+ before call to expand_string */
+char* get_at_string(void) {
+ if(at_string) {
+ return at_string;
+ } else {
+ return at_buffer;
+ }
+}
+
+void set_at_string(char* s) {
+ if(s!=at_buffer) {
+ at_string=s;
+ } else {
+ at_string=NULL;
+ }
+}
+
+void free_at_string(void) {
+ if(at_string) {
+ if(at_string!=at_buffer) free(at_string);
+ at_string=NULL;
+ }
+}
+
+boolean is_in_delete_action(void) {
+ return in_delete_action;
+}
+
+void set_in_delete_action(boolean b) {
+ in_delete_action=b;
+}
+
+boolean is_in_commit(void) {
+ return in_commit;
+}
+
+void set_in_commit(boolean b) {
+ in_commit=b;
+}
+
+boolean is_in_exec(void) {
+ return in_exec;
+}
+
+void set_in_exec(boolean b) {
+ in_exec=b;
+}
+
+boolean is_echo(void) {
+ return _is_echo;
+}
+
+void set_echo(boolean b) {
+ _is_echo=b;
+}
+
+boolean is_silent_msg(void) {
+ return _is_silent_msg;
+}
+
+void set_silent_msg(boolean b) {
+ _is_silent_msg=b;
+}
+
+valstruct* get_cli_value_ptr(void) {
+ return &cli_value;
+}
+
+first_seg* get_f_seg_a_ptr(void) {
+ return &f_seg_a;
+}
+
+first_seg* get_f_seg_c_ptr(void) {
+ return &f_seg_c;
+}
+
+first_seg* get_f_seg_m_ptr(void) {
+ return &f_seg_m;
+}
+
+int is_in_cond_tik(void) {
+ return in_cond_tik;
+}
+
+void set_in_cond_tik(int ict) {
+ in_cond_tik=ict;
+}
+
+void dec_in_cond_tik(void) {
+ --in_cond_tik;
+}
+
+const char* get_tdirp(void) {
+
+ const char* tdirp=getenv(ENV_T_DIR);
+
+ if (!tdirp)
+ tdirp = DEF_T_DIR;
+ if(!tdirp)
+ tdirp="";
+
+ return tdirp;
+}
+
+const char* get_cdirp(void) {
+ return getenv(ENV_C_DIR);
+}
+
+const char* get_adirp(void) {
+
+ const char* adirp=getenv(ENV_A_DIR);
+
+ if (!adirp)
+ adirp = DEF_A_DIR;
+ if(!adirp)
+ adirp="";
+
+ return adirp;
+}
+
+const char* get_mdirp(void) {
+
+ const char* mdirp=getenv(ENV_M_DIR);
+
+ if(!mdirp)
+ mdirp="";
+
+ return mdirp;
+}
+
+const char* get_tmpp(void) {
+
+ const char* tmpp=getenv(ENV_TMP_DIR);
+
+ if(!tmpp)
+ tmpp="";
+
+ return tmpp;
+}
+
+char* get_elevp(void) {
+
+ static char elevp_buffer[2049];
+ static char* elevp=NULL;
+
+ if(elevp==NULL) {
+
+ const char* tmp=getenv(ENV_EDIT_LEVEL);
+
+ if(tmp) {
+ strncpy(elevp_buffer,tmp,sizeof(elevp_buffer)-1);
+ elevp=elevp_buffer;
+ }
+ }
+
+ return elevp;
+}
+
+char* get_tlevp(void) {
+
+ static char tlevp_buffer[2049];
+ static char* tlevp=NULL;
+
+ if(tlevp==NULL) {
+
+ const char* tmp=getenv(ENV_TEMPLATE_LEVEL);
+
+ if(tmp) {
+ strncpy(tlevp_buffer,tmp,sizeof(tlevp_buffer)-1);
+ tlevp=tlevp_buffer;
+ }
+ }
+
+ return tlevp;
+}
+
+/************************* Init ***************************/
+
+void init_edit()
+{
+ int elevlen = 0;
+ int tlevlen = 0;
+
+ init_paths(TRUE);
+ if (!get_elevp())
+ bye("Not in configuration mode");
+ if (!get_tlevp())
+ bye("INTERNAL: environment var |%s| is not set",ENV_TEMPLATE_LEVEL);
+ elevlen = strlen(get_elevp());
+ tlevlen = strlen(get_tlevp());
+ if (elevlen > 0 && get_elevp()[elevlen - 1]=='/') {
+ /* cut off terminateing slash */
+ --elevlen;
+ get_elevp()[elevlen] = 0;
+ }
+ if (elevlen) {
+ char *slashp;
+ char * scanp;
+ if (*get_elevp()!='/')
+ INTERNAL;
+ scanp = get_elevp() + 1;
+ while (TRUE) {
+ slashp = strchr(scanp, '/');
+ if (slashp)
+ *slashp = 0;
+ push_path(&m_path, scanp);
+ if (slashp) {
+ *slashp = '/';
+ scanp = slashp+1;
+ }else
+ break;
+ }
+ }
+ switch_path(MPATH);
+ if (tlevlen > 0 && get_tlevp()[tlevlen - 1]=='/') {
+ /* cut off terminateing slash */
+ --tlevlen;
+ get_tlevp()[tlevlen] = 0;
+ }
+ if (tlevlen) {
+ char *slashp;
+ char * scanp;
+ if (*get_tlevp()!='/')
+ INTERNAL;
+ scanp = get_tlevp() + 1;
+ while (TRUE) {
+ slashp = strchr(scanp, '/');
+ if (slashp)
+ *slashp = 0;
+ push_path(&t_path, scanp);
+ if (slashp) {
+ *slashp = '/';
+ scanp = slashp+1;
+ }else
+ break;
+ }
+ }
+}
+
+void init_paths(boolean for_commit)
+{
+ struct stat statbuf;
+ const char* tdirp = get_tdirp();
+ const char* cdirp = get_cdirp();
+ const char* adirp = get_adirp();
+ const char* mdirp = get_mdirp();
+ const char* tmpp = get_tmpp();
+
+ if (!mdirp || !mdirp[0])
+ bye("Environment variable %s for temp configuration is not set",ENV_M_DIR);
+ if (!cdirp)
+ bye("INTERNAL: environment var |%s| is not set",
+ ENV_C_DIR);
+ if (!tmpp || !tmpp[0])
+ bye("INTERNAL: environment var |%s| is not set",
+ ENV_TMP_DIR);
+ /* make sure that template root is present */
+
+ if (lstat(tdirp, &statbuf) < 0)
+ bye("Template directory |%s| isn't present\n", tdirp);
+ if ((statbuf.st_mode & S_IFMT) != S_IFDIR)
+ bye("Template directory |%s| isn't a directory\n", tdirp);
+ /* set paths to current roots */
+ if (for_commit) {
+ int max_len;
+ const char *startp;
+
+ /* make sure that master configuration root is present */
+ if (lstat(adirp, &statbuf) < 0)
+ bye("Master configuration directory |%s| isn't present\n", adirp);
+ if ((statbuf.st_mode & S_IFMT) != S_IFDIR)
+ bye("Master configuration directory |%s| isn't a directory\n", adirp);
+
+ get_f_seg_a_ptr()->f_segp = adirp;
+ get_f_seg_c_ptr()->f_segp = cdirp;
+ get_f_seg_m_ptr()->f_segp = mdirp;
+ get_f_seg_a_ptr()->f_seglen = strlen(adirp);
+ get_f_seg_c_ptr()->f_seglen = strlen(cdirp);
+ get_f_seg_m_ptr()->f_seglen = strlen(mdirp);
+ if(get_f_seg_a_ptr()->f_seglen > get_f_seg_m_ptr()->f_seglen) {
+ max_len = get_f_seg_a_ptr()->f_seglen;
+ startp = adirp;
+ }else{
+ max_len = get_f_seg_m_ptr()->f_seglen;
+ startp = mdirp;
+ }
+ if(get_f_seg_c_ptr()->f_seglen > max_len) {
+ max_len = get_f_seg_c_ptr()->f_seglen;
+ startp = cdirp;
+ }
+ get_f_seg_a_ptr()->f_segoff = max_len - get_f_seg_a_ptr()->f_seglen;
+ get_f_seg_c_ptr()->f_segoff = max_len - get_f_seg_c_ptr()->f_seglen;
+ get_f_seg_m_ptr()->f_segoff = max_len - get_f_seg_m_ptr()->f_seglen;
+ init_path(&m_path, startp);
+ switch_path(get_f_seg_c_ptr());
+ m_path.print_offset = max_len;
+ }else
+ init_path(&m_path, mdirp);
+
+ init_path(&t_path, tdirp);
+}
+
+/**********************************************************/
diff --git a/src/cli_objects.h b/src/cli_objects.h
new file mode 100644
index 0000000..12b6b38
--- /dev/null
+++ b/src/cli_objects.h
@@ -0,0 +1,65 @@
+#ifndef CLI_OBJ_H
+#define CLI_OBJ_H
+
+#include "cli_val.h"
+
+#define APATH (get_f_seg_a_ptr())
+#define CPATH (get_f_seg_c_ptr())
+#define MPATH (get_f_seg_m_ptr())
+
+/* names of VYATTA env vars */
+#define ENV_EDIT_LEVEL "VYATTA_EDIT_LEVEL"
+#define ENV_TEMPLATE_LEVEL "VYATTA_TEMPLATE_LEVEL"
+#define ENV_A_DIR "VYATTA_ACTIVE_CONFIGURATION_DIR"
+#define ENV_C_DIR "VYATTA_CHANGES_ONLY_DIR"
+#define ENV_M_DIR "VYATTA_TEMP_CONFIG_DIR"
+#define ENV_T_DIR "VYATTA_CONFIG_TEMPLATE"
+#define ENV_TMP_DIR "VYATTA_CONFIG_TMP"
+#define DEF_A_DIR "/opt/vyatta/config/active"
+#define DEF_T_DIR "/opt/vyatta/share/ofr/template"
+#define ENV_OLD_PS1 "VYATTA_OLD_PS1"
+
+/* the string to use as $(@), must be set
+ before call to expand_string */
+char* get_at_string(void);
+void set_at_string(char* s);
+void free_at_string(void);
+
+boolean is_in_delete_action(void);
+void set_in_delete_action(boolean b);
+
+boolean is_in_commit(void);
+void set_in_commit(boolean b);
+
+boolean is_in_exec(void);
+void set_in_exec(boolean b);
+
+boolean is_echo(void);
+void set_echo(boolean b);
+
+boolean is_silent_msg(void);
+void set_silent_msg(boolean b);
+
+valstruct* get_cli_value_ptr(void);
+
+first_seg* get_f_seg_a_ptr(void);
+first_seg* get_f_seg_c_ptr(void);
+first_seg* get_f_seg_m_ptr(void);
+
+int is_in_cond_tik(void);
+void set_in_cond_tik(int ict);
+void dec_in_cond_tik(void);
+
+const char* get_tdirp(void);
+const char* get_cdirp(void);
+const char* get_adirp(void);
+const char* get_mdirp(void);
+const char* get_tmpp(void);
+
+char* get_elevp(void);
+char* get_tlevp(void);
+
+void init_edit(void);
+void init_paths(boolean for_commit);
+
+#endif /* CLI_OBJ_H */
diff --git a/src/cli_parse.y b/src/cli_parse.y
new file mode 100644
index 0000000..f0e43a8
--- /dev/null
+++ b/src/cli_parse.y
@@ -0,0 +1,206 @@
+%{
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "cli_val.h"
+
+extern int yy_cli_def_lineno;
+static vtw_def *parse_defp;
+static int parse_status;
+static boolean cli_def_type_only=0; /*{ if (cli_def_type_only) YYACCEPT;}*/
+/* XXX: sigh, the -p flag to yacc should do this for us kkkk*/
+#define yystacksize tpltstacksize
+#define yysslim tpltsslim
+
+/* forward prototypes */
+extern int yy_cli_parse_parse();
+extern int yy_cli_def_lex();
+extern void yy_cli_parse_error(const char *);
+static void cli_deferror(const char *);
+ extern FILE *yy_cli_def_in;
+#define YYDEBUG 1
+#define yy_cli_parse_lex yy_cli_def_lex
+%}
+%token EOL
+%token MULTI
+%token TAG
+%token TYPE
+%token HELP
+%token DEFAULT
+%token PATTERN
+%token EXEC
+%token SYNTAX
+%token COMMIT
+%token CHECK
+%left SEMI
+%token <val>VALUE
+%token <type>TYPE_DEF
+%token <strp>VAR
+%token <strp> STRING
+%token <strp> EX_STRING
+%token SYNTAX_ERROR
+%token <action>ACTION
+%left ASSIGN
+%left OR
+%left AND
+%right NOT
+%left COMMA
+%nonassoc <cond>COND
+%token RP
+%token LP
+%type <nodep> val
+%type <nodep> exp
+%type <val> val0
+%type <nodep> action
+%type <nodep> action0
+
+%union {
+ char *strp;
+ valstruct val;
+ vtw_type_e type;
+ vtw_cond_e cond;
+ vtw_node *nodep;
+ vtw_act_type action;
+}
+
+%%
+
+input: tag
+ | EOL input
+ | tag otherinput
+ ;
+
+otherinput: type EOL
+ | cause EOL
+ | otherinput type EOL
+ | otherinput cause EOL
+ | otherinput EOL
+ | syntax_error
+ ;
+
+tag: /* empty */
+ | TAG EOL {parse_defp->tag = TRUE;}
+ | MULTI EOL {parse_defp->multi = TRUE;}
+ ;
+
+type: TYPE TYPE_DEF SEMI STRING
+ { parse_defp->def_type = $2;
+ parse_defp->def_type_help = $4; }
+ ;
+
+
+type: TYPE TYPE_DEF
+ { parse_defp->def_type = $2;
+ }
+ ;
+
+cause: help_cause
+ | default_cause
+ | syntax_cause
+ | ACTION action { append(parse_defp->actions + $1, $2, 0);}
+ ;
+
+help_cause: HELP STRING
+ { parse_defp->def_type_help = $2; /* no semantics for now */
+ }
+
+default_cause: DEFAULT VALUE
+ {
+ if ($2.val_type != parse_defp->def_type)
+ yy_cli_parse_error((const char *)"Bad default\n");
+ parse_defp->def_default = $2.val;
+ }
+default_cause: DEFAULT STRING
+ {
+ if (TEXT_TYPE != parse_defp->def_type)
+ yy_cli_parse_error((const char *)"Bad default\n");
+ parse_defp->def_default = $2;
+ }
+
+syntax_cause: SYNTAX exp {append(parse_defp->actions + syntax_act, $2, 0);}
+ ;
+
+syntax_cause: COMMIT exp {append(parse_defp->actions + syntax_act, $2, 1);}
+ ;
+
+action0: STRING { $$ = make_node(EXEC_OP, make_str_node($1),NULL);}
+ ;
+action: action0
+ | action0 SEMI STRING
+ {$$ = make_node(HELP_OP, $1, make_str_node($3));}
+ | exp
+ ;
+exp: LP exp RP
+ {$$=$2;}
+ | exp AND exp {$$ = make_node(AND_OP,$1,$3);}
+ | exp OR exp {$$ = make_node(OR_OP,$1,$3);}
+ | val COND val
+ {$$ = make_node(COND_OP,$1,$3);$$->vtw_node_aux = $2;}
+ | PATTERN VAR STRING
+ { $$ = make_node(PATTERN_OP,make_var_node($2),
+ make_str_node($3));}
+ | EXEC STRING
+ { $$ = make_node(EXEC_OP,make_str_node($2),NULL);}
+ | NOT exp {$$ = make_node(NOT_OP,$2,NULL);}
+ | exp SEMI STRING
+ {$$ = make_node(HELP_OP, $1, make_str_node($3));}
+ | VAR ASSIGN val
+ {$$ = make_node(ASSIGN_OP, make_var_node($1), $3);}
+ ;
+
+val: VAR {$$ = make_var_node($1);}
+ | val0 {$$ = make_val_node(&($1));}
+ | EX_STRING {$$=make_str_node0($1, B_QUOTE_OP);}
+ ;
+
+val0: VALUE
+
+ | val0 COMMA val0 { add_val(&($1), &($3)); $$=$1; }
+
+ | STRING {$$ = str2val($1);}
+ ;
+
+syntax_error: SYNTAX_ERROR {
+ cli_deferror("syntax error");
+ }
+ ;
+
+
+%%
+char *parse_path;
+int parse_def(vtw_def *defp, char *path, boolean type_only)
+{
+ int status;
+ yy_cli_def_lineno = 1;
+ parse_status = 0;
+ parse_defp = defp;
+ cli_def_type_only = type_only;
+ yy_cli_def_in = fopen(path, "r");
+#if 0
+ yy_cli_parse_debug = 1;
+#endif
+ if (!yy_cli_def_in)
+ return -5;
+ parse_path = path;
+ status = yy_cli_parse_parse(); /* 0 is OK */
+ fclose(yy_cli_def_in);
+ return status;
+}
+static void
+cli_deferror(const char *s)
+{
+ printf("Error: %s in file %s, line %d\n",s, parse_path,
+ yy_cli_def_lineno);
+}
+
+void yy_cli_parse_error(const char *s)
+{
+ cli_deferror(s);
+}
+
+
+
+
+
diff --git a/src/cli_path_utils.c b/src/cli_path_utils.c
new file mode 100644
index 0000000..4045516
--- /dev/null
+++ b/src/cli_path_utils.c
@@ -0,0 +1,535 @@
+
+/************************************************************************
+
+ Module: cli
+
+ **** License ****
+ Version: VPL 1.0
+
+ The contents of this file are subject to the Vyatta Public License
+ Version 1.0 ("License"); you may not use this file except in
+ compliance with the License. You may obtain a copy of the License at
+ http://www.vyatta.com/vpl
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ This code was originally developed by Vyatta, Inc.
+ Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc.
+ All Rights Reserved.
+
+ Author: Oleg Moskalenko
+ Date: 2007
+ Description: "new" cli path-handling utilities
+
+ **** End License ****
+
+*************************************************************************/
+
+#if !defined(_GNU_SOURCE)
+#define _GNU_SOURCE
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <regex.h>
+#include <dirent.h>
+
+#include <string.h>
+
+#include "cli_path_utils.h"
+
+/*********************
+ * Data definitions
+ *
+ *********************/
+
+typedef char* clind_dir_name;
+
+/**
+ * Definition of the path structure to hold all path-like information:
+ */
+
+struct _clind_path_impl {
+
+ int absolute;
+ int path_len;
+ char* path_string;
+ clind_dir_name* path;
+
+};
+
+/******************************
+ * Path utils. We use them
+ * to manipulate the path-like
+ * structures.
+ *
+ ******************************/
+
+static void clind_reset_path_string(clind_path_impl* obj) {
+
+ char* newpath=NULL;
+
+ if(obj->path_len<1) {
+
+ newpath=strdup("");
+
+ } else {
+
+ int i=0;
+
+ if(!obj->absolute || (strlen(obj->path[0])>0 && ((char*)(obj->path[0]))[0]=='/')) {
+ newpath=strdup(obj->path[0]);
+ } else {
+ newpath=(char*)malloc(strlen(obj->path[0])+1+1);
+ newpath[0]='/';
+ strcpy(newpath+1,(char*)(obj->path[0]));
+ }
+
+ for(i=1;i<obj->path_len;i++) {
+ newpath=(char*)realloc(newpath,strlen(newpath)+1+strlen(obj->path[i])+1);
+ strcpy(newpath+strlen(newpath),"/");
+ strcpy(newpath+strlen(newpath),obj->path[i]);
+ }
+ }
+
+ if(obj->path_string==NULL) {
+ obj->path_string=newpath;
+ } else {
+ obj->path_string=(char*)realloc(obj->path_string,strlen(newpath)+1);
+ strcpy(obj->path_string,newpath);
+ free(newpath);
+ }
+}
+
+clind_path_ref clind_path_construct(const char* path) {
+
+ if(!path) return NULL;
+ else {
+
+ const char* delim="/ \t";
+
+ clind_path_impl *obj = (clind_path_impl*)malloc(sizeof(clind_path_impl));
+ char* tokpath=strdup(path);
+ char* token=strtok(tokpath,delim);
+
+ obj->path_len=0;
+ obj->path_string=strdup("");
+ obj->path=NULL;
+
+ while(token) {
+ clind_path_push((clind_path_ref)obj,token);
+ token=strtok(NULL,delim);
+ }
+
+ free(tokpath);
+
+ obj->absolute=(*path=='/');
+
+ clind_reset_path_string(obj);
+
+ return (clind_path_ref)obj;
+ }
+}
+
+void clind_path_destruct(clind_path_ref* path) {
+
+ if(path && *path) {
+
+ clind_path_impl* obj = (clind_path_impl*)(*path);
+
+ if(obj->path_string) {
+ free(obj->path_string);
+ obj->path_string=NULL;
+ }
+
+ if(obj->path) {
+ while(obj->path_len>0) {
+ char* dir_name = (char*)(obj->path[obj->path_len-1]);
+ if(dir_name) {
+ free(dir_name);
+ }
+ obj->path_len--;
+ }
+ free(obj->path);
+ obj->path=0;
+ }
+
+ *path=0;
+ }
+}
+
+clind_path_ref clind_path_clone(const clind_path_ref path) {
+
+ clind_path_ref ret=NULL;
+ if(path) {
+
+ clind_path_impl* obj = (clind_path_impl*)path;
+
+ ret = clind_path_construct(obj->path_string);
+
+ if(ret) {
+
+ ((clind_path_impl*)ret)->absolute=obj->absolute;
+
+ clind_reset_path_string((clind_path_impl*)ret);
+ }
+ }
+
+ return ret;
+}
+
+int clind_path_get_size(clind_path_ref path) {
+ if(path) {
+ clind_path_impl* obj = (clind_path_impl*)path;
+ return obj->path_len;
+ }
+ return 0;
+}
+
+const char* clind_path_get_path_string(clind_path_ref path) {
+ if(path) {
+ clind_path_impl* obj = (clind_path_impl*)path;
+ if(obj->path_string) {
+ return obj->path_string;
+ }
+ }
+ return "";
+}
+
+int clind_path_is_absolute(clind_path_ref path) {
+ if(path) {
+ clind_path_impl* obj = (clind_path_impl*)path;
+ return obj->absolute;
+ }
+ return 0;
+}
+
+void clind_path_push(clind_path_ref path,const char* dir) {
+
+ if(path && dir && *dir) {
+
+ clind_path_impl* obj = (clind_path_impl*)path;
+ int absolute=(*dir=='/');
+
+ while(*dir && *dir=='/') dir++;
+
+ if(obj->path_len<=0) {
+
+ obj->path_len=1;
+ obj->absolute=absolute;
+
+ if(obj->path) {
+ free(obj->path);
+ }
+
+ obj->path=(clind_dir_name*)malloc(sizeof(clind_dir_name));
+ obj->path[0]=(clind_dir_name)strdup(dir);
+
+ } else {
+
+ obj->path_len++;
+
+ obj->path=(clind_dir_name*)realloc(obj->path,
+ sizeof(clind_dir_name)*(obj->path_len));
+ obj->path[obj->path_len-1]=strdup(dir);
+ }
+
+ clind_reset_path_string(obj);
+ }
+}
+
+char* clind_path_pop_string(clind_path_ref path) {
+
+ char* ret=NULL;
+
+ if(path) {
+
+ clind_path_impl* obj = (clind_path_impl*)path;
+
+ if(obj->path_len<=0) {
+ return ret;
+ }
+
+ obj->path_len--;
+
+ if(obj->path) {
+ if(obj->path[obj->path_len]) {
+ ret = obj->path[obj->path_len];
+ obj->path[obj->path_len]=NULL;
+ }
+ }
+
+ if(obj->path_len<1) {
+ obj->absolute=0;
+ }
+
+ clind_reset_path_string(obj);
+ }
+
+ return ret;
+}
+
+int clind_path_pop(clind_path_ref path) {
+
+ int ret=-1;
+
+ char* ps = clind_path_pop_string(path);
+
+ if(ps) {
+ free(ps);
+ ret=0;
+ }
+
+ return ret;
+}
+
+const char* clind_path_last_string(clind_path_ref path) {
+
+ char* ret=NULL;
+
+ if(path) {
+ clind_path_impl* obj = (clind_path_impl*)path;
+ if(obj->path && obj->path_len>0) {
+ ret=obj->path[obj->path_len-1];
+ }
+ }
+
+ return ret;
+}
+
+void clind_path_unshift(clind_path_ref path,const char* dir) {
+
+ if(path && dir && *dir) {
+
+ clind_path_impl* obj = (clind_path_impl*)path;
+
+ int absolute=(*dir=='/');
+
+ while(*dir && *dir=='/') dir++;
+
+ if(obj->path_len<=0) {
+
+ clind_path_push(path,dir);
+
+ } else {
+
+ obj->path_len++;
+
+ obj->path=(clind_dir_name*)realloc(obj->path,
+ sizeof(clind_dir_name)*(obj->path_len));
+ memmove((char*)(obj->path)+sizeof(clind_dir_name),(char*)(obj->path),
+ sizeof(clind_dir_name)*(obj->path_len-1));
+ obj->path[0]=strdup(dir);
+
+ }
+
+ obj->absolute=absolute;
+
+ clind_reset_path_string(obj);
+ }
+}
+
+const char* clind_path_get_string(clind_path_ref path,int index) {
+
+ const char* ret=NULL;
+
+ if(path) {
+ clind_path_impl* obj = (clind_path_impl*)path;
+ if(obj->path && obj->path_len>index) {
+ ret=obj->path[index];
+ }
+ }
+
+ return ret;
+}
+
+const char* clind_path_first_string(clind_path_ref path) {
+ return clind_path_get_string(path,0);
+}
+
+char* clind_path_shift_string(clind_path_ref path) {
+
+ char* ret=NULL;
+
+ if(path) {
+
+ clind_path_impl* obj = (clind_path_impl*)path;
+
+ if(obj->path_len<=0) {
+ return ret;
+ }
+
+ obj->path_len--;
+
+ if(obj->path) {
+ if(obj->path[0]) {
+ ret = obj->path[0];
+ obj->path[0]=NULL;
+ }
+
+ memmove((char*)(obj->path),(char*)(obj->path)+sizeof(clind_dir_name),
+ sizeof(clind_dir_name)*obj->path_len);
+ }
+
+ obj->absolute=0;
+
+ clind_reset_path_string(obj);
+ }
+
+ return ret;
+}
+
+int clind_path_shift(clind_path_ref path) {
+
+ int ret=-1;
+
+ char* ps = clind_path_shift_string(path);
+
+ if(ps) {
+ free(ps);
+ ret=0;
+ }
+
+ return ret;
+}
+
+void clind_path_debug_print(clind_path_ref path) {
+
+ if(path) {
+
+ int i=0;
+ clind_path_impl* obj = (clind_path_impl*)path;
+
+ if(obj->path_string) {
+ printf("obj->path_string=%s, obj->path_len=%d,obj->absolute=%d\n",
+ obj->path_string,obj->path_len,obj->absolute);
+ } else {
+ printf("obj->path_string=NULL, obj->path_len=%d,obj->absolute=%d\n",
+ obj->path_len,obj->absolute);
+ }
+
+ if(obj->path) {
+ for(i=0;i<obj->path_len;i++) {
+ if(obj->path[i]) {
+ printf(" obj->path[%d]=%s\n",i,obj->path[i]);
+ } else {
+ printf(" obj->path[%d]=NULL\n",i);
+ }
+ }
+ } else {
+ printf(" obj->path=NULL\n");
+ }
+ }
+}
+
+int clind_file_exists(const char* dir,const char* file) {
+
+ int ret=0;
+
+ if(file) {
+
+ char* fname=strdup(file);
+ struct stat statbuf;
+
+ if(dir) {
+ free(fname);
+ fname=(char*)malloc(strlen(dir)+1+strlen(file)+1);
+ strcpy(fname,dir);
+ strcpy(fname+strlen(fname),"/");
+ strcpy(fname+strlen(fname),file);
+ }
+
+ if (lstat(fname, &statbuf) == 0) {
+ ret=1;
+ }
+
+ free(fname);
+ }
+
+ return ret;
+}
+
+char *clind_unescape(const char *name)
+{
+ const char *cp;
+ char *rcp, *ret;
+ char len;
+
+ for(cp=name, len=0;*cp;++cp, ++len)
+ if(*cp=='%')
+ cp +=2;
+ rcp = ret = malloc(len+1);
+ for(cp=name, len=0;*cp;++cp, ++rcp)
+ if(*cp=='%') {
+ ++cp;
+ if (*cp >='a' && *cp<='f')
+ *rcp = (*cp-'a'+10)*16;
+ else if (*cp >='A' && *cp<='F')
+ *rcp = (*cp-'A'+10)*16;
+ else if (*cp >='0' && *cp<='9')
+ *rcp = (*cp-'0')*16;
+ else {
+ printf("Bad escape in |%s|\n", name);
+ exit(-1);
+ }
+ ++cp;
+ if (*cp >='a' && *cp<='f')
+ *rcp += (*cp-'a'+10);
+ else if (*cp >='A' && *cp<='F')
+ *rcp += (*cp-'A'+10);
+ else if (*cp >='0' && *cp<='9')
+ *rcp += (*cp-'0');
+ else {
+ printf("Bad escape in |%s|\n", name);
+ exit(-1);
+ }
+ }else
+ *rcp = *cp;
+ *rcp = 0;
+ return ret;
+}
+
+char* clind_quote(const char* s) {
+
+ char* ret=NULL;
+ if(s) {
+ int i=0;
+ int len=strlen(s);
+ int sz=0;
+ char SQ='\'';
+
+ ret=(char*)malloc(1+5*len+1+1+10);
+ ret[sz++]=SQ;
+
+ for(i=0;i<len;i++) {
+ if(s[i]==SQ) {
+ ret[sz++]=SQ;/*'\''*/
+ ret[sz++]='\\';
+ ret[sz++]=SQ;
+ ret[sz++]=SQ;
+ } else {
+ ret[sz++]=s[i];
+ }
+ }
+
+ ret[sz++]=SQ;
+ ret[sz]=0;
+ }
+ return ret;
+}
+
+
diff --git a/src/cli_path_utils.h b/src/cli_path_utils.h
new file mode 100644
index 0000000..6cc3be8
--- /dev/null
+++ b/src/cli_path_utils.h
@@ -0,0 +1,72 @@
+
+/************************************************************************
+
+ Module: cli
+
+ **** License ****
+ Version: VPL 1.0
+
+ The contents of this file are subject to the Vyatta Public License
+ Version 1.0 ("License"); you may not use this file except in
+ compliance with the License. You may obtain a copy of the License at
+ http://www.vyatta.com/vpl
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ This code was originally developed by Vyatta, Inc.
+ Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc.
+ All Rights Reserved.
+
+ Author: Oleg Moskalenko
+ Date: 2007
+ Description: "new" cli path-handling utilities
+
+ **** End License ****
+
+*************************************************************************/
+
+#if !defined(__CLI_PATH_UTILS__)
+#define __CLI_PATH_UTILS__
+
+/*******************
+ * Type definitions
+ *
+ *******************/
+
+typedef struct _clind_path_impl clind_path_impl;
+typedef clind_path_impl* clind_path_ref;
+
+/************************
+ * Path utils
+ *
+ ************************/
+
+clind_path_ref clind_path_construct(const char* path);
+void clind_path_destruct(clind_path_ref* path);
+clind_path_ref clind_path_clone(const clind_path_ref path);
+
+int clind_path_get_size(clind_path_ref path);
+const char* clind_path_get_path_string(clind_path_ref path);
+void clind_path_debug_print(clind_path_ref path);
+int clind_path_is_absolute(clind_path_ref path);
+
+int clind_path_pop(clind_path_ref path);
+char* clind_path_pop_string(clind_path_ref path);
+const char* clind_path_last_string(clind_path_ref path);
+void clind_path_push(clind_path_ref path,const char* dir);
+
+int clind_path_shift(clind_path_ref path);
+char* clind_path_shift_string(clind_path_ref path);
+const char* clind_path_get_string(clind_path_ref path,int index);
+const char* clind_path_first_string(clind_path_ref path);
+void clind_path_unshift(clind_path_ref path,const char* dir);
+
+int clind_file_exists(const char* dir,const char* file);
+
+char *clind_unescape(const char *name);
+char* clind_quote(const char* s);
+
+#endif /* __CLI_PATH_UTILS__ */
diff --git a/src/cli_val.h b/src/cli_val.h
new file mode 100644
index 0000000..8a98a88
--- /dev/null
+++ b/src/cli_val.h
@@ -0,0 +1,206 @@
+#ifndef CLI_DEF_H
+#define CLI_DEF_H
+
+#define BITWISE 0 /* no partial commit */
+
+#define boolean int
+#define TRUE 1
+#define FALSE 0
+
+/* allocation unit for vals in valstruct */
+#define MULTI_ALLOC 5 /* we have room if cnt%MULTI_ALLOC != 0 */
+
+typedef enum {
+ do_del_mode,
+ del_mode,
+ create_mode,
+ update_mode
+}vtw_cmode;
+typedef enum {
+ ERROR_TYPE,
+ INT_TYPE,
+ IPV4_TYPE,
+ IPV4NET_TYPE,
+ IPV6_TYPE,
+ IPV6NET_TYPE,
+ MACADDR_TYPE,
+ DOMAIN_TYPE, /*end of addr types */
+ TEXT_TYPE,
+ BOOL_TYPE
+}vtw_type_e;
+
+typedef enum {
+ EQ_COND = 1,
+ NE_COND,
+ LT_COND,
+ LE_COND,
+ GT_COND,
+ GE_COND,
+ IN_COND,
+ TOP_COND
+}vtw_cond_e;
+/* IN_COND is like EQ for singular compare, but OR for multivalue right operand */
+
+typedef enum {
+ LIST_OP, /* right is next, left is list elem */
+ HELP_OP, /* right is help string, left is elem */
+ EXEC_OP, /* left command string, right help string */
+ PATTERN_OP, /* left to var, right to pattern */
+ OR_OP,
+ AND_OP,
+ NOT_OP,
+ COND_OP, /* aux field specifies cond type (GT, GE, etc.)*/
+ VAL_OP, /* for strings used in other nodes */
+ VAR_OP, /* string points to var */
+ B_QUOTE_OP, /* string points to operand to be executed */
+ ASSIGN_OP /* left to var, right to exp */
+}vtw_oper_e;
+
+typedef struct {
+ vtw_type_e val_type;
+ char *val;
+ int cnt; /* >0 means multivalue */
+ char **vals; /* We might union with val */
+ boolean free_me;
+}valstruct;
+
+typedef struct vtw_node{
+ vtw_oper_e vtw_node_oper;
+ struct vtw_node *vtw_node_left;
+ struct vtw_node *vtw_node_right;
+ char *vtw_node_string;
+ int vtw_node_aux;
+ vtw_type_e vtw_node_type;
+ valstruct vtw_node_val; /* we'll union it later */
+}vtw_node;
+
+typedef struct {
+ vtw_node *vtw_list_head;
+ vtw_node *vtw_list_tail;
+}vtw_list;
+
+typedef struct {
+ int t_lev;
+ int m_lev;
+}vtw_mark;
+
+typedef enum {
+ delete_act,
+ create_act,
+ activate_act,
+ update_act,
+ syntax_act,
+ commit_act,
+ begin_act,
+ end_act,
+ top_act
+}vtw_act_type;
+
+typedef struct {
+ vtw_type_e def_type;
+ char *def_type_help;
+ char *def_default;
+ boolean tag;
+ boolean multi;
+ vtw_list actions[top_act];
+}vtw_def;
+
+typedef struct {
+ const char *f_segp;
+ int f_seglen;
+ int f_segoff;
+} first_seg;
+/* the first segment might be ADIR, or CDIR, or MDIR
+ we reserve space large enough for any one.
+ If the shorter one is used, it right aligned.
+ path points to the start of the current first
+ segment
+*/
+typedef struct {
+ char *path_buf; /* path buffer */
+ char *path; /* path */
+ int path_len; /* path length used */
+ int path_alloc; /* allocated - 1*/
+ int *path_ends; /* path ends for dif levels*/
+ int path_lev; /* how many used */
+ int path_ends_alloc; /* how many allocated */
+ int print_offset; /* for additional optional output information */
+} vtw_path; /* vyatta tree walk */
+
+typedef struct {
+ int num;
+ int partnum;
+ void **ptrs;
+ unsigned int *parts;
+}vtw_sorted;
+
+extern int char2val(vtw_def *def, char *value, valstruct *valp);
+extern int get_value(char **valpp, vtw_path *pathp);
+extern int get_value_to_at_string(vtw_path *pathp);
+extern vtw_node * make_node(vtw_oper_e oper, vtw_node *left,
+ vtw_node *right);
+extern vtw_node *make_str_node(char *str);
+extern vtw_node *make_var_node(char *str);
+extern vtw_node *make_str_node0(char *str, vtw_oper_e op);
+extern void append(vtw_list *l, vtw_node *n, int aux);
+extern int parse_def(vtw_def *defp, char *path, boolean type_only);
+
+extern int yy_cli_val_lex(void);
+extern void cli_val_start(char *s);
+extern void cli_val_done(void);
+extern void init_path(vtw_path *path, const char *root);
+extern void pop_path(vtw_path *path);
+extern void push_path(vtw_path *path, char *segm);
+extern void free_def(vtw_def *defp);
+extern void free_sorted(vtw_sorted *sortp);
+extern void *my_malloc(size_t size, const char *name);
+extern void *my_realloc(void *ptr, size_t size, const char *name);
+
+extern vtw_path m_path, t_path;
+
+/*************************************************
+ GLOBAL FUNCTIONS
+***************************************************/
+extern void add_val(valstruct *first, valstruct *second);
+extern int cli_val_read(char *buf, int max_size);
+extern vtw_node *make_val_node(valstruct *val);
+extern char *my_strdup(const char *s, const char *name);
+extern valstruct str2val(char *cp);
+extern void dump_tree(vtw_node *node, int lev);
+extern void dump_def(vtw_def *defp);
+extern boolean val_cmp(valstruct *left, valstruct *right, vtw_cond_e cond);
+extern void out_of_memory(void);
+extern boolean validate_value(vtw_def *def,
+ char *value);
+extern void internal_error(int line, char *file);
+extern void done(void);
+extern void del_value(vtw_def *defp, char *cp);
+extern void bye(char *msg, ...);
+extern void print_msg(char *msg, ...);
+extern void switch_path(first_seg *seg);
+extern void vtw_sort(valstruct *valp, vtw_sorted *sortp);
+extern void free_val(valstruct *val);
+extern void my_free(void *ptr);
+extern void touch(void);
+extern void dump_log(int argc, char **argv);
+extern char *type_to_name(vtw_type_e type);
+extern boolean execute_list(vtw_node *cur, vtw_def *def);
+extern void touch_dir(const char *dp);
+
+void mark_paths(vtw_mark *markp);
+void restore_paths(vtw_mark *markp);
+
+extern boolean get_config_lock(const char* adirp, const char* lock_name);
+
+#define VTWERR_BADPATH -2
+#define VTWERR_OK 0
+#define TAG_NAME "node.tag"
+#define DEF_NAME "node.def"
+#define VAL_NAME "node.val"
+#define MOD_NAME ".modified"
+#define OPQ_NAME ".wh.__dir_opaque"
+#define LOCK_NAME ".commit.lck"
+
+#define INTERNAL internal_error(__LINE__, __FILE__)
+
+#endif
diff --git a/src/cli_val.l b/src/cli_val.l
new file mode 100644
index 0000000..5856cea
--- /dev/null
+++ b/src/cli_val.l
@@ -0,0 +1,293 @@
+%{
+#include "cli_val.h"
+#include "cli_parse.h"
+#include "cli_objects.h"
+static void make_val_value(vtw_type_e type);
+#define YY_INPUT(buf,result,max_size) (result)=cli_val_read((buf), (max_size))
+%}
+%option noyywrap
+%option nounput
+%option never-interactive
+
+/*
+ * Regular expressions of IP and MAC addresses, URLs, etc.
+ */
+
+/*
+ * IPv4 address representation.
+ */
+RE_IPV4_BYTE 25[0-5]|2[0-4][0-9]|[01][0-9][0-9]|([0-9]{1,2})
+RE_IPV4 {RE_IPV4_BYTE}(\.{RE_IPV4_BYTE}){3}
+RE_IPV4_PREFIXLEN (3[012]|[12][0-9]|[0-9])
+RE_IPV4NET {RE_IPV4}"/"{RE_IPV4_PREFIXLEN}
+
+/*
+ * IPv6 address representation in Augmented Backus-Naur Form (ABNF)
+ * as defined in RFC-2234.
+ * IPv6 address representation taken from RFC-3986:
+ *
+ * IPv6address = 6( h16 ":" ) ls32
+ * / "::" 5( h16 ":" ) ls32
+ * / [ h16 ] "::" 4( h16 ":" ) ls32
+ * / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32
+ * / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32
+ * / [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32
+ * / [ *4( h16 ":" ) h16 ] "::" ls32
+ * / [ *5( h16 ":" ) h16 ] "::" h16
+ * / [ *6( h16 ":" ) h16 ] "::"
+ *
+ * h16 = 1*4HEXDIG
+ * ls32 = ( h16 ":" h16 ) / IPv4address
+ * IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet
+ * dec-octet = DIGIT ; 0-9
+ * / %x31-39 DIGIT ; 10-99
+ * / "1" 2DIGIT ; 100-199
+ * / "2" %x30-34 DIGIT ; 200-249
+ * / "25" %x30-35 ; 250-255
+ */
+
+RE_H16 [a-fA-F0-9]{1,4}
+RE_H16_COLON {RE_H16}":"
+RE_LS32 (({RE_H16}":"{RE_H16})|{RE_IPV4})
+RE_IPV6_P1 {RE_H16_COLON}{6}{RE_LS32}
+RE_IPV6_P2 "::"{RE_H16_COLON}{5}{RE_LS32}
+RE_IPV6_P3 ({RE_H16})?"::"{RE_H16_COLON}{4}{RE_LS32}
+RE_IPV6_P4 ({RE_H16_COLON}{0,1}{RE_H16})?"::"{RE_H16_COLON}{3}{RE_LS32}
+RE_IPV6_P5 ({RE_H16_COLON}{0,2}{RE_H16})?"::"{RE_H16_COLON}{2}{RE_LS32}
+RE_IPV6_P6 ({RE_H16_COLON}{0,3}{RE_H16})?"::"{RE_H16_COLON}{1}{RE_LS32}
+RE_IPV6_P7 ({RE_H16_COLON}{0,4}{RE_H16})?"::"{RE_LS32}
+RE_IPV6_P8 ({RE_H16_COLON}{0,5}{RE_H16})?"::"{RE_H16}
+RE_IPV6_P9 ({RE_H16_COLON}{0,6}{RE_H16})?"::"
+RE_IPV6 {RE_IPV6_P1}|{RE_IPV6_P2}|{RE_IPV6_P3}|{RE_IPV6_P4}|{RE_IPV6_P5}|{RE_IPV6_P6}|{RE_IPV6_P7}|{RE_IPV6_P8}|{RE_IPV6_P9}
+RE_IPV6_PREFIXLEN 12[0-8]|1[01][0-9]|[0-9][0-9]?
+RE_IPV6NET {RE_IPV6}"/"{RE_IPV6_PREFIXLEN}
+
+/*
+ * Ethernet MAC address representation.
+ */
+RE_MACADDR [a-fA-F0-9]{1,2}(:[a-fA-F0-9]{1,2}){5}
+
+/*
+ * URL-related regular expressions.
+ *
+ * Implementation is based on the BNF-like specification from:
+ * - RFC-1738: HTTP, FTP, FILE
+ * - RFC-3617: TFTP
+ * - RFC-3986: update of RFC-1738
+ */
+RE_URL {RE_URL_FILE}|{RE_URL_FTP}|{RE_URL_HTTP}|{RE_URL_TFTP}
+
+/*
+ * URL schemeparts for IP based protocols.
+ * Representation taken from RFC-1738, and some of it is updated by RFC-3986.
+ *
+ * login = [ user [ ":" password ] "@" ] hostport
+ * hostport = host [ ":" port ]
+ * host = hostname | hostnumber
+ * hostname = *[ domainlabel "." ] toplabel
+ * domainlabel = alphadigit | alphadigit *[ alphadigit | "-" ] alphadigit
+ * toplabel = alpha | alpha *[ alphadigit | "-" ] alphadigit
+ * alphadigit = alpha | digit
+ * hostnumber = digits "." digits "." digits "." digits
+ * port = digits
+ * user = *[ uchar | ";" | "?" | "&" | "=" ]
+ * password = *[ uchar | ";" | "?" | "&" | "=" ]
+ */
+RE_URL_LOGIN ({RE_URL_USER}(":"{RE_URL_PASSWORD})?"@")?{RE_URL_HOSTPORT}
+RE_URL_HOSTPORT {RE_URL_HOST}(":"{RE_URL_PORT})?
+RE_URL_HOST {RE_URL_HOSTNAME}|{RE_IPV4}|{RE_URL_IP_LITERAL}
+RE_URL_IP_LITERAL "["({RE_IPV6}|{RE_URL_IPV_FUTURE})"]"
+RE_URL_IPV_FUTURE "v"({RE_URL_HEXDIG})+"."({RE_URL_UNRESERVED}|{RE_URL_SUBDELIMS}|":")+
+RE_URL_HOSTNAME ({RE_URL_DOMAINLABEL}".")*{RE_URL_TOPLABEL}
+RE_URL_DOMAINLABEL {RE_URL_ALPHADIGIT}|{RE_URL_ALPHADIGIT}({RE_URL_ALPHADIGIT}|"-")*{RE_URL_ALPHADIGIT}
+RE_URL_TOPLABEL {RE_URL_ALPHA}|{RE_URL_ALPHA}({RE_URL_ALPHADIGIT}|"-")*{RE_URL_ALPHADIGIT}
+RE_URL_ALPHADIGIT {RE_URL_ALPHA}|{RE_URL_DIGIT}
+RE_URL_HOSTNUMBER {RE_URL_DIGITS}"."{RE_URL_DIGITS}"."{RE_URL_DIGITS}"."{RE_URL_DIGITS}
+RE_URL_PORT {RE_URL_DIGITS}
+RE_URL_USER ({RE_URL_UCHAR}|";"|"?"|"&"|"=")*
+RE_URL_PASSWORD ({RE_URL_UCHAR}|";"|"?"|"&"|"=")*
+
+/*
+ * FILE URL regular expression.
+ * Representation taken from RFC-1738.
+ *
+ * fileurl = "file://" [ host | "localhost" ] "/" fpath
+ */
+RE_URL_FILE "file://"({RE_URL_HOST}|"localhost")?"/"{RE_URL_FPATH}
+
+/*
+ * FTP URL regular expression.
+ * Representation taken from RFC-1738.
+ *
+ * ftpurl = "ftp://" login [ "/" fpath [ ";type=" ftptype ] ]
+ * fpath = fsegment *[ "/" fsegment ]
+ * fsegment = *[ uchar | "?" | ":" | "@" | "&" | "=" ]
+ * ftptype = "A" | "I" | "D" | "a" | "i" | "d"
+ */
+RE_URL_FTP "ftp://"{RE_URL_LOGIN}("/"{RE_URL_FPATH}(";type="{RE_URL_FTPTYPE})?)?
+RE_URL_FPATH {RE_URL_FSEGMENT}("/"{RE_URL_FSEGMENT})*
+RE_URL_FSEGMENT ({RE_URL_UCHAR}|"?"|":"|"@"|"&"|"=")*
+RE_URL_FTPTYPE "A"|"I"|"D"|"a"|"i"|"d"
+
+/*
+ * HTTP URL regular expression.
+ * Representation taken from RFC-1738.
+ *
+ * httpurl = "http://" hostport [ "/" hpath [ "?" search ] ]
+ * hpath = hsegment *[ "/" hsegment ]
+ * hsegment = *[ uchar | ";" | ":" | "@" | "&" | "=" ]
+ * search = *[ uchar | ";" | ":" | "@" | "&" | "=" ]
+ */
+RE_URL_HTTP "http://"{RE_URL_HOSTPORT}("/"{RE_URL_HPATH}("?"{RE_URL_SEARCH})?)?
+RE_URL_HPATH {RE_URL_HSEGMENT}("/"{RE_URL_HSEGMENT})*
+RE_URL_HSEGMENT ({RE_URL_UCHAR}|";"|":"|"@"|"&"|"=")*
+RE_URL_SEARCH ({RE_URL_UCHAR}|";"|":"|"@"|"&"|"=")*
+
+/*
+ * TFTP URL regular expression.
+ * Representation taken from RFC-3617.
+ *
+ * tftpURI = "tftp://" host "/" file [ mode ]
+ * mode = ";" "mode=" ( "netascii" / "octet" )
+ * file = *( unreserved / escaped )
+ * host = <as specified by RFC 2732 [3]>
+ * unreserved = <as specified in RFC 2396 [4]>
+ * escaped = <as specified in RFC 2396>
+ */
+RE_URL_TFTP "tftp://"{RE_URL_HOST}"/"{RE_URL_TFTP_FILE}({RE_URL_TFTP_MODE})?
+RE_URL_TFTP_MODE ";""mode="("netascii"|"octet")
+RE_URL_TFTP_FILE ({RE_URL_UNRESERVED}|{RE_URL_ESCAPE})*
+
+/*
+ * URL-related miscellaneous definitions.
+ * Representation taken from RFC-1738 and from RFC-3986.
+ *
+ * lowalpha = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" |
+ * "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" |
+ * "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" |
+ * "y" | "z"
+ * hialpha = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" |
+ * "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" |
+ * "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z"
+ * alpha = lowalpha | hialpha
+ * digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" |
+ * "8" | "9"
+ * safe = "$" | "-" | "_" | "." | "+"
+ * extra = "!" | "*" | "'" | "(" | ")" | ","
+ * national = "{" | "}" | "|" | "\" | "^" | "~" | "[" | "]" | "`"
+ * punctuation = "<" | ">" | "#" | "%" | <">
+ *
+ *
+ * reserved = ";" | "/" | "?" | ":" | "@" | "&" | "="
+ * hex = digit | "A" | "B" | "C" | "D" | "E" | "F" |
+ * "a" | "b" | "c" | "d" | "e" | "f"
+ * escape = "%" hex hex
+ *
+ * unreserved = alpha | digit | safe | extra
+ * uchar = unreserved | escape
+ * xchar = unreserved | reserved | escape
+ * digits = 1*digit
+ *
+ * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
+ * / "*" / "+" / "," / ";" / "="
+ */
+RE_URL_LOWALPHA [a-z]
+RE_URL_HIALPHA [A-Z]
+RE_URL_ALPHA {RE_URL_LOWALPHA}|{RE_URL_HIALPHA}
+RE_URL_DIGIT [0-9]
+RE_URL_SAFE "$"|"-"|"_"|"."|"+"
+RE_URL_EXTRA "!"|"*"|"'"|"("|")"|","
+RE_URL_NATIONAL "{"|"}"|"|"|"\"|"^"|"~"|"["|"]"|"`"
+RE_URL_PUNCTUATION "<"|">"|"#"|"%"|<">
+RE_URL_RESERVED ";"|"/"|"?"|":"|"@"|"&"|"="
+RE_URL_HEXDIG {RE_URL_DIGIT}|[A-F]|[a-f]
+RE_URL_ESCAPE "%"{RE_URL_HEXDIG}{RE_URL_HEXDIG}
+RE_URL_UNRESERVED {RE_URL_ALPHA}|{RE_URL_DIGIT}|{RE_URL_SAFE}|{RE_URL_EXTRA}
+RE_URL_UCHAR {RE_URL_UNRESERVED}|{RE_URL_ESCAPE}
+RE_URL_XCHAR {RE_URL_UNRESERVED}|{RE_URL_RESERVED}|{RE_URL_ESCAPE}
+RE_URL_DIGITS {RE_URL_DIGIT}{1,}
+RE_URL_SUBDELIMS "!"|"$"|"&"|"'"|"("|")"|"*"|"+"|","|";"|"="
+
+
+%%
+:: {
+ make_val_value(IPV6_TYPE);
+ return VALUE;
+ }
+
+true {
+ make_val_value(BOOL_TYPE);
+ return VALUE;
+ }
+
+false {
+ make_val_value(BOOL_TYPE);
+ return VALUE;
+ }
+
+[0-9]+ {
+ make_val_value(INT_TYPE);
+ return VALUE;
+ }
+
+{RE_IPV4} {
+ make_val_value(IPV4_TYPE);
+ return VALUE;
+ }
+
+{RE_IPV4NET} {
+ make_val_value(IPV4NET_TYPE);
+ return VALUE;
+ }
+
+{RE_IPV6} {
+ make_val_value(IPV6_TYPE);
+ return VALUE;
+ }
+
+{RE_IPV6NET} {
+ make_val_value(IPV6NET_TYPE);
+ return VALUE;
+ }
+
+{RE_MACADDR} {
+ make_val_value(MACADDR_TYPE);
+ return VALUE;
+ }
+
+\\\n /* whitespace */
+
+[ \t]+ /*whitespace */
+\n return EOL;
+. {
+ /* everything else is a syntax error */
+ return SYNTAX_ERROR;
+ }
+
+
+%%
+static void make_val_value(vtw_type_e type)
+{
+ memset(get_cli_value_ptr(), 0, sizeof(valstruct));
+ get_cli_value_ptr()->free_me = TRUE;
+ get_cli_value_ptr()->val = my_strdup(yytext, "cli_val.l");
+ get_cli_value_ptr()->val_type = type;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/cli_val_engine.c b/src/cli_val_engine.c
new file mode 100644
index 0000000..d597c68
--- /dev/null
+++ b/src/cli_val_engine.c
@@ -0,0 +1,881 @@
+
+/************************************************************************
+
+ Module: cli
+
+ **** License ****
+ Version: VPL 1.0
+
+ The contents of this file are subject to the Vyatta Public License
+ Version 1.0 ("License"); you may not use this file except in
+ compliance with the License. You may obtain a copy of the License at
+ http://www.vyatta.com/vpl
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ This code was originally developed by Vyatta, Inc.
+ Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc.
+ All Rights Reserved.
+
+ Author: Oleg Moskalenko
+ Date: 2007
+ Description: "new" cli handler for the reference variables
+
+ **** End License ****
+
+*************************************************************************/
+
+#if !defined(_GNU_SOURCE)
+#define _GNU_SOURCE
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <regex.h>
+#include <dirent.h>
+
+#include <string.h>
+
+#include "cli_val_engine.h"
+
+/*********************
+ * Data definitions
+ *
+ *********************/
+
+/**
+ * Special file names:
+ */
+#define VALUE_FILE ("node.val")
+#define NODE_TAG ("node.tag")
+#define NODE_DEF ("node.def")
+
+/**
+ * "Command" definition (element of a variable path):
+ */
+
+typedef struct {
+ clind_cmd_type cmd_type;
+ const char* text[10];
+} cmd_parse_definition;
+
+/**
+ * Structure to hold information about possible command entries:
+ */
+
+static cmd_parse_definition cmd_parse_definitions[] = {
+ { CLIND_CMD_PARENT_VALUE, {"..","@",NULL } },
+ { CLIND_CMD_NEIGHBOR, {"..","*",NULL } },
+ { CLIND_CMD_PARENT, {"..",NULL } },
+ { CLIND_CMD_PARENT, {".","..",NULL } },
+ { CLIND_CMD_CHILD, {".","*",NULL } },
+ { CLIND_CMD_VALUE, {".","@",NULL } },
+ { CLIND_CMD_SELF_NAME, {".",NULL } },
+ { CLIND_CMD_VALUE, {"@",NULL } },
+ { CLIND_CMD_MULTI_VALUE, {"@@",NULL } },
+ { CLIND_CMD_CHILD, {"*",NULL } },
+ { CLIND_CMD_UNKNOWN, {NULL} }
+};
+
+/************************
+ * Cmd utils forward declarations
+ *
+ ************************/
+
+static int clind_path_shift_cmd(clind_path_ref path,clind_cmd *cmd);
+
+/******************************
+ * Variable evaluation engine *
+ * *
+ ******************************/
+
+/**
+ * For a given config path, return the 'value' of the path.
+ * If the path ends with "node.val", then return the file content.
+ * If not, then return the last path element.
+ * If path is empty, or the file is empty, or the file does not exist,
+ * then return NULL.
+ * The user of this function is responsible for the memory deallocation.
+ */
+
+static char** clind_get_current_value(clind_path_ref cfg_path,
+ clind_path_ref tmpl_path,
+ int check_existence,
+ vtw_type_e *val_type,
+ const char* root_tmpl_path,
+ int return_value_file_name,
+ int multi_value,
+ int *ret_size) {
+
+ char** ret=NULL;
+ *ret_size=0;
+
+ if(val_type) *val_type=TEXT_TYPE;
+
+ if(cfg_path && (clind_path_get_size(cfg_path)>0)) {
+
+ clind_path_ref tmpl_path_clone = clind_path_clone(tmpl_path);
+
+ const char* cfg_path_string = clind_path_get_path_string(cfg_path);
+ const char* cfg_end = clind_path_last_string(cfg_path);
+ const char* tmpl_path_string = clind_path_get_path_string(tmpl_path_clone);
+ const char* tmpl_end = clind_path_last_string(tmpl_path_clone);
+
+ /*
+ printf("%s:111.111:%s:%s:%s:%s:%d\n",__FUNCTION__,
+ cfg_path_string,
+ cfg_end,
+ tmpl_path_string,
+ tmpl_end,
+ multi_value);
+ */
+
+ if(cfg_path_string && cfg_end) {
+
+ if(strcmp(cfg_end,VALUE_FILE)==0) {
+
+ /* Value reference: */
+
+ if(return_value_file_name) {
+
+ ret=(char**)realloc(ret,sizeof(char*)*1);
+ ret[0]=strdup(cfg_path_string);
+ *ret_size=1;
+
+ } else {
+
+ FILE* f = fopen(cfg_path_string,"r");
+ if(f) {
+ char buffer[8193];
+ if(multi_value) {
+ while(fgets(buffer, sizeof(buffer)-1,f)) {
+ int len=strlen(buffer);
+ while(len>0 && (buffer[len-1]==10 || buffer[len-1]==13)) {
+ buffer[len-1]=0;
+ len--;
+ }
+ if(len>0) {
+ ret=(char**)realloc(ret,sizeof(char*)*(*ret_size+1));
+ ret[*ret_size]=strdup(buffer);
+ *ret_size+=1;
+ }
+ }
+ } else {
+ int sz = fread(buffer, 1, sizeof(buffer)-1, f);
+ if(sz>0) {
+ int len=0;
+ buffer[sz]=0;
+ len=strlen(buffer);
+ while(len>0 && (buffer[len-1]==10 || buffer[len-1]==13)) {
+ buffer[len-1]=0;
+ len--;
+ }
+ ret=(char**)malloc(sizeof(char*)*1);
+ ret[0]=strdup(buffer);
+ *ret_size=1;
+ }
+ }
+ fclose(f);
+ }
+ }
+
+ } else if(return_value_file_name) {
+
+ ret=(char**)realloc(ret,sizeof(char*)*1);
+ ret[0]=(char*)malloc(strlen(cfg_path_string)+1+strlen(VALUE_FILE)+1);
+ strcpy(ret[0],cfg_path_string);
+ strcpy(ret[0]+strlen(ret[0]),"/");
+ strcpy(ret[0]+strlen(ret[0]),VALUE_FILE);
+ *ret_size=1;
+
+ } else {
+
+ struct stat statbuf;
+
+ /* Directory reference: */
+
+ if(!check_existence || (lstat(cfg_path_string, &statbuf) == 0)) {
+ ret=(char**)realloc(ret,sizeof(char*)*1);
+ ret[0]=clind_unescape(cfg_end);
+ *ret_size=1;
+ }
+ }
+
+ if(ret) {
+ if(tmpl_end && (strcmp(tmpl_end,NODE_TAG)==0)) {
+ clind_path_pop(tmpl_path_clone);
+ tmpl_path_string = clind_path_get_path_string(tmpl_path_clone);
+ tmpl_end = clind_path_last_string(tmpl_path_clone);
+ }
+ }
+
+ if(ret && tmpl_path_string && !return_value_file_name) {
+
+ vtw_def def;
+ struct stat statbuf;
+ int fn_node_def_size=strlen(tmpl_path_string)+1+strlen(NODE_DEF)+1;
+ char* fn_node_def=(char*)malloc(fn_node_def_size);
+
+ memset(&def, 0, sizeof(def));
+
+ fn_node_def[0]=0;
+
+ if(*tmpl_path_string!='/' && root_tmpl_path) {
+ fn_node_def_size+=strlen(root_tmpl_path+1);
+ fn_node_def=(char*)realloc(fn_node_def,fn_node_def_size);
+ strcpy(fn_node_def+strlen(fn_node_def),root_tmpl_path);
+ strcpy(fn_node_def+strlen(fn_node_def),"/");
+ }
+
+ strcpy(fn_node_def+strlen(fn_node_def),tmpl_path_string);
+ strcpy(fn_node_def+strlen(fn_node_def),"/");
+ strcpy(fn_node_def+strlen(fn_node_def),NODE_DEF);
+
+ if ((lstat(fn_node_def, &statbuf) == 0)&&
+ (parse_def(&def, fn_node_def, TRUE)==0)) {
+
+ if(def.def_type != ERROR_TYPE) {
+
+ int status=0;
+ valstruct res;
+ int i=0;
+
+ memset(&res,0,sizeof(res));
+
+ for(i=0;i<*ret_size;i++) {
+
+ if(ret[i]) {
+
+ /* return the value in the correct type */
+ status = char2val(&def, ret[i], &res);
+
+ if(status==0) {
+
+ if(val_type) *val_type=res.val_type;
+
+ if(res.free_me && res.val) {
+ free(ret[i]);
+ ret[i]=res.val;
+ }
+ } else {
+ /* Bad value ? */
+ }
+ }
+ }
+ }
+
+ } else {
+
+ while(*ret_size>0) {
+ if(ret[*ret_size-1]) {
+ free(ret[*ret_size-1]);
+ }
+ *ret_size-=1;
+ }
+ free(ret);
+ ret=NULL;
+
+ }
+
+ free(fn_node_def);
+ }
+ }
+
+ clind_path_destruct(&tmpl_path_clone);
+ }
+
+ return ret;
+}
+
+/**
+ * Return TRUE if current node is a multi-node value
+ */
+static int is_multi_node(clind_path_ref tmpl_path) {
+
+ int ret=0;
+
+ if(tmpl_path) {
+
+ const char* t_end = clind_path_last_string(tmpl_path);
+
+ if(t_end && (strcmp(t_end,NODE_TAG)==0)) {
+ ret=1;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * Return TRUE if current node is node.def
+ */
+static int is_node_def(clind_path_ref tmpl_path) {
+
+ int ret=0;
+
+ if(tmpl_path) {
+
+ const char* t_end = clind_path_last_string(tmpl_path);
+
+ if(t_end && (strcmp(t_end,NODE_DEF)==0)) {
+ ret=1;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * Apply a single command to the configuration path.
+ * cfg_path - absolute configuration path,
+ * tmpl_path - logical template path,
+ * cmd - single reference command from the variable path.
+ * The result is the array of "derived" paths.
+ * result_len output parameter contains the array size.
+ */
+
+static clind_path_ref* clind_config_engine_apply_command(clind_path_ref cfg_path,
+ clind_path_ref tmpl_path,
+ clind_cmd *cmd,
+ int *result_len) {
+ clind_path_ref* ret=NULL;
+
+ if(cfg_path && tmpl_path && result_len && cmd) {
+
+ /*
+ printf("%s:111.111:%s:%s:%d\n",__FUNCTION__,
+ clind_path_get_path_string(cfg_path),
+ clind_path_get_path_string(tmpl_path),
+ cmd->type);
+ */
+
+ switch (cmd->type) {
+
+ case CLIND_CMD_PARENT:
+
+ {
+ *result_len=1;
+ ret=(clind_path_ref*)(malloc(*result_len * sizeof(clind_path_ref)));
+ cfg_path = clind_path_clone(cfg_path);
+ ret[0]=cfg_path;
+
+ if(is_multi_node(tmpl_path)) {
+ clind_path_pop(cfg_path);
+ clind_path_pop(tmpl_path);
+ } else if(is_node_def(tmpl_path)) {
+ clind_path_pop(tmpl_path);
+ }
+
+ clind_path_pop(cfg_path);
+ clind_path_pop(tmpl_path);
+
+ if(is_multi_node(tmpl_path)) {
+ clind_path_pop(cfg_path);
+ clind_path_pop(tmpl_path);
+ }
+ }
+
+ break;
+
+ case CLIND_CMD_SELF_NAME:
+
+ {
+ *result_len=1;
+ ret=(clind_path_ref*)(malloc(*result_len * sizeof(clind_path_ref)));
+ cfg_path = clind_path_clone(cfg_path);
+ ret[0]=cfg_path;
+
+ if(is_multi_node(tmpl_path)) {
+ clind_path_pop(cfg_path);
+ clind_path_pop(tmpl_path);
+ } else if(is_node_def(tmpl_path)) {
+ clind_path_pop(tmpl_path);
+ }
+ }
+
+ break;
+
+ case CLIND_CMD_CHILD:
+
+ {
+ *result_len=1;
+ ret=(clind_path_ref*)(malloc(*result_len * sizeof(clind_path_ref)));
+ cfg_path = clind_path_clone(cfg_path);
+ ret[0]=cfg_path;
+
+ clind_path_push(cfg_path,cmd->value);
+ clind_path_push(tmpl_path,cmd->value);
+ }
+ break;
+
+ case CLIND_CMD_NEIGHBOR:
+
+ {
+ *result_len=1;
+ ret=(clind_path_ref*)(malloc(*result_len * sizeof(clind_path_ref)));
+ cfg_path = clind_path_clone(cfg_path);
+ ret[0]=cfg_path;
+
+ if(is_multi_node(tmpl_path)) {
+ clind_path_pop(cfg_path);
+ clind_path_pop(tmpl_path);
+ } else if(is_node_def(tmpl_path)) {
+ clind_path_pop(tmpl_path);
+ }
+
+ clind_path_pop(cfg_path);
+ clind_path_pop(tmpl_path);
+
+ clind_path_push(cfg_path,cmd->value);
+ clind_path_push(tmpl_path,cmd->value);
+ }
+
+ break;
+
+ case CLIND_CMD_VALUE:
+
+ {
+ const char* t_path_string = clind_path_get_path_string(tmpl_path);
+ const char* t_end = clind_path_last_string(tmpl_path);
+ const char* c_end = clind_path_last_string(cfg_path);
+
+ *result_len=1;
+ ret=(clind_path_ref*)(malloc(*result_len * sizeof(clind_path_ref)));
+ cfg_path = clind_path_clone(cfg_path);
+ ret[0]=cfg_path;
+
+ if(t_end && (strcmp(t_end,NODE_TAG)==0)) {
+ /* do nothing, we are there already */
+ } else if(t_path_string && clind_file_exists(t_path_string,NODE_TAG)) {
+ clind_path_push(tmpl_path,NODE_TAG);
+ } else if(c_end && (strcmp(c_end,VALUE_FILE)==0)) {
+ /* do nothing, we are there already */
+ } else {
+ clind_path_push(cfg_path,VALUE_FILE);
+ }
+ }
+
+ break;
+
+ case CLIND_CMD_PARENT_VALUE:
+
+ {
+ *result_len=1;
+ ret=(clind_path_ref*)(malloc(*result_len * sizeof(clind_path_ref)));
+ cfg_path = clind_path_clone(cfg_path);
+ ret[0]=cfg_path;
+
+ if(is_multi_node(tmpl_path)) {
+ clind_path_pop(cfg_path);
+ clind_path_pop(tmpl_path);
+ } else if(is_node_def(tmpl_path)) {
+ clind_path_pop(tmpl_path);
+ }
+
+ clind_path_pop(cfg_path);
+ clind_path_pop(tmpl_path);
+ }
+
+ break;
+
+ case CLIND_CMD_MULTI_VALUE:
+
+ {
+ const char* cfg_path_string = NULL;
+
+ if(is_multi_node(tmpl_path)) {
+ clind_path_pop(cfg_path);
+ clind_path_pop(tmpl_path);
+ } else if(is_node_def(tmpl_path)) {
+ clind_path_pop(tmpl_path);
+ }
+
+ cfg_path_string = clind_path_get_path_string(cfg_path);
+
+ if(cfg_path_string) {
+
+ const char* c_end = clind_path_last_string(cfg_path);
+
+ if(c_end && (strcmp(c_end,VALUE_FILE)==0)) {
+
+ *result_len=1;
+ ret=(clind_path_ref*)(malloc(*result_len * sizeof(clind_path_ref)));
+ cfg_path = clind_path_clone(cfg_path);
+ ret[0]=cfg_path;
+
+ } else if(clind_file_exists(cfg_path_string,VALUE_FILE)) {
+
+ *result_len=1;
+ ret=(clind_path_ref*)(malloc(*result_len * sizeof(clind_path_ref)));
+ cfg_path = clind_path_clone(cfg_path);
+ ret[0]=cfg_path;
+
+ clind_path_push(cfg_path,VALUE_FILE);
+
+ } else {
+
+ DIR* dir=NULL;
+
+ dir = opendir(cfg_path_string);
+
+ if(dir) {
+
+ *result_len=0;
+
+ ret=(clind_path_ref*)(malloc(*result_len * sizeof(clind_path_ref)));
+
+ do {
+ struct dirent *de = readdir(dir);
+
+ if(!de) break;
+ else if(de->d_name[0] && de->d_name[0]!='.') {
+ clind_path_ref cfg_path_1 = clind_path_clone(cfg_path);
+ clind_path_push(cfg_path_1,de->d_name);
+ (*result_len)++;
+ ret=(clind_path_ref*)(realloc(ret,*result_len * sizeof(clind_path_ref)));
+ ret[*result_len-1]=cfg_path_1;
+ }
+ } while(1);
+
+ clind_path_push(tmpl_path,NODE_TAG);
+
+ closedir(dir);
+ }
+ }
+ }
+ }
+ break;
+
+ default:
+ ;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * cfg_path - absolute configuration path,
+ * tmpl_path - logical template path,
+ * cmd_path - variable command path.
+ */
+
+int clind_config_engine_apply_command_path(clind_path_ref cfg_path_orig,
+ clind_path_ref tmpl_path_orig,
+ clind_path_ref cmd_path,
+ int check_existence,
+ clind_val* res,
+ const char* root_cfg_path,
+ const char* root_tmpl_path,
+ int return_value_file_name) {
+
+ int ret=-1;
+
+ /*
+ printf("%s:111.111:cfg_path=%s,tmpl_path=%s,cmd_path=%s,rtp=%s\n",__FUNCTION__,
+ clind_path_get_path_string(cfg_path_orig),
+ clind_path_get_path_string(tmpl_path_orig),
+ clind_path_get_path_string(cmd_path),
+ root_tmpl_path);
+ */
+
+ if(cfg_path_orig && tmpl_path_orig && cmd_path && res) {
+
+ /* Command to be processed: */
+ clind_cmd cmd;
+
+ /* Array of configuration pointers. Initially, contains only one
+ element - cfg_path. */
+ clind_path_ref* config_paths=
+ (clind_path_ref*)malloc(sizeof(clind_path_ref)*1);
+
+ /* Size of the array (initially - just one): */
+ int config_paths_size=1;
+
+ /* Clone the input paths to preserve the input objects intact: */
+ clind_path_ref tmpl_path=NULL;
+ clind_path_ref cfg_path=NULL;
+
+ if(clind_path_is_absolute(cmd_path)) {
+ tmpl_path=clind_path_construct(root_tmpl_path);
+ if(!tmpl_path) {
+ return -1;
+ }
+ cfg_path=clind_path_construct(root_cfg_path);
+ if(!cfg_path) {
+ return -1;
+ }
+ } else {
+ cfg_path=clind_path_clone(cfg_path_orig);
+ tmpl_path=clind_path_clone(tmpl_path_orig);
+ }
+
+ res->value=NULL;
+ res->val_type=TEXT_TYPE;
+
+ /* Set the initial array content: */
+ config_paths[0]=cfg_path;
+
+ /* Apply the commands one-by-one: */
+ while(clind_path_get_size(cmd_path)>0 &&
+ (clind_path_shift_cmd(cmd_path,&cmd)==0)) {
+
+ int i=0;
+
+ /* Temporary array to keep the config paths for the next
+ command application: */
+ clind_path_ref* new_config_paths=NULL;
+ int new_config_paths_size=0;
+
+ /* This path contains the template path at the beginning
+ of the cycle: */
+ clind_path_ref tmpl_path_curr=clind_path_clone(tmpl_path);
+
+ for (i=0;i<config_paths_size;i++) {
+
+ int size=0;
+
+ clind_path_ref* new_config_paths_1=NULL;
+
+ if(i==0) {
+ new_config_paths_1=
+ clind_config_engine_apply_command(config_paths[i],
+ tmpl_path,
+ &cmd,
+ &size);
+ } else {
+ clind_path_ref tmpl_path_curr_clone=clind_path_clone(tmpl_path_curr);
+ new_config_paths_1=
+ clind_config_engine_apply_command(config_paths[i],
+ tmpl_path_curr_clone,
+ &cmd,
+ &size);
+ clind_path_destruct(&tmpl_path_curr_clone);
+ }
+
+ if(new_config_paths_1) {
+
+ int j=0;
+
+ for(j=0;j<size;j++) {
+ if(new_config_paths_1[j]) {
+ new_config_paths_size++;
+ new_config_paths=
+ (clind_path_ref*)realloc(new_config_paths,
+ sizeof(clind_path_ref)*
+ new_config_paths_size);
+ new_config_paths[new_config_paths_size-1]=new_config_paths_1[j];
+ }
+ }
+
+ free(new_config_paths_1);
+ }
+
+ clind_path_destruct(&(config_paths[i]));
+ }
+
+ free(config_paths);
+ config_paths=new_config_paths;
+ config_paths_size=new_config_paths_size;
+ clind_path_destruct(&tmpl_path_curr);
+ }
+
+ if(config_paths) {
+
+ char** sarr=NULL;
+ int sarrlen=0;
+
+ vtw_type_e val_type=TEXT_TYPE;
+
+ int i=0;
+
+ for(i=0;i<config_paths_size;i++) {
+
+ if(config_paths[i]) {
+
+ int vallen=0;
+
+ char** valarr=clind_get_current_value(config_paths[i],
+ tmpl_path,
+ check_existence,
+ &val_type,
+ root_tmpl_path,
+ return_value_file_name,
+ /*Last command: */
+ (cmd.type==CLIND_CMD_MULTI_VALUE),
+ &vallen);
+
+ clind_path_destruct(&config_paths[i]);
+
+ if(valarr) {
+
+ int k=0;
+
+ for(k=0;k<vallen;k++) {
+
+ char* s=valarr[k];
+
+ if(s) {
+
+ /* search if we already have it: */
+ int j=0;
+ for(j=0;j<sarrlen;j++) {
+ if(!strcmp(sarr[j],s)) {
+ break;
+ }
+ }
+
+ if(j<sarrlen) {
+ free(s);
+ } else {
+ sarrlen++;
+ sarr=(char**)realloc(sarr,sizeof(char*)*sarrlen);
+ sarr[sarrlen-1]=s;
+ }
+ }
+ }
+ free(valarr);
+ }
+ }
+ }
+
+ free(config_paths);
+
+ if(sarr) {
+
+ ret=0;
+
+ if(sarrlen==1) {
+
+ res->value=sarr[0];
+
+ } else {
+
+ for(i=0;i<sarrlen;i++) {
+
+ if(sarr[i]) {
+
+ char* s = clind_quote(sarr[i]);
+
+ free(sarr[i]);
+ sarr[i]=NULL;
+
+ if(s) {
+
+ if(!res->value) {
+
+ res->value=s;
+
+ } else if(res->value[0]==0) {
+
+ free(res->value);
+ res->value=s;
+
+ } else {
+
+ res->value=(char*)realloc(res->value,
+ strlen(res->value)+1+strlen(s)+1);
+
+ strcpy(res->value+strlen(res->value)," ");
+ strcpy(res->value+strlen(res->value),s);
+
+ free(s);
+ }
+ }
+ }
+ }
+ }
+ free(sarr);
+ }
+ }
+
+ clind_path_destruct(&cmd_path);
+ clind_path_destruct(&cfg_path);
+ clind_path_destruct(&tmpl_path);
+ }
+
+ return ret;
+}
+
+/******************************
+ * Cmd utils.
+ *
+ ******************************/
+
+static int clind_path_shift_cmd(clind_path_ref path,clind_cmd *cmd) {
+
+ int ret=-1;
+
+ if(cmd) {
+
+ cmd->type = CLIND_CMD_UNKNOWN;
+ cmd->value[0]=0;
+
+ if(path && clind_path_get_size(path)>0) {
+
+ int i=0;
+ int done=0;
+
+ while(cmd_parse_definitions[i].text!=NULL && cmd_parse_definitions[i].text[0]!=NULL) {
+
+ int j=0;
+
+ while(cmd_parse_definitions[i].text[j]) {
+ const char* str = clind_path_get_string(path,j);
+ if(str) {
+ if(!strcmp(cmd_parse_definitions[i].text[j],"*")) {
+ if(*str!='.' && *str!='@') {
+ j++;
+ strncpy(cmd->value,str,sizeof(cmd->value)-1);
+ continue;
+ }
+ } else if(!strcmp(cmd_parse_definitions[i].text[j],str)) {
+ j++;
+ strncpy(cmd->value,str,sizeof(cmd->value)-1);
+ continue;
+ }
+ }
+ j=0;
+ break;
+ }
+
+ if(j<1) {
+ i++;
+ continue;
+ } else {
+ done=1;
+ }
+
+ cmd->type = cmd_parse_definitions[i].cmd_type;
+
+ while(j) {
+ clind_path_shift(path);
+ j--;
+ }
+
+ break;
+ }
+
+ if(done) {
+ ret=0;
+ }
+ }
+ }
+
+ return ret;
+}
+
diff --git a/src/cli_val_engine.h b/src/cli_val_engine.h
new file mode 100644
index 0000000..0f32276
--- /dev/null
+++ b/src/cli_val_engine.h
@@ -0,0 +1,86 @@
+
+/************************************************************************
+
+ Module: cli
+
+ **** License ****
+ Version: VPL 1.0
+
+ The contents of this file are subject to the Vyatta Public License
+ Version 1.0 ("License"); you may not use this file except in
+ compliance with the License. You may obtain a copy of the License at
+ http://www.vyatta.com/vpl
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ This code was originally developed by Vyatta, Inc.
+ Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc.
+ All Rights Reserved.
+
+ Author: Oleg Moskalenko
+ Date: 2007
+ Description: "new" cli handler for the reference variables
+
+ **** End License ****
+
+*************************************************************************/
+
+#if !defined(__CLI_VAL_ENGINE__)
+#define __CLI_VAL_ENGINE__
+
+#include "cli_path_utils.h"
+#include "cli_val.h"
+
+/*******************
+ * Type definitions
+ *
+ *******************/
+
+typedef enum {
+
+ CLIND_CMD_UNKNOWN=0, /* ??? */
+ CLIND_CMD_PARENT, /* .. */
+ CLIND_CMD_SELF_NAME, /* . */
+ CLIND_CMD_CHILD, /* <name> */
+ CLIND_CMD_NEIGHBOR, /* ../<name> */
+ CLIND_CMD_VALUE, /* @ */
+ CLIND_CMD_PARENT_VALUE, /* ../@ */
+ CLIND_CMD_MULTI_VALUE /* @@ */
+
+} clind_cmd_type;
+
+typedef struct {
+
+ clind_cmd_type type;
+ char value[1025];
+
+} clind_cmd;
+
+typedef struct {
+
+ vtw_type_e val_type;
+ char* value;
+
+} clind_val;
+
+/********************************
+ * Main command-handling method:
+ *
+ ********************************/
+
+int clind_config_engine_apply_command_path(clind_path_ref cfg_path,
+ clind_path_ref tmpl_path,
+ clind_path_ref cmd_path,
+ int check_existence,
+ clind_val *res,
+ const char* root_cfg_path,
+ const char* root_tmpl_path,
+ int return_value_file_name);
+
+
+
+
+#endif /* __CLI_VAL_ENGINE__*/
diff --git a/src/commit.c b/src/commit.c
new file mode 100644
index 0000000..a136b58
--- /dev/null
+++ b/src/commit.c
@@ -0,0 +1,1364 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <limits.h>
+
+#include "cli_val.h"
+#include "cli_objects.h"
+#include "cli_parse.h"
+#include "cli_path_utils.h"
+
+static char def_name[] = DEF_NAME;
+static char tag_name[] = TAG_NAME;
+static char opaque_name[] = OPQ_NAME;
+
+static int fin_commit(boolean ok);
+static boolean commit_value(vtw_def *defp, char *cp,
+ vtw_cmode mode, boolean in_txn);
+static void perform_create_node();
+static void perform_delete_node();
+static void perform_move();
+static boolean commit_delete_child(vtw_def *pdefp, char *child,
+ boolean deleting, boolean in_txn);
+static boolean commit_delete_children(vtw_def *defp, boolean deleting,
+ boolean in_txn);
+static boolean commit_update_children(vtw_def *defp, boolean creating,
+ boolean in_txn, boolean *parent_update);
+
+#if BITWISE
+static void make_dir()
+{
+ struct stat statbuf;
+ if (lstat(m_path.path, &statbuf) < 0) {
+ char *command;
+ command = my_malloc(strlen(m_path.path) + 10, "set");
+ sprintf(command, "mkdir -p %s", m_path.path);
+ system(command);
+ free(command);
+ return;
+ }
+ if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
+ bye("directory %s expected, found regular file", m_path.path);
+ }
+ return;
+}
+#endif
+
+/*************************************************
+ validate_dir_for_commit:
+ validate value.value if there is one, validate
+ subdirectries names (for tag directory, and for
+ regular directory);
+ validate subdirectories
+ returns TRUE if OK, FASLE if errors
+ exits with status != 0 in case of parse error
+*/
+static boolean validate_dir_for_commit()
+{
+ struct stat statbuf;
+ int status=0;
+ vtw_def def;
+ boolean def_present=FALSE;
+ boolean value_present=FALSE;
+ int subdirs_number=0;
+ DIR *dp=NULL;
+ struct dirent *dirp=NULL;
+ char *cp=NULL;
+ boolean ret=TRUE;
+ char *uename = NULL;
+
+#ifdef DEBUG
+ printf("validating directory (node_cnt %d, free_node_cnt %d)\n"
+ "t_path |%s|\n",
+ node_cnt, free_node_cnt, t_path.path);
+#endif
+
+ /* find definition */
+
+ push_path(&t_path, def_name); /* PUSH 1 */
+ if ((lstat(t_path.path, &statbuf) >= 0) &&
+ ((statbuf.st_mode & S_IFMT) == S_IFREG)) {
+ /* definition present */
+ def_present = TRUE;
+ memset(&def, 0, sizeof(def));
+#ifdef DEBUG1
+ printf("Parsing definition\n");
+#endif
+
+ if ((status = parse_def(&def, t_path.path,
+ FALSE))) {
+ exit(status);
+ }
+
+ }
+#ifdef DEBUG1
+ else
+ printf("No definition\n");
+#endif
+ pop_path(&t_path); /* for PUSH 1 */
+
+ /* look at modified stuff */
+ if ((dp = opendir(m_path.path)) == NULL){
+ INTERNAL;
+ }
+ if (def_present && def.tag) {
+ push_path(&t_path, tag_name); /* PUSH 2a */
+ }
+
+ while ((dirp = readdir(dp)) != NULL) {
+
+ if (strcmp(dirp->d_name, ".") == 0 ||
+ strcmp(dirp->d_name, "..") == 0 ||
+ strcmp(dirp->d_name, MOD_NAME) == 0 ||
+ strcmp(dirp->d_name, LOCK_NAME) == 0 ||
+ strcmp(dirp->d_name, opaque_name) == 0 ||
+ strncmp(dirp->d_name, ".wh.", 4) == 0) {
+ continue; /*ignore dot and dot-dot*/
+ }
+
+ subdirs_number++;
+
+ if(uename)
+ my_free(uename);
+
+ uename = clind_unescape(dirp->d_name);
+
+ if (strcmp(uename, VAL_NAME) == 0) {
+
+ value_present=TRUE;
+
+ /* deal with the value */
+ if (!def_present) {
+ printf("There is no definition specified in template\n"
+ "\t%s\n\t - therefore no value permitted\n",
+ t_path.path);
+ ret = FALSE;
+ continue;
+ }
+
+ if (def.tag) {
+ printf("Tag specified in template\n"
+ "\t%s\n\t - therefore no value permitted\n",
+ t_path.path);
+ ret = FALSE;
+ continue;
+ }
+
+ /* value is OK */
+ /* read it */
+ cp = NULL;
+ status = get_value(&cp, &m_path);
+ if (status == VTWERR_OK){
+#ifdef DEBUG1
+ printf("Validating value |%s|\n"
+ "for path %s\n", cp, m_path.path);
+#endif
+ status = validate_value(&def, cp);
+ ret = ret && status;
+ }
+ if (cp)
+ my_free(cp);
+ continue;
+ }
+
+ push_path(&m_path, uename); /* PUSH 3 */
+ if (lstat(m_path.path, &statbuf) < 0) {
+ printf("Can't read directory %s\n",
+ m_path.path);
+ ret = FALSE;
+ pop_path(&m_path); /* for PUSH 3 */
+ continue;
+ }
+
+ if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
+ printf("Non directory file %s\n", m_path.path);
+ ret = FALSE;
+ pop_path(&m_path); /* for PUSH 3 */
+ continue;
+ }
+
+ if (def_present && def.tag) {
+ /* do not push t_path, it is already pushed above */
+ /* validate dir name against definition */
+ boolean res = validate_value(&def, uename);
+ value_present=TRUE;
+ if (!res) {
+ ret = FALSE;
+ /* do not go inside bad directory */
+ pop_path(&m_path); /* for PUSH 3 */
+ continue;
+ }
+ } else {
+ push_path(&t_path, uename); /* PUSH 2b the same as PUSH 2a */
+ if (lstat(t_path.path, &statbuf) < 0) {
+ printf("No such template directory (%s)\n"
+ "for directory %s",
+ t_path.path, m_path.path);
+ ret = FALSE;
+ pop_path(&t_path); /* for PUSH 2b */
+ pop_path(&m_path); /* for PUSH 3 */
+ continue;
+ }
+ if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
+ printf("Non directory file %s\n", m_path.path);
+ ret = FALSE;
+ pop_path(&t_path); /* for PUSH 2b */
+ pop_path(&m_path); /* for PUSH 3 */
+ continue;
+ }
+ }
+
+ status = validate_dir_for_commit();
+ ret = ret && status;
+ pop_path(&m_path); /* for PUSH 3 */
+ if (!def_present || !def.tag)
+ pop_path(&t_path); /* for PUSH 2b */
+
+ }
+ status = closedir(dp);
+ if (status)
+ bye("Cannot close dir %s\n", m_path.path);
+
+ if(!value_present && def_present && !def.tag) {
+ ret = ret && validate_value(&def, "");
+ }
+
+ if (def_present && def.tag)
+ pop_path(&t_path); /* for PUSH 3a */
+ if (def_present)
+ free_def(&def);
+#ifdef DEBUG
+ printf("directory done(node_cnt %d, free_node_cnt %d)\n"
+ "mpath |%s|\n",
+ node_cnt, free_node_cnt, t_path.path);
+#endif
+ if (uename)
+ my_free(uename);
+ return ret;
+}
+
+/***************************************************
+ main:
+ main function (for now)
+***************************************************/
+
+int main(int argc, char **argv)
+{
+
+ boolean status;
+ char *mod;
+ struct stat statbuf;
+ int st;
+ boolean update_pending = FALSE;
+
+ set_in_commit(TRUE);
+ dump_log( argc, argv);
+ init_paths(TRUE);
+ mod = my_malloc(strlen(get_mdirp()) + strlen(MOD_NAME)+2, "COMMIT");
+ sprintf(mod, "%s/%s", get_mdirp(), MOD_NAME);
+ st = lstat(mod, &statbuf);
+ my_free(mod);
+ if (st < 0 ) {
+ bye("No configuration changes to commit\n");
+ exit(-1);
+ }
+
+ if(!get_config_lock(get_adirp(), LOCK_NAME)) {
+ bye("Configuration is locked\n");
+ }
+
+ status = validate_dir_for_commit();
+ if (status == TRUE) {
+ switch_path(CPATH);
+ status = commit_delete_children(NULL, FALSE, FALSE);
+ }
+ if (status == TRUE)
+ status = commit_update_children(NULL, FALSE, FALSE, &update_pending);
+ fin_commit(status);
+
+ done();
+
+ return (status == TRUE) ? 0 : 1;
+}
+
+/*************************************************************
+ perform_create_node -
+ remove node and descendent from woring path
+ and create a new node
+*************************************************************/
+static void perform_create_node()
+{
+#if BITWISE
+ static char format[]="rm -f -r %s;mkdir %s";
+ char *command;
+ switch_path(APATH);
+ command = my_malloc(2 * strlen(m_path.path) + sizeof(format),
+ "commit_create");
+ sprintf(command, format, m_path.path, m_path.path);
+ system(command);
+ my_free(command);
+ switch_path(CPATH);
+#endif
+ return;
+}
+
+
+/*************************************************************
+ perform_delete_node -
+ delete node in current path
+*************************************************************/
+static void perform_delete_node()
+{
+#if BITWISE
+ static char format[]="rm -f -r %s";
+ char *command;
+ command = my_malloc(strlen(m_path.path) + sizeof(format),
+ "commit_delete");
+ sprintf(command, format, m_path.path);
+ system(command);
+ my_free(command);
+#endif
+ return;
+}
+static void perform_move()
+{
+#if BITWISE
+ static char format[] = "rm -r -f %s;mkdir %s;mv %s/" VAL_NAME " %s";
+ char *a_path;
+ char *command;
+ switch_path(APATH);
+ a_path = my_strdup(m_path.path, "");
+ switch_path(CPATH);
+ command = my_malloc(sizeof(format)+3*strlen(a_path)+strlen(m_path.path),"");
+ sprintf(command, format, a_path, a_path, m_path.path, a_path);
+ system(command);
+ my_free(command);
+ my_free(a_path);
+#endif
+ return;
+}
+
+/*************************************************
+ commit_update_child:
+ preconditions: t_path and c_path set up for parent
+ pdefp (IN) - parent definition (may be NULL)
+ child (IN) - name of the child
+ creating (IN) - mode of commiting (update or create)
+ update_parent (OUT) - unfulfilled update request
+ commit child and its descendants
+ returns TRUE if no errors
+ exit if error during execution
+*/
+boolean commit_update_child(vtw_def *pdefp, char *child,
+ boolean creating, boolean in_txn, boolean *update_parent)
+{
+ boolean update_pending = FALSE;
+ boolean multi_tag = FALSE;
+ struct stat statbuf;
+ int status;
+ vtw_def def;
+ vtw_def *my_defp; /*def to be given to my children */
+ vtw_def *act_defp;/*def to be used for actions */
+ char *cp;
+ vtw_mark mark;
+ vtw_act_type act;
+ boolean do_end, ok, do_begin = FALSE, do_txn = FALSE;
+
+ set_at_string(NULL);
+
+ ok = TRUE;
+#ifdef DEBUG
+ printf("commiting directory (node_cnt %d, free_node_cnt %d)\n"
+ "mpath |%s|\n",
+ node_cnt, free_node_cnt, t_path.path);
+#endif
+ if (strcmp(child, ".") == 0 ||
+ strcmp(child, "..") == 0 ||
+ strcmp(child, MOD_NAME) == 0 ||
+ strcmp(child, LOCK_NAME) == 0 ||
+ strcmp(child, opaque_name) == 0)
+ return TRUE;
+
+ /* deleted subdirectory */
+ if (strncmp(child, ".wh.", 4) == 0) { /* whiteout */
+ /* ignore */
+ return TRUE;
+ }
+ mark_paths(&mark);
+ if (!creating) {
+ /* are we marked with opaque */
+ push_path(&m_path, child);
+ push_path(&m_path, opaque_name);
+ if (lstat(m_path.path, &statbuf) >= 0 && !creating) {
+ creating = TRUE;
+ }
+ pop_path(&m_path);
+ pop_path(&m_path);
+ }
+ /* find our definition */
+ if (pdefp && pdefp->tag) {
+ my_defp = NULL;
+ act_defp = pdefp;
+ push_path(&t_path,tag_name);
+ } else {
+ push_path(&t_path, child);
+ push_path(&t_path, def_name);
+ if ((lstat(t_path.path, &statbuf) >= 0) &&
+ ((statbuf.st_mode & S_IFMT) == S_IFREG)) {
+ /* defniition present */
+ act_defp = my_defp = &def;
+ memset(&def, 0, sizeof(def));
+#ifdef DEBUG1
+ printf("Parsing definition\n");
+#endif
+ if ((status = parse_def(&def, t_path.path,
+ FALSE)))
+ exit(status);
+ my_defp = act_defp = &def;
+ if (def.tag)
+ multi_tag = TRUE;
+ } else {
+ my_defp = act_defp = NULL;
+ }
+ pop_path(&t_path); /*def_name */
+ }
+ do_end = FALSE;
+ if (!multi_tag && !in_txn && act_defp &&
+ (act_defp->actions[begin_act].vtw_list_head ||
+ act_defp->actions[end_act].vtw_list_head)){
+ /* we are traversing change directory
+ if we are here, there is a change somewhere */
+ do_txn = in_txn = TRUE;
+ if (act_defp->actions[end_act].vtw_list_head)
+ do_end = TRUE;
+ /* if creating, delete skipped this directory,
+ we have to do begin act */
+ if (creating &&
+ act_defp->actions[begin_act].vtw_list_head)
+ do_begin = TRUE;
+ }
+ push_path(&m_path, child);
+ /* are we a "value" parent node */
+ if (act_defp && !act_defp->tag && act_defp->def_type != ERROR_TYPE ) {
+ /* we are value node */
+ if (!act_defp->multi) {
+ /* single node */
+ /* create_mode do create,
+ update_mode do create or update */
+
+ if (creating)
+ act = create_act;
+ else
+ act = update_act;
+
+ if ((act==update_act) && !act_defp->actions[update_act].vtw_list_head){
+ /* updating but no action
+ ask parent to do - propagate up */
+ *update_parent = TRUE;
+ }
+ /* look for actions */
+ /* if act != create_act => run actions[act] if not empty */
+ /* if act == create_act
+ * if actions[create_act] not empty
+ * run it
+ * else
+ * run actions[update_act] if not empty
+ * run actions[activate_act] if not empty
+ */
+ if (act_defp->actions[act].vtw_list_head
+ || (act == create_act
+ && (act_defp->actions[activate_act].vtw_list_head
+ || act_defp->actions[update_act].vtw_list_head))) {
+ status = get_value_to_at_string(&m_path);
+ if (status != VTWERR_OK)
+ bye("Can not read value at %s", m_path.path);
+ /* remove \n at the line end */
+ cp = strchr(get_at_string(), '\n');
+ if (cp)
+ *cp = 0;
+ if (act_defp->actions[act].vtw_list_head) {
+ status = execute_list(act_defp->actions[act].vtw_list_head,
+ act_defp);
+ if (!status) {
+#ifdef DEBUG
+ bye("begin action not NULL for %s\n", get_at_string());
+#else
+ return(FALSE);
+#endif
+ }
+ } else {
+ /* creating but no create action */
+ /* try update action */
+ if ((act == create_act)
+ && act_defp->actions[update_act].vtw_list_head) {
+ status
+ = execute_list(act_defp->actions[update_act].vtw_list_head,
+ act_defp);
+ if (!status) {
+ return (FALSE);
+ }
+ }
+ }
+ /* try activate action if creating */
+ if ((act == create_act)
+ && act_defp->actions[activate_act].vtw_list_head) {
+ status
+ = execute_list(act_defp->actions[activate_act].vtw_list_head,
+ act_defp);
+ if (!status) {
+ return (FALSE);
+ }
+ }
+ free_at_string();
+ }
+ /* now handle commit value */
+ if (!in_txn) { /* ELSE WAIT TILL THE END OF TXN */
+ perform_move();
+ perform_delete_node();
+ }
+ goto restore;
+ }
+ /* else multi_node */
+ cp = NULL;
+ status = get_value(&cp, &m_path);
+ if (status != VTWERR_OK)
+ bye("Can not read value at %s", m_path.path);
+ ok = commit_value(act_defp, cp, creating?create_mode:update_mode, in_txn);
+ if (cp)
+ my_free(cp);
+ goto restore;
+ }
+ /* else not a value */
+ /* regular */
+ /* do not do anything for tag type multinode */
+ if (!multi_tag && creating) {
+ set_at_string(child); /* for expand inside actions */
+ if (do_begin) {
+ status = execute_list(act_defp->
+ actions[begin_act].
+ vtw_list_head, act_defp);
+ if (!status) {
+#ifdef DEBUG
+ bye("begin action not NULL for %s\n", get_at_string());
+#else
+ return(FALSE);
+#endif
+ }
+ }
+
+ if (act_defp) {
+ if (act_defp->actions[create_act].vtw_list_head) {
+ status
+ = execute_list(act_defp->actions[create_act].vtw_list_head,
+ act_defp);
+ if (!status) {
+ return (FALSE);
+ }
+ } else if (act_defp->actions[update_act].vtw_list_head) {
+ /* no create action => use update action */
+ status
+ = execute_list(act_defp->actions[update_act].vtw_list_head,
+ act_defp);
+ if (!status) {
+ return (FALSE);
+ }
+ }
+ /* not trying activate action here (activate after children are
+ * configured)
+ */
+ }
+ }
+ if (creating && !in_txn) /* ELSE WAIT TILL THE END OF TXN */
+ perform_create_node();
+ /* children */
+ ok = commit_update_children(my_defp, creating, in_txn, &update_pending);
+ if (!ok)
+ return(FALSE);
+
+ if (update_pending){
+ if (!multi_tag && act_defp &&
+ act_defp->actions[update_act].vtw_list_head){
+ set_at_string(child); /* for expand inside actions */
+ ok = execute_list(act_defp->actions[update_act].
+ vtw_list_head, act_defp);
+ if (!ok)
+ return(FALSE);
+ /* update_pending = FALSE; */
+ } else
+ *update_parent = TRUE;
+ }
+ if (creating && !multi_tag && act_defp &&
+ act_defp->actions[activate_act].vtw_list_head){
+ set_at_string(child); /* for expand inside actions */
+ ok = execute_list(act_defp->actions[activate_act].
+ vtw_list_head, act_defp);
+ /* ignore result */
+ }
+ if (!in_txn) /* ELSE WAIT TILL THE END OF TXN */
+ perform_delete_node();
+ restore:
+ if (do_end){
+ set_at_string(child);
+ ok = execute_list(act_defp->actions[end_act].
+ vtw_list_head, act_defp);
+ }
+#if BITWISE
+ if (do_txn && ok) {
+ int len;
+ char *command;
+ char format1[]="rm -r -f %s/*;cp -r -f %s/%s %s";
+ char format2[]="rm -r -f %s/%s;mv %s/%s %s";
+ char format3[]="rm -r -f %s/%s";
+ restore_paths(&mark);
+ switch_path(MPATH);
+ len = sizeof(format1) + 2 * strlen(get_tmpp()) +
+ strlen(m_path.path) + strlen(child);
+ command = my_malloc(len, "");
+ sprintf(command, format1, get_tmpp(), m_path.path, child,
+ get_tmpp());
+ system(command);
+ my_free(command);
+ switch_path(APATH);
+ len = sizeof(format2) + 2 *strlen(m_path.path) +
+ 2 * strlen( child) + strlen(get_tmpp());
+ command = my_malloc(len, "");
+ sprintf(command, format2, m_path.path, child, get_tmpp(),
+ child, m_path.path);
+ system(command);
+ my_free(command);
+ switch_path(CPATH);
+ len = sizeof(format3) + strlen(m_path.path) +
+ strlen( child);
+ command = my_malloc(len, "");
+ sprintf(command, format3, m_path.path, child);
+ system(command);
+ my_free(command);
+ }
+#endif
+ restore_paths(&mark);
+ if (my_defp && my_defp != pdefp)
+ free_def(my_defp);
+ return ok;
+}
+
+
+/*************************************************
+ commit_delete_child:
+ preconditions: t_path and c_path set up for parent
+ pdefp (IN) - parent definition (may be NULL)
+ child (IN) - name of the child
+ do_del (IN) - if FALSE we are looking for delete target
+ if TRUE, we found and switched to A (working) PATH
+ commit deleted child and its descendants
+ returns TRUE if no errors
+ exit if error during execution
+*/
+static boolean commit_delete_child(vtw_def *pdefp, char *child,
+ boolean deleting, boolean in_txn)
+{
+ boolean do_children, multi_tag = FALSE;
+ struct stat statbuf;
+ int status;
+ vtw_def def;
+ vtw_def *my_defp; /*def to be given to my children */
+ vtw_def *act_defp;/*def to be used for actions */
+ char *cp;
+ vtw_mark mark;
+ boolean ok, do_txn = FALSE;
+ int st;
+ ok = TRUE;
+
+#ifdef DEBUG
+ printf("commiting directory (node_cnt %d, free_node_cnt %d)\n"
+ "mpath |%s|\n",
+ node_cnt, free_node_cnt, t_path.path);
+#endif
+
+ /* deleted subdirectory */
+ if (strncmp(child, ".wh.", 4) == 0) { /* whiteout */
+ if (deleting)
+ /* in do_delete mode we traverse A hierarchy, no white-outs possible */
+ INTERNAL; /* it is exit */
+ else {
+ /* deal with counterpart in working */
+ switch_path(APATH);
+ push_path(&m_path, child+4);
+ st = lstat(m_path.path, &statbuf);
+ pop_path(&m_path);
+ if (st >= 0){
+ /*get rid of ".wh. part in child name"*/
+ /* deleting mode will handle txn, both
+ begin and end
+ */
+ ok = commit_delete_child(pdefp, child + 4, TRUE, in_txn);
+ }else {
+ /* I do not understand how we could be here */
+ printf("Mystery #1\n");
+ ok = TRUE;
+ }
+ switch_path(CPATH);
+ if (ok) {
+ /* delete whiteout */
+ if (!in_txn){ /*ELSE WAIT TILL THE END OF TXN*/
+ push_path(&m_path, child);
+ perform_delete_node();
+ pop_path(&m_path);
+ }
+ }
+ return ok;
+ }
+ /* done with whiteouts */
+ }
+ /* not white out */
+ mark_paths(&mark);
+ if (!deleting) {
+ int status;
+ /* are we marked with opaque */
+ push_path(&m_path, child);
+ push_path(&m_path, opaque_name);
+ status = lstat(m_path.path, &statbuf);
+ pop_path(&m_path);
+ pop_path(&m_path);
+ if (status >= 0) {
+ /* brand new directory, nothing is
+ deleted there;
+ update will handle txn (both begin and end)
+ */
+ return TRUE;
+ }
+ }
+ /* find our definition */
+ if (pdefp && pdefp->tag) {
+ /* parent is a tag, node is a tag value node */
+ my_defp = NULL;
+ act_defp = pdefp;
+ push_path(&t_path,tag_name);
+ } else {
+ push_path(&t_path, child);
+ push_path(&t_path, def_name);
+ if ((lstat(t_path.path, &statbuf) >= 0) &&
+ ((statbuf.st_mode & S_IFMT) == S_IFREG)) {
+ /* defniition present */
+ act_defp = my_defp = &def;
+ memset(&def, 0, sizeof(def));
+#ifdef DEBUG1
+ printf("Parsing definition\n");
+#endif
+ if ((status = parse_def(&def, t_path.path,
+ FALSE)))
+ exit(status);
+ my_defp = act_defp = &def;
+ if (def.tag)
+ multi_tag = TRUE; /* tag node itself*/
+ } else {
+ my_defp = act_defp = NULL;
+ }
+ pop_path(&t_path); /*def_name */
+ }
+ push_path(&m_path, child);
+ if (!multi_tag) {
+ set_at_string(child); /* for expand inside actions */
+ /* deal with txn */
+ if (!in_txn && act_defp &&
+ (act_defp->actions[begin_act].vtw_list_head ||
+ act_defp->actions[end_act].vtw_list_head)) {
+ /* if we are here we have change,
+ we either in do_del and our node is change node
+ or we are in change directory and our node is
+ change node also */
+ if (deleting)
+ /* if not deleting, update will handle values */
+ do_txn = TRUE;
+ in_txn = TRUE;
+ if (act_defp->actions[begin_act].vtw_list_head){
+ status = execute_list(act_defp->actions[begin_act].
+ vtw_list_head, act_defp);
+ if (!status) {
+#ifdef DEBUG
+ bye("begin action not NULL for %s\n", get_at_string());
+#else
+ return(FALSE);
+#endif
+ }
+ }
+ }
+ }
+ /* are we a "value" parent node */
+ if (act_defp && !act_defp->tag && act_defp->def_type != ERROR_TYPE ) {
+ /* we are value node */
+ if (!act_defp->multi) {
+ /* single node */
+ if (!deleting) {
+ /* if it was whiteout, it was converted to do_del_mode */
+ restore_paths(&mark);
+ return ok;
+ }
+
+ /* do we have actions */
+ if (act_defp->actions[delete_act].vtw_list_head){
+ status = get_value_to_at_string(&m_path);
+ if (status != VTWERR_OK)
+ bye("Can not read value at %s", m_path.path);
+ /* remove \n at the line end */
+ cp = strchr(get_at_string(), '\n');
+ if (cp)
+ *cp = 0;
+ if (act_defp->actions[delete_act].vtw_list_head) {
+ set_in_delete_action(TRUE);
+ status = execute_list(act_defp->actions[delete_act].
+ vtw_list_head, act_defp);
+ set_in_delete_action(FALSE);
+ if (!status) {
+#ifdef DEBUG
+ bye("begin action not NULL for %s\n", get_at_string());
+#else
+ return(FALSE);
+#endif
+ }
+ }
+ free_at_string();
+ }
+ /* now handle commit value */
+ if (!in_txn) /* ELSE WAIT TILL THE END OF TXN */
+ perform_delete_node();
+ goto restore;
+ }
+ /* else multi_node */
+ cp = NULL;
+ status = get_value(&cp, &m_path);
+ if (status != VTWERR_OK)
+ bye("Can not read value at %s", m_path.path);
+ ok = commit_value(act_defp, cp, deleting?do_del_mode:del_mode,in_txn);
+ if (cp)
+ my_free(cp);
+ goto restore;
+ }
+ /* else not a value */
+ /* regular */
+ do_children = TRUE;
+ /* do not do anything for tag itself, all action belong to values */
+ if (!multi_tag) {
+ set_at_string(child); /* for expand inside actions */
+ if (deleting) {
+ if (act_defp &&
+ act_defp->actions[delete_act].vtw_list_head){
+ do_children = FALSE;
+ set_in_delete_action(TRUE);
+ status = execute_list(act_defp->actions[delete_act].
+ vtw_list_head, act_defp);
+ set_in_delete_action(FALSE);
+ if (!status) {
+#ifdef DEBUG
+ bye("begin action not NULL for %s\n", get_at_string());
+#else
+ return(FALSE);
+#endif
+ }
+ }
+ }
+ }
+ /* children */
+ if (do_children){
+ ok = commit_delete_children(my_defp, deleting, in_txn);
+ if (!ok)
+ goto restore;
+ }
+ if (deleting) {
+ if (do_txn && act_defp &&
+ act_defp->actions[end_act].vtw_list_head) {
+ set_at_string(child);
+ ok = execute_list(act_defp->actions[end_act].
+ vtw_list_head, act_defp);
+ if (!ok)
+ goto restore;
+ }
+ /* delete node and all its descendants */
+ if (!in_txn || do_txn)/* ELSE WAIT TILL THE END OF TXN */
+ perform_delete_node();
+ }
+ restore:
+ restore_paths(&mark);
+ if (my_defp && my_defp != pdefp)
+ free_def(my_defp);
+ return ok;
+}
+
+static boolean commit_delete_children(vtw_def *defp, boolean deleting,
+ boolean in_txn)
+{
+ DIR *dp;
+ int status;
+ struct dirent *dirp;
+ boolean ok = TRUE;
+ char *child;
+ vtw_type_e type;
+ valstruct mvals;
+ boolean first;
+ char *cp;
+ int elem, curi;
+ vtw_sorted cur_sorted;
+ char *uename = NULL;
+
+ if ((dp = opendir(m_path.path)) == NULL){
+ if (deleting)
+ return TRUE;
+ INTERNAL;
+ }
+ if (defp)
+ type = defp->def_type;
+ else
+ type = TEXT_TYPE;
+ if (type == ERROR_TYPE)
+ type = TEXT_TYPE;
+ first = TRUE;
+ memset(&mvals, 0, sizeof (valstruct));
+ memset(&cur_sorted, 0, sizeof(vtw_sorted));
+
+ while ((dirp = readdir(dp)) != NULL) {
+ child = dirp->d_name;
+ if (strcmp(child, ".") == 0 ||
+ strcmp(child, "..") == 0 ||
+ strcmp(child, MOD_NAME) == 0 ||
+ strcmp(child, LOCK_NAME) == 0 ||
+ strcmp(child, OPQ_NAME) == 0)
+ continue;
+ uename = clind_unescape(child);
+ cp = uename;
+ if (first) {
+ mvals.free_me = TRUE;
+ mvals.val = cp;
+ mvals.val_type = type;
+ first = FALSE;
+ } else {
+ if (mvals.cnt%MULTI_ALLOC == 0) {
+ /* convert into multivalue */
+ mvals.vals = my_realloc(mvals.vals,
+ (mvals.cnt + MULTI_ALLOC) *
+ sizeof(char *), "add_value");
+ if (mvals.cnt == 0) { /* single value - convert */
+ mvals.vals[0] = mvals.val;
+ mvals.cnt= 1;
+ mvals.val = NULL;
+ }
+ }
+ mvals.vals[mvals.cnt] = cp;
+ ++mvals.cnt;
+ }
+ }
+ status = closedir(dp);
+ if (status)
+ INTERNAL;
+ if (first) {
+ return TRUE;
+ }
+ vtw_sort(&mvals, &cur_sorted);
+ for (curi = 0; curi < cur_sorted.num && ok; ++curi){
+ if (type == TEXT_TYPE ||
+ type == BOOL_TYPE)
+ child = (char *)(cur_sorted.ptrs[curi]);
+ else {
+ elem = (((unsigned int *)(cur_sorted.ptrs[curi]))-
+ cur_sorted.parts)/
+ cur_sorted.partnum;
+ child = mvals.cnt?mvals.vals[elem]:
+ mvals.val;
+ }
+ ok = commit_delete_child(defp, child, deleting, in_txn);
+ }
+ free_val(&mvals);
+ free_sorted(&cur_sorted);
+ return ok;
+}
+
+static boolean commit_update_children(vtw_def *defp, boolean creating,
+ boolean in_txn, boolean *parent_update)
+{
+ DIR *dp;
+ int status;
+ struct dirent *dirp;
+ boolean ok = TRUE;
+ char *child;
+ vtw_type_e type;
+ valstruct mvals;
+ boolean first;
+ char *cp;
+ int elem, curi;
+ vtw_sorted cur_sorted;
+ char *uename = NULL;
+
+
+ if ((dp = opendir(m_path.path)) == NULL){
+ printf("%s:%d: opendir error: path=%s\n",
+ __FUNCTION__,__LINE__,m_path.path);
+ INTERNAL;
+ }
+
+ memset(&mvals, 0, sizeof (valstruct));
+ memset(&cur_sorted, 0, sizeof(vtw_sorted));
+ if (defp)
+ type = defp->def_type;
+ else
+ type = TEXT_TYPE;
+ if (type == ERROR_TYPE)
+ type = TEXT_TYPE;
+ first = TRUE;
+
+ while ((dirp = readdir(dp)) != NULL) {
+ child = dirp->d_name;
+ if (strcmp(child, ".") == 0 ||
+ strcmp(child, "..") == 0 ||
+ strcmp(child, MOD_NAME) == 0 ||
+ strcmp(child, LOCK_NAME) == 0 ||
+ strcmp(child, OPQ_NAME) == 0)
+ continue;
+ cp = uename = clind_unescape(child);
+ if (first) {
+ mvals.free_me = TRUE;
+ mvals.val = cp;
+ mvals.val_type = type;
+ first = FALSE;
+ } else {
+ if (mvals.cnt%MULTI_ALLOC == 0) {
+ /* convert into multivalue */
+ mvals.vals = my_realloc(mvals.vals,
+ (mvals.cnt + MULTI_ALLOC) *
+ sizeof(char *), "add_value");
+ if (mvals.cnt == 0) { /* single value - convert */
+ mvals.vals[0] = mvals.val;
+ mvals.cnt= 1;
+ mvals.val = NULL;
+ }
+ }
+ mvals.vals[mvals.cnt] = cp;
+ ++mvals.cnt;
+ }
+ }
+ status = closedir(dp);
+ if (status)
+ INTERNAL;
+ if (first) {
+ if (uename)
+ my_free(uename);
+ return TRUE;
+ }
+ vtw_sort(&mvals, &cur_sorted);
+ for (curi = 0; curi < cur_sorted.num && ok; ++curi){
+ if (type == TEXT_TYPE ||
+ type == BOOL_TYPE)
+ child = (char *)(cur_sorted.ptrs[curi]);
+ else {
+ elem = (((unsigned int *)(cur_sorted.ptrs[curi]))-
+ cur_sorted.parts)/
+ cur_sorted.partnum;
+ child = mvals.cnt?mvals.vals[elem]:
+ mvals.val;
+ }
+ ok = commit_update_child(defp, child, creating, in_txn, parent_update);
+ }
+ free_val(&mvals);
+ free_sorted(&cur_sorted);
+ return ok;
+}
+
+
+/*************************************************
+ commit_value:
+ executes commit for the value leave node
+**************************************************/
+static boolean commit_value(vtw_def *defp, char *cp,
+ vtw_cmode mode, boolean in_txn)
+{
+
+ valstruct act_value;
+ int status;
+ int curi,acti, partnum, res=0;
+ void *actp, *curp;
+ boolean no_shadow;
+ boolean ok;
+ int total, a_res, c_res;
+ char **a_ptr, **c_ptr, *val_string;
+ boolean cur_pr_val;
+ int pr_index;
+ int sign;
+ boolean creating;
+ vtw_node *actions;
+ valstruct cur_value;
+ vtw_sorted cur_sorted;
+ vtw_sorted act_sorted;
+
+ ok = TRUE;
+ actions = NULL;
+ if(mode == del_mode || mode == do_del_mode) {
+ creating = FALSE;
+ if (defp && defp->actions[delete_act].vtw_list_head) {
+ set_in_delete_action(TRUE);
+ actions = defp->actions[delete_act].vtw_list_head;
+ }
+ } else {
+ creating = TRUE;
+ if (defp && defp->actions[create_act].vtw_list_head)
+ actions = defp->actions[create_act].vtw_list_head;
+ }
+ /* prepare cur_value */
+
+ status = char2val(defp, cp, &cur_value);
+ if (mode != do_del_mode && mode != create_mode) {
+ /* get active value */
+ switch_path(APATH); /* switch form CCD to ACD */
+ status = get_value(&cp, &m_path);
+ switch_path(CPATH); /* back to CCD */
+ if (status != VTWERR_OK) {
+ no_shadow = TRUE;
+ }else
+ no_shadow = FALSE;
+ } else {
+ no_shadow = TRUE;
+ }
+ vtw_sort(&cur_value, &cur_sorted);
+ if(no_shadow) {
+ act_sorted.num = 0;
+ }else {
+ status = char2val(defp, cp, &act_value);
+ if (status != VTWERR_OK) {
+ INTERNAL;
+ }
+ /* sort them */
+ vtw_sort(&act_value, &act_sorted);
+ }
+ if (mode == do_del_mode) {
+ /* it was actually act_sorted, not cur_sorted */
+ act_sorted = cur_sorted;
+ cur_sorted.num = 0;
+ act_value = cur_value;
+ /* act_value will be freed by freeing cur_value
+ do not zero out it here */
+ }
+
+ acti = 0;
+ curi = 0;
+ total = act_sorted.num + cur_sorted.num;
+ a_res=0;
+ c_res=0;
+ a_ptr = my_malloc(total*sizeof(char *), "");
+ c_ptr = my_malloc(total*sizeof(char *), "");
+ while (acti < act_sorted.num || curi < cur_sorted.num) {
+ if (acti == act_sorted.num) {
+ cur_pr_val = TRUE;
+ pr_index = curi;
+ sign = +1;
+ ++curi;
+ } else if (curi == cur_sorted.num) {
+ cur_pr_val = FALSE;
+ pr_index = acti;
+ sign = -1;
+ ++acti;
+ } else {
+ /* compare */
+ actp = act_sorted.ptrs[acti];
+ curp = cur_sorted.ptrs[curi];
+ /* compare */
+ if (act_sorted.partnum){
+ for(partnum = 0; partnum < act_sorted.partnum;
+ ++partnum) {
+ res = *((int *)actp + partnum) -
+ *((int *)curp + partnum);
+ if (res)
+ break;
+ }
+ } else{
+ res = strcmp((char *)actp, (char *) curp);
+ }
+ if (res == 0) {
+ /* the same */
+ cur_pr_val = TRUE;
+ pr_index = curi;
+ sign = 0;
+ ++acti;
+ ++curi;
+ } else if (res < 0) {
+ /* act < cur, act is unmatched */
+ cur_pr_val = FALSE;
+ pr_index = acti;
+ sign = -1;
+ ++acti;
+ }else {
+ /* cur < act, cur is unmatched */
+ cur_pr_val = TRUE;
+ pr_index = curi;
+ sign = 1;
+ ++curi;
+ }
+ }
+ if (defp->def_type == TEXT_TYPE ||
+ defp->def_type == BOOL_TYPE) {
+ val_string = cur_pr_val?
+ ((char *)(cur_sorted.ptrs[pr_index])):
+ ((char *)(act_sorted.ptrs[pr_index]));
+ } else {
+ if (cur_pr_val) {
+ int elem = (((unsigned int *)(cur_sorted.ptrs[pr_index]))-
+ cur_sorted.parts)/
+ cur_sorted.partnum;
+ val_string = cur_value.cnt?cur_value.vals[elem]:
+ cur_value.val;
+ } else {
+ int elem = (((unsigned int *)(act_sorted.ptrs[pr_index]))-
+ act_sorted.parts)/
+ act_sorted.partnum;
+ val_string = act_value.cnt?act_value.vals[elem]:
+ act_value.val;
+ }
+ }
+ set_at_string(val_string);
+ switch (sign) {
+ case 0: /* found in both, no actions, include in both */
+ a_ptr[a_res++]=val_string;
+ c_ptr[c_res++]=val_string;
+ break;
+ case 1: /* found only in change */
+ if (ok && creating) {
+ if (actions) {
+ /* do create action */
+ ok = execute_list(actions, defp);
+ } else if (defp && defp->actions[update_act].vtw_list_head) {
+ /* no create action => use update action */
+ ok = execute_list(defp->actions[update_act].vtw_list_head, defp);
+ }
+ if (ok && defp && defp->actions[activate_act].vtw_list_head) {
+ /* try activate action */
+ ok = execute_list(defp->actions[activate_act].vtw_list_head, defp);
+ }
+ /* if succ, make it look old */
+ if(ok)
+ a_ptr[a_res++]=val_string;
+ }
+ c_ptr[c_res++]=val_string; /* in all cases */
+ break;
+ case -1: /* found only in working */
+ if (ok && !creating && actions) {/* ok and deleting */
+ ok = execute_list(actions, defp);
+ }
+ /* if succ and deleting - do nothing, else */
+ if (!ok || creating)
+ a_ptr[a_res++]=val_string;
+ }
+ }
+ if (creating && ok)
+ c_res = 0;
+#if BITWISE
+ if (!in_txn) {/* ELSE WAIT TILL THE END OF TXN */
+ switch_path(APATH);
+ if (a_res) {
+ make_dir();
+ push_path(&m_path, VAL_NAME);
+ fp = fopen(m_path.path, "w");
+ if (fp == NULL)
+ bye("Can not open value file %s", m_path.path);
+ for(i=0;i<a_res;++i)
+ if (fputs(a_ptr[i], fp) < 0 || fputc('\n',fp) < 0)
+ bye("Error writing file %s", m_path.path);
+ fclose(fp);
+ pop_path(&m_path);
+ }else{
+ perform_delete_node();
+ }
+ switch_path(CPATH);
+ if (c_res) {
+ make_dir();
+ push_path(&m_path, VAL_NAME);
+ fp = fopen(m_path.path, "w");
+ if (fp == NULL)
+ bye("Can not open value file %s", m_path.path);
+ for(i=0;i<c_res;++i)
+ if (fputs(c_ptr[i], fp) < 0 || fputc('\n',fp) < 0)
+ bye("Error writing file %s", m_path.path);
+ fclose(fp);
+ pop_path(&m_path);
+ }else{
+ perform_delete_node();
+ }
+ }
+#endif /*BITWISE*/
+ if (mode == do_del_mode)
+ switch_path(APATH);
+ else
+ switch_path(CPATH);
+ if(act_sorted.num)
+ free_sorted(&act_sorted);
+ if(cur_sorted.num)
+ free_sorted(&cur_sorted);
+ my_free(a_ptr);
+ my_free(c_ptr);
+ free_val(&cur_value);
+ if (!no_shadow)
+ free_val(&act_value);
+ set_in_delete_action(FALSE);
+ return ok;
+}
+
+static int fin_commit(boolean ok)
+{
+ char *command;
+ static char format1[]="cp -r -f %s/* %s"; /*mdirp, tmpp*/
+ static char format2[]="sudo umount %s"; /*mdirp*/
+ static char format3[]="rm -f %s/" MOD_NAME " >&/dev/null ; /bin/true";
+ /*tmpp*/
+ static char format4[]="rm -rf %s/{.*,*} >&/dev/null ; /bin/true"; /*cdirp*/
+ static char format5[]="rm -rf %s/{.*,*} >&/dev/null ; /bin/true"; /*adirp*/
+ static char format6[]="mv -f %s/* -t %s";/*tmpp, adirp*/
+ static char format7[]="sudo mount -t unionfs -o dirs=%s=rw:%s=ro"
+ " unionfs %s"; /*cdirp, adirp, mdirp*/
+ int m_len = strlen(get_mdirp());
+ int t_len = strlen(get_tmpp());
+ int c_len = strlen(get_cdirp());
+ int a_len = strlen(get_adirp());
+ set_echo(TRUE);
+ if (!ok){
+ printf("Commit FAILED!\n");
+ return -1;
+ }
+ command = my_malloc(strlen(format1) + m_len + t_len, "");
+ sprintf(command, format1, get_mdirp(), get_tmpp());
+ system(command);
+ my_free(command);
+
+ command = my_malloc(strlen(format2) + m_len, "");
+ sprintf(command, format2, get_mdirp());
+ system(command);
+ my_free(command);
+
+ command = my_malloc(strlen(format3) + c_len, "");
+ sprintf(command, format3, get_tmpp());
+ system(command);
+ my_free(command);
+
+ command = my_malloc(strlen(format4) + c_len, "");
+ sprintf(command, format4, get_cdirp());
+ system(command);
+ my_free(command);
+
+ command = my_malloc(strlen(format5) + a_len, "");
+ sprintf(command, format5, get_adirp());
+ system(command);
+ my_free(command);
+
+ command = my_malloc(strlen(format6) + t_len + a_len, "");
+ sprintf(command, format6, get_tmpp(), get_adirp());
+ system(command);
+ my_free(command);
+
+ command = my_malloc(strlen(format7) + c_len + a_len + m_len, "");
+ sprintf(command, format7, get_cdirp(), get_adirp(), get_mdirp());
+ system(command);
+ my_free(command);
+
+ return 0;
+}
+
diff --git a/src/delete.c b/src/delete.c
new file mode 100644
index 0000000..a825a22
--- /dev/null
+++ b/src/delete.c
@@ -0,0 +1,258 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <sys/stat.h>
+#include <dirent.h>
+
+#include "cli_val.h"
+#include "cli_objects.h"
+
+static void remove_rf(boolean do_umount)
+{
+ char *command;
+ touch();
+ if (do_umount) {
+ command = my_malloc(strlen(get_mdirp()) + 20, "delete");
+ sprintf(command, "sudo umount %s", get_mdirp());
+ system(command);
+ free(command);
+ }
+ command = my_malloc(strlen(m_path.path) + 10, "delete");
+ sprintf(command, "rm -rf %s", m_path.path);
+ system(command);
+ free(command);
+ if (do_umount) {
+ command = my_malloc(strlen(get_mdirp()) + strlen(get_cdirp()) +
+ strlen(get_mdirp()) + 100,
+ "delete");
+ sprintf(command, "sudo mount -t unionfs -o dirs=%s=rw:%s=ro:"
+ " unionfs %s", get_cdirp(), get_adirp(), get_mdirp());
+ system(command);
+ free(command);
+ }
+}
+/***************************************************
+ set_validate:
+ validate value against definition
+ return TRUE if OK, FALSE otherwise
+****************************************************/
+boolean set_validate(vtw_def *defp, char *valp)
+{
+ boolean res;
+ int status;
+ struct stat statbuf;
+
+ pop_path(&t_path); /* it was tag or real value */
+ push_path(&t_path, DEF_NAME);
+ if (lstat(t_path.path, &statbuf) < 0 ||
+ (statbuf.st_mode & S_IFMT) != S_IFREG) {
+ bye("Can not set value (2), no definition for %s", m_path.path);
+ }
+ /* defniition present */
+ memset(defp, 0, sizeof(vtw_def));
+ if ((status = parse_def(defp, t_path.path, FALSE)))
+ exit(status);
+ res = validate_value(defp, valp);
+ pop_path(&t_path);
+ return res;
+}
+
+int main(int argc, char **argv)
+{
+ int ai;
+ struct stat statbuf;
+ vtw_def def;
+ boolean last_tag=0;
+ int status;
+ FILE *fp;
+ boolean res;
+ char *cp, *delp, *endp;
+ boolean do_umount;
+
+ if (argc < 2) {
+ fprintf(stderr, "Need to specify the config node to delete\n");
+ exit(1);
+ }
+
+ dump_log( argc, argv);
+ do_umount = FALSE;
+ init_edit();
+ /* extend both paths per arguments given */
+ /* last argument is new value */
+ for (ai = 1; ai < argc; ++ai) {
+ push_path(&t_path, argv[ai]);
+ push_path(&m_path, argv[ai]);
+ if (lstat(t_path.path, &statbuf) >= 0) {
+ if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
+ bye("INTERNAL:regular file %s in templates", t_path.path);
+ }
+ last_tag = FALSE;
+ continue;
+ } /*else */
+ pop_path(&t_path);
+ push_path(&t_path, TAG_NAME);
+ if (lstat(t_path.path, &statbuf) >= 0) {
+ if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
+ bye("INTERNAL:regular file %s in templates", t_path.path);
+ }
+ last_tag = TRUE;
+ continue;
+ }
+ /* no match */
+ break;
+ }
+ /*
+ cases:
+ multiple tag-value - not achild
+ mutilple tag-value - not the last child
+ multiple tag-value - last child
+ single value modified
+ signle value unmodified
+ multiple non-tag value - the last value
+ multiple non-tag value - not the last value
+ regular child
+ */
+ if (ai == argc) {
+ /* full path found */
+ /* all cases except multiple non-tag value */
+ /* check for single value */
+ if (last_tag) {
+ /* case of multiple tag-value
+ was this a real child?
+ was it the last child?
+ */
+ struct dirent *dirp;
+ DIR *dp;
+
+ if (lstat(m_path.path, &statbuf) < 0)
+ bye("Nothing to delete at %s", m_path.path);
+ remove_rf(FALSE);
+ pop_path(&m_path);
+ if ((dp = opendir(m_path.path)) == NULL){
+ INTERNAL;
+ }
+ while ((dirp = readdir(dp)) != NULL) {
+ /*do we have real child */
+ if (strcmp(dirp->d_name, ".") &&
+ strcmp(dirp->d_name, "..") &&
+ strcmp(dirp->d_name, MOD_NAME) && /* XXX */
+ strcmp(dirp->d_name, LOCK_NAME) && /* XXX */
+ strncmp(dirp->d_name, ".wh.", 4) )
+ break;
+ }
+ if (dirp == NULL) {
+ /* no real children left */
+ /* kill parent also */
+ remove_rf(FALSE);
+ }
+ exit(0);
+ }
+ /*not tag */
+ push_path(&t_path, DEF_NAME);
+ if (lstat(t_path.path, &statbuf) >= 0 &&
+ (statbuf.st_mode & S_IFMT) == S_IFREG) {
+ /* defniition present */
+ memset(&def, 0, sizeof(vtw_def));
+ if ((status = parse_def(&def, t_path.path, FALSE)))
+ exit(status);
+ if (!def.tag && !def.multi && def.def_type!= ERROR_TYPE) {
+ /* signgle value */
+ /* is it modified ==
+ it is in C, but not OPAQUE */
+ switch_path(CPATH);
+ if(lstat(m_path.path, &statbuf) >= 0) {
+ push_path(&m_path, OPQ_NAME);
+ if(lstat(m_path.path, &statbuf) < 0) {
+ /* yes remove from C only */
+ pop_path(&m_path);
+ remove_rf(TRUE);
+ exit(0);
+ }
+ pop_path(&m_path); /*OPQ_NAME */
+ }
+ switch_path(MPATH);
+ }
+ }
+ /* else no defnition, remove it also */
+ remove_rf(FALSE);
+ exit(0);
+ }
+ if(ai < argc -1 || last_tag) {
+ bye("There is no appropriate template for %s",
+ m_path.path + strlen(get_mdirp()));
+ }
+ /*ai == argc -1, must be actual value */
+ pop_path(&m_path); /*it was value, not path segment */
+ push_path(&m_path, VAL_NAME);
+ /* set value */
+ if (lstat(m_path.path, &statbuf) < 0)
+ bye("Nothing to delete at %s", m_path.path);
+ if ((statbuf.st_mode & S_IFMT) != S_IFREG)
+ bye("Not a regular file %s", m_path.path);
+ /* get definition to deal with potential multi */
+ pop_path(&t_path); /* it was tag or real value */
+ push_path(&t_path, DEF_NAME);
+ if (lstat(t_path.path, &statbuf) < 0 ||
+ (statbuf.st_mode & S_IFMT) != S_IFREG) {
+ bye("Can not delete value, no definition for %s", m_path.path);
+ }
+ /* defniition present */
+ memset(&def, 0, sizeof(vtw_def));
+ if ((status = parse_def(&def, t_path.path, FALSE)))
+ exit(status);
+ if (def.multi) {
+ /* delete from multivalue */
+ valstruct new_value, old_value;
+ status = char2val(&def, argv[argc - 1], &new_value);
+ if (status)
+ exit(0);
+ cp = NULL;
+ pop_path(&m_path); /* get_value will push VAL_NAME */
+ status = get_value(&cp, &m_path);
+ if (status != VTWERR_OK)
+ bye("Cannot read old value %s\n", m_path.path);
+ status = char2val(&def, cp, &old_value);
+ if (status != VTWERR_OK)
+ bye("Corrupted old value ---- \n%s\n-----\n", cp);
+ res = val_cmp(&new_value, &old_value, IN_COND);
+ if (!res)
+ bye("Not in multivalue");
+ touch();
+ if (old_value.cnt) {
+ push_path(&m_path, VAL_NAME);
+ fp = fopen(m_path.path, "w");
+ if (fp == NULL)
+ bye("Can not open value file %s", m_path.path);
+ if (is_in_cond_tik()) {
+ for(delp=cp;delp && is_in_cond_tik(); dec_in_cond_tik()) {
+ delp = strchr(delp, '\n');
+ if (!delp)
+ INTERNAL;
+ ++delp; /* over \n */
+ }
+ /* write "left" of deleted */
+ fwrite(cp, 1, delp-cp, fp);
+ }else
+ delp = cp;
+ /* find end of value */
+ endp = strchr(delp, '\n');
+ if (endp && *++endp) {
+ /* write "right" of deleted */
+ fwrite(endp, 1, strlen(endp), fp);
+ /* need the final '\n' */
+ fwrite("\n", 1, 1, fp);
+ }
+ fclose(fp);
+ return 0;
+ }
+ /* it multi with only 1 value, remove */
+ remove_rf(FALSE);
+ return 0;
+ }
+ bye("There is no appropriate template for %s",
+ m_path.path + strlen(get_mdirp()));
+
+ return 0;
+}
+
diff --git a/src/set.c b/src/set.c
new file mode 100644
index 0000000..566dfe0
--- /dev/null
+++ b/src/set.c
@@ -0,0 +1,310 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+
+#include "cli_val.h"
+#include "cli_objects.h"
+#include "cli_path_utils.h"
+
+static void make_dir(void);
+static void handle_defaults(void);
+
+static void make_dir()
+{
+ touch_dir(m_path.path);
+}
+/***************************************************
+ set_validate:
+ validate value against definition
+ return TRUE if OK, FALSE otherwise
+****************************************************/
+boolean set_validate(vtw_def *defp, char *valp, boolean empty_val)
+{
+ boolean res;
+ int status;
+ struct stat statbuf;
+ char* path_end=NULL;
+
+ if (!empty_val) {
+ int i = 0;
+ int val_len = strlen(valp);
+
+ for (i = 0; i < val_len; i++) {
+ if (valp[i] == '\'') {
+ fprintf(stderr, "Cannot use the \"'\" (single quote) character "
+ "in a value string\n");
+ exit(1);
+ }
+ }
+
+ {
+ clind_path_ref tp = clind_path_construct(t_path.path);
+ if(tp) {
+ path_end=clind_path_pop_string(tp);
+ }
+ clind_path_destruct(&tp);
+ }
+
+ pop_path(&t_path); /* it was tag or real value */
+
+ }
+ push_path(&t_path, DEF_NAME);
+ if (lstat(t_path.path, &statbuf) < 0 ||
+ (statbuf.st_mode & S_IFMT) != S_IFREG) {
+ bye("Can not set value (4), no definition for %s, template %s",
+ m_path.path,t_path.path);
+ }
+ /* defniition present */
+ memset(defp, 0, sizeof(vtw_def));
+ if ((status = parse_def(defp, t_path.path, FALSE)))
+ exit(status);
+ pop_path(&t_path);
+ if(path_end) {
+ push_path(&t_path,path_end);
+ free(path_end);
+ path_end=NULL;
+ }
+ if (empty_val) {
+ if (defp->def_type != TEXT_TYPE || defp->tag || defp->multi){
+ printf("Empty string may be assigned only to TEXT type leaf node\n");
+ return FALSE;
+ }
+ return TRUE;
+ }
+ res = validate_value(defp, valp);
+ return res;
+}
+
+int main(int argc, char **argv)
+{
+
+ int ai;
+ struct stat statbuf;
+ vtw_def def;
+ boolean last_tag;
+ int status;
+ FILE *fp;
+ boolean res;
+ char *cp;
+ char *command;
+ boolean need_mod = FALSE, not_new = FALSE;
+ boolean empty_val = FALSE;
+
+ dump_log( argc, argv);
+ init_edit();
+ last_tag = FALSE;
+
+ /* extend both paths per arguments given */
+ /* last argument is new value */
+ for (ai = 1; ai < argc; ++ai) {
+ if (!*argv[ai]) { /* empty string */
+ if (ai < argc -1) {
+ bye("empty string in argument list \n");
+ }
+ empty_val = TRUE;
+ last_tag = FALSE;
+ break;
+ }
+ push_path(&t_path, argv[ai]);
+ push_path(&m_path, argv[ai]);
+ if (lstat(t_path.path, &statbuf) >= 0) {
+ if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
+ bye("INTERNAL:regular file %s in templates", t_path.path);
+ }
+ last_tag = FALSE;
+ continue;
+ } /*else */
+ pop_path(&t_path);
+ push_path(&t_path, TAG_NAME);
+ if (lstat(t_path.path, &statbuf) >= 0) {
+ if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
+ bye("INTERNAL:regular file %s in templates", t_path.path);
+ }
+ last_tag = TRUE;
+ /* every time tag match, need to verify*/
+ if(!set_validate(&def, argv[ai], FALSE)) {
+ exit(1);
+ }
+ continue;
+ }
+ /* no match */
+ break;
+ }
+
+ if (ai == argc) {
+ /* full path found */
+ /* every tag match validated already */
+ /* non tag matches are OK by definition */
+ /* do we already have it? */
+ if (lstat(m_path.path, &statbuf) >= 0)
+ bye("Already exists %s", m_path.path + strlen(get_mdirp()));
+ /* else */
+ /* prevent value node without actual value */
+ push_path(&t_path, DEF_NAME);
+ if (lstat(t_path.path, &statbuf) >= 0) {
+ memset(&def, 0, sizeof(vtw_def));
+ if ((status = parse_def(&def, t_path.path, FALSE)))
+ exit(status);
+ if (def.def_type != ERROR_TYPE && !def.tag)
+ bye("Must provide actual value\n");
+ if (def.def_type == ERROR_TYPE && !def.tag) {
+ pop_path(&t_path);
+ if(!validate_value(&def, "")) {
+ exit(1);
+ }
+ push_path(&t_path, DEF_NAME);
+ }
+ }
+ touch();
+ pop_path(&t_path);
+ make_dir();
+ handle_defaults();
+ exit(0);
+ }
+ if(ai < argc -1 || last_tag) {
+ fprintf(stderr, "There is no appropriate template for %s",
+ m_path.path + strlen(get_mdirp()));
+ exit(1);
+ }
+ /*ai == argc -1, must be actual value */
+ if (!empty_val)
+ pop_path(&m_path); /*it was value, not path segment */
+
+ if(!set_validate(&def, argv[argc-1], empty_val)) {
+ exit(1);
+ }
+ push_path(&m_path, VAL_NAME);
+ /* set value */
+ if (lstat(m_path.path, &statbuf) >= 0) {
+ valstruct new_value, old_value;
+ not_new = TRUE;
+ if ((statbuf.st_mode & S_IFMT) != S_IFREG)
+ bye("Not a regular file at path \"%s\"", m_path.path);
+ /* check if this new value */
+ status = char2val(&def, argv[argc - 1], &new_value);
+ if (status)
+ exit(0);
+ cp = NULL;
+ pop_path(&m_path); /* get_value will push VAL_NAME */
+ status = get_value(&cp, &m_path);
+ if (status != VTWERR_OK)
+ bye("Cannot read old value %s\n", m_path.path);
+ status = char2val(&def, cp, &old_value);
+ if (status != VTWERR_OK)
+ bye("Corrupted old value ---- \n%s\n-----\n", cp);
+ res = val_cmp(&new_value, &old_value, IN_COND);
+ if (res) {
+ if (def.multi) {
+ bye("Already in multivalue");
+ } else {
+ bye("The same value \"%s\" for path \"%s\"\n", cp, m_path.path);
+ }
+ }
+ } else {
+ pop_path(&m_path);
+ }
+ make_dir();
+ push_path(&m_path, VAL_NAME);
+ if(not_new && !def.multi) {
+ /* it is not multi and seen from M */
+ /* is it in C */
+ switch_path(CPATH);
+ if (lstat(m_path.path, &statbuf) < 0)
+ /* yes, we are modifying original value */
+ need_mod = TRUE;
+ switch_path(MPATH);
+ }
+ touch();
+ /* in case of multi we always append, never overwrite */
+ /* in case of single we always overwrite */
+ /* append and overwrite work the same for new file */
+ fp = fopen(m_path.path, def.multi?"a":"w");
+ if (fp == NULL)
+ bye("Can not open value file %s", m_path.path);
+ if (fputs(argv[argc-1], fp) < 0 || fputc('\n',fp) < 0)
+ bye("Error writing file %s", m_path.path);
+ if (need_mod) {
+ pop_path(&m_path); /* get rid of "value" */
+ command = my_malloc(strlen(m_path.path) + 30, "set");
+ sprintf(command, "touch %s/" MOD_NAME, m_path.path);
+ system(command);
+ }
+ return 0;
+}
+/**********************************************
+ handle_defaults:
+ now deal with defaults for children
+ if child has definition and not tag, nor multi, and
+ has type, and has default, and not have value
+ already, make a default value
+*/
+
+
+static void handle_defaults()
+{
+ DIR *dp;
+ int status;
+ struct dirent *dirp;
+ struct stat statbuf;
+ FILE *fp;
+ vtw_def def;
+ char *uename;
+
+ if ((dp = opendir(t_path.path)) == NULL){
+ INTERNAL;
+ }
+ while ((dirp = readdir(dp)) != NULL) {
+ if (strcmp(dirp->d_name, ".")==0 ||
+ strcmp(dirp->d_name, "..")==0 ||
+ strcmp(dirp->d_name, MOD_NAME) == 0 ||
+ strcmp(dirp->d_name, LOCK_NAME) == 0 ||
+ strcmp(dirp->d_name, DEF_NAME)==0)
+ continue;
+ uename = clind_unescape(dirp->d_name);
+ push_path(&t_path, uename);
+ if (lstat(t_path.path, &statbuf) < 0) {
+ bye("Cannot stat template directory %s\n",
+ t_path.path);
+ }
+ if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
+ bye("Non directory file %s\n", t_path.path);
+ }
+ push_path(&t_path, DEF_NAME);
+ if (lstat(t_path.path, &statbuf) < 0) {
+ /* no definition */
+ pop_path(&t_path); /* definition */
+ pop_path(&t_path); /* child */
+ continue;
+ }
+ memset(&def, 0, sizeof(def));
+ if ((status = parse_def(&def, t_path.path,
+ FALSE)))
+ exit(status);
+ if (def.def_default) {
+ push_path(&m_path, uename);
+ push_path(&m_path, VAL_NAME);
+ if (lstat(m_path.path, &statbuf) < 0) {
+ /* no value, write one */
+ pop_path(&m_path);
+ make_dir();/* make sure directory exist */
+ push_path(&m_path, VAL_NAME);
+ fp = fopen(m_path.path, "w");
+ if (fp == NULL)
+ bye("Can not open value file %s", m_path.path);
+ if (fputs(def.def_default, fp) < 0 ||
+ fputc('\n',fp) < 0)
+ bye("Error writing file %s", m_path.path);
+ fclose(fp);
+ }
+ pop_path(&m_path); /* value */
+ pop_path(&m_path); /* child */
+ }
+ free_def(&def);
+ pop_path(&t_path); /* definition */
+ pop_path(&t_path); /* child */
+ }
+}