==> src/views.c <==1/* crates/mesh-top/src/views.rs, ported. */
2#include "mesh/top_views.h"
3
4#include <ctype.h>
5#include <errno.h>
6#include <math.h>
7#include <stdlib.h>
8#include <string.h>
9
10static char *dup(const char *s, size_t n) {
11 char *d = malloc(n + 1);
12 if (!d) abort();
13 memcpy(d, s, n);
14 d[n] = 0;
15 return d;
16}
17
18const char *mt_vrec_get(const mt_vrec *r, const char *k) {
19 for (size_t i = 0; i < r->nkv; i++)
20 if (strcmp(r->kv[i].key, k) == 0) return strcmp(r->kv[i].value, "-") == 0 ? NULL : r->kv[i].value;
21 return NULL;
22}
23
24const char *mt_vrec_s(const mt_vrec *r, const char *k) {
25 const char *v = mt_vrec_get(r, k);
26 return v ? v : "-";
27}
28
29bool mt_parse_u64(const char *s, uint64_t *out) {
30 const char *p = s;
31 if (*p == '+') p++;
32 if (!*p) return false;
33 uint64_t v = 0;
34 for (; *p; p++) {
35 if (*p < '0' || *p > '9') return false;
36 uint64_t d = (uint64_t)(*p - '0');
37 if (v > (UINT64_MAX - d) / 10) return false;
38 v = v * 10 + d;
39 }
40 *out = v;
41 return true;
42}
43
44static bool ieq(const char *a, const char *b) {
45 for (; *a && *b; a++, b++)
46 if (tolower((unsigned char)*a) != tolower((unsigned char)*b)) return false;
47 return !*a && !*b;
48}
49
50bool mt_parse_f64(const char *s, double *out) {
51 const char *p = s;
52 bool neg = false;
53 if (*p == '+' || *p == '-') neg = *p++ == '-';
54 if (ieq(p, "inf") || ieq(p, "infinity")) {
55 *out = neg ? -HUGE_VAL : HUGE_VAL;
56 return true;
57 }
58 if (ieq(p, "nan")) {
59 *out = (double)NAN;
60 return true;
61 }
62 /* digits [. digits] | . digits, then an optional exponent: Rust's grammar (no hex). */
63 const char *q = p;
64 size_t ip = 0, fp = 0;
65 while (*q >= '0' && *q <= '9') q++, ip++;
66 if (*q == '.') {
67 q++;
68 while (*q >= '0' && *q <= '9') q++, fp++;
69 }
70 if (ip + fp == 0) return false;
71 if (*q == 'e' || *q == 'E') {
72 q++;
73 if (*q == '+' || *q == '-') q++;
74 if (!(*q >= '0' && *q <= '9')) return false;
75 while (*q >= '0' && *q <= '9') q++;
76 }
77 if (*q) return false;
78 errno = 0;
79 *out = strtod(s, NULL);
80 return true;
81}
82
83bool mt_vrec_u(const mt_vrec *r, const char *k, uint64_t *out) {
84 const char *v = mt_vrec_get(r, k);
85 return v && mt_parse_u64(v, out);
86}
87
88bool mt_vrec_f(const mt_vrec *r, const char *k, double *out) {
89 const char *v = mt_vrec_get(r, k);
90 return v && mt_parse_f64(v, out);
91}
92
93bool mt_vrec_flag(const mt_vrec *r, const char *k) {
94 const char *v = mt_vrec_get(r, k);
95 return v && strcmp(v, "1") == 0;
96}
97
98size_t mt_vrec_list(const mt_vrec *r, const char *k, uint32_t *out, size_t cap) {
99 const char *v = mt_vrec_get(r, k);
100 size_t n = 0;
101 if (!v) return 0;
102 while (*v && n < cap) {
103 const char *c = strchr(v, ',');
104 size_t len = c ? (size_t)(c - v) : strlen(v);
105 char tmp[32];
106 if (len < sizeof tmp) {
107 memcpy(tmp, v, len);
108 tmp[len] = 0;
109 uint64_t x;
110 if (mt_parse_u64(tmp, &x) && x <= UINT32_MAX) out[n++] = (uint32_t)x;
111 }
112 v += len + (c ? 1 : 0);
113 if (!c) break;
114 }
115 return n;
116}
117
118static bool is_ws(unsigned char c) { return c == ' ' || (c >= '\t' && c <= '\r'); }
119
120size_t mt_views_parse(const char *t, mt_vrec **out) {
121 size_t n = 0, cap = 0;
122 mt_vrec *recs = NULL;
123 size_t len = strlen(t);
124 for (size_t start = 0; start < len;) {
125 const char *nl = memchr(t + start, '\n', len - start);
126 size_t end = nl ? (size_t)(nl - t) : len, e = end;
127 if (nl && e > start && t[e - 1] == '\r') e--;
128 const char *line = t + start;
129 size_t ll = e - start;
130 start = nl ? end + 1 : len;
131 bool blank = true;
132 for (size_t i = 0; i < ll; i++)
133 if (!is_ws((unsigned char)line[i])) blank = false;
134 if (blank) continue;
135 size_t hl = ll;
136 const char *text = "";
137 size_t tl = 0;
138 for (size_t i = 0; i + 4 <= ll; i++)
139 if (memcmp(line + i, " -- ", 4) == 0) {
140 hl = i;
141 text = line + i + 4;
142 tl = ll - i - 4;
143 break;
144 }
145 if (n == cap) {
146 cap = cap ? cap * 2 : 16;
147 recs = realloc(recs, cap * sizeof *recs);
148 if (!recs) abort();
149 }
150 mt_vrec r = {NULL, NULL, 0, dup(text, tl)};
151 size_t kvcap = 0;
152 for (size_t i = 0; i < hl;) {
153 while (i < hl && is_ws((unsigned char)line[i])) i++;
154 if (i >= hl) break;
155 size_t w = i;
156 while (i < hl && !is_ws((unsigned char)line[i])) i++;
157 if (!r.kind) {
158 r.kind = dup(line + w, i - w);
159 continue;
160 }
161 const char *eq = memchr(line + w, '=', i - w);
162 if (r.nkv == kvcap) {
163 kvcap = kvcap ? kvcap * 2 : 8;
164 r.kv = realloc(r.kv, kvcap * sizeof *r.kv);
165 if (!r.kv) abort();
166 }
167 if (eq) r.kv[r.nkv++] = (mt_kv){dup(line + w, (size_t)(eq - (line + w))), dup(eq + 1, i - (size_t)(eq - line) - 1)};
168 else r.kv[r.nkv++] = (mt_kv){dup(line + w, i - w), dup("", 0)};
169 }
170 if (!r.kind) r.kind = dup("", 0);
171 recs[n++] = r;
172 }
173 *out = recs;
174 return n;
175}
176
177void mt_vrecs_free(mt_vrec *r, size_t n) {
178 for (size_t i = 0; i < n; i++) {
179 free(r[i].kind);
180 free(r[i].text);
181 for (size_t k = 0; k < r[i].nkv; k++) {
182 free(r[i].kv[k].key);
183 free(r[i].kv[k].value);
184 }
185 free(r[i].kv);
186 }
187 free(r);
188}
189
190mt_view mt_view_new(size_t machine, mt_instant at, const char *text) {
191 mt_view v = {machine, at, NULL, 0, NULL};
192 if (strncmp(text, "err ", 4) == 0) {
193 const char *s = text + 4;
194 size_t l = strlen(s);
195 while (l && is_ws((unsigned char)s[l - 1])) l--;
196 while (l && is_ws((unsigned char)*s)) s++, l--;
197 v.err = dup(s, l);
198 return v;
199 }
200 v.n = mt_views_parse(text, &v.recs);
201 return v;
202}
203
204void mt_view_free(mt_view *v) {
205 mt_vrecs_free(v->recs, v->n);
206 free(v->err);
207 v->recs = NULL;
208 v->n = 0;
209 v->err = NULL;
210}
211
212const mt_vrec *mt_view_first(const mt_view *v, const char *kind) { return mt_view_nth(v, kind, 0); }
213
214const mt_vrec *mt_view_nth(const mt_view *v, const char *kind, size_t i) {
215 for (size_t k = 0; k < v->n; k++)
216 if (strcmp(v->recs[k].kind, kind) == 0 && i-- == 0) return &v->recs[k];
217 return NULL;
218}
219
220size_t mt_view_count(const mt_view *v, const char *kind) {
221 size_t c = 0;
222 for (size_t k = 0; k < v->n; k++) c += strcmp(v->recs[k].kind, kind) == 0;
223 return c;
224}