• 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 && uci_load(*c, ucv_string_get(conf), &p))
350                 err_return((*c)->err);
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 int
702 uc_uci_pkg_command_single(struct uci_context *ctx, enum pkg_cmd cmd,
703                           struct uci_package *pkg)
704 {
705         struct uci_ptr ptr = { 0 };
706 
707         switch (cmd) {
708         case CMD_COMMIT:
709                 return uci_commit(ctx, &pkg, false);
710 
711         case CMD_SAVE:
712                 return uci_save(ctx, pkg);
713 
714         case CMD_REVERT:
715                 ptr.p = pkg;
716 
717                 return uci_revert(ctx, &ptr);
718 
719         default:
720                 return UCI_ERR_INVAL;
721         }
722 }
723 
724 static uc_value_t *
725 uc_uci_pkg_command(uc_vm_t *vm, size_t nargs, enum pkg_cmd cmd)
726 {
727         struct uci_context **c = uc_fn_this("uci.cursor");
728         uc_value_t *conf = uc_fn_arg(0);
729         struct uci_package *p;
730         char **configs = NULL;
731         int rv, res = UCI_OK;
732         size_t i;
733 
734         if (conf) {
735                 if (ucv_type(conf) != UC_STRING)
736                         err_return(UCI_ERR_INVAL);
737 
738                 if (!(p = uci_lookup_package(*c, ucv_string_get(conf))))
739                         err_return(UCI_ERR_NOTFOUND);
740 
741                 res = uc_uci_pkg_command_single(*c, cmd, p);
742         }
743         else {
744                 if (uci_list_configs(*c, &configs))
745                         err_return((*c)->err);
746 
747                 if (!configs || !configs[0])
748                         err_return(UCI_ERR_NOTFOUND);
749 
750                 for (i = 0; configs[i]; i++) {
751                         if (!(p = uci_lookup_package(*c, configs[i])))
752                                 continue;
753 
754                         rv = uc_uci_pkg_command_single(*c, cmd, p);
755 
756                         if (rv != UCI_OK)
757                                 res = rv;
758                 }
759 
760                 free(configs);
761         }
762 
763         if (res != UCI_OK)
764                 err_return(res);
765 
766         return ucv_boolean_new(true);
767 }
768 
769 static uc_value_t *
770 uc_uci_save(uc_vm_t *vm, size_t nargs)
771 {
772         return uc_uci_pkg_command(vm, nargs, CMD_SAVE);
773 }
774 
775 static uc_value_t *
776 uc_uci_commit(uc_vm_t *vm, size_t nargs)
777 {
778         return uc_uci_pkg_command(vm, nargs, CMD_COMMIT);
779 }
780 
781 static uc_value_t *
782 uc_uci_revert(uc_vm_t *vm, size_t nargs)
783 {
784         return uc_uci_pkg_command(vm, nargs, CMD_REVERT);
785 }
786 
787 static uc_value_t *
788 change_to_uval(uc_vm_t *vm, struct uci_delta *d)
789 {
790         const char *types[] = {
791                 [UCI_CMD_REORDER]  = "order",
792                 [UCI_CMD_REMOVE]   = "remove",
793                 [UCI_CMD_RENAME]   = "rename",
794                 [UCI_CMD_ADD]      = "add",
795                 [UCI_CMD_LIST_ADD] = "list-add",
796                 [UCI_CMD_LIST_DEL] = "list-del",
797                 [UCI_CMD_CHANGE]   = "set",
798         };
799 
800         uc_value_t *a;
801 
802         if (!d->section)
803                 return NULL;
804 
805         a = ucv_array_new(vm);
806 
807         if (!a)
808                 return NULL;
809 
810         ucv_array_push(a, ucv_string_new(types[d->cmd]));
811         ucv_array_push(a, ucv_string_new(d->section));
812 
813         if (d->e.name)
814                 ucv_array_push(a, ucv_string_new(d->e.name));
815 
816         if (d->value) {
817                 if (d->cmd == UCI_CMD_REORDER)
818                         ucv_array_push(a, ucv_int64_new(strtoul(d->value, NULL, 10)));
819                 else
820                         ucv_array_push(a, ucv_string_new(d->value));
821         }
822 
823         return a;
824 }
825 
826 static uc_value_t *
827 changes_to_uval(uc_vm_t *vm, struct uci_context *ctx, const char *package)
828 {
829         uc_value_t *a = NULL, *c;
830         struct uci_package *p = NULL;
831         struct uci_element *e;
832         bool unload = false;
833 
834         uci_foreach_element(&ctx->root, e) {
835                 if (strcmp(e->name, package))
836                         continue;
837 
838                 p = uci_to_package(e);
839         }
840 
841         if (!p) {
842                 unload = true;
843                 uci_load(ctx, package, &p);
844         }
845 
846         if (!p)
847                 return NULL;
848 
849         if (!uci_list_empty(&p->delta) || !uci_list_empty(&p->saved_delta)) {
850                 a = ucv_array_new(vm);
851 
852                 if (!a)
853                         err_return(UCI_ERR_MEM);
854 
855                 uci_foreach_element(&p->saved_delta, e) {
856                         c = change_to_uval(vm, uci_to_delta(e));
857 
858                         if (c)
859                                 ucv_array_push(a, c);
860                 }
861 
862                 uci_foreach_element(&p->delta, e) {
863                         c = change_to_uval(vm, uci_to_delta(e));
864 
865                         if (c)
866                                 ucv_array_push(a, c);
867                 }
868         }
869 
870         if (unload)
871                 uci_unload(ctx, p);
872 
873         return a;
874 }
875 
876 static uc_value_t *
877 uc_uci_changes(uc_vm_t *vm, size_t nargs)
878 {
879         struct uci_context **c = uc_fn_this("uci.cursor");
880         uc_value_t *conf = uc_fn_arg(0);
881         uc_value_t *res, *chg;
882         char **configs;
883         int rv, i;
884 
885         if (conf && ucv_type(conf) != UC_STRING)
886                 err_return(UCI_ERR_INVAL);
887 
888         rv = uci_list_configs(*c, &configs);
889 
890         if (rv != UCI_OK)
891                 err_return(rv);
892 
893         res = ucv_object_new(vm);
894 
895         for (i = 0; configs[i]; i++) {
896                 if (conf && strcmp(configs[i], ucv_string_get(conf)))
897                         continue;
898 
899                 chg = changes_to_uval(vm, *c, configs[i]);
900 
901                 if (chg)
902                         ucv_object_add(res, configs[i], chg);
903         }
904 
905         free(configs);
906 
907         return res;
908 }
909 
910 static uc_value_t *
911 uc_uci_foreach(uc_vm_t *vm, size_t nargs)
912 {
913         struct uci_context **c = uc_fn_this("uci.cursor");
914         uc_value_t *conf = uc_fn_arg(0);
915         uc_value_t *type = uc_fn_arg(1);
916         uc_value_t *func = uc_fn_arg(2);
917         uc_value_t *rv = NULL;
918         struct uci_package *p = NULL;
919         struct uci_element *e, *tmp;
920         struct uci_section *sc;
921         uc_exception_type_t ex;
922         bool stop = false;
923         bool ret = false;
924         int i = 0;
925 
926         if (ucv_type(conf) != UC_STRING ||
927             (type && ucv_type(type) != UC_STRING))
928             err_return(UCI_ERR_INVAL);
929 
930         uci_foreach_element(&(*c)->root, e) {
931                 if (strcmp(e->name, ucv_string_get(conf)))
932                         continue;
933 
934                 p = uci_to_package(e);
935                 break;
936         }
937 
938         if (!p && uci_load(*c, ucv_string_get(conf), &p))
939                 err_return((*c)->err);
940 
941         uci_foreach_element_safe(&p->sections, tmp, e) {
942                 sc = uci_to_section(e);
943                 i++;
944 
945                 if (type && strcmp(sc->type, ucv_string_get(type)))
946                         continue;
947 
948                 uc_value_push(ucv_get(func));
949                 uc_value_push(section_to_uval(vm, sc, i - 1));
950 
951                 ex = uc_call(1);
952 
953                 /* stop on exception in callback */
954                 if (ex)
955                         break;
956 
957                 ret = true;
958                 rv = uc_value_pop();
959                 stop = (ucv_type(rv) == UC_BOOLEAN && !ucv_boolean_get(rv));
960 
961                 ucv_put(rv);
962 
963                 if (stop)
964                         break;
965         }
966 
967         return ucv_boolean_new(ret);
968 }
969 
970 static uc_value_t *
971 uc_uci_configs(uc_vm_t *vm, size_t nargs)
972 {
973         struct uci_context **c = uc_fn_this("uci.cursor");
974         uc_value_t *a;
975         char **configs;
976         int i, rv;
977 
978         rv = uci_list_configs(*c, &configs);
979 
980         if (rv != UCI_OK)
981                 err_return(rv);
982 
983         a = ucv_array_new(vm);
984 
985         for (i = 0; configs[i]; i++)
986                 ucv_array_push(a, ucv_string_new(configs[i]));
987 
988         free(configs);
989 
990         return a;
991 }
992 
993 
994 static const uc_function_list_t cursor_fns[] = {
995         { "load",               uc_uci_load },
996         { "unload",             uc_uci_unload },
997         { "get",                uc_uci_get },
998         { "get_all",    uc_uci_get_all },
999         { "get_first",  uc_uci_get_first },
1000         { "add",                uc_uci_add },
1001         { "set",                uc_uci_set },
1002         { "rename",             uc_uci_rename },
1003         { "save",               uc_uci_save },
1004         { "delete",             uc_uci_delete },
1005         { "commit",             uc_uci_commit },
1006         { "revert",             uc_uci_revert },
1007         { "reorder",    uc_uci_reorder },
1008         { "changes",    uc_uci_changes },
1009         { "foreach",    uc_uci_foreach },
1010         { "configs",    uc_uci_configs },
1011         { "error",              uc_uci_error },
1012 };
1013 
1014 static const uc_function_list_t global_fns[] = {
1015         { "error",              uc_uci_error },
1016         { "cursor",             uc_uci_cursor },
1017 };
1018 
1019 
1020 static void close_uci(void *ud) {
1021         uci_free_context((struct uci_context *)ud);
1022 }
1023 
1024 void uc_module_init(uc_vm_t *vm, uc_value_t *scope)
1025 {
1026         uc_function_list_register(scope, global_fns);
1027 
1028         cursor_type = uc_type_declare(vm, "uci.cursor", cursor_fns, close_uci);
1029 }
1030 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt