ksession_parse.c 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  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_NOTFOUND; // For ENTRY itself
  38. kpargv_status_e rc = KPARSE_NOTFOUND; // For nested ENTRYs
  39. assert(current_entry);
  40. if (!current_entry)
  41. return KPARSE_ERROR;
  42. assert(argv_iter);
  43. if (!argv_iter)
  44. return KPARSE_ERROR;
  45. assert(pargv);
  46. if (!pargv)
  47. return KPARSE_ERROR;
  48. // If all arguments are resolved already then return INCOMPLETED
  49. if (!*argv_iter)
  50. return KPARSE_INCOMPLETED;
  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 = faux_argv_current(*argv_iter);
  55. if (ksession_validate_arg(entry, current_arg)) {
  56. kparg_t *parg = kparg_new(entry, current_arg);
  57. kpargv_add_pargs(pargv, parg);
  58. // Command is an ENTRY with ACTIONs or NAVigation
  59. if (kentry_actions_len(entry) > 0)
  60. kpargv_set_command(pargv, entry);
  61. faux_argv_each(argv_iter); // Next argument
  62. retcode = KPARSE_INPROGRESS;
  63. } else {
  64. // It's not a container and is not validated so
  65. // no chance to find anything here.
  66. return KPARSE_NOTFOUND;
  67. }
  68. }
  69. // ENTRY has no nested ENTRYs so return
  70. if (kentry_entrys_is_empty(entry))
  71. return retcode;
  72. // Walk through the nested entries
  73. mode = kentry_mode(entry);
  74. if (KENTRY_MODE_EMPTY == mode)
  75. return retcode;
  76. // SWITCH
  77. // Entries within SWITCH can't has 'min'/'max' else than 1.
  78. // So these attributes will be ignored. Note SWITCH itself can have
  79. // 'min'/'max'.
  80. if (KENTRY_MODE_SWITCH == mode) {
  81. kentry_entrys_node_t *iter = kentry_entrys_iter(entry);
  82. kentry_t *nested = NULL;
  83. while ((nested = kentry_entrys_each(&iter))) {
  84. rc = ksession_parse_arg(nested, argv_iter, pargv);
  85. // Any variant of error or INPROGRESS
  86. if (rc != KPARSE_NOTFOUND)
  87. break;
  88. }
  89. // SEQUENCE
  90. } else if (KENTRY_MODE_SEQUENCE == mode) {
  91. kentry_entrys_node_t *iter = kentry_entrys_iter(entry);
  92. kentry_entrys_node_t *saved_iter = iter;
  93. kentry_t *nested = NULL;
  94. while ((nested = kentry_entrys_each(&iter))) {
  95. kpargv_status_e nrc = KPARSE_NOTFOUND;
  96. size_t num = 0;
  97. size_t min = kentry_min(nested);
  98. //printf("Arg: %s, entry %s\n", faux_argv_current(*argv_iter), kentry_name(nested));
  99. // Filter out double parsing for optional entries.
  100. if (kpargv_entry_exists(pargv, nested))
  101. continue;
  102. // Try to match argument and current entry
  103. // (from 'min' to 'max' times)
  104. for (num = 0; num < kentry_max(nested); num++) {
  105. nrc = ksession_parse_arg(nested, argv_iter, pargv);
  106. if (nrc != KPARSE_INPROGRESS)
  107. break;
  108. }
  109. // All errors will break the loop
  110. if ((nrc != KPARSE_INPROGRESS) && (nrc != KPARSE_NOTFOUND)) {
  111. rc = nrc;
  112. break;
  113. }
  114. // Not found all mandatory instances (NOTFOUND)
  115. if (num < min) {
  116. rc = KPARSE_NOTFOUND;
  117. break;
  118. }
  119. // It's not an error if optional parameter is absend
  120. rc = KPARSE_INPROGRESS;
  121. // Mandatory or ordered parameter
  122. if ((min > 0) || kentry_order(nested))
  123. saved_iter = iter;
  124. // If optional entry is found then go back to nearest
  125. // non-optional (or ordered) entry to try to find
  126. // another optional entries.
  127. if ((0 == min) && (num > 0))
  128. iter = saved_iter;
  129. }
  130. }
  131. // When ENTRY (not container) is found but mandatory nested ENTRY is
  132. // not resolved. It's inconsistent. So NOTFOUND is not suitable in
  133. // this case.
  134. if ((KPARSE_NOTFOUND == rc) && (KPARSE_INPROGRESS == retcode))
  135. return KPARSE_ILLEGAL;
  136. return rc;
  137. }
  138. kpargv_t *ksession_parse_line(ksession_t *session, const char *line)
  139. {
  140. faux_argv_t *argv = NULL;
  141. faux_argv_node_t *argv_iter = NULL;
  142. kpargv_t *pargv = NULL;
  143. kpargv_status_e pstatus = KPARSE_NONE;
  144. kpath_levels_node_t *levels_iterr = NULL;
  145. klevel_t *level = NULL;
  146. size_t level_found = 0; // Level where command was found
  147. kpath_t *path = NULL;
  148. assert(session);
  149. if (!session)
  150. return NULL;
  151. assert(line);
  152. if (!line)
  153. return NULL;
  154. // Split line to arguments
  155. argv = faux_argv_new();
  156. assert(argv);
  157. if (!argv)
  158. return NULL;
  159. if (faux_argv_parse(argv, line) < 0) {
  160. faux_argv_free(argv);
  161. return NULL;
  162. }
  163. argv_iter = faux_argv_iter(argv);
  164. pargv = kpargv_new();
  165. assert(pargv);
  166. kpargv_set_continuable(pargv, faux_argv_is_continuable(argv));
  167. // Iterate levels of path from higher to lower. Note the reversed
  168. // iterator will be used.
  169. path = ksession_path(session);
  170. levels_iterr = kpath_iterr(path);
  171. level_found = kpath_len(path);
  172. while ((level = kpath_eachr(&levels_iterr))) {
  173. kentry_t *current_entry = klevel_entry(level);
  174. pstatus = ksession_parse_arg(current_entry, &argv_iter, pargv);
  175. if (pstatus != KPARSE_NOTFOUND)
  176. break;
  177. level_found--;
  178. }
  179. // It's a higher level of parsing, so some statuses can have different
  180. // meanings
  181. if (KPARSE_NONE == pstatus)
  182. pstatus = KPARSE_ERROR; // Strange case
  183. else if (KPARSE_INPROGRESS == pstatus) {
  184. if (NULL == argv_iter) // All args are parsed
  185. pstatus = KPARSE_OK;
  186. else
  187. pstatus = KPARSE_ILLEGAL; // Additional not parsable args
  188. } else if (KPARSE_NOTFOUND == pstatus)
  189. pstatus = KPARSE_ILLEGAL; // Unknown command
  190. kpargv_set_status(pargv, pstatus);
  191. kpargv_set_level(pargv, level_found);
  192. faux_argv_free(argv);
  193. return pargv;
  194. }