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 "compiler.h" |
11 | | |
12 | | #include <string.h> |
13 | | #include <ctype.h> |
14 | | #include <time.h> |
15 | | |
16 | | #include "printfrr.h" |
17 | | #include "monotime.h" |
18 | | |
19 | | printfrr_ext_autoreg_p("HX", printfrr_hexdump); |
20 | | static ssize_t printfrr_hexdump(struct fbuf *buf, struct printfrr_eargs *ea, |
21 | | const void *ptr) |
22 | 0 | { |
23 | 0 | ssize_t ret = 0; |
24 | 0 | ssize_t input_len = printfrr_ext_len(ea); |
25 | 0 | char sep = ' '; |
26 | 0 | const uint8_t *pos, *end; |
27 | |
|
28 | 0 | if (ea->fmt[0] == 'c') { |
29 | 0 | ea->fmt++; |
30 | 0 | sep = ':'; |
31 | 0 | } else if (ea->fmt[0] == 'n') { |
32 | 0 | ea->fmt++; |
33 | 0 | sep = '\0'; |
34 | 0 | } |
35 | |
|
36 | 0 | if (input_len < 0) |
37 | 0 | return 0; |
38 | | |
39 | 0 | for (pos = ptr, end = pos + input_len; pos < end; pos++) { |
40 | 0 | if (sep && pos != ptr) |
41 | 0 | ret += bputch(buf, sep); |
42 | 0 | ret += bputhex(buf, *pos); |
43 | 0 | } |
44 | |
|
45 | 0 | return ret; |
46 | 0 | } |
47 | | |
48 | | /* string analog for hexdumps / the "this." in ("74 68 69 73 0a |this.|") */ |
49 | | |
50 | | printfrr_ext_autoreg_p("HS", printfrr_hexdstr); |
51 | | static ssize_t printfrr_hexdstr(struct fbuf *buf, struct printfrr_eargs *ea, |
52 | | const void *ptr) |
53 | 0 | { |
54 | 0 | ssize_t ret = 0; |
55 | 0 | ssize_t input_len = printfrr_ext_len(ea); |
56 | 0 | const uint8_t *pos, *end; |
57 | |
|
58 | 0 | if (input_len < 0) |
59 | 0 | return 0; |
60 | | |
61 | 0 | for (pos = ptr, end = pos + input_len; pos < end; pos++) { |
62 | 0 | if (*pos >= 0x20 && *pos < 0x7f) |
63 | 0 | ret += bputch(buf, *pos); |
64 | 0 | else |
65 | 0 | ret += bputch(buf, '.'); |
66 | 0 | } |
67 | |
|
68 | 0 | return ret; |
69 | 0 | } |
70 | | |
71 | | enum escape_flags { |
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 */ |
81 | | |
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 = ... */ |
86 | | }; |
87 | | |
88 | | static ssize_t bquote(struct fbuf *buf, const uint8_t *pos, size_t len, |
89 | | unsigned int flags) |
90 | 0 | { |
91 | 0 | ssize_t ret = 0; |
92 | 0 | const uint8_t *end = pos + len; |
93 | |
|
94 | 0 | for (; pos < end; pos++) { |
95 | | /* here's to hoping this might be a bit faster... */ |
96 | 0 | if (__builtin_expect(!!isalnum(*pos), 1)) { |
97 | 0 | ret += bputch(buf, *pos); |
98 | 0 | continue; |
99 | 0 | } |
100 | | |
101 | 0 | switch (*pos) { |
102 | 0 | case '%': |
103 | 0 | case '+': |
104 | 0 | case ',': |
105 | 0 | case '-': |
106 | 0 | case '.': |
107 | 0 | case '/': |
108 | 0 | case ':': |
109 | 0 | case '@': |
110 | 0 | case '_': |
111 | 0 | ret += bputch(buf, *pos); |
112 | 0 | continue; |
113 | | |
114 | 0 | case '\r': |
115 | 0 | if (!(flags & ESC_N_R_T)) |
116 | 0 | break; |
117 | 0 | ret += bputch(buf, '\\'); |
118 | 0 | ret += bputch(buf, 'r'); |
119 | 0 | continue; |
120 | 0 | case '\n': |
121 | 0 | if (!(flags & ESC_N_R_T)) |
122 | 0 | break; |
123 | 0 | ret += bputch(buf, '\\'); |
124 | 0 | ret += bputch(buf, 'n'); |
125 | 0 | continue; |
126 | 0 | case '\t': |
127 | 0 | if (!(flags & ESC_N_R_T)) |
128 | 0 | break; |
129 | 0 | ret += bputch(buf, '\\'); |
130 | 0 | ret += bputch(buf, 't'); |
131 | 0 | continue; |
132 | | |
133 | 0 | case ' ': |
134 | 0 | if (flags & ESC_SPACE) |
135 | 0 | ret += bputch(buf, '\\'); |
136 | 0 | ret += bputch(buf, *pos); |
137 | 0 | continue; |
138 | | |
139 | 0 | case '\\': |
140 | 0 | if (flags & ESC_BACKSLASH) |
141 | 0 | ret += bputch(buf, '\\'); |
142 | 0 | ret += bputch(buf, *pos); |
143 | 0 | continue; |
144 | | |
145 | 0 | case '"': |
146 | 0 | if (flags & ESC_DBLQUOTE) |
147 | 0 | ret += bputch(buf, '\\'); |
148 | 0 | ret += bputch(buf, *pos); |
149 | 0 | continue; |
150 | | |
151 | 0 | case '\'': |
152 | 0 | if (flags & ESC_SGLQUOTE) |
153 | 0 | ret += bputch(buf, '\\'); |
154 | 0 | ret += bputch(buf, *pos); |
155 | 0 | continue; |
156 | | |
157 | 0 | case '`': |
158 | 0 | if (flags & ESC_BACKTICK) |
159 | 0 | ret += bputch(buf, '\\'); |
160 | 0 | ret += bputch(buf, *pos); |
161 | 0 | continue; |
162 | | |
163 | 0 | case '$': |
164 | 0 | if (flags & ESC_DOLLAR) |
165 | 0 | ret += bputch(buf, '\\'); |
166 | 0 | ret += bputch(buf, *pos); |
167 | 0 | continue; |
168 | | |
169 | 0 | case ']': |
170 | 0 | if (flags & ESC_CLBRACKET) |
171 | 0 | ret += bputch(buf, '\\'); |
172 | 0 | ret += bputch(buf, *pos); |
173 | 0 | continue; |
174 | | |
175 | | /* remaining: !#&'()*;<=>?[^{|}~ */ |
176 | | |
177 | 0 | default: |
178 | 0 | if (*pos >= 0x20 && *pos < 0x7f) { |
179 | 0 | if (flags & ESC_OTHER) |
180 | 0 | ret += bputch(buf, '\\'); |
181 | 0 | ret += bputch(buf, *pos); |
182 | 0 | continue; |
183 | 0 | } |
184 | 0 | } |
185 | 0 | ret += bputch(buf, '\\'); |
186 | 0 | ret += bputch(buf, 'x'); |
187 | 0 | ret += bputhex(buf, *pos); |
188 | 0 | } |
189 | | |
190 | 0 | return ret; |
191 | 0 | } |
192 | | |
193 | | printfrr_ext_autoreg_p("SE", printfrr_escape); |
194 | | static ssize_t printfrr_escape(struct fbuf *buf, struct printfrr_eargs *ea, |
195 | | const void *vptr) |
196 | 0 | { |
197 | 0 | ssize_t len = printfrr_ext_len(ea); |
198 | 0 | const uint8_t *ptr = vptr; |
199 | 0 | bool null_is_empty = false; |
200 | |
|
201 | 0 | if (ea->fmt[0] == 'n') { |
202 | 0 | null_is_empty = true; |
203 | 0 | ea->fmt++; |
204 | 0 | } |
205 | |
|
206 | 0 | if (!ptr) { |
207 | 0 | if (null_is_empty) |
208 | 0 | return 0; |
209 | 0 | return bputs(buf, "(null)"); |
210 | 0 | } |
211 | | |
212 | 0 | if (len < 0) |
213 | 0 | len = strlen((const char *)ptr); |
214 | |
|
215 | 0 | return bquote(buf, ptr, len, ESC_ALL); |
216 | 0 | } |
217 | | |
218 | | printfrr_ext_autoreg_p("SQ", printfrr_quote); |
219 | | static ssize_t printfrr_quote(struct fbuf *buf, struct printfrr_eargs *ea, |
220 | | const void *vptr) |
221 | 0 | { |
222 | 0 | ssize_t len = printfrr_ext_len(ea); |
223 | 0 | const uint8_t *ptr = vptr; |
224 | 0 | ssize_t ret = 0; |
225 | 0 | bool null_is_empty = false; |
226 | 0 | bool do_quotes = false; |
227 | 0 | unsigned int flags = ESC_QUOTSTRING; |
228 | |
|
229 | 0 | while (ea->fmt[0]) { |
230 | 0 | switch (ea->fmt[0]) { |
231 | 0 | case 'n': |
232 | 0 | null_is_empty = true; |
233 | 0 | ea->fmt++; |
234 | 0 | continue; |
235 | 0 | case 'q': |
236 | 0 | do_quotes = true; |
237 | 0 | ea->fmt++; |
238 | 0 | continue; |
239 | 0 | case 's': |
240 | 0 | flags |= ESC_CLBRACKET; |
241 | 0 | flags &= ~ESC_N_R_T; |
242 | 0 | ea->fmt++; |
243 | 0 | continue; |
244 | 0 | } |
245 | 0 | break; |
246 | 0 | } |
247 | | |
248 | 0 | if (!ptr) { |
249 | 0 | if (null_is_empty) |
250 | 0 | return bputs(buf, do_quotes ? "\"\"" : ""); |
251 | 0 | return bputs(buf, "(null)"); |
252 | 0 | } |
253 | | |
254 | 0 | if (len < 0) |
255 | 0 | len = strlen((const char *)ptr); |
256 | |
|
257 | 0 | if (do_quotes) |
258 | 0 | ret += bputch(buf, '"'); |
259 | 0 | ret += bquote(buf, ptr, len, flags); |
260 | 0 | if (do_quotes) |
261 | 0 | ret += bputch(buf, '"'); |
262 | 0 | return ret; |
263 | 0 | } |
264 | | |
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); |
269 | | |
270 | | ssize_t printfrr_time(struct fbuf *buf, struct printfrr_eargs *ea, |
271 | | const struct timespec *ts, unsigned int flags) |
272 | 0 | { |
273 | 0 | bool have_abs, have_anchor; |
274 | |
|
275 | 0 | if (!(flags & TIMEFMT_PRESELECT)) { |
276 | 0 | switch (ea->fmt[0]) { |
277 | 0 | case 'I': |
278 | | /* no bit set */ |
279 | 0 | break; |
280 | 0 | case 'M': |
281 | 0 | flags |= TIMEFMT_MONOTONIC; |
282 | 0 | break; |
283 | 0 | case 'R': |
284 | 0 | flags |= TIMEFMT_REALTIME; |
285 | 0 | break; |
286 | 0 | default: |
287 | 0 | return bputs(buf, |
288 | 0 | "{invalid time format input specifier}"); |
289 | 0 | } |
290 | 0 | ea->fmt++; |
291 | |
|
292 | 0 | if (ea->fmt[0] == 's') { |
293 | 0 | flags |= TIMEFMT_SINCE; |
294 | 0 | ea->fmt++; |
295 | 0 | } else if (ea->fmt[0] == 'u') { |
296 | 0 | flags |= TIMEFMT_UNTIL; |
297 | 0 | ea->fmt++; |
298 | 0 | } |
299 | 0 | } |
300 | | |
301 | 0 | have_abs = !!(flags & TIMEFMT_ABSOLUTE); |
302 | 0 | have_anchor = !!(flags & TIMEFMT_ANCHORS); |
303 | |
|
304 | 0 | if (have_abs ^ have_anchor) |
305 | 0 | return printfrr_abstime(buf, ea, ts, flags); |
306 | 0 | else |
307 | 0 | return printfrr_reltime(buf, ea, ts, flags); |
308 | 0 | } |
309 | | |
310 | | static ssize_t do_subsec(struct fbuf *buf, const struct timespec *ts, |
311 | | int precision, unsigned int flags) |
312 | 0 | { |
313 | 0 | unsigned long long frac; |
314 | |
|
315 | 0 | if (precision <= 0 || (flags & TIMEFMT_SECONDS)) |
316 | 0 | return 0; |
317 | | |
318 | 0 | frac = ts->tv_nsec; |
319 | 0 | if (precision > 9) |
320 | 0 | precision = 9; |
321 | 0 | for (int i = precision; i < 9; i++) |
322 | 0 | frac /= 10; |
323 | 0 | return bprintfrr(buf, ".%0*llu", precision, frac); |
324 | 0 | } |
325 | | |
326 | | static ssize_t printfrr_abstime(struct fbuf *buf, struct printfrr_eargs *ea, |
327 | | const struct timespec *ts, unsigned int flags) |
328 | 0 | { |
329 | 0 | struct timespec real_ts[1]; |
330 | 0 | struct tm tm; |
331 | 0 | char cbuf[32] = ""; /* manpage says 26 for ctime_r */ |
332 | 0 | ssize_t ret = 0; |
333 | 0 | int precision = ea->precision; |
334 | |
|
335 | 0 | while (ea->fmt[0]) { |
336 | 0 | char ch = *ea->fmt++; |
337 | |
|
338 | 0 | switch (ch) { |
339 | 0 | case 'p': |
340 | 0 | flags |= TIMEFMT_SPACE; |
341 | 0 | continue; |
342 | 0 | case 'i': |
343 | 0 | flags |= TIMEFMT_ISO8601; |
344 | 0 | continue; |
345 | 0 | } |
346 | | |
347 | 0 | ea->fmt--; |
348 | 0 | break; |
349 | 0 | } |
350 | | |
351 | 0 | if (flags & TIMEFMT_SKIP) |
352 | 0 | return 0; |
353 | 0 | if (!ts) |
354 | 0 | return bputch(buf, '-'); |
355 | | |
356 | 0 | if (flags & TIMEFMT_REALTIME) |
357 | 0 | *real_ts = *ts; |
358 | 0 | else if (flags & TIMEFMT_MONOTONIC) { |
359 | 0 | struct timespec mono_now[1]; |
360 | |
|
361 | 0 | clock_gettime(CLOCK_REALTIME, real_ts); |
362 | 0 | clock_gettime(CLOCK_MONOTONIC, mono_now); |
363 | |
|
364 | 0 | timespecsub(real_ts, mono_now, real_ts); |
365 | 0 | timespecadd(real_ts, ts, real_ts); |
366 | 0 | } else { |
367 | 0 | clock_gettime(CLOCK_REALTIME, real_ts); |
368 | |
|
369 | 0 | if (flags & TIMEFMT_SINCE) |
370 | 0 | timespecsub(real_ts, ts, real_ts); |
371 | 0 | else /* flags & TIMEFMT_UNTIL */ |
372 | 0 | timespecadd(real_ts, ts, real_ts); |
373 | 0 | } |
374 | |
|
375 | 0 | localtime_r(&real_ts->tv_sec, &tm); |
376 | |
|
377 | 0 | if (flags & TIMEFMT_ISO8601) { |
378 | 0 | if (flags & TIMEFMT_SPACE) |
379 | 0 | strftime(cbuf, sizeof(cbuf), "%Y-%m-%d %H:%M:%S", &tm); |
380 | 0 | else |
381 | 0 | strftime(cbuf, sizeof(cbuf), "%Y-%m-%dT%H:%M:%S", &tm); |
382 | 0 | ret += bputs(buf, cbuf); |
383 | |
|
384 | 0 | if (precision == -1) |
385 | 0 | precision = 3; |
386 | 0 | ret += do_subsec(buf, real_ts, precision, flags); |
387 | 0 | } else { |
388 | 0 | size_t len; |
389 | |
|
390 | 0 | asctime_r(&tm, cbuf); |
391 | |
|
392 | 0 | len = strlen(cbuf); |
393 | 0 | if (!len) |
394 | | /* WTF. */ |
395 | 0 | return 0; |
396 | 0 | if (cbuf[len - 1] == '\n') |
397 | 0 | cbuf[len - 1] = '\0'; |
398 | |
|
399 | 0 | ret += bputs(buf, cbuf); |
400 | 0 | } |
401 | 0 | return ret; |
402 | 0 | } |
403 | | |
404 | | static ssize_t printfrr_reltime(struct fbuf *buf, struct printfrr_eargs *ea, |
405 | | const struct timespec *ts, unsigned int flags) |
406 | 0 | { |
407 | 0 | struct timespec real_ts[1]; |
408 | 0 | ssize_t ret = 0; |
409 | 0 | const char *space = ""; |
410 | 0 | const char *dashes = "-"; |
411 | 0 | int precision = ea->precision; |
412 | |
|
413 | 0 | while (ea->fmt[0]) { |
414 | 0 | char ch = *ea->fmt++; |
415 | |
|
416 | 0 | switch (ch) { |
417 | 0 | case 'p': |
418 | 0 | flags |= TIMEFMT_SPACE; |
419 | 0 | space = " "; |
420 | 0 | continue; |
421 | 0 | case 't': |
422 | 0 | flags |= TIMEFMT_BASIC; |
423 | 0 | continue; |
424 | 0 | case 'd': |
425 | 0 | flags |= TIMEFMT_DECIMAL; |
426 | 0 | continue; |
427 | 0 | case 'm': |
428 | 0 | flags |= TIMEFMT_MMSS; |
429 | 0 | dashes = "--:--"; |
430 | 0 | continue; |
431 | 0 | case 'h': |
432 | 0 | flags |= TIMEFMT_HHMMSS; |
433 | 0 | dashes = "--:--:--"; |
434 | 0 | continue; |
435 | 0 | case 'x': |
436 | 0 | flags |= TIMEFMT_DASHES; |
437 | 0 | continue; |
438 | 0 | } |
439 | | |
440 | 0 | ea->fmt--; |
441 | 0 | break; |
442 | 0 | } |
443 | | |
444 | 0 | if (flags & TIMEFMT_SKIP) |
445 | 0 | return 0; |
446 | 0 | if (!ts) |
447 | 0 | return bputch(buf, '-'); |
448 | | |
449 | 0 | if (flags & TIMEFMT_ABSOLUTE) { |
450 | 0 | struct timespec anchor[1]; |
451 | |
|
452 | 0 | if (flags & TIMEFMT_REALTIME) |
453 | 0 | clock_gettime(CLOCK_REALTIME, anchor); |
454 | 0 | else |
455 | 0 | clock_gettime(CLOCK_MONOTONIC, anchor); |
456 | 0 | if (flags & TIMEFMT_UNTIL) |
457 | 0 | timespecsub(ts, anchor, real_ts); |
458 | 0 | else /* flags & TIMEFMT_SINCE */ |
459 | 0 | timespecsub(anchor, ts, real_ts); |
460 | 0 | } else |
461 | 0 | *real_ts = *ts; |
462 | |
|
463 | 0 | if (real_ts->tv_sec == 0 && real_ts->tv_nsec == 0 && |
464 | 0 | (flags & TIMEFMT_DASHES)) |
465 | 0 | return bputs(buf, dashes); |
466 | | |
467 | 0 | if (real_ts->tv_sec < 0) { |
468 | 0 | if (flags & TIMEFMT_DASHES) |
469 | 0 | return bputs(buf, dashes); |
470 | | |
471 | | /* -0.3s is { -1s + 700ms } */ |
472 | 0 | real_ts->tv_sec = -real_ts->tv_sec - 1; |
473 | 0 | real_ts->tv_nsec = 1000000000L - real_ts->tv_nsec; |
474 | 0 | if (real_ts->tv_nsec >= 1000000000L) { |
475 | 0 | real_ts->tv_sec++; |
476 | 0 | real_ts->tv_nsec -= 1000000000L; |
477 | 0 | } |
478 | | |
479 | | /* all formats have a - make sense in front */ |
480 | 0 | ret += bputch(buf, '-'); |
481 | 0 | } |
482 | | |
483 | 0 | if (flags & TIMEFMT_DECIMAL) { |
484 | 0 | ret += bprintfrr(buf, "%lld", (long long)real_ts->tv_sec); |
485 | 0 | if (precision == -1) |
486 | 0 | precision = 3; |
487 | 0 | ret += do_subsec(buf, real_ts, precision, flags); |
488 | 0 | return ret; |
489 | 0 | } |
490 | | |
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 |
493 | | */ |
494 | 0 | lldiv_t min_sec = lldiv(real_ts->tv_sec, 60); |
495 | |
|
496 | 0 | if (flags & TIMEFMT_MMSS) { |
497 | 0 | ret += bprintfrr(buf, "%02lld:%02lld", min_sec.quot, |
498 | 0 | min_sec.rem); |
499 | 0 | ret += do_subsec(buf, real_ts, precision, flags); |
500 | 0 | return ret; |
501 | 0 | } |
502 | | |
503 | 0 | lldiv_t hour_min = min_sec.quot ? lldiv(min_sec.quot, 60) : (lldiv_t){}; |
504 | |
|
505 | 0 | if (flags & TIMEFMT_HHMMSS) { |
506 | 0 | ret += bprintfrr(buf, "%02lld:%02lld:%02lld", hour_min.quot, |
507 | 0 | hour_min.rem, min_sec.rem); |
508 | 0 | ret += do_subsec(buf, real_ts, precision, flags); |
509 | 0 | return ret; |
510 | 0 | } |
511 | | |
512 | 0 | lldiv_t day_hour = |
513 | 0 | hour_min.quot ? lldiv(hour_min.quot, 24) : (lldiv_t){}; |
514 | 0 | lldiv_t week_day = |
515 | 0 | day_hour.quot ? lldiv(day_hour.quot, 7) : (lldiv_t){}; |
516 | | |
517 | | /* if sub-second precision is not supported, return */ |
518 | 0 | if (flags & TIMEFMT_BASIC) { |
519 | | /* match frrtime_to_interval (without space flag) */ |
520 | 0 | if (week_day.quot) |
521 | 0 | ret += bprintfrr(buf, "%lldw%s%lldd%s%02lldh", |
522 | 0 | week_day.quot, space, week_day.rem, |
523 | 0 | space, day_hour.rem); |
524 | 0 | else if (day_hour.quot) |
525 | 0 | ret += bprintfrr(buf, "%lldd%s%02lldh%s%02lldm", |
526 | 0 | day_hour.quot, space, day_hour.rem, |
527 | 0 | space, hour_min.rem); |
528 | 0 | else |
529 | 0 | ret += bprintfrr(buf, "%02lld:%02lld:%02lld", |
530 | 0 | hour_min.quot, hour_min.rem, |
531 | 0 | min_sec.rem); |
532 | | /* no sub-seconds here */ |
533 | 0 | return ret; |
534 | 0 | } |
535 | | |
536 | | /* default format */ |
537 | 0 | if (week_day.quot) |
538 | 0 | ret += bprintfrr(buf, "%lldw%s", week_day.quot, space); |
539 | 0 | if (week_day.rem || week_day.quot) |
540 | 0 | ret += bprintfrr(buf, "%lldd%s", week_day.rem, space); |
541 | |
|
542 | 0 | ret += bprintfrr(buf, "%02lld:%02lld:%02lld", day_hour.rem, |
543 | 0 | hour_min.rem, min_sec.rem); |
544 | |
|
545 | 0 | if (precision == -1) |
546 | 0 | precision = 3; |
547 | 0 | ret += do_subsec(buf, real_ts, precision, flags); |
548 | 0 | return ret; |
549 | 0 | } |
550 | | |
551 | | printfrr_ext_autoreg_p("TS", printfrr_ts); |
552 | | static ssize_t printfrr_ts(struct fbuf *buf, struct printfrr_eargs *ea, |
553 | | const void *vptr) |
554 | 0 | { |
555 | 0 | const struct timespec *ts = vptr; |
556 | |
|
557 | 0 | return printfrr_time(buf, ea, ts, 0); |
558 | 0 | } |
559 | | |
560 | | printfrr_ext_autoreg_p("TV", printfrr_tv); |
561 | | static ssize_t printfrr_tv(struct fbuf *buf, struct printfrr_eargs *ea, |
562 | | const void *vptr) |
563 | 0 | { |
564 | 0 | const struct timeval *tv = vptr; |
565 | 0 | struct timespec ts; |
566 | |
|
567 | 0 | if (!tv) |
568 | 0 | return printfrr_time(buf, ea, NULL, 0); |
569 | | |
570 | 0 | ts.tv_sec = tv->tv_sec; |
571 | 0 | ts.tv_nsec = tv->tv_usec * 1000; |
572 | 0 | return printfrr_time(buf, ea, &ts, 0); |
573 | 0 | } |
574 | | |
575 | | printfrr_ext_autoreg_p("TT", printfrr_tt); |
576 | | static ssize_t printfrr_tt(struct fbuf *buf, struct printfrr_eargs *ea, |
577 | | const void *vptr) |
578 | 0 | { |
579 | 0 | const time_t *tt = vptr; |
580 | 0 | struct timespec ts; |
581 | |
|
582 | 0 | if (!tt) |
583 | 0 | return printfrr_time(buf, ea, NULL, TIMEFMT_SECONDS); |
584 | | |
585 | 0 | ts.tv_sec = *tt; |
586 | 0 | ts.tv_nsec = 0; |
587 | 0 | return printfrr_time(buf, ea, &ts, TIMEFMT_SECONDS); |
588 | 0 | } |