summaryrefslogtreecommitdiffstats
path: root/lib/printfrr.h
diff options
context:
space:
mode:
Diffstat (limited to 'lib/printfrr.h')
-rw-r--r--lib/printfrr.h159
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