struct / union in initializer, RFE #901.
[sdcc.git] / sdcc / support / packihx / packihx.c
blob1201786ed250935b0e1902d836ff852aadc43a75
1 /*-----------------------------------------------------------------------
2 * packihx.c:
4 * utility to pack an Intel HEX format file by removing redundant
5 * extended offset records and accumulating data records up to
6 * OUTPUT_CHUNK (currently 16) bytes.
8 * Released to the public domain 10/16/2000 Kevin Vigor.
9 */
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <ctype.h>
15 #include <assert.h>
17 #if defined(_MSC_VER) || defined(__BORLANDC__)
19 typedef unsigned char Uint8 ;
20 typedef unsigned Uint16 ;
22 #else
24 #include "config.h"
25 #endif
27 /* A cooked line of input. */
28 typedef struct _Line
30 Uint8 len; /* length of data portion of record. */
31 Uint16 offset;
32 Uint8 type;
33 Uint8 *data;
34 Uint8 checksum;
35 } Line;
37 /* Largest expected line of raw input. */
38 #define MAX_INPUT_RAW 128
39 /* Largest expected cooked data portion of an input line. */
40 #define MAX_INPUT_COOKED (MAX_INPUT_RAW / 2)
42 /* Globals: current input & output line numbers. */
43 int lineno = 0;
44 int outlineno = 0;
46 /* Convert hex digit to numeric value 0 - 15; assumes input is a
47 * valid digit (i.e. passes isxdigit()).
49 static Uint8 hexDigit(const unsigned char c)
51 if (isdigit(c))
53 return (Uint8)(c - '0');
55 else
57 return (Uint8)((islower(c) ? toupper(c) : c) - 'A' + 10);
61 /* Convert two hex digits from cp to a byte value. */
62 static int getHexByte(const char *cp, Uint8 *byte)
64 if (cp && cp[0] && isxdigit(cp[0]) && cp[1] && isxdigit(cp[1]))
66 *byte = (hexDigit(cp[0]) << 4) + hexDigit(cp[1]);
68 else
70 return -1;
72 return 0;
75 /* Convert four hex digits from cp to a 2 byte value. */
76 static int getHexWord(const char *cp, Uint16 *word)
78 Uint8 byte1, byte2;
80 if (getHexByte(cp, &byte1) || getHexByte(cp + 2, &byte2))
82 return -1;
84 *word = (byte1 << 8) + byte2;
85 return 0;
88 /* Return a single cooked line of input from the passed file handle. */
89 Line *readLine(FILE *inFile)
91 static char buffer[MAX_INPUT_RAW];
92 const char *bp;
93 Line *line;
94 unsigned i;
96 line = (Line *)malloc(sizeof(Line));
97 if (!line)
99 fprintf(stderr, "packihx: no memory!\n");
100 return NULL;
105 if (!fgets(buffer, MAX_INPUT_RAW, inFile))
107 return NULL;
109 ++lineno;
111 if (!buffer[0] || buffer[0] == '\r' || buffer[0] == '\n')
113 /* Empty input line. */
114 return NULL;
116 } while (buffer[0] != ':');
118 bp = buffer;
119 bp++; /* Skip leading : */
121 if (getHexByte(bp, &line->len))
123 fprintf(stderr, "packihx: can't read line length @ line %d\n",
124 lineno);
125 free(line);
126 return NULL;
128 bp += 2; /* Two digits consumed. */
130 if (line->len > MAX_INPUT_COOKED)
132 fprintf(stderr, "packihx: line length %X too long @ line %d\n",
133 (int)line->len, lineno);
134 free(line);
135 return NULL;
138 if (getHexWord(bp, &line->offset))
140 fprintf(stderr, "packihx: can't read line offset @ line %d\n",
141 lineno);
142 free(line);
143 return NULL;
145 bp += 4; /* Four digits consumed. */
147 if (getHexByte(bp, &line->type))
149 fprintf(stderr, "packihx: can't read record type @ line %d\n",
150 lineno);
151 free(line);
152 return NULL;
154 bp += 2; /* Two digits consumed. */
156 /* Hack - always allocate something, even if len is zero.
157 * Avoids special case for len == 0. */
158 line->data = (Uint8 *)malloc(line->len ? line->len : 1);
159 if (!line->data)
161 free(line);
162 fprintf(stderr, "packihx: no memory!\n");
163 return NULL;
166 for (i = 0; i < (unsigned)line->len; i++)
168 if (getHexByte(bp, &(line->data[i])))
170 fprintf(stderr,
171 "packihx: can't read data byte %u of %u @ line %d\n",
172 i, (unsigned) line->len, lineno);
173 free(line->data);
174 free(line);
175 return NULL;
177 bp += 2; /* Two digits consumed. */
180 if (getHexByte(bp, &line->checksum))
182 fprintf(stderr, "packihx: can't read checksum @ line %d\n",
183 lineno);
184 free(line->data);
185 free(line);
186 return NULL;
188 /* bp += 2; */ /* Two digits consumed. */
190 return line;
193 /* Compute the checksum of a line. */
194 Uint16 lineChecksum(unsigned len, unsigned offset, unsigned type,
195 const Uint8 *data)
197 Uint16 checksum;
198 unsigned i;
200 checksum = len + type + (offset >> 8) +
201 (offset & 0xff);
203 for (i = 0; i < len; i++)
205 checksum += data[i];
208 checksum &= 0xff;
209 if (checksum)
211 checksum = 0x100 - checksum;
213 return checksum;
216 /* Ensure that the checksum of a line matches the expected value. */
217 int validateChecksum(Line *line)
219 Uint16 checksum;
221 checksum = lineChecksum(line->len, line->offset, line->type, line->data);
223 if (checksum != line->checksum)
225 fprintf(stderr, "packihx: invalid checksum %X (want %X) @ line %d\n",
226 (unsigned)(line->checksum), (unsigned)checksum,
227 lineno);
228 return -1;
231 return 0;
234 /* Write a single record line. */
235 int writeRecord(unsigned len, unsigned offset, unsigned type,
236 const Uint8 *data)
238 unsigned i;
240 if (printf(":%02X%04X%02X", len, offset, type) == EOF)
242 return -1;
245 for (i = 0; i < len; i++)
247 if (printf("%02X", data[i]) == EOF)
249 return -1;
253 if (printf("%02X\n", lineChecksum(len, offset, type, data)) == EOF)
255 return -1;
257 outlineno++;
258 return 0;
261 #define OUTPUT_CHUNK 16
262 static unsigned pendingLen = 0;
263 static unsigned pendingOffset = 0;
264 static Uint8 pending[MAX_INPUT_COOKED + OUTPUT_CHUNK];
266 /* Buffer up a data record. */
267 int bufferOutput(Line *line)
269 unsigned offset = 0;
270 int rc = 0;
272 /* Stick the data onto any pending data. */
273 assert(pendingLen < OUTPUT_CHUNK);
274 memcpy(&pending[pendingLen], line->data, line->len);
275 pendingLen += line->len;
277 /* Write it out untill we have less than an OUTPUT_CHUNK left. */
278 while (!rc && pendingLen >= OUTPUT_CHUNK)
280 rc = writeRecord(OUTPUT_CHUNK, pendingOffset, 0, &pending[offset]);
281 offset += OUTPUT_CHUNK;
282 pendingOffset += OUTPUT_CHUNK;
283 pendingLen -= OUTPUT_CHUNK;
286 /* Copy any remaining bits back to the beginning of the buffer. */
287 if (pendingLen)
289 memmove(pending, &pending[offset], pendingLen);
291 return rc;
294 /* Write out any pending data. */
295 int flushPendingData(void)
297 int rc = 0;
299 assert(pendingLen < OUTPUT_CHUNK);
301 if (pendingLen)
303 rc = writeRecord(pendingLen, pendingOffset, 0, pending);
304 pendingLen = pendingOffset = 0;
306 return rc;
309 /* Write an arbitrary line of output (buffering if possible) */
310 int writeLine(Line *line)
312 static Uint16 lastExtendedOffset = 0;
313 int rc;
315 if (line->type)
317 /* Not a data record. */
318 if (line->type == 4)
320 Uint16 offset;
322 /* Extended offset record. */
323 if (line->len != 2)
325 fprintf(stderr,
326 "packihx: invalid extended offset record @ line %d\n",
327 lineno);
328 return -1;
331 offset = (line->data[0] << 8) + line->data[1];
333 if (offset == lastExtendedOffset)
335 /* We can simply skip this line. */
336 return 0;
338 else
340 lastExtendedOffset = offset;
344 if (flushPendingData())
346 return -1;
349 /* Write the line as is. */
350 rc = writeRecord(line->len, line->offset, line->type, line->data);
352 else
354 if (pendingOffset + pendingLen != (unsigned)line->offset)
356 /* This line is not contigous with the last one. Dump pending. */
357 if (flushPendingData())
359 return -1;
361 pendingOffset = line->offset;
363 rc = bufferOutput(line);
365 return rc;
368 int main(int argc, char *argv[])
370 FILE *inFile;
371 Line *line;
372 int closeFile;
373 int rc = 0;
375 if (argc > 1)
377 inFile = fopen(argv[1], "rt");
378 if (!inFile)
380 fprintf(stderr, "packihx: cannot open %s\n",
381 argv[1]);
382 return 1;
384 closeFile = 1;
386 else
388 inFile = stdin;
389 closeFile = 0;
392 while (!rc && ((line = readLine(inFile)) != NULL))
394 rc = validateChecksum(line);
396 if (!rc)
398 rc = writeLine(line);
401 free(line->data);
402 free(line);
405 if (!rc && !feof(inFile))
407 /* readLine must have failed for some reason. */
408 fprintf(stderr, "packihx: aborting after %d lines.\n", lineno);
409 rc = 1;
412 if (!rc)
414 /* Just in case there's something still pending. */
415 rc = flushPendingData();
418 if (!rc)
420 fprintf(stderr, "packihx: read %d lines, wrote %d: OK.\n", lineno, outlineno);
423 if (closeFile)
425 fclose(inFile);
427 return rc;