1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 2005,2008 Oracle. All rights reserved. 5 * 6 * $Id: code.c,v 1.16 2008/01/08 20:58:23 bostic Exp $ 7 */ 8 9#include "csv.h" 10 11typedef struct { 12 char *name; /* Field name */ 13 char *upper; /* Field name in upper-case */ 14 datatype type; /* Data type */ 15 int indx; /* Index */ 16} FIELD; 17 18int code_source(void); 19int code_header(void); 20int desc_dump(void); 21int desc_load(void); 22char *type_to_string(datatype); 23int usage(void); 24 25/* 26 * Globals 27 */ 28FILE *cfp; /* C source file */ 29FILE *hfp; /* C source file */ 30char *progname; /* Program name */ 31int verbose; /* Verbose flag */ 32 33u_int field_cnt; /* Count of fields */ 34FIELD *fields; /* Field list */ 35 36int 37main(int argc, char *argv[]) 38{ 39 int ch; 40 char *cfile, *hfile; 41 42 /* Initialize globals. */ 43 if ((progname = strrchr(argv[0], '/')) == NULL) 44 progname = argv[0]; 45 else 46 ++progname; 47 48 /* Initialize arguments. */ 49 cfile = "csv_local.c"; /* Default header/source files */ 50 hfile = "csv_local.h"; 51 52 /* Process arguments. */ 53 while ((ch = getopt(argc, argv, "c:f:h:v")) != EOF) 54 switch (ch) { 55 case 'c': 56 cfile = optarg; 57 break; 58 case 'f': 59 if (freopen(optarg, "r", stdin) == NULL) { 60 fprintf(stderr, 61 "%s: %s\n", optarg, strerror(errno)); 62 return (EXIT_FAILURE); 63 } 64 break; 65 case 'h': 66 hfile = optarg; 67 break; 68 case 'v': 69 ++verbose; 70 break; 71 case '?': 72 default: 73 return (usage()); 74 } 75 argc -= optind; 76 argv += optind; 77 78 if (*argv != NULL) 79 return (usage()); 80 81 /* Load records from the input file. */ 82 if (desc_load()) 83 return (EXIT_FAILURE); 84 85 /* Dump records for debugging. */ 86 if (verbose && desc_dump()) 87 return (EXIT_FAILURE); 88 89 /* Open output files. */ 90 if ((cfp = fopen(cfile, "w")) == NULL) { 91 fprintf(stderr, 92 "%s: %s: %s\n", progname, cfile, strerror(errno)); 93 return (EXIT_FAILURE); 94 } 95 if ((hfp = fopen(hfile, "w")) == NULL) { 96 fprintf(stderr, 97 "%s: %s: %s\n", progname, hfile, strerror(errno)); 98 return (EXIT_FAILURE); 99 } 100 101 /* Build the source and header files. */ 102 if (code_header()) 103 return (EXIT_FAILURE); 104 if (code_source()) 105 return (EXIT_FAILURE); 106 107 return (EXIT_SUCCESS); 108} 109 110/* 111 * desc_load -- 112 * Load a description file. 113 */ 114int 115desc_load() 116{ 117 u_int field_alloc; 118 int version; 119 char *p, *t, save_ch, buf[256]; 120 121 field_alloc = version = 0; 122 while (fgets(buf, sizeof(buf), stdin) != NULL) { 123 if ((p = strchr(buf, '\n')) == NULL) { 124 fprintf(stderr, "%s: input line too long\n", progname); 125 return (1); 126 } 127 *p = '\0'; 128 129 /* Skip leading whitespace. */ 130 for (p = buf; isspace(*p); ++p) 131 ; 132 133 /* Skip empty lines or lines beginning with '#'. */ 134 if (*p == '\0' || *p == '#') 135 continue; 136 137 /* Get a version. */ 138 if (!version) { 139 if (strncasecmp( 140 p, "version", sizeof("version") - 1) == 0) { 141 version = 1; 142 continue; 143 } 144 fprintf(stderr, 145 "%s: expected \"version\" line\n", progname); 146 return (1); 147 } 148 149 /* 150 * Skip block close -- not currently useful, but when this 151 * code supports versioned descriptions, it will matter. 152 */ 153 if (*p == '}') { 154 version = 0; 155 continue; 156 } 157 158 /* Allocate a new field structure as necessary. */ 159 if (field_cnt == field_alloc && 160 (fields = realloc(fields, field_alloc += 100)) == NULL) { 161 fprintf(stderr, "%s: %s\n", progname, strerror(errno)); 162 return (1); 163 } 164 165 /* Find the end of the field name. */ 166 for (t = p; *t != '\0' && !isspace(*t); ++t) 167 ; 168 save_ch = *t; 169 *t = '\0'; 170 if ((fields[field_cnt].name = strdup(p)) == NULL || 171 (fields[field_cnt].upper = strdup(p)) == NULL) { 172 fprintf(stderr, "%s: %s\n", progname, strerror(errno)); 173 return (1); 174 } 175 *t = save_ch; 176 p = t; 177 178 fields[field_cnt].indx = 0; 179 fields[field_cnt].type = NOTSET; 180 for (;;) { 181 /* Skip to the next field, if any. */ 182 for (; *p != '\0' && isspace(*p); ++p) 183 ; 184 if (*p == '\0') 185 break; 186 187 /* Find the end of the field. */ 188 for (t = p; *t != '\0' && !isspace(*t); ++t) 189 ; 190 save_ch = *t; 191 *t = '\0'; 192 if (strcasecmp(p, "double") == 0) 193 fields[field_cnt].type = DOUBLE; 194 else if (strcasecmp(p, "index") == 0) 195 fields[field_cnt].indx = 1; 196 else if (strcasecmp(p, "string") == 0) 197 fields[field_cnt].type = STRING; 198 else if (strcasecmp(p, "unsigned_long") == 0) 199 fields[field_cnt].type = UNSIGNED_LONG; 200 else { 201 fprintf(stderr, 202 "%s: unknown keyword: %s\n", progname, p); 203 return (1); 204 } 205 *t = save_ch; 206 p = t; 207 } 208 209 /* Create a copy of the field name that's upper-case. */ 210 for (p = fields[field_cnt].upper; *p != '\0'; ++p) 211 if (islower(*p)) 212 *p = (char)toupper(*p); 213 ++field_cnt; 214 } 215 if (ferror(stdin)) { 216 fprintf(stderr, "%s: stdin: %s\n", progname, strerror(errno)); 217 return (1); 218 } 219 return (0); 220} 221 222/* 223 * desc_dump -- 224 * Dump a set of FIELD structures. 225 */ 226int 227desc_dump() 228{ 229 FIELD *f; 230 u_int i; 231 232 for (f = fields, i = 0; i < field_cnt; ++i, ++f) { 233 fprintf(stderr, "field {%s}: (", f->name); 234 switch (f->type) { 235 case NOTSET: 236 fprintf(stderr, "ignored"); 237 break; 238 case DOUBLE: 239 fprintf(stderr, "double"); 240 break; 241 case STRING: 242 fprintf(stderr, "string"); 243 break; 244 case UNSIGNED_LONG: 245 fprintf(stderr, "unsigned_long"); 246 break; 247 } 248 if (f->indx) 249 fprintf(stderr, ", indexed"); 250 fprintf(stderr, ")\n"); 251 } 252 return (0); 253} 254 255/* 256 * code_header -- 257 * Print out the C #include file. 258 */ 259int 260code_header() 261{ 262 FIELD *f; 263 u_int i; 264 265 fprintf(hfp, "/*\n"); 266 fprintf(hfp, " * DO NOT EDIT: automatically built by %s.\n", progname); 267 fprintf(hfp, " *\n"); 268 fprintf(hfp, " * Record structure.\n"); 269 fprintf(hfp, " */\n"); 270 fprintf(hfp, "typedef struct __DbRecord {\n"); 271 fprintf(hfp, "\tu_int32_t\t recno;\t\t/* Record number */\n"); 272 fprintf(hfp, "\n"); 273 fprintf(hfp, "\t/*\n"); 274 fprintf(hfp, "\t * Management fields\n"); 275 fprintf(hfp, "\t */\n"); 276 fprintf(hfp, "\tvoid\t\t*raw;\t\t/* Memory returned by DB */\n"); 277 fprintf(hfp, "\tu_char\t\t*record;\t/* Raw record */\n"); 278 fprintf(hfp, "\tsize_t\t\t record_len;\t/* Raw record length */\n\n"); 279 fprintf(hfp, "\tu_int32_t\t field_count;\t/* Field count */\n"); 280 fprintf(hfp, "\tu_int32_t\t version;\t/* Record version */\n\n"); 281 fprintf(hfp, "\tu_int32_t\t*offset;\t/* Offset table */\n"); 282 fprintf(hfp, "\n"); 283 284 fprintf(hfp, "\t/*\n"); 285 fprintf(hfp, "\t * Indexed fields\n"); 286 fprintf(hfp, "\t */\n"); 287 for (f = fields, i = 0; i < field_cnt; ++i, ++f) { 288 if (f->type == NOTSET) 289 continue; 290 if (i != 0) 291 fprintf(hfp, "\n"); 292 fprintf(hfp, "#define CSV_INDX_%s\t%d\n", f->upper, i + 1); 293 switch (f->type) { 294 case NOTSET: 295 /* NOTREACHED */ 296 abort(); 297 break; 298 case DOUBLE: 299 fprintf(hfp, "\tdouble\t\t %s;\n", f->name); 300 break; 301 case STRING: 302 fprintf(hfp, "\tchar\t\t*%s;\n", f->name); 303 break; 304 case UNSIGNED_LONG: 305 fprintf(hfp, "\tu_long\t\t %s;\n", f->name); 306 break; 307 } 308 } 309 fprintf(hfp, "} DbRecord;\n"); 310 311 return (0); 312} 313 314/* 315 * code_source -- 316 * Print out the C structure initialization. 317 */ 318int 319code_source() 320{ 321 FIELD *f; 322 u_int i; 323 324 fprintf(cfp, "/*\n"); 325 fprintf(cfp, 326 " * DO NOT EDIT: automatically built by %s.\n", progname); 327 fprintf(cfp, " *\n"); 328 fprintf(cfp, " * Initialized record structure.\n"); 329 fprintf(cfp, " */\n"); 330 fprintf(cfp, "\n"); 331 fprintf(cfp, "#include \"csv.h\"\n"); 332 fprintf(cfp, "#include \"csv_local.h\"\n"); 333 fprintf(cfp, "\n"); 334 fprintf(cfp, "DbRecord DbRecord_base = {\n"); 335 fprintf(cfp, "\t0,\t\t/* Record number */\n"); 336 fprintf(cfp, "\tNULL,\t\t/* Memory returned by DB */\n"); 337 fprintf(cfp, "\tNULL,\t\t/* Raw record */\n"); 338 fprintf(cfp, "\t0,\t\t/* Raw record length */\n"); 339 fprintf(cfp, "\t%d,\t\t/* Field count */\n", field_cnt); 340 fprintf(cfp, "\t0,\t\t/* Record version */\n"); 341 fprintf(cfp, "\tNULL,\t\t/* Offset table */\n"); 342 fprintf(cfp, "\n"); 343 for (f = fields, i = 0; i < field_cnt; ++i, ++f) { 344 if (f->type == NOTSET) 345 continue; 346 switch (f->type) { 347 case NOTSET: 348 abort(); 349 /* NOTREACHED */ 350 break; 351 case DOUBLE: 352 case UNSIGNED_LONG: 353 fprintf(cfp, "\t0,\t\t/* %s */\n", f->name); 354 break; 355 case STRING: 356 fprintf(cfp, "\tNULL,\t\t/* %s */\n", f->name); 357 break; 358 } 359 } 360 fprintf(cfp, "};\n"); 361 362 fprintf(cfp, "\n"); 363 fprintf(cfp, "DbField fieldlist[] = {\n"); 364 for (f = fields, i = 0; i < field_cnt; ++i, ++f) { 365 if (f->type == NOTSET) 366 continue; 367 fprintf(cfp, "\t{ \"%s\",", f->name); 368 fprintf(cfp, " CSV_INDX_%s,", f->upper); 369 fprintf(cfp, "\n\t %s,", type_to_string(f->type)); 370 fprintf(cfp, " %d,", f->indx ? 1 : 0); 371 fprintf(cfp, " NULL,"); 372 fprintf(cfp, " FIELD_OFFSET(%s)},\n", f->name); 373 } 374 fprintf(cfp, "\t{NULL, 0, STRING, 0, NULL, 0}\n};\n"); 375 376 return (0); 377} 378 379char * 380type_to_string(type) 381 datatype type; 382{ 383 switch (type) { 384 case NOTSET: 385 return ("NOTSET"); 386 case DOUBLE: 387 return ("DOUBLE"); 388 case STRING: 389 return ("STRING"); 390 case UNSIGNED_LONG: 391 return ("UNSIGNED_LONG"); 392 } 393 394 abort(); 395 /* NOTREACHED */ 396} 397 398int 399usage() 400{ 401 (void)fprintf(stderr, 402 "usage: %s [-v] [-c source-file] [-f input] [-h header-file]\n", 403 progname); 404 exit(1); 405} 406