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}