|
@@ -0,0 +1,334 @@
|
|
|
+#include <stdlib.h>
|
|
|
+#include <stdio.h>
|
|
|
+#include <string.h>
|
|
|
+#include <assert.h>
|
|
|
+
|
|
|
+#include <faux/str.h>
|
|
|
+#include <faux/list.h>
|
|
|
+#include <klish/khelper.h>
|
|
|
+#include <klish/ientry.h>
|
|
|
+#include <klish/kptype.h>
|
|
|
+#include <klish/kentry.h>
|
|
|
+
|
|
|
+#define TAG "ENTRY"
|
|
|
+
|
|
|
+
|
|
|
+bool_t ientry_parse(const ientry_t *info, kentry_t *entry, faux_error_t *error)
|
|
|
+{
|
|
|
+ bool_t retcode = BOOL_TRUE;
|
|
|
+
|
|
|
+ if (!info)
|
|
|
+ return BOOL_FALSE;
|
|
|
+ if (!entry)
|
|
|
+ return BOOL_FALSE;
|
|
|
+
|
|
|
+ // Help
|
|
|
+ if (!faux_str_is_empty(info->help)) {
|
|
|
+ if (!kentry_set_help(entry, info->help)) {
|
|
|
+ faux_error_add(error, TAG": Illegal 'help' attribute");
|
|
|
+ retcode = BOOL_FALSE;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Container
|
|
|
+ if (!faux_str_is_empty(info->container)) {
|
|
|
+ bool_t b = BOOL_FALSE;
|
|
|
+ if (!faux_conv_str2bool(info->container, &b) ||
|
|
|
+ !kentry_set_container(entry, b)) {
|
|
|
+ faux_error_add(error, TAG": Illegal 'container' attribute");
|
|
|
+ retcode = BOOL_FALSE;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Mode
|
|
|
+ if (!faux_str_is_empty(info->mode)) {
|
|
|
+ kentry_mode_e mode = KENTRY_MODE_NONE;
|
|
|
+ if (!faux_str_casecmp(info->mode, "sequence"))
|
|
|
+ mode = KENTRY_MODE_SEQUENCE;
|
|
|
+ else if (!faux_str_casecmp(info->mode, "switch"))
|
|
|
+ mode = KENTRY_MODE_SWITCH;
|
|
|
+ else if (!faux_str_casecmp(info->mode, "empty"))
|
|
|
+ mode = KENTRY_MODE_EMPTY;
|
|
|
+ if ((KENTRY_MODE_NONE == mode) || !kentry_set_mode(entry, mode)) {
|
|
|
+ faux_error_add(error, TAG": Illegal 'mode' attribute");
|
|
|
+ retcode = BOOL_FALSE;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Min occurs
|
|
|
+ if (!faux_str_is_empty(info->min)) {
|
|
|
+ unsigned int i = 0;
|
|
|
+ if (!faux_conv_atoui(info->min, &i, 0) ||
|
|
|
+ !kentry_set_min(entry, (size_t)i)) {
|
|
|
+ faux_error_add(error, TAG": Illegal 'min' attribute");
|
|
|
+ retcode = BOOL_FALSE;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Max occurs
|
|
|
+ if (!faux_str_is_empty(info->max)) {
|
|
|
+ unsigned int i = 0;
|
|
|
+ if (!faux_conv_atoui(info->max, &i, 0) ||
|
|
|
+ !kentry_set_max(entry, (size_t)i)) {
|
|
|
+ faux_error_add(error, TAG": Illegal 'max' attribute");
|
|
|
+ retcode = BOOL_FALSE;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Ptype string
|
|
|
+ if (!faux_str_is_empty(info->ptype)) {
|
|
|
+ if (!kentry_set_ptype_str(entry, info->ptype)) {
|
|
|
+ faux_error_add(error, TAG": Illegal 'ptype' attribute");
|
|
|
+ retcode = BOOL_FALSE;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Ref string
|
|
|
+ if (!faux_str_is_empty(info->ref)) {
|
|
|
+ if (!kentry_set_ref_str(entry, info->ref)) {
|
|
|
+ faux_error_add(error, TAG": Illegal 'ref' attribute");
|
|
|
+ retcode = BOOL_FALSE;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Value
|
|
|
+ if (!faux_str_is_empty(info->value)) {
|
|
|
+ if (!kentry_set_value(entry, info->value)) {
|
|
|
+ faux_error_add(error, TAG": Illegal 'value' attribute");
|
|
|
+ retcode = BOOL_FALSE;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Restore
|
|
|
+ if (!faux_str_is_empty(info->restore)) {
|
|
|
+ bool_t b = BOOL_FALSE;
|
|
|
+ if (!faux_conv_str2bool(info->restore, &b) ||
|
|
|
+ !kentry_set_restore(entry, b)) {
|
|
|
+ faux_error_add(error, TAG": Illegal 'restore' attribute");
|
|
|
+ retcode = BOOL_FALSE;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return retcode;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+bool_t ientry_parse_nested(const ientry_t *ientry, kentry_t *kentry,
|
|
|
+ faux_error_t *error)
|
|
|
+{
|
|
|
+ bool_t retval = BOOL_TRUE;
|
|
|
+
|
|
|
+ if (!kentry || !ientry) {
|
|
|
+ faux_error_add(error, TAG": Internal error");
|
|
|
+ return BOOL_FALSE;
|
|
|
+ }
|
|
|
+
|
|
|
+ // ENTRY list
|
|
|
+ // ENTRYs can be duplicate. Duplicated ENTRY will add nested
|
|
|
+ // elements to existent ENTRY. Also it can overwrite ENTRY attributes.
|
|
|
+ // So there is no special rule which attribute value will be "on top".
|
|
|
+ // It's a random. Technically later ENTRYs will rewrite previous
|
|
|
+ // values.
|
|
|
+ if (ientry->entrys) {
|
|
|
+ ientry_t **p_ientry = NULL;
|
|
|
+ for (p_ientry = *ientry->entrys; *p_ientry; p_ientry++) {
|
|
|
+ kentry_t *nkentry = NULL;
|
|
|
+ ientry_t *nientry = *p_ientry;
|
|
|
+ const char *entry_name = nientry->name;
|
|
|
+
|
|
|
+ if (entry_name)
|
|
|
+ nkentry = kentry_find_entry(kentry, entry_name);
|
|
|
+
|
|
|
+ // ENTRY already exists
|
|
|
+ if (nkentry) {
|
|
|
+ if (!ientry_parse(nientry, nkentry, error)) {
|
|
|
+ retval = BOOL_FALSE;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (!ientry_parse_nested(nientry, nkentry,
|
|
|
+ error)) {
|
|
|
+ retval = BOOL_FALSE;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // New ENTRY
|
|
|
+ nkentry = ientry_load(nientry, error);
|
|
|
+ if (!nkentry) {
|
|
|
+ retval = BOOL_FALSE;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ kentry_set_parent(nkentry, kentry); // Set parent entry
|
|
|
+ if (!kentry_add_entry(kentry, nkentry)) {
|
|
|
+ faux_error_sprintf(error,
|
|
|
+ TAG": Can't add ENTRY \"%s\"",
|
|
|
+ kentry_name(nkentry));
|
|
|
+ kentry_free(nkentry);
|
|
|
+ retval = BOOL_FALSE;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // ACTION list
|
|
|
+ if (ientry->actions) {
|
|
|
+ iaction_t **p_iaction = NULL;
|
|
|
+ for (p_iaction = *ientry->actions; *p_iaction; p_iaction++) {
|
|
|
+ kaction_t *kaction = NULL;
|
|
|
+ iaction_t *iaction = *p_iaction;
|
|
|
+
|
|
|
+ kaction = iaction_load(iaction, error);
|
|
|
+ if (!kaction) {
|
|
|
+ retval = BOOL_FALSE;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (!kentry_add_action(kentry, kaction)) {
|
|
|
+ faux_error_sprintf(error,
|
|
|
+ TAG": Can't add ACTION #%d",
|
|
|
+ kentry_actions_len(kentry) + 1);
|
|
|
+ kaction_free(kaction);
|
|
|
+ retval = BOOL_FALSE;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!retval)
|
|
|
+ faux_error_sprintf(error, TAG" \"%s\": Illegal nested elements",
|
|
|
+ kentry_name(kentry));
|
|
|
+
|
|
|
+ return retval;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+kentry_t *ientry_load(const ientry_t *ientry, faux_error_t *error)
|
|
|
+{
|
|
|
+ kentry_t *kentry = NULL;
|
|
|
+
|
|
|
+ if (!ientry)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ // Name [mandatory]
|
|
|
+ if (faux_str_is_empty(ientry->name)) {
|
|
|
+ faux_error_add(error, TAG": Empty 'name' attribute");
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ kentry = kentry_new(ientry->name);
|
|
|
+ if (!kentry) {
|
|
|
+ faux_error_sprintf(error, TAG" \"%s\": Can't create object",
|
|
|
+ ientry->name);
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!ientry_parse(ientry, kentry, error)) {
|
|
|
+ kentry_free(kentry);
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Parse nested elements
|
|
|
+ if (!ientry_parse_nested(ientry, kentry, error)) {
|
|
|
+ kentry_free(kentry);
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ return kentry;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+char *ientry_deploy(const kentry_t *kentry, int level)
|
|
|
+{
|
|
|
+ char *str = NULL;
|
|
|
+ char *tmp = NULL;
|
|
|
+ char *mode = NULL;
|
|
|
+ kentry_entrys_node_t *entrys_iter = NULL;
|
|
|
+ char *num = NULL;
|
|
|
+
|
|
|
+ tmp = faux_str_sprintf("%*cENTRY {\n", level, ' ');
|
|
|
+ faux_str_cat(&str, tmp);
|
|
|
+ faux_str_free(tmp);
|
|
|
+
|
|
|
+ attr2ctext(&str, "name", kentry_name(kentry), level + 1);
|
|
|
+ attr2ctext(&str, "help", kentry_help(kentry), level + 1);
|
|
|
+ attr2ctext(&str, "container", faux_conv_bool2str(kentry_container(kentry)), level + 1);
|
|
|
+
|
|
|
+ // Mode
|
|
|
+ switch (kentry_mode(kentry)) {
|
|
|
+ case KENTRY_MODE_SEQUENCE:
|
|
|
+ mode = "sequence";
|
|
|
+ break;
|
|
|
+ case KENTRY_MODE_SWITCH:
|
|
|
+ mode = "switch";
|
|
|
+ break;
|
|
|
+ case KENTRY_MODE_EMPTY:
|
|
|
+ mode = "empty";
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ mode = NULL;
|
|
|
+ }
|
|
|
+ attr2ctext(&str, "mode", mode, level + 1);
|
|
|
+
|
|
|
+ // Min occurs
|
|
|
+ num = faux_str_sprintf("%u", kentry_min(kentry));
|
|
|
+ attr2ctext(&str, "min", num, level + 1);
|
|
|
+ faux_str_free(num);
|
|
|
+ num = NULL;
|
|
|
+
|
|
|
+ // Max occurs
|
|
|
+ num = faux_str_sprintf("%u", kentry_max(kentry));
|
|
|
+ attr2ctext(&str, "max", num, level + 1);
|
|
|
+ faux_str_free(num);
|
|
|
+ num = NULL;
|
|
|
+
|
|
|
+ attr2ctext(&str, "ptype", kentry_ptype_str(kentry), level + 1);
|
|
|
+ attr2ctext(&str, "ref", kentry_ref_str(kentry), level + 1);
|
|
|
+ attr2ctext(&str, "value", kentry_value(kentry), level + 1);
|
|
|
+ attr2ctext(&str, "restore", faux_conv_bool2str(kentry_restore(kentry)), level + 1);
|
|
|
+
|
|
|
+ // ENTRY list
|
|
|
+ entrys_iter = kentry_entrys_iter(kentry);
|
|
|
+ if (entrys_iter) {
|
|
|
+ kentry_t *nentry = NULL;
|
|
|
+
|
|
|
+ tmp = faux_str_sprintf("\n%*cENTRY_LIST\n\n", level + 1, ' ');
|
|
|
+ faux_str_cat(&str, tmp);
|
|
|
+ faux_str_free(tmp);
|
|
|
+
|
|
|
+ while ((nentry = kentry_entrys_each(&entrys_iter))) {
|
|
|
+ tmp = ientry_deploy(nentry, level + 2);
|
|
|
+ faux_str_cat(&str, tmp);
|
|
|
+ faux_str_free(tmp);
|
|
|
+ }
|
|
|
+
|
|
|
+ tmp = faux_str_sprintf("%*cEND_ENTRY_LIST,\n", level + 1, ' ');
|
|
|
+ faux_str_cat(&str, tmp);
|
|
|
+ faux_str_free(tmp);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ACTION list
|
|
|
+ actions_iter = kentry_actions_iter(kentry);
|
|
|
+ if (actions_iter) {
|
|
|
+ kaction_t *action = NULL;
|
|
|
+
|
|
|
+ tmp = faux_str_sprintf("\n%*cACTION_LIST\n\n", level + 1, ' ');
|
|
|
+ faux_str_cat(&str, tmp);
|
|
|
+ faux_str_free(tmp);
|
|
|
+
|
|
|
+ while ((action = kentry_actions_each(&actions_iter))) {
|
|
|
+ tmp = iaction_deploy(action, level + 2);
|
|
|
+ faux_str_cat(&str, tmp);
|
|
|
+ faux_str_free(tmp);
|
|
|
+ }
|
|
|
+
|
|
|
+ tmp = faux_str_sprintf("%*cEND_ACTION_LIST,\n", level + 1, ' ');
|
|
|
+ faux_str_cat(&str, tmp);
|
|
|
+ faux_str_free(tmp);
|
|
|
+ }
|
|
|
+
|
|
|
+ tmp = faux_str_sprintf("%*c},\n\n", level, ' ');
|
|
|
+ faux_str_cat(&str, tmp);
|
|
|
+ faux_str_free(tmp);
|
|
|
+
|
|
|
+ return str;
|
|
|
+}
|