ksession_parse.c 16 KB

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