|
|
tploot-ff.c - ploot - simple plotting tools |
|
|
 |
git clone git://bitreich.org/ploot git://hg6vgqziawt5s4dj.onion/ploot (git://bitreich.org) |
|
|
 |
Log |
|
|
 |
Files |
|
|
 |
Refs |
|
|
 |
Tags |
|
|
 |
README |
|
|
|
--- |
|
|
|
tploot-ff.c (13227B) |
|
|
|
--- |
|
|
|
1 #include <arpa/inet.h> |
|
|
|
2 |
|
|
|
3 #include <math.h> |
|
|
|
4 #include <stdint.h> |
|
|
|
5 #include <stdio.h> |
|
|
|
6 #include <stdlib.h> |
|
|
|
7 #include <string.h> |
|
|
|
8 #include <time.h> |
|
|
|
9 #include <time.h> |
|
|
|
10 #include <stdlib.h> |
|
|
|
11 #include <stdio.h> |
|
|
|
12 #include <fcntl.h> |
|
|
|
13 #include <limits.h> |
|
|
|
14 #include <string.h> |
|
|
|
15 #include <ctype.h> |
|
|
|
16 #include <time.h> |
|
|
|
17 #include <stdint.h> |
|
|
|
18 |
|
|
|
19 #include "arg.h" |
|
|
|
20 #include "util.h" |
|
|
|
21 #include "font.h" |
|
|
|
22 |
|
|
|
23 #define MARGIN 4 |
|
|
|
24 |
|
|
|
25 #define XDENSITY 7 /* nb of values on x axis */ |
|
|
|
26 #define YDENSITY 7 /* nb of values on y axis */ |
|
|
|
27 |
|
|
|
28 #define TITLE_X (IMAGE_H - TITLE_H) |
|
|
|
29 #define TITLE_Y (XLABEL_W) |
|
|
|
30 #define TITLE_H (FONT_H * 2) |
|
|
|
31 #define TITLE_W (PLOT_W) |
|
|
|
32 |
|
|
|
33 #define XLABEL_X (PLOT_X) |
|
|
|
34 #define XLABEL_Y (0) |
|
|
|
35 #define XLABEL_H (PLOT_H) |
|
|
|
36 #define XLABEL_W (FONT_W * 9 + MARGIN) |
|
|
|
37 |
|
|
|
38 #define YLABEL_X (0) |
|
|
|
39 #define YLABEL_Y (PLOT_Y) |
|
|
|
40 #define YLABEL_H (FONT_H * 2) |
|
|
|
41 #define YLABEL_W (PLOT_W) |
|
|
|
42 |
|
|
|
43 #define PLOT_X (YLABEL_H) |
|
|
|
44 #define PLOT_Y (XLABEL_W) |
|
|
|
45 #define PLOT_W 700 |
|
|
|
46 #define PLOT_H 160 |
|
|
|
47 |
|
|
|
48 #define LEGEND_X (YLABEL_H) |
|
|
|
49 #define LEGEND_Y (IMAGE_W - LEGEND_W) |
|
|
|
50 #define LEGEND_W (FONT_W + 150 + FONT_W) |
|
|
|
51 #define LEGEND_H (PLOT_H) |
|
|
|
52 |
|
|
|
53 #define IMAGE_H (TITLE_H + PLOT_H + YLABEL_H) |
|
|
|
54 #define IMAGE_W (XLABEL_W + PLOT_W + LEGEND_W) |
|
|
|
55 |
|
|
|
56 typedef uint16_t Color[4]; |
|
|
|
57 typedef struct clist Clist; |
|
|
|
58 typedef struct vlist Vlist; |
|
|
|
59 typedef struct canvas Canvas; |
|
|
|
60 typedef struct font Font; |
|
|
|
61 |
|
|
|
62 struct vlist { |
|
|
|
63 Color col; /* color to use to draw the line */ |
|
|
|
64 time_t *t; /* array of timestamps */ |
|
|
|
65 double *v; /* array of values */ |
|
|
|
66 int n; /* number of values */ |
|
|
|
67 char *label; /* for the legend */ |
|
|
|
68 }; |
|
|
|
69 |
|
|
|
70 struct canvas { |
|
|
|
71 int w; /* width */ |
|
|
|
72 int h; /* height */ |
|
|
|
73 int x; /* x offset */ |
|
|
|
74 int y; /* x offset */ |
|
|
|
75 Color b[IMAGE_W * IMAGE_H]; |
|
|
|
76 }; |
|
|
|
77 |
|
|
|
78 struct font { |
|
|
|
79 int w; /* width */ |
|
|
|
80 int h; /* height */ |
|
|
|
81 char **b; /* buffer */ |
|
|
|
82 }; |
|
|
|
83 |
|
|
|
84 struct clist { |
|
|
|
85 char *name; |
|
|
|
86 Color col; |
|
|
|
87 }; |
|
|
|
88 |
|
|
|
89 char *argv0; |
|
|
|
90 char *tflag = ""; |
|
|
|
91 char *uflag = ""; |
|
|
|
92 |
|
|
|
93 Clist clist[] = { |
|
|
|
94 /* name red green blue alpha */ |
|
|
|
95 { "red", { 0xffff, 0x4444, 0x4444, 0xffff } }, |
|
|
|
96 { "orange", { 0xffff, 0x9999, 0x4444, 0xffff } }, |
|
|
|
97 { "yellow", { 0xffff, 0xffff, 0x4444, 0xffff } }, |
|
|
|
98 { "green", { 0x2222, 0xffff, 0x5555, 0xffff } }, |
|
|
|
99 { "cyan", { 0x0000, 0xffff, 0xdddd, 0xffff } }, |
|
|
|
100 { "blue", { 0x2222, 0x9999, 0xffff, 0xffff } }, |
|
|
|
101 { NULL, { 0, 0, 0, 0 } } |
|
|
|
102 }; |
|
|
|
103 |
|
|
|
104 Font font = { FONT_W, FONT_H, glyph }; |
|
|
|
105 |
|
|
|
106 static int |
|
|
|
107 color(Color *col, char *name) |
|
|
|
108 { |
|
|
|
109 Clist *c; |
|
|
|
110 |
|
|
|
111 for (c = clist; c->name != NULL; c++) { |
|
|
|
112 if (strcmp(name, c->name) == 0) { |
|
|
|
113 memcpy(col, c->col, sizeof(*col)); |
|
|
|
114 return 0; |
|
|
|
115 } |
|
|
|
116 } |
|
|
|
117 |
|
|
|
118 return -1; |
|
|
|
119 } |
|
|
|
120 |
|
|
|
121 static void |
|
|
|
122 scale_minmax(Vlist *v, int n, |
|
|
|
123 double *vmin, double *vmax, |
|
|
|
124 time_t *tmin, time_t *tmax) |
|
|
|
125 { |
|
|
|
126 int i; |
|
|
|
127 |
|
|
|
128 *vmin = *vmax = 0; |
|
|
|
129 *tmin = *tmax = *v->t; |
|
|
|
130 |
|
|
|
131 for (; n-- > 0; v++) { |
|
|
|
132 for (i = 0; i < v->n; i++) { |
|
|
|
133 if (v->v[i] < *vmin) |
|
|
|
134 *vmin = v->v[i]; |
|
|
|
135 if (v->v[i] > *vmax) |
|
|
|
136 *vmax = v->v[i]; |
|
|
|
137 if (v->t[i] < *tmin) |
|
|
|
138 *tmin = v->t[i]; |
|
|
|
139 if (v->t[i] > *tmax) |
|
|
|
140 *tmax = v->t[i]; |
|
|
|
141 } |
|
|
|
142 } |
|
|
|
143 } |
|
|
|
144 |
|
|
|
145 static void |
|
|
|
146 scale_tstep(time_t *step, int density, time_t min, time_t max) |
|
|
|
147 { |
|
|
|
148 time_t dt, *s, scale[] = { |
|
|
|
149 1, 5, 2, 10, 20, 30, 60, 60*2, 60*5, 60*10, 60*20, 60*30, 3600, |
|
|
|
150 3600*2, 3600*5, 3600*10, 3600*18, 3600*24, 3600*24*2, |
|
|
|
151 3600*24*5, 3600*24*10, 3600*24*20, 3600*24*30, 3600*24*50, |
|
|
|
152 3600*24*100, 3600*24*365 |
|
|
|
153 }; |
|
|
|
154 |
|
|
|
155 dt = max - min; |
|
|
|
156 |
|
|
|
157 for (s = scale; s < scale + LEN(scale); s++) { |
|
|
|
158 if (dt < *s * density) { |
|
|
|
159 *step = *s; |
|
|
|
160 break; |
|
|
|
161 } |
|
|
|
162 } |
|
|
|
163 } |
|
|
|
164 |
|
|
|
165 static void |
|
|
|
166 scale_vstep(double *step, int density, double min, double max) |
|
|
|
167 { |
|
|
|
168 double dv, *s, scale[] = { 1, 2, 3, 5 }; |
|
|
|
169 int i; |
|
|
|
170 |
|
|
|
171 dv = max - min; |
|
|
|
172 |
|
|
|
173 if (dv > 1) { |
|
|
|
174 for (i = 1; i != 0; i *= 10) { |
|
|
|
175 for (s = scale; s < scale + LEN(scale); s++) { |
|
|
|
176 if (dv < *s * i * density) { |
|
|
|
177 *step = *s * i; |
|
|
|
178 return; |
|
|
|
179 } |
|
|
|
180 } |
|
|
|
181 } |
|
|
|
182 } else { |
|
|
|
183 for (i = 1; i != 0; i *= 10) { |
|
|
|
184 for (s = scale + LEN(scale) - 1; s >= scale; s--) { |
|
|
|
185 if (dv > *s / i * density / 2) { |
|
|
|
186 *step = *s / i; |
|
|
|
187 return; |
|
|
|
188 } |
|
|
|
189 } |
|
|
|
190 } |
|
|
|
191 } |
|
|
|
192 } |
|
|
|
193 |
|
|
|
194 static void |
|
|
|
195 scale(Vlist *v, int n, |
|
|
|
196 double *vmin, double *vmax, double *vstep, |
|
|
|
197 time_t *tmin, time_t *tmax, time_t *tstep) |
|
|
|
198 { |
|
|
|
199 scale_minmax(v, n, vmin, vmax, tmin, tmax); |
|
|
|
200 scale_tstep(tstep, YDENSITY, *tmin, *tmax); |
|
|
|
201 scale_vstep(vstep, XDENSITY, *vmin, *vmax); |
|
|
|
202 } |
|
|
|
203 |
|
|
|
204 /* |
|
|
|
205 * Convert (x,y) coordinates to (row,col) for printing into the buffer. |
|
|
|
206 * The buffer only contain one number, so the coordinate is a single integer: |
|
|
|
207 * width * x + y. |
|
|
|
208 * The coordinates are shifted by offx and offy to permit relative coordinates. |
|
|
|
209 * |
|
|
|
210 * The convention used: y |
|
|
|
211 * - (0,0) is at the lower left corner of the canvas. | |
|
|
|
212 * - (0,1) is above it. +--x |
|
|
|
213 */ |
|
|
|
214 static void |
|
|
|
215 ff_pixel(Canvas *can, Color *col, |
|
|
|
216 int x, int y) |
|
|
|
217 { |
|
|
|
218 x += can->x; |
|
|
|
219 y += can->y; |
|
|
|
220 if (x < 0 || x >= can->h || y < 0 || y >= can->w) |
|
|
|
221 return; |
|
|
|
222 memcpy(can->b + can->w * (can->h - 1 - x) + y, col, sizeof(*can->b)); |
|
|
|
223 } |
|
|
|
224 |
|
|
|
225 static void |
|
|
|
226 ff_rectangle(Canvas *can, Color *col, |
|
|
|
227 int x1, int y1, |
|
|
|
228 int x2, int y2) |
|
|
|
229 { |
|
|
|
230 int x, y, xmin, ymin, xmax, ymax; |
|
|
|
231 |
|
|
|
232 xmin = MIN(x1, x2); xmax = MAX(x1, x2); |
|
|
|
233 ymin = MIN(y1, y2); ymax = MAX(y1, y2); |
|
|
|
234 |
|
|
|
235 for (x = xmin; x <= xmax; x++) |
|
|
|
236 for (y = ymin; y <= ymax; y++) |
|
|
|
237 ff_pixel(can, col, x, y); |
|
|
|
238 } |
|
|
|
239 |
|
|
|
240 /* |
|
|
|
241 * From Bresenham's line algorithm and dcat's tplot. |
|
|
|
242 */ |
|
|
|
243 static void |
|
|
|
244 ff_line(Canvas *can, Color *col, |
|
|
|
245 int x0, int y0, |
|
|
|
246 int x1, int y1) |
|
|
|
247 { |
|
|
|
248 int dx, dy, sx, sy, err, e; |
|
|
|
249 |
|
|
|
250 sx = x0 < x1 ? 1 : -1; |
|
|
|
251 sy = y0 < y1 ? 1 : -1; |
|
|
|
252 dx = abs(x1 - x0); |
|
|
|
253 dy = abs(y1 - y0); |
|
|
|
254 err = (dx > dy ? dx : -dy) / 2; |
|
|
|
255 |
|
|
|
256 for (;;) { |
|
|
|
257 ff_pixel(can, col, x0, y0); |
|
|
|
258 |
|
|
|
259 if (x0 == x1 && y0 == y1) |
|
|
|
260 break; |
|
|
|
261 |
|
|
|
262 e = err; |
|
|
|
263 if (e > -dx) { |
|
|
|
264 x0 += sx; |
|
|
|
265 err -= dy; |
|
|
|
266 } |
|
|
|
267 if (e < dy) { |
|
|
|
268 y0 += sy; |
|
|
|
269 err += dx; |
|
|
|
270 } |
|
|
|
271 } |
|
|
|
272 } |
|
|
|
273 |
|
|
|
274 /* |
|
|
|
275 * Draw a coloured glyph from font f centered on x. |
|
|
|
276 */ |
|
|
|
277 static void |
|
|
|
278 ff_char(Canvas *can, Color *col, char c, Font *f, |
|
|
|
279 int x, int y) |
|
|
|
280 { |
|
|
|
281 int xf, yf; |
|
|
|
282 |
|
|
|
283 if (c & 0x80) |
|
|
|
284 c = '\0'; |
|
|
|
285 |
|
|
|
286 |
|
|
|
287 x -= f->h / 2; |
|
|
|
288 |
|
|
|
289 for (xf = 0; xf < f->h; xf++) |
|
|
|
290 for (yf = 0; yf < f->w; yf++) |
|
|
|
291 if (f->b[(int)c][f->w * (f->h - xf) + yf] == 1) |
|
|
|
292 ff_pixel(can, col, x + xf, y + yf); |
|
|
|
293 } |
|
|
|
294 |
|
|
|
295 /* |
|
|
|
296 * Draw a left aligned string without wrapping it. |
|
|
|
297 */ |
|
|
|
298 static void |
|
|
|
299 ff_str_left(Canvas *can, Color *col, char *s, Font *f, |
|
|
|
300 int x, int y) |
|
|
|
301 { |
|
|
|
302 for (; *s != '\0'; y += f->w, s++) |
|
|
|
303 ff_char(can, col, *s, f, x, y); |
|
|
|
304 } |
|
|
|
305 |
|
|
|
306 /* |
|
|
|
307 * Draw a center aligned string without wrapping it. |
|
|
|
308 */ |
|
|
|
309 static void |
|
|
|
310 ff_str_center(Canvas *can, Color *col, char *s, Font *f, |
|
|
|
311 int x, int y) |
|
|
|
312 { |
|
|
|
313 y -= f->w * strlen(s) / 2; |
|
|
|
314 ff_str_left(can, col, s, f, x, y); |
|
|
|
315 } |
|
|
|
316 |
|
|
|
317 /* |
|
|
|
318 * Draw a right aligned string without wrapping it. |
|
|
|
319 */ |
|
|
|
320 static void |
|
|
|
321 ff_str_right(Canvas *can, Color *col, char *s, Font *f, |
|
|
|
322 int x, int y) |
|
|
|
323 { |
|
|
|
324 y -= f->w * strlen(s); |
|
|
|
325 ff_str_left(can, col, s, f, x, y); |
|
|
|
326 } |
|
|
|
327 |
|
|
|
328 static void |
|
|
|
329 ff_print(Canvas *can) |
|
|
|
330 { |
|
|
|
331 uint32_t w, h; |
|
|
|
332 |
|
|
|
333 w = htonl(can->w); |
|
|
|
334 h = htonl(can->h); |
|
|
|
335 |
|
|
|
336 fputs("farbfeld", stdout); |
|
|
|
337 fwrite(&w, sizeof(w), 1, stdout); |
|
|
|
338 fwrite(&h, sizeof(h), 1, stdout); |
|
|
|
339 fwrite(can->b, can->w * can->h, sizeof(*can->b), stdout); |
|
|
|
340 } |
|
|
|
341 |
|
|
|
342 static int |
|
|
|
343 ff_t2y(time_t t, time_t tmin, time_t tmax) |
|
|
|
344 { |
|
|
|
345 return (t - tmin) * PLOT_W / (tmax - tmin); |
|
|
|
346 } |
|
|
|
347 |
|
|
|
348 static int |
|
|
|
349 ff_v2x(double v, double vmin, double vmax) |
|
|
|
350 { |
|
|
|
351 return (v - vmin) * PLOT_H / (vmax - vmin); |
|
|
|
352 } |
|
|
|
353 |
|
|
|
354 static void |
|
|
|
355 ff_xaxis(Canvas *can, Color *label, Color *grid, |
|
|
|
356 double vmin, double vmax, double vstep) |
|
|
|
357 { |
|
|
|
358 double v; |
|
|
|
359 int x; |
|
|
|
360 char str[8 + 1]; |
|
|
|
361 |
|
|
|
362 for (v = vmax - fmod(vmax, vstep); v >= vmin; v -= vstep) { |
|
|
|
363 x = ff_v2x(v, vmin, vmax); |
|
|
|
364 |
|
|
|
365 ff_line(can, grid, |
|
|
|
366 x, XLABEL_W, |
|
|
|
367 x, XLABEL_W + PLOT_W); |
|
|
|
368 |
|
|
|
369 humanize(str, v); |
|
|
|
370 ff_str_right(can, label, str, &font, |
|
|
|
371 x, XLABEL_W - MARGIN); |
|
|
|
372 } |
|
|
|
373 } |
|
|
|
374 |
|
|
|
375 static void |
|
|
|
376 ff_yaxis(Canvas *can, Color *label, Color *grid, |
|
|
|
377 time_t tmin, time_t tmax, time_t tstep) |
|
|
|
378 { |
|
|
|
379 time_t t; |
|
|
|
380 int y; |
|
|
|
381 char str[sizeof("MM/DD HH/MM")], *fmt; |
|
|
|
382 |
|
|
|
383 if (tstep < 3600 * 12) |
|
|
|
384 fmt = "%H:%M:%S"; |
|
|
|
385 else if (tstep < 3600 * 24) |
|
|
|
386 fmt = "%m/%d %H:%M"; |
|
|
|
387 else |
|
|
|
388 fmt = "%Y/%m/%d"; |
|
|
|
389 |
|
|
|
390 for (t = tmax - tmax % tstep; t >= tmin; t -= tstep) { |
|
|
|
391 y = ff_t2y(t, tmin, tmax); |
|
|
|
392 |
|
|
|
393 ff_line(can, grid, |
|
|
|
394 YLABEL_H, y, |
|
|
|
395 YLABEL_H + PLOT_H, y); |
|
|
|
396 |
|
|
|
397 strftime(str, sizeof(str), fmt, localtime(&t)); |
|
|
|
398 ff_str_center(can, label, str, &font, |
|
|
|
399 YLABEL_H / 2, y); |
|
|
|
400 } |
|
|
|
401 } |
|
|
|
402 |
|
|
|
403 static void |
|
|
|
404 ff_title(Canvas *can, |
|
|
|
405 Color *ct, char *title, |
|
|
|
406 Color *cu, char *unit) |
|
|
|
407 { |
|
|
|
408 ff_str_left(can, ct, title, &font, |
|
|
|
409 TITLE_H / 2, 0); |
|
|
|
410 ff_str_right(can, cu, unit, &font, |
|
|
|
411 TITLE_H / 2, TITLE_W); |
|
|
|
412 } |
|
|
|
413 |
|
|
|
414 static void |
|
|
|
415 ff_plot(Canvas *can, Vlist *v, |
|
|
|
416 double vmin, double vmax, |
|
|
|
417 time_t tmin, time_t tmax) |
|
|
|
418 { |
|
|
|
419 time_t *tp; |
|
|
|
420 double *vp; |
|
|
|
421 int x, y, n, xlast, ylast, first; |
|
|
|
422 |
|
|
|
423 first = 1; |
|
|
|
424 for (tp = v->t, vp = v->v, n = v->n; n > 0; n--, vp++, tp++) { |
|
|
|
425 x = ff_v2x(*vp, vmin, vmax); |
|
|
|
426 y = ff_t2y(*tp, tmin, tmax); |
|
|
|
427 |
|
|
|
428 if (!first) |
|
|
|
429 ff_line(can, &v->col, xlast, ylast, x, y); |
|
|
|
430 |
|
|
|
431 xlast = x; |
|
|
|
432 ylast = y; |
|
|
|
433 first = 0; |
|
|
|
434 } |
|
|
|
435 } |
|
|
|
436 |
|
|
|
437 static void |
|
|
|
438 ff_values(Canvas *can, Vlist *v, int n, |
|
|
|
439 double vmin, double vmax, |
|
|
|
440 time_t tmin, time_t tmax) |
|
|
|
441 { |
|
|
|
442 for (; n > 0; n--, v++) |
|
|
|
443 ff_plot(can, v, vmin, vmax, tmin, tmax); |
|
|
|
444 } |
|
|
|
445 |
|
|
|
446 static void |
|
|
|
447 ff_legend(Canvas *can, Color *label_fg, Vlist *v, int n) |
|
|
|
448 { |
|
|
|
449 int i, x, y; |
|
|
|
450 |
|
|
|
451 for (i = 0; i < n; i++, v++) { |
|
|
|
452 x = LEGEND_H - i * (FONT_H + MARGIN) - FONT_H / 2; |
|
|
|
453 |
|
|
|
454 y = MARGIN + FONT_W; |
|
|
|
455 ff_str_left(can, &v->col, "\1", &font, x, y); |
|
|
|
456 |
|
|
|
457 y += FONT_W * 2; |
|
|
|
458 ff_str_left(can, label_fg, v->label, &font, x, y); |
|
|
|
459 } |
|
|
|
460 } |
|
|
|
461 |
|
|
|
462 /* |
|
|
|
463 * Plot the 'n' values list of the 'v' array with title 'name' and |
|
|
|
464 * 'units' label. |
|
|
|
465 * |
|
|
|
466 * Title (units) |
|
|
|
467 * y ^ Legend |
|
|
|
468 * label |- + - + - + - + - .... |
|
|
|
469 * here |- + - + - + - + - .... |
|
|
|
470 * +--+---+---+---+--> |
|
|
|
471 * x label here |
|
|
|
472 */ |
|
|
|
473 static void |
|
|
|
474 ff(Vlist *v, int n, char *name, char *units) |
|
|
|
475 { |
|
|
|
476 Canvas can = { IMAGE_W, IMAGE_H, 0, 0, { { 0 }, { 0 } } }; |
|
|
|
477 Color plot_bg = { 0x2222, 0x2222, 0x2222, 0xffff }; |
|
|
|
478 Color grid_bg = { 0x2929, 0x2929, 0x2929, 0xffff }; |
|
|
|
479 Color grid_fg = { 0x3737, 0x3737, 0x3737, 0xffff }; |
|
|
|
480 Color label_fg = { 0x8888, 0x8888, 0x8888, 0xffff }; |
|
|
|
481 Color title_fg = { 0xdddd, 0xdddd, 0xdddd, 0xffff }; |
|
|
|
482 double vmin, vmax, vstep; |
|
|
|
483 time_t tmin, tmax, tstep; |
|
|
|
484 |
|
|
|
485 scale(v, n, &vmin, &vmax, &vstep, &tmin, &tmax, &tstep); |
|
|
|
486 |
|
|
|
487 can.x = 0; |
|
|
|
488 can.y = 0; |
|
|
|
489 ff_rectangle(&can, &plot_bg, 0, 0, IMAGE_H - 1, IMAGE_W - 1); |
|
|
|
490 |
|
|
|
491 can.x = PLOT_X; |
|
|
|
492 can.y = PLOT_Y; |
|
|
|
493 ff_rectangle(&can, &grid_bg, 0, 0, PLOT_H, PLOT_W); |
|
|
|
494 |
|
|
|
495 can.x = YLABEL_X; |
|
|
|
496 can.y = YLABEL_Y; |
|
|
|
497 ff_yaxis(&can, &label_fg, &grid_fg, tmin, tmax, tstep); |
|
|
|
498 |
|
|
|
499 can.x = XLABEL_X; |
|
|
|
500 can.y = XLABEL_Y; |
|
|
|
501 ff_xaxis(&can, &label_fg, &grid_fg, vmin, vmax, vstep); |
|
|
|
502 |
|
|
|
503 can.x = TITLE_X; |
|
|
|
504 can.y = TITLE_Y; |
|
|
|
505 ff_title(&can, &title_fg, name, &label_fg, units); |
|
|
|
506 |
|
|
|
507 can.x = PLOT_X; |
|
|
|
508 can.y = PLOT_Y; |
|
|
|
509 ff_values(&can, v, n, vmin, vmax, tmin, tmax); |
|
|
|
510 |
|
|
|
511 can.x = LEGEND_X; |
|
|
|
512 can.y = LEGEND_Y; |
|
|
|
513 ff_legend(&can, &label_fg, v, n); |
|
|
|
514 |
|
|
|
515 ff_print(&can); |
|
|
|
516 } |
|
|
|
517 |
|
|
|
518 static void |
|
|
|
519 csv_labels(Vlist *v, char **argv, char *buf) |
|
|
|
520 { |
|
|
|
521 if (esfgets(buf, LINE_MAX, stdin) == NULL) |
|
|
|
522 fputs("missing label line\n", stderr), exit(1); |
|
|
|
523 |
|
|
|
524 if (strcmp(strsep(&buf, ","), "epoch") != 0) |
|
|
|
525 fputs("first label must be \"epoch\"\n", stderr), exit(1); |
|
|
|
526 |
|
|
|
527 for (; *argv != NULL; v++, argv++) { |
|
|
|
528 if ((v->label = strsep(&buf, ",")) == NULL) |
|
|
|
529 fputs("more arguments than columns\n", stderr), exit(1); |
|
|
|
530 else if (color(&v->col, *argv) == -1) |
|
|
|
531 fprintf(stderr, "unknown color: %s\n", *argv), exit(1); |
|
|
|
532 } |
|
|
|
533 |
|
|
|
534 if (strsep(&buf, ",") != NULL) |
|
|
|
535 fputs("more columns than arguments\n", stderr), exit(1); |
|
|
|
536 } |
|
|
|
537 |
|
|
|
538 static int |
|
|
|
539 csv_addval(Vlist *v, int bufsize, int nval, double field, time_t epoch) |
|
|
|
540 { |
|
|
|
541 if (nval >= bufsize) { |
|
|
|
542 bufsize = bufsize * 2 + 1; |
|
|
|
543 if ((v->v = realloc(v->v, bufsize * sizeof(*v->v))) == NULL) |
|
|
|
544 perror("reallocating values buffer"), exit(1); |
|
|
|
545 if ((v->t = realloc(v->t, bufsize * sizeof(*v->t))) == NULL) |
|
|
|
546 perror("reallocating values buffer"), exit(1); |
|
|
|
547 } |
|
|
|
548 v->v[nval] = field; |
|
|
|
549 v->t[nval] = epoch; |
|
|
|
550 v->n = nval + 1; |
|
|
|
551 |
|
|
|
552 return bufsize; |
|
|
|
553 } |
|
|
|
554 |
|
|
|
555 /* |
|
|
|
556 * Add to each column the value on the current row. |
|
|
|
557 */ |
|
|
|
558 static int |
|
|
|
559 csv_addrow(Vlist *v, int bufsize, int ncol, int nval, char *line) |
|
|
|
560 { |
|
|
|
561 time_t epoch; |
|
|
|
562 int bs; |
|
|
|
563 char *field, *dot; |
|
|
|
564 |
|
|
|
565 if ((field = strsep(&line, ",")) == NULL) |
|
|
|
566 fprintf(stderr, "%d: missing epoch\n", nval), exit(1); |
|
|
|
567 |
|
|
|
568 if ((dot = strchr(field, '.')) != NULL) |
|
|
|
569 *dot = '\0'; |
|
|
|
570 epoch = eatol(field); |
|
|
|
571 for (; (field = strsep(&line, ",")) != NULL; ncol--, v++) { |
|
|
|
572 if (ncol <= 0) |
|
|
|
573 fprintf(stderr, "%d: too many fields\n", nval), exit(1); |
|
|
|
574 bs = csv_addval(v, bufsize, nval, eatof(field), epoch); |
|
|
|
575 } |
|
|
|
576 if (ncol > 0) |
|
|
|
577 fprintf(stderr, "%d: too few fields\n", nval), exit(1); |
|
|
|
578 |
|
|
|
579 return bs; |
|
|
|
580 } |
|
|
|
581 |
|
|
|
582 /* |
|
|
|
583 * < ncol > |
|
|
|
584 * epoch,a1,b1,c1 ^ |
|
|
|
585 * epoch,a2,b2,c2 nval |
|
|
|
586 * epoch,a3,b3,c3 v |
|
|
|
587 */ |
|
|
|
588 static void |
|
|
|
589 csv_values(Vlist *v, int ncol) |
|
|
|
590 { |
|
|
|
591 int nval, bufsize; |
|
|
|
592 char line[LINE_MAX]; |
|
|
|
593 |
|
|
|
594 bufsize = 0; |
|
|
|
595 for (nval = 0; esfgets(line, sizeof(line), stdin) != NULL; nval++) |
|
|
|
596 bufsize = csv_addrow(v, bufsize, ncol, nval, line); |
|
|
|
597 if (nval == 0) |
|
|
|
598 fputs("no value could be read\n", stderr), exit(1); |
|
|
|
599 } |
|
|
|
600 |
|
|
|
601 static void |
|
|
|
602 usage(void) |
|
|
|
603 { |
|
|
|
604 Clist *c; |
|
|
|
605 |
|
|
|
606 fprintf(stderr, "usage: %s [-t title] [-u unit] {", argv0); |
|
|
|
607 fputs(clist->name, stderr); |
|
|
|
608 for (c = clist + 1; c->name != NULL; c++) |
|
|
|
609 fprintf(stderr, ",%s", c->name); |
|
|
|
610 fputs("}...\n", stderr); |
|
|
|
611 exit(1); |
|
|
|
612 } |
|
|
|
613 |
|
|
|
614 int |
|
|
|
615 main(int argc, char **argv) |
|
|
|
616 { |
|
|
|
617 Vlist *v; |
|
|
|
618 char labels[LINE_MAX]; |
|
|
|
619 |
|
|
|
620 ARGBEGIN { |
|
|
|
621 case 't': |
|
|
|
622 tflag = EARGF(usage()); |
|
|
|
623 break; |
|
|
|
624 case 'u': |
|
|
|
625 uflag = EARGF(usage()); |
|
|
|
626 break; |
|
|
|
627 default: |
|
|
|
628 usage(); |
|
|
|
629 } ARGEND; |
|
|
|
630 |
|
|
|
631 if ((v = calloc(argc, sizeof(*v))) == NULL) |
|
|
|
632 perror("calloc value list"), exit(1); |
|
|
|
633 |
|
|
|
634 csv_labels(v, argv, labels); |
|
|
|
635 csv_values(v, argc); |
|
|
|
636 |
|
|
|
637 ff(v, argc, tflag, uflag); |
|
|
|
638 |
|
|
|
639 return 0; |
|
|
|
640 } |
|