• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/ucode/lib/uci.c

  1 /*
  2  * Copyright (C) 2020-2021 Jo-Philipp Wich <jo@mein.io>
  3  *
  4  * Permission to use, copy, modify, and/or distribute this software for any
  5  * purpose with or without fee is hereby granted, provided that the above
  6  * copyright notice and this permission notice appear in all copies.
  7  *
  8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 15  */
 16 
 17 #include <string.h>
 18 #include <uci.h>
 19 
 20 #include "ucode/module.h"
 21 
 22 #define err_return(err) do { last_error = err; return NULL; } while(0)
 23 
 24 static int last_error = 0;
 25 static uc_resource_type_t *cursor_type;
 26 
 27 enum pkg_cmd {
 28         CMD_SAVE,
 29         CMD_COMMIT,
 30         CMD_REVERT
 31 };
 32 
 33 static uc_value_t *
 34 uc_uci_error(uc_vm_t *vm, size_t nargs)
 35 {
 36         char buf[sizeof("Unknown error: -9223372036854775808")];
 37         uc_value_t *errmsg;
 38 
 39         const char *errstr[] = {
 40                 [UCI_ERR_MEM] =       "Out of memory",
 41                 [UCI_ERR_INVAL] =     "Invalid argument",
 42                 [UCI_ERR_NOTFOUND] =  "Entry not found",
 43                 [UCI_ERR_IO] =        "I/O error",
 44                 [UCI_ERR_PARSE] =     "Parse error",
 45                 [UCI_ERR_DUPLICATE] = "Duplicate entry",
 46                 [UCI_ERR_UNKNOWN] =   "Unknown error",
 47         };
 48 
 49         if (last_error == 0)
 50                 return NULL;
 51 
 52         if (last_error >= 0 && (unsigned)last_error < ARRAY_SIZE(errstr)) {
 53                 errmsg = ucv_string_new(errstr[last_error]);
 54         }
 55         else {
 56                 snprintf(buf, sizeof(buf), "Unknown error: %d", last_error);
 57                 errmsg = ucv_string_new(buf);
 58         }
 59 
 60         last_error = 0;
 61 
 62         return errmsg;
 63 }
 64 
 65 
 66 static uc_value_t *
 67 uc_uci_cursor(uc_vm_t *vm, size_t nargs)
 68 {
 69         uc_value_t *cdir = uc_fn_arg(0);
 70         uc_value_t *sdir = uc_fn_arg(1);
 71         struct uci_context *c;
 72         int rv;
 73 
 74         if ((cdir && ucv_type(cdir) != UC_STRING) ||
 75             (sdir && ucv_type(sdir) != UC_STRING))
 76                 err_return(UCI_ERR_INVAL);
 77 
 78         c = uci_alloc_context();
 79 
 80         if (!c)
 81                 err_return(UCI_ERR_MEM);
 82 
 83         if (cdir) {
 84                 rv = uci_set_confdir(c, ucv_string_get(cdir));
 85 
 86                 if (rv)
 87                         err_return(rv);
 88         }
 89 
 90         if (sdir) {
 91                 rv = uci_set_savedir(c, ucv_string_get(sdir));
 92 
 93                 if (rv)
 94                         err_return(rv);
 95         }
 96 
 97         return uc_resource_new(cursor_type, c);
 98 }
 99 
