/*- * See the file LICENSE for redistribution information. * * Copyright (c) 2005,2008 Oracle. All rights reserved. * * $Id: code.c,v 1.16 2008/01/08 20:58:23 bostic Exp $ */ #include "csv.h" typedef struct { char *name; /* Field name */ char *upper; /* Field name in upper-case */ datatype type; /* Data type */ int indx; /* Index */ } FIELD; int code_source(void); int code_header(void); int desc_dump(void); int desc_load(void); char *type_to_string(datatype); int usage(void); /* * Globals */ FILE *cfp; /* C source file */ FILE *hfp; /* C source file */ char *progname; /* Program name */ int verbose; /* Verbose flag */ u_int field_cnt; /* Count of fields */ FIELD *fields; /* Field list */ int main(int argc, char *argv[]) { int ch; char *cfile, *hfile; /* Initialize globals. */ if ((progname = strrchr(argv[0], '/')) == NULL) progname = argv[0]; else ++progname; /* Initialize arguments. */ cfile = "csv_local.c"; /* Default header/source files */ hfile = "csv_local.h"; /* Process arguments. */ while ((ch = getopt(argc, argv, "c:f:h:v")) != EOF) switch (ch) { case 'c': cfile = optarg; break; case 'f': if (freopen(optarg, "r", stdin) == NULL) { fprintf(stderr, "%s: %s\n", optarg, strerror(errno)); return (EXIT_FAILURE); } break; case 'h': hfile = optarg; break; case 'v': ++verbose; break; case '?': default: return (usage()); } argc -= optind; argv += optind; if (*argv != NULL) return (usage()); /* Load records from the input file. */ if (desc_load()) return (EXIT_FAILURE); /* Dump records for debugging. */ if (verbose && desc_dump()) return (EXIT_FAILURE); /* Open output files. */ if ((cfp = fopen(cfile, "w")) == NULL) { fprintf(stderr, "%s: %s: %s\n", progname, cfile, strerror(errno)); return (EXIT_FAILURE); } if ((hfp = fopen(hfile, "w")) == NULL) { fprintf(stderr, "%s: %s: %s\n", progname, hfile, strerror(errno)); return (EXIT_FAILURE); } /* Build the source and header files. */ if (code_header()) return (EXIT_FAILURE); if (code_source()) return (EXIT_FAILURE); return (EXIT_SUCCESS); } /* * desc_load -- * Load a description file. */ int desc_load() { u_int field_alloc; int version; char *p, *t, save_ch, buf[256]; field_alloc = version = 0; while (fgets(buf, sizeof(buf), stdin) != NULL) { if ((p = strchr(buf, '\n')) == NULL) { fprintf(stderr, "%s: input line too long\n", progname); return (1); } *p = '\0'; /* Skip leading whitespace. */ for (p = buf; isspace(*p); ++p) ; /* Skip empty lines or lines beginning with '#'. */ if (*p == '\0' || *p == '#') continue; /* Get a version. */ if (!version) { if (strncasecmp( p, "version", sizeof("version") - 1) == 0) { version = 1; continue; } fprintf(stderr, "%s: expected \"version\" line\n", progname); return (1); } /* * Skip block close -- not currently useful, but when this * code supports versioned descriptions, it will matter. */ if (*p == '}') { version = 0; continue; } /* Allocate a new field structure as necessary. */ if (field_cnt == field_alloc && (fields = realloc(fields, field_alloc += 100)) == NULL) { fprintf(stderr, "%s: %s\n", progname, strerror(errno)); return (1); } /* Find the end of the field name. */ for (t = p; *t != '\0' && !isspace(*t); ++t) ; save_ch = *t; *t = '\0'; if ((fields[field_cnt].name = strdup(p)) == NULL || (fields[field_cnt].upper = strdup(p)) == NULL) { fprintf(stderr, "%s: %s\n", progname, strerror(errno)); return (1); } *t = save_ch; p = t; fields[field_cnt].indx = 0; fields[field_cnt].type = NOTSET; for (;;) { /* Skip to the next field, if any. */ for (; *p != '\0' && isspace(*p); ++p) ; if (*p == '\0') break; /* Find the end of the field. */ for (t = p; *t != '\0' && !isspace(*t); ++t) ; save_ch = *t; *t = '\0'; if (strcasecmp(p, "double") == 0) fields[field_cnt].type = DOUBLE; else if (strcasecmp(p, "index") == 0) fields[field_cnt].indx = 1; else if (strcasecmp(p, "string") == 0) fields[field_cnt].type = STRING; else if (strcasecmp(p, "unsigned_long") == 0) fields[field_cnt].type = UNSIGNED_LONG; else { fprintf(stderr, "%s: unknown keyword: %s\n", progname, p); return (1); } *t = save_ch; p = t; } /* Create a copy of the field name that's upper-case. */ for (p = fields[field_cnt].upper; *p != '\0'; ++p) if (islower(*p)) *p = (char)toupper(*p); ++field_cnt; } if (ferror(stdin)) { fprintf(stderr, "%s: stdin: %s\n", progname, strerror(errno)); return (1); } return (0); } /* * desc_dump -- * Dump a set of FIELD structures. */ int desc_dump() { FIELD *f; u_int i; for (f = fields, i = 0; i < field_cnt; ++i, ++f) { fprintf(stderr, "field {%s}: (", f->name); switch (f->type) { case NOTSET: fprintf(stderr, "ignored"); break; case DOUBLE: fprintf(stderr, "double"); break; case STRING: fprintf(stderr, "string"); break; case UNSIGNED_LONG: fprintf(stderr, "unsigned_long"); break; } if (f->indx) fprintf(stderr, ", indexed"); fprintf(stderr, ")\n"); } return (0); } /* * code_header -- * Print out the C #include file. */ int code_header() { FIELD *f; u_int i; fprintf(hfp, "/*\n"); fprintf(hfp, " * DO NOT EDIT: automatically built by %s.\n", progname); fprintf(hfp, " *\n"); fprintf(hfp, " * Record structure.\n"); fprintf(hfp, " */\n"); fprintf(hfp, "typedef struct __DbRecord {\n"); fprintf(hfp, "\tu_int32_t\t recno;\t\t/* Record number */\n"); fprintf(hfp, "\n"); fprintf(hfp, "\t/*\n"); fprintf(hfp, "\t * Management fields\n"); fprintf(hfp, "\t */\n"); fprintf(hfp, "\tvoid\t\t*raw;\t\t/* Memory returned by DB */\n"); fprintf(hfp, "\tu_char\t\t*record;\t/* Raw record */\n"); fprintf(hfp, "\tsize_t\t\t record_len;\t/* Raw record length */\n\n"); fprintf(hfp, "\tu_int32_t\t field_count;\t/* Field count */\n"); fprintf(hfp, "\tu_int32_t\t version;\t/* Record version */\n\n"); fprintf(hfp, "\tu_int32_t\t*offset;\t/* Offset table */\n"); fprintf(hfp, "\n"); fprintf(hfp, "\t/*\n"); fprintf(hfp, "\t * Indexed fields\n"); fprintf(hfp, "\t */\n"); for (f = fields, i = 0; i < field_cnt; ++i, ++f) { if (f->type == NOTSET) continue; if (i != 0) fprintf(hfp, "\n"); fprintf(hfp, "#define CSV_INDX_%s\t%d\n", f->upper, i + 1); switch (f->type) { case NOTSET: /* NOTREACHED */ abort(); break; case DOUBLE: fprintf(hfp, "\tdouble\t\t %s;\n", f->name); break; case STRING: fprintf(hfp, "\tchar\t\t*%s;\n", f->name); break; case UNSIGNED_LONG: fprintf(hfp, "\tu_long\t\t %s;\n", f->name); break; } } fprintf(hfp, "} DbRecord;\n"); return (0); } /* * code_source -- * Print out the C structure initialization. */ int code_source() { FIELD *f; u_int i; fprintf(cfp, "/*\n"); fprintf(cfp, " * DO NOT EDIT: automatically built by %s.\n", progname); fprintf(cfp, " *\n"); fprintf(cfp, " * Initialized record structure.\n"); fprintf(cfp, " */\n"); fprintf(cfp, "\n"); fprintf(cfp, "#include \"csv.h\"\n"); fprintf(cfp, "#include \"csv_local.h\"\n"); fprintf(cfp, "\n"); fprintf(cfp, "DbRecord DbRecord_base = {\n"); fprintf(cfp, "\t0,\t\t/* Record number */\n"); fprintf(cfp, "\tNULL,\t\t/* Memory returned by DB */\n"); fprintf(cfp, "\tNULL,\t\t/* Raw record */\n"); fprintf(cfp, "\t0,\t\t/* Raw record length */\n"); fprintf(cfp, "\t%d,\t\t/* Field count */\n", field_cnt); fprintf(cfp, "\t0,\t\t/* Record version */\n"); fprintf(cfp, "\tNULL,\t\t/* Offset table */\n"); fprintf(cfp, "\n"); for (f = fields, i = 0; i < field_cnt; ++i, ++f) { if (f->type == NOTSET) continue; switch (f->type) { case NOTSET: abort(); /* NOTREACHED */ break; case DOUBLE: case UNSIGNED_LONG: fprintf(cfp, "\t0,\t\t/* %s */\n", f->name); break; case STRING: fprintf(cfp, "\tNULL,\t\t/* %s */\n", f->name); break; } } fprintf(cfp, "};\n"); fprintf(cfp, "\n"); fprintf(cfp, "DbField fieldlist[] = {\n"); for (f = fields, i = 0; i < field_cnt; ++i, ++f) { if (f->type == NOTSET) continue; fprintf(cfp, "\t{ \"%s\",", f->name); fprintf(cfp, " CSV_INDX_%s,", f->upper); fprintf(cfp, "\n\t %s,", type_to_string(f->type)); fprintf(cfp, " %d,", f->indx ? 1 : 0); fprintf(cfp, " NULL,"); fprintf(cfp, " FIELD_OFFSET(%s)},\n", f->name); } fprintf(cfp, "\t{NULL, 0, STRING, 0, NULL, 0}\n};\n"); return (0); } char * type_to_string(type) datatype type; { switch (type) { case NOTSET: return ("NOTSET"); case DOUBLE: return ("DOUBLE"); case STRING: return ("STRING"); case UNSIGNED_LONG: return ("UNSIGNED_LONG"); } abort(); /* NOTREACHED */ } int usage() { (void)fprintf(stderr, "usage: %s [-v] [-c source-file] [-f input] [-h header-file]\n", progname); exit(1); }