auto-scale - 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 c3911021718dc5e5b2dd6469495e7f3bc4befdd2
 (DIR) parent d97badd46c19a6903a589c41b8b87d044f48f8dc
 (HTM) Author: Josuah Demangeon <mail@josuah.net>
       Date:   Wed,  2 May 2018 06:37:05 +0200
       
       auto-scale
       
       Diffstat:
         M Makefile                            |       2 +-
         M ffplot.c                            |      66 +++++++++++++++++++++++++++++--
         D main.c                              |     191 -------------------------------
         M ploot.c                             |     364 +++++++++++--------------------
         M ploot.h                             |       3 +--
       
       5 files changed, 186 insertions(+), 440 deletions(-)
       ---
 (DIR) diff --git a/Makefile b/Makefile
       @@ -1,7 +1,7 @@
        CFLAGS        = -Wall -Wextra -Werror -std=c89 -pedantic -D_POSIX_C_SOURCE=200809L
        LDFLAGS = -static
        
       -SRC = main.c ffplot.c ffdraw.c font_14x7.c
       +SRC = ploot.c ffplot.c ffdraw.c font_14x7.c
        OBJ = $(SRC:.c=.o)
        LIB = -lm
        
 (DIR) diff --git a/ffplot.c b/ffplot.c
       @@ -18,6 +18,7 @@
        #include "font_14x7.h"
        
        #define ABS(x) ((x) < 0 ? -(x) : (x))
       +#define LEN(x) (sizeof(x) / sizeof(*x))
        
        #define MARGIN 4
        
       @@ -42,7 +43,7 @@
        #define PLOT_X (YLABEL_H)
        #define PLOT_Y (XLABEL_W)
        #define PLOT_W 700
       -#define PLOT_H 200
       +#define PLOT_H 160
        
        #define LEGEND_X (YLABEL_H)
        #define LEGEND_Y (IMAGE_W - LEGEND_W)
       @@ -198,9 +199,60 @@ legend(Canvas *can, Color *label_fg, Vlist *v, int n)
        }
        
        void
       -ffdraw(char *name, char *units, Vlist *v, int n,
       -        double vmin, double vmax, double vstep,
       -        time_t tmin, time_t tmax, time_t tstep)
       +find_scales(Vlist *v, int n,
       +        double *vmin, double *vmax, double *vstep,
       +        time_t *tmin, time_t *tmax, time_t *tstep)
       +{
       +        double dv, *vs, vscale[] = { 5, 2, 1 };
       +        time_t dt, *ts, tscale[] = {
       +                3600*24*30, 3600*24*5, 3600*24*2, 3600*24, 3600*18, 3600*10, 
       +                3600*5, 3600*2, 3600, 60*30, 60*20, 60*10, 60*5, 60*2, 60, 30, 
       +                20, 10, 5, 2, 1
       +        };
       +        int i;
       +
       +        *vmin = *vmax = *tmin = *tmax = 0;
       +
       +        for (; n-- > 0; v++) {
       +                for (i = 0; i < v->n; i++) {
       +                        if (v->v[i] < *vmin)
       +                                *vmin = v->v[i];
       +                        if (v->v[i] > *vmax)
       +                                *vmax = v->v[i];
       +                        if (v->t[i] < *tmin)
       +                                *tmin = v->t[i];
       +                        if (v->t[i] > *tmax)
       +                                *tmax = v->t[i];
       +                }
       +        }
       +
       +        dv = *vmax - *vmin;
       +        dt = *tmax - *tmin;
       +
       +        for (ts = tscale; ts < tscale + LEN(tscale); ts++) {
       +                if (dt > *ts * 5) {
       +                        *tstep = *ts;
       +                        break;
       +                }
       +        }
       +
       +        for (i = 1; i != 0; i *= 10) {
       +                for (vs = vscale; vs < vscale + LEN(vscale); vs++) {
       +                        if (dv > *vs * i * 1) {
       +                                *vstep = *vs * i * 10;
       +                                i = 0;
       +                                break;
       +                        }
       +                }
       +        }
       +}
       +
       +/*
       + * Plot the 'n' values list of the 'v' array with title 'name' and
       + * 'units' label.
       + */
       +void
       +ffplot(Vlist *v, int n, char *name, char *units)
        {
                Canvas can = { IMAGE_W, IMAGE_H, buffer, 0, 0 };
                Color plot_bg        = { 0x2222, 0x2222, 0x2222, 0xffff };
       @@ -208,6 +260,12 @@ ffdraw(char *name, char *units, Vlist *v, int n,
                Color grid_fg        = { 0x3737, 0x3737, 0x3737, 0xffff };
                Color label_fg        = { 0x8888, 0x8888, 0x8888, 0xffff };
                Color title_fg        = { 0xdddd, 0xdddd, 0xdddd, 0xffff };
       +        double vmin, vmax, vstep = 30;
       +        time_t tmin, tmax, tstep = 30;
       +
       +        find_scales(v, n, &vmin, &vmax, &vstep, &tmin, &tmax, &tstep);
       +
       +        fprintf(stderr, "%f %f  %lld %lld\n", vmin, vmax, tmin, tmax);
        
                can.x = 0;
                can.y = 0;
 (DIR) diff --git a/main.c b/main.c
       @@ -1,191 +0,0 @@
       -#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 "ploot.h"
       -#include "config.h"        /* after ploot.h for type definitions */
       -
       -#define LEN(x) (sizeof(x) / sizeof(*x))
       -
       -char *argv0;
       -char *tflag = "";
       -char *uflag = "";
       -
       -static int
       -color(Color *col, char *name)
       -{
       -        ColorList *c;
       -
       -        for (c = colorlist; c->name != NULL; c++) {
       -                if (strcmp(name, c->name) == 0) {
       -                        memcpy(col, &c->col, sizeof(*col));
       -                        return 0;
       -                }
       -        }
       -
       -        return -1;
       -}
       -
       -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 (strcmp(strsep(&buf, ","), "epoch") != 0)
       -                fputs("first label must be \"epoch\"\n", stderr), exit(1);
       -
       -        for (; *argv != NULL; v++, argv++)
       -                if ((v->label = strsep(&buf, ",")) == NULL)
       -                        fputs("more arguments than columns\n", stderr), exit(1);
       -                else if (color(&v->col, *argv) == -1)
       -                        fprintf(stderr, "unknown color: %s\n", *argv), exit(1);
       -
       -        if (strsep(&buf, ",") != NULL)
       -                fputs("more columns than arguments\n", stderr), exit(1);
       -}
       -
       -double
       -eatof(char *str)
       -{
       -        char *s;
       -
       -        for (s = str; *s != '\0'; s++)
       -                if (!isdigit(*s) && *s != '.')
       -                        fputs("invalid floatrformat", stderr), exit(0);
       -        return atof(str);
       -}
       -
       -long
       -eatol(char *str)
       -{
       -        char *s;
       -
       -        for (s = str; *s != '\0'; s++)
       -                if (!isdigit(*s))
       -                        fputs("invalid number format", stderr), exit(0);
       -        return atol(str);
       -}
       -
       -void
       -add_val(Vlist *v, int *bufsiz, int nval, double field, time_t epoch)
       -{
       -        if (nval >= *bufsiz) {
       -                *bufsiz = *bufsiz * 2 + 1;
       -                if ((v->v = realloc(v->v, *bufsiz * sizeof(*v->v))) == NULL)
       -                        perror("reallocating values buffer"), exit(1);
       -                if ((v->t = realloc(v->t, *bufsiz * sizeof(*v->t))) == NULL)
       -                        perror("reallocating values buffer"), exit(1);
       -        }
       -        v->v[nval] = field;
       -        v->t[nval] = epoch;
       -        v->n = nval + 1;
       -}
       -
       -/*
       - * Add to each column the value on the current row.
       - */
       -void
       -add_row(Vlist *v, int *bufsiz, int ncol, int nval, char *line)
       -{
       -        time_t epoch;
       -        int n;
       -        char *field;
       -
       -        if ((field = strsep(&line, ",")) == NULL)
       -                fprintf(stderr, "%d: missing epoch\n", nval), exit(0);
       -
       -        epoch = eatol(field);
       -        for (n = 0; (field = strsep(&line, ",")) != NULL; n++, v++) {
       -                if (n > ncol)
       -                        fprintf(stderr, "%d: too many fields\n", nval), exit(0);
       -                add_val(v, bufsiz, nval, eatof(field), epoch);
       -        }
       -        if (n < ncol)
       -                fprintf(stderr, "%d: too few fields\n", nval), exit(0);
       -}
       -
       -/*
       - *       < ncol >
       - * epoch,a1,b1,c1  ^
       - * epoch,a2,b2,c2 nval
       - * epoch,a3,b3,c3  v
       - */
       -void
       -read_values(Vlist *v, int ncol)
       -{
       -        int nval, bufsiz;
       -        char line[LINE_MAX];
       -
       -        bufsiz = 0;
       -        for (nval = 0; fgets(line, sizeof(line), stdin); nval++) {
       -                estriplf(line);
       -                add_row(v, &bufsiz, ncol, nval, line);
       -        }
       -}
       -
       -static void
       -usage(void)
       -{
       -        ColorList *c;
       -
       -        fprintf(stderr, "usage: %s [-t title] [-u unit] color...\n"
       -                "available colors as defined by \"config.h\":\n", argv0);
       -        for (c = colorlist; c->name != NULL; c++)
       -                fprintf(stderr, "- %s\n", c->name);
       -        exit(1);
       -}
       -
       -int
       -main(int argc, char **argv)
       -{
       -        Vlist *v;
       -        double vmin, vmax, vstep;
       -        time_t tmin, tmax, tstep;
       -        char labels[LINE_MAX];
       -
       -        ARGBEGIN {
       -        case 't':
       -                tflag = EARGF(usage());
       -                break;
       -        case 'u':
       -                uflag = EARGF(usage());
       -                break;
       -        } ARGEND;
       -
       -        if ((v = calloc(argc, sizeof(*v))) == NULL)
       -                perror("calloc value list"), exit(1);
       -
       -        vmin = -30; vmax =  700; vstep = 120;
       -        tmin =   0; tmax = 2000; tstep = 300;
       -
       -        read_labels(v, argv, labels);
       -        read_values(v, argc);
       -
       -        ffdraw(tflag, uflag, v, argc,
       -                vmin, vmax, vstep,
       -                tmin, tmax, tstep);
       -
       -        return 0;
       -}
 (DIR) diff --git a/ploot.c b/ploot.c
       @@ -1,304 +1,184 @@
       -#include <sys/time.h>
       -
       -#include <stdio.h>
       +#include <time.h>
        #include <stdlib.h>
       +#include <stdio.h>
       +#include <fcntl.h>
       +#include <limits.h>
        #include <string.h>
       -#include <unistd.h>
       -#include <time.h>
       +#include <ctype.h>
        
        #include "arg.h"
       -#include "config.h"
       +#include "ploot.h"
       +#include "config.h"        /* after ploot.h for type definitions */
        
       -#define ABS(x)                ((x) < 0 ? -(x) : (x))
       -#define MIN(x, y)        ((x) < (y) ? (x) : (y))
       -#define MAX(x, y)        ((x) > (y) ? (x) : (y))
       -#define LEN(buf)        (sizeof(buf) / sizeof(*(buf)))
       +#define LEN(x) (sizeof(x) / sizeof(*x))
        
        char *argv0;
       +char *tflag = "";
       +char *uflag = "";
        
       -/*
       -** Add 'val' at the current position 'pos' of the 'ring' buffer and set pos to
       -** the next postion.
       -*/
       -#define RING_ADD(rbuf, len, pos, val)                                        \
       -do {                                                                        \
       -        rbuf[pos] = val;                                                \
       -        pos = (pos + 1 < len) ? (pos + 1) : (0);                        \
       -} while (0)
       -
       -/*
       -** Copy the ring buffer 'rbuf' content with current position 'pos' into the
       -** buffer 'buf'.  Both buffer of length 'len'.
       -*/
       -#define RING_COPY(buf, rbuf, len, pos)                                        \
       -do {                                                                        \
       -        memcpy(buf, rbuf + pos, (len - pos) * sizeof(*rbuf));                \
       -        memcpy(buf + (len - pos), rbuf, pos * sizeof(*rbuf));                \
       -} while (0)
       -
       -#define MAX_VAL        80
       -#define MARGIN        7
       -
       -int        hflag = 20;
       -char        *tflag = NULL;
       -time_t        oflag = 0;
       -
       -/*
       -** Set 'str' to a human-readable form of 'num' with always a width of 7 (+ 1
       -** the '\0' terminator).  Buffer overflow is ensured not to happen due to the
       -** max size of a double.
       -*/
       -void
       -humanize(char *str, double val)
       +static int
       +color(Color *col, char *name)
        {
       -        int        exp, precision;
       -        char        *label = "\0kMGTE";
       +        ColorList *c;
        
       -        for (exp = 0; ABS(val) > 1000; exp++)
       -                val /= 1000;
       +        for (c = colorlist; c->name != NULL; c++) {
       +                if (strcmp(name, c->name) == 0) {
       +                        memcpy(col, &c->col, sizeof(*col));
       +                        return 0;
       +                }
       +        }
        
       -        precision = (ABS(val) < 10) ? (3) : (ABS(val) < 100) ? (2) : (1);
       -        if (exp == 0)
       -                precision++;
       -        snprintf(str, 8, "%+.*f%c", precision, val, label[exp]);
       -        str[7] = '\0';
       -        if (val >= 0)
       -                str[0] = ' ';
       +        return -1;
        }
        
       -/*
       -** Returns the maximal double of values between 'beg' and 'end'.
       -*/
       -double
       -maxdv(double *beg, double *end)
       +static void
       +estriplf(char *line)
        {
       -        double        *val, max;
       +        char *lf;
        
       -        max = *beg;
       -        for (val = beg; val < end; val++) {
       -                if (*val > max)
       -                        max = *val;
       -        }
       -        return max;
       +        if ((lf = strchr(line, '\n')) == NULL || lf[1] != '\0')
       +                fputs("invalid input\n", stderr), exit(1);
       +        *lf = '\0';
        }
        
       -/*
       -** If not null, print the title 'str' centered on width.
       -*/
       -void
       -title(char *str, int width)
       +static void
       +read_labels(Vlist *v, char **argv, char *buf)
        {
       -        if (str == NULL)
       -                return;
       -        printf("%*s\n", (int)(width + strlen(str)) / 2 + MARGIN + 3, str);
       -}
       -
       -/*
       -** Print vertical axis with humanized number from time to time, with occurences
       -** determined after the position on the vertical axis from the bottom 'pos'.
       -*/
       -void
       -vaxis(double val, int pos)
       -{
       -        char        label[10];
       -
       -        if (pos % 4 == 0) {
       -                humanize(label, val);
       -                printf("%*s -", MARGIN, label);
       -        } else {
       -                printf("%*c  ", MARGIN, ' ');
       +        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);
        
       -/*
       -** Print horizontal axis for up to 'col' values along with dates if reading time
       -** series.
       -*/
       -void
       -haxis(double *beg, double *end, time_t time)
       -{
       -        double                *tp;
       -        char                buf[9], dbeg[11], dend[11];
       +        if (strcmp(strsep(&buf, ","), "epoch") != 0)
       +                fputs("first label must be \"epoch\"\n", stderr), exit(1);
        
       -        printf("%*d -+", MARGIN, 0);
       -        for (tp = beg; tp < end; tp++)
       -                putchar((*tp < 0) ? ('x') : ('-'));
       -        putchar('\n');
       -        if (oflag > 0) {
       -                printf("%*c", MARGIN - 1, ' ');
       -                strftime(dbeg, sizeof(dbeg), "%Y/%m/%d", localtime(&time));
       -                for (tp = beg; tp < end; tp += 7) {
       -                        strftime(buf, sizeof(buf), "  %H:%M", localtime(&time));
       -                        fputs(buf, stdout);
       -                        time += oflag * 7;
       -                }
       -                strftime(dend, sizeof(dend), "%Y/%m/%d", localtime(&time));
       -                printf("\n     %-*s %s\n", (int)(beg - end) + 4, dbeg, dend);
       -        }
       +        for (; *argv != NULL; v++, argv++)
       +                if ((v->label = strsep(&buf, ",")) == NULL)
       +                        fputs("more arguments than columns\n", stderr), exit(1);
       +                else if (color(&v->col, *argv) == -1)
       +                        fprintf(stderr, "unknown color: %s\n", *argv), exit(1);
       +
       +        if (strsep(&buf, ",") != NULL)
       +                fputs("more columns than arguments\n", stderr), exit(1);
        }
        
       -/*
       -** Print two rows of a plot into a single line using ' ', '.' and ':'.
       -*/
       -void
       -line(double *beg, double *end, double top, double bot)
       +static double
       +eatof(char *str)
        {
       -        double        *val;
       +        char *s;
        
       -        putchar('|');
       -        for (val = beg; val != end; val++)
       -                putchar((*val < bot) ? ' ' : (*val < top) ? '.' : ':');
       -        putchar('\n');
       +        for (s = str; *s != '\0'; s++)
       +                if (!isdigit(*s) && *s != '-' && *s != '.')
       +                        fputs("invalid floatrformat", stderr), exit(0);
       +        return atof(str);
        }
        
       -/*
       -** Plot values between 'beg' and 'end' in a plot of height 'height'.
       -** If 'str' is not NULL, it is set as a title above the graph.
       -*/
       -void
       -plot(double *beg, double *end, int height, char *str, time_t start)
       +static long
       +eatol(char *str)
        {
       -        double        top, bot, max;
       -        int        h;
       +        char *s;
        
       -        putchar('\n');
       -
       -        max = maxdv(beg, end);
       -        for (h = height + height % 2; h > 0; h -= 2) {
       -                top = h * max / height;
       -                bot = (h - 1) * max / height;
       -
       -                vaxis(top, h);
       -                line(beg, end, top, bot);
       -        }
       -
       -        haxis(beg, end, start);
       -
       -        if (str != NULL)
       -                title(str, end - beg);
       -
       -        putchar('\n');
       +        for (s = str; *s != '\0'; s++)
       +                if (!isdigit(*s) && *s != '-')
       +                        fputs("invalid number format", stderr), exit(0);
       +        return atol(str);
        }
        
       -/*
       -** Read a simple format with one double per line and save the last 'MAX_WIDTH'
       -** values into 'buf' which must be at least MAX_VAL wide and return a pointer
       -** to the last element or NULL if the input contains error.
       -*/
       -double *
       -read_simple(double buf[MAX_VAL])
       +static void
       +add_val(Vlist *v, int *bufsiz, int nval, double field, time_t epoch)
        {
       -        double        rbuf[MAX_VAL], val;
       -        size_t        p, pos, len;
       -
       -        len = LEN(rbuf);
       -        for (p = pos = 0; scanf("%lf\n", &val) > 0; p++)
       -                RING_ADD(rbuf, len, pos, val);
       -        len = MIN(len, p);
       -
       -        RING_COPY(buf, rbuf, len, pos);
       -
       -        return buf + len;
       +        if (nval >= *bufsiz) {
       +                *bufsiz = *bufsiz * 2 + 1;
       +                if ((v->v = realloc(v->v, *bufsiz * sizeof(*v->v))) == NULL)
       +                        perror("reallocating values buffer"), exit(1);
       +                if ((v->t = realloc(v->t, *bufsiz * sizeof(*v->t))) == NULL)
       +                        perror("reallocating values buffer"), exit(1);
       +        }
       +        v->v[nval] = field;
       +        v->t[nval] = epoch;
       +        v->n = nval + 1;
        }
        
        /*
       -** Read a format with blank separated time_t-double pairs, one per line and save
       -** the last 'MAX_WIDTH' values into 'tbuf' and 'vbuf' which must both be at
       -** least MAX_VAL wide and return a pointer to the last element of 'vbuf' or
       -** NULL if the input contains error.
       -*/
       -time_t *
       -read_time_series(double *vbuf, time_t *tbuf)
       + * Add to each column the value on the current row.
       + */
       +static void
       +add_row(Vlist *v, int *bufsiz, int ncol, int nval, char *line)
        {
       -        size_t        p, pos, nul, len;
       -        double        vrbuf[MAX_VAL], vval, dval;
       -        time_t        trbuf[MAX_VAL], tval;
       -
       -        len = LEN(vrbuf);
       -        for (p = pos = 0; scanf("%lf %lf\n", &dval, &vval) > 0; p++) {
       -                tval = (time_t)dval;
       -                RING_ADD(trbuf, len, pos, tval);
       -                RING_ADD(vrbuf, len, nul, vval);
       +        time_t epoch;
       +        int n;
       +        char *field;
       +
       +        if ((field = strsep(&line, ",")) == NULL)
       +                fprintf(stderr, "%d: missing epoch\n", nval), exit(0);
       +
       +        epoch = eatol(field);
       +        for (n = 0; (field = strsep(&line, ",")) != NULL; n++, v++) {
       +                if (n > ncol)
       +                        fprintf(stderr, "%d: too many fields\n", nval), exit(0);
       +                add_val(v, bufsiz, nval, eatof(field), epoch);
                }
       -        len = MIN(len, p);
       -
       -        RING_COPY(tbuf, trbuf, len, pos);
       -        RING_COPY(vbuf, vrbuf, len, pos);
       -
       -        return tbuf + len;
       +        if (n < ncol)
       +                fprintf(stderr, "%d: too few fields\n", nval), exit(0);
        }
        
        /*
       -** Walk from 'tbeg' and 'tend' and add offset in 'tbuf' every time there is no
       -** value in 'step' amount of time, by setting a value to -1.
       -*/
       -double *
       -skip_gaps(time_t *tbeg, time_t *tend, double *vbuf, time_t step)
       + *       < ncol >
       + * epoch,a1,b1,c1  ^
       + * epoch,a2,b2,c2 nval
       + * epoch,a3,b3,c3  v
       + */
       +static void
       +read_values(Vlist *v, int ncol)
        {
       -        size_t        p, pos, len;
       -        time_t        *tp, toff;
       -        double        *vp, vrbuf[MAX_VAL];
       -
       -        /* Compute the average alignment of the timestamps values according to
       -        ** the step size. */
       -        toff = 0;
       -        for (tp = tbeg; tp < tend; tp++)
       -                toff += *tp % step;
       -        toff = *tbeg + toff / (tend - tbeg) + step / 2;
       +        int nval, bufsiz;
       +        char line[LINE_MAX];
        
       -        /* Fill 'vbuf' with gap added at each time gap using vrbuf as
       -        ** intermediate ring buffer. */
       -        len = LEN(vrbuf);
       -        for (p = pos = 0, tp = tbeg, vp = vbuf; tp < tend; p++, vp++, tp++) {
       -                for (; toff < *tp; toff += step)
       -                        RING_ADD(vrbuf, len, pos, -1);
       -                RING_ADD(vrbuf, len, pos, *vp);
       -                toff += step;
       +        bufsiz = 0;
       +        for (nval = 0; fgets(line, sizeof(line), stdin); nval++) {
       +                estriplf(line);
       +                add_row(v, &bufsiz, ncol, nval, line);
                }
       -        len = MAX(MIN(p, len), pos);
       -
       -        RING_COPY(vbuf, vrbuf, len, pos);
       -
       -        return vbuf + len;
        }
        
       -void
       +static void
        usage(void)
        {
       -        printf("usage: ploot [-h <height>] [-o <offset>] [-t <title>]\n");
       +        ColorList *c;
       +
       +        fprintf(stderr, "usage: %s [-t title] [-u unit] color...\n"
       +                "available colors as defined by \"config.h\":\n", argv0);
       +        for (c = colorlist; c->name != NULL; c++)
       +                fprintf(stderr, "- %s\n", c->name);
                exit(1);
        }
        
        int
        main(int argc, char **argv)
        {
       -        time_t        tbuf[MAX_VAL], *tend, start;
       -        double        vbuf[MAX_VAL], *vend;
       +        Vlist *v;
       +        char labels[LINE_MAX];
        
       -        ARGBEGIN(argc, argv) {
       -        case 'h':
       -                if ((hflag = atoi(EARGF(usage()))) <= 0)
       -                        usage();
       -                break;
       +        ARGBEGIN {
                case 't':
                        tflag = EARGF(usage());
                        break;
       -        case 'o':
       -                oflag = atol(EARGF(usage()));
       +        case 'u':
       +                uflag = EARGF(usage());
                        break;
       -        default:
       -                usage();
       -        } ARGEND
       +        } ARGEND;
        
       -        if (oflag == 0) {
       -                vend = read_simple(vbuf);
       -                start = 0;
       -        } else {
       -                tend = read_time_series(vbuf, tbuf);
       -                vend = skip_gaps(tbuf, tend, vbuf, oflag);
       -                start = *tbuf;
       -        }
       +        if ((v = calloc(argc, sizeof(*v))) == NULL)
       +                perror("calloc value list"), exit(1);
       +
       +        read_labels(v, argv, labels);
       +        read_values(v, argc);
       +
       +        ffplot(v, argc, tflag, uflag);
        
       -        plot(vbuf, vend, hflag, tflag, start);
                return 0;
        }
 (DIR) diff --git a/ploot.h b/ploot.h
       @@ -45,8 +45,7 @@ void                 ffdraw_fill        (Canvas *, Color *);
        void                 ffdraw_print        (Canvas *);
        
        /* ffplot.c */
       -void                 ffdraw                (char *, char *, Vlist *, int, double, double,
       -                                 double, time_t, time_t, time_t);
       +void                 ffplot                (Vlist *, int, char *, char *);
        
        /* util.c */
        char                *strsep                (char **, const char *);