back to topotato report
topotato coverage report
Current view: top level - lib - printfrr.h (source / functions) Hit Total Coverage
Test: test_bgp_ecmp_enhe.py::BGP_Unnumbered_ECMP Lines: 12 28 42.9 %
Date: 2023-11-16 17:19:14 Functions: 1 4 25.0 %

          Line data    Source code
       1             : // SPDX-License-Identifier: ISC
       2             : /*
       3             :  * Copyright (c) 2019  David Lamparter, for NetDEF, Inc.
       4             :  */
       5             : 
       6             : #ifndef _FRR_PRINTFRR_H
       7             : #define _FRR_PRINTFRR_H
       8             : 
       9             : #include <stddef.h>
      10             : #include <stdarg.h>
      11             : #include <stdint.h>
      12             : 
      13             : #include "compiler.h"
      14             : #include "memory.h"
      15             : 
      16             : #ifdef __cplusplus
      17             : extern "C" {
      18             : #endif
      19             : 
      20             : struct fmt_outpos {
      21             :         unsigned int off_start, off_end;
      22             : };
      23             : 
      24             : struct fbuf {
      25             :         char *buf;
      26             :         char *pos;
      27             :         size_t len;
      28             : 
      29             :         struct fmt_outpos *outpos;
      30             :         size_t outpos_n, outpos_i;
      31             : };
      32             : 
      33             : #define at(a, b) PRINTFRR(a, b)
      34             : #define atn(a, b) \
      35             :         at(a, b) __attribute__((nonnull(1) _RET_NONNULL))
      36             : #define atm(a, b) \
      37             :         atn(a, b) __attribute__((malloc))
      38             : 
      39             : /* return value is length needed for the full string (excluding \0) in all
      40             :  * cases.  The functions write as much as they can, but continue regardless,
      41             :  * so the return value is independent of buffer length.  Both bprintfrr and
      42             :  * snprintf also accept NULL as output buffer.
      43             :  */
      44             : 
      45             : /* bprintfrr does NOT null terminate! use sparingly (only provided since it's
      46             :  * the most direct interface) - useful for incrementally building long text
      47             :  * (call bprintfrr repeatedly with the same buffer)
      48             :  */
      49             : ssize_t vbprintfrr(struct fbuf *out, const char *fmt, va_list) at(2, 0);
      50             : ssize_t  bprintfrr(struct fbuf *out, const char *fmt, ...)     at(2, 3);
      51             : 
      52             : /* these do null terminate like their snprintf cousins */
      53             : ssize_t vsnprintfrr(char *out, size_t sz, const char *fmt, va_list) at(3, 0);
      54             : ssize_t  snprintfrr(char *out, size_t sz, const char *fmt, ...)     at(3, 4);
      55             : 
      56             : /* c = continue / concatenate (append at the end of the string)
      57             :  * return value is would-be string length (regardless of buffer length),
      58             :  * i.e. includes already written chars */
      59             : ssize_t vcsnprintfrr(char *out, size_t sz, const char *fmt, va_list) at(3, 0);
      60             : ssize_t  csnprintfrr(char *out, size_t sz, const char *fmt, ...)     at(3, 4);
      61             : 
      62             : /* memory allocations don't fail in FRR, so you always get something here.
      63             :  * (in case of error, returns a strdup of the format string) */
      64             : char *vasprintfrr(struct memtype *mt, const char *fmt, va_list) atm(2, 0);
      65             : char  *asprintfrr(struct memtype *mt, const char *fmt, ...)     atm(2, 3);
      66             : 
      67             : /* try to use provided buffer (presumably from stack), allocate if it's too
      68             :  * short.  Must call XFREE(mt, return value) if return value != out.
      69             :  */
      70             : char *vasnprintfrr(struct memtype *mt, char *out, size_t sz,
      71             :                    const char *fmt, va_list) atn(4, 0);
      72             : char  *asnprintfrr(struct memtype *mt, char *out, size_t sz,
      73             :                    const char *fmt, ...)     atn(4, 5);
      74             : 
      75             : #define printfrr(fmt, ...)                                                     \
      76             :         do {                                                                   \
      77             :                 char buf[256], *out;                                           \
      78             :                 out = asnprintfrr(MTYPE_TMP, buf, sizeof(buf), fmt,            \
      79             :                                   ##__VA_ARGS__);                              \
      80             :                 fputs(out, stdout);                                            \
      81             :                 if (out != buf)                                                \
      82             :                         XFREE(MTYPE_TMP, out);                                 \
      83             :         } while (0)
      84             : 
      85             : #undef at
      86             : #undef atm
      87             : #undef atn
      88             : 
      89             : /* extension specs must start with a capital letter (this is a restriction
      90             :  * for both performance's and human understanding's sake.)
      91             :  *
      92             :  * Note that the entire thing mostly works because a letter directly following
      93             :  * a %p print specifier is extremely unlikely to occur (why would you want to
      94             :  * print "0x12345678HELLO"?)  Normally, you'd expect spacing or punctuation
      95             :  * after a placeholder.  That also means that neither of those works well for
      96             :  * extension purposes, e.g. "%p{foo}" is reasonable to see actually used.
      97             :  *
      98             :  * TODO: would be nice to support a "%pF%dF" specifier that consumes 2
      99             :  * arguments, e.g. to pass an integer + a list of known values...  can be
     100             :  * done, but a bit tricky.
     101             :  */
     102             : #define printfrr_ext_char(ch) ((ch) >= 'A' && (ch) <= 'Z')
     103             : 
     104             : struct printfrr_eargs;
     105             : 
     106             : struct printfrr_ext {
     107             :         /* embedded string to minimize cache line pollution */
     108             :         char match[8];
     109             : 
     110             :         /* both can be given, if not the code continues searching
     111             :          * (you can do %pX and %dX in 2 different entries)
     112             :          *
     113             :          * return value: number of bytes that would be printed if the buffer
     114             :          * was large enough.  be careful about not under-reporting this;
     115             :          * otherwise asnprintf() & co. will get broken.  Returning -1 means
     116             :          * something went wrong & default %p/%d handling should be executed.
     117             :          *
     118             :          * to consume extra input flags after %pXY, increment *fmt.  It points
     119             :          * at the first character after %pXY at entry.  Convention is to make
     120             :          * those flags lowercase letters or numbers.
     121             :          */
     122             :         ssize_t (*print_ptr)(struct fbuf *buf, struct printfrr_eargs *info,
     123             :                              const void *);
     124             :         ssize_t (*print_int)(struct fbuf *buf, struct printfrr_eargs *info,
     125             :                              uintmax_t);
     126             : };
     127             : 
     128             : /* additional information passed to extended formatters */
     129             : 
     130             : struct printfrr_eargs {
     131             :         /* position in the format string.  Points to directly after the
     132             :          * extension specifier.  Increment when consuming extra "flag
     133             :          * characters".
     134             :          */
     135             :         const char *fmt;
     136             : 
     137             :         /* %.1234x / %.*x
     138             :          * not POSIX compatible when used with %p, will cause warnings from
     139             :          * GCC & clang.  Usable with %d.  Not used by the printfrr() itself
     140             :          * for extension specifiers, so essentially available as a "free"
     141             :          * parameter.  -1 if not specified.  Value in the format string
     142             :          * cannot be negative, but negative values can be passed with %.*x
     143             :          */
     144             :         int precision;
     145             : 
     146             :         /* %1234x / %*x
     147             :          * regular width specification.  Internally handled by printfrr(), set
     148             :          * to 0 if consumed by the extension in order to suppress standard
     149             :          * width/padding behavior.  0 if not specified.
     150             :          *
     151             :          * NB: always positive, even if a negative value is passed in with
     152             :          * %*x.  (The sign is used for the - flag.)
     153             :          */
     154             :         int width;
     155             : 
     156             :         /* %#x
     157             :          * "alternate representation" flag, not POSIX compatible when used
     158             :          * with %p or %d, will cause warnings from GCC & clang.  Not used by
     159             :          * printfrr() itself for extension specifiers.
     160             :          */
     161             :         bool alt_repr;
     162             : 
     163             :         /* %-x
     164             :          * left-pad flag.  Internally handled by printfrr() if width is
     165             :          * nonzero.  Only use if the extension sets width to 0.
     166             :          */
     167             :         bool leftadj;
     168             : };
     169             : 
     170             : /* for any extension that needs a buffer length */
     171             : 
     172           0 : static inline ssize_t printfrr_ext_len(struct printfrr_eargs *ea)
     173             : {
     174           0 :         ssize_t rv;
     175             : 
     176           0 :         if (ea->precision >= 0)
     177           0 :                 rv = ea->precision;
     178           0 :         else if (ea->width >= 0) {
     179           0 :                 rv = ea->width;
     180           0 :                 ea->width = -1;
     181             :         } else
     182             :                 rv = -1;
     183             : 
     184           0 :         return rv;
     185             : }
     186             : 
     187             : /* no locking - must be called when single threaded (e.g. at startup.)
     188             :  * this restriction hopefully won't be a huge bother considering normal usage
     189             :  * scenarios...
     190             :  */
     191             : void printfrr_ext_reg(const struct printfrr_ext *);
     192             : 
     193             : #define printfrr_ext_autoreg_p(matchs, print_fn)                               \
     194             :         static ssize_t print_fn(struct fbuf *, struct printfrr_eargs *,        \
     195             :                                 const void *);                                 \
     196             :         static const struct printfrr_ext _printext_##print_fn = {              \
     197             :                 .match = matchs,                                               \
     198             :                 .print_ptr = print_fn,                                         \
     199             :         };                                                                     \
     200             :         static void _printreg_##print_fn(void) __attribute__((constructor));   \
     201             :         static void _printreg_##print_fn(void)                                 \
     202             :         {                                                                      \
     203             :                 printfrr_ext_reg(&_printext_##print_fn);                       \
     204             :         }                                                                      \
     205             :         MACRO_REQUIRE_SEMICOLON()
     206             : 
     207             : #define printfrr_ext_autoreg_i(matchs, print_fn)                               \
     208             :         static ssize_t print_fn(struct fbuf *, struct printfrr_eargs *,        \
     209             :                                 uintmax_t);                                    \
     210             :         static const struct printfrr_ext _printext_##print_fn = {              \
     211             :                 .match = matchs,                                               \
     212             :                 .print_int = print_fn,                                         \
     213             :         };                                                                     \
     214             :         static void _printreg_##print_fn(void) __attribute__((constructor));   \
     215             :         static void _printreg_##print_fn(void)                                 \
     216             :         {                                                                      \
     217             :                 printfrr_ext_reg(&_printext_##print_fn);                       \
     218             :         }                                                                      \
     219             :         MACRO_REQUIRE_SEMICOLON()
     220             : 
     221             : /* fbuf helper functions - note all 3 of these return the length that would
     222             :  * be written regardless of how much space was available in the buffer, as
     223             :  * needed for implementing printfrr extensions.  (They also accept NULL buf
     224             :  * for that.)
     225             :  */
     226             : 
     227        1251 : static inline ssize_t bputs(struct fbuf *buf, const char *str)
     228             : {
     229        1251 :         size_t len = strlen(str);
     230        1251 :         size_t ncopy;
     231             : 
     232        1251 :         if (!buf)
     233           0 :                 return len;
     234             : 
     235        1251 :         ncopy = MIN(len, (size_t)(buf->buf + buf->len - buf->pos));
     236        1251 :         memcpy(buf->pos, str, ncopy);
     237        1251 :         buf->pos += ncopy;
     238             : 
     239        1251 :         return len;
     240             : }
     241             : 
     242        2806 : static inline ssize_t bputch(struct fbuf *buf, char ch)
     243             : {
     244        2806 :         if (buf && buf->pos < buf->buf + buf->len)
     245        2806 :                 *buf->pos++ = ch;
     246        2760 :         return 1;
     247             : }
     248             : 
     249           0 : static inline ssize_t bputhex(struct fbuf *buf, uint8_t val)
     250             : {
     251           0 :         static const char hexch[] = "0123456789abcdef";
     252             : 
     253           0 :         if (buf && buf->pos < buf->buf + buf->len)
     254           0 :                 *buf->pos++ = hexch[(val >> 4) & 0xf];
     255           0 :         if (buf && buf->pos < buf->buf + buf->len)
     256           0 :                 *buf->pos++ = hexch[val & 0xf];
     257           0 :         return 2;
     258             : }
     259             : 
     260             : /* %pVA extension, equivalent to Linux kernel %pV */
     261             : 
     262             : struct va_format {
     263             :         const char *fmt;
     264             :         va_list *va;
     265             : };
     266             : 
     267             : #ifdef _FRR_ATTRIBUTE_PRINTFRR
     268             : #pragma FRR printfrr_ext "%pFB" (struct fbuf *)
     269             : #pragma FRR printfrr_ext "%pVA" (struct va_format *)
     270             : 
     271             : #pragma FRR printfrr_ext "%pHX" (signed char *)
     272             : #pragma FRR printfrr_ext "%pHX" (unsigned char *)
     273             : #pragma FRR printfrr_ext "%pHX" (void *)
     274             : #pragma FRR printfrr_ext "%pHS" (signed char *)
     275             : #pragma FRR printfrr_ext "%pHS" (unsigned char *)
     276             : #pragma FRR printfrr_ext "%pHS" (void *)
     277             : 
     278             : #pragma FRR printfrr_ext "%pSE" (char *)
     279             : #pragma FRR printfrr_ext "%pSQ" (char *)
     280             : 
     281             : #pragma FRR printfrr_ext "%pTS" (struct timespec *)
     282             : #pragma FRR printfrr_ext "%pTV" (struct timeval *)
     283             : #pragma FRR printfrr_ext "%pTT" (time_t *)
     284             : #endif
     285             : 
     286             : /* when using non-ISO-C compatible extension specifiers... */
     287             : 
     288             : #ifdef _FRR_ATTRIBUTE_PRINTFRR
     289             : #define FMT_NSTD_BEGIN
     290             : #define FMT_NSTD_END
     291             : #else /* !_FRR_ATTRIBUTE_PRINTFRR */
     292             : #define FMT_NSTD_BEGIN \
     293             :         _Pragma("GCC diagnostic push")                                         \
     294             :         _Pragma("GCC diagnostic ignored \"-Wformat\"")                         \
     295             :         /* end */
     296             : #define FMT_NSTD_END \
     297             :         _Pragma("GCC diagnostic pop")                                          \
     298             :         /* end */
     299             : #endif
     300             : 
     301             : #define FMT_NSTD(expr)                                                         \
     302             :         ({                                                                     \
     303             :                 FMT_NSTD_BEGIN                                                 \
     304             :                 typeof(expr) _v;                                               \
     305             :                 _v = expr;                                                     \
     306             :                 FMT_NSTD_END                                                   \
     307             :                 _v;                                                            \
     308             :         })                                                                     \
     309             :         /* end */
     310             : 
     311             : #ifdef __cplusplus
     312             : }
     313             : #endif
     314             : 
     315             : #endif

Generated by: LCOV version v1.16-topotato