csv: fix use of uninitialized memory - 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 f7f88c2ee5573abff4c44c36bf7b2e705081b2ed
 (DIR) parent c3fcef87d156b02a9ad8ca7cd47fee4a826534f4
 (HTM) Author: Josuah Demangeon <me@josuah.net>
       Date:   Sun, 27 Jun 2021 01:04:39 +0200
       
       csv: fix use of uninitialized memory
       
       Diffstat:
         M Makefile                            |       6 +++---
         M csv.c                               |      33 ++++++++++++++-----------------
         M csv.h                               |       1 -
         M ploot-braille.c                     |     157 ++++++++++++++++++++++---------
         M ploot-farbfeld.c                    |      14 +++++++-------
         D scale.c                             |      94 -------------------------------
         D scale.h                             |      14 --------------
       
       7 files changed, 140 insertions(+), 179 deletions(-)
       ---
 (DIR) diff --git a/Makefile b/Makefile
       @@ -7,9 +7,9 @@ LFLAGS = -static -lm
        PREFIX = /usr/local
        MANOREFIX = $(PREFIX)/share/man
        
       -SRC = csv.c drawille.c font.c font13.c font8.c scale.c util.c
       -INC = csv.h drawille.h font.h scale.h util.h
       -BIN = ploot-farbfeld ploot-feed ploot-braille ploot-text
       +SRC = csv.c drawille.c font.c font13.c font8.c util.c
       +INC = csv.h drawille.h font.h util.h
       +BIN = ploot-feed ploot-braille ploot-text # ploot-farbfeld
        OBJ = ${SRC:.c=.o}
        
        all: ${BIN}
 (DIR) diff --git a/csv.c b/csv.c
       @@ -13,11 +13,10 @@
         */
        
        static void
       -csv_addtime(struct csv *vl, time_t epoch)
       +csv_add_time(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;
       @@ -25,11 +24,10 @@ csv_addtime(struct csv *vl, time_t epoch)
        }
        
        static void
       -csv_addval(struct csv *vl, double field)
       +csv_add_val(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;
       @@ -40,8 +38,8 @@ csv_addval(struct csv *vl, double 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)
       +static void
       +csv_add_row(struct csv *vl, size_t ncol, char *line)
        {
                char *field;
                time_t *tbuf;
       @@ -55,7 +53,7 @@ csv_addrow(struct csv *vl, size_t ncol, char *line)
                if (errno)
                        err(100, "parsing number '%s'", field);
        
       -        csv_addtime(vl, l);
       +        csv_add_time(vl, l);
                tbuf = vl[0].t;
                for (; (field = strsep(&line, ",")); ncol--, vl->n++, vl++) {
                        if (ncol == 0)
       @@ -63,7 +61,7 @@ csv_addrow(struct csv *vl, size_t ncol, char *line)
                        d = strtod(field, NULL);
                        if (errno)
                                err(100, "parsing double '%s'", field);
       -                csv_addval(vl, d);
       +                csv_add_val(vl, d);
                        vl->t = tbuf;
                }
                if (ncol > 0)
       @@ -75,10 +73,10 @@ csv_addrow(struct csv *vl, size_t ncol, char *line)
         * label1,label2,label3
         */
        void
       -csv_labels(FILE *fp, struct csv **vl, size_t *ncol)
       +csv_labels(FILE *fp, struct csv **vlp, size_t *ncol)
        {
                char *field, *line, *cp;
       -        struct csv *col;
       +        struct csv *vl, *col;
                size_t sz;
                ssize_t r;
        
       @@ -94,16 +92,16 @@ csv_labels(FILE *fp, struct csv **vl, size_t *ncol)
                if (strcmp(strsep(&cp, ","), "epoch") != 0)
                        err(1, "first label must be 'epoch'");
        
       -        *vl = NULL;
       -        *ncol = 0;
       +        sz = 0, vl = NULL, *ncol = 0;
                while ((field = strsep(&cp, ","))) {
       -                if ((*vl = realloc(*vl, sz += sizeof **vl)) == NULL)
       +                if ((vl = realloc(vl, sz += sizeof *vl)) == NULL)
                                err(1, "realloc: %s", strerror(errno));
       -                col = (*vl) + (*ncol)++;
       -                strlcpy(col->label, field, sizeof(col->label));
       +                memset(vl, 0, sizeof *vl);
       +                col = vl + (*ncol)++;
       +                strlcpy(col->label, field, sizeof col->label);
                }
       -
                free(line);
       +        *vlp = vl;
        }
        
        /*
       @@ -122,11 +120,10 @@ csv_values(FILE *fp, struct csv *vl, size_t ncol)
        
                sz = 0, line = NULL;
                while (getline(&line, &sz, fp) > -1)
       -                csv_addrow(vl, ncol, line);
       +                csv_add_row(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
       @@ -15,7 +15,6 @@ struct csv {
                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);
        
 (DIR) diff --git a/ploot-braille.c b/ploot-braille.c
       @@ -8,13 +8,90 @@
        #include <math.h>
        #include <unistd.h>
        #include "drawille.h"
       -#include "scale.h"
        #include "util.h"
       +#include "csv.h"
        
        #ifndef __OpenBSD__
        #define pledge(...) 0
        #endif
        
       +static int
       +get_min_max(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; /* always show 0 on the scale */
       +        *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)
       +                return -1;
       +        return 0;
       +}
       +
       +static time_t
       +time_mark_step(time_t min, time_t max, int dots)
       +{
       +        time_t dt, scale[] = {
       +                1, 5, 2, 10, 20, 30, 60, 60*2, 60*5, 60*10, 60*20, 60*30, 3600, 
       +                3600*2, 3600*6, 3600*12, 3600*24, 3600*24*2, 
       +                3600*24*7, 3600*24*14, 3600*24*20, 3600*24*21, 3600*24*28, 3600*24*50,
       +                3600*24*100, 3600*24*365, 0
       +        };
       +
       +        dt = max - min;
       +        for (time_t *sc = scale; *sc > 0; sc++)
       +                if (dt < *sc * dots)
       +                        return *sc;
       +        return dt / dots;
       +}
       +
       +/*
       + * Make the value scale aligned with round values by changing the
       + * minimal and maximal values.
       + */
       +static void
       +adjust_scale(double *min, double *max, int rows)
       +{
       +        double dv, step, scale[] = { 1, 2, 2.5, 5, };
       +
       +        dv = *max - *min;
       +
       +        if (dv > 1) {
       +                for (double mant = 1;; mant *= 10) {
       +                        double *sc = scale;
       +                        for (; sc < scale + LEN(scale); sc++) {
       +                                step = mant * *sc;
       +                                if (dv < (rows - 2) * step)
       +                                        goto end;
       +                        }
       +                }
       +        } else {
       +                for (double mant = 1;; mant /= 10) {
       +                        double *sc = scale + LEN(scale) - 1;
       +                        for (; sc >= scale; sc--) {
       +                                step = mant * *sc;
       +                                if (dv > (rows - 2) * step)
       +                                        goto end;
       +                        }
       +                }
       +        }
       +end:
       +        *min = (int)(*min / step) * step;
       +        *max = *min + step * rows;
       +}
       +
        /*
         * Plot the body as an histogram interpolating the gaps and include
         * a vertical and horizontal axis.
       @@ -28,25 +105,31 @@ braille_histogram(struct csv *vl, struct drawille *drw,
                time_t                *t;
                size_t                n;
        
       -        zero = scale_ypos(0, vmin, vmax, drw->row*4);
       +#define SHIFT (4 / 2)
       +#define POSITION(val, min, max, sz) ((sz) * ((val) - (min)) / ((max) - (min)) + SHIFT)
       +
       +        zero = POSITION(0, vmin, vmax, drw->row*4);
                v = vl->v;
                t = vl->t;
                n = vl->n;
                for (; n > 0; n--, t++, v++) {
       -                if (isnan(*v))  /* XXX: better handling? */
       +                if (isnan(*v)) /* XXX: better handling? */
                                continue;
       -                y = scale_ypos(*v, vmin, vmax, drw->row * 4);
       -                x = scale_xpos(*t, tmin, tmax, drw->col * 2);
       -                if (n < vl->n)  /* only plot when xprev, yprev are set */
       +                y = POSITION(*v, vmin, vmax, drw->row * 4);
       +                x = POSITION(*t, tmin, tmax, drw->col * 2);
       +                if (n < vl->n) /* only plot when xprev, yprev are set */
                                drawille_histogram_line(drw, xprev, yprev, x, y, zero);
                        xprev = x;
                        yprev = y;
                }
       +
       +#undef POSITION
       +
                return 0;
        }
        
        static int
       -braille_axis_x(FILE *fp, time_t tmin, time_t tmax, time_t csvep, int col)
       +braille_axis_x(FILE *fp, time_t tmin, time_t tmax, time_t tstep, int col)
        {
                int x, o, prec;
                char tmp[sizeof("MM/DD HH:MM")], *fmt;
       @@ -54,13 +137,13 @@ braille_axis_x(FILE *fp, time_t tmin, time_t tmax, time_t csvep, int col)
                time_t t;
        
                fmt =
       -          (csvep < 3600 * 12) ? "^%H:%M:%S" :
       -          (csvep < 3600 * 24) ? "^%m/%d %H:%M" :
       -          "^%Y/%m/%d";
       +            (tstep < 3600 * 12) ? "^%H:%M:%S" :
       +            (tstep < 3600 * 24) ? "^%m/%d %H:%M" :
       +            "^%Y/%m/%d";
                n = x = 0;
        
       -        t = tmin + csvep - tmin % csvep;
       -        for (; t < tmax; t += csvep) {
       +        t = tmin + tstep - tmin % tstep;
       +        for (; t < tmax; t += tstep) {
                        x = (t - tmin) * col / (tmax - tmin);
                        strftime(tmp, sizeof tmp, fmt, localtime(&t));
                        prec = x - n + strlen(tmp);
       @@ -76,51 +159,41 @@ braille_axis_x(FILE *fp, time_t tmin, time_t tmax, time_t csvep, int col)
         * Plot a single line out of the y axis, at row <r> out of <rows>.
         */
        static void
       -braille_axis_y(FILE *fp, double vmin, double vmax, int r, int rows)
       +braille_axis_y(FILE *fp, double min, double max, int r, int rows)
        {
       -        char tmp[10] = "", *s;
       -        double val;
       -
       -        val = (rows - r) * (vmax - vmin) / rows;
       -        humanize(tmp, val);
       -        s =
       -          (r == 0) ? "┌" :
       -          (r == rows - 1) ? "└" :
       -          "├";
       -        fprintf(fp, "%s%-6s ", s, tmp);
       +        char buf[10] = "";
       +
       +        humanize(buf, (rows - 1 - r) * (max - min) / rows);
       +        fprintf(fp, "├%s ", buf);
        }
        
        static int
       -braille_render(struct drawille *drw, FILE *fp, double vmin, double vmax)
       +braille_render(struct drawille *drw, FILE *fp, double min, double max)
        {
                int row;
        
                for (row = 0; row < drw->row; row++) {
                        drawille_put_row(fp, drw, row);
       -                braille_axis_y(fp, vmin, vmax, row, drw->row);
       +                braille_axis_y(fp, min, max, row, drw->row);
                        fprintf(fp, "\n");
                }
                return 0;
        }
        
        static void
       -plot(struct csv *vl, FILE *fp, size_t ncol, int rows, int cols)
       +plot(struct csv *vl, size_t ncol, int rows, int cols, FILE *fp)
        {
       -        double vmin, vmax, vstep;
       -        time_t tmin, tmax, csvep;
       +        double vmin, vmax;
       +        time_t tmin, tmax, tstep;
                struct drawille *drw;
        
       -        cols -= 9;                /* scale printed at the right */
       -
       -        scale_minmax(vl, ncol, &tmin, &tmax, &vmin, &vmax);
       -        csvep = scale_csvep(tmin, tmax, cols / 10);
       -        vstep = scale_vstep(vmin, vmax, rows / 10);
       +        rows = MAX(rows, 2);        /* readable */
        
       -        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, csvep, ncol, rows);
       +        if (get_min_max(vl, ncol, &tmin, &tmax, &vmin, &vmax) < 0)
       +                err(1, "invalid scale: tmin=%lld tmax=%lld vmin=%fd vmax=%fd",
       +                    (long long)tmin, (long long)tmax, vmin, vmax);
       +        adjust_scale(&vmin, &vmax, rows);
       +        tstep = time_mark_step(tmin, tmax, cols);
        
                for (; ncol > 0; vl++, ncol--) {
                        if ((drw = drawille_new(rows, cols)) == NULL)
       @@ -132,7 +205,7 @@ plot(struct csv *vl, FILE *fp, size_t ncol, int rows, int cols)
                                err(1, "rendering braille canvas");
                        free(drw);
                }
       -        if (braille_axis_x(fp, tmin, tmax, csvep, cols) == -1)
       +        if (braille_axis_x(fp, tmin, tmax, tstep * 10, cols) == -1)
                        err(1, "printing x axis");;
        }
        
       @@ -153,7 +226,7 @@ main(int argc, char **argv)
                if (pledge("stdio", "") < 0)
                        err(1, "pledge: %s", strerror(errno));
        
       -        rows = 20, cols = 80;
       +        rows = 4, cols = 60;
                arg0 = *argv;
                while ((c = getopt(argc, argv, "r:c:")) > -1) {
                        switch (c) {
       @@ -185,8 +258,8 @@ main(int argc, char **argv)
                csv_labels(stdin, &vl, &ncol);
                csv_values(stdin, vl, ncol);
        
       -        plot(vl, stdout, ncol, rows, cols);
       +        plot(vl, ncol, rows, cols, stdout);
        
                free(vl);
       -        return 1;
       +        return 0;
        }
 (DIR) diff --git a/ploot-farbfeld.c b/ploot-farbfeld.c
       @@ -236,20 +236,20 @@ ffplot_v2y(double v, double vmin, double vmax)
        
        static void
        ffplot_xaxis(struct ffplot *plot, struct ffcolor *label, struct ffcolor *grid,
       -        time_t tmin, time_t tmax, time_t csvep)
       +        time_t tmin, time_t tmax, time_t tstep)
        {
                time_t t;
                int x;
                char str[sizeof("MM/DD HH/MM")], *fmt;
        
       -        if (csvep < 3600 * 12)
       +        if (tstep < 3600 * 12)
                        fmt = "%H:%M:%S";
       -        else if (csvep < 3600 * 24)
       +        else if (tstep < 3600 * 24)
                        fmt = "%m/%d %H:%M";
                else
                        fmt = "%X/%m/%d";
        
       -        for (t = tmax - tmax % csvep; t >= tmin; t -= csvep) {
       +        for (t = tmax - tmax % tstep; t >= tmin; t -= tstep) {
                        x = ffplot_t2x(t, tmin, tmax);
        
                        ffplot_line(plot, grid,
       @@ -358,10 +358,10 @@ 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, csvep;
       +        time_t tmin, tmax, tstep;
        
                scale_minmax(vl, ncol, &tmin, &tmax, &vmin, &vmax);
       -        csvep = scale_csvep(tmin, tmax, 7);
       +        tstep = scale_tstep(tmin, tmax, 7);
                vstep = scale_vstep(vmin, vmax, 7);
        
                if ((plot.buf = calloc(IMAGE_H * IMAGE_W, sizeof *plot.buf)) == NULL)
       @@ -377,7 +377,7 @@ plot(struct csv *vl, struct ffcolor **cl, size_t ncol, char *name, char *units)
        
                plot.x = XLABEL_X;
                plot.y = XLABEL_Y;
       -        ffplot_xaxis(&plot, &label_fg, &grid_fg, tmin, tmax, csvep);
       +        ffplot_xaxis(&plot, &label_fg, &grid_fg, tmin, tmax, tstep);
        
                plot.x = YLABEL_X;
                plot.y = YLABEL_Y;
 (DIR) diff --git a/scale.c b/scale.c
       @@ -1,94 +0,0 @@
       -#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
       @@ -1,14 +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_csvep(time_t, time_t, int);
       -double        scale_vstep(double, double, int);
       -
       -#endif