load.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836
  1. /** @file load.c
  2. * @brief Common part for XML parsing.
  3. *
  4. * Different XML parsing engines can provide a functions in a form of
  5. * standardized API. This code uses this API and parses XML to kscheme.
  6. */
  7. #include <stdlib.h>
  8. #include <string.h>
  9. #include <assert.h>
  10. #include <errno.h>
  11. #include <sys/types.h>
  12. #include <dirent.h>
  13. #include <faux/faux.h>
  14. #include <faux/str.h>
  15. #include <faux/error.h>
  16. #include <klish/kscheme.h>
  17. #include <klish/ischeme.h>
  18. #include <klish/kxml.h>
  19. #define TAG "XML"
  20. typedef bool_t (kxml_process_fn)(const kxml_node_t *element,
  21. void *parent, faux_error_t *error);
  22. static kxml_process_fn
  23. process_action,
  24. process_param,
  25. process_command,
  26. process_view,
  27. process_ptype,
  28. process_plugin,
  29. process_klish,
  30. process_entry;
  31. // Different TAGs types
  32. typedef enum {
  33. KTAG_NONE,
  34. KTAG_ACTION,
  35. KTAG_PARAM,
  36. KTAG_SWITCH, // PARAM alias
  37. KTAG_SEQ, // PARAM alias
  38. KTAG_COMMAND,
  39. KTAG_FILTER,
  40. KTAG_VIEW,
  41. KTAG_PTYPE,
  42. KTAG_PLUGIN,
  43. KTAG_KLISH,
  44. KTAG_ENTRY,
  45. KTAG_COND,
  46. KTAG_COMPL,
  47. KTAG_HELP,
  48. KTAG_PROMPT,
  49. KTAG_MAX,
  50. } ktags_e;
  51. static const char * const kxml_tags[] = {
  52. NULL,
  53. "ACTION",
  54. "PARAM",
  55. "SWITCH",
  56. "SEQ",
  57. "COMMAND",
  58. "FILTER",
  59. "VIEW",
  60. "PTYPE",
  61. "PLUGIN",
  62. "KLISH",
  63. "ENTRY",
  64. "COND",
  65. "COMPL",
  66. "HELP",
  67. "PROMPT",
  68. };
  69. static kxml_process_fn *kxml_handlers[] = {
  70. NULL,
  71. process_action,
  72. process_param,
  73. process_param,
  74. process_param,
  75. process_command,
  76. process_command,
  77. process_view,
  78. process_ptype,
  79. process_plugin,
  80. process_klish,
  81. process_entry,
  82. process_command,
  83. process_command,
  84. process_command,
  85. process_command,
  86. };
  87. static const char *kxml_tag_name(ktags_e tag)
  88. {
  89. if ((KTAG_NONE == tag) || (tag >= KTAG_MAX))
  90. return "NONE";
  91. return kxml_tags[tag];
  92. }
  93. static ktags_e kxml_node_tag(const kxml_node_t *node)
  94. {
  95. ktags_e tag = KTAG_NONE;
  96. char *name = NULL;
  97. if (!node)
  98. return KTAG_NONE;
  99. if (kxml_node_type(node) != KXML_NODE_ELM)
  100. return KTAG_NONE;
  101. name = kxml_node_name(node);
  102. if (!name)
  103. return KTAG_NONE; // Strange case
  104. for (tag = (KTAG_NONE + 1); tag < KTAG_MAX; tag++) {
  105. if (faux_str_casecmp(name, kxml_tags[tag]) == 0)
  106. break;
  107. }
  108. kxml_node_name_free(name);
  109. if (tag >= KTAG_MAX)
  110. return KTAG_NONE;
  111. return tag;
  112. }
  113. static kxml_process_fn *kxml_node_handler(const kxml_node_t *node)
  114. {
  115. return kxml_handlers[kxml_node_tag(node)];
  116. }
  117. /** @brief Reads an element from the XML stream and processes it.
  118. */
  119. static bool_t process_node(const kxml_node_t *node, void *parent, faux_error_t *error)
  120. {
  121. kxml_process_fn *handler = NULL;
  122. // Process only KXML_NODE_ELM. Don't process other types like:
  123. // KXML_NODE_DOC,
  124. // KXML_NODE_TEXT,
  125. // KXML_NODE_ATTR,
  126. // KXML_NODE_COMMENT,
  127. // KXML_NODE_PI,
  128. // KXML_NODE_DECL,
  129. // KXML_NODE_UNKNOWN,
  130. if (kxml_node_type(node) != KXML_NODE_ELM)
  131. return BOOL_TRUE;
  132. handler = kxml_node_handler(node);
  133. if (!handler) { // Unknown element
  134. faux_error_sprintf(error,
  135. TAG": Unknown tag \"%s\"", kxml_node_name(node));
  136. return BOOL_FALSE;
  137. }
  138. #ifdef KXML_DEBUG
  139. printf("kxml: Tag \"%s\"\n", kxml_node_name(node));
  140. #endif
  141. return handler(node, parent, error);
  142. }
  143. static bool_t kxml_load_file(kscheme_t *scheme, const char *filename,
  144. faux_error_t *error)
  145. {
  146. kxml_doc_t *doc = NULL;
  147. kxml_node_t *root = NULL;
  148. bool_t r = BOOL_FALSE;
  149. if (!scheme)
  150. return BOOL_FALSE;
  151. if (!filename)
  152. return BOOL_FALSE;
  153. #ifdef KXML_DEBUG
  154. printf("kxml: Processing XML file \"%s\"\n", filename);
  155. #endif
  156. doc = kxml_doc_read(filename);
  157. if (!kxml_doc_is_valid(doc)) {
  158. /* int errcaps = kxml_doc_error_caps(doc);
  159. printf("Unable to open file '%s'", filename);
  160. if ((errcaps & kxml_ERR_LINE) == kxml_ERR_LINE)
  161. printf(", at line %d", kxml_doc_err_line(doc));
  162. if ((errcaps & kxml_ERR_COL) == kxml_ERR_COL)
  163. printf(", at column %d", kxml_doc_err_col(doc));
  164. if ((errcaps & kxml_ERR_DESC) == kxml_ERR_DESC)
  165. printf(", message is %s", kxml_doc_err_msg(doc));
  166. printf("\n");
  167. */ kxml_doc_release(doc);
  168. return BOOL_FALSE;
  169. }
  170. root = kxml_doc_root(doc);
  171. r = process_node(root, scheme, error);
  172. kxml_doc_release(doc);
  173. if (!r) {
  174. faux_error_sprintf(error, TAG": Illegal file %s", filename);
  175. return BOOL_FALSE;
  176. }
  177. return BOOL_TRUE;
  178. }
  179. /** @brief Default path to get XML files from.
  180. */
  181. static const char *default_path = "/etc/klish;~/.klish";
  182. static const char *path_separators = ":;";
  183. bool_t kxml_load_scheme(kscheme_t *scheme, const char *xml_path,
  184. faux_error_t *error)
  185. {
  186. char *path = NULL;
  187. char *fn = NULL;
  188. char *saveptr = NULL;
  189. bool_t ret = BOOL_TRUE;
  190. assert(scheme);
  191. if (!scheme)
  192. return BOOL_FALSE;
  193. // Use the default path if xml path is not specified.
  194. // Dup is needed because sring will be tokenized but
  195. // the xml_path is must be const.
  196. if (!xml_path)
  197. path = faux_str_dup(default_path);
  198. else
  199. path = faux_str_dup(xml_path);
  200. #ifdef KXML_DEBUG
  201. printf("kxml: Loading scheme \"%s\"\n", path);
  202. #endif
  203. // Loop through each directory
  204. for (fn = strtok_r(path, path_separators, &saveptr);
  205. fn; fn = strtok_r(NULL, path_separators, &saveptr)) {
  206. DIR *dir = NULL;
  207. struct dirent *entry = NULL;
  208. char *realpath = NULL;
  209. // Expand tilde. Tilde must be the first symbol.
  210. realpath = faux_expand_tilde(fn);
  211. // Regular file
  212. if (faux_isfile(realpath)) {
  213. if (!kxml_load_file(scheme, realpath, error))
  214. ret = BOOL_FALSE;
  215. faux_str_free(realpath);
  216. continue;
  217. }
  218. // Search this directory for any XML files
  219. #ifdef KXML_DEBUG
  220. printf("kxml: Processing XML dir \"%s\"\n", realpath);
  221. #endif
  222. dir = opendir(realpath);
  223. if (!dir) {
  224. faux_str_free(realpath);
  225. continue;
  226. }
  227. for (entry = readdir(dir); entry; entry = readdir(dir)) {
  228. const char *extension = strrchr(entry->d_name, '.');
  229. char *filename = NULL;
  230. // Check the filename
  231. if (!extension || strcmp(".xml", extension))
  232. continue;
  233. filename = faux_str_sprintf("%s/%s", realpath, entry->d_name);
  234. if (!kxml_load_file(scheme, filename, error))
  235. ret = BOOL_FALSE;
  236. faux_str_free(filename);
  237. }
  238. closedir(dir);
  239. faux_str_free(realpath);
  240. }
  241. faux_str_free(path);
  242. return ret;
  243. }
  244. /** @brief Iterate through element's children.
  245. */
  246. static bool_t process_children(const kxml_node_t *element, void *parent,
  247. faux_error_t *error)
  248. {
  249. const kxml_node_t *node = NULL;
  250. while ((node = kxml_node_next_child(element, node)) != NULL) {
  251. bool_t res = BOOL_FALSE;
  252. res = process_node(node, parent, error);
  253. if (!res)
  254. return res;
  255. }
  256. return BOOL_TRUE;
  257. }
  258. static bool_t process_klish(const kxml_node_t *element, void *parent,
  259. faux_error_t *error)
  260. {
  261. return process_children(element, parent, error);
  262. }
  263. static bool_t process_plugin(const kxml_node_t *element, void *parent,
  264. faux_error_t *error)
  265. {
  266. iplugin_t iplugin = {};
  267. kplugin_t *plugin = NULL;
  268. bool_t res = BOOL_FALSE;
  269. ktags_e parent_tag = kxml_node_tag(kxml_node_parent(element));
  270. if (parent_tag != KTAG_KLISH) {
  271. faux_error_sprintf(error,
  272. TAG": Tag \"%s\" can't contain PLUGIN tag",
  273. kxml_tag_name(parent_tag));
  274. return BOOL_FALSE;
  275. }
  276. iplugin.name = kxml_node_attr(element, "name");
  277. iplugin.id = kxml_node_attr(element, "id");
  278. iplugin.file = kxml_node_attr(element, "file");
  279. iplugin.conf = kxml_node_content(element);
  280. plugin = iplugin_load(&iplugin, error);
  281. if (!plugin)
  282. goto err;
  283. if (!kscheme_add_plugins((kscheme_t *)parent, plugin)) {
  284. faux_error_sprintf(error, TAG": Can't add PLUGIN \"%s\". "
  285. "Probably duplication",
  286. kplugin_name(plugin));
  287. kplugin_free(plugin);
  288. goto err;
  289. }
  290. if (!process_children(element, plugin, error))
  291. goto err;
  292. res = BOOL_TRUE;
  293. err:
  294. kxml_node_attr_free(iplugin.name);
  295. kxml_node_attr_free(iplugin.id);
  296. kxml_node_attr_free(iplugin.file);
  297. kxml_node_content_free(iplugin.conf);
  298. return res;
  299. }
  300. static bool_t process_action(const kxml_node_t *element, void *parent,
  301. faux_error_t *error)
  302. {
  303. iaction_t iaction = {};
  304. kaction_t *action = NULL;
  305. bool_t res = BOOL_FALSE;
  306. ktags_e parent_tag = kxml_node_tag(kxml_node_parent(element));
  307. kentry_t *parent_entry = (kentry_t *)parent;
  308. iaction.sym = kxml_node_attr(element, "sym");
  309. iaction.lock = kxml_node_attr(element, "lock");
  310. iaction.interrupt = kxml_node_attr(element, "interrupt");
  311. iaction.interactive = kxml_node_attr(element, "interactive");
  312. iaction.exec_on = kxml_node_attr(element, "exec_on");
  313. iaction.update_retcode = kxml_node_attr(element, "update_retcode");
  314. iaction.permanent = kxml_node_attr(element, "permanent");
  315. iaction.sync = kxml_node_attr(element, "sync");
  316. iaction.script = kxml_node_content(element);
  317. action = iaction_load(&iaction, error);
  318. if (!action)
  319. goto err;
  320. if ( (KTAG_ENTRY != parent_tag) &&
  321. (KTAG_COMMAND != parent_tag) &&
  322. (KTAG_FILTER != parent_tag) &&
  323. (KTAG_COND != parent_tag) &&
  324. (KTAG_COMPL != parent_tag) &&
  325. (KTAG_HELP != parent_tag) &&
  326. (KTAG_PROMPT != parent_tag) &&
  327. (KTAG_PTYPE != parent_tag)) {
  328. faux_error_sprintf(error,
  329. TAG": Tag \"%s\" can't contain ACTION tag",
  330. kxml_tag_name(parent_tag));
  331. kaction_free(action);
  332. goto err;
  333. }
  334. if (!kentry_add_actions(parent_entry, action)) {
  335. faux_error_sprintf(error,
  336. TAG": Can't add ACTION #%d to ENTRY \"%s\". "
  337. "Probably duplication",
  338. kentry_actions_len(parent_entry) + 1,
  339. kentry_name(parent_entry));
  340. kaction_free(action);
  341. goto err;
  342. }
  343. if (!process_children(element, action, error))
  344. goto err;
  345. res = BOOL_TRUE;
  346. err:
  347. kxml_node_attr_free(iaction.sym);
  348. kxml_node_attr_free(iaction.lock);
  349. kxml_node_attr_free(iaction.interrupt);
  350. kxml_node_attr_free(iaction.interactive);
  351. kxml_node_attr_free(iaction.exec_on);
  352. kxml_node_attr_free(iaction.update_retcode);
  353. kxml_node_attr_free(iaction.permanent);
  354. kxml_node_attr_free(iaction.sync);
  355. kxml_node_content_free(iaction.script);
  356. return res;
  357. }
  358. static kentry_t *add_entry_to_hierarchy(const kxml_node_t *element, void *parent,
  359. ientry_t *ientry, faux_error_t *error)
  360. {
  361. kentry_t *entry = NULL;
  362. ktags_e tag = kxml_node_tag(element);
  363. ktags_e parent_tag = kxml_node_tag(kxml_node_parent(element));
  364. assert(ientry);
  365. // Parent is mandatory field
  366. if (!parent) {
  367. faux_error_sprintf(error,
  368. TAG": Broken parent object for entry \"%s\"",
  369. ientry->name);
  370. return NULL;
  371. }
  372. if ((parent_tag == KTAG_ACTION) ||
  373. // (parent_tag == KTAG_HOTKEY) ||
  374. (parent_tag == KTAG_PLUGIN)) {
  375. faux_error_sprintf(error,
  376. TAG": Tag \"%s\" can't contain %s tag \"%s\"",
  377. kxml_tag_name(parent_tag),
  378. kxml_tag_name(tag), ientry->name);
  379. return NULL;
  380. }
  381. // High level ENTRY
  382. if (KTAG_KLISH == parent_tag) {
  383. kscheme_t *scheme = (kscheme_t *)parent;
  384. // Does such ENTRY already exist
  385. entry = kscheme_find_entry(scheme, ientry->name);
  386. if (entry) {
  387. if (!ientry_parse(ientry, entry, error))
  388. return NULL;
  389. } else { // New entry object
  390. entry = ientry_load(ientry, error);
  391. if (!entry)
  392. return NULL;
  393. if (!kscheme_add_entrys(scheme, entry)) {
  394. faux_error_sprintf(error, TAG": Can't add entry \"%s\". "
  395. "Probably duplication",
  396. kentry_name(entry));
  397. kentry_free(entry);
  398. return NULL;
  399. }
  400. }
  401. // ENTRY within ENTRY
  402. } else {
  403. kentry_t *parent_entry = (kentry_t *)parent;
  404. // Does such ENTRY already exist
  405. entry = kentry_find_entry(parent_entry, ientry->name);
  406. if (entry) {
  407. if (!ientry_parse(ientry, entry, error))
  408. return NULL;
  409. } else { // New entry object
  410. entry = ientry_load(ientry, error);
  411. if (!entry)
  412. return NULL;
  413. kentry_set_parent(entry, parent_entry);
  414. if (!kentry_add_entrys(parent_entry, entry)) {
  415. faux_error_sprintf(error, TAG": Can't add entry \"%s\". "
  416. "Probably duplication",
  417. kentry_name(entry));
  418. kentry_free(entry);
  419. return NULL;
  420. }
  421. }
  422. }
  423. return entry;
  424. }
  425. static bool_t process_entry(const kxml_node_t *element, void *parent,
  426. faux_error_t *error)
  427. {
  428. ientry_t ientry = {};
  429. kentry_t *entry = NULL;
  430. bool_t res = BOOL_FALSE;
  431. // Mandatory entry name
  432. ientry.name = kxml_node_attr(element, "name");
  433. if (!ientry.name) {
  434. faux_error_sprintf(error, TAG": entry without name");
  435. return BOOL_FALSE;
  436. }
  437. ientry.help = kxml_node_attr(element, "help");
  438. ientry.container = kxml_node_attr(element, "container");
  439. ientry.mode = kxml_node_attr(element, "mode");
  440. ientry.purpose = kxml_node_attr(element, "purpose");
  441. ientry.min = kxml_node_attr(element, "min");
  442. ientry.max = kxml_node_attr(element, "max");
  443. ientry.ref = kxml_node_attr(element, "ref");
  444. ientry.value = kxml_node_attr(element, "value");
  445. ientry.restore = kxml_node_attr(element, "restore");
  446. ientry.order = kxml_node_attr(element, "order");
  447. ientry.filter = kxml_node_attr(element, "filter");
  448. if (!(entry = add_entry_to_hierarchy(element, parent, &ientry, error)))
  449. goto err;
  450. if (!process_children(element, entry, error))
  451. goto err;
  452. res = BOOL_TRUE;
  453. err:
  454. kxml_node_attr_free(ientry.name);
  455. kxml_node_attr_free(ientry.help);
  456. kxml_node_attr_free(ientry.container);
  457. kxml_node_attr_free(ientry.mode);
  458. kxml_node_attr_free(ientry.purpose);
  459. kxml_node_attr_free(ientry.min);
  460. kxml_node_attr_free(ientry.max);
  461. kxml_node_attr_free(ientry.ref);
  462. kxml_node_attr_free(ientry.value);
  463. kxml_node_attr_free(ientry.restore);
  464. kxml_node_attr_free(ientry.order);
  465. kxml_node_attr_free(ientry.filter);
  466. return res;
  467. }
  468. static bool_t process_view(const kxml_node_t *element, void *parent,
  469. faux_error_t *error)
  470. {
  471. ientry_t ientry = {};
  472. kentry_t *entry = NULL;
  473. bool_t res = BOOL_FALSE;
  474. // Mandatory VIEW name
  475. ientry.name = kxml_node_attr(element, "name");
  476. if (!ientry.name) {
  477. faux_error_sprintf(error, TAG": VIEW without name");
  478. return BOOL_FALSE;
  479. }
  480. ientry.help = kxml_node_attr(element, "help");
  481. ientry.container = "true";
  482. ientry.mode = "switch";
  483. ientry.purpose = "common";
  484. ientry.min = "1";
  485. ientry.max = "1";
  486. ientry.ref = kxml_node_attr(element, "ref");
  487. ientry.value = NULL;
  488. ientry.restore = "false";
  489. ientry.order = "false";
  490. ientry.filter = "false";
  491. if (!(entry = add_entry_to_hierarchy(element, parent, &ientry, error)))
  492. goto err;
  493. if (!process_children(element, entry, error))
  494. goto err;
  495. res = BOOL_TRUE;
  496. err:
  497. kxml_node_attr_free(ientry.name);
  498. kxml_node_attr_free(ientry.help);
  499. kxml_node_attr_free(ientry.ref);
  500. return res;
  501. }
  502. static bool_t process_ptype(const kxml_node_t *element, void *parent,
  503. faux_error_t *error)
  504. {
  505. ientry_t ientry = {};
  506. kentry_t *entry = NULL;
  507. bool_t res = BOOL_FALSE;
  508. bool_t is_name = BOOL_FALSE;
  509. // Mandatory PTYPE name or reference
  510. ientry.name = kxml_node_attr(element, "name");
  511. ientry.ref = kxml_node_attr(element, "ref");
  512. if (ientry.name) {
  513. is_name = BOOL_TRUE;
  514. } else {
  515. if (!ientry.ref) {
  516. faux_error_sprintf(error,
  517. TAG": PTYPE without name or reference");
  518. return BOOL_FALSE;
  519. }
  520. ientry.name = "__ptype";
  521. }
  522. ientry.help = kxml_node_attr(element, "help");
  523. ientry.container = "true";
  524. ientry.mode = "sequence";
  525. ientry.purpose = "ptype";
  526. ientry.min = "1";
  527. ientry.max = "1";
  528. ientry.value = kxml_node_attr(element, "value");
  529. ientry.restore = "false";
  530. ientry.order = "true";
  531. ientry.filter = "false";
  532. if (!(entry = add_entry_to_hierarchy(element, parent, &ientry, error)))
  533. goto err;
  534. if (!process_children(element, entry, error))
  535. goto err;
  536. res = BOOL_TRUE;
  537. err:
  538. if (is_name)
  539. kxml_node_attr_free(ientry.name);
  540. kxml_node_attr_free(ientry.help);
  541. kxml_node_attr_free(ientry.ref);
  542. kxml_node_attr_free(ientry.value);
  543. return res;
  544. }
  545. // PARAM, SWITCH, SEQ
  546. static bool_t process_param(const kxml_node_t *element, void *parent,
  547. faux_error_t *error)
  548. {
  549. ientry_t ientry = {};
  550. kentry_t *entry = NULL;
  551. bool_t res = BOOL_FALSE;
  552. ktags_e parent_tag = kxml_node_tag(kxml_node_parent(element));
  553. kentry_t *parent_entry = (kentry_t *)parent;
  554. kentry_t *entry_add_to = parent_entry;
  555. // Mandatory PARAM name
  556. ientry.name = kxml_node_attr(element, "name");
  557. if (!ientry.name) {
  558. faux_error_sprintf(error, TAG": PARAM without name");
  559. return BOOL_FALSE;
  560. }
  561. ientry.help = kxml_node_attr(element, "help");
  562. ientry.container = kxml_node_attr(element, "container");
  563. ientry.mode = kxml_node_attr(element, "mode");
  564. ientry.purpose = "common";
  565. ientry.min = kxml_node_attr(element, "min");
  566. ientry.max = kxml_node_attr(element, "max");
  567. ientry.ref = kxml_node_attr(element, "ref");
  568. ientry.value = kxml_node_attr(element, "value");
  569. ientry.restore = "false";
  570. ientry.order = kxml_node_attr(element, "order");
  571. ientry.filter = "false";
  572. entry = ientry_load(&ientry, error);
  573. if (!entry)
  574. goto err;
  575. if ((KTAG_COMMAND != parent_tag) &&
  576. (KTAG_PARAM != parent_tag) &&
  577. (KTAG_ENTRY != parent_tag) &&
  578. (KTAG_SWITCH != parent_tag) &&
  579. (KTAG_SEQ != parent_tag) &&
  580. (KTAG_COND != parent_tag) &&
  581. (KTAG_COMPL != parent_tag) &&
  582. (KTAG_HELP != parent_tag) &&
  583. (KTAG_PTYPE != parent_tag)) {
  584. faux_error_sprintf(error,
  585. TAG": Tag \"%s\" can't contain PARAM tag",
  586. kxml_tag_name(parent_tag));
  587. kentry_free(entry);
  588. goto err;
  589. }
  590. // Add newly created entry to special container in 'sequence' mode if
  591. // parent entry can has 'switch' mode.
  592. if (kentry_mode(parent_entry) == KENTRY_MODE_SWITCH) {
  593. const char *seq_entry_name = "__sequence";
  594. kentry_t *seq_entry = kentry_find_entry(parent_entry, seq_entry_name);
  595. if (!seq_entry) {
  596. seq_entry = kentry_new(seq_entry_name);
  597. assert(seq_entry);
  598. kentry_set_container(seq_entry, BOOL_TRUE);
  599. kentry_set_mode(seq_entry, KENTRY_MODE_SEQUENCE);
  600. kentry_add_entrys(parent_entry, seq_entry);
  601. }
  602. entry_add_to = seq_entry;
  603. }
  604. if (!kentry_add_entrys(entry_add_to, entry)) {
  605. faux_error_sprintf(error,
  606. TAG": Can't add PARAM \"%s\" to ENTRY \"%s\". "
  607. "Probably duplication",
  608. kentry_name(entry_add_to), kentry_name(entry_add_to));
  609. kentry_free(entry);
  610. goto err;
  611. }
  612. if (!process_children(element, entry, error))
  613. goto err;
  614. res = BOOL_TRUE;
  615. err:
  616. kxml_node_attr_free(ientry.name);
  617. kxml_node_attr_free(ientry.help);
  618. kxml_node_attr_free(ientry.container);
  619. kxml_node_attr_free(ientry.mode);
  620. kxml_node_attr_free(ientry.min);
  621. kxml_node_attr_free(ientry.max);
  622. kxml_node_attr_free(ientry.ref);
  623. kxml_node_attr_free(ientry.value);
  624. kxml_node_attr_free(ientry.order);
  625. return res;
  626. }
  627. // COMMAND, FILTER, COND, COMPL, HELP, PROMPT
  628. static bool_t process_command(const kxml_node_t *element, void *parent,
  629. faux_error_t *error)
  630. {
  631. ientry_t ientry = {};
  632. kentry_t *entry = NULL;
  633. bool_t res = BOOL_FALSE;
  634. ktags_e tag = kxml_node_tag(element);
  635. bool_t is_name = BOOL_FALSE;
  636. // Mandatory COMMAND name
  637. ientry.name = kxml_node_attr(element, "name");
  638. if (ientry.name) {
  639. is_name = BOOL_TRUE;
  640. } else {
  641. switch (tag) {
  642. case KTAG_COMMAND:
  643. case KTAG_FILTER:
  644. faux_error_sprintf(error, TAG": COMMAND without name");
  645. return BOOL_FALSE;
  646. case KTAG_COND:
  647. ientry.name = "__cond";
  648. break;
  649. case KTAG_COMPL:
  650. ientry.name = "__compl";
  651. break;
  652. case KTAG_HELP:
  653. ientry.name = "__help";
  654. break;
  655. case KTAG_PROMPT:
  656. ientry.name = "__prompt";
  657. break;
  658. default:
  659. faux_error_sprintf(error, TAG": Unknown tag");
  660. return BOOL_FALSE;
  661. }
  662. }
  663. ientry.help = kxml_node_attr(element, "help");
  664. ientry.container = "false";
  665. ientry.mode = "sequence";
  666. // Purpose
  667. switch (tag) {
  668. case KTAG_COND:
  669. ientry.purpose = "cond";
  670. break;
  671. case KTAG_COMPL:
  672. ientry.purpose = "completion";
  673. break;
  674. case KTAG_HELP:
  675. ientry.purpose = "help";
  676. break;
  677. case KTAG_PROMPT:
  678. ientry.purpose = "prompt";
  679. break;
  680. default:
  681. ientry.purpose = "common";
  682. break;
  683. }
  684. ientry.min = "1";
  685. ientry.max = "1";
  686. ientry.ref = kxml_node_attr(element, "ref");
  687. if ((KTAG_FILTER == tag) || (KTAG_COMMAND == tag)) {
  688. ientry.value = kxml_node_attr(element, "value");
  689. ientry.restore = kxml_node_attr(element, "restore");
  690. } else {
  691. ientry.value = NULL;
  692. ientry.restore = "false";
  693. }
  694. ientry.order = "false";
  695. // Filter
  696. if (KTAG_FILTER == tag)
  697. ientry.filter = "true";
  698. else
  699. ientry.filter = "false";
  700. if (!(entry = add_entry_to_hierarchy(element, parent, &ientry, error)))
  701. goto err;
  702. if (!process_children(element, entry, error))
  703. goto err;
  704. res = BOOL_TRUE;
  705. err:
  706. if (is_name)
  707. kxml_node_attr_free(ientry.name);
  708. kxml_node_attr_free(ientry.help);
  709. kxml_node_attr_free(ientry.ref);
  710. if ((KTAG_FILTER == tag) || (KTAG_COMMAND == tag)) {
  711. kxml_node_attr_free(ientry.value);
  712. kxml_node_attr_free(ientry.restore);
  713. }
  714. return res;
  715. }