ksession_parse.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559
  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/list.h>
  8. #include <faux/argv.h>
  9. #include <faux/error.h>
  10. #include <klish/khelper.h>
  11. #include <klish/kview.h>
  12. #include <klish/kscheme.h>
  13. #include <klish/kpath.h>
  14. #include <klish/kpargv.h>
  15. #include <klish/kexec.h>
  16. #include <klish/ksession.h>
  17. static bool_t ksession_validate_arg(kentry_t *entry, const char *arg)
  18. {
  19. const char *str = NULL;
  20. assert(entry);
  21. if (!entry)
  22. return BOOL_FALSE;
  23. assert(arg);
  24. if (!arg)
  25. return BOOL_FALSE;
  26. // Temporary test code that implements COMMAND i.e. it compares argument
  27. // to ENTRY's 'name' or 'value'. Later it will be removed by real code.
  28. str = kentry_value(entry);
  29. if (!str)
  30. str = kentry_name(entry);
  31. if (faux_str_casecmp(str, arg) == 0)
  32. return BOOL_TRUE;
  33. return BOOL_FALSE;
  34. }
  35. static kpargv_status_e ksession_parse_arg(kentry_t *current_entry,
  36. faux_argv_node_t **argv_iter, kpargv_t *pargv, bool_t entry_is_command)
  37. {
  38. kentry_t *entry = current_entry;
  39. kentry_mode_e mode = KENTRY_MODE_NONE;
  40. kpargv_status_e retcode = KPARSE_INPROGRESS; // For ENTRY itself
  41. kpargv_status_e rc = KPARSE_NOTFOUND; // For nested ENTRYs
  42. faux_argv_node_t *saved_argv_iter = NULL;
  43. kpargv_purpose_e purpose = KPURPOSE_NONE;
  44. assert(current_entry);
  45. if (!current_entry)
  46. return KPARSE_ERROR;
  47. assert(argv_iter);
  48. if (!argv_iter)
  49. return KPARSE_ERROR;
  50. assert(pargv);
  51. if (!pargv)
  52. return KPARSE_ERROR;
  53. purpose = kpargv_purpose(pargv); // Purpose of parsing
  54. // If we know the entry is a command then don't validate it. This
  55. // behaviour is usefull for special purpose entries like PTYPEs, CONDs,
  56. // etc. These entries are the starting point for parsing their args.
  57. // We don't need to parse command itself. Command is predefined.
  58. if (entry_is_command) {
  59. kparg_t *parg = NULL;
  60. // Command is an ENTRY with ACTIONs
  61. if (kentry_actions_len(entry) <= 0)
  62. return KPARSE_ILLEGAL;
  63. parg = kparg_new(entry, NULL);
  64. kpargv_add_pargs(pargv, parg);
  65. kpargv_set_command(pargv, entry);
  66. retcode = KPARSE_INPROGRESS;
  67. // Is entry candidate to resolve current arg?
  68. // Container can't be a candidate.
  69. } else if (!kentry_container(entry)) {
  70. const char *current_arg = NULL;
  71. //printf("arg: %s, entry: %s\n", *argv_iter ? faux_argv_current(*argv_iter) : "<empty>",
  72. // kentry_name(entry));
  73. // When purpose is COMPLETION or HELP then fill completion list.
  74. // Additionally if it's last continuable argument then lie to
  75. // engine: make all last arguments NOTFOUND. It's necessary to walk
  76. // through all variants to gether all completions.
  77. if ((KPURPOSE_COMPLETION == purpose) ||
  78. (KPURPOSE_HELP == purpose)) {
  79. if (!*argv_iter) {
  80. // That's time to add entry to completions list.
  81. if (!kpargv_continuable(pargv))
  82. kpargv_add_completions(pargv, entry);
  83. return KPARSE_INCOMPLETED;
  84. } else {
  85. // Add entry to completions if it's last incompleted arg.
  86. if (faux_argv_is_last(*argv_iter) &&
  87. kpargv_continuable(pargv)) {
  88. kpargv_add_completions(pargv, entry);
  89. return KPARSE_NOTFOUND;
  90. }
  91. }
  92. }
  93. // If all arguments are resolved already then return INCOMPLETED
  94. if (!*argv_iter)
  95. return KPARSE_INCOMPLETED;
  96. // Validate argument
  97. current_arg = faux_argv_current(*argv_iter);
  98. if (ksession_validate_arg(entry, current_arg)) {
  99. kparg_t *parg = kparg_new(entry, current_arg);
  100. kpargv_add_pargs(pargv, parg);
  101. // Command is an ENTRY with ACTIONs or NAVigation
  102. if (kentry_actions_len(entry) > 0)
  103. kpargv_set_command(pargv, entry);
  104. faux_argv_each(argv_iter); // Next argument
  105. retcode = KPARSE_INPROGRESS;
  106. } else {
  107. // It's not a container and is not validated so
  108. // no chance to find anything here.
  109. return KPARSE_NOTFOUND;
  110. }
  111. }
  112. // ENTRY has no nested ENTRYs so return
  113. if (kentry_entrys_is_empty(entry))
  114. return retcode;
  115. // Walk through the nested entries:
  116. saved_argv_iter = *argv_iter;
  117. // EMPTY mode
  118. mode = kentry_mode(entry);
  119. if (KENTRY_MODE_EMPTY == mode)
  120. return retcode;
  121. // SWITCH mode
  122. // Entries within SWITCH can't has 'min'/'max' else than 1.
  123. // So these attributes will be ignored. Note SWITCH itself can have
  124. // 'min'/'max'.
  125. if (KENTRY_MODE_SWITCH == mode) {
  126. kentry_entrys_node_t *iter = kentry_entrys_iter(entry);
  127. kentry_t *nested = NULL;
  128. while ((nested = kentry_entrys_each(&iter))) {
  129. // Ignore entries with non-COMMON purpose.
  130. if (kentry_purpose(nested) != KENTRY_PURPOSE_COMMON)
  131. continue;
  132. //printf("SWITCH arg: %s, entry %s\n", *argv_iter ? faux_argv_current(*argv_iter) : "<empty>", kentry_name(nested));
  133. rc = ksession_parse_arg(nested, argv_iter, pargv, BOOL_FALSE);
  134. //printf("%s\n", kpargv_status_decode(rc));
  135. // If some arguments was consumed then we will not check
  136. // next SWITCH's entries in any case.
  137. if (saved_argv_iter != *argv_iter)
  138. break;
  139. // Try next entries if current status is NOTFOUND.
  140. // The INCOMPLETED status is for completion list. In this
  141. // case all next statuses will be INCOMPLETED too.
  142. if ((rc != KPARSE_NOTFOUND) && (rc != KPARSE_INCOMPLETED))
  143. break;
  144. }
  145. // SEQUENCE mode
  146. } else if (KENTRY_MODE_SEQUENCE == mode) {
  147. kentry_entrys_node_t *iter = kentry_entrys_iter(entry);
  148. kentry_entrys_node_t *saved_iter = iter;
  149. kentry_t *nested = NULL;
  150. while ((nested = kentry_entrys_each(&iter))) {
  151. kpargv_status_e nrc = KPARSE_NOTFOUND;
  152. size_t num = 0;
  153. size_t min = kentry_min(nested);
  154. // Ignore entries with non-COMMON purpose.
  155. if (kentry_purpose(nested) != KENTRY_PURPOSE_COMMON)
  156. continue;
  157. // Filter out double parsing for optional entries.
  158. if (kpargv_entry_exists(pargv, nested))
  159. continue;
  160. // Try to match argument and current entry
  161. // (from 'min' to 'max' times)
  162. for (num = 0; num < kentry_max(nested); num++) {
  163. //printf("SEQ arg: %s, entry %s\n", *argv_iter ? faux_argv_current(*argv_iter) : "<empty>", kentry_name(nested));
  164. nrc = ksession_parse_arg(nested, argv_iter,
  165. pargv, BOOL_FALSE);
  166. //printf("%s\n", kpargv_status_decode(nrc));
  167. if (nrc != KPARSE_INPROGRESS)
  168. break;
  169. }
  170. // All errors will break the loop
  171. if ((KPARSE_ERROR == nrc) ||
  172. (KPARSE_ILLEGAL == nrc) ||
  173. (KPARSE_NONE == nrc)) {
  174. rc = nrc;
  175. break;
  176. }
  177. // Not found necessary number of mandatory instances
  178. if (num < min) {
  179. if (KPARSE_INPROGRESS == nrc)
  180. rc = KPARSE_NOTFOUND;
  181. else
  182. rc = nrc; // NOTFOUND or INCOMPLETED
  183. break;
  184. }
  185. // It's not an error if optional parameter is absend
  186. rc = KPARSE_INPROGRESS;
  187. // Mandatory or ordered parameter
  188. if ((min > 0) || kentry_order(nested))
  189. saved_iter = iter;
  190. // If optional entry is found then go back to nearest
  191. // non-optional (or ordered) entry to try to find
  192. // another optional entries.
  193. if ((0 == min) && (num > 0))
  194. iter = saved_iter;
  195. }
  196. }
  197. // If nested result is NOTFOUND but argument was consumed
  198. // within nested entries or by entry itself then whole sequence
  199. // is ILLEGAL.
  200. if ((KPARSE_NOTFOUND == rc) &&
  201. ((saved_argv_iter != *argv_iter) || !kentry_container(entry)))
  202. rc = KPARSE_ILLEGAL;
  203. return rc;
  204. }
  205. kpargv_t *ksession_parse_line(ksession_t *session, const faux_argv_t *argv,
  206. kpargv_purpose_e purpose)
  207. {
  208. faux_argv_node_t *argv_iter = NULL;
  209. kpargv_t *pargv = NULL;
  210. kpargv_status_e pstatus = KPARSE_NONE;
  211. kpath_levels_node_t *levels_iterr = NULL;
  212. klevel_t *level = NULL;
  213. size_t level_found = 0; // Level where command was found
  214. kpath_t *path = NULL;
  215. assert(session);
  216. if (!session)
  217. return NULL;
  218. assert(argv);
  219. if (!argv)
  220. return NULL;
  221. argv_iter = faux_argv_iter(argv);
  222. // Initialize kpargv_t
  223. pargv = kpargv_new();
  224. assert(pargv);
  225. kpargv_set_continuable(pargv, faux_argv_is_continuable(argv));
  226. kpargv_set_purpose(pargv, purpose);
  227. // Iterate levels of path from higher to lower. Note the reversed
  228. // iterator will be used.
  229. path = ksession_path(session);
  230. levels_iterr = kpath_iterr(path);
  231. level_found = kpath_len(path);
  232. while ((level = kpath_eachr(&levels_iterr))) {
  233. kentry_t *current_entry = klevel_entry(level);
  234. // Ignore entries with non-COMMON purpose. These entries are for
  235. // special processing and will be ignored here.
  236. if (kentry_purpose(current_entry) != KENTRY_PURPOSE_COMMON)
  237. continue;
  238. // Parsing
  239. pstatus = ksession_parse_arg(current_entry, &argv_iter, pargv,
  240. BOOL_FALSE);
  241. if (pstatus != KPARSE_NOTFOUND)
  242. break;
  243. // NOTFOUND but some args were parsed.
  244. // When it's completion for first argument (that can be continued)
  245. // len == 0 and engine will search for completions on higher
  246. // levels of path.
  247. if (kpargv_pargs_len(pargv) > 0)
  248. break;
  249. level_found--;
  250. }
  251. // Save last argument
  252. if (argv_iter)
  253. kpargv_set_last_arg(pargv, faux_argv_current(argv_iter));
  254. // It's a higher level of parsing, so some statuses can have different
  255. // meanings
  256. if (KPARSE_NONE == pstatus)
  257. pstatus = KPARSE_ERROR; // Strange case
  258. else if (KPARSE_INPROGRESS == pstatus) {
  259. if (NULL == argv_iter) // All args are parsed
  260. pstatus = KPARSE_OK;
  261. else
  262. pstatus = KPARSE_ILLEGAL; // Additional not parsable args
  263. } else if (KPARSE_NOTFOUND == pstatus)
  264. pstatus = KPARSE_ILLEGAL; // Unknown command
  265. // If no ACTIONs were found i.e. command was not found
  266. if ((KPARSE_OK == pstatus) && !kpargv_command(pargv))
  267. pstatus = KPARSE_NOACTION;
  268. kpargv_set_status(pargv, pstatus);
  269. kpargv_set_level(pargv, level_found);
  270. return pargv;
  271. }
  272. // Delimeter of commands is '|' (pipe)
  273. faux_list_t *ksession_split_pipes(const char *raw_line, faux_error_t *error)
  274. {
  275. faux_list_t *list = NULL;
  276. faux_argv_t *argv = NULL;
  277. faux_argv_node_t *argv_iter = NULL;
  278. faux_argv_t *cur_argv = NULL; // Current argv
  279. const char *delimeter = "|";
  280. const char *arg = NULL;
  281. assert(raw_line);
  282. if (!raw_line)
  283. return NULL;
  284. // Split raw line to arguments
  285. argv = faux_argv_new();
  286. assert(argv);
  287. if (!argv)
  288. return NULL;
  289. if (faux_argv_parse(argv, raw_line) < 0) {
  290. faux_argv_free(argv);
  291. return NULL;
  292. }
  293. list = faux_list_new(FAUX_LIST_UNSORTED, FAUX_LIST_NONUNIQUE,
  294. NULL, NULL, (void (*)(void *))faux_argv_free);
  295. assert(list);
  296. if (!list) {
  297. faux_argv_free(argv);
  298. return NULL;
  299. }
  300. argv_iter = faux_argv_iter(argv);
  301. cur_argv = faux_argv_new();
  302. assert(cur_argv);
  303. while ((arg = faux_argv_each(&argv_iter))) {
  304. if (strcmp(arg, delimeter) == 0) {
  305. // End of current line (from "|" to "|")
  306. // '|' in a first position is an error
  307. if (faux_argv_len(cur_argv) == 0) {
  308. faux_argv_free(argv);
  309. faux_list_free(list);
  310. faux_error_sprintf(error, "The pipe '|' can't "
  311. "be at the first position");
  312. return NULL;
  313. }
  314. // Add argv to argv's list
  315. faux_list_add(list, cur_argv);
  316. cur_argv = faux_argv_new();
  317. assert(cur_argv);
  318. } else {
  319. faux_argv_add(cur_argv, arg);
  320. }
  321. }
  322. // Continuable flag is usefull for last argv
  323. faux_argv_set_continuable(cur_argv, faux_argv_is_continuable(argv));
  324. // Empty cur_argv is not an error. It's usefull for completion and help.
  325. // But empty cur_argv and continuable is abnormal.
  326. if ((faux_argv_len(cur_argv) == 0) &&
  327. faux_argv_is_continuable(cur_argv)) {
  328. faux_argv_free(argv);
  329. faux_list_free(list);
  330. faux_error_sprintf(error, "The pipe '|' can't "
  331. "be the last argument");
  332. return NULL;
  333. }
  334. faux_list_add(list, cur_argv);
  335. faux_argv_free(argv);
  336. return list;
  337. }
  338. // All components except last one must be legal for execution but last
  339. // component must be parsed for completion.
  340. // Completion is a "back-end" operation so it doesn't need detailed error
  341. // reporting.
  342. kpargv_t *ksession_parse_for_completion(ksession_t *session,
  343. const char *raw_line)
  344. {
  345. faux_list_t *split = NULL;
  346. faux_list_node_t *iter = NULL;
  347. kpargv_t *pargv = NULL;
  348. assert(session);
  349. if (!session)
  350. return NULL;
  351. assert(raw_line);
  352. if (!raw_line)
  353. return NULL;
  354. // Split raw line (with '|') to components
  355. split = ksession_split_pipes(raw_line, NULL);
  356. if (!split || (faux_list_len(split) < 1)) {
  357. faux_list_free(split);
  358. return NULL;
  359. }
  360. iter = faux_list_head(split);
  361. while (iter) {
  362. faux_argv_t *argv = (faux_argv_t *)faux_list_data(iter);
  363. if (iter == faux_list_tail(split)) { // Last item
  364. pargv = ksession_parse_line(session, argv,
  365. KPURPOSE_COMPLETION);
  366. if (!pargv) {
  367. faux_list_free(split);
  368. return NULL;
  369. }
  370. } else { // Non-last item
  371. pargv = ksession_parse_line(session, argv,
  372. KPURPOSE_EXEC);
  373. // All non-last components must be ready for execution
  374. if (!pargv || kpargv_status(pargv) != KPARSE_OK) {
  375. kpargv_free(pargv);
  376. faux_list_free(split);
  377. return NULL;
  378. }
  379. }
  380. iter = faux_list_next_node(iter);
  381. }
  382. faux_list_free(split);
  383. return pargv;
  384. }
  385. kexec_t *ksession_parse_for_exec(ksession_t *session, const char *raw_line,
  386. faux_error_t *error)
  387. {
  388. faux_list_t *split = NULL;
  389. faux_list_node_t *iter = NULL;
  390. kpargv_t *pargv = NULL;
  391. kexec_t *exec = NULL;
  392. assert(session);
  393. if (!session)
  394. return NULL;
  395. assert(raw_line);
  396. if (!raw_line)
  397. return NULL;
  398. // Split raw line (with '|') to components
  399. split = ksession_split_pipes(raw_line, error);
  400. if (!split || (faux_list_len(split) < 1)) {
  401. faux_list_free(split);
  402. return NULL;
  403. }
  404. // Create exec list
  405. exec = kexec_new();
  406. assert(exec);
  407. if (!exec) {
  408. faux_list_free(split);
  409. return NULL;
  410. }
  411. iter = faux_list_head(split);
  412. while (iter) {
  413. faux_argv_t *argv = (faux_argv_t *)faux_list_data(iter);
  414. kcontext_t *context = NULL;
  415. pargv = ksession_parse_line(session, argv, KPURPOSE_EXEC);
  416. // All components must be ready for execution
  417. if (!pargv) {
  418. faux_list_free(split);
  419. return NULL;
  420. }
  421. if (kpargv_status(pargv) != KPARSE_OK) {
  422. faux_error_sprintf(error, "%s",
  423. kpargv_status_str(pargv));
  424. kpargv_free(pargv);
  425. faux_list_free(split);
  426. return NULL;
  427. }
  428. // Only the first component can have 'restore=true' attribute
  429. if ((iter != faux_list_head(split)) &&
  430. kentry_restore(kpargv_command(pargv))) {
  431. faux_error_sprintf(error, "The command \"%s\" "
  432. "can't be destination of pipe",
  433. kentry_name(kpargv_command(pargv)));
  434. kpargv_free(pargv);
  435. faux_list_free(split);
  436. return NULL;
  437. }
  438. // Fill the kexec_t
  439. context = kcontext_new(KCONTEXT_PLUGIN_ACTION);
  440. assert(context);
  441. kcontext_set_pargv(context, pargv);
  442. kexec_add_contexts(exec, context);
  443. // Next component
  444. iter = faux_list_next_node(iter);
  445. }
  446. faux_list_free(split);
  447. return exec;
  448. }
  449. kexec_t *ksession_parse_for_local_exec(kentry_t *entry)
  450. {
  451. faux_argv_node_t *argv_iter = NULL;
  452. kpargv_t *pargv = NULL;
  453. kexec_t *exec = NULL;
  454. faux_argv_t *argv = faux_argv_new();
  455. kcontext_t *context = NULL;
  456. kpargv_status_e pstatus = KPARSE_NONE;
  457. const char *line = NULL; // TODO: Must be 'line' field of ENTRY
  458. assert(entry);
  459. if (!entry)
  460. return NULL;
  461. exec = kexec_new();
  462. assert(exec);
  463. argv = faux_argv_new();
  464. assert(argv);
  465. faux_argv_parse(argv, line);
  466. argv_iter = faux_argv_iter(argv);
  467. pargv = kpargv_new();
  468. assert(pargv);
  469. kpargv_set_continuable(pargv, faux_argv_is_continuable(argv));
  470. kpargv_set_purpose(pargv, KPURPOSE_EXEC);
  471. pstatus = ksession_parse_arg(entry, &argv_iter, pargv, BOOL_TRUE);
  472. // Parsing problems
  473. if ((pstatus != KPARSE_INPROGRESS) || (argv_iter != NULL)) {
  474. kexec_free(exec);
  475. faux_argv_free(argv);
  476. kpargv_free(pargv);
  477. return NULL;
  478. }
  479. context = kcontext_new(KCONTEXT_PLUGIN_ACTION);
  480. assert(context);
  481. kcontext_set_pargv(context, pargv);
  482. kexec_add_contexts(exec, context);
  483. faux_argv_free(argv);
  484. return exec;
  485. }