+static bool
+dt_type_regex(struct dt_state *s, int nargs)
+{
+ bool rv;
+ int relen;
+ regex_t pattern;
+ char *re = NULL;
+
+ if (nargs < 1 || s->stack[s->pos].type != OP_STRING)
+ return false;
+
+ relen = s->stack[s->pos].length;
+ re = alloca(relen + 3);
+
+ if (!re)
+ return false;
+
+ memset(re, 0, relen + 3);
+ memcpy(re + 1, s->stack[s->pos].value.string, relen);
+
+ re[0] = '^';
+ re[relen + 1] = '$';
+
+ if (regcomp(&pattern, re, REG_EXTENDED | REG_NOSUB))
+ return false;
+
+ rv = !regexec(&pattern, s->value, 0, NULL, 0);
+
+ regfree(&pattern);
+
+ return rv;
+}
+
+static void *
+dt_uci_lookup(struct dt_state *s, const char *pkg,
+ const char *sct, const char *opt, enum uci_type type)
+{
+ struct uci_ptr ptr = {
+ .package = pkg,
+ .section = sct,
+ .option = opt
+ };
+
+ if (!s->ctx || uci_lookup_ptr(s->ctx, &ptr, NULL, false) ||
+ !(ptr.flags & UCI_LOOKUP_COMPLETE))
+ return NULL;
+
+ if (ptr.last->type != type)
+ return NULL;
+
+ switch (type)
+ {
+ case UCI_TYPE_PACKAGE:
+ return uci_to_package(ptr.last);
+
+ case UCI_TYPE_SECTION:
+ return uci_to_section(ptr.last);
+
+ case UCI_TYPE_OPTION:
+ return uci_to_option(ptr.last);
+
+ default:
+ return NULL;
+ }
+}
+
+static bool
+dt_uci_cmp(struct dt_state *s,
+ const char *pkg, const char *sct, const char *opt)
+{
+ struct uci_element *e;
+ struct uci_option *o = dt_uci_lookup(s, pkg, sct, opt, UCI_TYPE_OPTION);
+
+ if (!o)
+ return false;
+
+ switch (o->type)
+ {
+ case UCI_TYPE_STRING:
+ if (!strcmp(s->value, o->v.string))
+ return true;
+ break;
+
+ case UCI_TYPE_LIST:
+ uci_foreach_element(&o->v.list, e)
+ if (!strcmp(s->value, e->name))
+ return true;
+ break;
+ }
+
+ return false;
+}
+
+static bool
+dt_type_uci(struct dt_state *s, int nargs)
+{
+ int i, len;
+ struct uci_element *e;
+ struct uci_package *p;
+ char *cso[3] = { };
+
+ if (!s->ctx)
+ return false;
+
+ for (i = 0; i < nargs && i < 3; i++)
+ {
+ if (s->stack[s->pos + i].type != OP_STRING)
+ continue;
+
+ len = s->stack[s->pos + i].length;
+ cso[i] = alloca(len + 1);
+
+ if (!cso[i])
+ continue;
+
+ memset(cso[i], 0, len + 1);
+ memcpy(cso[i], s->stack[s->pos + i].value.string, len);
+ }
+
+ if (!cso[0] || !cso[1] || (*cso[1] != '@' && !cso[2]))
+ return false;
+
+ if (*cso[1] != '@')
+ return dt_uci_cmp(s, cso[0], cso[1], cso[2]);
+
+ p = dt_uci_lookup(s, cso[0], NULL, NULL, UCI_TYPE_PACKAGE);
+
+ if (!p)
+ return false;
+
+ uci_foreach_element(&p->sections, e)
+ {
+ if (strcmp(uci_to_section(e)->type, cso[1] + 1))
+ continue;
+
+ if (!cso[2])
+ {
+ if (!strcmp(s->value, e->name))
+ return true;
+ }
+ else
+ {
+ if (dt_uci_cmp(s, cso[0], e->name, cso[2]))
+ return true;
+ }
+ }
+
+ return false;
+}
+