ploot

plot images or real-time plain text from a .csv file
Log | Files | Refs | README

commit 817580dbf249b75e747cc81f56f930d80600c59e
parent 0f63680382fb6f810f4ac27df9793db7cba292cc
Author: Josuah Demangeon <mail@josuah.net>
Date:   Sun,  6 May 2018 23:11:07 +0200

initial WIP implementation of plain text version

Diffstat:
M.gitignore | 1+
MMakefile | 29+++++++++++++++++++----------
Mploot.c | 47++++-------------------------------------------
Aplootxt.c | 188+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mutil.c | 50++++++++++++++++++++++++++++++++++++++++++++++++++
Autil.h | 6++++++
6 files changed, 268 insertions(+), 53 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -1,3 +1,4 @@ *.o *.core ploot +plootxt diff --git a/Makefile b/Makefile @@ -1,22 +1,31 @@ CFLAGS = -Wall -Wextra -Werror -std=c89 -pedantic -D_POSIX_C_SOURCE=200809L LDFLAGS = -static -SRC = ploot.c ffplot.c ffdraw.c font_14x7.c -OBJ = $(SRC:.c=.o) +PLOOT_SRC = ploot.c ffplot.c ffdraw.c font_14x7.c util.c +PLOOT_OBJ = $(PLOOT_SRC:.c=.o) + +PLOOTXT_SRC = plootxt.c util.c +PLOOTXT_OBJ = $(PLOOTXT_SRC:.c=.o) + LIB = -lm -all:x ploot +all:V ploot plootxt -ploot: $(OBJ) - ${CC} $(LDFLAGS) -o $@ $(OBJ) $(LIB) +ploot: $(PLOOT_OBJ) + ${CC} $(LDFLAGS) -o $@ $(PLOOT_OBJ) $(LIB) -install:x ploot +plootxt: $(PLOOTXT_OBJ) + ${CC} $(LDFLAGS) -o $@ $(PLOOTXT_OBJ) $(LIB) + +install:V ploot plootxt mkdir -p ${PREFIX}/bin - cp ploot ${PREFIX}/bin/ploot + cp ploot plootxt ${PREFIX}/bin -clean:x +clean:V rm -f *.o ploot -x: +V: # :V acts like .PHONY: + +$(PLOOT_SRC) $(PLOOTXT_SRC): \ +arg.h ploot.h util.h font.h font_14x7.h -$(SRC): arg.h ploot.h font.h font_14x7.h diff --git a/ploot.c b/ploot.c @@ -8,6 +8,7 @@ #include "arg.h" #include "ploot.h" +#include "util.h" #include "config.h" /* after ploot.h for type definitions */ #define LEN(x) (sizeof(x) / sizeof(*x)) @@ -32,26 +33,10 @@ color(Color *col, char *name) } static void -estriplf(char *line) -{ - char *lf; - - if ((lf = strchr(line, '\n')) == NULL || lf[1] != '\0') - fputs("invalid input\n", stderr), exit(1); - *lf = '\0'; -} - -static void read_labels(Vlist *v, char **argv, char *buf) { - if (fgets(buf, LINE_MAX, stdin) == NULL) { - if (ferror(stdin)) - perror("fread from stdin"); - else - fputs("missing label line\n", stderr); - exit(1); - } - estriplf(buf); + if (esfgets(buf, LINE_MAX, stdin) == NULL) + fputs("missing label line\n", stderr), exit(1); if (strcmp(strsep(&buf, ","), "epoch") != 0) fputs("first label must be \"epoch\"\n", stderr), exit(1); @@ -66,28 +51,6 @@ read_labels(Vlist *v, char **argv, char *buf) fputs("more columns than arguments\n", stderr), exit(1); } -static double -eatof(char *str) -{ - char *s; - - for (s = str; *s != '\0'; s++) - if (!isdigit(*s) && *s != '-' && *s != '.') - fputs("invalid float format\n", stderr), exit(0); - return atof(str); -} - -static long -eatol(char *str) -{ - char *s; - - for (s = str; *s != '\0'; s++) - if (!isdigit(*s) && *s != '-') - fputs("invalid number format\n", stderr), exit(0); - return atol(str); -} - static int add_val(Vlist *v, int bufsize, int nval, double field, time_t epoch) { @@ -145,10 +108,8 @@ read_values(Vlist *v, int ncol) char line[LINE_MAX]; bufsize = 0; - for (nval = 0; fgets(line, sizeof(line), stdin); nval++) { - estriplf(line); + for (nval = 0; esfgets(line, sizeof(line), stdin) != NULL; nval++) bufsize = add_row(v, bufsize, ncol, nval, line); - } } static void diff --git a/plootxt.c b/plootxt.c @@ -0,0 +1,188 @@ +#include <time.h> +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <limits.h> +#include <string.h> +#include <ctype.h> + +#include "arg.h" +#include "util.h" + +#define LEN(x) (sizeof(x) / sizeof(*x)) + +#define WIDTH_MAX 1024 + +int screenwidth = 80; + +char *argv0; + +static void +usage(void) +{ + fprintf(stderr, "usage: %s <csv\n", argv0); + exit(1); +} + +void +fmt_labels(char out[LINE_MAX], int ncol, char *labels[LINE_MAX / 2]) +{ + int i, w; + + w = screenwidth / ncol; + for (i = 0; i < ncol; labels++, i++) + out += snprintf(out, w - 1, " %.*s", w - 1, *labels); +} + +/* + * Label must be able to store all pointers to token buf has to + * offer: sizeof(*buf / 2). + */ +static int +read_labels(char out[LINE_MAX]) +{ + int ncol; + char *l, line[LINE_MAX], *labels[LINE_MAX / 2], *tok; + + l = line; + if (esfgets(line, LINE_MAX, stdin) == NULL) + fputs("missing label line\n", stderr), exit(1); + + if (strcmp(strsep(&l, ","), "epoch") != 0) + fputs("first label must be \"epoch\"\n", stderr), exit(1); + + for (ncol = 1; (tok = strsep(&l, ",")) != NULL; ncol++) + *labels = tok; + *labels = NULL; + + if (ncol < 2) + fputs("no label found\n", stderr), exit(1); + + fmt_labels(out, ncol, labels); + + return ncol; +} + +void +plot_val(char *out, double val, int nrow, int width) +{ + (void)val; + (void)out; + (void)nrow; + (void)width; +} + +/* + * Change the braille characters on a whole row, this for all the + * values line. + */ +time_t +plot_row(char *out, char *line, int nrow, int ncol, int width) +{ + time_t epoch; + double val; + int n; + char *tok; + + if ((tok = strsep(&line, ",")) == NULL) + fputs("*** missing epoch value\n", stderr), exit(1); + epoch = eatol(tok); + + for (n = 1; (tok = strsep(&line, ",")) != NULL; n++) { + if (n >= ncol) + fputs("too many values\n", stderr), exit(1); + val = eatof(tok); + plot_val(out + n * width, nrow, val, width); + } + if (n < ncol) + fputs("not enough values\n", stderr), exit(1); + + return epoch; +} + +/* + * Read enough input in order to print one line and plot it into 'out'. + */ +time_t +plot_line(char *out, int ncol, int width) +{ + time_t epoch; + int nrow; + char line[LINE_MAX]; + + for (nrow = 0; nrow < 4; nrow++) { + if ((esfgets(line, LINE_MAX, stdin)) == NULL) + exit(0); + epoch = plot_row(out, line, nrow, ncol, width); + } + + return epoch; +} + +void +put_time(time_t epoch, time_t last, int nline) +{ + char *out, buf[sizeof("XXxXXxXX |")]; + + switch (nline % 3) { + case 0: + strftime(buf, sizeof(buf), "%H:%M:%S _|", localtime(&epoch)); + out = buf; + break; + case 1: + strftime(buf, sizeof(buf), "%y/%m/%d |", localtime(&last)); + out = buf; + break; + case 2: + out = " |"; + break; + } + + fputs(out, stdout); +} + +void +plot(char labels[LINE_MAX], int ncol) +{ + time_t epoch, last_epoch; + int n, width; + char out[WIDTH_MAX * 3 + 1]; + + width = screenwidth / ncol; + last_epoch = epoch = 0; + + for (n = 0;; n++) { + if (n >= 20) { + puts(labels); + n = 0; + } + + epoch = plot_line(out, ncol, width); + put_time(epoch, last_epoch, n); + last_epoch = epoch; + puts(out); + + fflush(stdout); + } +} + +void +parse_args(int argc, char **argv) +{ + argv0 = *argv; + if (argc != 1) + usage(); +} + +int +main(int argc, char **argv) +{ + char labels[LINE_MAX]; + int ncol; + + parse_args(argc, argv); + ncol = read_labels(labels); + plot(labels, ncol); + + return 0; +} diff --git a/util.c b/util.c @@ -1,4 +1,9 @@ #include <string.h> +#include <errno.h> +#include <stdio.h> +#include <limits.h> +#include <stdlib.h> +#include <ctype.h> #include "ploot.h" @@ -19,3 +24,48 @@ strsep(char **strp, const char *sep) return prev; } + +void +estriplf(char *line) +{ + char *lf; + + if ((lf = strchr(line, '\n')) == NULL || lf[1] != '\0') + fputs("invalid input\n", stderr), exit(1); + *lf = '\0'; +} + +double +eatof(char *str) +{ + char *s; + + for (s = str; *s != '\0'; s++) + if (!isdigit(*s) && *s != '-' && *s != '.') + fputs("invalid float format\n", stderr), exit(0); + return atof(str); +} + +long +eatol(char *str) +{ + char *s; + + for (s = str; *s != '\0'; s++) + if (!isdigit(*s) && *s != '-') + fputs("invalid number format\n", stderr), exit(0); + return atol(str); +} + +char * +esfgets(char *buf, size_t n, FILE *file) +{ + if (fgets(buf, n, file) == NULL) { + if (ferror(stdin)) + perror("fread from stdin"), exit(1); + else + return NULL; + } + estriplf(buf); + return buf; +} diff --git a/util.h b/util.h @@ -0,0 +1,6 @@ +/* util.c */ +char *strsep (char **, const char *); +void estriplf (char *); +double eatof (char *); +long eatol (char *); +char *esfgets (char *, size_t, FILE *);