]>
git.proxmox.com Git - mirror_frr.git/blob - lib/strformat.c
2 * Copyright (c) 2019 David Lamparter, for NetDEF, Inc.
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.
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.
30 printfrr_ext_autoreg_p("HX", printfrr_hexdump
);
31 static ssize_t
printfrr_hexdump(struct fbuf
*buf
, struct printfrr_eargs
*ea
,
35 ssize_t input_len
= printfrr_ext_len(ea
);
37 const uint8_t *pos
, *end
;
39 if (ea
->fmt
[0] == 'c') {
42 } else if (ea
->fmt
[0] == 'n') {
50 for (pos
= ptr
, end
= pos
+ input_len
; pos
< end
; pos
++) {
51 if (sep
&& pos
!= ptr
)
52 ret
+= bputch(buf
, sep
);
53 ret
+= bputhex(buf
, *pos
);
59 /* string analog for hexdumps / the "this." in ("74 68 69 73 0a |this.|") */
61 printfrr_ext_autoreg_p("HS", printfrr_hexdstr
);
62 static ssize_t
printfrr_hexdstr(struct fbuf
*buf
, struct printfrr_eargs
*ea
,
66 ssize_t input_len
= printfrr_ext_len(ea
);
67 const uint8_t *pos
, *end
;
72 for (pos
= ptr
, end
= pos
+ input_len
; pos
< end
; pos
++) {
73 if (*pos
>= 0x20 && *pos
< 0x7f)
74 ret
+= bputch(buf
, *pos
);
76 ret
+= bputch(buf
, '.');
83 ESC_N_R_T
= (1 << 0), /* use \n \r \t instead of \x0a ...*/
84 ESC_SPACE
= (1 << 1), /* \ */
85 ESC_BACKSLASH
= (1 << 2), /* \\ */
86 ESC_DBLQUOTE
= (1 << 3), /* \" */
87 ESC_SGLQUOTE
= (1 << 4), /* \' */
88 ESC_BACKTICK
= (1 << 5), /* \` */
89 ESC_DOLLAR
= (1 << 6), /* \$ */
90 ESC_CLBRACKET
= (1 << 7), /* \] for RFC5424 syslog */
91 ESC_OTHER
= (1 << 8), /* remaining non-alpha */
93 ESC_ALL
= ESC_N_R_T
| ESC_SPACE
| ESC_BACKSLASH
| ESC_DBLQUOTE
94 | ESC_SGLQUOTE
| ESC_DOLLAR
| ESC_OTHER
,
95 ESC_QUOTSTRING
= ESC_N_R_T
| ESC_BACKSLASH
| ESC_DBLQUOTE
,
96 /* if needed: ESC_SHELL = ... */
99 static ssize_t
bquote(struct fbuf
*buf
, const uint8_t *pos
, size_t len
,
103 const uint8_t *end
= pos
+ len
;
105 for (; pos
< end
; pos
++) {
106 /* here's to hoping this might be a bit faster... */
107 if (__builtin_expect(!!isalnum(*pos
), 1)) {
108 ret
+= bputch(buf
, *pos
);
122 ret
+= bputch(buf
, *pos
);
126 if (!(flags
& ESC_N_R_T
))
128 ret
+= bputch(buf
, '\\');
129 ret
+= bputch(buf
, 'r');
132 if (!(flags
& ESC_N_R_T
))
134 ret
+= bputch(buf
, '\\');
135 ret
+= bputch(buf
, 'n');
138 if (!(flags
& ESC_N_R_T
))
140 ret
+= bputch(buf
, '\\');
141 ret
+= bputch(buf
, 't');
145 if (flags
& ESC_SPACE
)
146 ret
+= bputch(buf
, '\\');
147 ret
+= bputch(buf
, *pos
);
151 if (flags
& ESC_BACKSLASH
)
152 ret
+= bputch(buf
, '\\');
153 ret
+= bputch(buf
, *pos
);
157 if (flags
& ESC_DBLQUOTE
)
158 ret
+= bputch(buf
, '\\');
159 ret
+= bputch(buf
, *pos
);
163 if (flags
& ESC_SGLQUOTE
)
164 ret
+= bputch(buf
, '\\');
165 ret
+= bputch(buf
, *pos
);
169 if (flags
& ESC_BACKTICK
)
170 ret
+= bputch(buf
, '\\');
171 ret
+= bputch(buf
, *pos
);
175 if (flags
& ESC_DOLLAR
)
176 ret
+= bputch(buf
, '\\');
177 ret
+= bputch(buf
, *pos
);
181 if (flags
& ESC_CLBRACKET
)
182 ret
+= bputch(buf
, '\\');
183 ret
+= bputch(buf
, *pos
);
186 /* remaining: !#&'()*;<=>?[^{|}~ */
189 if (*pos
>= 0x20 && *pos
< 0x7f) {
190 if (flags
& ESC_OTHER
)
191 ret
+= bputch(buf
, '\\');
192 ret
+= bputch(buf
, *pos
);
196 ret
+= bputch(buf
, '\\');
197 ret
+= bputch(buf
, 'x');
198 ret
+= bputhex(buf
, *pos
);
204 printfrr_ext_autoreg_p("SE", printfrr_escape
);
205 static ssize_t
printfrr_escape(struct fbuf
*buf
, struct printfrr_eargs
*ea
,
208 ssize_t len
= printfrr_ext_len(ea
);
209 const uint8_t *ptr
= vptr
;
210 bool null_is_empty
= false;
212 if (ea
->fmt
[0] == 'n') {
213 null_is_empty
= true;
220 return bputs(buf
, "(null)");
224 len
= strlen((const char *)ptr
);
226 return bquote(buf
, ptr
, len
, ESC_ALL
);
229 printfrr_ext_autoreg_p("SQ", printfrr_quote
);
230 static ssize_t
printfrr_quote(struct fbuf
*buf
, struct printfrr_eargs
*ea
,
233 ssize_t len
= printfrr_ext_len(ea
);
234 const uint8_t *ptr
= vptr
;
236 bool null_is_empty
= false;
237 bool do_quotes
= false;
238 unsigned int flags
= ESC_QUOTSTRING
;
241 switch (ea
->fmt
[0]) {
243 null_is_empty
= true;
251 flags
|= ESC_CLBRACKET
;
261 return bputs(buf
, do_quotes
? "\"\"" : "");
262 return bputs(buf
, "(null)");
266 len
= strlen((const char *)ptr
);
269 ret
+= bputch(buf
, '"');
270 ret
+= bquote(buf
, ptr
, len
, flags
);
272 ret
+= bputch(buf
, '"');
276 static ssize_t
printfrr_abstime(struct fbuf
*buf
, struct printfrr_eargs
*ea
,
277 const struct timespec
*ts
, unsigned int flags
);
278 static ssize_t
printfrr_reltime(struct fbuf
*buf
, struct printfrr_eargs
*ea
,
279 const struct timespec
*ts
, unsigned int flags
);
281 ssize_t
printfrr_time(struct fbuf
*buf
, struct printfrr_eargs
*ea
,
282 const struct timespec
*ts
, unsigned int flags
)
284 bool have_abs
, have_anchor
;
286 if (!(flags
& TIMEFMT_PRESELECT
)) {
287 switch (ea
->fmt
[0]) {
292 flags
|= TIMEFMT_MONOTONIC
;
295 flags
|= TIMEFMT_REALTIME
;
299 "{invalid time format input specifier}");
303 if (ea
->fmt
[0] == 's') {
304 flags
|= TIMEFMT_SINCE
;
306 } else if (ea
->fmt
[0] == 'u') {
307 flags
|= TIMEFMT_UNTIL
;
312 have_abs
= !!(flags
& TIMEFMT_ABSOLUTE
);
313 have_anchor
= !!(flags
& TIMEFMT_ANCHORS
);
315 if (have_abs
^ have_anchor
)
316 return printfrr_abstime(buf
, ea
, ts
, flags
);
318 return printfrr_reltime(buf
, ea
, ts
, flags
);
321 static ssize_t
do_subsec(struct fbuf
*buf
, const struct timespec
*ts
,
322 int precision
, unsigned int flags
)
324 unsigned long long frac
;
326 if (precision
<= 0 || (flags
& TIMEFMT_SECONDS
))
332 for (int i
= precision
; i
< 9; i
++)
334 return bprintfrr(buf
, ".%0*llu", precision
, frac
);
337 static ssize_t
printfrr_abstime(struct fbuf
*buf
, struct printfrr_eargs
*ea
,
338 const struct timespec
*ts
, unsigned int flags
)
340 struct timespec real_ts
[1];
342 char cbuf
[32] = ""; /* manpage says 26 for ctime_r */
344 int precision
= ea
->precision
;
347 char ch
= *ea
->fmt
++;
351 flags
|= TIMEFMT_SPACE
;
354 flags
|= TIMEFMT_ISO8601
;
362 if (flags
& TIMEFMT_SKIP
)
365 return bputch(buf
, '-');
367 if (flags
& TIMEFMT_REALTIME
)
369 else if (flags
& TIMEFMT_MONOTONIC
) {
370 struct timespec mono_now
[1];
372 clock_gettime(CLOCK_REALTIME
, real_ts
);
373 clock_gettime(CLOCK_MONOTONIC
, mono_now
);
375 timespecsub(real_ts
, mono_now
, real_ts
);
376 timespecadd(real_ts
, ts
, real_ts
);
378 clock_gettime(CLOCK_REALTIME
, real_ts
);
380 if (flags
& TIMEFMT_SINCE
)
381 timespecsub(real_ts
, ts
, real_ts
);
382 else /* flags & TIMEFMT_UNTIL */
383 timespecadd(real_ts
, ts
, real_ts
);
386 localtime_r(&real_ts
->tv_sec
, &tm
);
388 if (flags
& TIMEFMT_ISO8601
) {
389 if (flags
& TIMEFMT_SPACE
)
390 strftime(cbuf
, sizeof(cbuf
), "%Y-%m-%d %H:%M:%S", &tm
);
392 strftime(cbuf
, sizeof(cbuf
), "%Y-%m-%dT%H:%M:%S", &tm
);
393 ret
+= bputs(buf
, cbuf
);
397 ret
+= do_subsec(buf
, real_ts
, precision
, flags
);
401 asctime_r(&tm
, cbuf
);
407 if (cbuf
[len
- 1] == '\n')
408 cbuf
[len
- 1] = '\0';
410 ret
+= bputs(buf
, cbuf
);
415 static ssize_t
printfrr_reltime(struct fbuf
*buf
, struct printfrr_eargs
*ea
,
416 const struct timespec
*ts
, unsigned int flags
)
418 struct timespec real_ts
[1];
420 const char *space
= "";
421 const char *dashes
= "-";
422 int precision
= ea
->precision
;
425 char ch
= *ea
->fmt
++;
429 flags
|= TIMEFMT_SPACE
;
433 flags
|= TIMEFMT_BASIC
;
436 flags
|= TIMEFMT_DECIMAL
;
439 flags
|= TIMEFMT_MMSS
;
443 flags
|= TIMEFMT_HHMMSS
;
447 flags
|= TIMEFMT_DASHES
;
455 if (flags
& TIMEFMT_SKIP
)
458 return bputch(buf
, '-');
460 if (flags
& TIMEFMT_ABSOLUTE
) {
461 struct timespec anchor
[1];
463 if (flags
& TIMEFMT_REALTIME
)
464 clock_gettime(CLOCK_REALTIME
, anchor
);
466 clock_gettime(CLOCK_MONOTONIC
, anchor
);
467 if (flags
& TIMEFMT_UNTIL
)
468 timespecsub(ts
, anchor
, real_ts
);
469 else /* flags & TIMEFMT_SINCE */
470 timespecsub(anchor
, ts
, real_ts
);
474 if (real_ts
->tv_sec
== 0 && real_ts
->tv_nsec
== 0 &&
475 (flags
& TIMEFMT_DASHES
))
476 return bputs(buf
, dashes
);
478 if (real_ts
->tv_sec
< 0) {
479 if (flags
& TIMEFMT_DASHES
)
480 return bputs(buf
, dashes
);
482 /* -0.3s is { -1s + 700ms } */
483 real_ts
->tv_sec
= -real_ts
->tv_sec
- 1;
484 real_ts
->tv_nsec
= 1000000000L - real_ts
->tv_nsec
;
485 if (real_ts
->tv_nsec
>= 1000000000L) {
487 real_ts
->tv_nsec
-= 1000000000L;
490 /* all formats have a - make sense in front */
491 ret
+= bputch(buf
, '-');
494 if (flags
& TIMEFMT_DECIMAL
) {
495 ret
+= bprintfrr(buf
, "%lld", (long long)real_ts
->tv_sec
);
498 ret
+= do_subsec(buf
, real_ts
, precision
, flags
);
502 /* these divisions may be slow on embedded boxes, hence only do the
503 * ones we need, plus the ?: zero check to hopefully skip zeros fast
505 lldiv_t min_sec
= lldiv(real_ts
->tv_sec
, 60);
507 if (flags
& TIMEFMT_MMSS
) {
508 ret
+= bprintfrr(buf
, "%02lld:%02lld", min_sec
.quot
,
510 ret
+= do_subsec(buf
, real_ts
, precision
, flags
);
514 lldiv_t hour_min
= min_sec
.quot
? lldiv(min_sec
.quot
, 60) : (lldiv_t
){};
516 if (flags
& TIMEFMT_HHMMSS
) {
517 ret
+= bprintfrr(buf
, "%02lld:%02lld:%02lld", hour_min
.quot
,
518 hour_min
.rem
, min_sec
.rem
);
519 ret
+= do_subsec(buf
, real_ts
, precision
, flags
);
524 hour_min
.quot
? lldiv(hour_min
.quot
, 24) : (lldiv_t
){};
526 day_hour
.quot
? lldiv(day_hour
.quot
, 7) : (lldiv_t
){};
528 /* if sub-second precision is not supported, return */
529 if (flags
& TIMEFMT_BASIC
) {
530 /* match frrtime_to_interval (without space flag) */
532 ret
+= bprintfrr(buf
, "%lldw%s%lldd%s%02lldh",
533 week_day
.quot
, space
, week_day
.rem
,
534 space
, day_hour
.rem
);
535 else if (day_hour
.quot
)
536 ret
+= bprintfrr(buf
, "%lldd%s%02lldh%s%02lldm",
537 day_hour
.quot
, space
, day_hour
.rem
,
538 space
, hour_min
.rem
);
540 ret
+= bprintfrr(buf
, "%02lld:%02lld:%02lld",
541 hour_min
.quot
, hour_min
.rem
,
543 /* no sub-seconds here */
549 ret
+= bprintfrr(buf
, "%lldw%s", week_day
.quot
, space
);
550 if (week_day
.rem
|| week_day
.quot
)
551 ret
+= bprintfrr(buf
, "%lldd%s", week_day
.rem
, space
);
553 ret
+= bprintfrr(buf
, "%02lld:%02lld:%02lld", day_hour
.rem
,
554 hour_min
.rem
, min_sec
.rem
);
558 ret
+= do_subsec(buf
, real_ts
, precision
, flags
);
562 printfrr_ext_autoreg_p("TS", printfrr_ts
);
563 static ssize_t
printfrr_ts(struct fbuf
*buf
, struct printfrr_eargs
*ea
,
566 const struct timespec
*ts
= vptr
;
568 return printfrr_time(buf
, ea
, ts
, 0);
571 printfrr_ext_autoreg_p("TV", printfrr_tv
);
572 static ssize_t
printfrr_tv(struct fbuf
*buf
, struct printfrr_eargs
*ea
,
575 const struct timeval
*tv
= vptr
;
579 return printfrr_time(buf
, ea
, NULL
, 0);
581 ts
.tv_sec
= tv
->tv_sec
;
582 ts
.tv_nsec
= tv
->tv_usec
* 1000;
583 return printfrr_time(buf
, ea
, &ts
, 0);
586 printfrr_ext_autoreg_p("TT", printfrr_tt
);
587 static ssize_t
printfrr_tt(struct fbuf
*buf
, struct printfrr_eargs
*ea
,
590 const time_t *tt
= vptr
;
594 return printfrr_time(buf
, ea
, NULL
, TIMEFMT_SECONDS
);
598 return printfrr_time(buf
, ea
, &ts
, TIMEFMT_SECONDS
);