100 
101 static uc_value_t *
102 uc_uci_load(uc_vm_t *vm, size_t nargs)
103 {
104         struct uci_context **c = uc_fn_this("uci.cursor");
105         uc_value_t *conf = uc_fn_arg(0);
106         struct uci_element *e;
107         char *s;
108 
109         if (!c || !*c)
110                 err_return(UCI_ERR_INVAL);
111 
112         if (ucv_type(conf) != UC_STRING)
113                 err_return(UCI_ERR_INVAL);
114 
115         s = ucv_string_get(conf);
116 
117         uci_foreach_element(&(*c)->root, e) {
118                 if (!strcmp(e->name, s)) {
119                         uci_unload(*c, uci_to_package(e));
120                         break;
121                 }
122         }
123 
124         if (uci_load(*c, s, NULL))
125                 err_return((*c)->err);
126 
127         return ucv_boolean_new(true);
128 }
129 
130 static uc_value_t *
131 uc_uci_unload(uc_vm_t *vm, size_t nargs)
132 {
133         struct uci_context **c = uc_fn_this("uci.cursor");
134         uc_value_t *conf = uc_fn_arg(0);
135         struct uci_element *e;
136 
137         if (!c || !*c)
138                 err_return(UCI_ERR_INVAL);
139 
140         if (ucv_type(conf) != UC_STRING)
141                 err_return(UCI_ERR_INVAL);
142 
143         uci_foreach_element(&(*c)->root, e) {
144                 if (!strcmp(e->name, ucv_string_get(conf))) {
145                         uci_unload(*c, uci_to_package(e));
146 
147                         return ucv_boolean_new(true);
148                 }
149         }
150 
151         return ucv_boolean_new(false);
152 }
153 
154 static int
155 lookup_extended(struct uci_context *ctx, struct uci_ptr *ptr, bool extended)
156 {
157         int rv;
158         struct uci_ptr lookup;
159 
160         /* use a copy of the passed ptr since failing lookups will
161          * clobber the state */
162         lookup = *ptr;
163         lookup.flags |= UCI_LOOKUP_EXTENDED;
164 
165         rv = uci_lookup_ptr(ctx, &lookup, NULL, extended);
166 
167         /* copy to passed ptr on success */
168         if (!rv)
169                 *ptr = lookup;
170 
171         return rv;
172 }
173 
174 static int
175 lookup_ptr(struct uci_context *ctx, struct uci_ptr *ptr, bool extended)
176 {
177         if (ptr && !ptr->s && ptr->section && *ptr->section == '@')
178                 return lookup_extended(ctx, ptr, extended);
179 
180         return uci_lookup_ptr(ctx, ptr, NULL, extended);
181 }
182 
183 static uc_value_t *
184 option_to_uval(uc_vm_t *vm, struct uci_option *o)
185 {
186         struct uci_element *e;
187         uc_value_t *arr;
188 
189         switch (o->type) {
190         case UCI_TYPE_STRING:
191                 return ucv_string_new(o->v.string);
192 
193         case UCI_TYPE_LIST:
194                 arr = ucv_array_new(vm);
195 
196                 if (arr)
197                         uci_foreach_element(&o->v.list, e)
198                                 ucv_array_push(arr, ucv_string_new(e->name));
199 
200                 return arr;
201 
202         default:
203                 return NULL;
204         }
205 }
206 
207 static uc_value_t *
208 section_to_uval(uc_vm_t *vm, struct uci_section *s, int index)
209 {
210         uc_value_t *so = ucv_object_new(vm);
211         struct uci_element *e;
212         struct uci_option *o;
213 
214         if (!so)
215                 return NULL;
216 
217         ucv_object_add(so, ".anonymous", ucv_boolean_new(s->anonymous));
218         ucv_object_add(so, ".type", ucv_string_new(s->type));
219         ucv_object_add(so, ".name", ucv_string_new(s->e.name));
220 
221         if (index >= 0)
222                 ucv_object_add(so, ".index", ucv_int64_new(index));
223 
224         uci_foreach_element(&s->options, e) {
225                 o = uci_to_option(e);
226                 ucv_object_add(so, o->e.name, option_to_uval(vm, o));
227         }
228 
229         return so;
230 }
231 
232 static uc_value_t *
233 package_to_uval(uc_vm_t *vm, struct uci_package *p)
234 {
235         uc_value_t *po = ucv_object_new(vm);
236         uc_value_t *so;
237         struct uci_element *e;
238         int i = 0;
239 
240         if (!po)
241                 return NULL;
242 
243         uci_foreach_element(&p->sections, e) {
244                 so = section_to_uval(vm, uci_to_section(e), i++);
245                 ucv_object_add(po, e->name, so);
246         }
247 
248         return po;
249 }
250 
251 static uc_value_t *
252 uc_uci_get_any(uc_vm_t *vm, size_t nargs, bool all)
253 {
254         struct uci_context **c = uc_fn_this("uci.cursor");
255         uc_value_t *conf = uc_fn_arg(0);
256         uc_value_t *sect = uc_fn_arg(1);
257         uc_value_t *opt = uc_fn_arg(2);
258         struct uci_ptr ptr = { 0 };
259         int rv;
260 
261         if (!c || !*c)
262                 err_return(UCI_ERR_INVAL);
263 
264         if ((ucv_type(conf) != UC_STRING) ||
265             (sect && ucv_type(sect) != UC_STRING) ||
266             (opt && ucv_type(opt) != UC_STRING))
267                 err_return(UCI_ERR_INVAL);
268 
269         if ((!sect && !all) || (opt && all))
270                 err_return(UCI_ERR_INVAL);
271 
272         ptr.package = ucv_string_get(conf);
273         ptr.section = sect ? ucv_string_get(sect) : NULL;
274         ptr.option = opt ? ucv_string_get(opt) : NULL;
275 
276         rv = lookup_ptr(*c, &ptr, true);
277 
278         if (rv != UCI_OK)
279                 err_return(rv);
280 
281         if (!(ptr.flags & UCI_LOOKUP_COMPLETE))
282                 err_return(UCI_ERR_NOTFOUND);
283 
284         if (all) {
285                 if (ptr.section) {
286                         if (!ptr.s)
287                                 err_return(UCI_ERR_NOTFOUND);
288 
289                         return section_to_uval(vm, ptr.s, -1);
290                 }
291 
292                 if (!ptr.p)
293                         err_return(UCI_ERR_NOTFOUND);
294 
295                 return package_to_uval(vm, ptr.p);
296         }
297 
298         if (ptr.option) {
299                 if (!ptr.o)
300                         err_return(UCI_ERR_NOTFOUND);
301 
302                 return option_to_uval(vm, ptr.o);
303         }
304 
305         if (!ptr.s)
306                 err_return(UCI_ERR_NOTFOUND);
307 
308         return ucv_string_new(ptr.s->type);
309 }
310 
311 static uc_value_t *
312 uc_uci_get(uc_vm_t *vm, size_t nargs)
313 {
314         return uc_uci_get_any(vm, nargs, false);
315 }
316 
317 static uc_value_t *
318 uc_uci_get_all(uc_vm_t *vm, size_t nargs)
319 {
320         return uc_uci_get_any(vm, nargs, true);
321 }
322 
323 static uc_value_t *
324 uc_uci_get_first(uc_vm_t *vm, size_t nargs)
325 {
326         struct uci_context **c = uc_fn_this("uci.cursor");
327         uc_value_t *conf = uc_fn_arg(0);
328         uc_value_t *type = uc_fn_arg(1);
329         uc_value_t *opt = uc_fn_arg(2);
330         struct uci_package *p = NULL;
331         struct uci_section *sc;
332         struct uci_element *e;
333         struct uci_ptr ptr = { 0 };
334         int rv;
335 
336         if (ucv_type(conf) != UC_STRING ||
337             ucv_type(type) != UC_STRING ||
338             (opt && ucv_type(opt) != UC_STRING))
339                 err_return(UCI_ERR_INVAL);
340 
341         uci_foreach_element(&(*c)->root, e) {
342                 if (strcmp(e->name, ucv_string_get(conf)))
343                         continue;
344 
345                 p = uci_to_package(e);
346                 break;
347         }
348 
349         if (!p)
350                 err_return(UCI_ERR_NOTFOUND);
351 
352         uci_foreach_element(&p->sections, e) {
353                 sc = uci_to_section(e);
354 
355                 if (strcmp(sc->type, ucv_string_get(type)))
356                         continue;
357 
358                 if (!opt)
359                         return ucv_string_new(sc->e.name);
360 
361                 ptr.package = ucv_string_get(conf);
362                 ptr.section = sc->e.name;
363                 ptr.option = ucv_string_get(opt);
364                 ptr.p = p;
365                 ptr.s = sc;
366 
367                 rv = lookup_ptr(*c, &ptr, false);
368 
369                 if (rv != UCI_OK)
370                         err_return(rv);
371 
372                 if (!(ptr.flags & UCI_LOOKUP_COMPLETE))
373                         err_return(UCI_ERR_NOTFOUND);
374 
375                 return option_to_uval(vm, ptr.o);
376         }
377 
378         err_return(UCI_ERR_NOTFOUND);
379 }
380 
381 static uc_value_t *
382 uc_uci_add(uc_vm_t *vm, size_t nargs)
383 {
384         struct uci_context **c = uc_fn_this("uci.cursor");
385         uc_value_t *conf = uc_fn_arg(0);
386         uc_value_t *type = uc_fn_arg(1);
387         struct uci_element *e = NULL;
388         struct uci_package *p = NULL;
389         struct uci_section *sc = NULL;
390         int rv;
391 
392         if (ucv_type(conf) != UC_STRING ||
393             ucv_type(type) != UC_STRING)
394             err_return(UCI_ERR_INVAL);
395 
396         uci_foreach_element(&(*c)->root, e) {
397                 if (!strcmp(e->name, ucv_string_get(conf))) {
398                         p = uci_to_package(e);
399                         break;
400                 }
401         }
402 
403         if (!p)
404                 err_return(UCI_ERR_NOTFOUND);
405 
406         rv = uci_add_section(*c, p, ucv_string_get(type), &sc);
407 
408         if (rv != UCI_OK)
409                 err_return(rv);
410         else if (!sc)
411                 err_return(UCI_ERR_NOTFOUND);
412 
413         return ucv_string_new(sc->e.name);
414 }
415 
416 static bool
417 uval_to_uci(uc_vm_t *vm, uc_value_t *val, const char **p, bool *is_list)
418 {
419         uc_value_t *item;
420 
421         *p = NULL;
422 
423         if (is_list)
424                 *is_list = false;
425 
426         switch (ucv_type(val)) {
427         case UC_ARRAY:
428                 if (ucv_array_length(val) == 0)
429                         return false;
430 
431                 item = ucv_array_get(val, 0);
432 
433                 /* don't recurse */
434                 if (ucv_type(item) == UC_ARRAY)
435                         return false;
436 
437                 if (is_list)
438                         *is_list = true;
439 
440                 return uval_to_uci(vm, item, p, NULL);
441 
442         case UC_BOOLEAN:
443                 *p = xstrdup(ucv_boolean_get(val) ? "1" : "");
444 
445                 return true;
446 
447         case UC_DOUBLE:
448         case UC_INTEGER:
449         case UC_STRING:
450                 *p = ucv_to_string(vm, val);
451                 /* fall through */
452 
453         case UC_NULL:
454                 return true;
455 
456         default:
457                 return false;
458         }
459 }
460 
461 static uc_value_t *
462 uc_uci_set(uc_vm_t *vm, size_t nargs)
463 {
464         struct uci_context **c = uc_fn_this("uci.cursor");
465         uc_value_t *conf = uc_fn_arg(0);
466         uc_value_t *sect = uc_fn_arg(1);
467         uc_value_t *opt = NULL, *val = NULL;
468         struct uci_ptr ptr = { 0 };
469         bool is_list = false;
470         size_t i;
471         int rv;
472 
473         if (ucv_type(conf) != UC_STRING ||
474             ucv_type(sect) != UC_STRING)
475             err_return(UCI_ERR_INVAL);
476 
477         switch (nargs) {
478         /* conf, sect, opt, val */
479         case 4:
480                 opt = uc_fn_arg(2);
481                 val = uc_fn_arg(3);
482 
483                 if (ucv_type(opt) != UC_STRING)
484                         err_return(UCI_ERR_INVAL);
485 
486                 break;
487 
488         /* conf, sect, type */
489         case 3:
490                 val = uc_fn_arg(2);
491 
492                 if (ucv_type(val) != UC_STRING)
493                         err_return(UCI_ERR_INVAL);
494 
495                 break;
496 
497         default:
498                 err_return(UCI_ERR_INVAL);
499         }
500 
501         ptr.package = ucv_string_get(conf);
502         ptr.section = ucv_string_get(sect);
503         ptr.option = opt ? ucv_string_get(opt) : NULL;
504 
505         rv = lookup_ptr(*c, &ptr, true);
506 
507         if (rv != UCI_OK)
508                 err_return(rv);
509 
510         if (!ptr.s && ptr.option)
511                 err_return(UCI_ERR_NOTFOUND);
512 
513         if (!uval_to_uci(vm, val, &ptr.value, &is_list))
514                 err_return(UCI_ERR_INVAL);
515 
516         if (is_list) {
517                 /* if we got a one-element array, delete existing option (if any)
518                  * and iterate array at offset 0 */
519                 if (ucv_array_length(val) == 1) {
520                         i = 0;
521 
522                         free((char *)ptr.value);
523                         ptr.value = NULL;
524 
525                         if (ptr.o) {
526                                 rv = uci_delete(*c, &ptr);
527 
528                                 if (rv != UCI_OK)
529                                         err_return(rv);
530                         }
531                 }
532                 /* if we get a multi element array, overwrite existing option (if any)
533                  * with first value and iterate remaining array at offset 1 */
534                 else {
535                         i = 1;
536 
537                         rv = uci_set(*c, &ptr);
538                         free((char *)ptr.value);
539 
540                         if (rv != UCI_OK)
541                                 err_return(rv);
542                 }
543 
544                 for (; i < ucv_array_length(val); i++) {
545                         if (!uval_to_uci(vm, ucv_array_get(val, i), &ptr.value, NULL))
546                                 continue;
547 
548                         rv = uci_add_list(*c, &ptr);
549                         free((char *)ptr.value);
550 
551                         if (rv != UCI_OK)
552                                 err_return(rv);
553                 }
554         }
555         else {
556                 rv = uci_set(*c, &ptr);
557                 free((char *)ptr.value);
558 
559                 if (rv != UCI_OK)
560                         err_return(rv);
561         }
562 
563         return ucv_boolean_new(true);
564 }
565 
566 static uc_value_t *
567 uc_uci_delete(uc_vm_t *vm, size_t nargs)
568 {
569         struct uci_context **c = uc_fn_this("uci.cursor");
570         uc_value_t *conf = uc_fn_arg(0);
571         uc_value_t *sect = uc_fn_arg(1);
572         uc_value_t *opt = uc_fn_arg(2);
573         struct uci_ptr ptr = { 0 };
574         int rv;
575 
576         if (ucv_type(conf) != UC_STRING ||
577             ucv_type(sect) != UC_STRING ||
578             (opt && ucv_type(opt) != UC_STRING))
579             err_return(UCI_ERR_INVAL);
580 
581         ptr.package = ucv_string_get(conf);
582         ptr.section = ucv_string_get(sect);
583         ptr.option = opt ? ucv_string_get(opt) : NULL;
584 
585         rv = lookup_ptr(*c, &ptr, true);
586 
587         if (rv != UCI_OK)
588                 err_return(rv);
589 
590         if (opt ? !ptr.o : !ptr.s)
591                 err_return(UCI_ERR_NOTFOUND);
592 
593         rv = uci_delete(*c, &ptr);
594 
595         if (rv != UCI_OK)
596                 err_return(rv);
597 
598         return ucv_boolean_new(true);
599 }
600 
601 static uc_value_t *
602 uc_uci_rename(uc_vm_t *vm, size_t nargs)
603 {
604         struct uci_context **c = uc_fn_this("uci.cursor");
605         uc_value_t *conf = uc_fn_arg(0);
606         uc_value_t *sect = uc_fn_arg(1);
607         uc_value_t *opt = NULL, *val = NULL;
608         struct uci_ptr ptr = { 0 };
609         int rv;
610 
611         if (ucv_type(conf) != UC_STRING ||
612             ucv_type(sect) != UC_STRING)
613             err_return(UCI_ERR_INVAL);
614 
615         switch (nargs) {
616         /* conf, sect, opt, val */
617         case 4:
618                 opt = uc_fn_arg(2);
619                 val = uc_fn_arg(3);
620 
621                 if (ucv_type(opt) != UC_STRING ||
622                     ucv_type(val) != UC_STRING)
623                         err_return(UCI_ERR_INVAL);
624 
625                 break;
626 
627         /* conf, sect, type */
628         case 3:
629                 val = uc_fn_arg(2);
630 
631                 if (ucv_type(val) != UC_STRING)
632                         err_return(UCI_ERR_INVAL);
633 
634                 break;
635 
636         default:
637                 err_return(UCI_ERR_INVAL);
638         }
639 
640         ptr.package = ucv_string_get(conf);
641         ptr.section = ucv_string_get(sect);
642         ptr.option = opt ? ucv_string_get(opt) : NULL;
643         ptr.value = ucv_string_get(val);
644 
645         rv = lookup_ptr(*c, &ptr, true);
646 
647         if (rv != UCI_OK)
648                 err_return(rv);
649 
650         if (!ptr.s && ptr.option)
651                 err_return(UCI_ERR_NOTFOUND);
652 
653         rv = uci_rename(*c, &ptr);
654 
655         if (rv != UCI_OK)
656                 err_return(rv);
657 
658         return ucv_boolean_new(true);
659 }
660 
661 static uc_value_t *
662 uc_uci_reorder(uc_vm_t *vm, size_t nargs)
663 {
664         struct uci_context **c = uc_fn_this("uci.cursor");
665         uc_value_t *conf = uc_fn_arg(0);
666         uc_value_t *sect = uc_fn_arg(1);
667         uc_value_t *val = uc_fn_arg(2);
668         struct uci_ptr ptr = { 0 };
669         int64_t n;
670         int rv;
671 
672         if (ucv_type(conf) != UC_STRING ||
673             ucv_type(sect) != UC_STRING ||
674             ucv_type(val) != UC_INTEGER)
675             err_return(UCI_ERR_INVAL);
676 
677         n = ucv_int64_get(val);
678 
679         if (n < 0)
680                 err_return(UCI_ERR_INVAL);
681 
682         ptr.package = ucv_string_get(conf);
683         ptr.section = ucv_string_get(sect);
684 
685         rv = lookup_ptr(*c, &ptr, true);
686 
687         if (rv != UCI_OK)
688                 err_return(rv);
689 
690         if (!ptr.s)
691                 err_return(UCI_ERR_NOTFOUND);
692 
693         rv = uci_reorder_section(*c, ptr.s, n);
694 
695         if (rv != UCI_OK)
696                 err_return(rv);
697 
698         return ucv_boolean_new(true);
699 }
700 
701 static uc_value_t *
702 uc_uci_pkg_command(uc_vm_t *vm, size_t nargs, enum pkg_cmd cmd)
703 {
704         struct uci_context **c = uc_fn_this("uci.cursor");
705         uc_value_t *conf = uc_fn_arg(0);
706         struct uci_element *e, *tmp;
707         struct uci_package *p;
708         struct uci_ptr ptr = { 0 };
709         int rv, res = UCI_OK;
710 
711         if (cmd != CMD_REVERT && conf)
712                 err_return(UCI_ERR_INVAL);
713 
714         if (conf && ucv_type(conf) != UC_STRING)
715                 err_return(UCI_ERR_INVAL);
716 
717         uci_foreach_element_safe(&(*c)->root, tmp, e) {
718                 p = uci_to_package(e);
719 
720                 if (conf && strcmp(e->name, ucv_string_get(conf)))
721                         continue;
722 
723                 switch (cmd) {
724                 case CMD_COMMIT:
725                         rv = uci_commit(*c, &p, false);
726                         break;
727 
728                 case CMD_SAVE:
729                         rv = uci_save(*c, p);
730                         break;
731 
732                 case CMD_REVERT:
733                         ptr.p = p;
734                         rv = uci_revert(*c, &ptr);
735                         break;
736 
737                 default:
738                         rv = UCI_ERR_INVAL;
739                 }
740 
741                 if (rv != UCI_OK)
742                         res = rv;
743         }
744 
745         if (res != UCI_OK)
746                 err_return(res);
747 
748         return ucv_boolean_new(true);
749 }
750 
751 static uc_value_t *
752 uc_uci_save(uc_vm_t *vm, size_t nargs)
753 {
754         return uc_uci_pkg_command(vm, nargs, CMD_SAVE);
755 }
756 
757 static uc_value_t *
758 uc_uci_commit(uc_vm_t *vm, size_t nargs)
759 {
760         return uc_uci_pkg_command(vm, nargs, CMD_COMMIT);
761 }
762 
763 static uc_value_t *
764 uc_uci_revert(uc_vm_t *vm, size_t nargs)
765 {
766         return uc_uci_pkg_command(vm, nargs, CMD_REVERT);
767 }
768 
769 static uc_value_t *
770 change_to_uval(uc_vm_t *vm, struct uci_delta *d)
771 {
772         const char *types[] = {
773                 [UCI_CMD_REORDER]  = "order",
774                 [UCI_CMD_REMOVE]   = "remove",
775                 [UCI_CMD_RENAME]   = "rename",
776                 [UCI_CMD_ADD]      = "add",
777                 [UCI_CMD_LIST_ADD] = "list-add",
778                 [UCI_CMD_LIST_DEL] = "list-del",
779                 [UCI_CMD_CHANGE]   = "set",
780         };
781 
782         uc_value_t *a;
783 
784         if (!d->section)
785                 return NULL;
786 
787         a = ucv_array_new(vm);
788 
789         if (!a)
790                 return NULL;
791 
792         ucv_array_push(a, ucv_string_new(types[d->cmd]));
793         ucv_array_push(a, ucv_string_new(d->section));
794 
795         if (d->e.name)
796                 ucv_array_push(a, ucv_string_new(d->e.name));
797 
798         if (d->value) {
799                 if (d->cmd == UCI_CMD_REORDER)
800                         ucv_array_push(a, ucv_int64_new(strtoul(d->value, NULL, 10)));
801                 else
802                         ucv_array_push(a, ucv_string_new(d->value));
803         }
804 
805         return a;
806 }
807 
808 static uc_value_t *
809 changes_to_uval(uc_vm_t *vm, struct uci_context *ctx, const char *package)
810 {
811         uc_value_t *a = NULL, *c;
812         struct uci_package *p = NULL;
813         struct uci_element *e;
814         bool unload = false;
815 
816         uci_foreach_element(&ctx->root, e) {
817                 if (strcmp(e->name, package))
818                         continue;
819 
820                 p = uci_to_package(e);
821         }
822 
823         if (!p) {
824                 unload = true;
825                 uci_load(ctx, package, &p);
826         }
827 
828         if (!p)
829                 return NULL;
830 
831         if (!uci_list_empty(&p->delta) || !uci_list_empty(&p->saved_delta)) {
832                 a = ucv_array_new(vm);
833 
834                 if (!a)
835                         err_return(UCI_ERR_MEM);
836 
837                 uci_foreach_element(&p->saved_delta, e) {
838                         c = change_to_uval(vm, uci_to_delta(e));
839 
840                         if (c)
841                                 ucv_array_push(a, c);
842                 }
843 
844                 uci_foreach_element(&p->delta, e) {
845                         c = change_to_uval(vm, uci_to_delta(e));
846 
847                         if (c)
848                                 ucv_array_push(a, c);
849                 }
850         }
851 
852         if (unload)
853                 uci_unload(ctx, p);
854 
855         return a;
856 }
857 
858 static uc_value_t *
859 uc_uci_changes(uc_vm_t *vm, size_t nargs)
860 {
861         struct uci_context **c = uc_fn_this("uci.cursor");
862         uc_value_t *conf = uc_fn_arg(0);
863         uc_value_t *res, *chg;
864         char **configs;
865         int rv, i;
866 
867         if (conf && ucv_type(conf) != UC_STRING)
868                 err_return(UCI_ERR_INVAL);
869 
870         rv = uci_list_configs(*c, &configs);
871 
872         if (rv != UCI_OK)
873                 err_return(rv);
874 
875         res = ucv_object_new(vm);
876 
877         for (i = 0; configs[i]; i++) {
878                 if (conf && strcmp(configs[i], ucv_string_get(conf)))
879                         continue;
880 
881                 chg = changes_to_uval(vm, *c, configs[i]);
882 
883                 if (chg)
884                         ucv_object_add(res, configs[i], chg);
885         }
886 
887         free(configs);
888 
889         return res;
890 }
891 
892 static uc_value_t *
893 uc_uci_foreach(uc_vm_t *vm, size_t nargs)
894 {
895         struct uci_context **c = uc_fn_this("uci.cursor");
896         uc_value_t *conf = uc_fn_arg(0);
897         uc_value_t *type = uc_fn_arg(1);
898         uc_value_t *func = uc_fn_arg(2);
899         uc_value_t *rv = NULL;
900         struct uci_package *p = NULL;
901         struct uci_element *e, *tmp;
902         struct uci_section *sc;
903         uc_exception_type_t ex;
904         bool stop = false;
905         bool ret = false;
906         int i = 0;
907 
908         if (ucv_type(conf) != UC_STRING ||
909             (type && ucv_type(type) != UC_STRING))
910             err_return(UCI_ERR_INVAL);
911 
912         uci_foreach_element(&(*c)->root, e) {
913                 if (strcmp(e->name, ucv_string_get(conf)))
914                         continue;
915 
916                 p = uci_to_package(e);
917                 break;
918         }
919 
920         if (!p)
921                 err_return(UCI_ERR_NOTFOUND);
922 
923         uci_foreach_element_safe(&p->sections, tmp, e) {
924                 sc = uci_to_section(e);
925                 i++;
926 
927                 if (type && strcmp(sc->type, ucv_string_get(type)))
928                         continue;
929 
930                 uc_value_push(ucv_get(func));
931                 uc_value_push(section_to_uval(vm, sc, i - 1));
932 
933                 ex = uc_call(1);
934 
935                 /* stop on exception in callback */
936                 if (ex)
937                         break;
938 
939                 ret = true;
940                 rv = uc_value_pop();
941                 stop = (ucv_type(rv) == UC_BOOLEAN && !ucv_boolean_get(rv));
942 
943                 ucv_put(rv);
944 
945                 if (stop)
946                         break;
947         }
948 
949         /* XXX: rethrow */
950 
951         return ucv_boolean_new(ret);
952 }
953 
954 static uc_value_t *
955 uc_uci_configs(uc_vm_t *vm, size_t nargs)
956 {
957         struct uci_context **c = uc_fn_this("uci.cursor");
958         uc_value_t *a;
959         char **configs;
960         int i, rv;
961 
962         rv = uci_list_configs(*c, &configs);
963 
964         if (rv != UCI_OK)
965                 err_return(rv);
966 
967         a = ucv_array_new(vm);
968 
969         for (i = 0; configs[i]; i++)
970                 ucv_array_push(a, ucv_string_new(configs[i]));
971 
972         free(configs);
973 
974         return a;
975 }
976 
977 
978 static const uc_function_list_t cursor_fns[] = {
979         { "load",               uc_uci_load },
980         { "unload",             uc_uci_unload },
981         { "get",                uc_uci_get },
982         { "get_all",    uc_uci_get_all },
983         { "get_first",  uc_uci_get_first },
984         { "add",                uc_uci_add },
985         { "set",                uc_uci_set },
986         { "rename",             uc_uci_rename },
987         { "save",               uc_uci_save },
988         { "delete",             uc_uci_delete },
989         { "commit",             uc_uci_commit },
990         { "revert",             uc_uci_revert },
991         { "reorder",    uc_uci_reorder },
992         { "changes",    uc_uci_changes },
993         { "foreach",    uc_uci_foreach },
994         { "configs",    uc_uci_configs },
995         { "error",              uc_uci_error },
996 };
997 
998 static const uc_function_list_t global_fns[] = {
999         { "error",              uc_uci_error },
1000         { "cursor",             uc_uci_cursor },
1001 };
1002 
1003 
1004 static void close_uci(void *ud) {
1005         uci_free_context((struct uci_context *)ud);
1006 }
1007 
1008 void uc_module_init(uc_vm_t *vm, uc_value_t *scope)
1009 {
1010         uc_function_list_register(scope, global_fns);
1011 
1012         cursor_type = uc_type_declare(vm, "uci.cursor", cursor_fns, close_uci);
1013 }
1014 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt