initial WIP implementation of plain text version - ploot - simple plotting tools
 (HTM) git clone git://bitreich.org/ploot git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/ploot
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) Tags
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 817580dbf249b75e747cc81f56f930d80600c59e
 (DIR) parent 0f63680382fb6f810f4ac27df9793db7cba292cc
 (HTM) 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 +
         M Makefile                            |      29 +++++++++++++++++++----------
         M ploot.c                             |      47 +++----------------------------
         A plootxt.c                           |     188 +++++++++++++++++++++++++++++++
         M util.c                              |      50 +++++++++++++++++++++++++++++++
         A util.h                              |       6 ++++++
       
       6 files changed, 268 insertions(+), 53 deletions(-)
       ---
 (DIR) diff --git a/.gitignore b/.gitignore
       @@ -1,3 +1,4 @@
        *.o
        *.core
        ploot
       +plootxt
 (DIR) 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
 (DIR) 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
 (DIR) 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;
       +}
 (DIR) 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;
       +}
 (DIR) 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 *);