/*----------------------------------------------------------------------- * packihx.c: * * utility to pack an Intel HEX format file by removing redundant * extended offset records and accumulating data records up to * OUTPUT_CHUNK (currently 16) bytes. * * Released to the public domain 10/16/2000 Kevin Vigor. */ #include #include #include #include #include #if defined(_MSC_VER) || defined(__BORLANDC__) typedef unsigned char Uint8 ; typedef unsigned Uint16 ; #else #include "config.h" #endif /* A cooked line of input. */ typedef struct _Line { Uint8 len; /* length of data portion of record. */ Uint16 offset; Uint8 type; Uint8 *data; Uint8 checksum; } Line; /* Largest expected line of raw input. */ #define MAX_INPUT_RAW 128 /* Largest expected cooked data portion of an input line. */ #define MAX_INPUT_COOKED (MAX_INPUT_RAW / 2) /* Globals: current input & output line numbers. */ int lineno = 0; int outlineno = 0; /* Convert hex digit to numeric value 0 - 15; assumes input is a * valid digit (i.e. passes isxdigit()). */ static Uint8 hexDigit(const unsigned char c) { if (isdigit(c)) { return (Uint8)(c - '0'); } else { return (Uint8)((islower(c) ? toupper(c) : c) - 'A' + 10); } } /* Convert two hex digits from cp to a byte value. */ static int getHexByte(const char *cp, Uint8 *byte) { if (cp && cp[0] && isxdigit(cp[0]) && cp[1] && isxdigit(cp[1])) { *byte = (hexDigit(cp[0]) << 4) + hexDigit(cp[1]); } else { return -1; } return 0; } /* Convert four hex digits from cp to a 2 byte value. */ static int getHexWord(const char *cp, Uint16 *word) { Uint8 byte1, byte2; if (getHexByte(cp, &byte1) || getHexByte(cp + 2, &byte2)) { return -1; } *word = (byte1 << 8) + byte2; return 0; } /* Return a single cooked line of input from the passed file handle. */ Line *readLine(FILE *inFile) { static char buffer[MAX_INPUT_RAW]; const char *bp; Line *line; unsigned i; line = (Line *)malloc(sizeof(Line)); if (!line) { fprintf(stderr, "packihx: no memory!\n"); return NULL; } do { if (!fgets(buffer, MAX_INPUT_RAW, inFile)) { return NULL; } ++lineno; if (!buffer[0] || buffer[0] == '\r' || buffer[0] == '\n') { /* Empty input line. */ return NULL; } } while (buffer[0] != ':'); bp = buffer; bp++; /* Skip leading : */ if (getHexByte(bp, &line->len)) { fprintf(stderr, "packihx: can't read line length @ line %d\n", lineno); free(line); return NULL; } bp += 2; /* Two digits consumed. */ if (line->len > MAX_INPUT_COOKED) { fprintf(stderr, "packihx: line length %X too long @ line %d\n", (int)line->len, lineno); free(line); return NULL; } if (getHexWord(bp, &line->offset)) { fprintf(stderr, "packihx: can't read line offset @ line %d\n", lineno); free(line); return NULL; } bp += 4; /* Four digits consumed. */ if (getHexByte(bp, &line->type)) { fprintf(stderr, "packihx: can't read record type @ line %d\n", lineno); free(line); return NULL; } bp += 2; /* Two digits consumed. */ /* Hack - always allocate something, even if len is zero. * Avoids special case for len == 0. */ line->data = (Uint8 *)malloc(line->len ? line->len : 1); if (!line->data) { free(line); fprintf(stderr, "packihx: no memory!\n"); return NULL; } for (i = 0; i < (unsigned)line->len; i++) { if (getHexByte(bp, &(line->data[i]))) { fprintf(stderr, "packihx: can't read data byte %u of %u @ line %d\n", i, (unsigned) line->len, lineno); free(line->data); free(line); return NULL; } bp += 2; /* Two digits consumed. */ } if (getHexByte(bp, &line->checksum)) { fprintf(stderr, "packihx: can't read checksum @ line %d\n", lineno); free(line->data); free(line); return NULL; } /* bp += 2; */ /* Two digits consumed. */ return line; } /* Compute the checksum of a line. */ Uint16 lineChecksum(unsigned len, unsigned offset, unsigned type, const Uint8 *data) { Uint16 checksum; unsigned i; checksum = len + type + (offset >> 8) + (offset & 0xff); for (i = 0; i < len; i++) { checksum += data[i]; } checksum &= 0xff; if (checksum) { checksum = 0x100 - checksum; } return checksum; } /* Ensure that the checksum of a line matches the expected value. */ int validateChecksum(Line *line) { Uint16 checksum; checksum = lineChecksum(line->len, line->offset, line->type, line->data); if (checksum != line->checksum) { fprintf(stderr, "packihx: invalid checksum %X (want %X) @ line %d\n", (unsigned)(line->checksum), (unsigned)checksum, lineno); return -1; } return 0; } /* Write a single record line. */ int writeRecord(unsigned len, unsigned offset, unsigned type, const Uint8 *data) { unsigned i; if (printf(":%02X%04X%02X", len, offset, type) == EOF) { return -1; } for (i = 0; i < len; i++) { if (printf("%02X", data[i]) == EOF) { return -1; } } if (printf("%02X\n", lineChecksum(len, offset, type, data)) == EOF) { return -1; } outlineno++; return 0; } #define OUTPUT_CHUNK 16 static unsigned pendingLen = 0; static unsigned pendingOffset = 0; static Uint8 pending[MAX_INPUT_COOKED + OUTPUT_CHUNK]; /* Buffer up a data record. */ int bufferOutput(Line *line) { unsigned offset = 0; int rc = 0; /* Stick the data onto any pending data. */ assert(pendingLen < OUTPUT_CHUNK); memcpy(&pending[pendingLen], line->data, line->len); pendingLen += line->len; /* Write it out untill we have less than an OUTPUT_CHUNK left. */ while (!rc && pendingLen >= OUTPUT_CHUNK) { rc = writeRecord(OUTPUT_CHUNK, pendingOffset, 0, &pending[offset]); offset += OUTPUT_CHUNK; pendingOffset += OUTPUT_CHUNK; pendingLen -= OUTPUT_CHUNK; } /* Copy any remaining bits back to the beginning of the buffer. */ if (pendingLen) { memmove(pending, &pending[offset], pendingLen); } return rc; } /* Write out any pending data. */ int flushPendingData(void) { int rc = 0; assert(pendingLen < OUTPUT_CHUNK); if (pendingLen) { rc = writeRecord(pendingLen, pendingOffset, 0, pending); pendingLen = pendingOffset = 0; } return rc; } /* Write an arbitrary line of output (buffering if possible) */ int writeLine(Line *line) { static Uint16 lastExtendedOffset = 0; int rc; if (line->type) { /* Not a data record. */ if (line->type == 4) { Uint16 offset; /* Extended offset record. */ if (line->len != 2) { fprintf(stderr, "packihx: invalid extended offset record @ line %d\n", lineno); return -1; } offset = (line->data[0] << 8) + line->data[1]; if (offset == lastExtendedOffset) { /* We can simply skip this line. */ return 0; } else { lastExtendedOffset = offset; } } if (flushPendingData()) { return -1; } /* Write the line as is. */ rc = writeRecord(line->len, line->offset, line->type, line->data); } else { if (pendingOffset + pendingLen != (unsigned)line->offset) { /* This line is not contigous with the last one. Dump pending. */ if (flushPendingData()) { return -1; } pendingOffset = line->offset; } rc = bufferOutput(line); } return rc; } int main(int argc, char *argv[]) { FILE *inFile; Line *line; int closeFile; int rc = 0; if (argc > 1) { inFile = fopen(argv[1], "rt"); if (!inFile) { fprintf(stderr, "packihx: cannot open %s\n", argv[1]); return 1; } closeFile = 1; } else { inFile = stdin; closeFile = 0; } while (!rc && ((line = readLine(inFile)) != NULL)) { rc = validateChecksum(line); if (!rc) { rc = writeLine(line); } free(line->data); free(line); } if (!rc && !feof(inFile)) { /* readLine must have failed for some reason. */ fprintf(stderr, "packihx: aborting after %d lines.\n", lineno); rc = 1; } if (!rc) { /* Just in case there's something still pending. */ rc = flushPendingData(); } if (!rc) { fprintf(stderr, "packihx: read %d lines, wrote %d: OK.\n", lineno, outlineno); } if (closeFile) { fclose(inFile); } return rc; }