load.c 22 KB

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