add an experimental ploot-braille tool - 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 1f4e757723ea483ab2c60c8fec2937569441af9e DIR parent ffb9fc9caeaf3a79f5ab4c7fcbbf4994c1037582 HTML Author: Josuah Demangeon <me@josuah.net> Date: Sat, 15 Feb 2020 15:23:03 +0100 add an experimental ploot-braille tool Diffstat: M .gitignore | 1 + M Makefile | 4 ++-- M arg.h | 2 -- A csv.c | 95 ++++++++++++++++++++++++++++++ M def.h | 24 ++++++++++++++++++++++-- M drawille.c | 39 +++++++++++++++---------------- D log.h | 45 ------------------------------- M ploot-braille.c | 201 +++++++++++++------------------ M ploot-farbfeld.c | 7 +++---- M ploot-feed.c | 14 ++++++++------ A scale.c | 139 ++++++++++++++++++++++++++++++ M util.c | 53 ++++++++++++++++++++++++++++--- 12 files changed, 425 insertions(+), 199 deletions(-) --- DIR diff --git a/.gitignore b/.gitignore @@ -1,4 +1,5 @@ *.o *.core +ploot-braille ploot-farbfeld ploot-feed DIR diff --git a/Makefile b/Makefile @@ -1,5 +1,4 @@ -CFLAGS = -Wall -Wextra -std=c99 -pedantic -fPIC \ - -D_POSIX_C_SOURCE=200809L +CFLAGS = -Wall -Wextra -std=c99 -pedantic -fPIC LFLAGS = -static BIN = ploot-farbfeld ploot-feed ploot-braille LIB = -lm @@ -9,6 +8,7 @@ SRC = csv.c drawille.c font.c font7.c font8.c font13.c util.c scale.c all: $(BIN) +${SRC:.c=.o} ${BIN:=.o}: arg.h def.h Makefile ${BIN}: ${SRC:.c=.o} ${BIN:=.o} ${CC} $(LFLAGS) -o $@ $@.o ${SRC:.c=.o} $(LIB) DIR diff --git a/arg.h b/arg.h @@ -1,8 +1,6 @@ #ifndef ARG_H #define ARG_H -extern char const *arg0; - #define ARG_SWITCH(argc, argv) \ arg0 = *argv; \ while (++argv && --argc && **argv == '-' && (*argv)[1]) \ DIR diff --git a/csv.c b/csv.c @@ -0,0 +1,95 @@ +/* + * Read CSV data onto a set of (struct vlist). + */ + +#include <string.h> +#include <time.h> +#include <stdlib.h> + +#include "def.h" + +static void +csv_addtime(struct vlist *vl, time_t epoch) +{ + if ((vl->t = realloc(vl->t, (vl->n + 1) * sizeof(*vl->t))) == NULL) + err(1, "reallocating values buffer"); + vl->t[vl->n] = epoch; +} + +static void +csv_addval(struct vlist *vl, double field) +{ + if ((vl->v = realloc(vl->v, (vl->n + 1) * sizeof(*vl->v))) == NULL) + err(1, "reallocating values buffer"); + vl->v[vl->n] = field; +} + +/* + * Add to each column the value on the current row. + */ +void +csv_addrow(struct vlist *vl, size_t ncol, char *line) +{ + char *field; + + if ((field = strsep(&line, ",")) == NULL) + err(1, "missing epoch at row %zu", vl->n); + + csv_addtime(vl, eatol(field)); + for (; (field = strsep(&line, ",")) != NULL; ncol--, vl->n++, vl++) { + if (ncol == 0) + err(1, "too many fields at line %zu", vl->n); + csv_addval(vl, eatof(field)); + } + if (ncol > 0) + err(1, "too few fields at line %zu", vl->n); +} + +/* + * < *ncol > + * epoch,label1,label2,label3 + */ +void +csv_labels(FILE *fp, char *buf, struct vlist **vl, size_t *ncol) +{ + char *field; + size_t sz; + + if (esfgets(buf, LINE_MAX, fp) == NULL) + err(1, "missing label line"); + + if (strcmp(strsep(&buf, ","), "epoch") != 0) + err(1, "first label must be \"epoch\""); + + *vl = NULL; + for (*ncol = 0; (field = strsep(&buf, ",")) != NULL; ++*ncol) { + sz = (*ncol + 1) * sizeof **vl; + if ((*vl = realloc(*vl, sz)) == NULL) + err(1, "realloc"); + (*vl)[*ncol].label = field; + } +} + +/* + * < ncol > + * epoch,a1,b1,c1 ^ + * epoch,a2,b2,c2 vl->n + * epoch,a3,b3,c3 v + */ +void +csv_values(FILE *fp, struct vlist *vl, size_t ncol) +{ + char line[LINE_MAX]; + time_t *tbuf; + + while (esfgets(line, sizeof(line), fp) != NULL) + csv_addrow(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"); + + /* The same time buffer can be used for all. */ + for (tbuf = vl->t; ncol > 0; ncol--, vl++) + vl->t = tbuf; +} DIR diff --git a/def.h b/def.h @@ -1,4 +1,5 @@ #include <limits.h> +#include <stdarg.h> #include <stdint.h> #include <stdio.h> @@ -37,8 +38,8 @@ struct vlist { /* csv.c */ void csv_addrow (struct vlist *, size_t, char *); -void csv_values (struct vlist *, size_t); -void csv_labels (struct vlist *, char **, char *); +void csv_labels (FILE *, char *, struct vlist **, size_t *); +void csv_values (FILE *, struct vlist *, size_t); /* drawille.c */ @@ -61,12 +62,28 @@ struct font font13; struct font font7; struct font font8; +/* ploot-braille.c */ + +char const *arg0; + +/* ploot-farbfeld.c */ + +char const *arg0; + +/* ploot-feed.c */ + +char const *arg0; + /* scale.c */ +int scale_ypos (double, double, double, int); +int scale_xpos (time_t, time_t, time_t, int); +void scale_vminmax (double *, double *, int); void scale (struct vlist *, int, time_t *, time_t *, time_t *, double *, double *, double *); /* util.c */ +size_t strlcpy (char *, const char *, size_t); void put3utf (long); char * strsep (char **, const char *); void estriplf (char *); @@ -74,3 +91,6 @@ double eatof (char *); long eatol (char *); char * esfgets (char *, size_t, FILE *); int humanize (char *, double); +void vlog (char const *, char const *, va_list); +void warn (char const *, ...); +void err (int, char const *, ...); DIR diff --git a/drawille.c b/drawille.c @@ -1,3 +1,7 @@ +/* + * Terminal-based plotting using drawille character, aka drawille. + */ + #include <stdint.h> #include <stdio.h> #include <stdlib.h> @@ -5,14 +9,10 @@ #include "def.h" -/* - * Terminal-based plotting using drawille character, aka drawille. - */ - /* parameters used to draw a line */ struct line { - int x0, y0, x1, y1; /* point of the line */ - int dx, dy, sx, sy, err; /* parameters for the algorythm */ + int x0, y0, x1, y1; /* point of the line */ + int dx, dy, sx, sy, err; /* parameters for the algorythm */ }; /* @@ -36,7 +36,7 @@ drawille_cell_dot(uint8_t *cell, int row, int col) static size_t drawille_cell_utf(uint8_t cell, char *utf) { - long rune; + long rune; rune = 10240 + cell; utf[0] = (char)(0xe0 | (0x0f & (rune >> 12))); /* 1110xxxx */ @@ -54,8 +54,8 @@ drawille_get(struct drawille *drw, int row, int col) size_t drawille_fmt_row(struct drawille *drw, char *buf, size_t sz, int row) { - char txt[] = "xxx"; - size_t n; + char txt[] = "xxx"; + size_t n; n = 0; for (int col = 0; col < drw->col; col++) { @@ -82,7 +82,7 @@ drawille_dot(struct drawille *drw, int x, int y) struct drawille * drawille_new(int row, int col) { - struct drawille *drw; + struct drawille *drw; if ((drw = calloc(sizeof(struct drawille) + row * col, 1)) == NULL) return NULL; @@ -108,10 +108,10 @@ drawille_line_init(struct line *l, int x0, int y0, int x1, int y1) static int drawille_line_next(struct line *l) { - int e; + int e; if (l->x0 == l->x1 && l->y0 == l->y1) - return 0; + return -1; e = l->err; if (e > -l->dx) { @@ -122,13 +122,13 @@ drawille_line_next(struct line *l) l->y0 += l->sy; l->err += l->dx; } - return 1; + return 0; } void drawille_line(struct drawille *drw, int x0, int y0, int x1, int y1) { - struct line l; + struct line l; drawille_line_init(&l, x0, y0, x1, y1); do { @@ -139,8 +139,8 @@ drawille_line(struct drawille *drw, int x0, int y0, int x1, int y1) void drawille_line_hist(struct drawille *drw, int x0, int y0, int x1, int y1, int zero) { - struct line l; - int sign; + struct line l; + int sign; drawille_line_init(&l, x0, y0, x1, y1); do { @@ -153,7 +153,7 @@ drawille_line_hist(struct drawille *drw, int x0, int y0, int x1, int y1, int zer void drawille_dot_hist(struct drawille *drw, int x, int y, int zero) { - int sign; + int sign; sign = (y > zero) ? (-1) : (+1); for (; y != zero + sign; y += sign) @@ -163,8 +163,8 @@ drawille_dot_hist(struct drawille *drw, int x, int y, int zero) static int drawille_text_glyph(struct drawille *drw, int x, int y, struct font *font, char c) { - int width; - char *glyph; + int width; + char *glyph; if ((unsigned)c > 127) glyph = font->glyph[0]; @@ -187,7 +187,6 @@ drawille_text(struct drawille *drw, int x, int y, struct font *font, char *s) { if (drw->row*4 < font->height) return NULL; - for (; *s != '\0' && x < drw->col/2; s++, x++) x += drawille_text_glyph(drw, x, y, font, *s); return s; DIR diff --git a/log.h b/log.h @@ -1,45 +0,0 @@ -#ifndef LOG_H -#define LOG_H - -#include <errno.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -char const *arg0; /* Should be set by the library caller. */ - -static inline void -vlog(char const *base, char const *fmt, va_list va) -{ - fprintf(stderr, "%s: ", base); - vfprintf(stderr, fmt, va); - if (errno) - fprintf(stderr, ": %s", strerror(errno)); - fputc('\n', stderr); - fflush(stderr); - errno = 0; /* avoid repeating the error in loop */ -} - -static inline void -warn(char const *fmt, ...) -{ - va_list va; - - va_start(va, fmt); - vlog(arg0, fmt, va); - va_end(va); -} - -static inline void -err(int e, char const *fmt, ...) -{ - va_list va; - - va_start(va, fmt); - vlog(arg0, fmt, va); - va_end(va); - exit(e); -} - -#endif DIR diff --git a/ploot-braille.c b/ploot-braille.c @@ -8,48 +8,15 @@ #include <math.h> #include "def.h" +#include "arg.h" -/* - * Adjust the vertical scale so that it gets possible to - */ -static void -plot_scale(double *min, double *max, int row) -{ - double unit, range, mi; - - range = *max - *min; - unit = 1; - - /* Zoom until it fills the canvas. */ - for (; (row - 1) * unit > range; unit /= 10) - continue; - - /* Dezoom until it fits the canvas. */ - for (; (row - 1) * unit < range; unit *= 10) - continue; - - /* Fine tune. */ - if ((row - 1) * unit / 5 > range) - unit /= 5; - if ((row - 1) * unit / 4 > range) - unit /= 4; - if ((row - 1) * unit / 2 > range) - unit /= 2; - - /* Align the minimum (and the zero). */ - for (mi = 0; mi > *min - unit; mi -= unit) - continue; - - /* Update the displayed minimal and maximal. */ - *min = mi; - *max = mi + unit * row; -} +char const *arg0 = NULL; /* * Return the step between two values. */ static int -plot_time_interval(time_t step) +braille_time_interval(time_t step) { time_t scale[] = { 1, 5, 2, 10, 20, 30, 60, 60*2, 60*5, 60*10, 60*20, 60*30, @@ -65,136 +32,142 @@ plot_time_interval(time_t step) } static size_t -plot_axis_x(char *buf, size_t sz, time_t step, time_t t2, int col) +braille_axis_x(FILE *fp, time_t step, time_t tmax, int col) { int x, prec; char tmp[sizeof("MM/DD HH:MM")], *fmt; size_t n; time_t t, interval; - interval = plot_time_interval(step); + interval = braille_time_interval(step); fmt = (step < 3600 * 12) ? "^%H:%M:%S" : (step < 3600 * 24) ? "^%m/%d %H:%M" : "^%Y/%m/%d"; n = x = 0; - t = t2 - col * 2 * step; + t = tmax - col * 2 * step; t += interval - t % interval; - for (; t < t2; t += interval) { + for (; t < tmax; t += interval) { strftime(tmp, sizeof tmp, fmt, localtime(&t)); - x = ((t - t2) / 2 + col * step) / step; + x = ((t - tmax) / 2 + col * step) / step; prec = x - n + strlen(tmp); - assert((n += snprintf(buf+n, sz-n, "%*s", prec, tmp)) <= sz); + fprintf(fp, "%*s", prec, tmp); } - assert((n += strlcpy(buf+n, "\n", sz-n)) < sz); - return n; + fputc('\n', fp); + return 1; } /* * Plot a single line out of the y axis, at row <r> out of <rows>. */ -static size_t -plot_axis_y(char *buf, size_t sz, double min, double max, int r, int rows) +static void +braille_axis_y(FILE *fp, double min, double max, int r, int rows) { - size_t i; char tmp[10] = "", *s; double val; val = (max - min) * (rows - r) / rows + min; - humanize(tmp, sizeof tmp, val); + humanize(tmp, val); s = (r == 0) ? "┌" : (r == rows - 1) ? "└" : "├"; - i = snprintf(buf, sz, "%s%-6s ", s, tmp); - return (i > sz) ? (sz) : (i); + fprintf(fp, "%s%-6s ", s, tmp); } -static char * -plot_render(struct drawille *drw, double min, double max, time_t step, time_t t2) +static int +braille_render(struct drawille *drw, FILE *fp, time_t tmin, time_t tmax) { - char *buf; - size_t sz; - size_t n; + char buf[LINE_MAX]; /* Render the plot line by line. */ - sz = drw->row * (20 + drw->col * 3 + 1) + 1; - sz += drw->col + 1 + 100000; - if ((buf = calloc(sz, 1)) == NULL) - goto err; - n = 0; for (int row = 0; row < drw->row; row++) { - n += drawille_fmt_row(drw, buf+n, sz-n, row); - n += plot_axis_y(buf+n, sz-n, min, max, row, drw->row); - n += strlcpy(buf+n, "\n", sz-n); + drawille_fmt_row(drw, buf, sizeof buf, row); + braille_axis_y(fp, tmin, tmax, row, drw->row); + fputc('\n', fp); } - plot_axis_x(buf+n, sz-n, step, t2, drw->col); - return buf; -err: - errno = ENOBUFS; - free(buf); - return NULL; + return 0; } /* * Plot the body as an histogram interpolating the gaps and include * a vertical and horizontal axis. */ -static char * -plot_hist(struct vlist *vl, time_t t2, struct drawille *drw) +static int +braille_hist(struct vlist *vl, FILE *fp, time_t tmin, time_t tmax, int row, int col) { int x, y, zero, shift; - double min, max, val; - time_t t1, t; - - /* Adjust the y scale. */ - shift = min = max = 0; - timeserie_stats(vl, &min, &max); - if (drw->row > 1) { - shift = 2; /* Align values to the middle of the scale: |- */ - plot_scale(&min, &max, drw->row); - } - zero = timeserie_ypos(0, min, max, drw->row*4) - shift; - - /* Adjust the x scale. */ - t2 = t2 + vl->step - t2 % vl->step; - t1 = t2 - vl->step * vl->len; - - /* Plot the data in memory in <drw> starting from the end (t2). */ - t = t2; - for (x = drw->col * 2; x > 0; x--) { - val = timeserie_get(vl, t); - if (!isnan(val)) { - y = timeserie_ypos(val, min, max, drw->row*4) - shift; - drawille_dot_hist(drw, x, y, zero); - } - t -= vl->step; - } + double *v, vmin, vmax; + time_t *t; + size_t n; + struct drawille *drw; - return plot_render(drw, min, max, vl->step, t2); + if ((drw = drawille_new(row, col)) == NULL) + err(1, "allocating drawille canvas"); + + shift = (drw->row > 1) ? (2) : (0); /* center values on "|-" marks */ + vmin = vmax = 0; + zero = scale_ypos(0, vmin, vmax, drw->row*4) - shift; + v = vl->v; + t = vl->t; + n = vl->n; + for (; n > 0; n--, t++, v++) { + if (isnan(*v)) /* XXX: better handling? */ + continue; + y = scale_ypos(*v, vmin, vmax, drw->row * 4) - shift; + x = scale_xpos(*t, tmin, tmax, drw->col * 2); + drawille_dot_hist(drw, x, y, zero); + } + if (braille_render(drw, fp, tmin, tmax) == -1) + err(1, "rendering braille canvas"); + free(drw); + return 0; } -static char * -plot(struct vlist *vl, time_t t2, int row, int col) +static int +plot(struct vlist *vl, FILE *fp, size_t ncol, int row, int col) { - struct drawille *drw; size_t len; - char *buf; + double vmin, vmax, vstep; + time_t tmin, tmax, tstep; len = 500; - buf = NULL; - drw = NULL; col -= 8; - if (timeserie_read(vl) == -1) - goto err; + scale(vl, ncol, &tmin, &tmax, &tstep, &vmin, &vmax, &vstep); - if ((drw = drawille_new(row, col)) == NULL) - goto err; + if (braille_hist(vl, fp, tmin, tmax, row, col) == -1) + err(1, "allocating drawille canvas"); + braille_axis_x(fp, tstep, tmax, col); + return 0; +} - buf = plot_hist(vl, t2, drw); -err: - if (buf == NULL) - timedb_close(&vl->db); - free(drw); - return buf; +static void +usage(void) +{ + fprintf(stderr, "usage: %s\n", arg0); + exit(1); +} + +int +main(int argc, char **argv) +{ + struct vlist *vl; + char labels[LINE_MAX]; + size_t ncol; + + ARG_SWITCH(argc, argv) { + default: + usage(); + } + + if (argc > 0) + usage(); + + csv_labels(stdin, labels, &vl, &ncol); + csv_values(stdin, vl, ncol); + + plot(vl, stdout, ncol, 20, 80); + + free(vl); + return 1; } DIR diff --git a/ploot-farbfeld.c b/ploot-farbfeld.c @@ -13,7 +13,6 @@ #include <math.h> #include "arg.h" -#include "log.h" #include "def.h" #define MARGIN 4 @@ -66,9 +65,9 @@ struct canvas { struct color *buf; }; -char const *arg0; -static char *tflag = ""; -static char *uflag = ""; +char const *arg0 = NULL; +static char *tflag = ""; +static char *uflag = ""; static struct font *font = &font13; static struct cname cname[] = { DIR diff --git a/ploot-feed.c b/ploot-feed.c @@ -13,9 +13,9 @@ #define WIDTH_MAX 1024 #define BRAILLE_START 10240 -int wflag = 80; -int width = 0; -char const *arg0 = NULL; +char const *arg0 = NULL; +static int wflag = 80; +static int width = 0; /* * Turn the bit at position (row, col) on in the . @@ -139,8 +139,11 @@ plot(char labels[LINE_MAX], double *max, int ncol) last_epoch = epoch = 0; for (n = 0;; n = (n == 25 ? 0 : n + 1)) { - if (n == 0) - put_time(0, 0, 2), fputs(labels, stdout), puts("│"); + if (n == 0) { + put_time(0, 0, 2); + fputs(labels, stdout); + puts("│"); + } epoch = plot_line(out, max, ncol); put_time(epoch, last_epoch, n); @@ -224,7 +227,6 @@ main(int argc, char **argv) int ncol, nmax; char *labv[LINE_MAX / 2], labels[LINE_MAX]; - setvbuf(stdin, NULL, _IOLBF, 0); nmax = parse_args(argc, argv, max); ncol = read_labels(labv); width = (wflag - sizeof("XXxXXxXX _")) / ncol - sizeof("|"); DIR diff --git a/scale.c b/scale.c @@ -0,0 +1,139 @@ +#include "def.h" +#include "err.h" + +#define XDENSITY 7 /* nb of values on x axis */ +#define YDENSITY 7 /* nb of values on y axis */ + +/* + * - <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); +} + +static void +scale_minmax(struct vlist *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); +} + +static time_t +scale_tstep(time_t min, time_t max, int density) +{ + time_t dt, *s, 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 (s = scale; s < scale + LEN(scale); s++) + if (dt < *s * density) + return *s; + return 0; +} + +static double +scale_vstep(double min, double max, int density) +{ + double dv, d, *s, scale[] = { 1, 2, 3, 5 }; + + dv = max - min; + + if (dv > 1) + for (d = 1; d != 0; d *= 10) + for (s = scale; s < scale + LEN(scale); s++) + if (dv < *s * d * density) + return *s * d; + if (dv < 1) + for (d = 1; d != 0; d *= 10) + for (s = scale + LEN(scale) - 1; s >= scale; s--) + if (dv > *s / d * density / 2) + return *s / d; + return 0; +} + +/* + * Adjust the vertical scale so that everything fits, with nice + * scale values. + */ +void +scale_vminmax(double *min, double *max, int row) +{ + double unit, range, mi; + + range = *max - *min; + unit = 1; + + /* Zoom until it fills the canvas. */ + for (; (row - 1) * unit > range; unit /= 10) + continue; + + /* Dezoom until it fits the canvas. */ + for (; (row - 1) * unit < range; unit *= 10) + continue; + + /* Fine tune. */ + if ((row - 1) * unit / 5 > range) + unit /= 5; + if ((row - 1) * unit / 4 > range) + unit /= 4; + if ((row - 1) * unit / 2 > range) + unit /= 2; + + /* Align the minimum (and the zero). */ + for (mi = 0; mi > *min - unit; mi -= unit) + continue; + + /* Update the displayed minimal and maximal. */ + *min = mi; + *max = mi + unit * row; +} + +void +scale(struct vlist *vl, int ncol, + time_t *tmin, time_t *tmax, time_t *tstep, + double *vmin, double *vmax, double *vstep) +{ + scale_minmax(vl, ncol, tmin, tmax, vmin, vmax); + *tstep = scale_tstep(*tmin, *tmax, XDENSITY); + *vstep = scale_vstep(*vmin, *vmax, YDENSITY); +} DIR diff --git a/util.c b/util.c @@ -1,12 +1,24 @@ -#include <string.h> +#include <ctype.h> #include <errno.h> -#include <stdio.h> #include <limits.h> +#include <stdarg.h> +#include <stdio.h> #include <stdlib.h> -#include <ctype.h> +#include <string.h> #include "def.h" +size_t +strlcpy(char *buf, const char *str, size_t sz) +{ + size_t len, cpy; + + cpy = ((len = strlen(str)) > sz) ? (sz) : (len); + memcpy(buf, str, cpy); + buf[sz - 1] = '\0'; + return len; +} + void put3utf(long rune) { @@ -79,7 +91,7 @@ esfgets(char *buf, size_t n, FILE *file) } /* - * Set 'str' to a human-readable form of 'num' with always a width of 8 (+ 1 + * Set 'str' to a human-readable form of 'num' with always a width of 8 (+1 for * the '\0' terminator). Buffer overflow is ensured not to happen due to the * max size of a double. Return the exponent. */ @@ -102,3 +114,36 @@ humanize(char *str, double val) return exp * 3; } + +void +vlog(char const *base, char const *fmt, va_list va) +{ + fprintf(stderr, "%s: ", base); + vfprintf(stderr, fmt, va); + if (errno) + fprintf(stderr, ": %s", strerror(errno)); + fputc('\n', stderr); + fflush(stderr); + errno = 0; /* avoid repeating the error in loop */ +} + +void +warn(char const *fmt, ...) +{ + va_list va; + + va_start(va, fmt); + vlog(arg0, fmt, va); + va_end(va); +} + +void +err(int e, char const *fmt, ...) +{ + va_list va; + + va_start(va, fmt); + vlog(arg0, fmt, va); + va_end(va); + exit(e); +}