diff options
Diffstat (limited to 'src/cli_new.c')
-rw-r--r-- | src/cli_new.c | 1938 |
1 files changed, 1938 insertions, 0 deletions
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; + + } +} + +/**********************************************************/ |