load.c 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985
  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_LOG != parent_tag) &&
  337. (KTAG_PTYPE != parent_tag)) {
  338. faux_error_sprintf(error,
  339. TAG": Tag \"%s\" can't contain ACTION tag",
  340. kxml_tag_name(parent_tag));
  341. kaction_free(action);
  342. goto err;
  343. }
  344. if (!kentry_add_actions(parent_entry, action)) {
  345. faux_error_sprintf(error,
  346. TAG": Can't add ACTION #%d to ENTRY \"%s\". "
  347. "Probably duplication",
  348. kentry_actions_len(parent_entry) + 1,
  349. kentry_name(parent_entry));
  350. kaction_free(action);
  351. goto err;
  352. }
  353. if (!process_children(element, action, error))
  354. goto err;
  355. res = BOOL_TRUE;
  356. err:
  357. kxml_node_attr_free(iaction.sym);
  358. kxml_node_attr_free(iaction.lock);
  359. kxml_node_attr_free(iaction.interrupt);
  360. kxml_node_attr_free(iaction.in);
  361. kxml_node_attr_free(iaction.out);
  362. kxml_node_attr_free(iaction.exec_on);
  363. kxml_node_attr_free(iaction.update_retcode);
  364. kxml_node_attr_free(iaction.permanent);
  365. kxml_node_attr_free(iaction.sync);
  366. kxml_node_content_free(iaction.script);
  367. return res;
  368. }
  369. static kentry_t *add_entry_to_hierarchy(const kxml_node_t *element, void *parent,
  370. ientry_t *ientry, faux_error_t *error)
  371. {
  372. kentry_t *entry = NULL;
  373. ktags_e tag = kxml_node_tag(element);
  374. ktags_e parent_tag = kxml_node_tag(kxml_node_parent(element));
  375. assert(ientry);
  376. // Parent is mandatory field
  377. if (!parent) {
  378. faux_error_sprintf(error,
  379. TAG": Broken parent object for entry \"%s\"",
  380. ientry->name);
  381. return NULL;
  382. }
  383. if ((parent_tag == KTAG_ACTION) ||
  384. // (parent_tag == KTAG_HOTKEY) ||
  385. (parent_tag == KTAG_PLUGIN)) {
  386. faux_error_sprintf(error,
  387. TAG": Tag \"%s\" can't contain %s tag \"%s\"",
  388. kxml_tag_name(parent_tag),
  389. kxml_tag_name(tag), ientry->name);
  390. return NULL;
  391. }
  392. // High level ENTRY
  393. if (KTAG_KLISH == parent_tag) {
  394. kscheme_t *scheme = (kscheme_t *)parent;
  395. // Does such ENTRY already exist
  396. entry = kscheme_find_entry(scheme, ientry->name);
  397. if (entry) {
  398. if (!ientry_parse(ientry, entry, error))
  399. return NULL;
  400. } else { // New entry object
  401. entry = ientry_load(ientry, error);
  402. if (!entry)
  403. return NULL;
  404. if (!kscheme_add_entrys(scheme, entry)) {
  405. faux_error_sprintf(error, TAG": Can't add entry \"%s\". "
  406. "Probably duplication",
  407. kentry_name(entry));
  408. kentry_free(entry);
  409. return NULL;
  410. }
  411. }
  412. // ENTRY within ENTRY
  413. } else {
  414. kentry_t *parent_entry = (kentry_t *)parent;
  415. // Does such ENTRY already exist
  416. entry = kentry_find_entry(parent_entry, ientry->name);
  417. if (entry) {
  418. if (!ientry_parse(ientry, entry, error))
  419. return NULL;
  420. } else { // New entry object
  421. entry = ientry_load(ientry, error);
  422. if (!entry)
  423. return NULL;
  424. kentry_set_parent(entry, parent_entry);
  425. if (!kentry_add_entrys(parent_entry, entry)) {
  426. faux_error_sprintf(error, TAG": Can't add entry \"%s\". "
  427. "Probably duplication",
  428. kentry_name(entry));
  429. kentry_free(entry);
  430. return NULL;
  431. }
  432. }
  433. }
  434. return entry;
  435. }
  436. static bool_t process_entry(const kxml_node_t *element, void *parent,
  437. faux_error_t *error)
  438. {
  439. ientry_t ientry = {};
  440. kentry_t *entry = NULL;
  441. bool_t res = BOOL_FALSE;
  442. // Mandatory entry name
  443. ientry.name = kxml_node_attr(element, "name");
  444. if (!ientry.name) {
  445. faux_error_sprintf(error, TAG": entry without name");
  446. return BOOL_FALSE;
  447. }
  448. ientry.help = kxml_node_attr(element, "help");
  449. ientry.container = kxml_node_attr(element, "container");
  450. ientry.mode = kxml_node_attr(element, "mode");
  451. ientry.purpose = kxml_node_attr(element, "purpose");
  452. ientry.min = kxml_node_attr(element, "min");
  453. ientry.max = kxml_node_attr(element, "max");
  454. ientry.ref = kxml_node_attr(element, "ref");
  455. ientry.value = kxml_node_attr(element, "value");
  456. ientry.restore = kxml_node_attr(element, "restore");
  457. ientry.order = kxml_node_attr(element, "order");
  458. ientry.filter = kxml_node_attr(element, "filter");
  459. if (!(entry = add_entry_to_hierarchy(element, parent, &ientry, error)))
  460. goto err;
  461. if (!process_children(element, entry, error))
  462. goto err;
  463. res = BOOL_TRUE;
  464. err:
  465. kxml_node_attr_free(ientry.name);
  466. kxml_node_attr_free(ientry.help);
  467. kxml_node_attr_free(ientry.container);
  468. kxml_node_attr_free(ientry.mode);
  469. kxml_node_attr_free(ientry.purpose);
  470. kxml_node_attr_free(ientry.min);
  471. kxml_node_attr_free(ientry.max);
  472. kxml_node_attr_free(ientry.ref);
  473. kxml_node_attr_free(ientry.value);
  474. kxml_node_attr_free(ientry.restore);
  475. kxml_node_attr_free(ientry.order);
  476. kxml_node_attr_free(ientry.filter);
  477. return res;
  478. }
  479. static bool_t process_view(const kxml_node_t *element, void *parent,
  480. faux_error_t *error)
  481. {
  482. ientry_t ientry = {};
  483. kentry_t *entry = NULL;
  484. bool_t res = BOOL_FALSE;
  485. // Mandatory VIEW name
  486. ientry.name = kxml_node_attr(element, "name");
  487. if (!ientry.name) {
  488. faux_error_sprintf(error, TAG": VIEW without name");
  489. return BOOL_FALSE;
  490. }
  491. ientry.help = kxml_node_attr(element, "help");
  492. ientry.container = "true";
  493. ientry.mode = "switch";
  494. ientry.purpose = "common";
  495. ientry.min = "1";
  496. ientry.max = "1";
  497. ientry.ref = kxml_node_attr(element, "ref");
  498. ientry.value = NULL;
  499. ientry.restore = "false";
  500. ientry.order = "false";
  501. ientry.filter = "false";
  502. if (!(entry = add_entry_to_hierarchy(element, parent, &ientry, error)))
  503. goto err;
  504. if (!process_children(element, entry, error))
  505. goto err;
  506. res = BOOL_TRUE;
  507. err:
  508. kxml_node_attr_free(ientry.name);
  509. kxml_node_attr_free(ientry.help);
  510. kxml_node_attr_free(ientry.ref);
  511. return res;
  512. }
  513. static bool_t process_ptype(const kxml_node_t *element, void *parent,
  514. faux_error_t *error)
  515. {
  516. ientry_t ientry = {};
  517. kentry_t *entry = NULL;
  518. bool_t res = BOOL_FALSE;
  519. bool_t is_name = BOOL_FALSE;
  520. // Mandatory PTYPE name or reference
  521. ientry.name = kxml_node_attr(element, "name");
  522. ientry.ref = kxml_node_attr(element, "ref");
  523. if (ientry.name) {
  524. is_name = BOOL_TRUE;
  525. } else {
  526. if (!ientry.ref) {
  527. faux_error_sprintf(error,
  528. TAG": PTYPE without name or reference");
  529. return BOOL_FALSE;
  530. }
  531. ientry.name = "__ptype";
  532. }
  533. ientry.help = kxml_node_attr(element, "help");
  534. ientry.container = "true";
  535. ientry.mode = "sequence";
  536. ientry.purpose = "ptype";
  537. ientry.min = "1";
  538. ientry.max = "1";
  539. ientry.value = kxml_node_attr(element, "value");
  540. ientry.restore = "false";
  541. ientry.order = "true";
  542. ientry.filter = "false";
  543. if (!(entry = add_entry_to_hierarchy(element, parent, &ientry, error)))
  544. goto err;
  545. if (!process_children(element, entry, error))
  546. goto err;
  547. res = BOOL_TRUE;
  548. err:
  549. if (is_name)
  550. kxml_node_attr_free(ientry.name);
  551. kxml_node_attr_free(ientry.help);
  552. kxml_node_attr_free(ientry.ref);
  553. kxml_node_attr_free(ientry.value);
  554. return res;
  555. }
  556. static kentry_t *create_ptype(const char *ptype,
  557. const char *compl, const char *help, const char *ref)
  558. {
  559. kentry_t *ptype_entry = NULL;
  560. if (!ptype && !ref)
  561. return NULL;
  562. ptype_entry = kentry_new("__ptype");
  563. assert(ptype_entry);
  564. kentry_set_purpose(ptype_entry, KENTRY_PURPOSE_PTYPE);
  565. if (ref) {
  566. kentry_set_ref_str(ptype_entry, ref);
  567. // If ptype is reference then another actions is not needed.
  568. return ptype_entry;
  569. } else {
  570. kaction_t *ptype_action = kaction_new();
  571. assert(ptype_action);
  572. kaction_set_sym_ref(ptype_action, ptype);
  573. kaction_set_permanent(ptype_action, TRI_TRUE);
  574. kentry_add_actions(ptype_entry, ptype_action);
  575. }
  576. if (compl) {
  577. kentry_t *compl_entry = NULL;
  578. kaction_t *compl_action = NULL;
  579. compl_action = kaction_new();
  580. assert(compl_action);
  581. kaction_set_sym_ref(compl_action, compl);
  582. kaction_set_permanent(compl_action, TRI_TRUE);
  583. compl_entry = kentry_new("__compl");
  584. assert(compl_entry);
  585. kentry_set_purpose(compl_entry, KENTRY_PURPOSE_COMPLETION);
  586. kentry_add_actions(compl_entry, compl_action);
  587. kentry_add_entrys(ptype_entry, compl_entry);
  588. }
  589. if (help) {
  590. kentry_t *help_entry = NULL;
  591. kaction_t *help_action = NULL;
  592. help_action = kaction_new();
  593. assert(help_action);
  594. kaction_set_sym_ref(help_action, help);
  595. kaction_set_permanent(help_action, TRI_TRUE);
  596. help_entry = kentry_new("__help");
  597. assert(help_entry);
  598. kentry_set_purpose(help_entry, KENTRY_PURPOSE_HELP);
  599. kentry_add_actions(help_entry, help_action);
  600. kentry_add_entrys(ptype_entry, help_entry);
  601. }
  602. return ptype_entry;
  603. }
  604. // PARAM, SWITCH, SEQ
  605. static bool_t process_param(const kxml_node_t *element, void *parent,
  606. faux_error_t *error)
  607. {
  608. ientry_t ientry = {};
  609. kentry_t *entry = NULL;
  610. bool_t res = BOOL_FALSE;
  611. ktags_e tag = kxml_node_tag(element);
  612. bool_t is_mode = BOOL_FALSE;
  613. char *ptype_str = NULL;
  614. // Mandatory PARAM name
  615. ientry.name = kxml_node_attr(element, "name");
  616. if (!ientry.name) {
  617. faux_error_sprintf(error, TAG": PARAM without name");
  618. return BOOL_FALSE;
  619. }
  620. ientry.help = kxml_node_attr(element, "help");
  621. // Container
  622. if (KTAG_PARAM == tag)
  623. ientry.container = "false";
  624. else
  625. ientry.container = "true"; // SWITCH, SEQ
  626. // Mode
  627. switch (tag) {
  628. case KTAG_PARAM:
  629. ientry.mode = kxml_node_attr(element, "mode");
  630. is_mode = BOOL_TRUE;
  631. break;
  632. case KTAG_SWITCH:
  633. ientry.mode = "switch";
  634. break;
  635. case KTAG_SEQ:
  636. ientry.mode = "sequence";
  637. break;
  638. default:
  639. ientry.mode = "empty";
  640. break;
  641. }
  642. ientry.purpose = "common";
  643. ientry.min = kxml_node_attr(element, "min");
  644. ientry.max = kxml_node_attr(element, "max");
  645. ientry.ref = kxml_node_attr(element, "ref");
  646. ientry.value = kxml_node_attr(element, "value");
  647. ientry.restore = "false";
  648. ientry.order = kxml_node_attr(element, "order");
  649. ientry.filter = "false";
  650. if (!(entry = add_entry_to_hierarchy(element, parent, &ientry, error)))
  651. goto err;
  652. // Special attribute "ptype". It exists for more simple XML only. It
  653. // just links existing PTYPE. User can to don't specify nested tag PTYPE.
  654. ptype_str = kxml_node_attr(element, "ptype");
  655. if (ptype_str) {
  656. kentry_t *ptype_entry = create_ptype(NULL, NULL, NULL, ptype_str);
  657. assert(ptype_entry);
  658. kentry_add_entrys(entry, ptype_entry);
  659. }
  660. if (!process_children(element, entry, error))
  661. goto err;
  662. res = BOOL_TRUE;
  663. err:
  664. kxml_node_attr_free(ientry.name);
  665. kxml_node_attr_free(ientry.help);
  666. if (is_mode)
  667. kxml_node_attr_free(ientry.mode);
  668. kxml_node_attr_free(ientry.min);
  669. kxml_node_attr_free(ientry.max);
  670. kxml_node_attr_free(ientry.ref);
  671. kxml_node_attr_free(ientry.value);
  672. kxml_node_attr_free(ientry.order);
  673. kxml_node_attr_free(ptype_str);
  674. return res;
  675. }
  676. // COMMAND, FILTER, COND, COMPL, HELP, PROMPT, LOG
  677. static bool_t process_command(const kxml_node_t *element, void *parent,
  678. faux_error_t *error)
  679. {
  680. ientry_t ientry = {};
  681. kentry_t *entry = NULL;
  682. bool_t res = BOOL_FALSE;
  683. ktags_e tag = kxml_node_tag(element);
  684. bool_t is_name = BOOL_FALSE;
  685. bool_t is_filter = BOOL_FALSE;
  686. kentry_entrys_node_t *iter = NULL;
  687. kentry_t *nested_entry = NULL;
  688. bool_t ptype_exists = BOOL_FALSE;
  689. // Mandatory COMMAND name
  690. ientry.name = kxml_node_attr(element, "name");
  691. if (ientry.name) {
  692. is_name = BOOL_TRUE;
  693. } else {
  694. switch (tag) {
  695. case KTAG_COMMAND:
  696. case KTAG_FILTER:
  697. faux_error_sprintf(error, TAG": COMMAND without name");
  698. return BOOL_FALSE;
  699. case KTAG_COND:
  700. ientry.name = "__cond";
  701. break;
  702. case KTAG_COMPL:
  703. ientry.name = "__compl";
  704. break;
  705. case KTAG_HELP:
  706. ientry.name = "__help";
  707. break;
  708. case KTAG_PROMPT:
  709. ientry.name = "__prompt";
  710. break;
  711. case KTAG_LOG:
  712. ientry.name = "__log";
  713. break;
  714. default:
  715. faux_error_sprintf(error, TAG": Unknown tag");
  716. return BOOL_FALSE;
  717. }
  718. }
  719. ientry.help = kxml_node_attr(element, "help");
  720. ientry.container = "false";
  721. ientry.mode = kxml_node_attr(element, "mode");
  722. // Purpose
  723. switch (tag) {
  724. case KTAG_COND:
  725. ientry.purpose = "cond";
  726. break;
  727. case KTAG_COMPL:
  728. ientry.purpose = "completion";
  729. break;
  730. case KTAG_HELP:
  731. ientry.purpose = "help";
  732. break;
  733. case KTAG_PROMPT:
  734. ientry.purpose = "prompt";
  735. break;
  736. case KTAG_LOG:
  737. ientry.purpose = "log";
  738. break;
  739. default:
  740. ientry.purpose = "common";
  741. break;
  742. }
  743. ientry.min = kxml_node_attr(element, "min");
  744. ientry.max = kxml_node_attr(element, "max");
  745. ientry.ref = kxml_node_attr(element, "ref");
  746. if ((KTAG_FILTER == tag) || (KTAG_COMMAND == tag)) {
  747. ientry.value = kxml_node_attr(element, "value");
  748. ientry.restore = kxml_node_attr(element, "restore");
  749. } else {
  750. ientry.value = NULL;
  751. ientry.restore = "false";
  752. }
  753. ientry.order = "false";
  754. // Filter
  755. ientry.filter = kxml_node_attr(element, "filter");
  756. if (ientry.filter) {
  757. is_filter = BOOL_TRUE;
  758. } else {
  759. if (KTAG_FILTER == tag)
  760. ientry.filter = "true";
  761. else
  762. ientry.filter = "false";
  763. }
  764. if (!(entry = add_entry_to_hierarchy(element, parent, &ientry, error)))
  765. goto err;
  766. if (!process_children(element, entry, error))
  767. goto err;
  768. // Add special PTYPE for command. It uses symbol from internal klish
  769. // plugin.
  770. // Iterate child entries to find out is there PTYPE entry already. We
  771. // can't use kentry_nested_by_purpose() because it's not calculated yet.
  772. iter = kentry_entrys_iter(entry);
  773. while ((nested_entry = kentry_entrys_each(&iter))) {
  774. if (kentry_purpose(nested_entry) == KENTRY_PURPOSE_PTYPE) {
  775. ptype_exists = BOOL_TRUE;
  776. break;
  777. }
  778. }
  779. if (!ptype_exists) {
  780. kentry_t *ptype_entry = create_ptype(
  781. "COMMAND@klish",
  782. "completion_COMMAND@klish",
  783. "help_COMMAND@klish",
  784. NULL);
  785. assert(ptype_entry);
  786. kentry_add_entrys(entry, ptype_entry);
  787. }
  788. res = BOOL_TRUE;
  789. err:
  790. if (is_name)
  791. kxml_node_attr_free(ientry.name);
  792. kxml_node_attr_free(ientry.help);
  793. kxml_node_attr_free(ientry.mode);
  794. kxml_node_attr_free(ientry.min);
  795. kxml_node_attr_free(ientry.max);
  796. kxml_node_attr_free(ientry.ref);
  797. if ((KTAG_FILTER == tag) || (KTAG_COMMAND == tag)) {
  798. kxml_node_attr_free(ientry.value);
  799. kxml_node_attr_free(ientry.restore);
  800. }
  801. if (is_filter)
  802. kxml_node_attr_free(ientry.filter);
  803. return res;
  804. }
  805. static bool_t process_hotkey(const kxml_node_t *element, void *parent,
  806. faux_error_t *error)
  807. {
  808. ihotkey_t ihotkey = {};
  809. khotkey_t *hotkey = NULL;
  810. bool_t res = BOOL_FALSE;
  811. ktags_e parent_tag = kxml_node_tag(kxml_node_parent(element));
  812. kentry_t *parent_entry = (kentry_t *)parent;
  813. ihotkey.key = kxml_node_attr(element, "key");
  814. if (!ihotkey.key) {
  815. faux_error_sprintf(error, TAG": hotkey without \"key\" attribute");
  816. return BOOL_FALSE;
  817. }
  818. ihotkey.cmd = kxml_node_attr(element, "cmd");
  819. if (!ihotkey.cmd) {
  820. faux_error_sprintf(error, TAG": hotkey without \"cmd\" attribute");
  821. return BOOL_FALSE;
  822. }
  823. hotkey = ihotkey_load(&ihotkey, error);
  824. if (!hotkey)
  825. goto err;
  826. if ( (KTAG_ENTRY != parent_tag) &&
  827. (KTAG_VIEW != parent_tag)) {
  828. faux_error_sprintf(error,
  829. TAG": Tag \"%s\" can't contain HOTKEY tag",
  830. kxml_tag_name(parent_tag));
  831. khotkey_free(hotkey);
  832. goto err;
  833. }
  834. if (!kentry_add_hotkeys(parent_entry, hotkey)) {
  835. faux_error_sprintf(error,
  836. TAG": Can't add HOTKEY \"%s\" to ENTRY \"%s\". "
  837. "Probably duplication",
  838. khotkey_key(hotkey),
  839. kentry_name(parent_entry));
  840. khotkey_free(hotkey);
  841. goto err;
  842. }
  843. // HOTKEY doesn't have children
  844. res = BOOL_TRUE;
  845. err:
  846. kxml_node_attr_free(ihotkey.key);
  847. kxml_node_attr_free(ihotkey.cmd);
  848. return res;
  849. }