flatten the repository and simplify Makefile - ploot - simple plotting tools | |
git clone git://bitreich.org/ploot git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65… | |
Log | |
Files | |
Refs | |
Tags | |
README | |
LICENSE | |
--- | |
commit a546b97f9fe7f4a9e30b514bfc257ba85cd9a7f9 | |
parent 1a7e49697644fe2740519b2c5f62a314e675c616 | |
Author: Josuah Demangeon <[email protected]> | |
Date: Tue, 22 Jun 2021 00:32:54 +0200 | |
flatten the repository and simplify Makefile | |
Diffstat: | |
M Makefile | 34 +++++++++++++++++------------… | |
D README | 60 -----------------------------… | |
A README.md | 57 +++++++++++++++++++++++++++++… | |
D config.mk | 4 ---- | |
A csv.c | 132 +++++++++++++++++++++++++++++… | |
A csv.h | 22 ++++++++++++++++++++++ | |
A drawille.c | 194 ++++++++++++++++++++++++++++++ | |
A drawille.h | 25 +++++++++++++++++++++++++ | |
R test.csv -> example.csv | 0 | |
A ffplot.c | 148 +++++++++++++++++++++++++++++… | |
A ffplot.h | 34 +++++++++++++++++++++++++++++… | |
R src/font.c -> font.c | 0 | |
A font.h | 20 ++++++++++++++++++++ | |
R src/font13.c -> font13.c | 0 | |
R src/font8.c -> font8.c | 0 | |
M ploot-braille.c | 36 ++++++++++++++---------------… | |
M ploot-csv.5 | 6 +++--- | |
M ploot-farbfeld.1 | 16 ++++++++-------- | |
M ploot-farbfeld.c | 67 +++++++++++++++--------------… | |
M ploot-feed.1 | 2 +- | |
M ploot-feed.c | 27 ++++++++++++--------------- | |
M ploot-text.c | 19 ++++++++++++------- | |
D proto.sh | 73 -----------------------------… | |
A scale.c | 94 +++++++++++++++++++++++++++++… | |
A scale.h | 14 ++++++++++++++ | |
D src/csv.c | 122 -----------------------------… | |
D src/csv.h | 23 ----------------------- | |
D src/drawille.c | 196 -----------------------------… | |
D src/drawille.h | 28 ---------------------------- | |
D src/ffplot.c | 148 -----------------------------… | |
D src/ffplot.h | 35 -----------------------------… | |
D src/font.h | 21 --------------------- | |
D src/log.c | 97 ------------------------------ | |
D src/log.h | 15 --------------- | |
D src/scale.c | 95 ------------------------------ | |
D src/scale.h | 16 ---------------- | |
D src/util.c | 80 -----------------------------… | |
D src/util.h | 18 ------------------ | |
A util.c | 124 +++++++++++++++++++++++++++++… | |
A util.h | 22 ++++++++++++++++++++++ | |
40 files changed, 990 insertions(+), 1134 deletions(-) | |
--- | |
diff --git a/Makefile b/Makefile | |
@@ -1,27 +1,31 @@ | |
-include config.mk | |
+NAME = ploot | |
+VERSION = v0.1 | |
-src = src/csv.c src/drawille.c src/ffplot.c src/font.c src/font13.c \ | |
- src/font8.c src/log.c src/scale.c src/util.c | |
-inc = src/csv.h src/drawille.h src/ffplot.h src/font.h src/log.h \ | |
- src/scale.h src/util.h | |
-bin = ploot-farbfeld ploot-feed ploot-braille ploot-text | |
-obj = ${src:.c=.o} | |
-lib = -lm | |
+D = -D_POSIX_C_SOURCE=200811L -D_BSD_SOURCE | |
+CFLAGS = -Wall -Wextra -std=c99 -pedantic $W $D -fPIC | |
+LFLAGS = -static -lm | |
+PREFIX = /usr/local | |
+MANOREFIX = $(PREFIX)/share/man | |
-all: ${bin} | |
+SRC = csv.c drawille.c ffplot.c font.c font13.c font8.c scale.c util.c | |
+INC = csv.h drawille.h ffplot.h font.h scale.h util.h | |
+BIN = ploot-farbfeld ploot-feed ploot-braille ploot-text | |
+OBJ = ${SRC:.c=.o} | |
+ | |
+all: ${BIN} | |
.c.o: | |
${CC} -c ${CFLAGS} -o $@ $< | |
-${obj} ${bin:=.o}: ${inc} Makefile | |
-${bin}: ${obj} ${bin:=.o} | |
- ${CC} ${LFLAGS} -o $@ [email protected] ${obj} ${lib} | |
+${OBJ} ${BIN:=.o}: ${INC} Makefile | |
+${BIN}: ${OBJ} ${BIN:=.o} | |
+ ${CC} ${LFLAGS} -o $@ [email protected] ${OBJ} | |
-install: ${bin} | |
+install: ${BIN} | |
mkdir -p ${PREFIX}/bin ${MANDIR}/man1 ${MANDIR}/man5 | |
- cp ${bin} ${PREFIX}/bin | |
+ cp ${BIN} ${PREFIX}/bin | |
cp *.1 ${MANDIR}/man1 | |
cp *.5 ${MANDIR}/man5 | |
clean: | |
- rm -f *.o */*.o ${bin} | |
+ rm -f *.o ${BIN} | |
diff --git a/README b/README | |
@@ -1,60 +0,0 @@ | |
-ploot | |
-===== | |
- | |
- | |
-ploot-farbfeld | |
--------------- | |
- | |
-*ploot-farbfeld* reads collectd-style comma separated values (CSV) and produce… | |
-in the farbfeld [1] image format (pipe it to ff2png). It is an alternative to | |
-RRDtool [2]. | |
- | |
-It is targetting at generating monitoring graph, and it always read unix | |
-timestamp as first column on standard input. The first line determines the | |
-name of the curves. | |
- | |
-[1]: https://tools.suckless.org/farbfeld/ | |
-[2]: https://oss.oetiker.ch/rrdtool/ | |
- | |
- | |
-ploot-feed | |
----------- | |
- | |
-*ploot-feed* also reads collectd-style comma separated values (CSV) but produc… | |
-a plain text continuous waterfall chart for live monitoring in the terminal. it | |
-is an alternative to grafana [1]. | |
- | |
- % plootxt 1 1 1 <load-average.csv | |
- | |
- │shortterm │midterm │longterm … | |
- 17:34:00 _│⣦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀… | |
- 18/05/01 │⣟⡁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀… | |
- │⣛⣂⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀… | |
- 20:34:00 _│⣧⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀… | |
- 18/05/01 │⣧⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀… | |
- │⣟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀… | |
- 23:34:00 _│⣿⡒⠒⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀… | |
- 18/05/01 │⡧⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀… | |
- │⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀… | |
- 05:44:41 _│⣿⣆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀… | |
- 18/05/02 │⣛⣁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀… | |
- │⣷⠶⠶⠶⠄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀… | |
- 08:44:41 _│⡗⠂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀… | |
- 18/05/02 │⡯⠄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀… | |
- │⠗⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀… | |
- 11:44:41 _│⠗⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀… | |
- 18/05/02 │⡿⠶⠒⠒⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀… | |
- │⠖⠒⠒⠒⠂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀… | |
- 14:44:41 _│⣆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀… | |
- 18/05/02 │⣿⠟⠓⠒⠂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀… | |
- │⣿⠤⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀… | |
- 17:44:41 _│⡟⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀… | |
- 18/05/02 │⣭⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀… | |
- │⠄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀… | |
- 20:51:38 _│⣶⠂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀… | |
- 18/05/02 │⣿⣷⣶⣶⣶⣶⣶⠖⠒⠂⠀⠀⠀⠀⠀⠀⠀⠀⠀… | |
- │shortterm │midterm │longterm … | |
- 22:51:38 _│⣿⣿⣿⣟⣛⡋⠉⠉⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀… | |
- 18/05/02 │⣿⡿⠍⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀… | |
- | |
-[1]: https://grafana.com/ | |
diff --git a/README.md b/README.md | |
@@ -0,0 +1,57 @@ | |
+ploot | |
+===== | |
+ | |
+ploot-ffplot | |
+-------------- | |
+*ploot-ffplot* reads collectd-style comma separated values (CSV) and produces … | |
+in the ffplot [1] image format (pipe it to ff2png). It is an alternative to | |
+RRDtool [2]. | |
+ | |
+It is targetting at generating monitoring graph, and it always read unix | |
+timestamp as first column on standard input. The first line determines the | |
+name of the curves. | |
+ | |
+[1]: https://tools.suckless.org/ffplot/ | |
+[2]: https://oss.oetiker.ch/rrdtool/ | |
+ | |
+ploot-feed | |
+---------- | |
+*ploot-feed* also reads collectd-style comma separated values (CSV) but produc… | |
+a plain text continuous waterfall chart for live monitoring in the terminal. it | |
+is an alternative to grafana [1]. | |
+ | |
+``` | |
+% plootxt 1 1 1 <load-average.csv | |
+ │shortterm │midterm │longterm … | |
+17:34:00 _│⣦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│�… | |
+18/05/01 │⣟⡁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│�… | |
+ │⣛⣂⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│�… | |
+20:34:00 _│⣧⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│�… | |
+18/05/01 │⣧⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│�… | |
+ │⣟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│�… | |
+23:34:00 _│⣿⡒⠒⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│�… | |
+18/05/01 │⡧⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│�… | |
+ │⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│�… | |
+05:44:41 _│⣿⣆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│�… | |
+18/05/02 │⣛⣁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│�… | |
+ │⣷⠶⠶⠶⠄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│�… | |
+08:44:41 _│⡗⠂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│�… | |
+18/05/02 │⡯⠄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│�… | |
+ │⠗⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│�… | |
+11:44:41 _│⠗⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│�… | |
+18/05/02 │⡿⠶⠒⠒⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│�… | |
+ │⠖⠒⠒⠒⠂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│�… | |
+14:44:41 _│⣆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│�… | |
+18/05/02 │⣿⠟⠓⠒⠂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│�… | |
+ │⣿⠤⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│�… | |
+17:44:41 _│⡟⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│�… | |
+18/05/02 │⣭⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│�… | |
+ │⠄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│�… | |
+20:51:38 _│⣶⠂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│�… | |
+18/05/02 │⣿⣷⣶⣶⣶⣶⣶⠖⠒⠂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│�… | |
+ │shortterm │midterm │longterm … | |
+22:51:38 _│⣿⣿⣿⣟⣛⡋⠉⠉⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│�… | |
+18/05/02 │⣿⡿⠍⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│�… | |
+``` | |
+ | |
+[1]: https://grafana.com/ | |
diff --git a/config.mk b/config.mk | |
@@ -1,4 +0,0 @@ | |
-CFLAGS = -Wall -Wextra -std=c99 -pedantic -fPIC -I"src" -D_POSIX_C_SOUR… | |
-LFLAGS = -static | |
-PREFIX = /usr/local | |
-MANDIR = $(PREFIX)/share/man | |
diff --git a/csv.c b/csv.c | |
@@ -0,0 +1,132 @@ | |
+#include "csv.h" | |
+#include <errno.h> | |
+#include <assert.h> | |
+#include <string.h> | |
+#include <time.h> | |
+#include <stdlib.h> | |
+#include <limits.h> | |
+#include <time.h> | |
+#include "util.h" | |
+ | |
+/* | |
+ * Read CSV data onto a set of (struct csv). | |
+ */ | |
+ | |
+static void | |
+csv_addtime(struct csv *vl, time_t epoch) | |
+{ | |
+ void *mem; | |
+ | |
+ debug("csv_addtime %p", vl->t); | |
+ if ((mem = realloc(vl->t, (vl->n + 1) * sizeof *vl->t)) == NULL) | |
+ err(1, "realloc: %s", strerror(errno)); | |
+ vl->t = mem; | |
+ vl->t[vl->n] = epoch; | |
+} | |
+ | |
+static void | |
+csv_addval(struct csv *vl, double field) | |
+{ | |
+ void *mem; | |
+ | |
+ debug("csv_addval %p", vl->t); | |
+ if ((mem = realloc(vl->v, (vl->n + 1) * sizeof *vl->v)) == NULL) | |
+ err(1, "", strerror(errno)); | |
+ vl->v = mem; | |
+ vl->v[vl->n] = field; | |
+} | |
+ | |
+/* | |
+ * Add to each column the value on the current row. The time_t | |
+ * buffer is shared among all fields. | |
+ */ | |
+void | |
+csv_addrow(struct csv *vl, size_t ncol, char *line) | |
+{ | |
+ char *field; | |
+ time_t *tbuf; | |
+ long l; | |
+ double d; | |
+ | |
+ if ((field = strsep(&line, ",")) == NULL) | |
+ err(1, "missing epoch at row %zu", vl->n); | |
+ | |
+ l = strtol(field, NULL, 10); | |
+ if (errno) | |
+ err(100, "parsing number '%s'", field); | |
+ | |
+ csv_addtime(vl, l); | |
+ tbuf = vl[0].t; | |
+ for (; (field = strsep(&line, ",")); ncol--, vl->n++, vl++) { | |
+ if (ncol == 0) | |
+ err(1, "too many fields at line %zu", vl->n); | |
+ d = strtod(field, NULL); | |
+ if (errno) | |
+ err(100, "parsing double '%s'", field); | |
+ csv_addval(vl, d); | |
+ vl->t = tbuf; | |
+ } | |
+ if (ncol > 0) | |
+ err(1, "too few fields at line %zu", vl->n); | |
+} | |
+ | |
+/* | |
+ * < (ncol) > | |
+ * label1,label2,label3 | |
+ */ | |
+void | |
+csv_labels(FILE *fp, struct csv **vl, size_t *ncol) | |
+{ | |
+ char *field, *line, *cp; | |
+ struct csv *col; | |
+ size_t sz; | |
+ ssize_t r; | |
+ | |
+ sz = 0, line = NULL; | |
+ r = getline(&line, &sz, fp); | |
+ if (ferror(fp)) | |
+ err(111, "error while reading from file"); | |
+ if (feof(fp)) | |
+ err(100, "missing label line"); | |
+ strchomp(line); | |
+ | |
+ cp = line; | |
+ if (strcmp(strsep(&cp, ","), "epoch") != 0) | |
+ err(1, "first label must be 'epoch'"); | |
+ | |
+ *vl = NULL; | |
+ *ncol = 0; | |
+ while ((field = strsep(&cp, ","))) { | |
+ if ((*vl = realloc(*vl, sz += sizeof **vl)) == NULL) | |
+ err(1, "realloc: %s", strerror(errno)); | |
+ col = (*vl) + (*ncol)++; | |
+ strlcpy(col->label, field, sizeof(col->label)); | |
+ } | |
+ | |
+ free(line); | |
+} | |
+ | |
+/* | |
+ * < (ncol) > | |
+ * val1a,val1b,val1c ^ | |
+ * val2a,val2b,val2c | | |
+ * val3a,val3b,val3c (vl->n) | |
+ * val4a,val4b,val4c | | |
+ * val5a,val5b,val5c v | |
+ */ | |
+void | |
+csv_values(FILE *fp, struct csv *vl, size_t ncol) | |
+{ | |
+ char *line; | |
+ size_t sz; | |
+ | |
+ sz = 0, line = NULL; | |
+ while (getline(&line, &sz, fp) > -1) | |
+ csv_addrow(vl, ncol, line); | |
+ if (vl->n == 0) | |
+ err(1, "no value could be read"); | |
+ if (vl->n == 1) | |
+ err(1, "only one value could be read"); | |
+ | |
+ free(line); | |
+} | |
diff --git a/csv.h b/csv.h | |
@@ -0,0 +1,22 @@ | |
+#ifndef CSV_H | |
+#define CSV_H | |
+ | |
+#include <stdio.h> | |
+#include <time.h> | |
+ | |
+/* | |
+ * List of values and timestamps. Both have their dedicated buffer | |
+ * so that the timestamp buffer can be shared across csv objects. | |
+ */ | |
+struct csv { | |
+ time_t *t; /* array of timestamps */ | |
+ double *v; /* array of values */ | |
+ size_t n; /* number of values */ | |
+ char label[64]; /* for the legend */ | |
+}; | |
+ | |
+void csv_addrow(struct csv *, size_t, char *); | |
+void csv_labels(FILE *, struct csv **, size_t *); | |
+void csv_values(FILE *, struct csv *, size_t); | |
+ | |
+#endif | |
diff --git a/drawille.c b/drawille.c | |
@@ -0,0 +1,194 @@ | |
+#include "drawille.h" | |
+ | |
+#include <stdint.h> | |
+#include <stdio.h> | |
+#include <stdlib.h> | |
+#include <string.h> | |
+#include <math.h> | |
+ | |
+#include "font.h" | |
+ | |
+/* | |
+ * Terminal-based plotting using drawille character, aka drawille. | |
+ */ | |
+ | |
+/* parameters used to draw a line */ | |
+struct line { | |
+ int x0, y0, x1, y1; /* point of the line */ | |
+ int dx, dy, sx, sy, err; /* parameters for the algorythm */ | |
+}; | |
+ | |
+/* | |
+ * Turn on the bit at position (row, col) of a single cell. The | |
+ * pattern is not linear (1-4-2-5-3-6-7-8), because it matches the | |
+ * drawille pattern. | |
+ */ | |
+static void | |
+drawille_cell_dot(uint8_t *cell, int row, int col) | |
+{ | |
+ uint8_t flags[4][2] = { | |
+ { 0x01, 0x08 }, | |
+ { 0x02, 0x10 }, | |
+ { 0x04, 0x20 }, | |
+ { 0x40, 0x80 }, | |
+ }; | |
+ | |
+ *cell |= flags[row][col]; | |
+} | |
+ | |
+static size_t | |
+drawille_cell_utf(uint8_t cell, char *utf) | |
+{ | |
+ long rune; | |
+ | |
+ rune = 10240 + cell; | |
+ utf[0] = (char)(0xe0 | (0x0f & (rune >> 12))); /* 1110xxxx */ | |
+ utf[1] = (char)(0x80 | (0x3f & (rune >> 6))); /* 10xxxxxx */ | |
+ utf[2] = (char)(0x80 | (0x3f & (rune))); /* 10xxxxxx */ | |
+ return 3; | |
+} | |
+ | |
+static uint8_t | |
+drawille_get(struct drawille *drw, int row, int col) | |
+{ | |
+ return drw->buf[row * drw->col + col]; | |
+} | |
+ | |
+size_t | |
+drawille_put_row(FILE *fp, struct drawille *drw, int row) | |
+{ | |
+ char txt[] = "xxx"; | |
+ size_t n; | |
+ | |
+ n = 0; | |
+ for (int col = 0; col < drw->col; col++) { | |
+ drawille_cell_utf(drawille_get(drw, row, col), txt); | |
+ n += fputs(txt, fp); | |
+ } | |
+ return n; | |
+} | |
+ | |
+/* | |
+ * Coordinates are passed as (x, y), but the canvas stores bits as | |
+ * (row, col). Conversion is made by this function. | |
+ */ | |
+void | |
+drawille_dot(struct drawille *drw, int x, int y) | |
+{ | |
+ if (x < 0 || x / 2 >= drw->col || y < 0 || y / 4 >= drw->row) | |
+ return; | |
+ drawille_cell_dot(drw->buf + (drw->row - y / 4 - 1) * drw->col + (x / … | |
+ 3 - y % 4, | |
+ x % 2); | |
+} | |
+ | |
+struct drawille * | |
+drawille_new(int row, int col) | |
+{ | |
+ struct drawille *drw; | |
+ | |
+ if ((drw = calloc(sizeof(struct drawille) + row * col, 1)) == NULL) | |
+ return NULL; | |
+ drw->row = row; | |
+ drw->col = col; | |
+ return drw; | |
+} | |
+ | |
+static void | |
+drawille_line_init(struct line *l, int x0, int y0, int x1, int y1) | |
+{ | |
+ l->x0 = x0; | |
+ l->y0 = y0; | |
+ l->x1 = x1; | |
+ l->y1 = y1; | |
+ l->sx = x0 < x1 ? 1 : -1; | |
+ l->sy = y0 < y1 ? 1 : -1; | |
+ l->dx = abs(x1 - x0); | |
+ l->dy = abs(y1 - y0); | |
+ l->err = (l->dx > l->dy ? l->dx : -l->dy) / 2; | |
+} | |
+ | |
+static int | |
+drawille_line_next(struct line *l) | |
+{ | |
+ int e; | |
+ | |
+ if (l->x0 == l->x1 && l->y0 == l->y1) | |
+ return 0; | |
+ | |
+ e = l->err; | |
+ if (e > -l->dx) { | |
+ l->x0 += l->sx; | |
+ l->err -= l->dy; | |
+ } | |
+ if (e < l->dy) { | |
+ l->y0 += l->sy; | |
+ l->err += l->dx; | |
+ } | |
+ return 1; | |
+} | |
+ | |
+void | |
+drawille_line(struct drawille *drw, int x0, int y0, int x1, int y1) | |
+{ | |
+ struct line l; | |
+ | |
+ drawille_line_init(&l, x0, y0, x1, y1); | |
+ do { | |
+ drawille_dot(drw, l.x0, l.y0); | |
+ } while (drawille_line_next(&l)); | |
+} | |
+ | |
+void | |
+drawille_histogram_dot(struct drawille *drw, int x, int y, int zero) | |
+{ | |
+ int sign; | |
+ | |
+ sign = (y > zero) ? (+1) : (-1); | |
+ for (; y != zero; y -= sign) | |
+ drawille_dot(drw, x, y); | |
+ drawille_dot(drw, x, y); | |
+} | |
+ | |
+void | |
+drawille_histogram_line(struct drawille *drw, int x0, int y0, int x1, int y1, … | |
+{ | |
+ struct line l; | |
+ | |
+ drawille_line_init(&l, x0, y0, x1, y1); | |
+ do { | |
+ drawille_histogram_dot(drw, l.x0, l.y0, zero); | |
+ } while (drawille_line_next(&l)); | |
+} | |
+ | |
+static int | |
+drawille_text_glyph(struct drawille *drw, int x, int y, struct font *font, cha… | |
+{ | |
+ int width; | |
+ char *glyph; | |
+ | |
+ if ((unsigned)c > 127) | |
+ glyph = font->glyph[0]; | |
+ else | |
+ glyph = font->glyph[(unsigned)c]; | |
+ | |
+ width = strlen(glyph) / font->height; | |
+ | |
+ for (int ix = 0; ix < width; ix++) | |
+ for (int iy = 0; iy < font->height; iy++) { | |
+ if (glyph[ix + (font->height - 1) * width - iy * width] == 3) | |
+ drawille_dot(drw, x + ix, y + iy); | |
+ } | |
+ | |
+ return width; | |
+} | |
+ | |
+char * | |
+drawille_text(struct drawille *drw, int x, int y, struct font *font, char *s) | |
+{ | |
+ if (drw->row*4 < font->height) | |
+ return NULL; | |
+ for (; *s != '\0' && x < drw->col * 2; s++, x++) | |
+ x += drawille_text_glyph(drw, x, y, font, *s); | |
+ return s; | |
+} | |
diff --git a/drawille.h b/drawille.h | |
@@ -0,0 +1,25 @@ | |
+#ifndef DRAWILLE_H | |
+#define DRAWILLE_H | |
+ | |
+#include <stddef.h> | |
+#include <stdint.h> | |
+#include <stdio.h> | |
+#include "font.h" | |
+ | |
+/* | |
+ * Canvas to draw on with braille characters. | |
+ */ | |
+struct drawille { | |
+ int col, row; /* number of dots in total */ | |
+ uint8_t buf[]; /* buffer of size (col * … | |
+}; | |
+ | |
+size_t drawille_put_row(FILE *, struct drawille *, int); | |
+void drawille_dot(struct drawille *, int, int); | |
+struct drawille *drawille_new(int, int); | |
+void drawille_line(struct drawille *, int, int, int, int); | |
+void drawille_histogram_dot(struct drawille *, int, int, int); | |
+void drawille_histogram_line(struct drawille *, int, int, int, int, in… | |
+char *drawille_text(struct drawille *, int, int, struct font *, char *); | |
+ | |
+#endif | |
diff --git a/test.csv b/example.csv | |
diff --git a/ffplot.c b/ffplot.c | |
@@ -0,0 +1,148 @@ | |
+#include "ffplot.h" | |
+ | |
+#include <arpa/inet.h> | |
+#include <stddef.h> | |
+#include <string.h> | |
+#include <stdio.h> | |
+#include <stdint.h> | |
+ | |
+#include "font.h" | |
+#include "util.h" | |
+ | |
+/* | |
+ * Convert (x,y) coordinates to (row,col) for printing into the buffer. | |
+ * The buffer only contain one number, so the coordinate is a single integer: | |
+ * width * y + y. | |
+ * The coordinates are shifted by offx and offy to permit relative coordinates. | |
+ * | |
+ * The convention used: y | |
+ * - (0,0) is at the lower left corner of the plotvas. | | |
+ * - (0,1) is above it. +--x | |
+ */ | |
+void | |
+ffplot_pixel(struct ffplot *plot, struct ffcolor *color, | |
+ int x, int y) | |
+{ | |
+ x += plot->x; | |
+ y += plot->y; | |
+ if (x < 0 || x >= plot->w || y < 0 || y >= plot->h) | |
+ return; | |
+ memcpy(plot->buf + plot->w * (plot->h - 1 - y) + x, color, sizeof(*plo… | |
+} | |
+ | |
+void | |
+ffplot_rectangle(struct ffplot *plot, struct ffcolor *color, | |
+ int y1, int x1, | |
+ int y2, int x2) | |
+{ | |
+ int x, y, ymin, xmin, ymax, xmax; | |
+ | |
+ ymin = MIN(y1, y2); ymax = MAX(y1, y2); | |
+ xmin = MIN(x1, x2); xmax = MAX(x1, x2); | |
+ | |
+ for (y = ymin; y <= ymax; y++) | |
+ for (x = xmin; x <= xmax; x++) | |
+ ffplot_pixel(plot, color, x, y); | |
+} | |
+ | |
+/* | |
+ * From Bresenham's line algorithm and dcat's tplot. | |
+ */ | |
+void | |
+ffplot_line(struct ffplot *plot, struct ffcolor *color, | |
+ int x0, int y0, | |
+ int x1, int y1) | |
+{ | |
+ int dy, dx, sy, sx, err, e; | |
+ | |
+ sx = x0 < x1 ? 1 : -1; | |
+ sy = y0 < y1 ? 1 : -1; | |
+ dx = ABS(x1 - x0); | |
+ dy = ABS(y1 - y0); | |
+ err = (dy > dx ? dy : -dx) / 2; | |
+ | |
+ for (;;) { | |
+ ffplot_pixel(plot, color, x0, y0); | |
+ | |
+ if (y0 == y1 && x0 == x1) | |
+ break; | |
+ | |
+ e = err; | |
+ if (e > -dy) { | |
+ y0 += sy; | |
+ err -= dx; | |
+ } | |
+ if (e < dx) { | |
+ x0 += sx; | |
+ err += dy; | |
+ } | |
+ } | |
+} | |
+ | |
+/* | |
+ * Draw a coloured glyph from font f centered on y. | |
+ */ | |
+int | |
+ffplot_char(struct ffplot *plot, struct ffcolor *color, struct font *ft, char … | |
+ int x, int y) | |
+{ | |
+ int yf, xf, wf; | |
+ | |
+ if (c & 0x80) | |
+ c = '\0'; | |
+ y -= ft->height / 2; | |
+ wf = font_width(ft, c); | |
+ for (xf = 0; xf < wf; xf++) | |
+ for (yf = 0; yf < ft->height; yf++) | |
+ if (ft->glyph[(int)c][wf * (ft->height - yf) + xf] == … | |
+ ffplot_pixel(plot, color, x + xf, y + yf); | |
+ return wf + 1; | |
+} | |
+ | |
+/* | |
+ * Draw a left aligned string without wrapping it. | |
+ */ | |
+size_t | |
+ffplot_text_left(struct ffplot *plot, struct ffcolor *color, struct font *ft, | |
+ char *s, int x, int y) | |
+{ | |
+ for (; *s != '\0'; s++) | |
+ x += ffplot_char(plot, color, ft, *s, x, y); | |
+ return x; | |
+} | |
+ | |
+/* | |
+ * Draw a center aligned string without wrapping it. | |
+ */ | |
+size_t | |
+ffplot_text_center(struct ffplot *plot, struct ffcolor *color, struct font *ft, | |
+ char *s, int x, int y) | |
+{ | |
+ x -= font_strlen(ft, s) / 2; | |
+ return ffplot_text_left(plot, color, ft, s, x, y); | |
+} | |
+ | |
+/* | |
+ * Draw a right aligned string without wrapping it. | |
+ */ | |
+size_t | |
+ffplot_text_right(struct ffplot *plot, struct ffcolor *color, struct font *ft, | |
+ char *s, int x, int y) | |
+{ | |
+ x -= font_strlen(ft, s); | |
+ return ffplot_text_left(plot, color, ft, s, x, y); | |
+} | |
+ | |
+void | |
+ffplot_print(FILE *fp, struct ffplot *plot) | |
+{ | |
+ uint32_t w, h; | |
+ | |
+ w = htonl(plot->w); | |
+ h = htonl(plot->h); | |
+ | |
+ fprintf(stdout, "ffplot"); | |
+ fwrite(&w, sizeof(w), 1, fp); | |
+ fwrite(&h, sizeof(h), 1, fp); | |
+ fwrite(plot->buf, plot->w * plot->h, sizeof(*plot->buf), fp); | |
+} | |
diff --git a/ffplot.h b/ffplot.h | |
@@ -0,0 +1,34 @@ | |
+#ifndef FFPLOT_H | |
+#define FFPLOT_H | |
+ | |
+#include <stdio.h> | |
+#include <stddef.h> | |
+#include <stdint.h> | |
+ | |
+#include "font.h" | |
+ | |
+struct ffcolor { | |
+ uint16_t red; | |
+ uint16_t green; | |
+ uint16_t blue; | |
+ uint16_t alpha; | |
+}; | |
+ | |
+struct ffplot { | |
+ int w; /* width */ | |
+ int h; /* height */ | |
+ int x; /* x offset */ | |
+ int y; /* y offset */ | |
+ struct ffcolor *buf; | |
+}; | |
+ | |
+void ffplot_pixel(struct ffplot *, struct ffcolor *, int, int); | |
+void ffplot_rectangle(struct ffplot *, struct ffcolor *, int, int, int,… | |
+void ffplot_line(struct ffplot *, struct ffcolor *, int, int, int, int); | |
+int ffplot_char(struct ffplot *, struct ffcolor *, struct font *, char,… | |
+size_t ffplot_text_left(struct ffplot *, struct ffcolor *, struct font … | |
+size_t ffplot_text_center(struct ffplot *, struct ffcolor *, struct fon… | |
+size_t ffplot_text_right(struct ffplot *, struct ffcolor *, struct font… | |
+void ffplot_print(FILE *, struct ffplot *); | |
+ | |
+#endif | |
diff --git a/src/font.c b/font.c | |
diff --git a/font.h b/font.h | |
@@ -0,0 +1,20 @@ | |
+#ifndef FONT_H | |
+#define FONT_H | |
+ | |
+#include <stddef.h> | |
+ | |
+/* | |
+ * Bitmapped font saved as a '_' and 'X' pattern in a C source file. | |
+ */ | |
+struct font { | |
+ int height; /* The width is variable. */ | |
+ char *glyph[128]; /* 0: end, 1: off, 2: on. */ | |
+}; | |
+ | |
+extern struct font font8; | |
+extern struct font font13; | |
+ | |
+size_t font_width(struct font *, int); | |
+size_t font_strlen(struct font *, char *); | |
+ | |
+#endif | |
diff --git a/src/font13.c b/font13.c | |
diff --git a/src/font8.c b/font8.c | |
diff --git a/ploot-braille.c b/ploot-braille.c | |
@@ -7,13 +7,9 @@ | |
#include <time.h> | |
#include <math.h> | |
#include <unistd.h> | |
- | |
#include "drawille.h" | |
#include "scale.h" | |
#include "util.h" | |
-#include "log.h" | |
- | |
-char const *arg0 = NULL; | |
/* | |
* Plot the body as an histogram interpolating the gaps and include | |
@@ -46,7 +42,7 @@ braille_histogram(struct csv *vl, struct drawille *drw, | |
} | |
static int | |
-braille_axis_x(FILE *fp, time_t tmin, time_t tmax, time_t tstep, int col) | |
+braille_axis_x(FILE *fp, time_t tmin, time_t tmax, time_t csvep, int col) | |
{ | |
int x, o, prec; | |
char tmp[sizeof("MM/DD HH:MM")], *fmt; | |
@@ -54,13 +50,13 @@ braille_axis_x(FILE *fp, time_t tmin, time_t tmax, time_t t… | |
time_t t; | |
fmt = | |
- (tstep < 3600 * 12) ? "^%H:%M:%S" : | |
- (tstep < 3600 * 24) ? "^%m/%d %H:%M" : | |
+ (csvep < 3600 * 12) ? "^%H:%M:%S" : | |
+ (csvep < 3600 * 24) ? "^%m/%d %H:%M" : | |
"^%Y/%m/%d"; | |
n = x = 0; | |
- t = tmin + tstep - tmin % tstep; | |
- for (; t < tmax; t += tstep) { | |
+ t = tmin + csvep - tmin % csvep; | |
+ for (; t < tmax; t += csvep) { | |
x = (t - tmin) * col / (tmax - tmin); | |
strftime(tmp, sizeof tmp, fmt, localtime(&t)); | |
prec = x - n + strlen(tmp); | |
@@ -107,32 +103,33 @@ static void | |
plot(struct csv *vl, FILE *fp, size_t ncol, int rows, int cols) | |
{ | |
double vmin, vmax, vstep; | |
- time_t tmin, tmax, tstep; | |
+ time_t tmin, tmax, csvep; | |
struct drawille *drw; | |
cols -= 9; /* scale printed at the right */ | |
scale_minmax(vl, ncol, &tmin, &tmax, &vmin, &vmax); | |
- tstep = scale_tstep(tmin, tmax, cols / 10); | |
+ csvep = scale_csvep(tmin, tmax, cols / 10); | |
vstep = scale_vstep(vmin, vmax, rows / 10); | |
rows -= ncol - 1; /* room for the labels and the scale */ | |
rows /= ncol; /* plot <ncol> times */ | |
rows = MAX(rows, 3); /* readable */ | |
- debug("vstep=%lf vstep=%ld ncol=%zu rows=%zu", vstep, tstep, ncol, row… | |
+ debug("vstep=%lf vstep=%ld ncol=%zu rows=%zu", vstep, csvep, ncol, row… | |
for (; ncol > 0; vl++, ncol--) { | |
- assert(drw = drawille_new(rows, cols)); | |
+ if ((drw = drawille_new(rows, cols)) == NULL) | |
+ err(1, "drawille_new: %s", strerror(errno)); | |
fprintf(fp, " %s\n", vl->label); | |
if (braille_histogram(vl, drw, tmin, tmax, vmin, vmax) == -1) | |
- die(1, "allocating drawille canvas"); | |
+ err(1, "allocating drawille canvas"); | |
if (braille_render(drw, fp, vmin, vmax) == -1) | |
- die(1, "rendering braille canvas"); | |
+ err(1, "rendering braille canvas"); | |
free(drw); | |
} | |
- if (braille_axis_x(fp, tmin, tmax, tstep, cols) == -1) | |
- die(1, "printing x axis");; | |
+ if (braille_axis_x(fp, tmin, tmax, csvep, cols) == -1) | |
+ err(1, "printing x axis");; | |
} | |
static void | |
@@ -151,20 +148,19 @@ main(int argc, char **argv) | |
rows = 20, cols = 80; | |
arg0 = *argv; | |
- optind = 0; | |
while ((c = getopt(argc, argv, "r:c:")) > -1) { | |
switch (c) { | |
case 'r': | |
rows = atoi(optarg); | |
if (rows < 1) { | |
- error("invalid number of rows"); | |
+ warn("invalid number of rows"); | |
usage(); | |
} | |
break; | |
case 'c': | |
cols = atoi(optarg); | |
if (rows < 1) { | |
- error("invalid number of columns"); | |
+ warn("invalid number of columns"); | |
usage(); | |
} | |
break; | |
diff --git a/ploot-csv.5 b/ploot-csv.5 | |
@@ -15,20 +15,20 @@ | |
epoch,column-name-1,column-name-2 | |
timestamp,value1,value2 | |
timestamp,value1,value2 | |
-… | |
+\&... | |
.Ed | |
. | |
. | |
.Sh DESCRIPTION | |
. | |
-This is the simple comma-separated format used by the ploot-* programs. | |
+This is the simple coma-separated format used by the ploot-* programs. | |
. | |
. | |
.Sh INPUT FORMAT | |
. | |
.Nm | |
has a first header line, then zero or more data lines, both | |
-comma-separated list of values. | |
+coma-separated list of values. | |
. | |
. | |
.Ss Header line | |
diff --git a/ploot-farbfeld.1 b/ploot-farbfeld.1 | |
@@ -5,13 +5,13 @@ | |
. | |
.Sh NAME | |
. | |
-.Nm ploot-farbfeld | |
-.Nd produce a farbfeld image of csv input | |
+.Nm ploot-ffplot | |
+.Nd produce a ffplot image of csv input | |
. | |
. | |
.Sh SYNOPSIS | |
. | |
-.Nm ploot-farbfeld | |
+.Nm ploot-ffplot | |
.Op Fl t Ar title | |
.Op Fl u Ar unit | |
.Ar colors... | |
@@ -21,7 +21,7 @@ | |
. | |
The | |
.Nm | |
-utility plots an image in the farbfeld format out of csv values coming from st… | |
+utility plots an image in the ffplot format out of csv values coming from stdi… | |
. | |
.Bl -tag -width 6n | |
. | |
@@ -59,20 +59,20 @@ epoch,used_memory,free_memory | |
1533752055,301,260 | |
1533752056,303,258 | |
EOF | |
-$ ploot-farbfeld -t demo -u MB red yellow <sample.txt | |
+$ ploot-ffplot -t demo -u MB red yellow <sample.txt | |
.Ed | |
. | |
. | |
.Sh SEE ALSO | |
. | |
-.Xr ploot-farbfeld 1 , | |
+.Xr ploot-ffplot 1 , | |
.Xr ploot-csv 7 | |
. | |
.Pp | |
The | |
-.Xr farbfeld 7 | |
+.Xr ffplot 7 | |
image format: | |
-.Lk https://tools.suckless.org/farbfeld/ | |
+.Lk https://tools.suckless.org/ffplot/ | |
. | |
. | |
.Sh HISTORY | |
diff --git a/ploot-farbfeld.c b/ploot-farbfeld.c | |
@@ -1,6 +1,6 @@ | |
#include <arpa/inet.h> | |
-#include <assert.h> | |
#include <ctype.h> | |
+#include <errno.h> | |
#include <fcntl.h> | |
#include <limits.h> | |
#include <math.h> | |
@@ -10,11 +10,9 @@ | |
#include <string.h> | |
#include <time.h> | |
#include <unistd.h> | |
- | |
#include "csv.h" | |
#include "ffplot.h" | |
#include "font.h" | |
-#include "log.h" | |
#include "util.h" | |
#include "scale.h" | |
@@ -53,7 +51,6 @@ struct colorname { | |
struct ffcolor color; | |
}; | |
-char const *arg0 = NULL; | |
static char *tflag = ""; | |
static char *uflag = ""; | |
static struct font *font = &font13; | |
@@ -70,7 +67,7 @@ static struct colorname colorname[] = { | |
}; | |
static int | |
-farbfeld_t2x(time_t t, time_t tmin, time_t tmax) | |
+ffplot_t2x(time_t t, time_t tmin, time_t tmax) | |
{ | |
if (tmin == tmax) | |
return PLOT_W; | |
@@ -78,7 +75,7 @@ farbfeld_t2x(time_t t, time_t tmin, time_t tmax) | |
} | |
static int | |
-farbfeld_v2y(double v, double vmin, double vmax) | |
+ffplot_v2y(double v, double vmin, double vmax) | |
{ | |
if (vmin == vmax) | |
return PLOT_H; | |
@@ -86,22 +83,22 @@ farbfeld_v2y(double v, double vmin, double vmax) | |
} | |
static void | |
-farbfeld_xaxis(struct ffplot *plot, struct ffcolor *label, struct ffcolor *gri… | |
- time_t tmin, time_t tmax, time_t tstep) | |
+ffplot_xaxis(struct ffplot *plot, struct ffcolor *label, struct ffcolor *grid, | |
+ time_t tmin, time_t tmax, time_t csvep) | |
{ | |
time_t t; | |
int x; | |
char str[sizeof("MM/DD HH/MM")], *fmt; | |
- if (tstep < 3600 * 12) | |
+ if (csvep < 3600 * 12) | |
fmt = "%H:%M:%S"; | |
- else if (tstep < 3600 * 24) | |
+ else if (csvep < 3600 * 24) | |
fmt = "%m/%d %H:%M"; | |
else | |
fmt = "%X/%m/%d"; | |
- for (t = tmax - tmax % tstep; t >= tmin; t -= tstep) { | |
- x = farbfeld_t2x(t, tmin, tmax); | |
+ for (t = tmax - tmax % csvep; t >= tmin; t -= csvep) { | |
+ x = ffplot_t2x(t, tmin, tmax); | |
ffplot_line(plot, grid, | |
x, XLABEL_H, | |
@@ -114,7 +111,7 @@ farbfeld_xaxis(struct ffplot *plot, struct ffcolor *label, … | |
} | |
static void | |
-farbfeld_yaxis(struct ffplot *plot, struct ffcolor *label, struct ffcolor *gri… | |
+ffplot_yaxis(struct ffplot *plot, struct ffcolor *label, struct ffcolor *grid, | |
double vmin, double vmax, double vstep) | |
{ | |
double v; | |
@@ -122,7 +119,7 @@ farbfeld_yaxis(struct ffplot *plot, struct ffcolor *label, … | |
char str[8 + 1]; | |
for (v = vmax - fmod(vmax, vstep); v >= vmin; v -= vstep) { | |
- y = farbfeld_v2y(v, vmin, vmax); | |
+ y = ffplot_v2y(v, vmin, vmax); | |
ffplot_line(plot, grid, | |
YLABEL_W, y, | |
@@ -135,7 +132,7 @@ farbfeld_yaxis(struct ffplot *plot, struct ffcolor *label, … | |
} | |
static void | |
-farbfeld_title(struct ffplot *plot, | |
+ffplot_title(struct ffplot *plot, | |
struct ffcolor *ct, char *title, | |
struct ffcolor *cu, char *unit) | |
{ | |
@@ -144,7 +141,7 @@ farbfeld_title(struct ffplot *plot, | |
} | |
static void | |
-farbfeld_plot(struct ffplot *plot, struct csv *vl, struct ffcolor *color, | |
+ffplot_plot(struct ffplot *plot, struct csv *vl, struct ffcolor *color, | |
double vmin, double vmax, | |
time_t tmin, time_t tmax) | |
{ | |
@@ -154,8 +151,8 @@ farbfeld_plot(struct ffplot *plot, struct csv *vl, struct f… | |
first = 1; | |
for (tp = vl->t, vp = vl->v, n = vl->n; n > 0; n--, vp++, tp++) { | |
- y = farbfeld_v2y(*vp, vmin, vmax); | |
- x = farbfeld_t2x(*tp, tmin, tmax); | |
+ y = ffplot_v2y(*vp, vmin, vmax); | |
+ x = ffplot_t2x(*tp, tmin, tmax); | |
if (!first) | |
ffplot_line(plot, color, xlast, ylast, x, y); | |
@@ -167,16 +164,16 @@ farbfeld_plot(struct ffplot *plot, struct csv *vl, struct… | |
} | |
static void | |
-farbfeld_values(struct ffplot *plot, struct csv *vl, struct ffcolor **cl, size… | |
+ffplot_values(struct ffplot *plot, struct csv *vl, struct ffcolor **cl, size_t… | |
time_t tmin, time_t tmax, | |
double vmin, double vmax) | |
{ | |
for (; ncol > 0; ncol--, vl++, cl++) | |
- farbfeld_plot(plot, vl, *cl, vmin, vmax, tmin, tmax); | |
+ ffplot_plot(plot, vl, *cl, vmin, vmax, tmin, tmax); | |
} | |
static void | |
-farbfeld_legend(struct ffplot *plot, struct ffcolor *fg, struct csv *vl, struc… | |
+ffplot_legend(struct ffplot *plot, struct ffcolor *fg, struct csv *vl, struct … | |
{ | |
size_t x, y; | |
@@ -209,13 +206,14 @@ plot(struct csv *vl, struct ffcolor **cl, size_t ncol, ch… | |
struct ffcolor label_fg = { 0x8888, 0x8888, 0x8888, 0xffff }; | |
struct ffcolor title_fg = { 0xdddd, 0xdddd, 0xdddd, 0xffff }; | |
double vmin, vmax, vstep; | |
- time_t tmin, tmax, tstep; | |
+ time_t tmin, tmax, csvep; | |
scale_minmax(vl, ncol, &tmin, &tmax, &vmin, &vmax); | |
- tstep = scale_tstep(tmin, tmax, 7); | |
+ csvep = scale_csvep(tmin, tmax, 7); | |
vstep = scale_vstep(vmin, vmax, 7); | |
- assert(plot.buf = calloc(IMAGE_H * IMAGE_W, sizeof *plot.buf)); | |
+ if ((plot.buf = calloc(IMAGE_H * IMAGE_W, sizeof *plot.buf)) == NULL) | |
+ err(1, "calloc: %s", strerror(errno)); | |
plot.y = 0; | |
plot.x = 0; | |
@@ -227,23 +225,23 @@ plot(struct csv *vl, struct ffcolor **cl, size_t ncol, ch… | |
plot.x = XLABEL_X; | |
plot.y = XLABEL_Y; | |
- farbfeld_xaxis(&plot, &label_fg, &grid_fg, tmin, tmax, tstep); | |
+ ffplot_xaxis(&plot, &label_fg, &grid_fg, tmin, tmax, csvep); | |
plot.x = YLABEL_X; | |
plot.y = YLABEL_Y; | |
- farbfeld_yaxis(&plot, &label_fg, &grid_fg, vmin, vmax, vstep); | |
+ ffplot_yaxis(&plot, &label_fg, &grid_fg, vmin, vmax, vstep); | |
plot.x = TITLE_X; | |
plot.y = TITLE_Y; | |
- farbfeld_title(&plot, &title_fg, name, &label_fg, units); | |
+ ffplot_title(&plot, &title_fg, name, &label_fg, units); | |
plot.x = PLOT_X; | |
plot.y = PLOT_Y; | |
- farbfeld_values(&plot, vl, cl, ncol, tmin, tmax, vmin, vmax); | |
+ ffplot_values(&plot, vl, cl, ncol, tmin, tmax, vmin, vmax); | |
plot.x = LEGEND_X; | |
plot.y = LEGEND_Y; | |
- farbfeld_legend(&plot, &label_fg, vl, cl, ncol); | |
+ ffplot_legend(&plot, &label_fg, vl, cl, ncol); | |
ffplot_print(stdout, &plot); | |
} | |
@@ -264,7 +262,7 @@ argv_to_color(struct ffcolor **cl, char **argv) | |
{ | |
for (; *argv != NULL; cl++, argv++) | |
if ((*cl = name_to_color(*argv)) == NULL) | |
- die(1, "unknown color name: %s", *argv); | |
+ err(1, "unknown color name: %s", *argv); | |
} | |
static void | |
@@ -286,7 +284,7 @@ main(int argc, char **argv) | |
size_t ncol; | |
int c; | |
- optind = 0; | |
+ arg0 = *argv; | |
while ((c = getopt(argc, argv, "t:u:")) > -1) { | |
switch (c) { | |
case 't': | |
@@ -305,13 +303,14 @@ main(int argc, char **argv) | |
if (argc == 0) | |
usage(); | |
- assert(cl = calloc(argc, sizeof(*cl))); | |
+ if ((cl = calloc(argc, sizeof *cl)) == NULL) | |
+ err(1, "calloc: %s", strerror(errno)); | |
csv_labels(stdin, &vl, &ncol); | |
if (ncol > (size_t)argc) | |
- die(1, "too many columns or not enough arguments"); | |
+ err(1, "too many columns or not enough arguments"); | |
else if (ncol < (size_t)argc) | |
- die(1, "too many arguments or not enough columns"); | |
+ err(1, "too many arguments or not enough columns"); | |
csv_values(stdin, vl, ncol); | |
argv_to_color(cl, argv); | |
diff --git a/ploot-feed.1 b/ploot-feed.1 | |
@@ -60,7 +60,7 @@ $ ploot-feed -w 80 1 1 <sample.txt | |
. | |
.Sh SEE ALSO | |
. | |
-.Xr ploot-farbfeld 1 , | |
+.Xr ploot-ffplot 1 , | |
.Xr ploot-format 7 | |
. | |
. | |
diff --git a/ploot-feed.c b/ploot-feed.c | |
@@ -8,14 +8,11 @@ | |
#include <string.h> | |
#include <time.h> | |
#include <unistd.h> | |
- | |
#include "util.h" | |
-#include "log.h" | |
#define WIDTH_MAX 1024 | |
#define BRAILLE_START 10240 | |
-char const *arg0 = NULL; | |
static int wflag = 80; | |
static int width = 0; | |
@@ -60,19 +57,19 @@ plot_row(long *out, char *line, double *max, int nrow, int … | |
tok = strsep(&line, ","); | |
if (!tok) | |
- die(100, "*** missing epoch value"); | |
+ err(100, "*** missing epoch value"); | |
epoch = strtol(tok, NULL, 10); | |
if (errno) | |
- error("*** parsing epoch '%s'", tok); | |
+ warn("*** parsing epoch '%s'", tok); | |
for (n = 0; (tok = strsep(&line, ",")) != NULL; n++) { | |
if (n >= ncol) | |
- die(100, "too many values"); | |
+ err(100, "too many values"); | |
val = atof(tok); | |
plot_val(out + n * width, val, max[n], nrow); | |
} | |
if (n < ncol) | |
- die(100, "not enough values"); | |
+ err(100, "not enough values"); | |
return epoch; | |
} | |
@@ -100,7 +97,7 @@ plot_line(long *out, double *max, int ncol) | |
for (nrow = 0; nrow < 4; nrow++) { | |
if (getline(&line, &sz, stdin) == -1) { | |
if (ferror(stdin)) | |
- die(111, "reading row from stdin"); | |
+ err(111, "reading row from stdin"); | |
exit(0); | |
} | |
epoch = plot_row(out, line, max, nrow, ncol); | |
@@ -179,21 +176,21 @@ read_labels(char **labv) | |
line = NULL, sz = 0; | |
if (getline(&line, &sz, stdin) == -1) { | |
if (ferror(stdin)) | |
- die(111, "reading labels from stdin"); | |
- die(100, "missing label line", stderr); | |
+ err(111, "reading labels from stdin"); | |
+ err(100, "missing label line", stderr); | |
} | |
strchomp(line); | |
cp = line; | |
if (strcmp(strsep(&cp, ","), "epoch") != 0) | |
- die(100, "first label must be 'epoch'"); | |
+ err(100, "first label must be 'epoch'"); | |
for (ncol = 0; (tok = strsep(&cp, ",")) != NULL; ncol++, labv++) | |
*labv = tok; | |
*labv = NULL; | |
if (ncol < 1) | |
- die(100, "no label found"); | |
+ err(100, "no label found"); | |
return ncol; | |
} | |
@@ -223,7 +220,7 @@ main(int argc, char **argv) | |
char *labv[4069 / 2], labels[4069]; | |
int c; | |
- optind = 0; | |
+ arg0 = *argv; | |
while ((c = getopt(argc, argv, "w:")) > -1) { | |
switch (c) { | |
case 'w': | |
@@ -243,14 +240,14 @@ main(int argc, char **argv) | |
for (m = max; argc > 0; argc--, argv++, m++) { | |
*m = strtod(*argv, NULL); | |
if (errno) | |
- error("*** parsing float '%s'", *argv); | |
+ warn("*** parsing float '%s'", *argv); | |
} | |
ncol = read_labels(labv); | |
width = (wflag - sizeof("XXxXXxXX _")) / ncol - sizeof("|"); | |
fmt_labels(labels, ncol, labv); | |
if (ncol != nmax) | |
- die(100, "not as many labels and arguments"); | |
+ err(100, "not as many labels and arguments"); | |
plot(labels, max, ncol); | |
return 0; | |
diff --git a/ploot-text.c b/ploot-text.c | |
@@ -2,13 +2,13 @@ | |
#include <stdio.h> | |
#include <unistd.h> | |
#include <stdlib.h> | |
+#include <string.h> | |
+#include <errno.h> | |
#include "drawille.h" | |
#include "font.h" | |
#include "util.h" | |
-char *arg0 = NULL; | |
- | |
void | |
usage(void) | |
{ | |
@@ -22,9 +22,11 @@ main(int argc, char **argv) | |
struct font *ft; | |
struct drawille *drw; | |
char *text; | |
+ size_t h, w; | |
int c, row; | |
ft = &font8; | |
+ arg0 = *argv; | |
while ((c = getopt(argc, argv, "12")) > -1) { | |
switch (c) { | |
case '1': | |
@@ -37,7 +39,6 @@ main(int argc, char **argv) | |
usage(); | |
} | |
} | |
- arg0 = *argv; | |
argc -= optind; | |
argv += optind; | |
@@ -46,13 +47,17 @@ main(int argc, char **argv) | |
text = *argv; | |
- assert(drw = drawille_new((ft->height + 3) / 4, font_strlen(ft, text) … | |
+ h = (ft->height + 3) / 4; | |
+ w = font_strlen(ft, text) / 2; | |
+ if ((drw = drawille_new(h, w)) == NULL) | |
+ err(1, "drawille_new: %s", strerror(errno)); | |
drawille_text(drw, 0, 0, ft, text); | |
- for (row = 0; row < drw->row; row++) { | |
+ for (row = 0; row < drw->row; row++) { | |
drawille_put_row(stdout, drw, row); | |
- fprintf(stdout, "\n"); | |
- } | |
+ fprintf(stdout, "\n"); | |
+ } | |
free(drw); | |
+ return 0; | |
} | |
diff --git a/proto.sh b/proto.sh | |
@@ -1,73 +0,0 @@ | |
-#!/bin/sh | |
-awk=' | |
-BEGIN { | |
- tab = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t" | |
- print "/**/" | |
-} | |
- | |
-END { | |
- print "" | |
- print "#endif" | |
-} | |
- | |
-# functions | |
- | |
-args { | |
- sub(/^[ \t]*/, " ") | |
- args = args $0 | |
-} | |
- | |
-/^[a-zA-Z0-9_]+\([][)(a-z_A-Z0-9*,. \t]*$/ { | |
- if (match(type, "static") || match($0, ";$")) | |
- next | |
- | |
- symbol = $0 | |
- sub(/\(.*/, "", symbol) | |
- sub(/[a-zA-Z0-9_]*\(/, "", $0) | |
- if (symbol == "main") | |
- next | |
- | |
- args = $0 | |
- sub(/^[a-z]*\(/, "", args) | |
-} | |
- | |
-args && /\)$/ { | |
- gsub(/[\n \t]+/, " ", args) | |
- | |
- sub(/\)$/, "", args) | |
- | |
- gsub(/[a-zA-Z0-9_]+\[[^]]*\]/, "[]", args) | |
- gsub(/[*][a-zA-Z0-9_]+/, "*", args) | |
- gsub(/[ ][a-zA-Z0-9_]+,/, ",", args) | |
- gsub(/[ ][a-zA-Z0-9_]+$/, "", args) | |
- gsub(/[ ][a-zA-Z0-9_]+\*/, "*", args) | |
- gsub(/\.\.\.\$/, "...", args) | |
- gsub(/void\)$/, "void", args) | |
- | |
- printf("%s%s%s%s(%s);\n", | |
- type, substr(tab, 1, 20 / 8 - (length(type) - 3) / 8), | |
- symbol, substr(tab, 1, 30 / 8 - (length(symbol) - 1) / 8), | |
- args) | |
- | |
- args = "" | |
-} | |
- | |
-!args { | |
- type = $0 | |
-} | |
- | |
-# variables | |
- | |
-/^[a-zA-Z][][ \t*a-z_A-Z0-9]*=.*[;{]$/ && $1 != "static" && $1 != "enum" { | |
- sub(/ *=.*/, ";") | |
- sub(/[ \t]*;$/, ";"); | |
-} | |
-' | |
- | |
-for file in src/*.c; do file=${file%.c} | |
- grep -Fq '/**/' "$file.h" 2>/dev/null || continue | |
- header=$(awk '$0 == "/**/" { exit(0) } 1' "$file.h" | |
- awk "$awk" "$file.c") | |
- printf '%s\n' "$header" >"$file.h" | |
-done | |
diff --git a/scale.c b/scale.c | |
@@ -0,0 +1,94 @@ | |
+#include "scale.h" | |
+ | |
+#include <stddef.h> | |
+#include <time.h> | |
+ | |
+#include "util.h" | |
+ | |
+/* | |
+ * - <max ^ | |
+ * - | Translate the coordinates between double values | |
+ * - <val szy and height in the plot of <row> rows. | |
+ * - | | |
+ * - <min v | |
+ */ | |
+int | |
+scale_ypos(double val, double min, double max, int szy) | |
+{ | |
+ return szy * (val - min) / (max - min); | |
+} | |
+ | |
+/* | |
+ * <---- szx ----> Translate the coordinates between the… | |
+ * range and position in the plot of <col> cols. | |
+ * t1 t t2 | |
+ * | . . | . . | | |
+ */ | |
+int | |
+scale_xpos(time_t t, time_t t1, time_t t2, int szx) | |
+{ | |
+ return szx * (t - t1) / (t2 - t1); | |
+} | |
+ | |
+void | |
+scale_minmax(struct csv *vl, int ncol, | |
+ time_t *tmin, time_t *tmax, | |
+ double *vmin, double *vmax) | |
+{ | |
+ double *v; | |
+ time_t *t; | |
+ size_t n; | |
+ | |
+ *vmin = *vmax = 0; | |
+ *tmin = *tmax = *vl->t; | |
+ | |
+ for (; ncol > 0; ncol--, vl++) { | |
+ for (t = vl->t, v = vl->v, n = vl->n; n > 0; t++, v++, n--) { | |
+ if (*v < *vmin) *vmin = *v; | |
+ if (*v > *vmax) *vmax = *v; | |
+ if (*t < *tmin) *tmin = *t; | |
+ if (*t > *tmax) *tmax = *t; | |
+ } | |
+ } | |
+ | |
+ if (*tmin == *tmax) | |
+ err(1, "invalid time scale: min=%lld max=%lld", *tmin, *tmax); | |
+} | |
+ | |
+time_t | |
+scale_csvep(time_t min, time_t max, int nval) | |
+{ | |
+ time_t dt, *sc, scale[] = { | |
+ 1, 5, 2, 10, 20, 30, 60, 60*2, 60*5, 60*10, 60*20, 60*30, 3600… | |
+ 3600*2, 3600*5, 3600*10, 3600*18, 3600*24, 3600*24*2, | |
+ 3600*24*5, 3600*24*10, 3600*24*20, 3600*24*30, 3600*24*50, | |
+ 3600*24*100, 3600*24*365, 0 | |
+ }; | |
+ | |
+ dt = max - min; | |
+ | |
+ for (sc = scale; *sc > 0; sc++) | |
+ if (dt < *sc * nval) | |
+ return *sc; | |
+ return dt / nval; | |
+} | |
+ | |
+double | |
+scale_vstep(double min, double max, int nval) | |
+{ | |
+ double dv, d, *sc, scale[] = { 1, 2, 3, 5 }; | |
+ | |
+ dv = max - min; | |
+ | |
+ if (dv > 1) | |
+ for (d = 1; d != 0; d *= 10) | |
+ for (sc = scale; sc < scale + LEN(scale); sc++) | |
+ if (dv < *sc * d * nval) | |
+ return *sc * d; | |
+ if (dv < 1) | |
+ for (d = 1; d != 0; d *= 10) | |
+ for (sc = scale + LEN(scale) - 1; sc >= scale; sc--) | |
+ if (dv > *sc / d * nval / 2) | |
+ return *sc / d; | |
+ return 0; | |
+} | |
diff --git a/scale.h b/scale.h | |
@@ -0,0 +1,14 @@ | |
+#ifndef SCALE_H | |
+#define SCALE_H | |
+ | |
+#include <stddef.h> | |
+#include <time.h> | |
+#include "csv.h" | |
+ | |
+int scale_ypos(double, double, double, int); | |
+int scale_xpos(time_t, time_t, time_t, int); | |
+void scale_minmax(struct csv *, int, time_t *, time_t *, double *, doub… | |
+time_t scale_csvep(time_t, time_t, int); | |
+double scale_vstep(double, double, int); | |
+ | |
+#endif | |
diff --git a/src/csv.c b/src/csv.c | |
@@ -1,122 +0,0 @@ | |
-#include "csv.h" | |
- | |
-#include <errno.h> | |
-#include <assert.h> | |
-#include <string.h> | |
-#include <time.h> | |
-#include <stdlib.h> | |
-#include <limits.h> | |
-#include <time.h> | |
- | |
-#include "log.h" | |
-#include "util.h" | |
- | |
-/* | |
- * Read CSV data onto a set of (struct csv). | |
- */ | |
- | |
-static void | |
-csv_addtime(struct csv *vl, time_t epoch) | |
-{ | |
- assert(vl->t = realloc(vl->t, (vl->n + 1) * sizeof(*vl->t))); | |
- vl->t[vl->n] = epoch; | |
-} | |
- | |
-static void | |
-csv_addval(struct csv *vl, double field) | |
-{ | |
- assert(vl->v = realloc(vl->v, (vl->n + 1) * sizeof(*vl->v))); | |
- vl->v[vl->n] = field; | |
-} | |
- | |
-/* | |
- * Add to each column the value on the current row. The time_t | |
- * buffer is shared among all fields. | |
- */ | |
-void | |
-csv_addrow(struct csv *vl, size_t ncol, char *line) | |
-{ | |
- char *field; | |
- time_t *tbuf; | |
- long l; | |
- double d; | |
- | |
- field = strsep(&line, ","); | |
- if (!field) | |
- die(1, "missing epoch at row %zu", vl->n); | |
- | |
- l = strtol(field, NULL, 10); | |
- if (errno) | |
- die(100, "parsing number '%s'", field); | |
- csv_addtime(vl, l); | |
- tbuf = vl[0].t; | |
- for (; (field = strsep(&line, ",")); ncol--, vl->n++, vl++) { | |
- if (ncol == 0) | |
- die(1, "too many fields at line %zu", vl->n); | |
- d = strtod(field, NULL); | |
- if (errno) | |
- die(100, "parsing double '%s'", field); | |
- csv_addval(vl, d); | |
- vl->t = tbuf; | |
- } | |
- if (ncol > 0) | |
- die(1, "too few fields at line %zu", vl->n); | |
-} | |
- | |
-/* | |
- * < *ncol > | |
- * epoch,label1,label2,label3 | |
- */ | |
-void | |
-csv_labels(FILE *fp, struct csv **vl, size_t *ncol) | |
-{ | |
- char *field, *line, *cp; | |
- struct csv *col; | |
- size_t sz; | |
- ssize_t r; | |
- | |
- sz = 0, line = NULL; | |
- r = getline(&line, &sz, fp); | |
- if (ferror(fp)) | |
- die(111, "error while reading from file"); | |
- if (r == -1) | |
- die(100, "missing label line"); | |
- strchomp(line); | |
- | |
- cp = line; | |
- if (strcmp(strsep(&cp, ","), "epoch") != 0) | |
- die(1, "first label must be 'epoch'"); | |
- | |
- *vl = NULL; | |
- *ncol = 0; | |
- while ((field = strsep(&cp, ","))) { | |
- assert(*vl = realloc(*vl, sz += sizeof(**vl))); | |
- col = (*vl) + (*ncol)++; | |
- strlcpy(col->label, field, sizeof(col->label)); | |
- } | |
- | |
- free(line); | |
-} | |
- | |
-/* | |
- * < ncol > | |
- * epoch,a1,b1,c1 ^ | |
- * epoch,a2,b2,c2 vl->n | |
- * epoch,a3,b3,c3 v | |
- */ | |
-void | |
-csv_values(FILE *fp, struct csv *vl, size_t ncol) | |
-{ | |
- char *line; | |
- size_t sz; | |
- | |
- sz = 0, line = NULL; | |
- while (getline(&line, &sz, fp) > -1) | |
- csv_addrow(vl, ncol, line); | |
- if (vl->n == 0) | |
- die(1, "no value could be read"); | |
- if (vl->n == 1) | |
- die(1, "only one value could be read"); | |
- | |
- free(line); | |
-} | |
diff --git a/src/csv.h b/src/csv.h | |
@@ -1,23 +0,0 @@ | |
-#ifndef CSV_H | |
-#define CSV_H | |
- | |
-#include <stdio.h> | |
-#include <time.h> | |
- | |
-/* | |
- * List of values and timestamps. Both have their dedicated buffer | |
- * so that the timestamp buffer can be shared across csv objects. | |
- */ | |
-struct csv { | |
- time_t *t; /* array of timestamps */ | |
- double *v; /* array of values */ | |
- size_t n; /* number of values */ | |
- char label[64]; /* for the legend */ | |
-}; | |
- | |
-/**/ | |
-void csv_addrow (struct csv *, size_t, char *); | |
-void csv_labels (FILE *, struct csv **, size_t *… | |
-void csv_values (FILE *, struct csv *, size_t); | |
- | |
-#endif | |
diff --git a/src/drawille.c b/src/drawille.c | |
@@ -1,196 +0,0 @@ | |
-#include "drawille.h" | |
- | |
-#include <stdint.h> | |
-#include <stdio.h> | |
-#include <stdlib.h> | |
-#include <string.h> | |
-#include <math.h> | |
- | |
-#include "font.h" | |
- | |
-#include "log.h" /* XXX */ | |
- | |
-/* | |
- * Terminal-based plotting using drawille character, aka drawille. | |
- */ | |
- | |
-/* parameters used to draw a line */ | |
-struct line { | |
- int x0, y0, x1, y1; /* point of the line */ | |
- int dx, dy, sx, sy, err; /* parameters for the algorythm */ | |
-}; | |
- | |
-/* | |
- * Turn on the bit at position (row, col) of a single cell. The | |
- * pattern is not linear (1-4-2-5-3-6-7-8), because it matches the | |
- * drawille pattern. | |
- */ | |
-static void | |
-drawille_cell_dot(uint8_t *cell, int row, int col) | |
-{ | |
- uint8_t flags[4][2] = { | |
- { 0x01, 0x08 }, | |
- { 0x02, 0x10 }, | |
- { 0x04, 0x20 }, | |
- { 0x40, 0x80 }, | |
- }; | |
- | |
- *cell |= flags[row][col]; | |
-} | |
- | |
-static size_t | |
-drawille_cell_utf(uint8_t cell, char *utf) | |
-{ | |
- long rune; | |
- | |
- rune = 10240 + cell; | |
- utf[0] = (char)(0xe0 | (0x0f & (rune >> 12))); /* 1110xxxx */ | |
- utf[1] = (char)(0x80 | (0x3f & (rune >> 6))); /* 10xxxxxx */ | |
- utf[2] = (char)(0x80 | (0x3f & (rune))); /* 10xxxxxx */ | |
- return 3; | |
-} | |
- | |
-static uint8_t | |
-drawille_get(struct drawille *drw, int row, int col) | |
-{ | |
- return drw->buf[row * drw->col + col]; | |
-} | |
- | |
-size_t | |
-drawille_put_row(FILE *fp, struct drawille *drw, int row) | |
-{ | |
- char txt[] = "xxx"; | |
- size_t n; | |
- | |
- n = 0; | |
- for (int col = 0; col < drw->col; col++) { | |
- drawille_cell_utf(drawille_get(drw, row, col), txt); | |
- n += fputs(txt, fp); | |
- } | |
- return n; | |
-} | |
- | |
-/* | |
- * Coordinates are passed as (x, y), but the canvas stores bits as | |
- * (row, col). Conversion is made by this function. | |
- */ | |
-void | |
-drawille_dot(struct drawille *drw, int x, int y) | |
-{ | |
- if (x < 0 || x / 2 >= drw->col || y < 0 || y / 4 >= drw->row) | |
- return; | |
- drawille_cell_dot(drw->buf + (drw->row - y / 4 - 1) * drw->col + (x / … | |
- 3 - y % 4, | |
- x % 2); | |
-} | |
- | |
-struct drawille * | |
-drawille_new(int row, int col) | |
-{ | |
- struct drawille *drw; | |
- | |
- if ((drw = calloc(sizeof(struct drawille) + row * col, 1)) == NULL) | |
- return NULL; | |
- drw->row = row; | |
- drw->col = col; | |
- return drw; | |
-} | |
- | |
-static void | |
-drawille_line_init(struct line *l, int x0, int y0, int x1, int y1) | |
-{ | |
- l->x0 = x0; | |
- l->y0 = y0; | |
- l->x1 = x1; | |
- l->y1 = y1; | |
- l->sx = x0 < x1 ? 1 : -1; | |
- l->sy = y0 < y1 ? 1 : -1; | |
- l->dx = abs(x1 - x0); | |
- l->dy = abs(y1 - y0); | |
- l->err = (l->dx > l->dy ? l->dx : -l->dy) / 2; | |
-} | |
- | |
-static int | |
-drawille_line_next(struct line *l) | |
-{ | |
- int e; | |
- | |
- if (l->x0 == l->x1 && l->y0 == l->y1) | |
- return 0; | |
- | |
- e = l->err; | |
- if (e > -l->dx) { | |
- l->x0 += l->sx; | |
- l->err -= l->dy; | |
- } | |
- if (e < l->dy) { | |
- l->y0 += l->sy; | |
- l->err += l->dx; | |
- } | |
- return 1; | |
-} | |
- | |
-void | |
-drawille_line(struct drawille *drw, int x0, int y0, int x1, int y1) | |
-{ | |
- struct line l; | |
- | |
- drawille_line_init(&l, x0, y0, x1, y1); | |
- do { | |
- drawille_dot(drw, l.x0, l.y0); | |
- } while (drawille_line_next(&l)); | |
-} | |
- | |
-void | |
-drawille_histogram_dot(struct drawille *drw, int x, int y, int zero) | |
-{ | |
- int sign; | |
- | |
- sign = (y > zero) ? (+1) : (-1); | |
- for (; y != zero; y -= sign) | |
- drawille_dot(drw, x, y); | |
- drawille_dot(drw, x, y); | |
-} | |
- | |
-void | |
-drawille_histogram_line(struct drawille *drw, int x0, int y0, int x1, int y1, … | |
-{ | |
- struct line l; | |
- | |
- drawille_line_init(&l, x0, y0, x1, y1); | |
- do { | |
- drawille_histogram_dot(drw, l.x0, l.y0, zero); | |
- } while (drawille_line_next(&l)); | |
-} | |
- | |
-static int | |
-drawille_text_glyph(struct drawille *drw, int x, int y, struct font *font, cha… | |
-{ | |
- int width; | |
- char *glyph; | |
- | |
- if ((unsigned)c > 127) | |
- glyph = font->glyph[0]; | |
- else | |
- glyph = font->glyph[(unsigned)c]; | |
- | |
- width = strlen(glyph) / font->height; | |
- | |
- for (int ix = 0; ix < width; ix++) | |
- for (int iy = 0; iy < font->height; iy++) { | |
- if (glyph[ix + (font->height - 1) * width - iy * width] == 3) | |
- drawille_dot(drw, x + ix, y + iy); | |
- } | |
- | |
- return width; | |
-} | |
- | |
-char * | |
-drawille_text(struct drawille *drw, int x, int y, struct font *font, char *s) | |
-{ | |
- if (drw->row*4 < font->height) | |
- return NULL; | |
- for (; *s != '\0' && x < drw->col * 2; s++, x++) | |
- x += drawille_text_glyph(drw, x, y, font, *s); | |
- return s; | |
-} | |
diff --git a/src/drawille.h b/src/drawille.h | |
@@ -1,28 +0,0 @@ | |
-#ifndef DRAWILLE_H | |
-#define DRAWILLE_H | |
- | |
-#include <stddef.h> | |
-#include <stdint.h> | |
-#include <stdio.h> | |
- | |
-#include "csv.h" | |
-#include "font.h" | |
- | |
-/* | |
- * Canvas to draw on with braille characters. | |
- */ | |
-struct drawille { | |
- int col, row; /* number of dots in total */ | |
- uint8_t buf[]; /* buffer of size (col * … | |
-}; | |
- | |
-/**/ | |
-size_t drawille_put_row (FILE *, struct drawille *, int); | |
-void drawille_dot (struct drawille *, int, int); | |
-struct drawille *drawille_new (int, int); | |
-void drawille_line (struct drawille *, int, int,… | |
-void drawille_histogram_dot (struct drawille *, int, int… | |
-void drawille_histogram_line (struct drawille *, int, in… | |
-char * drawille_text (struct drawille *, int, in… | |
- | |
-#endif | |
diff --git a/src/ffplot.c b/src/ffplot.c | |
@@ -1,148 +0,0 @@ | |
-#include "ffplot.h" | |
- | |
-#include <arpa/inet.h> | |
-#include <stddef.h> | |
-#include <string.h> | |
-#include <stdio.h> | |
-#include <stdint.h> | |
- | |
-#include "font.h" | |
-#include "util.h" | |
- | |
-/* | |
- * Convert (x,y) coordinates to (row,col) for printing into the buffer. | |
- * The buffer only contain one number, so the coordinate is a single integer: | |
- * width * y + y. | |
- * The coordinates are shifted by offx and offy to permit relative coordinates. | |
- * | |
- * The convention used: y | |
- * - (0,0) is at the lower left corner of the plotvas. | | |
- * - (0,1) is above it. +--x | |
- */ | |
-void | |
-ffplot_pixel(struct ffplot *plot, struct ffcolor *color, | |
- int x, int y) | |
-{ | |
- x += plot->x; | |
- y += plot->y; | |
- if (x < 0 || x >= plot->w || y < 0 || y >= plot->h) | |
- return; | |
- memcpy(plot->buf + plot->w * (plot->h - 1 - y) + x, color, sizeof(*plo… | |
-} | |
- | |
-void | |
-ffplot_rectangle(struct ffplot *plot, struct ffcolor *color, | |
- int y1, int x1, | |
- int y2, int x2) | |
-{ | |
- int x, y, ymin, xmin, ymax, xmax; | |
- | |
- ymin = MIN(y1, y2); ymax = MAX(y1, y2); | |
- xmin = MIN(x1, x2); xmax = MAX(x1, x2); | |
- | |
- for (y = ymin; y <= ymax; y++) | |
- for (x = xmin; x <= xmax; x++) | |
- ffplot_pixel(plot, color, x, y); | |
-} | |
- | |
-/* | |
- * From Bresenham's line algorithm and dcat's tplot. | |
- */ | |
-void | |
-ffplot_line(struct ffplot *plot, struct ffcolor *color, | |
- int x0, int y0, | |
- int x1, int y1) | |
-{ | |
- int dy, dx, sy, sx, err, e; | |
- | |
- sx = x0 < x1 ? 1 : -1; | |
- sy = y0 < y1 ? 1 : -1; | |
- dx = ABS(x1 - x0); | |
- dy = ABS(y1 - y0); | |
- err = (dy > dx ? dy : -dx) / 2; | |
- | |
- for (;;) { | |
- ffplot_pixel(plot, color, x0, y0); | |
- | |
- if (y0 == y1 && x0 == x1) | |
- break; | |
- | |
- e = err; | |
- if (e > -dy) { | |
- y0 += sy; | |
- err -= dx; | |
- } | |
- if (e < dx) { | |
- x0 += sx; | |
- err += dy; | |
- } | |
- } | |
-} | |
- | |
-/* | |
- * Draw a coloured glyph from font f centered on y. | |
- */ | |
-int | |
-ffplot_char(struct ffplot *plot, struct ffcolor *color, struct font *ft, char … | |
- int x, int y) | |
-{ | |
- int yf, xf, wf; | |
- | |
- if (c & 0x80) | |
- c = '\0'; | |
- y -= ft->height / 2; | |
- wf = font_width(ft, c); | |
- for (xf = 0; xf < wf; xf++) | |
- for (yf = 0; yf < ft->height; yf++) | |
- if (ft->glyph[(int)c][wf * (ft->height - yf) + xf] == … | |
- ffplot_pixel(plot, color, x + xf, y + yf); | |
- return wf + 1; | |
-} | |
- | |
-/* | |
- * Draw a left aligned string without wrapping it. | |
- */ | |
-size_t | |
-ffplot_text_left(struct ffplot *plot, struct ffcolor *color, struct font *ft, | |
- char *s, int x, int y) | |
-{ | |
- for (; *s != '\0'; s++) | |
- x += ffplot_char(plot, color, ft, *s, x, y); | |
- return x; | |
-} | |
- | |
-/* | |
- * Draw a center aligned string without wrapping it. | |
- */ | |
-size_t | |
-ffplot_text_center(struct ffplot *plot, struct ffcolor *color, struct font *ft, | |
- char *s, int x, int y) | |
-{ | |
- x -= font_strlen(ft, s) / 2; | |
- return ffplot_text_left(plot, color, ft, s, x, y); | |
-} | |
- | |
-/* | |
- * Draw a right aligned string without wrapping it. | |
- */ | |
-size_t | |
-ffplot_text_right(struct ffplot *plot, struct ffcolor *color, struct font *ft, | |
- char *s, int x, int y) | |
-{ | |
- x -= font_strlen(ft, s); | |
- return ffplot_text_left(plot, color, ft, s, x, y); | |
-} | |
- | |
-void | |
-ffplot_print(FILE *fp, struct ffplot *plot) | |
-{ | |
- uint32_t w, h; | |
- | |
- w = htonl(plot->w); | |
- h = htonl(plot->h); | |
- | |
- fprintf(stdout, "farbfeld"); | |
- fwrite(&w, sizeof(w), 1, fp); | |
- fwrite(&h, sizeof(h), 1, fp); | |
- fwrite(plot->buf, plot->w * plot->h, sizeof(*plot->buf), fp); | |
-} | |
diff --git a/src/ffplot.h b/src/ffplot.h | |
@@ -1,35 +0,0 @@ | |
-#ifndef FFPLOT_H | |
-#define FFPLOT_H | |
- | |
-#include <stdio.h> | |
-#include <stddef.h> | |
-#include <stdint.h> | |
- | |
-#include "font.h" | |
- | |
-struct ffcolor { | |
- uint16_t red; | |
- uint16_t green; | |
- uint16_t blue; | |
- uint16_t alpha; | |
-}; | |
- | |
-struct ffplot { | |
- int w; /* width */ | |
- int h; /* height */ | |
- int x; /* x offset */ | |
- int y; /* y offset */ | |
- struct ffcolor *buf; | |
-}; | |
- | |
-/**/ | |
-void ffplot_pixel (struct ffplot *, struct ffcol… | |
-void ffplot_rectangle (struct ffplot *, struct ffcolor *… | |
-void ffplot_line (struct ffplot *, struct ffcolo… | |
-int ffplot_char (struct ffplot *, struct ffcolor… | |
-size_t ffplot_text_left (struct ffplot *, struct ffcolor… | |
-size_t ffplot_text_center (struct ffplot *, struct ffcol… | |
-size_t ffplot_text_right (struct ffplot *, struct ffcolo… | |
-void ffplot_print (FILE *, struct ffplot *); | |
- | |
-#endif | |
diff --git a/src/font.h b/src/font.h | |
@@ -1,21 +0,0 @@ | |
-#ifndef FONT_H | |
-#define FONT_H | |
- | |
-#include <stddef.h> | |
- | |
-/* | |
- * Bitmapped font saved as a '_' and 'X' pattern in a C source file. | |
- */ | |
-struct font { | |
- int height; /* The width is variable. */ | |
- char *glyph[128]; /* 0: end, 1: off, 2: on. */ | |
-}; | |
- | |
-extern struct font font8; | |
-extern struct font font13; | |
- | |
-/**/ | |
-size_t font_width (struct font *, int); | |
-size_t font_strlen (struct font *, char *); | |
- | |
-#endif | |
diff --git a/src/log.c b/src/log.c | |
@@ -1,97 +0,0 @@ | |
-#include "log.h" | |
- | |
-#include <string.h> | |
- | |
-/* | |
- * log.c - log to standard error according to the log level | |
- * | |
- * Instead of logging to syslog, delegate logging to a separate | |
- * tool, such as FreeBSD's daemon(8), POSIX's logger(1). | |
- * | |
- * log_init() sets the log level to the "LOG" environment variable | |
- * if set, or to 4 (log down to info included) otherwise. | |
- */ | |
- | |
-#include <errno.h> | |
-#include <stdlib.h> | |
-#include <stdio.h> | |
- | |
-#define LOG_DEFAULT 2 /* info */ | |
- | |
-int log_level = -1; | |
- | |
-void | |
-vlogf(int level, char const *flag, char const *fmt, va_list va) | |
-{ | |
- char *env; | |
- | |
- if (log_level == -1) { | |
- env = getenv("LOG"); | |
- log_level = env ? atoi(env) : 0; | |
- log_level = log_level > 0 ? log_level : LOG_DEFAULT; | |
- } | |
- | |
- if (log_level < level) | |
- return; | |
- | |
- fprintf(stderr, "%s: ", flag); | |
- vfprintf(stderr, fmt, va); | |
- | |
- if (errno) | |
- fprintf(stderr, ": %s", strerror(errno)); | |
- errno = 0; | |
- | |
- fprintf(stderr, "\n"); | |
- fflush(stderr); | |
-} | |
- | |
-void | |
-die(int exitcode, char const *fmt, ...) | |
-{ | |
- va_list va; | |
- | |
- va_start(va, fmt); | |
- vlogf(0, "error", fmt, va); | |
- va_end(va); | |
- exit(exitcode); | |
-} | |
- | |
-void | |
-error(char const *fmt, ...) | |
-{ | |
- va_list va; | |
- | |
- va_start(va, fmt); | |
- vlogf(0, "error", fmt, va); | |
- va_end(va); | |
-} | |
- | |
-void | |
-warn(char const *fmt, ...) | |
-{ | |
- va_list va; | |
- | |
- va_start(va, fmt); | |
- vlogf(1, "warn", fmt, va); | |
- va_end(va); | |
-} | |
- | |
-void | |
-info(char const *fmt, ...) | |
-{ | |
- va_list va; | |
- | |
- va_start(va, fmt); | |
- vlogf(2, "info", fmt, va); | |
- va_end(va); | |
-} | |
- | |
-void | |
-debug(char const *fmt, ...) | |
-{ | |
- va_list va; | |
- | |
- va_start(va, fmt); | |
- vlogf(3, "debug", fmt, va); | |
- va_end(va); | |
-} | |
diff --git a/src/log.h b/src/log.h | |
@@ -1,15 +0,0 @@ | |
-#ifndef LOG_H | |
-#define LOG_H | |
- | |
-#include <stdarg.h> | |
- | |
-/**/ | |
-extern int log_level; | |
-void vlogf (int, char const *, char cons… | |
-void die (int, char const *, ...); | |
-void error (char const *, ...); | |
-void warn (char const *, ...); | |
-void info (char const *, ...); | |
-void debug (char const *, ...); | |
- | |
-#endif | |
diff --git a/src/scale.c b/src/scale.c | |
@@ -1,95 +0,0 @@ | |
-#include "scale.h" | |
- | |
-#include <stddef.h> | |
-#include <time.h> | |
- | |
-#include "util.h" | |
-#include "log.h" | |
- | |
-/* | |
- * - <max ^ | |
- * - | Translate the coordinates between double values | |
- * - <val szy and height in the plot of <row> rows. | |
- * - | | |
- * - <min v | |
- */ | |
-int | |
-scale_ypos(double val, double min, double max, int szy) | |
-{ | |
- return szy * (val - min) / (max - min); | |
-} | |
- | |
-/* | |
- * <---- szx ----> Translate the coordinates between the… | |
- * range and position in the plot of <col> cols. | |
- * t1 t t2 | |
- * | . . | . . | | |
- */ | |
-int | |
-scale_xpos(time_t t, time_t t1, time_t t2, int szx) | |
-{ | |
- return szx * (t - t1) / (t2 - t1); | |
-} | |
- | |
-void | |
-scale_minmax(struct csv *vl, int ncol, | |
- time_t *tmin, time_t *tmax, | |
- double *vmin, double *vmax) | |
-{ | |
- double *v; | |
- time_t *t; | |
- size_t n; | |
- | |
- *vmin = *vmax = 0; | |
- *tmin = *tmax = *vl->t; | |
- | |
- for (; ncol > 0; ncol--, vl++) { | |
- for (t = vl->t, v = vl->v, n = vl->n; n > 0; t++, v++, n--) { | |
- if (*v < *vmin) *vmin = *v; | |
- if (*v > *vmax) *vmax = *v; | |
- if (*t < *tmin) *tmin = *t; | |
- if (*t > *tmax) *tmax = *t; | |
- } | |
- } | |
- | |
- if (*tmin == *tmax) | |
- die(1, "invalid time scale: min=%lld max=%lld", *tmin, *tmax); | |
-} | |
- | |
-time_t | |
-scale_tstep(time_t min, time_t max, int nval) | |
-{ | |
- time_t dt, *sc, scale[] = { | |
- 1, 5, 2, 10, 20, 30, 60, 60*2, 60*5, 60*10, 60*20, 60*30, 3600… | |
- 3600*2, 3600*5, 3600*10, 3600*18, 3600*24, 3600*24*2, | |
- 3600*24*5, 3600*24*10, 3600*24*20, 3600*24*30, 3600*24*50, | |
- 3600*24*100, 3600*24*365, 0 | |
- }; | |
- | |
- dt = max - min; | |
- | |
- for (sc = scale; *sc > 0; sc++) | |
- if (dt < *sc * nval) | |
- return *sc; | |
- return dt / nval; | |
-} | |
- | |
-double | |
-scale_vstep(double min, double max, int nval) | |
-{ | |
- double dv, d, *sc, scale[] = { 1, 2, 3, 5 }; | |
- | |
- dv = max - min; | |
- | |
- if (dv > 1) | |
- for (d = 1; d != 0; d *= 10) | |
- for (sc = scale; sc < scale + LEN(scale); sc++) | |
- if (dv < *sc * d * nval) | |
- return *sc * d; | |
- if (dv < 1) | |
- for (d = 1; d != 0; d *= 10) | |
- for (sc = scale + LEN(scale) - 1; sc >= scale; sc--) | |
- if (dv > *sc / d * nval / 2) | |
- return *sc / d; | |
- return 0; | |
-} | |
diff --git a/src/scale.h b/src/scale.h | |
@@ -1,16 +0,0 @@ | |
-#ifndef SCALE_H | |
-#define SCALE_H | |
- | |
-#include <stddef.h> | |
-#include <time.h> | |
- | |
-#include "csv.h" | |
- | |
-/**/ | |
-int scale_ypos (double, double, double, int); | |
-int scale_xpos (time_t, time_t, time_t, int); | |
-void scale_minmax (struct csv *, int, time_t *, … | |
-time_t scale_tstep (time_t, time_t, int); | |
-double scale_vstep (double, double, int); | |
- | |
-#endif | |
diff --git a/src/util.c b/src/util.c | |
@@ -1,80 +0,0 @@ | |
-#include "util.h" | |
- | |
-#include <ctype.h> | |
-#include <errno.h> | |
-#include <limits.h> | |
-#include <stdarg.h> | |
-#include <stdio.h> | |
-#include <stdlib.h> | |
-#include <string.h> | |
- | |
-size_t | |
-strlcpy(char *buf, const char *str, size_t sz) | |
-{ | |
- size_t len, cpy; | |
- | |
- cpy = ((len = strlen(str)) > sz) ? (sz) : (len); | |
- memcpy(buf, str, cpy); | |
- buf[sz - 1] = '\0'; | |
- return len; | |
-} | |
- | |
-void | |
-put3utf(long rune) | |
-{ | |
- putchar((char)(0xe0 | (0x0f & (rune >> 12)))); /* 1110xxxx */ | |
- putchar((char)(0x80 | (0x3f & (rune >> 6)))); /* 10xxxxxx */ | |
- putchar((char)(0x80 | (0x3f & (rune)))); /* 10xxxxxx */ | |
-} | |
- | |
-char * | |
-strsep(char **strp, const char *sep) | |
-{ | |
- char *s, *prev; | |
- | |
- if (*strp == NULL) | |
- return NULL; | |
- for (s = prev = *strp; strchr(sep, *s) == NULL; s++); | |
- if (*s == '\0') { | |
- *strp = NULL; | |
- return prev; | |
- } | |
- *s = '\0'; | |
- *strp = s + 1; | |
- | |
- return prev; | |
-} | |
- | |
-void | |
-strchomp(char *s) | |
-{ | |
- char *x = s + strlen(s); | |
- | |
- while (--x >= s && (*x == '\r' || *x == '\n')) | |
- *x = '\0'; | |
-} | |
- | |
-/* | |
- * Set 'str' to a human-readable form of 'num' with always a width of 8 (+1 for | |
- * the '\0' terminator). Buffer overflow is ensured not to happen due to the | |
- * max size of a double. Return the exponent. | |
- */ | |
-int | |
-humanize(char *str, double val) | |
-{ | |
- int exp, precision; | |
- char label[] = { '\0', 'M', 'G', 'T', 'E' }; | |
- | |
- for (exp = 0; ABS(val) > 1000; exp++) | |
- val /= 1000; | |
- | |
- precision = (ABS(val) < 10) ? 2 : (ABS(val) < 100) ? 1 : 0; | |
- precision += (exp == 0); | |
- | |
- snprintf(str, 9, "%+.*f %c", precision, val, label[exp]); | |
- str[8] = '\0'; | |
- if (val >= 0) | |
- str[0] = ' '; | |
- | |
- return exp * 3; | |
-} | |
diff --git a/src/util.h b/src/util.h | |
@@ -1,18 +0,0 @@ | |
-#ifndef TOOL_H | |
-#define TOOL_H | |
- | |
-#include <stddef.h> | |
- | |
-#define LEN(x) (sizeof(x) / sizeof(*x)) | |
-#define MAX(x, y) ((x) > (y) ? (x) : (y)) | |
-#define MIN(x, y) ((x) < (y) ? (x) : (y)) | |
-#define ABS(x) ((x) < 0 ? -(x) : (x)) | |
- | |
-/**/ | |
-size_t strlcpy (char *, const char *, si… | |
-void put3utf (long); | |
-char * strsep (char **, const char *); | |
-void strchomp (char *); | |
-int humanize (char *, double); | |
- | |
-#endif | |
diff --git a/util.c b/util.c | |
@@ -0,0 +1,124 @@ | |
+#include "util.h" | |
+#include <ctype.h> | |
+#include <errno.h> | |
+#include <limits.h> | |
+#include <stdarg.h> | |
+#include <stdio.h> | |
+#include <stdlib.h> | |
+#include <string.h> | |
+ | |
+char const *arg0; | |
+ | |
+static void | |
+_log(char const *fmt, va_list va) | |
+{ | |
+ if (arg0 != NULL) | |
+ fprintf(stderr, "%s: ", arg0); | |
+ vfprintf(stderr, fmt, va); | |
+ fprintf(stderr, "\n"); | |
+ fflush(stderr); | |
+} | |
+ | |
+void | |
+err(int e, char const *fmt, ...) | |
+{ | |
+ va_list va; | |
+ | |
+ va_start(va, fmt); | |
+ _log( fmt, va); | |
+ exit(e); | |
+} | |
+ | |
+void | |
+warn(char const *fmt, ...) | |
+{ | |
+ va_list va; | |
+ | |
+ va_start(va, fmt); | |
+ _log(fmt, va); | |
+} | |
+ | |
+void | |
+debug(char const *fmt, ...) | |
+{ | |
+ static int verbose = -1; | |
+ va_list va; | |
+ | |
+ if (verbose < 0) | |
+ verbose = (getenv("DEBUG") != NULL); | |
+ if (!verbose) | |
+ return; | |
+ va_start(va, fmt); | |
+ _log(fmt, va); | |
+} | |
+ | |
+size_t | |
+strlcpy(char *buf, const char *str, size_t sz) | |
+{ | |
+ size_t len, cpy; | |
+ | |
+ cpy = ((len = strlen(str)) > sz) ? (sz) : (len); | |
+ memcpy(buf, str, cpy); | |
+ buf[sz - 1] = '\0'; | |
+ return len; | |
+} | |
+ | |
+void | |
+put3utf(long rune) | |
+{ | |
+ putchar((char)(0xe0 | (0x0f & (rune >> 12)))); /* 1110xxxx */ | |
+ putchar((char)(0x80 | (0x3f & (rune >> 6)))); /* 10xxxxxx */ | |
+ putchar((char)(0x80 | (0x3f & (rune)))); /* 10xxxxxx */ | |
+} | |
+ | |
+char * | |
+strsep(char **strp, const char *sep) | |
+{ | |
+ char *s, *prev; | |
+ | |
+ if (*strp == NULL) | |
+ return NULL; | |
+ for (s = prev = *strp; strchr(sep, *s) == NULL; s++); | |
+ if (*s == '\0') { | |
+ *strp = NULL; | |
+ return prev; | |
+ } | |
+ *s = '\0'; | |
+ *strp = s + 1; | |
+ | |
+ return prev; | |
+} | |
+ | |
+void | |
+strchomp(char *s) | |
+{ | |
+ char *x = s + strlen(s); | |
+ | |
+ while (--x >= s && (*x == '\r' || *x == '\n')) | |
+ *x = '\0'; | |
+} | |
+ | |
+/* | |
+ * Set 'str' to a human-readable form of 'num' with always a width of 8 (+1 for | |
+ * the '\0' terminator). Buffer overflow is ensured not to happen due to the | |
+ * max size of a double. Return the exponent. | |
+ */ | |
+int | |
+humanize(char *str, double val) | |
+{ | |
+ int exp, precision; | |
+ char label[] = { '\0', 'M', 'G', 'T', 'E' }; | |
+ | |
+ for (exp = 0; ABS(val) > 1000; exp++) | |
+ val /= 1000; | |
+ | |
+ precision = (ABS(val) < 10) ? 2 : (ABS(val) < 100) ? 1 : 0; | |
+ precision += (exp == 0); | |
+ | |
+ snprintf(str, 9, "%+.*f %c", precision, val, label[exp]); | |
+ str[8] = '\0'; | |
+ if (val >= 0) | |
+ str[0] = ' '; | |
+ | |
+ return exp * 3; | |
+} | |
diff --git a/util.h b/util.h | |
@@ -0,0 +1,22 @@ | |
+#ifndef TOOL_H | |
+#define TOOL_H | |
+ | |
+#include <stddef.h> | |
+ | |
+#define LEN(x) (sizeof(x) / sizeof(*x)) | |
+#define MAX(x, y) ((x) > (y) ? (x) : (y)) | |
+#define MIN(x, y) ((x) < (y) ? (x) : (y)) | |
+#define ABS(x) ((x) < 0 ? -(x) : (x)) | |
+ | |
+extern char const *arg0; | |
+ | |
+void err(int, char const *fmt, ...); | |
+void warn(char const *fmt, ...); | |
+void debug(char const *fmt, ...); | |
+size_t strlcpy(char *, const char *, size_t); | |
+void put3utf(long); | |
+char *strsep(char **, const char *); | |
+void strchomp(char *); | |
+int humanize(char *, double); | |
+ | |
+#endif |