aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicholas <nbnoll@eml.cc>2021-11-11 08:10:09 -0800
committerNicholas <nbnoll@eml.cc>2021-11-11 08:16:49 -0800
commit45f9449ae0b904917110d3f6937d0266daa84769 (patch)
tree701847b3eb6a849e730581660aa5dfb1f0a90ba0
parent7ea1cdb7d31f00024f5a1d124b42cd19a03b959a (diff)
feat: added libfmt
-rw-r--r--include/libfmt.h62
-rw-r--r--sys/libfmt/do.c186
-rw-r--r--sys/libfmt/internal.h15
-rw-r--r--sys/libfmt/print.c0
4 files changed, 263 insertions, 0 deletions
diff --git a/include/libfmt.h b/include/libfmt.h
new file mode 100644
index 0000000..17effc6
--- /dev/null
+++ b/include/libfmt.h
@@ -0,0 +1,62 @@
+#pragma once
+
+typedef struct fmt·State fmt·State;
+
+struct fmt·State
+{
+ struct{
+ char *beg;
+ char *cur;
+ char *end;
+ } buffer;
+ int (*flush)(fmt·State *);
+ int n;
+ va_list args;
+ rune verb;
+ ulong flag;
+ int width;
+ int prec;
+};
+
+#define iota(x) (1 << (x))
+enum
+{
+ fmt·Width = iota(0),
+ fmt·Left = iota(1),
+ fmt·Prec = iota(2),
+ fmt·Sharp = iota(3),
+ fmt·Space = iota(4),
+ fmt·Sign = iota(5),
+ fmt·Apost = iota(6),
+ fmt·Zero = iota(7),
+ fmt·Unsigned = iota(8),
+ fmt·Short = iota(9),
+ fmt·Long = iota(10),
+ fmt·Vlong = iota(11),
+ fmt·Comma = iota(12),
+ fmt·Byte = iota(13),
+ fmt·Ldouble = iota(14),
+ fmt·Flag = iota(15),
+};
+#undef iota
+
+/* normal printing interface */
+int fmt·print(char *fmt, ...);
+int fmt·fprint(int fd, char *fmt, ...);
+
+int fmt·sprint(char *buf, char *fmt, ...);
+int fmt·nsprint(int len, char *buf, char *fmt, ...);
+int fmt·esprint(char *buf, char *end, char *fmt, ...);
+
+int fmt·vprint(char *fmt, va_list args);
+int fmt·vfprint(int fd, char *fmt, va_list args);
+int fmt·vwrite(char *buf, char *fmt, va_list args);
+int fmt·vnwrite(int len, char *buf, char *fmt, va_list args);
+int fmt·vewrite(char *buf, char *end, char *fmt, va_list args);
+
+/* low-level interface: custom printing verbs */
+int fmt·do(fmt·State *, char *fmt);
+int fmt·put(fmt·State *, char *fmt, ...);
+int fmt·vput(fmt·State *, char *fmt, va_list args);
+int fmt·install(fmt·State *, int (*put)(fmt·State*));
+int fmt·flush(fmt·State *);
diff --git a/sys/libfmt/do.c b/sys/libfmt/do.c
new file mode 100644
index 0000000..3b8ff5a
--- /dev/null
+++ b/sys/libfmt/do.c
@@ -0,0 +1,186 @@
+#include "internal.h"
+#include <stdatomic.h>
+
+#define atomic _Atomic
+#define MaxFmt 128
+#define atomic·load atomic_load
+#define atomic·store atomic_store
+
+static struct
+{
+ atomic int len;
+ Verb verb[MaxFmt];
+} formatter =
+{
+ ATOMIC_VAR_INIT(27),
+ {
+ {' ', ·fmtflag},
+ {'#', ·fmtflag},
+ {'%', ·fmtpercent},
+ {'\'',·fmtflag},
+ {'+', ·fmtflag},
+ {',', ·fmtflag},
+ {'-', ·fmtflag},
+ {'C', ·fmtrune},
+ {'E', ·fmtfloat},
+ {'G', ·fmtfloat},
+ {'S', ·fmtutf8},
+ {'X', ·fmtint},
+ {'b', ·fmtint},
+ {'c', ·fmtchar},
+ {'d', ·fmtint},
+ {'e', ·fmtfloat},
+ {'f', ·fmtfloat},
+ {'g', ·fmtfloat},
+ {'h', ·fmtflag},
+ {'l', ·fmtflag},
+ {'n', ·fmtcount},
+ {'o', ·fmtint},
+ {'p', ·fmtint},
+ {'r', ·fmterr},
+ {'s', ·fmtstr},
+ {'u', ·fmtflag},
+ {'u', ·fmtint},
+ {'x', ·fmtint},
+ }
+};
+
+static Formatter
+format(int c)
+{
+ Verb *v, *e;
+ e = &formatter.verb[atomic·load(&formatter.len)];
+ for(v=e; v > formatter.verb; --v){
+ if(v->c == c)
+ return v->fmt;
+ }
+
+ return ·fmterr;
+}
+
+static char *
+dispatch(fmt·State *io, char *fmt)
+{
+ rune r;
+ int i, n;
+
+ io->flag = 0;
+ io->width = io->prec = 0;
+
+ /*
+ * the form of each print verb:
+ * % [flags] verb
+ * + the verb is a single character
+ * + each flag is either
+ * - a single character
+ * - a decimal numeric string
+ * - up to 2 decimal strings can be used
+ * - [width|*].[prec|*]
+ * - if missing, set to 0
+ * - if *, grab from varargs
+ */
+ for(;;){
+ fmt += utf8·decode(fmt, &r);
+ io->verb = r;
+ switch(r){
+ case 0:
+ return nil;
+ case '.':
+ io->flag |= fmt·Width|fmt·Prec;
+ continue;
+ case '0':
+ if(!(io->flag & fmt·Width)){
+ io->flag |= fmt·Zero;
+ continue;
+ }
+ /* fallthrough */
+ case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ i = 0;
+ while('0' <= r && r <= '9'){
+ i = 10*i + (r-'0');
+ r = *fmt++;
+ }
+ fmt--;
+ number:
+ if(io->flag & fmt·Width){
+ io->flag |= fmt·Prec;
+ io->prec = i;
+ }else{
+ io->flag |= fmt·Width;
+ io->width = i;
+ }
+ continue;
+ case '*':
+ i = va_arg(io->args, int);
+ if(i < 0){
+ if(io->flag&fmt·Prec){
+ io->flag ^= fmt·Prec;
+ io->prec = 0;
+ }
+ i = -i;
+ io->flag |= fmt·Left;
+ }
+ goto number;
+ }
+ n = format(r)(io);
+ if(n < 0)
+ return nil;
+ if(!n)
+ return fmt;
+ }
+}
+
+static char *
+flush(fmt·State *io, char *b, int len)
+{
+ io->n += b - io->buffer.cur;
+ io->buffer.cur = b;
+ if(!io->flush || !(*io->flush)(io) || io->buffer.cur + len >= io->buffer.end) {
+ io->buffer.end = io->buffer.cur;
+ return nil;
+ }
+ return io->buffer.cur;
+}
+
+int
+fmt·do(fmt·State *io, char *fmt)
+{
+ rune r;
+ int c, n;
+ char *b, *e;
+
+ for(;;){
+ b = io->buffer.cur;
+ e = io->buffer.end;
+
+ while((c = *(uchar *)fmt) && c != '%'){
+ if(utf8·onebyte(c)){
+ if(b + 1 > e){
+ if(!(b=flush(io, b, sizeof(*b))))
+ return -1;
+ e = io->buffer.end;
+ }
+ *b++ = *fmt++;
+ }else{
+ n = utf8·decode(fmt, &r);
+ if(b + n > e){
+ if(!(b=flush(io, b, sizeof(*b))))
+ return -1;
+ e = io->buffer.end;
+ }
+ while(n--)
+ *b++ = *fmt++;
+ }
+ fmt++;
+ io->n += b - io->buffer.cur;
+ io->buffer.cur = b;
+ if(c=='\0') /* we hit our nul terminator */
+ return io->n = n;
+ io->buffer.end = e;
+ }
+
+ if(!(fmt=dispatch(io, fmt)))
+ return -1;
+ }
+}
diff --git a/sys/libfmt/internal.h b/sys/libfmt/internal.h
new file mode 100644
index 0000000..56e64c8
--- /dev/null
+++ b/sys/libfmt/internal.h
@@ -0,0 +1,15 @@
+#pragma once
+
+#include <u.h>
+#include <base.h>
+#include <libutf.h>
+#include <libfmt.h>
+
+typedef int (*Formatter)(fmt·State *io);
+typedef struct Verb Verb;
+
+struct Verb
+{
+ int c;
+ Formatter fmt;
+};
diff --git a/sys/libfmt/print.c b/sys/libfmt/print.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/sys/libfmt/print.c