]>
git.proxmox.com Git - mirror_frr.git/blob - lib/strformat.c
1 // SPDX-License-Identifier: ISC
3 * Copyright (c) 2019 David Lamparter, for NetDEF, Inc.
19 printfrr_ext_autoreg_p("HX", printfrr_hexdump
);
20 static ssize_t
printfrr_hexdump(struct fbuf
*buf
, struct printfrr_eargs
*ea
,
24 ssize_t input_len
= printfrr_ext_len(ea
);
26 const uint8_t *pos
, *end
;
28 if (ea
->fmt
[0] == 'c') {
31 } else if (ea
->fmt
[0] == 'n') {
39 for (pos
= ptr
, end
= pos
+ input_len
; pos
< end
; pos
++) {
40 if (sep
&& pos
!= ptr
)
41 ret
+= bputch(buf
, sep
);
42 ret
+= bputhex(buf
, *pos
);
48 /* string analog for hexdumps / the "this." in ("74 68 69 73 0a |this.|") */
50 printfrr_ext_autoreg_p("HS", printfrr_hexdstr
);
51 static ssize_t
printfrr_hexdstr(struct fbuf
*buf
, struct printfrr_eargs
*ea
,
55 ssize_t input_len
= printfrr_ext_len(ea
);
56 const uint8_t *pos
, *end
;
61 for (pos
= ptr
, end
= pos
+ input_len
; pos
< end
; pos
++) {
62 if (*pos
>= 0x20 && *pos
< 0x7f)
63 ret
+= bputch(buf
, *pos
);
65 ret
+= bputch(buf
, '.');
72 ESC_N_R_T
= (1 << 0), /* use \n \r \t instead of \x0a ...*/
73 ESC_SPACE
= (1 << 1), /* \ */
74 ESC_BACKSLASH
= (1 << 2), /* \\ */
75 ESC_DBLQUOTE
= (1 << 3), /* \" */
76 ESC_SGLQUOTE
= (1 << 4), /* \' */
77 ESC_BACKTICK
= (1 << 5), /* \` */
78 ESC_DOLLAR
= (1 << 6), /* \$ */
79 ESC_CLBRACKET
= (1 << 7), /* \] for RFC5424 syslog */
80 ESC_OTHER
= (1 << 8), /* remaining non-alpha */
82 ESC_ALL
= ESC_N_R_T
| ESC_SPACE
| ESC_BACKSLASH
| ESC_DBLQUOTE
83 | ESC_SGLQUOTE
| ESC_DOLLAR
| ESC_OTHER
,
84 ESC_QUOTSTRING
= ESC_N_R_T
| ESC_BACKSLASH
| ESC_DBLQUOTE
,
85 /* if needed: ESC_SHELL = ... */
88 static ssize_t
bquote(struct fbuf
*buf
, const uint8_t *pos
, size_t len
,
92 const uint8_t *end
= pos
+ len
;
94 for (; pos
< end
; pos
++) {
95 /* here's to hoping this might be a bit faster... */
96 if (__builtin_expect(!!isalnum(*pos
), 1)) {
97 ret
+= bputch(buf
, *pos
);
111 ret
+= bputch(buf
, *pos
);
115 if (!(flags
& ESC_N_R_T
))
117 ret
+= bputch(buf
, '\\');
118 ret
+= bputch(buf
, 'r');
121 if (!(flags
& ESC_N_R_T
))
123 ret
+= bputch(buf
, '\\');
124 ret
+= bputch(buf
, 'n');
127 if (!(flags
& ESC_N_R_T
))
129 ret
+= bputch(buf
, '\\');
130 ret
+= bputch(buf
, 't');
134 if (flags
& ESC_SPACE
)
135 ret
+= bputch(buf
, '\\');
136 ret
+= bputch(buf
, *pos
);
140 if (flags
& ESC_BACKSLASH
)
141 ret
+= bputch(buf
, '\\');
142 ret
+= bputch(buf
, *pos
);
146 if (flags
& ESC_DBLQUOTE
)
147 ret
+= bputch(buf
, '\\');
148 ret
+= bputch(buf
, *pos
);
152 if (flags
& ESC_SGLQUOTE
)
153 ret
+= bputch(buf
, '\\');
154 ret
+= bputch(buf
, *pos
);
158 if (flags
& ESC_BACKTICK
)
159 ret
+= bputch(buf
, '\\');
160 ret
+= bputch(buf
, *pos
);
164 if (flags
& ESC_DOLLAR
)
165 ret
+= bputch(buf
, '\\');
166 ret
+= bputch(buf
, *pos
);
170 if (flags
& ESC_CLBRACKET
)
171 ret
+= bputch(buf
, '\\');
172 ret
+= bputch(buf
, *pos
);
175 /* remaining: !#&'()*;<=>?[^{|}~ */
178 if (*pos
>= 0x20 && *pos
< 0x7f) {
179 if (flags
& ESC_OTHER
)
180 ret
+= bputch(buf
, '\\');
181 ret
+= bputch(buf
, *pos
);
185 ret
+= bputch(buf
, '\\');
186 ret
+= bputch(buf
, 'x');
187 ret
+= bputhex(buf
, *pos
);
193 printfrr_ext_autoreg_p("SE", printfrr_escape
);
194 static ssize_t
printfrr_escape(struct fbuf
*buf
, struct printfrr_eargs
*ea
,
197 ssize_t len
= printfrr_ext_len(ea
);
198 const uint8_t *ptr
= vptr
;
199 bool null_is_empty
= false;
201 if (ea
->fmt
[0] == 'n') {
202 null_is_empty
= true;
209 return bputs(buf
, "(null)");
213 len
= strlen((const char *)ptr
);
215 return bquote(buf
, ptr
, len
, ESC_ALL
);
218 printfrr_ext_autoreg_p("SQ", printfrr_quote
);
219 static ssize_t
printfrr_quote(struct fbuf
*buf
, struct printfrr_eargs
*ea
,
222 ssize_t len
= printfrr_ext_len(ea
);
223 const uint8_t *ptr
= vptr
;
225 bool null_is_empty
= false;
226 bool do_quotes
= false;
227 unsigned int flags
= ESC_QUOTSTRING
;
230 switch (ea
->fmt
[0]) {
232 null_is_empty
= true;
240 flags
|= ESC_CLBRACKET
;
250 return bputs(buf
, do_quotes
? "\"\"" : "");
251 return bputs(buf
, "(null)");
255 len
= strlen((const char *)ptr
);
258 ret
+= bputch(buf
, '"');
259 ret
+= bquote(buf
, ptr
, len
, flags
);
261 ret
+= bputch(buf
, '"');
265 static ssize_t
printfrr_abstime(struct fbuf
*buf
, struct printfrr_eargs
*ea
,
266 const struct timespec
*ts
, unsigned int flags
);
267 static ssize_t
printfrr_reltime(struct fbuf
*buf
, struct printfrr_eargs
*ea
,
268 const struct timespec
*ts
, unsigned int flags
);
270 ssize_t
printfrr_time(struct fbuf
*buf
, struct printfrr_eargs
*ea
,
271 const struct timespec
*ts
, unsigned int flags
)
273 bool have_abs
, have_anchor
;
275 if (!(flags
& TIMEFMT_PRESELECT
)) {
276 switch (ea
->fmt
[0]) {
281 flags
|= TIMEFMT_MONOTONIC
;
284 flags
|= TIMEFMT_REALTIME
;
288 "{invalid time format input specifier}");
292 if (ea
->fmt
[0] == 's') {
293 flags
|= TIMEFMT_SINCE
;
295 } else if (ea
->fmt
[0] == 'u') {
296 flags
|= TIMEFMT_UNTIL
;
301 have_abs
= !!(flags
& TIMEFMT_ABSOLUTE
);
302 have_anchor
= !!(flags
& TIMEFMT_ANCHORS
);
304 if (have_abs
^ have_anchor
)
305 return printfrr_abstime(buf
, ea
, ts
, flags
);
307 return printfrr_reltime(buf
, ea
, ts
, flags
);
310 static ssize_t
do_subsec(struct fbuf
*buf
, const struct timespec
*ts
,
311 int precision
, unsigned int flags
)
313 unsigned long long frac
;
315 if (precision
<= 0 || (flags
& TIMEFMT_SECONDS
))
321 for (int i
= precision
; i
< 9; i
++)
323 return bprintfrr(buf
, ".%0*llu", precision
, frac
);
326 static ssize_t
printfrr_abstime(struct fbuf
*buf
, struct printfrr_eargs
*ea
,
327 const struct timespec
*ts
, unsigned int flags
)
329 struct timespec real_ts
[1];
331 char cbuf
[32] = ""; /* manpage says 26 for ctime_r */
333 int precision
= ea
->precision
;
336 char ch
= *ea
->fmt
++;
340 flags
|= TIMEFMT_SPACE
;
343 flags
|= TIMEFMT_ISO8601
;
351 if (flags
& TIMEFMT_SKIP
)
354 return bputch(buf
, '-');
356 if (flags
& TIMEFMT_REALTIME
)
358 else if (flags
& TIMEFMT_MONOTONIC
) {
359 struct timespec mono_now
[1];
361 clock_gettime(CLOCK_REALTIME
, real_ts
);
362 clock_gettime(CLOCK_MONOTONIC
, mono_now
);
364 timespecsub(real_ts
, mono_now
, real_ts
);
365 timespecadd(real_ts
, ts
, real_ts
);
367 clock_gettime(CLOCK_REALTIME
, real_ts
);
369 if (flags
& TIMEFMT_SINCE
)
370 timespecsub(real_ts
, ts
, real_ts
);
371 else /* flags & TIMEFMT_UNTIL */
372 timespecadd(real_ts
, ts
, real_ts
);
375 localtime_r(&real_ts
->tv_sec
, &tm
);
377 if (flags
& TIMEFMT_ISO8601
) {
378 if (flags
& TIMEFMT_SPACE
)
379 strftime(cbuf
, sizeof(cbuf
), "%Y-%m-%d %H:%M:%S", &tm
);
381 strftime(cbuf
, sizeof(cbuf
), "%Y-%m-%dT%H:%M:%S", &tm
);
382 ret
+= bputs(buf
, cbuf
);
386 ret
+= do_subsec(buf
, real_ts
, precision
, flags
);
390 asctime_r(&tm
, cbuf
);
396 if (cbuf
[len
- 1] == '\n')
397 cbuf
[len
- 1] = '\0';
399 ret
+= bputs(buf
, cbuf
);
404 static ssize_t
printfrr_reltime(struct fbuf
*buf
, struct printfrr_eargs
*ea
,
405 const struct timespec
*ts
, unsigned int flags
)
407 struct timespec real_ts
[1];
409 const char *space
= "";
410 const char *dashes
= "-";
411 int precision
= ea
->precision
;
414 char ch
= *ea
->fmt
++;
418 flags
|= TIMEFMT_SPACE
;
422 flags
|= TIMEFMT_BASIC
;
425 flags
|= TIMEFMT_DECIMAL
;
428 flags
|= TIMEFMT_MMSS
;
432 flags
|= TIMEFMT_HHMMSS
;
436 flags
|= TIMEFMT_DASHES
;
444 if (flags
& TIMEFMT_SKIP
)
447 return bputch(buf
, '-');
449 if (flags
& TIMEFMT_ABSOLUTE
) {
450 struct timespec anchor
[1];
452 if (flags
& TIMEFMT_REALTIME
)
453 clock_gettime(CLOCK_REALTIME
, anchor
);
455 clock_gettime(CLOCK_MONOTONIC
, anchor
);
456 if (flags
& TIMEFMT_UNTIL
)
457 timespecsub(ts
, anchor
, real_ts
);
458 else /* flags & TIMEFMT_SINCE */
459 timespecsub(anchor
, ts
, real_ts
);
463 if (real_ts
->tv_sec
== 0 && real_ts
->tv_nsec
== 0 &&
464 (flags
& TIMEFMT_DASHES
))
465 return bputs(buf
, dashes
);
467 if (real_ts
->tv_sec
< 0) {
468 if (flags
& TIMEFMT_DASHES
)
469 return bputs(buf
, dashes
);
471 /* -0.3s is { -1s + 700ms } */
472 real_ts
->tv_sec
= -real_ts
->tv_sec
- 1;
473 real_ts
->tv_nsec
= 1000000000L - real_ts
->tv_nsec
;
474 if (real_ts
->tv_nsec
>= 1000000000L) {
476 real_ts
->tv_nsec
-= 1000000000L;
479 /* all formats have a - make sense in front */
480 ret
+= bputch(buf
, '-');
483 if (flags
& TIMEFMT_DECIMAL
) {
484 ret
+= bprintfrr(buf
, "%lld", (long long)real_ts
->tv_sec
);
487 ret
+= do_subsec(buf
, real_ts
, precision
, flags
);
491 /* these divisions may be slow on embedded boxes, hence only do the
492 * ones we need, plus the ?: zero check to hopefully skip zeros fast
494 lldiv_t min_sec
= lldiv(real_ts
->tv_sec
, 60);
496 if (flags
& TIMEFMT_MMSS
) {
497 ret
+= bprintfrr(buf
, "%02lld:%02lld", min_sec
.quot
,
499 ret
+= do_subsec(buf
, real_ts
, precision
, flags
);
503 lldiv_t hour_min
= min_sec
.quot
? lldiv(min_sec
.quot
, 60) : (lldiv_t
){};
505 if (flags
& TIMEFMT_HHMMSS
) {
506 ret
+= bprintfrr(buf
, "%02lld:%02lld:%02lld", hour_min
.quot
,
507 hour_min
.rem
, min_sec
.rem
);
508 ret
+= do_subsec(buf
, real_ts
, precision
, flags
);
513 hour_min
.quot
? lldiv(hour_min
.quot
, 24) : (lldiv_t
){};
515 day_hour
.quot
? lldiv(day_hour
.quot
, 7) : (lldiv_t
){};
517 /* if sub-second precision is not supported, return */
518 if (flags
& TIMEFMT_BASIC
) {
519 /* match frrtime_to_interval (without space flag) */
521 ret
+= bprintfrr(buf
, "%lldw%s%lldd%s%02lldh",
522 week_day
.quot
, space
, week_day
.rem
,
523 space
, day_hour
.rem
);
524 else if (day_hour
.quot
)
525 ret
+= bprintfrr(buf
, "%lldd%s%02lldh%s%02lldm",
526 day_hour
.quot
, space
, day_hour
.rem
,
527 space
, hour_min
.rem
);
529 ret
+= bprintfrr(buf
, "%02lld:%02lld:%02lld",
530 hour_min
.quot
, hour_min
.rem
,
532 /* no sub-seconds here */
538 ret
+= bprintfrr(buf
, "%lldw%s", week_day
.quot
, space
);
539 if (week_day
.rem
|| week_day
.quot
)
540 ret
+= bprintfrr(buf
, "%lldd%s", week_day
.rem
, space
);
542 ret
+= bprintfrr(buf
, "%02lld:%02lld:%02lld", day_hour
.rem
,
543 hour_min
.rem
, min_sec
.rem
);
547 ret
+= do_subsec(buf
, real_ts
, precision
, flags
);
551 printfrr_ext_autoreg_p("TS", printfrr_ts
);
552 static ssize_t
printfrr_ts(struct fbuf
*buf
, struct printfrr_eargs
*ea
,
555 const struct timespec
*ts
= vptr
;
557 return printfrr_time(buf
, ea
, ts
, 0);
560 printfrr_ext_autoreg_p("TV", printfrr_tv
);
561 static ssize_t
printfrr_tv(struct fbuf
*buf
, struct printfrr_eargs
*ea
,
564 const struct timeval
*tv
= vptr
;
568 return printfrr_time(buf
, ea
, NULL
, 0);
570 ts
.tv_sec
= tv
->tv_sec
;
571 ts
.tv_nsec
= tv
->tv_usec
* 1000;
572 return printfrr_time(buf
, ea
, &ts
, 0);
575 printfrr_ext_autoreg_p("TT", printfrr_tt
);
576 static ssize_t
printfrr_tt(struct fbuf
*buf
, struct printfrr_eargs
*ea
,
579 const time_t *tt
= vptr
;
583 return printfrr_time(buf
, ea
, NULL
, TIMEFMT_SECONDS
);
587 return printfrr_time(buf
, ea
, &ts
, TIMEFMT_SECONDS
);