flatten the repository and simplify Makefile - ploot - simple plotting tools HTML git clone git://bitreich.org/ploot git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/ploot DIR Log DIR Files DIR Refs DIR Tags DIR README DIR LICENSE --- DIR commit a546b97f9fe7f4a9e30b514bfc257ba85cd9a7f9 DIR parent 1a7e49697644fe2740519b2c5f62a314e675c616 HTML Author: Josuah Demangeon <me@josuah.net> 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(-) --- DIR 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 $@ $@.o ${obj} ${lib} +${OBJ} ${BIN:=.o}: ${INC} Makefile +${BIN}: ${OBJ} ${BIN:=.o} + ${CC} ${LFLAGS} -o $@ $@.o ${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} DIR diff --git a/README b/README @@ -1,60 +0,0 @@ -ploot -===== - - -ploot-farbfeld --------------- - -*ploot-farbfeld* reads collectd-style comma separated values (CSV) and produces a plot -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 produces -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/ DIR 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 a plot +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 produces +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/ DIR diff --git a/config.mk b/config.mk @@ -1,4 +0,0 @@ -CFLAGS = -Wall -Wextra -std=c99 -pedantic -fPIC -I"src" -D_POSIX_C_SOURCE=200811L -LFLAGS = -static -PREFIX = /usr/local -MANDIR = $(PREFIX)/share/man DIR 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); +} DIR 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 DIR 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 / 2), + 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, int zero) +{ + 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, char c) +{ + 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; +} DIR 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 * row) */ +}; + +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, int); +char *drawille_text(struct drawille *, int, int, struct font *, char *); + +#endif DIR diff --git a/test.csv b/example.csv DIR 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(*plot->buf)); +} + +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 c, + 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] == 3) + 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); +} DIR 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, int); +void ffplot_line(struct ffplot *, struct ffcolor *, int, int, int, int); +int ffplot_char(struct ffplot *, struct ffcolor *, struct font *, char, int, int); +size_t ffplot_text_left(struct ffplot *, struct ffcolor *, struct font *, char *, int, int); +size_t ffplot_text_center(struct ffplot *, struct ffcolor *, struct font *, char *, int, int); +size_t ffplot_text_right(struct ffplot *, struct ffcolor *, struct font *, char *, int, int); +void ffplot_print(FILE *, struct ffplot *); + +#endif DIR diff --git a/src/font.c b/font.c DIR 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 DIR diff --git a/src/font13.c b/font13.c DIR diff --git a/src/font8.c b/font8.c DIR 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 tstep, int col) 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, rows); + debug("vstep=%lf vstep=%ld ncol=%zu rows=%zu", vstep, csvep, ncol, rows); 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; DIR 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 DIR 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 stdin. +utility plots an image in the ffplot format out of csv values coming from stdin. . .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 DIR 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 *grid, - 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, struct ffcolor *grid, } static void -farbfeld_yaxis(struct ffplot *plot, struct ffcolor *label, struct ffcolor *grid, +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, struct ffcolor *grid, 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, struct ffcolor *grid, } 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 ffcolor *color, 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 ffcolor *color, } static void -farbfeld_values(struct ffplot *plot, struct csv *vl, struct ffcolor **cl, size_t ncol, +ffplot_values(struct ffplot *plot, struct csv *vl, struct ffcolor **cl, size_t ncol, 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, struct ffcolor **cl, size_t ncol) +ffplot_legend(struct ffplot *plot, struct ffcolor *fg, struct csv *vl, struct ffcolor **cl, size_t ncol) { size_t x, y; @@ -209,13 +206,14 @@ plot(struct csv *vl, struct ffcolor **cl, size_t ncol, char *name, char *units) 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, char *name, char *units) 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); DIR 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 . . DIR 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 ncol) 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; DIR 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) / 2)); + 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; } DIR 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]*;$/, ";"); - print -} -' - -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 DIR 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 time + * 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; +} DIR 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 *, double *); +time_t scale_csvep(time_t, time_t, int); +double scale_vstep(double, double, int); + +#endif DIR 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); -} DIR 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 DIR 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 / 2), - 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, int zero) -{ - 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, char c) -{ - 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; -} DIR 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 * row) */ -}; - -/**/ -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, int); -char * drawille_text (struct drawille *, int, int, struct font *, char *); - -#endif DIR 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(*plot->buf)); -} - -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 c, - 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] == 3) - 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); -} DIR 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 ffcolor *, int, int); -void ffplot_rectangle (struct ffplot *, struct ffcolor *, int, int, int, int); -void ffplot_line (struct ffplot *, struct ffcolor *, int, int, int, int); -int ffplot_char (struct ffplot *, struct ffcolor *, struct font *, char, int, int); -size_t ffplot_text_left (struct ffplot *, struct ffcolor *, struct font *, char *, int, int); -size_t ffplot_text_center (struct ffplot *, struct ffcolor *, struct font *, char *, int, int); -size_t ffplot_text_right (struct ffplot *, struct ffcolor *, struct font *, char *, int, int); -void ffplot_print (FILE *, struct ffplot *); - -#endif DIR 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 DIR 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); -} DIR 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 const *, va_list); -void die (int, char const *, ...); -void error (char const *, ...); -void warn (char const *, ...); -void info (char const *, ...); -void debug (char const *, ...); - -#endif DIR 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 time - * 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; -} DIR 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 *, double *, double *); -time_t scale_tstep (time_t, time_t, int); -double scale_vstep (double, double, int); - -#endif DIR 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; -} DIR 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 *, size_t); -void put3utf (long); -char * strsep (char **, const char *); -void strchomp (char *); -int humanize (char *, double); - -#endif DIR 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; +} DIR 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