vtk-data.c (6680B)
1 /* Copyright (C) 2020-2023, 2025 |Méso|Star> (contact@meso-star.com) 2 * 3 * This program is free software: you can redistribute it and/or modify 4 * it under the terms of the GNU General Public License as published by 5 * the Free Software Foundation, either version 3 of the License, or 6 * (at your option) any later version. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 * 13 * You should have received a copy of the GNU General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. */ 15 16 #define _POSIX_C_SOURCE 200112L /* getopt support */ 17 18 #include <rsys/cstr.h> 19 #include <rsys/mem_allocator.h> 20 #include <rsys/str.h> 21 #include <rsys/text_reader.h> 22 23 #include <errno.h> 24 #include <string.h> 25 #include <unistd.h> /* getopt */ 26 27 #define MAX_DATA 8 28 29 /* Input arguments */ 30 struct args { 31 const char* output; 32 const char* data; 33 unsigned long nitems; 34 const char* names[MAX_DATA]; 35 unsigned ndata; 36 }; 37 static const struct args ARGS_DEFAULT = {0}; 38 39 /* Command data */ 40 struct cmd { 41 const char* names[MAX_DATA]; 42 const char* data_name; 43 FILE* output; 44 FILE* data; 45 size_t nitems; 46 }; 47 static const struct cmd CMD_NULL = {0}; 48 49 /******************************************************************************* 50 * Helper functions 51 ******************************************************************************/ 52 static INLINE void 53 usage(FILE* stream) 54 { 55 fprintf(stream, 56 "usage: vtk-data [-n name ...] [-o output] -c cell_count [data]\n"); 57 } 58 59 static res_T 60 parse_name(struct args* args, const char* name) 61 { 62 unsigned i; 63 ASSERT(name); 64 65 if(args->ndata >= MAX_DATA) { 66 fprintf(stderr, "too many names\n"); 67 return RES_BAD_ARG; 68 } 69 70 /* Names cannot contain white spaces */ 71 if(strcspn(name, " \t") != strlen(name)) { 72 fprintf(stderr, "name \"%s\" cannot contain white spaces \n", name); 73 return RES_BAD_ARG; 74 } 75 76 /* Names must be unique */ 77 FOR_EACH(i, 0, args->ndata) { 78 if(!strcmp(args->names[i], name)) { 79 fprintf(stderr, "name \"%s\" already defined\n", name); 80 return RES_BAD_ARG; 81 } 82 } 83 84 args->names[args->ndata++] = name; 85 return RES_OK; 86 } 87 88 static res_T 89 args_init(struct args* args, const int argc, char** argv) 90 { 91 int opt = 0; 92 res_T res = RES_OK; 93 94 *args = ARGS_DEFAULT; 95 96 while((opt = getopt(argc, argv, "o:c:n:")) != -1) { 97 switch(opt) { 98 case 'n': res = parse_name(args, optarg); break; 99 case 'o': args->output = optarg; break; 100 case 'c': 101 res = cstr_to_ulong(optarg, &args->nitems); 102 if(res == RES_OK && args->nitems == 0) res = RES_BAD_ARG; 103 break; 104 default: res = RES_BAD_ARG; goto error; 105 } 106 if(res != RES_OK) goto error; 107 } 108 109 if(optind < argc) args->data = argv[optind]; 110 111 if(!args->nitems) { 112 fprintf(stderr, "missing item count -- option '-c'\n"); 113 res = RES_BAD_ARG; 114 goto error; 115 } 116 117 exit: 118 return res; 119 error: 120 usage(stderr); 121 goto exit; 122 } 123 124 static void 125 cmd_release(struct cmd* cmd) 126 { 127 if(cmd->data && cmd->data != stdin) CHK(!fclose(cmd->data)); 128 if(cmd->output && cmd->output != stdout) CHK(!fclose(cmd->output)); 129 } 130 131 static res_T 132 cmd_init(struct cmd* cmd, const struct args* args) 133 { 134 res_T res = RES_OK; 135 ASSERT(cmd && args); 136 137 if(!(cmd->output = args->output ? fopen(args->output, "w") : stdout)) { 138 perror(args->output); 139 res = RES_IO_ERR; 140 goto error; 141 } 142 143 if(!(cmd->data = args->data ? fopen(args->data, "r") : stdin)) { 144 perror(args->data); 145 res = RES_IO_ERR; 146 goto error; 147 } 148 149 cmd->data_name = args->data ? args->data : "stdin"; 150 memcpy(cmd->names, args->names, sizeof(char*[MAX_DATA])); 151 cmd->nitems = args->nitems; 152 153 exit: 154 return res; 155 error: 156 cmd_release(cmd); 157 goto exit; 158 } 159 160 static res_T 161 write_data(struct cmd* cmd, const int idata, struct txtrdr* txtrdr) 162 { 163 size_t i = 0; 164 int res = RES_OK; 165 166 /* Pre-conditions */ 167 ASSERT(cmd && idata < MAX_DATA && txtrdr); 168 ASSERT(txtrdr_get_line(txtrdr)); 169 170 if(cmd->names[idata]) { 171 res = fprintf(cmd->output, "SCALARS %s double 1\n", cmd->names[idata]); 172 } else { 173 /* Make default name unique */ 174 res = fprintf(cmd->output, "SCALARS data%d double 1\n", idata); 175 } 176 if(res < 0) { 177 perror(NULL); 178 res = RES_IO_ERR; 179 goto error; 180 } 181 182 if(fprintf(cmd->output, "LOOKUP_TABLE default\n") < 0) { 183 perror(NULL); 184 res = RES_IO_ERR; 185 goto error; 186 } 187 188 FOR_EACH(i, 0, cmd->nitems) { 189 const char* line = NULL; 190 double f = 0; 191 192 if(!(line = txtrdr_get_line(txtrdr))) { 193 fprintf(stderr, "missing data\n"); 194 res = RES_BAD_ARG; 195 goto error; 196 } 197 198 if((sscanf(line, "%lf\n", &f)) != 1) { 199 fprintf(stderr, "%s:%lu: unable to read double\n", 200 txtrdr_get_name(txtrdr), txtrdr_get_line_num(txtrdr)); 201 res = RES_IO_ERR; 202 goto error; 203 } 204 205 if(fprintf(cmd->output, "%lf\n", f) < 0) { 206 perror(NULL); 207 res = RES_IO_ERR; 208 goto error; 209 } 210 211 if((res = txtrdr_read_line(txtrdr)) != RES_OK) { 212 fprintf(stderr, "%s\n", res_to_cstr(res)); 213 goto error; 214 } 215 } 216 217 exit: 218 return res; 219 error: 220 goto exit; 221 } 222 223 static res_T 224 cmd_run(struct cmd* cmd) 225 { 226 struct txtrdr* txtrdr = NULL; 227 int i = 0; 228 res_T res = RES_OK; 229 ASSERT(cmd); 230 231 res = txtrdr_stream(NULL, cmd->data, cmd->data_name, '#', &txtrdr); 232 if(res != RES_OK) { 233 fprintf(stderr, "%s\n", res_to_cstr(res)); 234 goto error; 235 } 236 237 if((res = txtrdr_read_line(txtrdr)) != RES_OK) { 238 fprintf(stderr, "%s\n", res_to_cstr(res)); 239 goto error; 240 } 241 242 if(!txtrdr_get_line(txtrdr)) goto exit; /* No data */ 243 244 if(fprintf(cmd->output, "CELL_DATA %zu\n", cmd->nitems) < 0) { 245 fprintf(stderr, "%s\n", strerror(errno)); 246 res = RES_IO_ERR; 247 goto error; 248 } 249 250 FOR_EACH(i, 0, MAX_DATA) { 251 if((res = write_data(cmd, i, txtrdr)) != RES_OK) goto error; 252 if(!txtrdr_get_line(txtrdr)) break; /* No more data */ 253 } 254 255 exit: 256 if(txtrdr) txtrdr_ref_put(txtrdr); 257 return res; 258 error: 259 goto exit; 260 } 261 262 /******************************************************************************* 263 * Main function 264 ******************************************************************************/ 265 int 266 main(int argc, char** argv) 267 { 268 struct args args = ARGS_DEFAULT; 269 struct cmd cmd = CMD_NULL; 270 int err = 0; 271 res_T res = RES_OK; 272 273 if((res = args_init(&args, argc, argv)) != RES_OK) goto error; 274 if((res = cmd_init(&cmd, &args)) != RES_OK) goto error; 275 if((res = cmd_run(&cmd)) != RES_OK) goto error; 276 277 exit: 278 cmd_release(&cmd); 279 CHK(mem_allocated_size() == 0); 280 return err; 281 error: 282 err = 1; 283 goto exit; 284 }