Line data Source code
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 33 : ssize_t bprintfrr(struct fbuf *out, const char *fmt, ...)
29 : {
30 33 : ssize_t ret;
31 33 : va_list ap;
32 :
33 33 : va_start(ap, fmt);
34 33 : ret = vbprintfrr(out, fmt, ap);
35 33 : va_end(ap);
36 33 : return ret;
37 : }
38 :
39 0 : ssize_t vsnprintfrr(char *out, size_t outsz, const char *fmt, va_list ap)
40 : {
41 0 : struct fbuf fbb = { .buf = out, .pos = out, .len = outsz - 1, };
42 0 : struct fbuf *fb = (out && outsz) ? &fbb : NULL;
43 0 : ssize_t ret;
44 :
45 0 : ret = vbprintfrr(fb, fmt, ap);
46 0 : if (fb)
47 0 : fb->pos[0] = '\0';
48 0 : return ret;
49 : }
50 :
51 358 : ssize_t snprintfrr(char *out, size_t outsz, const char *fmt, ...)
52 : {
53 358 : struct fbuf fbb = { .buf = out, .pos = out, .len = outsz - 1, };
54 358 : struct fbuf *fb = (out && outsz) ? &fbb : NULL;
55 358 : ssize_t ret;
56 358 : va_list ap;
57 :
58 358 : va_start(ap, fmt);
59 358 : ret = vbprintfrr(fb, fmt, ap);
60 358 : va_end(ap);
61 358 : if (fb)
62 358 : fb->pos[0] = '\0';
63 358 : return ret;
64 : }
65 :
66 0 : ssize_t vcsnprintfrr(char *out, size_t outsz, const char *fmt, va_list ap)
67 : {
68 0 : if (!out || !outsz)
69 0 : return vbprintfrr(NULL, fmt, ap);
70 :
71 0 : struct fbuf fbb = { .buf = out, .pos = out, .len = outsz - 1, };
72 0 : ssize_t ret;
73 0 : size_t pos;
74 :
75 0 : pos = strnlen(out, outsz);
76 0 : fbb.pos += pos;
77 :
78 0 : ret = vbprintfrr(&fbb, fmt, ap);
79 0 : fbb.pos[0] = '\0';
80 0 : return ret >= 0 ? ret + (ssize_t)pos : ret;
81 : }
82 :
83 0 : ssize_t csnprintfrr(char *out, size_t outsz, const char *fmt, ...)
84 : {
85 0 : ssize_t ret;
86 0 : va_list ap;
87 :
88 0 : va_start(ap, fmt);
89 0 : ret = vcsnprintfrr(out, outsz, fmt, ap);
90 0 : va_end(ap);
91 0 : return ret;
92 : }
93 :
94 78 : char *vasnprintfrr(struct memtype *mt, char *out, size_t outsz, const char *fmt,
95 : va_list ap)
96 : {
97 78 : struct fbuf fb = { .buf = out, .pos = out, .len = outsz - 1, };
98 78 : ssize_t len;
99 78 : va_list ap2;
100 78 : char *ret = out;
101 :
102 78 : va_copy(ap2, ap);
103 78 : len = vbprintfrr(&fb, fmt, ap);
104 78 : if (len < 0) {
105 0 : va_end(ap2);
106 : /* error = malformed format string => try something useful */
107 0 : return qstrdup(mt, fmt);
108 : }
109 :
110 78 : if ((size_t)len >= outsz - 1) {
111 10 : ret = qmalloc(mt, len + 1);
112 10 : fb.buf = fb.pos = ret;
113 10 : fb.len = len;
114 :
115 10 : vbprintfrr(&fb, fmt, ap2);
116 : }
117 :
118 78 : va_end(ap2);
119 78 : ret[len] = '\0';
120 78 : return ret;
121 : }
122 :
123 0 : char *asnprintfrr(struct memtype *mt, char *out, size_t outsz, const char *fmt,
124 : ...)
125 : {
126 0 : va_list ap;
127 0 : char *ret;
128 :
129 0 : va_start(ap, fmt);
130 0 : ret = vasnprintfrr(mt, out, outsz, fmt, ap);
131 0 : va_end(ap);
132 0 : return ret;
133 : }
134 :
135 0 : char *vasprintfrr(struct memtype *mt, const char *fmt, va_list ap)
136 : {
137 0 : char buf[256];
138 0 : char *ret;
139 :
140 0 : ret = vasnprintfrr(mt, buf, sizeof(buf), fmt, ap);
141 :
142 0 : if (ret == buf)
143 0 : ret = qstrdup(mt, ret);
144 0 : return ret;
145 : }
146 :
147 0 : char *asprintfrr(struct memtype *mt, const char *fmt, ...)
148 : {
149 0 : char buf[256];
150 0 : va_list ap;
151 0 : char *ret;
152 :
153 0 : va_start(ap, fmt);
154 0 : ret = vasnprintfrr(mt, buf, sizeof(buf), fmt, ap);
155 0 : va_end(ap);
156 :
157 0 : if (ret == buf)
158 0 : ret = qstrdup(mt, ret);
159 0 : 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 141 : void printfrr_ext_reg(const struct printfrr_ext *ext)
186 : {
187 141 : uint8_t o;
188 141 : ptrdiff_t i;
189 :
190 141 : if (!printfrr_ext_char(ext->match[0]))
191 : return;
192 :
193 141 : o = ext->match[0] - 'A';
194 141 : for (i = ext_offsets[o];
195 174 : i < MAXEXT && entries[i].fmt[0] &&
196 135 : memcmp(entries[i].fmt, ext->match, 2) < 0;
197 33 : i++)
198 : ;
199 141 : if (i == MAXEXT)
200 : return;
201 1809 : for (o++; o <= 'Z' - 'A'; o++)
202 1668 : ext_offsets[o]++;
203 :
204 141 : memmove(entries + i + 1, entries + i,
205 141 : (MAXEXT - i - 1) * sizeof(entries[0]));
206 141 : memmove(exts + i + 1, exts + i,
207 : (MAXEXT - i - 1) * sizeof(exts[0]));
208 :
209 141 : memcpy(entries[i].fmt, ext->match, 2);
210 141 : exts[i] = ext;
211 : }
212 :
213 21 : ssize_t printfrr_extp(struct fbuf *buf, struct printfrr_eargs *ea,
214 : const void *ptr)
215 : {
216 21 : const char *fmt = ea->fmt;
217 21 : const struct printfrr_ext *ext;
218 21 : size_t i;
219 :
220 26 : for (i = ext_offsets[fmt[0] - 'A']; i < MAXEXT; i++) {
221 26 : if (!entries[i].fmt[0] || entries[i].fmt[0] > fmt[0])
222 : return -1;
223 26 : if (entries[i].fmt[1] && entries[i].fmt[1] != fmt[1])
224 5 : continue;
225 21 : ext = exts[i];
226 21 : if (!ext->print_ptr)
227 0 : continue;
228 21 : if (strncmp(ext->match, fmt, strlen(ext->match)))
229 0 : continue;
230 21 : ea->fmt += strlen(ext->match);
231 21 : return ext->print_ptr(buf, ea, ptr);
232 : }
233 : return -1;
234 : }
235 :
236 0 : ssize_t printfrr_exti(struct fbuf *buf, struct printfrr_eargs *ea,
237 : uintmax_t num)
238 : {
239 0 : const char *fmt = ea->fmt;
240 0 : const struct printfrr_ext *ext;
241 0 : size_t i;
242 :
243 0 : for (i = ext_offsets[fmt[0] - 'A']; i < MAXEXT; i++) {
244 0 : if (!entries[i].fmt[0] || entries[i].fmt[0] > fmt[0])
245 : return -1;
246 0 : if (entries[i].fmt[1] && entries[i].fmt[1] != fmt[1])
247 0 : continue;
248 0 : ext = exts[i];
249 0 : if (!ext->print_int)
250 0 : continue;
251 0 : if (strncmp(ext->match, fmt, strlen(ext->match)))
252 0 : continue;
253 0 : ea->fmt += strlen(ext->match);
254 0 : return ext->print_int(buf, ea, num);
255 : }
256 : return -1;
257 : }
258 :
259 6 : printfrr_ext_autoreg_p("FB", printfrr_fb);
260 0 : static ssize_t printfrr_fb(struct fbuf *out, struct printfrr_eargs *ea,
261 : const void *ptr)
262 : {
263 0 : const struct fbuf *in = ptr;
264 0 : ptrdiff_t copy_len;
265 :
266 0 : if (!in)
267 0 : return bputs(out, "NULL");
268 :
269 0 : if (out) {
270 0 : copy_len = MIN(in->pos - in->buf,
271 : out->buf + out->len - out->pos);
272 0 : if (copy_len > 0) {
273 0 : memcpy(out->pos, in->buf, copy_len);
274 0 : out->pos += copy_len;
275 : }
276 : }
277 :
278 0 : return in->pos - in->buf;
279 : }
280 :
281 6 : printfrr_ext_autoreg_p("VA", printfrr_va);
282 0 : static ssize_t printfrr_va(struct fbuf *buf, struct printfrr_eargs *ea,
283 : const void *ptr)
284 : {
285 0 : const struct va_format *vaf = ptr;
286 0 : va_list ap;
287 :
288 0 : if (!vaf || !vaf->fmt || !vaf->va)
289 0 : 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 0 : va_copy(ap, *vaf->va);
296 : #pragma GCC diagnostic push
297 : #pragma GCC diagnostic ignored "-Wformat-nonliteral"
298 : /* can't format check this */
299 0 : return vbprintfrr(buf, vaf->fmt, ap);
300 : #pragma GCC diagnostic pop
301 : }
|