master xplshn/aruu / cmd / dev / ninqu / kv.c
  1#include "ninqu.h"
  2
  3#include <stdio.h>
  4#include <stdlib.h>
  5#include <string.h>
  6
  7struct KvStore  kv;
  8struct KvStore *local_overlay;
  9struct StrList  kv_locked;
 10struct Map     *maps;
 11int             nmaps, mapscap;
 12
 13char *
 14kv_get_raw(struct KvStore *s, const char *key)
 15{
 16  int i;
 17  for (i = 0; i < s->n; i++)
 18    if (strcmp(s->v[i].key, key) == 0)
 19      return s->v[i].val;
 20  return NULL;
 21}
 22
 23void
 24kv_set_raw(struct KvStore *s, const char *key, const char *val)
 25{
 26  int i;
 27  for (i = 0; i < s->n; i++) {
 28    if (strcmp(s->v[i].key, key) == 0) {
 29      free(s->v[i].val);
 30      s->v[i].val = estrdup(val);
 31      return;
 32    }
 33  }
 34  GROW(s->v, s->n, s->cap, 16);
 35  s->v[s->n].key = estrdup(key);
 36  s->v[s->n].val = estrdup(val);
 37  s->n++;
 38}
 39
 40/* overlay beats global beats environ, so a per-match $(BASESTEM) can
 41 * shadow a (set BASESTEM ...) without clobbering it for other matches */
 42char *
 43kv_get(const char *key)
 44{
 45  char *v;
 46  if (local_overlay) {
 47    v = kv_get_raw(local_overlay, key);
 48    if (v)
 49      return v;
 50  }
 51  v = kv_get_raw(&kv, key);
 52  if (v)
 53    return v;
 54  return getenv(key);
 55}
 56
 57const char *
 58kv_get_or(const char *key, const char *def)
 59{
 60  char *v = kv_get(key);
 61  return v ? v : def;
 62}
 63
 64void
 65kv_set(const char *key, const char *val)
 66{
 67  kv_set_raw(&kv, key, val);
 68}
 69
 70/* -D on the cli locks the name so a later (set) in the file cannot
 71 * overwrite it */
 72void
 73kv_set_manifest(const char *key, const char *val)
 74{
 75  if (sl_has(&kv_locked, key))
 76    return;
 77  kv_set(key, val);
 78}
 79
 80void
 81kv_set_cli(const char *key, const char *val)
 82{
 83  kv_set(key, val);
 84  sl_push(&kv_locked, key);
 85}
 86
 87struct Map *
 88map_find(const char *name)
 89{
 90  int i;
 91  for (i = 0; i < nmaps; i++)
 92    if (strcmp(maps[i].name, name) == 0)
 93      return &maps[i];
 94  return NULL;
 95}
 96
 97struct Map *
 98map_new(const char *name)
 99{
100  struct Map *m;
101  GROW(maps, nmaps, mapscap, 8);
102  m = &maps[nmaps++];
103  memset(m, 0, sizeof *m);
104  strlcpy(m->name, name, sizeof m->name);
105  return m;
106}
107
108/* $(NAME) and $(NAME:KEY). KEY is expanded first, the map value is
109 * re-expanded once so $(LDLIBS_TLS) inside a map entry resolves. the
110 * result is not recursively expanded, matching simply-expanded vars */
111char *
112kv_expand(const char *s)
113{
114  size_t      cap = strlen(s) * 2 + 64, len = 0;
115  char       *out = emalloc(cap);
116  const char *p   = s;
117
118  while (*p) {
119    if (p[0] == '$' && p[1] == '(') {
120      const char *inner        = p + 2;
121      const char *close        = NULL;
122      char       *val          = NULL;
123      char       *expanded_val = NULL;
124
125      close = find_dollar_close(inner);
126
127      if (close) {
128        char   name[4096];
129        size_t nl = (size_t)(close - inner);
130        char  *colon;
131
132        if (nl >= sizeof name)
133          nl = sizeof name - 1;
134        memcpy(name, inner, nl);
135        name[nl] = '\0';
136
137        colon = strchr(name, ':');
138        if (colon) {
139          char        mapname[128];
140          char       *keypart;
141          struct Map *m;
142          size_t      mnl = (size_t)(colon - name);
143
144          if (mnl >= sizeof mapname)
145            mnl = sizeof mapname - 1;
146          memcpy(mapname, name, mnl);
147          mapname[mnl] = '\0';
148          m            = map_find(mapname);
149          keypart      = kv_expand(colon + 1);
150          if (m) {
151            char *raw = kv_get_raw(&m->entries, keypart);
152            if (raw)
153              expanded_val = kv_expand(raw);
154          }
155          free(keypart);
156          val = expanded_val;
157        } else {
158          val = kv_get(name);
159        }
160
161        if (!val)
162          val = "";
163        while (len + strlen(val) + 1 >= cap) {
164          cap *= 2;
165          out = erealloc(out, cap);
166        }
167        memcpy(out + len, val, strlen(val));
168        len += strlen(val);
169        free(expanded_val);
170        p = close + 1;
171        continue;
172      }
173    }
174    if (len + 2 >= cap) {
175      cap *= 2;
176      out = erealloc(out, cap);
177    }
178    out[len++] = *p++;
179  }
180  out[len] = '\0';
181  return out;
182}