summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke T. Shumaker <lukeshu@lukeshu.com>2025-05-18 08:57:29 -0600
committerLuke T. Shumaker <lukeshu@lukeshu.com>2025-05-30 14:07:17 -0600
commit222ee4838251a88bf23779b5c2b405a4c7dc619c (patch)
tree990a89da658a4196da122bd2a64ff20531fb97fa
parent691d3fe7ff920e8113d174c2ce6c3126000a2f82 (diff)
libmisc: fmt.h: Add fmt_print2() and fmt_asprint()
-rw-r--r--libmisc/include/libmisc/fmt.h31
-rw-r--r--libmisc/tests/test_fmt.c9
2 files changed, 32 insertions, 8 deletions
diff --git a/libmisc/include/libmisc/fmt.h b/libmisc/include/libmisc/fmt.h
index c0743ff..6c04d99 100644
--- a/libmisc/include/libmisc/fmt.h
+++ b/libmisc/include/libmisc/fmt.h
@@ -9,6 +9,7 @@
#include <stddef.h> /* for size_t */
#include <stdint.h> /* for (u)int{n}_t */
+#include <stdlib.h> /* for realloc() */
#include <libmisc/macro.h>
#include <libmisc/obj.h>
@@ -99,6 +100,11 @@ void fmt_print_bool(lo_interface fmt_dest w, bool b);
const char * : fmt_print_str , \
bool : fmt_print_bool )(w, val)
+/** Same as fmt_print(), but usable from inside of fmt_print(). */
+#define fmt_print2(w, ...) do { LM_FOREACH_PARAM2_(_fmt_param2, (w), __VA_ARGS__) } while (0)
+#define _fmt_param2(...) _LM_DEFER2(_fmt_param_indirect)()(__VA_ARGS__)
+#define _fmt_param_indirect() _fmt_param
+
/* print-to-memory ************************************************************/
struct fmt_buf {
@@ -116,16 +122,25 @@ LO_IMPLEMENTATION_H(fmt_dest, struct fmt_buf, fmt_buf);
_w.len; \
})
-/* justify ********************************************************************/
+#define fmt_asprint(...) ({ \
+ struct fmt_buf _w = {}; \
+ lo_interface fmt_dest w = lo_box_fmt_buf_as_fmt_dest(&_w); \
+ fmt_print(w, __VA_ARGS__); \
+ while (_w.cap <= _w.len) { \
+ _w.cap = _w.len + 1; \
+ _w.len = 0; \
+ _w.dat = realloc(_w.dat, _w.cap); \
+ fmt_print(w, __VA_ARGS__); \
+ } \
+ ((char *)_w.dat)[_w.len] = '\0'; \
+ _w.dat; \
+})
-/* *grubles about not being allowed to nest things* */
-#define _fmt_param_indirect() _fmt_param
-#define _fmt_print2(w, ...) do { LM_FOREACH_PARAM2_(_fmt_param2, (w), __VA_ARGS__) } while (0)
-#define _fmt_param2(...) _LM_DEFER2(_fmt_param_indirect)()(__VA_ARGS__)
+/* justify ********************************************************************/
#define fmt_print_ljust(w, width, fillchar, ...) do { \
size_t beg = LO_CALL(w, tell); \
- _fmt_print2(w, __VA_ARGS__); \
+ fmt_print2(w, __VA_ARGS__); \
while ((LO_CALL(w, tell) - beg) < width) \
fmt_print_byte(w, fillchar); \
} while (0)
@@ -133,10 +148,10 @@ LO_IMPLEMENTATION_H(fmt_dest, struct fmt_buf, fmt_buf);
#define fmt_print_rjust(w, width, fillchar, ...) do { \
struct fmt_buf _discard = {}; \
lo_interface fmt_dest discard = lo_box_fmt_buf_as_fmt_dest(&_discard); \
- _fmt_print2(discard, __VA_ARGS__); \
+ fmt_print2(discard, __VA_ARGS__); \
while (_discard.len++ < width) \
fmt_print_byte(w, fillchar); \
- _fmt_print2(w, __VA_ARGS__); \
+ fmt_print2(w, __VA_ARGS__); \
} while (0)
void fmt_print_base16_u8_(lo_interface fmt_dest w, uint8_t x);
diff --git a/libmisc/tests/test_fmt.c b/libmisc/tests/test_fmt.c
index a9157d6..64b3b8a 100644
--- a/libmisc/tests/test_fmt.c
+++ b/libmisc/tests/test_fmt.c
@@ -4,6 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
+#include <stdlib.h> /* for free() */
#include <string.h> /* for strcmp(), memcmp(), memset() */
#include <libmisc/fmt.h>
@@ -230,5 +231,13 @@ int main() {
test_assert(strcmp(str, "{0x68,0x65,0x6C,0x6C,0x6F,0x00}") == 0);
memset(str, 0, sizeof(str));
+ char *astr = fmt_asprint("");
+ test_assert(astr != NULL && astr[0] == '\0');
+ free(astr);
+
+ astr = fmt_asprint("hello ", (base2, 9), (qstr, " world!\n"));
+ test_assert(strcmp(astr, "hello 1001\" world!\\n\"") == 0);
+ free(astr);
+
return 0;
}