load.c 23 KB

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