1 #include <assert.h> 2 #include <err.h> 3 #include <errno.h> 4 #include <libprop/proplib.h> 5 #include <sys/stat.h> 6 #include <sys/types.h> 7 #include <sys/wait.h> 8 #include <stdio.h> 9 #include <stdlib.h> 10 #include <string.h> 11 #include <unistd.h> 12 13 #include "trivial.h" 14 #include "plotter.h" 15 16 struct ploticus_plot { 17 unsigned type; 18 prop_dictionary_t params; 19 FILE *fp; 20 char *path; 21 }; 22 23 struct ploticus_plotter { 24 int nr_plots; 25 struct ploticus_plot **plots; 26 const char *basepath; 27 }; 28 29 const char *plot_prefabs[] = { 30 [PLOT_TYPE_HIST] = "dist", 31 [PLOT_TYPE_LINE] = "lines", 32 }; 33 34 static 35 void * 36 ploticus_init(const char *base) 37 { 38 struct ploticus_plotter *ctx; 39 40 if (!(ctx = calloc(1, sizeof(*ctx)))) 41 return ctx; 42 if (!(ctx->basepath = strdup(base))) 43 goto free_ctx; 44 return ctx; 45 free_ctx: 46 free(ctx); 47 return NULL; 48 } 49 50 static 51 int 52 ploticus_new_plot_hist(struct ploticus_plot *plot) 53 { 54 prop_dictionary_t params = plot->params; 55 prop_string_t str; 56 57 if (!(str = prop_string_create_cstring_nocopy("1"))) 58 return !0; 59 if (!prop_dictionary_set(params, "x", str)) { 60 prop_object_release(str); 61 return !0; 62 } 63 if (!(str = prop_string_create_cstring_nocopy("yes"))) 64 return !0; 65 if (!prop_dictionary_set(params, "curve", str)) { 66 prop_object_release(str); 67 return !0; 68 } 69 return 0; 70 } 71 72 static 73 int 74 ploticus_new_plot_line(struct ploticus_plot *plot) 75 { 76 prop_dictionary_t params = plot->params; 77 prop_string_t str; 78 79 if (!(str = prop_string_create_cstring_nocopy("1"))) 80 return !0; 81 if (!prop_dictionary_set(params, "x", str)) { 82 prop_object_release(str); 83 return !0; 84 } 85 if (!(str = prop_string_create_cstring_nocopy("2"))) 86 return !0; 87 if (!prop_dictionary_set(params, "y", str)) { 88 prop_object_release(str); 89 return !0; 90 } 91 return 0; 92 } 93 94 int (*plot_type_initializers[])(struct ploticus_plot *) = { 95 [PLOT_TYPE_HIST] = ploticus_new_plot_hist, 96 [PLOT_TYPE_LINE] = ploticus_new_plot_line, 97 }; 98 99 static 100 plotid_t 101 ploticus_new_plot(void *_ctx, enum plot_type type, const char *title) 102 { 103 struct ploticus_plot *plot; 104 prop_dictionary_t params; 105 prop_string_t str; 106 struct ploticus_plotter *ctx = _ctx; 107 struct ploticus_plot **tmp; 108 char *datapath; 109 110 if ((type <= PLOT_TYPE_START) || (type >= PLOT_TYPE_END)) 111 return -1; 112 if (!(tmp = realloc(ctx->plots, sizeof(struct ploticus_plot *) * 113 (ctx->nr_plots + 1)))) 114 return -1; 115 ctx->plots = tmp; 116 117 if (!(params = prop_dictionary_create())) 118 return -1; 119 if (!(plot = calloc(1, sizeof(*plot)))) 120 goto free_params; 121 plot->params = params; 122 plot->type = type; 123 124 if (asprintf(&plot->path, "%s-%d-%s", ctx->basepath, type, 125 title) < 0) 126 goto free_plot; 127 if (asprintf(&datapath, "%s.data", plot->path) < 0) 128 goto free_path; 129 130 if (!(str = prop_string_create_cstring(title))) 131 goto free_datapath; 132 if (!prop_dictionary_set(params, "title", str)) { 133 prop_object_release(str); 134 goto free_datapath; 135 } 136 if (!(str = prop_string_create_cstring(datapath))) 137 goto free_datapath; 138 if (!prop_dictionary_set(params, "data", str)) { 139 prop_object_release(str); 140 goto free_datapath; 141 } 142 143 if (plot_type_initializers[type](plot)) 144 goto free_datapath; 145 if (!(plot->fp = fopen(datapath, "w"))) { 146 goto free_datapath; 147 } 148 free(datapath); 149 ctx->plots[ctx->nr_plots] = plot; 150 return ctx->nr_plots++; 151 152 free_datapath: 153 free(datapath); 154 free_path: 155 free(plot->path); 156 free_plot: 157 free(plot); 158 free_params: 159 prop_object_release(params); 160 return -1; 161 } 162 163 static 164 int 165 ploticus_plot_histogram(void *_ctx, plotid_t id, double val) 166 { 167 struct ploticus_plotter *ctx = _ctx; 168 struct ploticus_plot *plot; 169 170 if ((id < 0) || (id >= ctx->nr_plots)) 171 return ERANGE; 172 plot = ctx->plots[id]; 173 assert(plot != NULL); 174 175 fprintf(plot->fp, "%lf\n", val); 176 177 return 0; 178 } 179 180 static 181 int 182 ploticus_plot_line(void *_ctx, plotid_t id, double x, double y) 183 { 184 struct ploticus_plotter *ctx = _ctx; 185 struct ploticus_plot *plot; 186 187 if ((id < 0) || (id >= ctx->nr_plots)) 188 return ERANGE; 189 plot = ctx->plots[id]; 190 assert(plot != NULL); 191 192 fprintf(plot->fp, "%lf %lf\n", x, y); 193 194 return 0; 195 } 196 197 extern char **environ; 198 199 static 200 void 201 ploticus_run(struct ploticus_plot *plot) 202 { 203 unsigned nr_params; 204 const char **pl_argv; 205 prop_object_iterator_t it; 206 prop_object_t key, val; 207 const char *keystr; 208 const char *output_format = "-svg"; 209 int i; 210 211 printd(PLOT, "ploticus_run\n"); 212 nr_params = prop_dictionary_count(plot->params); 213 if (!(pl_argv = calloc(nr_params + 214 1 + /* progname */ 215 1 + /* trailing NULL */ 216 1 + /* -prefab */ 217 1 + /* dist */ 218 1 + /* output format */ 219 1 + /* -o */ 220 1 /* outpath */ 221 , sizeof(char *)))) 222 err(1, "can't allocate argv"); 223 if (!(it = prop_dictionary_iterator(plot->params))) 224 err(1, "can't allocate dictionary iterator"); 225 pl_argv[0] = "ploticus"; 226 pl_argv[1] = "-prefab"; 227 pl_argv[2] = plot_prefabs[plot->type]; 228 pl_argv[2 + nr_params + 1] = output_format; 229 pl_argv[2 + nr_params + 2] = "-o"; 230 if (asprintf(__DECONST(char **, &pl_argv[2 + nr_params + 3]), 231 "%s.svg", plot->path) < 0) 232 err(1, "Can't allocate args"); 233 key = prop_object_iterator_next(it); 234 for (i = 3; key; ++i, key = prop_object_iterator_next(it)) { 235 keystr = prop_dictionary_keysym_cstring_nocopy(key); 236 assert(keystr != NULL); 237 val = prop_dictionary_get_keysym(plot->params, key); 238 assert(val != NULL); 239 printd(PLOT, "%s=%s\n", keystr, 240 prop_string_cstring_nocopy(val)); 241 if (asprintf(__DECONST(char **, &pl_argv[i]), "%s=%s", keystr, 242 prop_string_cstring_nocopy(val)) < 0) 243 err(1, "can't allocate exec arguments"); 244 } 245 prop_object_iterator_release(it); 246 printd(PLOT, "about to exec with args:\n"); 247 for (i = 0; pl_argv[i]; ++i) 248 printd(PLOT, "%s\n", pl_argv[i]); 249 execve("/usr/local/bin/ploticus", __DECONST(char * const *, pl_argv), environ); 250 err(1, "failed to exec ploticus"); 251 } 252 253 static 254 int 255 ploticus_plot_generate(struct ploticus_plot *plot) 256 { 257 pid_t pid; 258 int status; 259 260 fclose(plot->fp); 261 262 switch ((pid = fork())) { 263 case -1: 264 return -1; 265 case 0: /* child */ 266 ploticus_run(plot); 267 assert(!"can't get here"); 268 } 269 /* parent */ 270 if (waitpid(pid, &status, 0) != pid) 271 err(1, "waitpid() failed"); 272 if (!WIFEXITED(status)) 273 warn("ploticus did not exit!"); 274 if (WEXITSTATUS(status)) 275 warn("ploticus did not run successfully"); 276 return 0; 277 } 278 279 static 280 int 281 ploticus_plot_finish(void *_ctx) 282 { 283 struct ploticus_plotter *ctx = _ctx; 284 int i; 285 286 for (i = 0; i < ctx->nr_plots; ++i) { 287 if (ploticus_plot_generate(ctx->plots[i])) 288 return -1; 289 } 290 return 0; 291 } 292 293 static struct plotter ploticus_plotter = { 294 .plot_init = ploticus_init, 295 .plot_new = ploticus_new_plot, 296 .plot_histogram = ploticus_plot_histogram, 297 .plot_line = ploticus_plot_line, 298 .plot_finish = ploticus_plot_finish, 299 }; 300 301 static const char *ploticus_path = "/usr/local/bin/ploticus"; 302 303 struct plotter * 304 plotter_factory(void) 305 { 306 struct stat st; 307 if ((!stat(ploticus_path, &st)) && 308 S_ISREG(st.st_mode) && 309 (st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))) 310 return &ploticus_plotter; 311 warnx("%s does not exist or is not an executable file", ploticus_path); 312 return NULL; 313 } 314