|
@@ -42,8 +42,10 @@ static kpargv_status_e ksession_parse_arg(kentry_t *current_entry,
|
|
|
{
|
|
|
kentry_t *entry = current_entry;
|
|
|
kentry_mode_e mode = KENTRY_MODE_NONE;
|
|
|
- kpargv_status_e retcode = KPARSE_NOTFOUND; // For ENTRY itself
|
|
|
+ kpargv_status_e retcode = KPARSE_INPROGRESS; // For ENTRY itself
|
|
|
kpargv_status_e rc = KPARSE_NOTFOUND; // For nested ENTRYs
|
|
|
+ faux_argv_node_t *saved_argv_iter = NULL;
|
|
|
+ kpargv_purpose_e purpose = KPURPOSE_NONE;
|
|
|
|
|
|
assert(current_entry);
|
|
|
if (!current_entry)
|
|
@@ -55,14 +57,43 @@ static kpargv_status_e ksession_parse_arg(kentry_t *current_entry,
|
|
|
if (!pargv)
|
|
|
return KPARSE_ERROR;
|
|
|
|
|
|
- // If all arguments are resolved already then return INCOMPLETED
|
|
|
- if (!*argv_iter)
|
|
|
- return KPARSE_INCOMPLETED;
|
|
|
+ purpose = kpargv_purpose(pargv);
|
|
|
|
|
|
// Is entry candidate to resolve current arg?
|
|
|
// Container can't be a candidate.
|
|
|
if (!kentry_container(entry)) {
|
|
|
- const char *current_arg = faux_argv_current(*argv_iter);
|
|
|
+ const char *current_arg = NULL;
|
|
|
+
|
|
|
+//printf("arg: %s, entry: %s\n", *argv_iter ? faux_argv_current(*argv_iter) : "<empty>",
|
|
|
+// kentry_name(entry));
|
|
|
+
|
|
|
+ // When purpose is COMPLETION or HELP then fill completion list.
|
|
|
+ // Additionally if it's last continuable argument then lie to
|
|
|
+ // engine: make all last arguments NOTFOUND. It's necessary to walk
|
|
|
+ // through all variants to gether all completions.
|
|
|
+ if ((KPURPOSE_COMPLETION == purpose) ||
|
|
|
+ (KPURPOSE_HELP == purpose)) {
|
|
|
+ if (!*argv_iter) {
|
|
|
+ // That's time to add entry to completions list.
|
|
|
+ if (!kpargv_continuable(pargv))
|
|
|
+ kpargv_add_completions(pargv, entry);
|
|
|
+ return KPARSE_INCOMPLETED;
|
|
|
+ } else {
|
|
|
+ // Add entry to completions if it's last incompleted arg.
|
|
|
+ if (faux_argv_is_last(*argv_iter) &&
|
|
|
+ kpargv_continuable(pargv)) {
|
|
|
+ kpargv_add_completions(pargv, entry);
|
|
|
+ return KPARSE_NOTFOUND;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // If all arguments are resolved already then return INCOMPLETED
|
|
|
+ if (!*argv_iter)
|
|
|
+ return KPARSE_INCOMPLETED;
|
|
|
+
|
|
|
+ // Validate argument
|
|
|
+ current_arg = faux_argv_current(*argv_iter);
|
|
|
if (ksession_validate_arg(entry, current_arg)) {
|
|
|
kparg_t *parg = kparg_new(entry, current_arg);
|
|
|
kpargv_add_pargs(pargv, parg);
|
|
@@ -82,26 +113,38 @@ static kpargv_status_e ksession_parse_arg(kentry_t *current_entry,
|
|
|
if (kentry_entrys_is_empty(entry))
|
|
|
return retcode;
|
|
|
|
|
|
- // Walk through the nested entries
|
|
|
+ // Walk through the nested entries:
|
|
|
+ saved_argv_iter = *argv_iter;
|
|
|
+
|
|
|
+ // EMPTY mode
|
|
|
mode = kentry_mode(entry);
|
|
|
if (KENTRY_MODE_EMPTY == mode)
|
|
|
return retcode;
|
|
|
|
|
|
- // SWITCH
|
|
|
+ // SWITCH mode
|
|
|
// Entries within SWITCH can't has 'min'/'max' else than 1.
|
|
|
// So these attributes will be ignored. Note SWITCH itself can have
|
|
|
// 'min'/'max'.
|
|
|
if (KENTRY_MODE_SWITCH == mode) {
|
|
|
kentry_entrys_node_t *iter = kentry_entrys_iter(entry);
|
|
|
kentry_t *nested = NULL;
|
|
|
+
|
|
|
while ((nested = kentry_entrys_each(&iter))) {
|
|
|
+printf("SWITCH arg: %s, entry %s\n", *argv_iter ? faux_argv_current(*argv_iter) : "<empty>", kentry_name(nested));
|
|
|
rc = ksession_parse_arg(nested, argv_iter, pargv);
|
|
|
- // Any variant of error or INPROGRESS
|
|
|
- if (rc != KPARSE_NOTFOUND)
|
|
|
+printf("%s\n", kpargv_status_decode(rc));
|
|
|
+ // If some arguments was consumed then we will not check
|
|
|
+ // next SWITCH's entries in any case.
|
|
|
+ if (saved_argv_iter != *argv_iter)
|
|
|
+ break;
|
|
|
+ // Try next entries if current status is NOTFOUND.
|
|
|
+ // The INCOMPLETED status is for completion list. In this
|
|
|
+ // case all next statuses will be INCOMPLETED too.
|
|
|
+ if ((rc != KPARSE_NOTFOUND) && (rc != KPARSE_INCOMPLETED))
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
- // SEQUENCE
|
|
|
+ // SEQUENCE mode
|
|
|
} else if (KENTRY_MODE_SEQUENCE == mode) {
|
|
|
kentry_entrys_node_t *iter = kentry_entrys_iter(entry);
|
|
|
kentry_entrys_node_t *saved_iter = iter;
|
|
@@ -112,25 +155,31 @@ static kpargv_status_e ksession_parse_arg(kentry_t *current_entry,
|
|
|
size_t num = 0;
|
|
|
size_t min = kentry_min(nested);
|
|
|
|
|
|
-//printf("Arg: %s, entry %s\n", faux_argv_current(*argv_iter), kentry_name(nested));
|
|
|
// Filter out double parsing for optional entries.
|
|
|
if (kpargv_entry_exists(pargv, nested))
|
|
|
continue;
|
|
|
// Try to match argument and current entry
|
|
|
// (from 'min' to 'max' times)
|
|
|
for (num = 0; num < kentry_max(nested); num++) {
|
|
|
+printf("SEQ arg: %s, entry %s\n", *argv_iter ? faux_argv_current(*argv_iter) : "<empty>", kentry_name(nested));
|
|
|
nrc = ksession_parse_arg(nested, argv_iter, pargv);
|
|
|
+printf("%s\n", kpargv_status_decode(nrc));
|
|
|
if (nrc != KPARSE_INPROGRESS)
|
|
|
break;
|
|
|
}
|
|
|
// All errors will break the loop
|
|
|
- if ((nrc != KPARSE_INPROGRESS) && (nrc != KPARSE_NOTFOUND)) {
|
|
|
+ if ((KPARSE_ERROR == nrc) ||
|
|
|
+ (KPARSE_ILLEGAL == nrc) ||
|
|
|
+ (KPARSE_NONE == nrc)) {
|
|
|
rc = nrc;
|
|
|
break;
|
|
|
}
|
|
|
- // Not found all mandatory instances (NOTFOUND)
|
|
|
+ // Not found necessary number of mandatory instances
|
|
|
if (num < min) {
|
|
|
- rc = KPARSE_NOTFOUND;
|
|
|
+ if (KPARSE_INPROGRESS == nrc)
|
|
|
+ rc = KPARSE_NOTFOUND;
|
|
|
+ else
|
|
|
+ rc = nrc; // NOTFOUND or INCOMPLETED
|
|
|
break;
|
|
|
}
|
|
|
// It's not an error if optional parameter is absend
|
|
@@ -146,20 +195,21 @@ static kpargv_status_e ksession_parse_arg(kentry_t *current_entry,
|
|
|
if ((0 == min) && (num > 0))
|
|
|
iter = saved_iter;
|
|
|
}
|
|
|
-
|
|
|
}
|
|
|
|
|
|
- // When ENTRY (not container) is found but mandatory nested ENTRY is
|
|
|
- // not resolved. It's inconsistent. So NOTFOUND is not suitable in
|
|
|
- // this case.
|
|
|
- if ((KPARSE_NOTFOUND == rc) && (KPARSE_INPROGRESS == retcode))
|
|
|
- return KPARSE_ILLEGAL;
|
|
|
+ // If nested result is NOTFOUND but argument was consumed
|
|
|
+ // within nested entries or by entry itself then whole sequence
|
|
|
+ // is ILLEGAL.
|
|
|
+ if ((KPARSE_NOTFOUND == rc) &&
|
|
|
+ ((saved_argv_iter != *argv_iter) || !kentry_container(entry)))
|
|
|
+ rc = KPARSE_ILLEGAL;
|
|
|
|
|
|
return rc;
|
|
|
}
|
|
|
|
|
|
|
|
|
-kpargv_t *ksession_parse_line(ksession_t *session, const char *line)
|
|
|
+kpargv_t *ksession_parse_line(ksession_t *session, const char *line,
|
|
|
+ kpargv_purpose_e purpose)
|
|
|
{
|
|
|
faux_argv_t *argv = NULL;
|
|
|
faux_argv_node_t *argv_iter = NULL;
|
|
@@ -188,9 +238,12 @@ kpargv_t *ksession_parse_line(ksession_t *session, const char *line)
|
|
|
}
|
|
|
argv_iter = faux_argv_iter(argv);
|
|
|
|
|
|
+ // Initialize kpargv_t
|
|
|
pargv = kpargv_new();
|
|
|
assert(pargv);
|
|
|
kpargv_set_continuable(pargv, faux_argv_is_continuable(argv));
|
|
|
+ kpargv_set_purpose(pargv, purpose);
|
|
|
+
|
|
|
// Iterate levels of path from higher to lower. Note the reversed
|
|
|
// iterator will be used.
|
|
|
path = ksession_path(session);
|
|
@@ -201,8 +254,17 @@ kpargv_t *ksession_parse_line(ksession_t *session, const char *line)
|
|
|
pstatus = ksession_parse_arg(current_entry, &argv_iter, pargv);
|
|
|
if (pstatus != KPARSE_NOTFOUND)
|
|
|
break;
|
|
|
+ // NOTFOUND but some args were parsed.
|
|
|
+ // When it's completion for first argument (that can be continued)
|
|
|
+ // len == 0 and engine will search for completions on higher
|
|
|
+ // levels of path.
|
|
|
+ if (kpargv_pargs_len(pargv) > 0)
|
|
|
+ break;
|
|
|
level_found--;
|
|
|
}
|
|
|
+ // Save last argument
|
|
|
+ if (argv_iter)
|
|
|
+ kpargv_set_last_arg(pargv, faux_argv_current(argv_iter));
|
|
|
// It's a higher level of parsing, so some statuses can have different
|
|
|
// meanings
|
|
|
if (KPARSE_NONE == pstatus)
|