diff options
Diffstat (limited to 'lib/printfrr.h')
-rw-r--r-- | lib/printfrr.h | 159 |
1 files changed, 144 insertions, 15 deletions
diff --git a/lib/printfrr.h b/lib/printfrr.h index 418e839d9..4338ac3a2 100644 --- a/lib/printfrr.h +++ b/lib/printfrr.h @@ -28,10 +28,17 @@ extern "C" { #endif +struct fmt_outpos { + unsigned int off_start, off_end; +}; + struct fbuf { char *buf; char *pos; size_t len; + + struct fmt_outpos *outpos; + size_t outpos_n, outpos_i; }; #define at(a, b) PRINTFRR(a, b) @@ -105,6 +112,8 @@ char *asnprintfrr(struct memtype *mt, char *out, size_t sz, */ #define printfrr_ext_char(ch) ((ch) >= 'A' && (ch) <= 'Z') +struct printfrr_eargs; + struct printfrr_ext { /* embedded string to minimize cache line pollution */ char match[8]; @@ -112,23 +121,80 @@ struct printfrr_ext { /* both can be given, if not the code continues searching * (you can do %pX and %dX in 2 different entries) * - * return value: number of bytes consumed from the format string, so - * you can consume extra flags (e.g. register for "%pX", consume - * "%pXfoo" or "%pXbar" for flags.) Convention is to make those flags - * lowercase letters or numbers. + * return value: number of bytes that would be printed if the buffer + * was large enough. be careful about not under-reporting this; + * otherwise asnprintf() & co. will get broken. Returning -1 means + * something went wrong & default %p/%d handling should be executed. * - * bsz is a compile-time constant in printf; it's gonna be relatively - * small. This isn't designed to print Shakespeare from a pointer. + * to consume extra input flags after %pXY, increment *fmt. It points + * at the first character after %pXY at entry. Convention is to make + * those flags lowercase letters or numbers. + */ + ssize_t (*print_ptr)(struct fbuf *buf, struct printfrr_eargs *info, + const void *); + ssize_t (*print_int)(struct fbuf *buf, struct printfrr_eargs *info, + uintmax_t); +}; + +/* additional information passed to extended formatters */ + +struct printfrr_eargs { + /* position in the format string. Points to directly after the + * extension specifier. Increment when consuming extra "flag + * characters". + */ + const char *fmt; + + /* %.1234x / %.*x + * not POSIX compatible when used with %p, will cause warnings from + * GCC & clang. Usable with %d. Not used by the printfrr() itself + * for extension specifiers, so essentially available as a "free" + * parameter. -1 if not specified. Value in the format string + * cannot be negative, but negative values can be passed with %.*x + */ + int precision; + + /* %1234x / %*x + * regular width specification. Internally handled by printfrr(), set + * to 0 if consumed by the extension in order to suppress standard + * width/padding behavior. 0 if not specified. * - * prec is the precision specifier (the 999 in "%.999p") -1 means - * none given (value in the format string cannot be negative) + * NB: always positive, even if a negative value is passed in with + * %*x. (The sign is used for the - flag.) + */ + int width; + + /* %#x + * "alternate representation" flag, not POSIX compatible when used + * with %p or %d, will cause warnings from GCC & clang. Not used by + * printfrr() itself for extension specifiers. */ - ssize_t (*print_ptr)(char *buf, size_t bsz, const char *fmt, int prec, - const void *); - ssize_t (*print_int)(char *buf, size_t bsz, const char *fmt, int prec, - uintmax_t); + bool alt_repr; + + /* %-x + * left-pad flag. Internally handled by printfrr() if width is + * nonzero. Only use if the extension sets width to 0. + */ + bool leftadj; }; +/* for any extension that needs a buffer length */ + +static inline ssize_t printfrr_ext_len(struct printfrr_eargs *ea) +{ + ssize_t rv; + + if (ea->precision >= 0) + rv = ea->precision; + else if (ea->width >= 0) { + rv = ea->width; + ea->width = -1; + } else + rv = -1; + + return rv; +} + /* no locking - must be called when single threaded (e.g. at startup.) * this restriction hopefully won't be a huge bother considering normal usage * scenarios... @@ -136,7 +202,7 @@ struct printfrr_ext { void printfrr_ext_reg(const struct printfrr_ext *); #define printfrr_ext_autoreg_p(matchs, print_fn) \ - static ssize_t print_fn(char *, size_t, const char *, int, \ + static ssize_t print_fn(struct fbuf *, struct printfrr_eargs *, \ const void *); \ static const struct printfrr_ext _printext_##print_fn = { \ .match = matchs, \ @@ -149,7 +215,8 @@ void printfrr_ext_reg(const struct printfrr_ext *); /* end */ #define printfrr_ext_autoreg_i(matchs, print_fn) \ - static ssize_t print_fn(char *, size_t, const char *, int, uintmax_t); \ + static ssize_t print_fn(struct fbuf *, struct printfrr_eargs *, \ + uintmax_t); \ static const struct printfrr_ext _printext_##print_fn = { \ .match = matchs, \ .print_int = print_fn, \ @@ -160,7 +227,11 @@ void printfrr_ext_reg(const struct printfrr_ext *); } \ /* end */ -/* fbuf helper functions */ +/* fbuf helper functions - note all 3 of these return the length that would + * be written regardless of how much space was available in the buffer, as + * needed for implementing printfrr extensions. (They also accept NULL buf + * for that.) + */ static inline ssize_t bputs(struct fbuf *buf, const char *str) { @@ -184,6 +255,64 @@ static inline ssize_t bputch(struct fbuf *buf, char ch) return 1; } +static inline ssize_t bputhex(struct fbuf *buf, uint8_t val) +{ + static const char hexch[] = "0123456789abcdef"; + + if (buf && buf->pos < buf->buf + buf->len) + *buf->pos++ = hexch[(val >> 4) & 0xf]; + if (buf && buf->pos < buf->buf + buf->len) + *buf->pos++ = hexch[val & 0xf]; + return 2; +} + +/* %pVA extension, equivalent to Linux kernel %pV */ + +struct va_format { + const char *fmt; + va_list *va; +}; + +#ifdef _FRR_ATTRIBUTE_PRINTFRR +#pragma FRR printfrr_ext "%pFB" (struct fbuf *) +#pragma FRR printfrr_ext "%pVA" (struct va_format *) + +#pragma FRR printfrr_ext "%pHX" (signed char *) +#pragma FRR printfrr_ext "%pHX" (unsigned char *) +#pragma FRR printfrr_ext "%pHX" (void *) +#pragma FRR printfrr_ext "%pHS" (signed char *) +#pragma FRR printfrr_ext "%pHS" (unsigned char *) +#pragma FRR printfrr_ext "%pHS" (void *) + +#pragma FRR printfrr_ext "%pSE" (char *) +#pragma FRR printfrr_ext "%pSQ" (char *) +#endif + +/* when using non-ISO-C compatible extension specifiers... */ + +#ifdef _FRR_ATTRIBUTE_PRINTFRR +#define FMT_NSTD_BEGIN +#define FMT_NSTD_END +#else /* !_FRR_ATTRIBUTE_PRINTFRR */ +#define FMT_NSTD_BEGIN \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wformat\"") \ + /* end */ +#define FMT_NSTD_END \ + _Pragma("GCC diagnostic pop") \ + /* end */ +#endif + +#define FMT_NSTD(expr) \ + ({ \ + typeof(expr) _v; \ + FMT_NSTD_BEGIN \ + _v = expr; \ + FMT_NSTD_END \ + _v; \ + }) \ + /* end */ + #ifdef __cplusplus } #endif |