]> git.proxmox.com Git - mirror_frr.git/blob - lib/printf/glue.c
Merge pull request #12798 from donaldsharp/rib_match_multicast
[mirror_frr.git] / lib / printf / glue.c
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 {
19 ssize_t ret;
20 va_list ap;
21
22 va_start(ap, fmt);
23 ret = vbprintfrr(out, fmt, ap);
24 va_end(ap);
25 return ret;
26 }
27
28 ssize_t vsnprintfrr(char *out, size_t outsz, const char *fmt, va_list ap)
29 {
30 struct fbuf fbb = { .buf = out, .pos = out, .len = outsz - 1, };
31 struct fbuf *fb = (out && outsz) ? &fbb : NULL;
32 ssize_t ret;
33
34 ret = vbprintfrr(fb, fmt, ap);
35 if (fb)
36 fb->pos[0] = '\0';
37 return ret;
38 }
39
40 ssize_t snprintfrr(char *out, size_t outsz, const char *fmt, ...)
41 {
42 struct fbuf fbb = { .buf = out, .pos = out, .len = outsz - 1, };
43 struct fbuf *fb = (out && outsz) ? &fbb : NULL;
44 ssize_t ret;
45 va_list ap;
46
47 va_start(ap, fmt);
48 ret = vbprintfrr(fb, fmt, ap);
49 va_end(ap);
50 if (fb)
51 fb->pos[0] = '\0';
52 return ret;
53 }
54
55 ssize_t vcsnprintfrr(char *out, size_t outsz, const char *fmt, va_list ap)
56 {
57 if (!out || !outsz)
58 return vbprintfrr(NULL, fmt, ap);
59
60 struct fbuf fbb = { .buf = out, .pos = out, .len = outsz - 1, };
61 ssize_t ret;
62 size_t pos;
63
64 pos = strnlen(out, outsz);
65 fbb.pos += pos;
66
67 ret = vbprintfrr(&fbb, fmt, ap);
68 fbb.pos[0] = '\0';
69 return ret >= 0 ? ret + (ssize_t)pos : ret;
70 }
71
72 ssize_t csnprintfrr(char *out, size_t outsz, const char *fmt, ...)
73 {
74 ssize_t ret;
75 va_list ap;
76
77 va_start(ap, fmt);
78 ret = vcsnprintfrr(out, outsz, fmt, ap);
79 va_end(ap);
80 return ret;
81 }
82
83 char *vasnprintfrr(struct memtype *mt, char *out, size_t outsz, const char *fmt,
84 va_list ap)
85 {
86 struct fbuf fb = { .buf = out, .pos = out, .len = outsz - 1, };
87 ssize_t len;
88 va_list ap2;
89 char *ret = out;
90
91 va_copy(ap2, ap);
92 len = vbprintfrr(&fb, fmt, ap);
93 if (len < 0) {
94 va_end(ap2);
95 /* error = malformed format string => try something useful */
96 return qstrdup(mt, fmt);
97 }
98
99 if ((size_t)len >= outsz - 1) {
100 ret = qmalloc(mt, len + 1);
101 fb.buf = fb.pos = ret;
102 fb.len = len;
103
104 vbprintfrr(&fb, fmt, ap2);
105 }
106
107 va_end(ap2);
108 ret[len] = '\0';
109 return ret;
110 }
111
112 char *asnprintfrr(struct memtype *mt, char *out, size_t outsz, const char *fmt,
113 ...)
114 {
115 va_list ap;
116 char *ret;
117
118 va_start(ap, fmt);
119 ret = vasnprintfrr(mt, out, outsz, fmt, ap);
120 va_end(ap);
121 return ret;
122 }
123
124 char *vasprintfrr(struct memtype *mt, const char *fmt, va_list ap)
125 {
126 char buf[256];
127 char *ret;
128
129 ret = vasnprintfrr(mt, buf, sizeof(buf), fmt, ap);
130
131 if (ret == buf)
132 ret = qstrdup(mt, ret);
133 return ret;
134 }
135
136 char *asprintfrr(struct memtype *mt, const char *fmt, ...)
137 {
138 char buf[256];
139 va_list ap;
140 char *ret;
141
142 va_start(ap, fmt);
143 ret = vasnprintfrr(mt, buf, sizeof(buf), fmt, ap);
144 va_end(ap);
145
146 if (ret == buf)
147 ret = qstrdup(mt, ret);
148 return ret;
149 }
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 #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 {
176 uint8_t o;
177 ptrdiff_t i;
178
179 if (!printfrr_ext_char(ext->match[0]))
180 return;
181
182 o = ext->match[0] - 'A';
183 for (i = ext_offsets[o];
184 i < MAXEXT && entries[i].fmt[0] &&
185 memcmp(entries[i].fmt, ext->match, 2) < 0;
186 i++)
187 ;
188 if (i == MAXEXT)
189 return;
190 for (o++; o <= 'Z' - 'A'; o++)
191 ext_offsets[o]++;
192
193 memmove(entries + i + 1, entries + i,
194 (MAXEXT - i - 1) * sizeof(entries[0]));
195 memmove(exts + i + 1, exts + i,
196 (MAXEXT - i - 1) * sizeof(exts[0]));
197
198 memcpy(entries[i].fmt, ext->match, 2);
199 exts[i] = ext;
200 }
201
202 ssize_t printfrr_extp(struct fbuf *buf, struct printfrr_eargs *ea,
203 const void *ptr)
204 {
205 const char *fmt = ea->fmt;
206 const struct printfrr_ext *ext;
207 size_t i;
208
209 for (i = ext_offsets[fmt[0] - 'A']; i < MAXEXT; i++) {
210 if (!entries[i].fmt[0] || entries[i].fmt[0] > fmt[0])
211 return -1;
212 if (entries[i].fmt[1] && entries[i].fmt[1] != fmt[1])
213 continue;
214 ext = exts[i];
215 if (!ext->print_ptr)
216 continue;
217 if (strncmp(ext->match, fmt, strlen(ext->match)))
218 continue;
219 ea->fmt += strlen(ext->match);
220 return ext->print_ptr(buf, ea, ptr);
221 }
222 return -1;
223 }
224
225 ssize_t printfrr_exti(struct fbuf *buf, struct printfrr_eargs *ea,
226 uintmax_t num)
227 {
228 const char *fmt = ea->fmt;
229 const struct printfrr_ext *ext;
230 size_t i;
231
232 for (i = ext_offsets[fmt[0] - 'A']; i < MAXEXT; i++) {
233 if (!entries[i].fmt[0] || entries[i].fmt[0] > fmt[0])
234 return -1;
235 if (entries[i].fmt[1] && entries[i].fmt[1] != fmt[1])
236 continue;
237 ext = exts[i];
238 if (!ext->print_int)
239 continue;
240 if (strncmp(ext->match, fmt, strlen(ext->match)))
241 continue;
242 ea->fmt += strlen(ext->match);
243 return ext->print_int(buf, ea, num);
244 }
245 return -1;
246 }
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 {
252 const struct fbuf *in = ptr;
253 ptrdiff_t copy_len;
254
255 if (!in)
256 return bputs(out, "NULL");
257
258 if (out) {
259 copy_len = MIN(in->pos - in->buf,
260 out->buf + out->len - out->pos);
261 if (copy_len > 0) {
262 memcpy(out->pos, in->buf, copy_len);
263 out->pos += copy_len;
264 }
265 }
266
267 return in->pos - in->buf;
268 }
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 {
274 const struct va_format *vaf = ptr;
275 va_list ap;
276
277 if (!vaf || !vaf->fmt || !vaf->va)
278 return bputs(buf, "NULL");
279
280 /* make sure we don't alter the data passed in - especially since
281 * bprintfrr (and thus this) might be called on the same format twice,
282 * when allocating a larger buffer in asnprintfrr()
283 */
284 va_copy(ap, *vaf->va);
285 #pragma GCC diagnostic push
286 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
287 /* can't format check this */
288 return vbprintfrr(buf, vaf->fmt, ap);
289 #pragma GCC diagnostic pop
290 }