ksession_parse.c 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. /** @file ksession_parse.c
  2. */
  3. #include <assert.h>
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <string.h>
  7. #include <faux/argv.h>
  8. #include <klish/khelper.h>
  9. #include <klish/kview.h>
  10. #include <klish/kscheme.h>
  11. #include <klish/kpath.h>
  12. #include <klish/kpargv.h>
  13. #include <klish/ksession.h>
  14. static bool_t ksession_validate_arg(kentry_t *entry, const char *arg)
  15. {
  16. const char *str = NULL;
  17. assert(entry);
  18. if (!entry)
  19. return BOOL_FALSE;
  20. assert(arg);
  21. if (!arg)
  22. return BOOL_FALSE;
  23. // Temporary test code that implements COMMAND i.e. it compares argument
  24. // to ENTRY's 'name' or 'value'. Later it will be removed by real code.
  25. str = kentry_value(entry);
  26. if (!str)
  27. str = kentry_name(entry);
  28. if (faux_str_casecmp(str, arg) == 0)
  29. return BOOL_TRUE;
  30. return BOOL_FALSE;
  31. }
  32. static kpargv_status_e ksession_parse_arg(kentry_t *current_entry,
  33. faux_argv_node_t **argv_iter, kpargv_t *pargv)
  34. {
  35. kentry_t *entry = current_entry;
  36. kentry_mode_e mode = KENTRY_MODE_NONE;
  37. kpargv_status_e retcode = KPARSE_INPROGRESS; // For ENTRY itself
  38. kpargv_status_e rc = KPARSE_NOTFOUND; // For nested ENTRYs
  39. faux_argv_node_t *saved_argv_iter = NULL;
  40. kpargv_purpose_e purpose = KPURPOSE_NONE;
  41. assert(current_entry);
  42. if (!current_entry)
  43. return KPARSE_ERROR;
  44. assert(argv_iter);
  45. if (!argv_iter)
  46. return KPARSE_ERROR;
  47. assert(pargv);
  48. if (!pargv)
  49. return KPARSE_ERROR;
  50. purpose = kpargv_purpose(pargv);
  51. // Is entry candidate to resolve current arg?
  52. // Container can't be a candidate.
  53. if (!kentry_container(entry)) {
  54. const char *current_arg = NULL;
  55. //printf("arg: %s, entry: %s\n", *argv_iter ? faux_argv_current(*argv_iter) : "<empty>",
  56. // kentry_name(entry));
  57. // When purpose is COMPLETION or HELP then fill completion list.
  58. // Additionally if it's last continuable argument then lie to
  59. // engine: make all last arguments NOTFOUND. It's necessary to walk
  60. // through all variants to gether all completions.
  61. if ((KPURPOSE_COMPLETION == purpose) ||
  62. (KPURPOSE_HELP == purpose)) {
  63. if (!*argv_iter) {
  64. // That's time to add entry to completions list.
  65. if (!kpargv_continuable(pargv))
  66. kpargv_add_completions(pargv, entry);
  67. return KPARSE_INCOMPLETED;
  68. } else {
  69. // Add entry to completions if it's last incompleted arg.
  70. if (faux_argv_is_last(*argv_iter) &&
  71. kpargv_continuable(pargv)) {
  72. kpargv_add_completions(pargv, entry);
  73. return KPARSE_NOTFOUND;
  74. }
  75. }
  76. }
  77. // If all arguments are resolved already then return INCOMPLETED
  78. if (!*argv_iter)
  79. return KPARSE_INCOMPLETED;
  80. // Validate argument
  81. current_arg = faux_argv_current(*argv_iter);
  82. if (ksession_validate_arg(entry, current_arg)) {
  83. kparg_t *parg = kparg_new(entry, current_arg);
  84. kpargv_add_pargs(pargv, parg);
  85. // Command is an ENTRY with ACTIONs or NAVigation
  86. if (kentry_actions_len(entry) > 0)
  87. kpargv_set_command(pargv, entry);
  88. faux_argv_each(argv_iter); // Next argument
  89. retcode = KPARSE_INPROGRESS;
  90. } else {
  91. // It's not a container and is not validated so
  92. // no chance to find anything here.
  93. return KPARSE_NOTFOUND;
  94. }
  95. }
  96. // ENTRY has no nested ENTRYs so return
  97. if (kentry_entrys_is_empty(entry))
  98. return retcode;
  99. // Walk through the nested entries:
  100. saved_argv_iter = *argv_iter;
  101. // EMPTY mode
  102. mode = kentry_mode(entry);
  103. if (KENTRY_MODE_EMPTY == mode)
  104. return retcode;
  105. // SWITCH mode
  106. // Entries within SWITCH can't has 'min'/'max' else than 1.
  107. // So these attributes will be ignored. Note SWITCH itself can have
  108. // 'min'/'max'.
  109. if (KENTRY_MODE_SWITCH == mode) {
  110. kentry_entrys_node_t *iter = kentry_entrys_iter(entry);
  111. kentry_t *nested = NULL;
  112. while ((nested = kentry_entrys_each(&iter))) {
  113. printf("SWITCH arg: %s, entry %s\n", *argv_iter ? faux_argv_current(*argv_iter) : "<empty>", kentry_name(nested));
  114. rc = ksession_parse_arg(nested, argv_iter, pargv);
  115. printf("%s\n", kpargv_status_decode(rc));
  116. // If some arguments was consumed then we will not check
  117. // next SWITCH's entries in any case.
  118. if (saved_argv_iter != *argv_iter)
  119. break;
  120. // Try next entries if current status is NOTFOUND.
  121. // The INCOMPLETED status is for completion list. In this
  122. // case all next statuses will be INCOMPLETED too.
  123. if ((rc != KPARSE_NOTFOUND) && (rc != KPARSE_INCOMPLETED))
  124. break;
  125. }
  126. // SEQUENCE mode
  127. } else if (KENTRY_MODE_SEQUENCE == mode) {
  128. kentry_entrys_node_t *iter = kentry_entrys_iter(entry);
  129. kentry_entrys_node_t *saved_iter = iter;
  130. kentry_t *nested = NULL;
  131. while ((nested = kentry_entrys_each(&iter))) {
  132. kpargv_status_e nrc = KPARSE_NOTFOUND;
  133. size_t num = 0;
  134. size_t min = kentry_min(nested);
  135. // Filter out double parsing for optional entries.
  136. if (kpargv_entry_exists(pargv, nested))
  137. continue;
  138. // Try to match argument and current entry
  139. // (from 'min' to 'max' times)
  140. for (num = 0; num < kentry_max(nested); num++) {
  141. printf("SEQ arg: %s, entry %s\n", *argv_iter ? faux_argv_current(*argv_iter) : "<empty>", kentry_name(nested));
  142. nrc = ksession_parse_arg(nested, argv_iter, pargv);
  143. printf("%s\n", kpargv_status_decode(nrc));
  144. if (nrc != KPARSE_INPROGRESS)
  145. break;
  146. }
  147. // All errors will break the loop
  148. if ((KPARSE_ERROR == nrc) ||
  149. (KPARSE_ILLEGAL == nrc) ||
  150. (KPARSE_NONE == nrc)) {
  151. rc = nrc;
  152. break;
  153. }
  154. // Not found necessary number of mandatory instances
  155. if (num < min) {
  156. if (KPARSE_INPROGRESS == nrc)
  157. rc = KPARSE_NOTFOUND;
  158. else
  159. rc = nrc; // NOTFOUND or INCOMPLETED
  160. break;
  161. }
  162. // It's not an error if optional parameter is absend
  163. rc = KPARSE_INPROGRESS;
  164. // Mandatory or ordered parameter
  165. if ((min > 0) || kentry_order(nested))
  166. saved_iter = iter;
  167. // If optional entry is found then go back to nearest
  168. // non-optional (or ordered) entry to try to find
  169. // another optional entries.
  170. if ((0 == min) && (num > 0))
  171. iter = saved_iter;
  172. }
  173. }
  174. // If nested result is NOTFOUND but argument was consumed
  175. // within nested entries or by entry itself then whole sequence
  176. // is ILLEGAL.
  177. if ((KPARSE_NOTFOUND == rc) &&
  178. ((saved_argv_iter != *argv_iter) || !kentry_container(entry)))
  179. rc = KPARSE_ILLEGAL;
  180. return rc;
  181. }
  182. kpargv_t *ksession_parse_line(ksession_t *session, const char *line,
  183. kpargv_purpose_e purpose)
  184. {
  185. faux_argv_t *argv = NULL;
  186. faux_argv_node_t *argv_iter = NULL;
  187. kpargv_t *pargv = NULL;
  188. kpargv_status_e pstatus = KPARSE_NONE;
  189. kpath_levels_node_t *levels_iterr = NULL;
  190. klevel_t *level = NULL;
  191. size_t level_found = 0; // Level where command was found
  192. kpath_t *path = NULL;
  193. assert(session);
  194. if (!session)
  195. return NULL;
  196. assert(line);
  197. if (!line)
  198. return NULL;
  199. // Split line to arguments
  200. argv = faux_argv_new();
  201. assert(argv);
  202. if (!argv)
  203. return NULL;
  204. if (faux_argv_parse(argv, line) < 0) {
  205. faux_argv_free(argv);
  206. return NULL;
  207. }
  208. argv_iter = faux_argv_iter(argv);
  209. // Initialize kpargv_t
  210. pargv = kpargv_new();
  211. assert(pargv);
  212. kpargv_set_continuable(pargv, faux_argv_is_continuable(argv));
  213. kpargv_set_purpose(pargv, purpose);
  214. // Iterate levels of path from higher to lower. Note the reversed
  215. // iterator will be used.
  216. path = ksession_path(session);
  217. levels_iterr = kpath_iterr(path);
  218. level_found = kpath_len(path);
  219. while ((level = kpath_eachr(&levels_iterr))) {
  220. kentry_t *current_entry = klevel_entry(level);
  221. pstatus = ksession_parse_arg(current_entry, &argv_iter, pargv);
  222. if (pstatus != KPARSE_NOTFOUND)
  223. break;
  224. // NOTFOUND but some args were parsed.
  225. // When it's completion for first argument (that can be continued)
  226. // len == 0 and engine will search for completions on higher
  227. // levels of path.
  228. if (kpargv_pargs_len(pargv) > 0)
  229. break;
  230. level_found--;
  231. }
  232. // Save last argument
  233. if (argv_iter)
  234. kpargv_set_last_arg(pargv, faux_argv_current(argv_iter));
  235. // It's a higher level of parsing, so some statuses can have different
  236. // meanings
  237. if (KPARSE_NONE == pstatus)
  238. pstatus = KPARSE_ERROR; // Strange case
  239. else if (KPARSE_INPROGRESS == pstatus) {
  240. if (NULL == argv_iter) // All args are parsed
  241. pstatus = KPARSE_OK;
  242. else
  243. pstatus = KPARSE_ILLEGAL; // Additional not parsable args
  244. } else if (KPARSE_NOTFOUND == pstatus)
  245. pstatus = KPARSE_ILLEGAL; // Unknown command
  246. kpargv_set_status(pargv, pstatus);
  247. kpargv_set_level(pargv, level_found);
  248. faux_argv_free(argv);
  249. return pargv;
  250. }