list.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657
  1. /**
  2. * @file list.c
  3. * @author Ambroz Bizjak <ambrop7@gmail.com>
  4. *
  5. * @section LICENSE
  6. *
  7. * This file is part of BadVPN.
  8. *
  9. * BadVPN is free software: you can redistribute it and/or modify
  10. * it under the terms of the GNU General Public License version 2
  11. * as published by the Free Software Foundation.
  12. *
  13. * BadVPN is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License along
  19. * with this program; if not, write to the Free Software Foundation, Inc.,
  20. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  21. *
  22. * @section DESCRIPTION
  23. *
  24. * List construction module.
  25. *
  26. * Synopsis:
  27. * list(elem1, ..., elemN)
  28. * list listfrom(list l1, ..., list lN)
  29. *
  30. * Description:
  31. * The first form creates a list with the given elements.
  32. * The second form creates a list by concatenating the given
  33. * lists.
  34. *
  35. * Variables:
  36. * (empty) - list containing elem1, ..., elemN
  37. *
  38. * Synopsis: list::append(arg)
  39. *
  40. * Synopsis: list::appendv(list arg)
  41. * Description: Appends the elements of arg to the list.
  42. *
  43. * Synopsis: list::length()
  44. * Variables:
  45. * (empty) - number of elements in list at the time of initialization
  46. *
  47. * Synopsis: list::get(string index)
  48. * Variables:
  49. * (empty) - element of list at position index (starting from zero) at the time of initialization
  50. *
  51. * Synopsis: list::shift()
  52. *
  53. * Synopsis: list::contains(value)
  54. * Variables:
  55. * (empty) - "true" if list contains value, "false" if not
  56. */
  57. #include <stdlib.h>
  58. #include <string.h>
  59. #include <stdio.h>
  60. #include <inttypes.h>
  61. #include <ncd/NCDModule.h>
  62. #include <generated/blog_channel_ncd_list.h>
  63. #define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__)
  64. struct instance {
  65. NCDModuleInst *i;
  66. NCDValue list;
  67. };
  68. struct append_instance {
  69. NCDModuleInst *i;
  70. };
  71. struct appendv_instance {
  72. NCDModuleInst *i;
  73. };
  74. struct length_instance {
  75. NCDModuleInst *i;
  76. size_t length;
  77. };
  78. struct get_instance {
  79. NCDModuleInst *i;
  80. NCDValue value;
  81. };
  82. struct shift_instance {
  83. NCDModuleInst *i;
  84. };
  85. struct contains_instance {
  86. NCDModuleInst *i;
  87. int contains;
  88. };
  89. static void func_new_list (NCDModuleInst *i)
  90. {
  91. // allocate instance
  92. struct instance *o = malloc(sizeof(*o));
  93. if (!o) {
  94. ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
  95. goto fail0;
  96. }
  97. NCDModuleInst_Backend_SetUser(i, o);
  98. // init arguments
  99. o->i = i;
  100. // copy list
  101. if (!NCDValue_InitCopy(&o->list, i->args)) {
  102. ModuleLog(i, BLOG_ERROR, "NCDValue_InitCopy failed");
  103. goto fail1;
  104. }
  105. // signal up
  106. NCDModuleInst_Backend_Up(o->i);
  107. return;
  108. fail1:
  109. free(o);
  110. fail0:
  111. NCDModuleInst_Backend_SetError(i);
  112. NCDModuleInst_Backend_Dead(i);
  113. }
  114. static void func_new_listfrom (NCDModuleInst *i)
  115. {
  116. // allocate instance
  117. struct instance *o = malloc(sizeof(*o));
  118. if (!o) {
  119. ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
  120. goto fail0;
  121. }
  122. NCDModuleInst_Backend_SetUser(i, o);
  123. // init arguments
  124. o->i = i;
  125. // init list
  126. NCDValue_InitList(&o->list);
  127. // append contents of list arguments
  128. for (NCDValue *arg = NCDValue_ListFirst(i->args); arg; arg = NCDValue_ListNext(i->args, arg)) {
  129. // check type
  130. if (NCDValue_Type(arg) != NCDVALUE_LIST) {
  131. ModuleLog(i, BLOG_ERROR, "wrong type");
  132. goto fail2;
  133. }
  134. // copy list
  135. NCDValue copy;
  136. if (!NCDValue_InitCopy(&copy, arg)) {
  137. ModuleLog(i, BLOG_ERROR, "NCDValue_InitCopy failed");
  138. goto fail2;
  139. }
  140. // append
  141. if (!NCDValue_ListAppendList(&o->list, copy)) {
  142. ModuleLog(i, BLOG_ERROR, "NCDValue_ListAppendList failed");
  143. NCDValue_Free(&copy);
  144. goto fail2;
  145. }
  146. }
  147. // signal up
  148. NCDModuleInst_Backend_Up(o->i);
  149. return;
  150. fail2:
  151. NCDValue_Free(&o->list);
  152. fail1:
  153. free(o);
  154. fail0:
  155. NCDModuleInst_Backend_SetError(i);
  156. NCDModuleInst_Backend_Dead(i);
  157. }
  158. static void func_die (void *vo)
  159. {
  160. struct instance *o = vo;
  161. NCDModuleInst *i = o->i;
  162. // free list
  163. NCDValue_Free(&o->list);
  164. // free instance
  165. free(o);
  166. NCDModuleInst_Backend_Dead(i);
  167. }
  168. static int func_getvar (void *vo, const char *name, NCDValue *out)
  169. {
  170. struct instance *o = vo;
  171. if (!strcmp(name, "")) {
  172. if (!NCDValue_InitCopy(out, &o->list)) {
  173. ModuleLog(o->i, BLOG_ERROR, "NCDValue_InitCopy failed");
  174. return 0;
  175. }
  176. return 1;
  177. }
  178. return 0;
  179. }
  180. static void append_func_new (NCDModuleInst *i)
  181. {
  182. // allocate instance
  183. struct append_instance *o = malloc(sizeof(*o));
  184. if (!o) {
  185. ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
  186. goto fail0;
  187. }
  188. NCDModuleInst_Backend_SetUser(i, o);
  189. // init arguments
  190. o->i = i;
  191. // check arguments
  192. NCDValue *arg;
  193. if (!NCDValue_ListRead(o->i->args, 1, &arg)) {
  194. ModuleLog(o->i, BLOG_ERROR, "wrong arity");
  195. goto fail1;
  196. }
  197. // get method object
  198. struct instance *mo = i->method_object->inst_user;
  199. // append
  200. NCDValue v;
  201. if (!NCDValue_InitCopy(&v, arg)) {
  202. ModuleLog(o->i, BLOG_ERROR, "NCDValue_InitCopy failed");
  203. goto fail1;
  204. }
  205. if (!NCDValue_ListAppend(&mo->list, v)) {
  206. NCDValue_Free(&v);
  207. ModuleLog(o->i, BLOG_ERROR, "NCDValue_ListAppend failed");
  208. goto fail1;
  209. }
  210. // signal up
  211. NCDModuleInst_Backend_Up(o->i);
  212. return;
  213. fail1:
  214. free(o);
  215. fail0:
  216. NCDModuleInst_Backend_SetError(i);
  217. NCDModuleInst_Backend_Dead(i);
  218. }
  219. static void append_func_die (void *vo)
  220. {
  221. struct append_instance *o = vo;
  222. NCDModuleInst *i = o->i;
  223. // free instance
  224. free(o);
  225. NCDModuleInst_Backend_Dead(i);
  226. }
  227. static void appendv_func_new (NCDModuleInst *i)
  228. {
  229. // allocate instance
  230. struct appendv_instance *o = malloc(sizeof(*o));
  231. if (!o) {
  232. ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
  233. goto fail0;
  234. }
  235. NCDModuleInst_Backend_SetUser(i, o);
  236. // init arguments
  237. o->i = i;
  238. // check arguments
  239. NCDValue *arg;
  240. if (!NCDValue_ListRead(o->i->args, 1, &arg)) {
  241. ModuleLog(o->i, BLOG_ERROR, "wrong arity");
  242. goto fail1;
  243. }
  244. if (NCDValue_Type(arg) != NCDVALUE_LIST) {
  245. ModuleLog(o->i, BLOG_ERROR, "wrong type");
  246. goto fail1;
  247. }
  248. // get method object
  249. struct instance *mo = i->method_object->inst_user;
  250. // append
  251. NCDValue l;
  252. if (!NCDValue_InitCopy(&l, arg)) {
  253. ModuleLog(o->i, BLOG_ERROR, "NCDValue_InitCopy failed");
  254. goto fail1;
  255. }
  256. if (!NCDValue_ListAppendList(&mo->list, l)) {
  257. ModuleLog(o->i, BLOG_ERROR, "NCDValue_ListAppendList failed");
  258. NCDValue_Free(&l);
  259. goto fail1;
  260. }
  261. // signal up
  262. NCDModuleInst_Backend_Up(o->i);
  263. return;
  264. fail1:
  265. free(o);
  266. fail0:
  267. NCDModuleInst_Backend_SetError(i);
  268. NCDModuleInst_Backend_Dead(i);
  269. }
  270. static void appendv_func_die (void *vo)
  271. {
  272. struct appendv_instance *o = vo;
  273. NCDModuleInst *i = o->i;
  274. // free instance
  275. free(o);
  276. NCDModuleInst_Backend_Dead(i);
  277. }
  278. static void length_func_new (NCDModuleInst *i)
  279. {
  280. // allocate instance
  281. struct length_instance *o = malloc(sizeof(*o));
  282. if (!o) {
  283. ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
  284. goto fail0;
  285. }
  286. NCDModuleInst_Backend_SetUser(i, o);
  287. // init arguments
  288. o->i = i;
  289. // check arguments
  290. if (!NCDValue_ListRead(o->i->args, 0)) {
  291. ModuleLog(o->i, BLOG_ERROR, "wrong arity");
  292. goto fail1;
  293. }
  294. // get method object
  295. struct instance *mo = i->method_object->inst_user;
  296. // remember length
  297. o->length = NCDValue_ListCount(&mo->list);
  298. // signal up
  299. NCDModuleInst_Backend_Up(o->i);
  300. return;
  301. fail1:
  302. free(o);
  303. fail0:
  304. NCDModuleInst_Backend_SetError(i);
  305. NCDModuleInst_Backend_Dead(i);
  306. }
  307. static void length_func_die (void *vo)
  308. {
  309. struct length_instance *o = vo;
  310. NCDModuleInst *i = o->i;
  311. // free instance
  312. free(o);
  313. NCDModuleInst_Backend_Dead(i);
  314. }
  315. static int length_func_getvar (void *vo, const char *name, NCDValue *out)
  316. {
  317. struct length_instance *o = vo;
  318. if (!strcmp(name, "")) {
  319. char str[50];
  320. snprintf(str, sizeof(str), "%"PRIuMAX, (uintmax_t)o->length);
  321. if (!NCDValue_InitString(out, str)) {
  322. ModuleLog(o->i, BLOG_ERROR, "NCDValue_InitString failed");
  323. return 0;
  324. }
  325. return 1;
  326. }
  327. return 0;
  328. }
  329. static void get_func_new (NCDModuleInst *i)
  330. {
  331. // allocate instance
  332. struct get_instance *o = malloc(sizeof(*o));
  333. if (!o) {
  334. ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
  335. goto fail0;
  336. }
  337. NCDModuleInst_Backend_SetUser(i, o);
  338. // init arguments
  339. o->i = i;
  340. // check arguments
  341. NCDValue *index_arg;
  342. if (!NCDValue_ListRead(o->i->args, 1, &index_arg)) {
  343. ModuleLog(o->i, BLOG_ERROR, "wrong arity");
  344. goto fail1;
  345. }
  346. if (NCDValue_Type(index_arg) != NCDVALUE_STRING) {
  347. ModuleLog(o->i, BLOG_ERROR, "wrong type");
  348. goto fail1;
  349. }
  350. uintmax_t index;
  351. if (sscanf(NCDValue_StringValue(index_arg), "%"SCNuMAX, &index) != 1) {
  352. ModuleLog(o->i, BLOG_ERROR, "wrong value");
  353. goto fail1;
  354. }
  355. // get method object
  356. struct instance *mo = i->method_object->inst_user;
  357. // check index
  358. if (index >= NCDValue_ListCount(&mo->list)) {
  359. ModuleLog(o->i, BLOG_ERROR, "no element at index %"PRIuMAX, index);
  360. goto fail1;
  361. }
  362. // copy value
  363. if (!NCDValue_InitCopy(&o->value, NCDValue_ListGet(&mo->list, index))) {
  364. ModuleLog(o->i, BLOG_ERROR, "NCDValue_InitCopy failed");
  365. goto fail1;
  366. }
  367. // signal up
  368. NCDModuleInst_Backend_Up(o->i);
  369. return;
  370. fail1:
  371. free(o);
  372. fail0:
  373. NCDModuleInst_Backend_SetError(i);
  374. NCDModuleInst_Backend_Dead(i);
  375. }
  376. static void get_func_die (void *vo)
  377. {
  378. struct get_instance *o = vo;
  379. NCDModuleInst *i = o->i;
  380. // free value
  381. NCDValue_Free(&o->value);
  382. // free instance
  383. free(o);
  384. NCDModuleInst_Backend_Dead(i);
  385. }
  386. static int get_func_getvar (void *vo, const char *name, NCDValue *out)
  387. {
  388. struct get_instance *o = vo;
  389. if (!strcmp(name, "")) {
  390. if (!NCDValue_InitCopy(out, &o->value)) {
  391. ModuleLog(o->i, BLOG_ERROR, "NCDValue_InitCopy failed");
  392. return 0;
  393. }
  394. return 1;
  395. }
  396. return 0;
  397. }
  398. static void shift_func_new (NCDModuleInst *i)
  399. {
  400. // allocate instance
  401. struct shift_instance *o = malloc(sizeof(*o));
  402. if (!o) {
  403. ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
  404. goto fail0;
  405. }
  406. NCDModuleInst_Backend_SetUser(i, o);
  407. // init arguments
  408. o->i = i;
  409. // check arguments
  410. if (!NCDValue_ListRead(o->i->args, 0)) {
  411. ModuleLog(o->i, BLOG_ERROR, "wrong arity");
  412. goto fail1;
  413. }
  414. // get method object
  415. struct instance *mo = i->method_object->inst_user;
  416. // shift
  417. if (!NCDValue_ListFirst(&mo->list)) {
  418. ModuleLog(o->i, BLOG_ERROR, "list has no elements");
  419. goto fail1;
  420. }
  421. NCDValue v = NCDValue_ListShift(&mo->list);
  422. NCDValue_Free(&v);
  423. // signal up
  424. NCDModuleInst_Backend_Up(o->i);
  425. return;
  426. fail1:
  427. free(o);
  428. fail0:
  429. NCDModuleInst_Backend_SetError(i);
  430. NCDModuleInst_Backend_Dead(i);
  431. }
  432. static void shift_func_die (void *vo)
  433. {
  434. struct shift_instance *o = vo;
  435. NCDModuleInst *i = o->i;
  436. // free instance
  437. free(o);
  438. NCDModuleInst_Backend_Dead(i);
  439. }
  440. static void contains_func_new (NCDModuleInst *i)
  441. {
  442. // allocate instance
  443. struct contains_instance *o = malloc(sizeof(*o));
  444. if (!o) {
  445. ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
  446. goto fail0;
  447. }
  448. NCDModuleInst_Backend_SetUser(i, o);
  449. // init arguments
  450. o->i = i;
  451. // read arguments
  452. NCDValue *value_arg;
  453. if (!NCDValue_ListRead(i->args, 1, &value_arg)) {
  454. ModuleLog(o->i, BLOG_ERROR, "wrong arity");
  455. goto fail1;
  456. }
  457. // get method object
  458. struct instance *mo = i->method_object->inst_user;
  459. // search
  460. o->contains = 0;
  461. for (NCDValue *v = NCDValue_ListFirst(&mo->list); v; v = NCDValue_ListNext(&mo->list, v)) {
  462. if (NCDValue_Compare(v, value_arg) == 0) {
  463. o->contains = 1;
  464. break;
  465. }
  466. }
  467. // signal up
  468. NCDModuleInst_Backend_Up(o->i);
  469. return;
  470. fail1:
  471. free(o);
  472. fail0:
  473. NCDModuleInst_Backend_SetError(i);
  474. NCDModuleInst_Backend_Dead(i);
  475. }
  476. static void contains_func_die (void *vo)
  477. {
  478. struct contains_instance *o = vo;
  479. NCDModuleInst *i = o->i;
  480. // free instance
  481. free(o);
  482. NCDModuleInst_Backend_Dead(i);
  483. }
  484. static int contains_func_getvar (void *vo, const char *name, NCDValue *out)
  485. {
  486. struct contains_instance *o = vo;
  487. if (!strcmp(name, "")) {
  488. const char *value = (o->contains ? "true" : "false");
  489. if (!NCDValue_InitString(out, value)) {
  490. ModuleLog(o->i, BLOG_ERROR, "NCDValue_InitString failed");
  491. return 0;
  492. }
  493. return 1;
  494. }
  495. return 0;
  496. }
  497. static const struct NCDModule modules[] = {
  498. {
  499. .type = "list",
  500. .func_new = func_new_list,
  501. .func_die = func_die,
  502. .func_getvar = func_getvar
  503. }, {
  504. .type = "listfrom",
  505. .base_type = "list",
  506. .func_new = func_new_listfrom,
  507. .func_die = func_die,
  508. .func_getvar = func_getvar
  509. }, {
  510. .type = "list::append",
  511. .func_new = append_func_new,
  512. .func_die = append_func_die
  513. }, {
  514. .type = "list::appendv",
  515. .func_new = appendv_func_new,
  516. .func_die = appendv_func_die
  517. }, {
  518. .type = "list::length",
  519. .func_new = length_func_new,
  520. .func_die = length_func_die,
  521. .func_getvar = length_func_getvar
  522. }, {
  523. .type = "list::get",
  524. .func_new = get_func_new,
  525. .func_die = get_func_die,
  526. .func_getvar = get_func_getvar
  527. }, {
  528. .type = "list::shift",
  529. .func_new = shift_func_new,
  530. .func_die = shift_func_die
  531. }, {
  532. .type = "list::contains",
  533. .func_new = contains_func_new,
  534. .func_die = contains_func_die,
  535. .func_getvar = contains_func_getvar
  536. }, {
  537. .type = NULL
  538. }
  539. };
  540. const struct NCDModuleGroup ncdmodule_list = {
  541. .modules = modules
  542. };