aboutsummaryrefslogtreecommitdiff
path: root/src/fmt.cff
diff options
context:
space:
mode:
Diffstat (limited to 'src/fmt.cff')
-rw-r--r--src/fmt.cff94
1 files changed, 94 insertions, 0 deletions
diff --git a/src/fmt.cff b/src/fmt.cff
new file mode 100644
index 0000000..885ea50
--- /dev/null
+++ b/src/fmt.cff
@@ -0,0 +1,94 @@
+import "all.hff";
+
+extern fn vpfmt(proc *fn(u8, *void) void, parg *void, fmt *const u8, ap va_list) void {
+ defmacro p(x) [ proc(x, parg) ]
+ defmacro ps(s) [
+ for let $i = 0; (s)[$i] != 0; ++$i {
+ p(s[$i]);
+ }
+ ]
+ let buf [100]u8 = {};
+ for let c u8 = *fmt; c != 0; c = *++fmt {
+ assert(c != 0, "?");
+ if c != '%' {
+ p(c);
+ if fmt[1] == 0 { break; }
+ continue;
+ }
+ let quote = #f;
+ #'fmt do {
+ switch (c = *++fmt) {
+ case 'i';
+ sprintf(buf, "%d", ap->arg(int));
+ ps(buf);
+ case 'q';
+ quote = #t;
+ continue #'fmt;
+ case 'c';
+ let ch u32 = ap->arg(int);
+ if quote {
+ extern fn isprint(int) int;
+ p('\'');
+ for ch = bswap32(ch); ch != 0; ch >>= 8 {
+ if ch & 0xFF != 0 {
+ if isprint(ch) != 0 { p(ch); }
+ else {
+ p('\\');
+ p('0' + (ch % 8));
+ p('0' + ((ch / 8) % 8));
+ p('0' + ((ch / 8 / 8) % 8));
+ }
+ }
+ }
+ p('\'');
+ } else {
+ if ch == 0 { p(0); }
+ else {
+ for ch = bswap32(ch); ch != 0; ch >>= 8 {
+ if ch & 0xFF != 0 {
+ p(ch & 0xFF);
+ }
+ }
+ }
+ }
+ case 's';
+ let s = ap->arg(*const u8);
+ if quote {
+ extern fn isprint(int) int;
+ p('\"');
+ for let c u8 #?; (c = *s++) != 0; {
+ if isprint(c) != 0 {
+ p(c);
+ } else {
+ p('\\');
+ p('0' + (c % 8));
+ p('0' + ((c / 8) % 8));
+ p('0' + ((c / 8 / 8) % 8));
+ }
+ }
+ p('\"');
+ } else {
+ ps(s);
+ }
+ case else
+ // assert(#f, "bad fmt '%c' @ %d", c, i);
+ }
+ } while #f;
+ }
+}
+
+extern fn vefmt(fmt *const u8, ap va_list) void {
+ fn epri(c u8, *void) void {
+ fputc(c, stderr);
+ }
+
+ vpfmt(&epri, #null, fmt, ap);
+}
+
+extern fn efmt(fmt *const u8, ...) void {
+ let ap va_list #?;
+ ap->start(fmt);
+
+ vefmt(fmt, ap);
+ ap->end();
+}