ientry.c 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  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. if ((KENTRY_PURPOSE_NONE == purpose) || !kentry_set_purpose(entry, purpose)) {
  63. faux_error_add(error, TAG": Illegal 'purpose' attribute");
  64. retcode = BOOL_FALSE;
  65. }
  66. }
  67. // Min occurs
  68. if (!faux_str_is_empty(info->min)) {
  69. unsigned int i = 0;
  70. if (!faux_conv_atoui(info->min, &i, 0) ||
  71. !kentry_set_min(entry, (size_t)i)) {
  72. faux_error_add(error, TAG": Illegal 'min' attribute");
  73. retcode = BOOL_FALSE;
  74. }
  75. }
  76. // Max occurs
  77. if (!faux_str_is_empty(info->max)) {
  78. unsigned int i = 0;
  79. if (!faux_conv_atoui(info->max, &i, 0) ||
  80. !kentry_set_max(entry, (size_t)i)) {
  81. faux_error_add(error, TAG": Illegal 'max' attribute");
  82. retcode = BOOL_FALSE;
  83. }
  84. }
  85. // Ref string
  86. if (!faux_str_is_empty(info->ref)) {
  87. if (!kentry_set_ref_str(entry, info->ref)) {
  88. faux_error_add(error, TAG": Illegal 'ref' attribute");
  89. retcode = BOOL_FALSE;
  90. }
  91. }
  92. // Value
  93. if (!faux_str_is_empty(info->value)) {
  94. if (!kentry_set_value(entry, info->value)) {
  95. faux_error_add(error, TAG": Illegal 'value' attribute");
  96. retcode = BOOL_FALSE;
  97. }
  98. }
  99. // Restore
  100. if (!faux_str_is_empty(info->restore)) {
  101. bool_t b = BOOL_FALSE;
  102. if (!faux_conv_str2bool(info->restore, &b) ||
  103. !kentry_set_restore(entry, b)) {
  104. faux_error_add(error, TAG": Illegal 'restore' attribute");
  105. retcode = BOOL_FALSE;
  106. }
  107. }
  108. // Order
  109. if (!faux_str_is_empty(info->order)) {
  110. bool_t b = BOOL_FALSE;
  111. if (!faux_conv_str2bool(info->order, &b) ||
  112. !kentry_set_order(entry, b)) {
  113. faux_error_add(error, TAG": Illegal 'order' attribute");
  114. retcode = BOOL_FALSE;
  115. }
  116. }
  117. // Filter
  118. if (!faux_str_is_empty(info->filter)) {
  119. bool_t b = BOOL_FALSE;
  120. if (!faux_conv_str2bool(info->filter, &b) ||
  121. !kentry_set_filter(entry, b)) {
  122. faux_error_add(error, TAG": Illegal 'filter' attribute");
  123. retcode = BOOL_FALSE;
  124. }
  125. }
  126. return retcode;
  127. }
  128. bool_t ientry_parse_nested(const ientry_t *ientry, kentry_t *kentry,
  129. faux_error_t *error)
  130. {
  131. bool_t retval = BOOL_TRUE;
  132. if (!kentry || !ientry) {
  133. faux_error_add(error, TAG": Internal error");
  134. return BOOL_FALSE;
  135. }
  136. // ENTRY list
  137. // ENTRYs can be duplicate. Duplicated ENTRY will add nested
  138. // elements to existent ENTRY. Also it can overwrite ENTRY attributes.
  139. // So there is no special rule which attribute value will be "on top".
  140. // It's a random. Technically later ENTRYs will rewrite previous
  141. // values.
  142. if (ientry->entrys) {
  143. ientry_t **p_ientry = NULL;
  144. for (p_ientry = *ientry->entrys; *p_ientry; p_ientry++) {
  145. kentry_t *nkentry = NULL;
  146. ientry_t *nientry = *p_ientry;
  147. const char *entry_name = nientry->name;
  148. if (entry_name)
  149. nkentry = kentry_find_entry(kentry, entry_name);
  150. // ENTRY already exists
  151. if (nkentry) {
  152. if (!ientry_parse(nientry, nkentry, error)) {
  153. retval = BOOL_FALSE;
  154. continue;
  155. }
  156. if (!ientry_parse_nested(nientry, nkentry,
  157. error)) {
  158. retval = BOOL_FALSE;
  159. continue;
  160. }
  161. continue;
  162. }
  163. // New ENTRY
  164. nkentry = ientry_load(nientry, error);
  165. if (!nkentry) {
  166. retval = BOOL_FALSE;
  167. continue;
  168. }
  169. kentry_set_parent(nkentry, kentry); // Set parent entry
  170. if (!kentry_add_entrys(kentry, nkentry)) {
  171. faux_error_sprintf(error,
  172. TAG": Can't add ENTRY \"%s\"",
  173. kentry_name(nkentry));
  174. kentry_free(nkentry);
  175. retval = BOOL_FALSE;
  176. continue;
  177. }
  178. }
  179. }
  180. // ACTION list
  181. if (ientry->actions) {
  182. iaction_t **p_iaction = NULL;
  183. for (p_iaction = *ientry->actions; *p_iaction; p_iaction++) {
  184. kaction_t *kaction = NULL;
  185. iaction_t *iaction = *p_iaction;
  186. kaction = iaction_load(iaction, error);
  187. if (!kaction) {
  188. retval = BOOL_FALSE;
  189. continue;
  190. }
  191. if (!kentry_add_actions(kentry, kaction)) {
  192. faux_error_sprintf(error,
  193. TAG": Can't add ACTION #%d",
  194. kentry_actions_len(kentry) + 1);
  195. kaction_free(kaction);
  196. retval = BOOL_FALSE;
  197. continue;
  198. }
  199. }
  200. }
  201. if (!retval)
  202. faux_error_sprintf(error, TAG" \"%s\": Illegal nested elements",
  203. kentry_name(kentry));
  204. return retval;
  205. }
  206. kentry_t *ientry_load(const ientry_t *ientry, faux_error_t *error)
  207. {
  208. kentry_t *kentry = NULL;
  209. if (!ientry)
  210. return NULL;
  211. // Name [mandatory]
  212. if (faux_str_is_empty(ientry->name)) {
  213. faux_error_add(error, TAG": Empty 'name' attribute");
  214. return NULL;
  215. }
  216. kentry = kentry_new(ientry->name);
  217. if (!kentry) {
  218. faux_error_sprintf(error, TAG" \"%s\": Can't create object",
  219. ientry->name);
  220. return NULL;
  221. }
  222. if (!ientry_parse(ientry, kentry, error)) {
  223. kentry_free(kentry);
  224. return NULL;
  225. }
  226. // Parse nested elements
  227. if (!ientry_parse_nested(ientry, kentry, error)) {
  228. kentry_free(kentry);
  229. return NULL;
  230. }
  231. return kentry;
  232. }
  233. char *ientry_deploy(const kentry_t *kentry, int level)
  234. {
  235. char *str = NULL;
  236. char *tmp = NULL;
  237. char *mode = NULL;
  238. char *purpose = NULL;
  239. kentry_entrys_node_t *entrys_iter = NULL;
  240. kentry_actions_node_t *actions_iter = NULL;
  241. char *num = NULL;
  242. tmp = faux_str_sprintf("%*cENTRY {\n", level, ' ');
  243. faux_str_cat(&str, tmp);
  244. faux_str_free(tmp);
  245. attr2ctext(&str, "name", kentry_name(kentry), level + 1);
  246. attr2ctext(&str, "help", kentry_help(kentry), level + 1);
  247. attr2ctext(&str, "ref", kentry_ref_str(kentry), level + 1);
  248. // Links (ENTRY with 'ref' attribute) doesn't need the following filds
  249. // that will be replaced by content of referenced ENTRY
  250. if (faux_str_is_empty(kentry_ref_str(kentry))) {
  251. attr2ctext(&str, "container", faux_conv_bool2str(kentry_container(kentry)), level + 1);
  252. // Mode
  253. switch (kentry_mode(kentry)) {
  254. case KENTRY_MODE_SEQUENCE:
  255. mode = "sequence";
  256. break;
  257. case KENTRY_MODE_SWITCH:
  258. mode = "switch";
  259. break;
  260. case KENTRY_MODE_EMPTY:
  261. mode = "empty";
  262. break;
  263. default:
  264. mode = NULL;
  265. }
  266. attr2ctext(&str, "mode", mode, level + 1);
  267. // Purpose
  268. switch (kentry_purpose(kentry)) {
  269. case KENTRY_PURPOSE_COMMON:
  270. purpose = "common";
  271. break;
  272. case KENTRY_PURPOSE_PTYPE:
  273. purpose = "ptype";
  274. break;
  275. case KENTRY_PURPOSE_PROMPT:
  276. purpose = "prompt";
  277. break;
  278. case KENTRY_PURPOSE_COND:
  279. purpose = "cond";
  280. break;
  281. case KENTRY_PURPOSE_COMPLETION:
  282. purpose = "completion";
  283. break;
  284. default:
  285. purpose = NULL;
  286. }
  287. attr2ctext(&str, "purpose", purpose, level + 1);
  288. // Min occurs
  289. num = faux_str_sprintf("%u", kentry_min(kentry));
  290. attr2ctext(&str, "min", num, level + 1);
  291. faux_str_free(num);
  292. num = NULL;
  293. // Max occurs
  294. num = faux_str_sprintf("%u", kentry_max(kentry));
  295. attr2ctext(&str, "max", num, level + 1);
  296. faux_str_free(num);
  297. num = NULL;
  298. attr2ctext(&str, "value", kentry_value(kentry), level + 1);
  299. attr2ctext(&str, "restore", faux_conv_bool2str(kentry_restore(kentry)), level + 1);
  300. attr2ctext(&str, "order", faux_conv_bool2str(kentry_order(kentry)), level + 1);
  301. attr2ctext(&str, "filter", faux_conv_bool2str(kentry_filter(kentry)), level + 1);
  302. // ENTRY list
  303. entrys_iter = kentry_entrys_iter(kentry);
  304. if (entrys_iter) {
  305. kentry_t *nentry = NULL;
  306. tmp = faux_str_sprintf("\n%*cENTRY_LIST\n\n", level + 1, ' ');
  307. faux_str_cat(&str, tmp);
  308. faux_str_free(tmp);
  309. while ((nentry = kentry_entrys_each(&entrys_iter))) {
  310. tmp = ientry_deploy(nentry, level + 2);
  311. faux_str_cat(&str, tmp);
  312. faux_str_free(tmp);
  313. }
  314. tmp = faux_str_sprintf("%*cEND_ENTRY_LIST,\n", level + 1, ' ');
  315. faux_str_cat(&str, tmp);
  316. faux_str_free(tmp);
  317. }
  318. // ACTION list
  319. actions_iter = kentry_actions_iter(kentry);
  320. if (actions_iter) {
  321. kaction_t *action = NULL;
  322. tmp = faux_str_sprintf("\n%*cACTION_LIST\n\n", level + 1, ' ');
  323. faux_str_cat(&str, tmp);
  324. faux_str_free(tmp);
  325. while ((action = kentry_actions_each(&actions_iter))) {
  326. tmp = iaction_deploy(action, level + 2);
  327. faux_str_cat(&str, tmp);
  328. faux_str_free(tmp);
  329. }
  330. tmp = faux_str_sprintf("%*cEND_ACTION_LIST,\n", level + 1, ' ');
  331. faux_str_cat(&str, tmp);
  332. faux_str_free(tmp);
  333. }
  334. } // ref_str
  335. tmp = faux_str_sprintf("%*c},\n\n", level, ' ');
  336. faux_str_cat(&str, tmp);
  337. faux_str_free(tmp);
  338. return str;
  339. }