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