Coverage Report

Created: 2025-10-08 06:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}