Mercurial > hg > dmlib
view tools/data2inc.c @ 2490:18ec4c092108
Fix dm_strncasecmp() edge case.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Tue, 28 Apr 2020 18:38:38 +0300 |
parents | f07e3e66ca58 |
children | c6ee41fd98dd |
line wrap: on
line source
/* * data2inc - Convert binary data to "C"-source or XA-compatible include file * Programmed and designed by Matti 'ccr' Hamalainen * (C) Copyright 2003,2009-2020 Tecnic Software productions (TNSP) * * Please read file 'COPYING' for information on license and distribution. */ #include "dmtool.h" #include "dmlib.h" #include "dmargs.h" #include "dmmutex.h" #define SET_DEF_LINELEN 16 #define SET_MAX_FEXTS 16 // // Types etc // typedef struct { char *name; char *desc; char *fexts[SET_MAX_FEXTS]; char *defDataType; int (*initContext) (void **pctx); int (*closeContext) (void *pctx); int (*writeHeader) (FILE *fh, void *ctx, const char *name); int (*writeDecl) (FILE *fh, void *ctx, const size_t len, const char *name); int (*writeData) (FILE *fh, void *ctx, const Uint8 *buf, const size_t len); int (*writeFooter) (FILE *fh, void *ctx, const size_t len, const char *name); } DMOutputFormat; // // Options // char *optInFilename = NULL, *optOutFilename = NULL, *optObjName = "default_object", *optDataType = NULL, *optAddLine = NULL; const DMOutputFormat *setFormat = NULL; int optIndentation = -1, optLineLen = SET_DEF_LINELEN; BOOL optHexMode = FALSE, optQuiet = FALSE, optExtraData = FALSE, optFormatting = TRUE; static const DMOptArg optList[] = { { 0, '?', "help" , "Show this help", OPT_NONE }, { 1, 0, "license" , "Print out this program's license agreement", OPT_NONE }, { 10, 'n', "name" , "Set object name", OPT_ARGREQ }, { 12, 't', "type" , "Set datatype (unsigned char/byte)", OPT_ARGREQ }, { 14, 'f', "format" , "Set output format (see list below)", OPT_ARGREQ }, { 16, 'a', "add-line" , "Add this line to start of file", OPT_ARGREQ }, { 18, 'l', "line-items" , "Set number of items per line", OPT_ARGREQ }, { 20, 'x', "hexadecimal" , "Use hexadecimal output", OPT_NONE }, { 22, 'q', "quiet" , "Do not add comments", OPT_NONE }, { 24, 'N', "no-formatting" , "Disable additional output formatting", OPT_NONE }, { 26, 'i', "indentation" , "Set indentation (negative value = tab)", OPT_ARGREQ }, { 28, 'e', "extra-data" , "Add object end labels and size in asm output", OPT_NONE }, }; static const int optListN = sizeof(optList) / sizeof(optList[0]); static const DMOutputFormat dmFormatList[]; static const int ndmFormatList; void argShowHelp() { dmPrintBanner(stdout, dmProgName, "[options] [sourcefile] [destfile]"); dmArgsPrintHelp(stdout, optList, optListN, 0, 80 - 2); fprintf(stdout, "\n" "Available output formats (-f <frmt>):\n" " frmt | Description (filename extensions)\n" "------+------------------------------------------\n"); for (int i = 0; i < ndmFormatList; i++) { const DMOutputFormat *fmt = &dmFormatList[i]; fprintf(stdout, "%-5s | %s (", fmt->name, fmt->desc); for (int n = 0; n < SET_MAX_FEXTS && fmt->fexts[n] != NULL; n++) { fprintf(stdout, ".%s%s", fmt->fexts[n], (n + 1 < SET_MAX_FEXTS && fmt->fexts[n + 1] != NULL) ? " " : ""); } fprintf(stdout, ")\n"); } fprintf(stdout, "\n" "To convert a data file to a C structure using 'Uint8' as type:\n" "$ data2inc -n variable_name -t Uint8 input.bin output.h\n" "\n" ); } BOOL argHandleOpt(const int optN, char *optArg, char *currArg) { switch (optN) { case 0: argShowHelp(); exit(0); break; case 1: dmPrintLicense(stdout); exit(0); break; case 10: optObjName = optArg; break; case 12: optDataType = optArg; break; case 14: for (int i = 0; i < ndmFormatList; i++) { const DMOutputFormat *fmt = &dmFormatList[i]; if (strcasecmp(fmt->name, optArg) == 0) { setFormat = fmt; return TRUE; } } dmErrorMsg("Invalid format name '%s'.\n", optArg); return FALSE; case 16: optAddLine = optArg; break; case 18: optLineLen = atoi(optArg); if (optLineLen < 1) { dmErrorMsg("Invalid line length / number of items per line '%s'.\n", optArg); return FALSE; } break; case 20: optHexMode = TRUE; break; case 22: optQuiet = TRUE; break; case 24: optFormatting = FALSE; break; case 26: optIndentation = atoi(optArg); break; case 28: optExtraData = TRUE; break; default: dmErrorMsg("Unimplemented option argument '%s'.\n", currArg); return FALSE; } return TRUE; } BOOL argHandleFile(char * currArg) { if (optInFilename == NULL) optInFilename = currArg; else if (optOutFilename == NULL) optOutFilename = currArg; else dmErrorMsg("Source and destination filenames already specified, extraneous argument '%s'.\n", currArg); return TRUE; } static void dmPrintIndentation(FILE *fh) { int level; const char *str; if (optIndentation < 0) { level = - optIndentation; str = "\t"; } else { level = optIndentation; str = " "; } for (int i = 0; i < level; i++) fputs(str, fh); } static int dmHandleError(const int res) { return res >= 0 ? DMERR_OK : dmGetErrno(); } /* Assembler include data output functions */ int writeHeader_ASM(FILE *fh, void *ctx, const char *name) { int res = 0; (void) ctx; if (name) res = fprintf(fh, "; '%s'", name); else res = fprintf(fh, "; Generated"); if (res >= 0) res = fprintf(fh, " by %s v%s\n", dmProgName, dmProgVersion); return dmHandleError(res); } int writeDecl_ASM(FILE *fh, void *ctx, const size_t len, const char *name) { int res = 0; (void) ctx; if (optExtraData) res = fprintf(fh, "%s_size = %" DM_PRIu_SIZE_T "\n", name, len); if (res >= 0) res = fprintf(fh, "%s:\n", name); return dmHandleError(res); } int writeData_ASM(FILE *fh, void *ctx, const Uint8 * buf, const size_t len) { int res; (void) ctx; dmPrintIndentation(fh); res = fprintf(fh, "%s ", optDataType); for (size_t i = 0; res >= 0 && i < len; i++) { if (optFormatting) { if (optHexMode) res = fprintf(fh, "$%.2x", buf[i]); else res = fprintf(fh, "%3d", buf[i]); } else { if (optHexMode) res = fprintf(fh, "$%x", buf[i]); else res = fprintf(fh, "%d", buf[i]); } if (res >= 0 && i + 1 < len) res = fprintf(fh, optFormatting ? ", " : ","); } if (res >= 0) res = fprintf(fh, "\n"); return dmHandleError(res); } int writeFooter_ASM(FILE *fh, void *ctx, const size_t len, const char *name) { int res; (void) len; (void) ctx; if (optExtraData) res = fprintf(fh, "%s_end: \n", name); else res = fprintf(fh, "\n"); return dmHandleError(res); } /* ANSI-C include data output functions */ int writeHeader_C(FILE *fh, void *ctx, const char *name) { int res; (void) ctx; if (name) res = fprintf(fh, "/* '%s' generated", name); else res = fprintf(fh, "/* Generated"); if (res >= 0) res = fprintf(fh, " by %s v%s\n" " */\n", dmProgName, dmProgVersion); return dmHandleError(res); } int writeDecl_C(FILE *fh, void *ctx, const size_t len, const char *name) { int res; (void) ctx; res = fprintf(fh, "%s %s[%" DM_PRIu_SIZE_T "] = {\n", optDataType, name, len); if (res >= 0) res = printf("extern %s %s[%" DM_PRIu_SIZE_T "];\n", optDataType, name, len); return dmHandleError(res); } int writeData_C(FILE *fh, void *ctx, const Uint8 *buf, const size_t len) { int res = 0; (void) ctx; dmPrintIndentation(fh); for (size_t i = 0; res >= 0 && i < len; i++) { if (optFormatting) { if (optHexMode) res = fprintf(fh, "0x%.2x", buf[i]); else res = fprintf(fh, "%3d", buf[i]); } else { if (optHexMode) res = fprintf(fh, "0x%x", buf[i]); else res = fprintf(fh, "%d", buf[i]); } if (res >= 0) res = fprintf(fh, optFormatting ? ", " : ","); } if (res >= 0) res = fprintf(fh, "\n"); return dmHandleError(res); } int writeFooter_C(FILE *fh, void *ctx, const size_t len, const char *name) { int res; (void) len; (void) name; (void) ctx; res = fprintf(fh, "};\n"); return dmHandleError(res); } /* * List of formats */ static const DMOutputFormat dmFormatList[] = { { "asm", "XA65 compatible assembler", { "s", "asm", NULL }, ".byte", NULL, NULL, writeHeader_ASM, writeDecl_ASM, writeData_ASM, writeFooter_ASM, }, { "c", "ANSI C array", { "c", "h", "cc", "cpp", "hpp", "c++", NULL }, "unsigned char", NULL, NULL, writeHeader_C, writeDecl_C, writeData_C, writeFooter_C, }, }; static const int ndmFormatList = sizeof(dmFormatList) / sizeof(dmFormatList[0]); off_t dmGetFileSize(FILE *fh) { off_t len, pos = ftello(fh); fseeko(fh, 0, SEEK_END); len = ftello(fh); fseeko(fh, pos, SEEK_SET); return len; } const DMOutputFormat *dmGuessFormatFromName(const char *filename) { const char *fext; if ((fext = strrchr(filename, '.')) == NULL) return NULL; for (int i = 0; i < ndmFormatList; i++) { const DMOutputFormat *fmt = &dmFormatList[i]; for (int n = 0; n < SET_MAX_FEXTS && fmt->fexts[n] != NULL; n++) { if (strcasecmp(fext + 1, fmt->fexts[n]) == 0) return fmt; } } return NULL; } int main(int argc, char *argv[]) { FILE *inFile = NULL, *outFile = NULL; Uint8 *dataBuf = NULL; void *ctx = NULL; size_t dataSize; off_t totalSize; int res; // Initialize dmInitProg("data2inc", "Data to include file converter", "0.7", NULL, NULL); dmVerbosity = 0; // Parse arguments if (!dmArgsProcess(argc, argv, optList, optListN, argHandleOpt, argHandleFile, OPTH_BAILOUT)) exit(1); // Determine output type, if not specified if (setFormat == NULL) { if (optOutFilename == NULL) { dmErrorMsg("Output format not specified and no output filename given (try --help)\n"); goto exit; } if ((setFormat = dmGuessFormatFromName(optOutFilename)) == NULL) { dmErrorMsg("Could not guess output format from filename '%s'.\n", optOutFilename); goto exit; } dmMsg(0, "Guessed output format: %s (%s)\n", setFormat->desc, setFormat->name); } // Set some option defaults if (optDataType == NULL) optDataType = setFormat->defDataType; // Open the files if (optInFilename == NULL) inFile = stdin; else if ((inFile = fopen(optInFilename, "rb")) == NULL) { res = dmGetErrno(); dmErrorMsg("Error opening input file '%s'. (%s)\n", optInFilename, dmErrorStr(res)); goto exit; } if (optOutFilename == NULL) outFile = stdout; else if ((outFile = fopen(optOutFilename, "wa")) == NULL) { res = dmGetErrno(); dmErrorMsg("Error creating output file '%s'. (%s)\n", optOutFilename, dmErrorStr(res)); goto exit; } // Allocate linebuffer dataSize = optLineLen * sizeof(Uint8); if ((dataBuf = dmMalloc(dataSize)) == NULL) { dmErrorMsg("Could not allocate %" DM_PRIu_SIZE_T " byte buffer.\n", dataSize); goto exit; } // Get sourcefile size totalSize = dmGetFileSize(inFile); // Call context init if (setFormat->initContext != NULL && (res = setFormat->initContext(&ctx)) != DMERR_OK) { dmErrorMsg("Error initializing format %s (%s) context: %s\n", setFormat->name, setFormat->desc, dmErrorStr(res)); goto exit; } // Output header if (!optQuiet && (res = setFormat->writeHeader(outFile, ctx, optOutFilename)) != DMERR_OK) { dmErrorMsg("Error writing output header: %s\n", dmErrorStr(res)); goto exit; } if (optAddLine) fprintf(outFile, "%s\n", optAddLine); // Output declaration if (setFormat->writeDecl != NULL && (res = setFormat->writeDecl(outFile, ctx, totalSize, optObjName)) != DMERR_OK) { dmErrorMsg("Error writing output declaration: %s\n", dmErrorStr(res)); goto exit; } // Output data while (!feof(inFile)) { res = fread(dataBuf, 1, dataSize, inFile); if (res > 0 && (res = setFormat->writeData(outFile, ctx, dataBuf, res)) != DMERR_OK) { dmErrorMsg("Error writing output data: %s\n", dmErrorStr(res)); goto exit; } } // Output footer if (setFormat->writeFooter != NULL && (res = setFormat->writeFooter(outFile, ctx, totalSize, optObjName)) != DMERR_OK) { dmErrorMsg("Error writing output footer: %s\n", dmErrorStr(res)); goto exit; } exit: // Cleanup if (inFile != NULL) fclose(inFile); if (outFile != NULL) fclose(outFile); dmFree(dataBuf); if (setFormat != NULL && setFormat->closeContext != NULL && (res = setFormat->closeContext(ctx)) != DMERR_OK) { dmErrorMsg("Error closing format %s (%s) context: %s\n", setFormat->name, setFormat->desc, dmErrorStr(res)); } return 0; }