ientry.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487
  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include <assert.h>
  5. #include <faux/str.h>
  6. #include <faux/list.h>
  7. #include <faux/conv.h>
  8. #include <klish/khelper.h>
  9. #include <klish/ientry.h>
  10. #include <klish/kentry.h>
  11. #define TAG "ENTRY"
  12. bool_t ientry_parse(const ientry_t *info, kentry_t *entry, faux_error_t *error)
  13. {
  14. bool_t retcode = BOOL_TRUE;
  15. if (!info)
  16. return BOOL_FALSE;
  17. if (!entry)
  18. return BOOL_FALSE;
  19. // Help
  20. if (!faux_str_is_empty(info->help)) {
  21. if (!kentry_set_help(entry, info->help)) {
  22. faux_error_add(error, TAG": Illegal 'help' attribute");
  23. retcode = BOOL_FALSE;
  24. }
  25. }
  26. // Container
  27. if (!faux_str_is_empty(info->container)) {
  28. bool_t b = BOOL_FALSE;
  29. if (!faux_conv_str2bool(info->container, &b) ||
  30. !kentry_set_container(entry, b)) {
  31. faux_error_add(error, TAG": Illegal 'container' attribute");
  32. retcode = BOOL_FALSE;
  33. }
  34. }
  35. // Mode
  36. if (!faux_str_is_empty(info->mode)) {
  37. kentry_mode_e mode = KENTRY_MODE_NONE;
  38. if (!faux_str_casecmp(info->mode, "sequence"))
  39. mode = KENTRY_MODE_SEQUENCE;
  40. else if (!faux_str_casecmp(info->mode, "switch"))
  41. mode = KENTRY_MODE_SWITCH;
  42. else if (!faux_str_casecmp(info->mode, "empty"))
  43. mode = KENTRY_MODE_EMPTY;
  44. if ((KENTRY_MODE_NONE == mode) || !kentry_set_mode(entry, mode)) {
  45. faux_error_add(error, TAG": Illegal 'mode' attribute");
  46. retcode = BOOL_FALSE;
  47. }
  48. }
  49. // Purpose
  50. if (!faux_str_is_empty(info->purpose)) {
  51. kentry_purpose_e purpose = KENTRY_PURPOSE_NONE;
  52. if (!faux_str_casecmp(info->purpose, "common"))
  53. purpose = KENTRY_PURPOSE_COMMON;
  54. else if (!faux_str_casecmp(info->purpose, "ptype"))
  55. purpose = KENTRY_PURPOSE_PTYPE;
  56. else if (!faux_str_casecmp(info->purpose, "prompt"))
  57. purpose = KENTRY_PURPOSE_PROMPT;
  58. else if (!faux_str_casecmp(info->purpose, "cond"))
  59. purpose = KENTRY_PURPOSE_COND;
  60. else if (!faux_str_casecmp(info->purpose, "completion"))
  61. purpose = KENTRY_PURPOSE_COMPLETION;
  62. else if (!faux_str_casecmp(info->purpose, "help"))
  63. purpose = KENTRY_PURPOSE_HELP;
  64. else if (!faux_str_casecmp(info->purpose, "log"))
  65. purpose = KENTRY_PURPOSE_LOG;
  66. if ((KENTRY_PURPOSE_NONE == purpose) || !kentry_set_purpose(entry, purpose)) {
  67. faux_error_add(error, TAG": Illegal 'purpose' attribute");
  68. retcode = BOOL_FALSE;
  69. }
  70. }
  71. // Min occurs
  72. if (!faux_str_is_empty(info->min)) {
  73. unsigned int i = 0;
  74. if (!faux_conv_atoui(info->min, &i, 0) ||
  75. !kentry_set_min(entry, (size_t)i)) {
  76. faux_error_add(error, TAG": Illegal 'min' attribute");
  77. retcode = BOOL_FALSE;
  78. }
  79. }
  80. // Max occurs
  81. if (!faux_str_is_empty(info->max)) {
  82. unsigned int i = 0;
  83. if (!faux_conv_atoui(info->max, &i, 0) ||
  84. !kentry_set_max(entry, (size_t)i)) {
  85. faux_error_add(error, TAG": Illegal 'max' attribute");
  86. retcode = BOOL_FALSE;
  87. }
  88. }
  89. // Ref string
  90. if (!faux_str_is_empty(info->ref)) {
  91. if (!kentry_set_ref_str(entry, info->ref)) {
  92. faux_error_add(error, TAG": Illegal 'ref' attribute");
  93. retcode = BOOL_FALSE;
  94. }
  95. }
  96. // Value
  97. if (!faux_str_is_empty(info->value)) {
  98. if (!kentry_set_value(entry, info->value)) {
  99. faux_error_add(error, TAG": Illegal 'value' attribute");
  100. retcode = BOOL_FALSE;
  101. }
  102. }
  103. // Restore
  104. if (!faux_str_is_empty(info->restore)) {
  105. bool_t b = BOOL_FALSE;
  106. if (!faux_conv_str2bool(info->restore, &b) ||
  107. !kentry_set_restore(entry, b)) {
  108. faux_error_add(error, TAG": Illegal 'restore' attribute");
  109. retcode = BOOL_FALSE;
  110. }
  111. }
  112. // Transparent
  113. if (!faux_str_is_empty(info->transparent)) {
  114. bool_t b = BOOL_FALSE;
  115. if (!faux_conv_str2bool(info->transparent, &b) ||
  116. !kentry_set_transparent(entry, b))
  117. {
  118. faux_error_add(error,
  119. TAG": Illegal 'transparent' attribute");
  120. retcode = BOOL_FALSE;
  121. }
  122. }
  123. // Order
  124. if (!faux_str_is_empty(info->order)) {
  125. bool_t b = BOOL_FALSE;
  126. if (!faux_conv_str2bool(info->order, &b) ||
  127. !kentry_set_order(entry, b)) {
  128. faux_error_add(error, TAG": Illegal 'order' attribute");
  129. retcode = BOOL_FALSE;
  130. }
  131. }
  132. // Filter
  133. if (!faux_str_is_empty(info->filter)) {
  134. kentry_filter_e filter = KENTRY_FILTER_NONE;
  135. if (!faux_str_casecmp(info->filter, "false"))
  136. filter = KENTRY_FILTER_FALSE;
  137. else if (!faux_str_casecmp(info->filter, "true"))
  138. filter = KENTRY_FILTER_TRUE;
  139. else if (!faux_str_casecmp(info->filter, "dual"))
  140. filter = KENTRY_FILTER_DUAL;
  141. if ((KENTRY_FILTER_NONE == filter) || !kentry_set_filter(entry, filter)) {
  142. faux_error_add(error, TAG": Illegal 'filter' attribute");
  143. retcode = BOOL_FALSE;
  144. }
  145. }
  146. return retcode;
  147. }
  148. bool_t ientry_parse_nested(const ientry_t *ientry, kentry_t *kentry,
  149. faux_error_t *error)
  150. {
  151. bool_t retval = BOOL_TRUE;
  152. if (!kentry || !ientry) {
  153. faux_error_add(error, TAG": Internal error");
  154. return BOOL_FALSE;
  155. }
  156. // ENTRY list
  157. // ENTRYs can be duplicate. Duplicated ENTRY will add nested
  158. // elements to existent ENTRY. Also it can overwrite ENTRY attributes.
  159. // So there is no special rule which attribute value will be "on top".
  160. // It's a random. Technically later ENTRYs will rewrite previous
  161. // values.
  162. if (ientry->entrys) {
  163. ientry_t **p_ientry = NULL;
  164. for (p_ientry = *ientry->entrys; *p_ientry; p_ientry++) {
  165. kentry_t *nkentry = NULL;
  166. ientry_t *nientry = *p_ientry;
  167. const char *entry_name = nientry->name;
  168. if (entry_name)
  169. nkentry = kentry_find_entry(kentry, entry_name);
  170. // ENTRY already exists
  171. if (nkentry) {
  172. if (!ientry_parse(nientry, nkentry, error)) {
  173. retval = BOOL_FALSE;
  174. continue;
  175. }
  176. if (!ientry_parse_nested(nientry, nkentry,
  177. error)) {
  178. retval = BOOL_FALSE;
  179. continue;
  180. }
  181. continue;
  182. }
  183. // New ENTRY
  184. nkentry = ientry_load(nientry, error);
  185. if (!nkentry) {
  186. retval = BOOL_FALSE;
  187. continue;
  188. }
  189. kentry_set_parent(nkentry, kentry); // Set parent entry
  190. if (!kentry_add_entrys(kentry, nkentry)) {
  191. faux_error_sprintf(error,
  192. TAG": Can't add ENTRY \"%s\"",
  193. kentry_name(nkentry));
  194. kentry_free(nkentry);
  195. retval = BOOL_FALSE;
  196. continue;
  197. }
  198. }
  199. }
  200. // ACTION list
  201. if (ientry->actions) {
  202. iaction_t **p_iaction = NULL;
  203. for (p_iaction = *ientry->actions; *p_iaction; p_iaction++) {
  204. kaction_t *kaction = NULL;
  205. iaction_t *iaction = *p_iaction;
  206. kaction = iaction_load(iaction, error);
  207. if (!kaction) {
  208. retval = BOOL_FALSE;
  209. continue;
  210. }
  211. if (!kentry_add_actions(kentry, kaction)) {
  212. faux_error_sprintf(error,
  213. TAG": Can't add ACTION #%d",
  214. kentry_actions_len(kentry) + 1);
  215. kaction_free(kaction);
  216. retval = BOOL_FALSE;
  217. continue;
  218. }
  219. }
  220. }
  221. // HOTKEY list
  222. if (ientry->hotkeys) {
  223. ihotkey_t **p_ihotkey = NULL;
  224. for (p_ihotkey = *ientry->hotkeys; *p_ihotkey; p_ihotkey++) {
  225. khotkey_t *khotkey = NULL;
  226. ihotkey_t *ihotkey = *p_ihotkey;
  227. khotkey = ihotkey_load(ihotkey, error);
  228. if (!khotkey) {
  229. retval = BOOL_FALSE;
  230. continue;
  231. }
  232. if (!kentry_add_hotkeys(kentry, khotkey)) {
  233. faux_error_sprintf(error,
  234. TAG": Can't add HOTKEY \"%s\"",
  235. khotkey_key(khotkey));
  236. khotkey_free(khotkey);
  237. retval = BOOL_FALSE;
  238. continue;
  239. }
  240. }
  241. }
  242. if (!retval)
  243. faux_error_sprintf(error, TAG" \"%s\": Illegal nested elements",
  244. kentry_name(kentry));
  245. return retval;
  246. }
  247. kentry_t *ientry_load(const ientry_t *ientry, faux_error_t *error)
  248. {
  249. kentry_t *kentry = NULL;
  250. if (!ientry)
  251. return NULL;
  252. // Name [mandatory]
  253. if (faux_str_is_empty(ientry->name)) {
  254. faux_error_add(error, TAG": Empty 'name' attribute");
  255. return NULL;
  256. }
  257. kentry = kentry_new(ientry->name);
  258. if (!kentry) {
  259. faux_error_sprintf(error, TAG" \"%s\": Can't create object",
  260. ientry->name);
  261. return NULL;
  262. }
  263. if (!ientry_parse(ientry, kentry, error)) {
  264. kentry_free(kentry);
  265. return NULL;
  266. }
  267. // Parse nested elements
  268. if (!ientry_parse_nested(ientry, kentry, error)) {
  269. kentry_free(kentry);
  270. return NULL;
  271. }
  272. return kentry;
  273. }
  274. char *ientry_deploy(const kentry_t *kentry, int level)
  275. {
  276. char *str = NULL;
  277. char *tmp = NULL;
  278. char *mode = NULL;
  279. char *purpose = NULL;
  280. char *filter = NULL;
  281. kentry_entrys_node_t *entrys_iter = NULL;
  282. kentry_actions_node_t *actions_iter = NULL;
  283. kentry_hotkeys_node_t *hotkeys_iter = NULL;
  284. char *num = NULL;
  285. tmp = faux_str_sprintf("%*cENTRY {\n", level, ' ');
  286. faux_str_cat(&str, tmp);
  287. faux_str_free(tmp);
  288. attr2ctext(&str, "name", kentry_name(kentry), level + 1);
  289. attr2ctext(&str, "help", kentry_help(kentry), level + 1);
  290. attr2ctext(&str, "ref", kentry_ref_str(kentry), level + 1);
  291. // Links (ENTRY with 'ref' attribute) doesn't need the following filds
  292. // that will be replaced by content of referenced ENTRY
  293. if (faux_str_is_empty(kentry_ref_str(kentry))) {
  294. attr2ctext(&str, "container", faux_conv_bool2str(kentry_container(kentry)), level + 1);
  295. // Mode
  296. switch (kentry_mode(kentry)) {
  297. case KENTRY_MODE_SEQUENCE:
  298. mode = "sequence";
  299. break;
  300. case KENTRY_MODE_SWITCH:
  301. mode = "switch";
  302. break;
  303. case KENTRY_MODE_EMPTY:
  304. mode = "empty";
  305. break;
  306. default:
  307. mode = NULL;
  308. }
  309. attr2ctext(&str, "mode", mode, level + 1);
  310. // Purpose
  311. switch (kentry_purpose(kentry)) {
  312. case KENTRY_PURPOSE_COMMON:
  313. purpose = "common";
  314. break;
  315. case KENTRY_PURPOSE_PTYPE:
  316. purpose = "ptype";
  317. break;
  318. case KENTRY_PURPOSE_PROMPT:
  319. purpose = "prompt";
  320. break;
  321. case KENTRY_PURPOSE_COND:
  322. purpose = "cond";
  323. break;
  324. case KENTRY_PURPOSE_COMPLETION:
  325. purpose = "completion";
  326. break;
  327. case KENTRY_PURPOSE_HELP:
  328. purpose = "help";
  329. break;
  330. case KENTRY_PURPOSE_LOG:
  331. purpose = "log";
  332. break;
  333. default:
  334. purpose = NULL;
  335. }
  336. attr2ctext(&str, "purpose", purpose, level + 1);
  337. // Min occurs
  338. num = faux_str_sprintf("%u", kentry_min(kentry));
  339. attr2ctext(&str, "min", num, level + 1);
  340. faux_str_free(num);
  341. num = NULL;
  342. // Max occurs
  343. num = faux_str_sprintf("%u", kentry_max(kentry));
  344. attr2ctext(&str, "max", num, level + 1);
  345. faux_str_free(num);
  346. num = NULL;
  347. attr2ctext(&str, "value", kentry_value(kentry), level + 1);
  348. attr2ctext(&str, "restore", faux_conv_bool2str(kentry_restore(kentry)), level + 1);
  349. attr2ctext(&str, "transparent",
  350. faux_conv_bool2str(kentry_transparent(kentry)),
  351. level + 1);
  352. attr2ctext(&str, "order", faux_conv_bool2str(kentry_order(kentry)), level + 1);
  353. // Filter
  354. switch (kentry_filter(kentry)) {
  355. case KENTRY_FILTER_FALSE:
  356. filter = "false";
  357. break;
  358. case KENTRY_FILTER_TRUE:
  359. filter = "true";
  360. break;
  361. case KENTRY_FILTER_DUAL:
  362. filter = "dual";
  363. break;
  364. default:
  365. filter = NULL;
  366. }
  367. attr2ctext(&str, "filter", filter, level + 1);
  368. // ENTRY list
  369. entrys_iter = kentry_entrys_iter(kentry);
  370. if (entrys_iter) {
  371. kentry_t *nentry = NULL;
  372. tmp = faux_str_sprintf("\n%*cENTRY_LIST\n\n", level + 1, ' ');
  373. faux_str_cat(&str, tmp);
  374. faux_str_free(tmp);
  375. while ((nentry = kentry_entrys_each(&entrys_iter))) {
  376. tmp = ientry_deploy(nentry, level + 2);
  377. faux_str_cat(&str, tmp);
  378. faux_str_free(tmp);
  379. }
  380. tmp = faux_str_sprintf("%*cEND_ENTRY_LIST,\n", level + 1, ' ');
  381. faux_str_cat(&str, tmp);
  382. faux_str_free(tmp);
  383. }
  384. // ACTION list
  385. actions_iter = kentry_actions_iter(kentry);
  386. if (actions_iter) {
  387. kaction_t *action = NULL;
  388. tmp = faux_str_sprintf("\n%*cACTION_LIST\n\n", level + 1, ' ');
  389. faux_str_cat(&str, tmp);
  390. faux_str_free(tmp);
  391. while ((action = kentry_actions_each(&actions_iter))) {
  392. tmp = iaction_deploy(action, level + 2);
  393. faux_str_cat(&str, tmp);
  394. faux_str_free(tmp);
  395. }
  396. tmp = faux_str_sprintf("%*cEND_ACTION_LIST,\n", level + 1, ' ');
  397. faux_str_cat(&str, tmp);
  398. faux_str_free(tmp);
  399. }
  400. // HOTKEY list
  401. hotkeys_iter = kentry_hotkeys_iter(kentry);
  402. if (hotkeys_iter) {
  403. khotkey_t *hotkey = NULL;
  404. tmp = faux_str_sprintf("\n%*cHOTKEY_LIST\n\n", level + 1, ' ');
  405. faux_str_cat(&str, tmp);
  406. faux_str_free(tmp);
  407. while ((hotkey = kentry_hotkeys_each(&hotkeys_iter))) {
  408. tmp = ihotkey_deploy(hotkey, level + 2);
  409. faux_str_cat(&str, tmp);
  410. faux_str_free(tmp);
  411. }
  412. tmp = faux_str_sprintf("%*cEND_HOTKEY_LIST,\n", level + 1, ' ');
  413. faux_str_cat(&str, tmp);
  414. faux_str_free(tmp);
  415. }
  416. } // ref_str
  417. tmp = faux_str_sprintf("%*c},\n\n", level, ' ');
  418. faux_str_cat(&str, tmp);
  419. faux_str_free(tmp);
  420. return str;
  421. }