/src/frr/lib/printf/glue.c
Line | Count | Source |
1 | | // SPDX-License-Identifier: ISC |
2 | | /* |
3 | | * Copyright (c) 2019 David Lamparter, for NetDEF, Inc. |
4 | | */ |
5 | | |
6 | | #ifdef HAVE_CONFIG_H |
7 | | #include "config.h" |
8 | | #endif |
9 | | |
10 | | #include <sys/types.h> |
11 | | #include <string.h> |
12 | | #include <wchar.h> |
13 | | |
14 | | #include "printfrr.h" |
15 | | #include "printflocal.h" |
16 | | |
17 | | ssize_t bprintfrr(struct fbuf *out, const char *fmt, ...) |
18 | 303k | { |
19 | 303k | ssize_t ret; |
20 | 303k | va_list ap; |
21 | | |
22 | 303k | va_start(ap, fmt); |
23 | 303k | ret = vbprintfrr(out, fmt, ap); |
24 | 303k | va_end(ap); |
25 | 303k | return ret; |
26 | 303k | } |
27 | | |
28 | | ssize_t vsnprintfrr(char *out, size_t outsz, const char *fmt, va_list ap) |
29 | 0 | { |
30 | 0 | struct fbuf fbb = { .buf = out, .pos = out, .len = outsz - 1, }; |
31 | 0 | struct fbuf *fb = (out && outsz) ? &fbb : NULL; |
32 | 0 | ssize_t ret; |
33 | |
|
34 | 0 | ret = vbprintfrr(fb, fmt, ap); |
35 | 0 | if (fb) |
36 | 0 | fb->pos[0] = '\0'; |
37 | 0 | return ret; |
38 | 0 | } |
39 | | |
40 | | ssize_t snprintfrr(char *out, size_t outsz, const char *fmt, ...) |
41 | 135k | { |
42 | 135k | struct fbuf fbb = { .buf = out, .pos = out, .len = outsz - 1, }; |
43 | 135k | struct fbuf *fb = (out && outsz) ? &fbb : NULL; |
44 | 135k | ssize_t ret; |
45 | 135k | va_list ap; |
46 | | |
47 | 135k | va_start(ap, fmt); |
48 | 135k | ret = vbprintfrr(fb, fmt, ap); |
49 | 135k | va_end(ap); |
50 | 135k | if (fb) |
51 | 135k | fb->pos[0] = '\0'; |
52 | 135k | return ret; |
53 | 135k | } |
54 | | |
55 | | ssize_t vcsnprintfrr(char *out, size_t outsz, const char *fmt, va_list ap) |
56 | 0 | { |
57 | 0 | if (!out || !outsz) |
58 | 0 | return vbprintfrr(NULL, fmt, ap); |
59 | | |
60 | 0 | struct fbuf fbb = { .buf = out, .pos = out, .len = outsz - 1, }; |
61 | 0 | ssize_t ret; |
62 | 0 | size_t pos; |
63 | |
|
64 | 0 | pos = strnlen(out, outsz); |
65 | 0 | fbb.pos += pos; |
66 | |
|
67 | 0 | ret = vbprintfrr(&fbb, fmt, ap); |
68 | 0 | fbb.pos[0] = '\0'; |
69 | 0 | return ret >= 0 ? ret + (ssize_t)pos : ret; |
70 | 0 | } |
71 | | |
72 | | ssize_t csnprintfrr(char *out, size_t outsz, const char *fmt, ...) |
73 | 0 | { |
74 | 0 | ssize_t ret; |
75 | 0 | va_list ap; |
76 | |
|
77 | 0 | va_start(ap, fmt); |
78 | 0 | ret = vcsnprintfrr(out, outsz, fmt, ap); |
79 | 0 | va_end(ap); |
80 | 0 | return ret; |
81 | 0 | } |
82 | | |
83 | | char *vasnprintfrr(struct memtype *mt, char *out, size_t outsz, const char *fmt, |
84 | | va_list ap) |
85 | 0 | { |
86 | 0 | struct fbuf fb = { .buf = out, .pos = out, .len = outsz - 1, }; |
87 | 0 | ssize_t len; |
88 | 0 | va_list ap2; |
89 | 0 | char *ret = out; |
90 | |
|
91 | 0 | va_copy(ap2, ap); |
92 | 0 | len = vbprintfrr(&fb, fmt, ap); |
93 | 0 | if (len < 0) { |
94 | 0 | va_end(ap2); |
95 | | /* error = malformed format string => try something useful */ |
96 | 0 | return qstrdup(mt, fmt); |
97 | 0 | } |
98 | | |
99 | 0 | if ((size_t)len >= outsz - 1) { |
100 | 0 | ret = qmalloc(mt, len + 1); |
101 | 0 | fb.buf = fb.pos = ret; |
102 | 0 | fb.len = len; |
103 | |
|
104 | 0 | vbprintfrr(&fb, fmt, ap2); |
105 | 0 | } |
106 | |
|
107 | 0 | va_end(ap2); |
108 | 0 | ret[len] = '\0'; |
109 | 0 | return ret; |
110 | 0 | } |
111 | | |
112 | | char *asnprintfrr(struct memtype *mt, char *out, size_t outsz, const char *fmt, |
113 | | ...) |
114 | 0 | { |
115 | 0 | va_list ap; |
116 | 0 | char *ret; |
117 | |
|
118 | 0 | va_start(ap, fmt); |
119 | 0 | ret = vasnprintfrr(mt, out, outsz, fmt, ap); |
120 | 0 | va_end(ap); |
121 | 0 | return ret; |
122 | 0 | } |
123 | | |
124 | | char *vasprintfrr(struct memtype *mt, const char *fmt, va_list ap) |
125 | 0 | { |
126 | 0 | char buf[256]; |
127 | 0 | char *ret; |
128 | |
|
129 | 0 | ret = vasnprintfrr(mt, buf, sizeof(buf), fmt, ap); |
130 | |
|
131 | 0 | if (ret == buf) |
132 | 0 | ret = qstrdup(mt, ret); |
133 | 0 | return ret; |
134 | 0 | } |
135 | | |
136 | | char *asprintfrr(struct memtype *mt, const char *fmt, ...) |
137 | 0 | { |
138 | 0 | char buf[256]; |
139 | 0 | va_list ap; |
140 | 0 | char *ret; |
141 | |
|
142 | 0 | va_start(ap, fmt); |
143 | 0 | ret = vasnprintfrr(mt, buf, sizeof(buf), fmt, ap); |
144 | 0 | va_end(ap); |
145 | |
|
146 | 0 | if (ret == buf) |
147 | 0 | ret = qstrdup(mt, ret); |
148 | 0 | return ret; |
149 | 0 | } |
150 | | |
151 | | /* Q: WTF? |
152 | | * A: since printf should be reasonably fast (think debugging logs), the idea |
153 | | * here is to keep things close by each other in a cacheline. That's why |
154 | | * ext_quick just has the first 2 characters of an extension, and we do a |
155 | | * nice linear continuous sweep. Only if we find something, we go do more |
156 | | * expensive things. |
157 | | * |
158 | | * Q: doesn't this need a mutex/lock? |
159 | | * A: theoretically, yes, but that's quite expensive and I rather elide that |
160 | | * necessity by putting down some usage rules. Just call this at startup |
161 | | * while singlethreaded and all is fine. Ideally, just use constructors |
162 | | * (and make sure dlopen() doesn't mess things up...) |
163 | | */ |
164 | 624k | #define MAXEXT 64 |
165 | | |
166 | | struct ext_quick { |
167 | | char fmt[2]; |
168 | | }; |
169 | | |
170 | | static uint8_t ext_offsets[26] __attribute__((aligned(32))); |
171 | | static struct ext_quick entries[MAXEXT] __attribute__((aligned(64))); |
172 | | static const struct printfrr_ext *exts[MAXEXT] __attribute__((aligned(64))); |
173 | | |
174 | | void printfrr_ext_reg(const struct printfrr_ext *ext) |
175 | 242 | { |
176 | 242 | uint8_t o; |
177 | 242 | ptrdiff_t i; |
178 | | |
179 | 242 | if (!printfrr_ext_char(ext->match[0])) |
180 | 0 | return; |
181 | | |
182 | 242 | o = ext->match[0] - 'A'; |
183 | 242 | for (i = ext_offsets[o]; |
184 | 314 | i < MAXEXT && entries[i].fmt[0] && |
185 | 278 | memcmp(entries[i].fmt, ext->match, 2) < 0; |
186 | 242 | i++) |
187 | 72 | ; |
188 | 242 | if (i == MAXEXT) |
189 | 0 | return; |
190 | 3.38k | for (o++; o <= 'Z' - 'A'; o++) |
191 | 3.13k | ext_offsets[o]++; |
192 | | |
193 | 242 | memmove(entries + i + 1, entries + i, |
194 | 242 | (MAXEXT - i - 1) * sizeof(entries[0])); |
195 | 242 | memmove(exts + i + 1, exts + i, |
196 | 242 | (MAXEXT - i - 1) * sizeof(exts[0])); |
197 | | |
198 | 242 | memcpy(entries[i].fmt, ext->match, 2); |
199 | 242 | exts[i] = ext; |
200 | 242 | } |
201 | | |
202 | | ssize_t printfrr_extp(struct fbuf *buf, struct printfrr_eargs *ea, |
203 | | const void *ptr) |
204 | 525k | { |
205 | 525k | const char *fmt = ea->fmt; |
206 | 525k | const struct printfrr_ext *ext; |
207 | 525k | size_t i; |
208 | | |
209 | 623k | for (i = ext_offsets[fmt[0] - 'A']; i < MAXEXT; i++) { |
210 | 623k | if (!entries[i].fmt[0] || entries[i].fmt[0] > fmt[0]) |
211 | 0 | return -1; |
212 | 623k | if (entries[i].fmt[1] && entries[i].fmt[1] != fmt[1]) |
213 | 89.3k | continue; |
214 | 533k | ext = exts[i]; |
215 | 533k | if (!ext->print_ptr) |
216 | 0 | continue; |
217 | 533k | if (strncmp(ext->match, fmt, strlen(ext->match))) |
218 | 8.62k | continue; |
219 | 525k | ea->fmt += strlen(ext->match); |
220 | 525k | return ext->print_ptr(buf, ea, ptr); |
221 | 533k | } |
222 | 0 | return -1; |
223 | 525k | } |
224 | | |
225 | | ssize_t printfrr_exti(struct fbuf *buf, struct printfrr_eargs *ea, |
226 | | uintmax_t num) |
227 | 0 | { |
228 | 0 | const char *fmt = ea->fmt; |
229 | 0 | const struct printfrr_ext *ext; |
230 | 0 | size_t i; |
231 | |
|
232 | 0 | for (i = ext_offsets[fmt[0] - 'A']; i < MAXEXT; i++) { |
233 | 0 | if (!entries[i].fmt[0] || entries[i].fmt[0] > fmt[0]) |
234 | 0 | return -1; |
235 | 0 | if (entries[i].fmt[1] && entries[i].fmt[1] != fmt[1]) |
236 | 0 | continue; |
237 | 0 | ext = exts[i]; |
238 | 0 | if (!ext->print_int) |
239 | 0 | continue; |
240 | 0 | if (strncmp(ext->match, fmt, strlen(ext->match))) |
241 | 0 | continue; |
242 | 0 | ea->fmt += strlen(ext->match); |
243 | 0 | return ext->print_int(buf, ea, num); |
244 | 0 | } |
245 | 0 | return -1; |
246 | 0 | } |
247 | | |
248 | | printfrr_ext_autoreg_p("FB", printfrr_fb); |
249 | | static ssize_t printfrr_fb(struct fbuf *out, struct printfrr_eargs *ea, |
250 | | const void *ptr) |
251 | 0 | { |
252 | 0 | const struct fbuf *in = ptr; |
253 | 0 | ptrdiff_t copy_len; |
254 | |
|
255 | 0 | if (!in) |
256 | 0 | return bputs(out, "NULL"); |
257 | | |
258 | 0 | if (out) { |
259 | 0 | copy_len = MIN(in->pos - in->buf, |
260 | 0 | out->buf + out->len - out->pos); |
261 | 0 | if (copy_len > 0) { |
262 | 0 | memcpy(out->pos, in->buf, copy_len); |
263 | 0 | out->pos += copy_len; |
264 | 0 | } |
265 | 0 | } |
266 | |
|
267 | 0 | return in->pos - in->buf; |
268 | 0 | } |
269 | | |
270 | | printfrr_ext_autoreg_p("VA", printfrr_va); |
271 | | static ssize_t printfrr_va(struct fbuf *buf, struct printfrr_eargs *ea, |
272 | | const void *ptr) |
273 | 0 | { |
274 | 0 | const struct va_format *vaf = ptr; |
275 | 0 | va_list ap; |
276 | 0 | ssize_t s; |
277 | |
|
278 | 0 | if (!vaf || !vaf->fmt || !vaf->va) |
279 | 0 | return bputs(buf, "NULL"); |
280 | | |
281 | | /* make sure we don't alter the data passed in - especially since |
282 | | * bprintfrr (and thus this) might be called on the same format twice, |
283 | | * when allocating a larger buffer in asnprintfrr() |
284 | | */ |
285 | 0 | va_copy(ap, *vaf->va); |
286 | 0 | #pragma GCC diagnostic push |
287 | 0 | #pragma GCC diagnostic ignored "-Wformat-nonliteral" |
288 | | /* can't format check this */ |
289 | 0 | s = vbprintfrr(buf, vaf->fmt, ap); |
290 | 0 | #pragma GCC diagnostic pop |
291 | 0 | va_end(ap); |
292 | |
|
293 | 0 | return s; |
294 | 0 | } |