csv: fix use of uninitialized memory - 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 f7f88c2ee5573abff4c44c36bf7b2e705081b2ed
DIR parent c3fcef87d156b02a9ad8ca7cd47fee4a826534f4
HTML 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