Add support for packihx to Borland makefiles
[fw/sdcc] / packihx / packihx.c
1 /*-----------------------------------------------------------------------
2  * packihx.c:
3  *
4  * utility to pack an Inter HEX format file by removing redundant 
5  * extended offset records and accumulating data records up to
6  * OUTPUT_CHUNK (currently 16) bytes.
7  *
8  * Released to the public domain 10/16/2000 Kevin Vigor.
9  */
10
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <ctype.h>
15 #include <assert.h>
16
17 #include "config.h"
18
19 /* A cooked line of input. */
20 typedef struct _Line
21 {
22     Uint8               len;            /* length of data portion of record. */
23     Uint16      offset;
24     Uint8               type;
25     Uint8               *data;          
26     Uint8               checksum;
27 } Line;
28
29 /* Largest expected line of raw input. */
30 #define MAX_INPUT_RAW   128
31 /* Largest expected cooked data portion of an input line. */
32 #define MAX_INPUT_COOKED (MAX_INPUT_RAW / 2)
33
34 /* Globals: current input & output line numbers. */
35 int lineno = 0;
36 int outlineno = 0;
37
38 /* Convert hex digit to numeric value 0 - 15; assumes input is a 
39  * valid digit (i.e. passes isxdigit()).
40  */
41 static Uint8 hexDigit(const char c)
42 {
43     if (isdigit(c))
44     {
45         return (Uint8)(c - '0');
46     }
47     else
48     {
49         return (Uint8)((islower(c) ? toupper(c) : c)  - 'A' + 10);
50     }
51 }
52
53 /* Convert two hex digits from cp to a byte value. */
54 static int getHexByte(const char *cp, Uint8 *byte)
55 {
56     if (cp && cp[0] && isxdigit(cp[0]) && cp[1] && isxdigit(cp[1]))
57     {
58         *byte = (hexDigit(cp[0]) << 4) + hexDigit(cp[1]);
59     }
60     else
61     {
62         return -1;
63     }
64     return 0;
65 }
66
67 /* Convert four hex digits from cp to a 2 byte value. */
68 static int getHexWord(const char *cp, Uint16 *word)
69 {
70     Uint8 byte1, byte2;
71     
72     if (getHexByte(cp, &byte1) || getHexByte(cp + 2, &byte2))
73     {
74         return -1;
75     }
76     *word = (byte1 << 8) + byte2;
77     return 0;
78 }
79
80 /* Return a single cooked line of input from the passed file handle. */
81 Line *readLine(FILE *inFile)
82 {
83     static char buffer[MAX_INPUT_RAW];
84     const char  *bp;
85     Line        *line;
86     unsigned    i;
87
88     line = (Line *)malloc(sizeof(Line));
89     if (!line)
90     {
91         fprintf(stderr, "packihx: no memory!\n");
92         return NULL;
93     }
94
95     do
96     {
97         if (!fgets(buffer, MAX_INPUT_RAW, inFile))
98         {
99             return NULL;
100         }
101         ++lineno;
102     
103         if (!buffer[0] || buffer[0] == '\r' || buffer[0] == '\n')
104         {
105             /* Empty input line. */
106             return NULL;
107         }
108     } while (buffer[0] != ':');
109
110     bp = buffer;
111     bp++;       /* Skip leading : */
112      
113     if (getHexByte(bp, &line->len))
114     {
115         fprintf(stderr, "packihx: can't read line length @ line %d\n", 
116                 lineno);
117         free(line);
118         return NULL;
119     }
120     bp += 2;    /* Two digits consumed. */
121
122     if (line->len > MAX_INPUT_COOKED)
123     {
124         fprintf(stderr, "packihx: line length %X too long @ line %d\n",
125                 (int)line->len, lineno);
126         free(line);
127         return NULL;
128     }
129     
130     if (getHexWord(bp, &line->offset))
131     {
132         fprintf(stderr, "packihx: can't read line offset @ line %d\n",
133                 lineno);
134         free(line);
135         return NULL;        
136     }
137     bp += 4; /* Four digits consumed. */
138     
139     if (getHexByte(bp, &line->type))
140     {
141         fprintf(stderr, "packihx: can't read record type @ line %d\n", 
142                 lineno);
143         free(line);
144         return NULL;
145     }
146     bp += 2;    /* Two digits consumed. */    
147     
148     line->data = (Uint8 *)malloc(line->len);
149     if (!line->data)
150     {
151         free(line);
152         fprintf(stderr, "packihx: no memory!\n");
153         return NULL;
154     }
155     
156     for (i = 0; i < (unsigned)line->len; i++)
157     {
158         if (getHexByte(bp, &(line->data[i])))
159         {
160             fprintf(stderr, 
161                     "packihx: can't read data byte %u of %u @ line %d\n", 
162                     i, (unsigned) line->len, lineno);
163             free(line->data);
164             free(line);
165             return NULL; 
166         }
167         bp += 2; /* Two digits consumed. */
168     }
169     
170     if (getHexByte(bp, &line->checksum))
171     {
172         fprintf(stderr, "packihx: can't read checksum @ line %d\n", 
173                 lineno);
174         free(line->data);
175         free(line);
176         return NULL;
177     }
178     /* bp += 2; */    /* Two digits consumed. */
179         
180     return line;
181 }
182
183 /* Compute the checksum of a line. */
184 Uint16 lineChecksum(unsigned len, unsigned offset, unsigned type, 
185                     const Uint8 *data)
186 {
187     Uint16  checksum;
188     unsigned    i;
189      
190     checksum = len + type + (offset >> 8) + 
191                (offset & 0xff);
192                 
193     for (i = 0; i < len; i++)
194     {
195         checksum += data[i];       
196     }
197     
198     checksum &= 0xff;
199     if (checksum)
200     {
201        checksum = 0x100 - checksum;
202     }
203     return checksum;
204 }
205
206 /* Ensure that the checksum of a line matches the expected value. */
207 int validateChecksum(Line *line)
208 {
209     Uint16 checksum;
210     
211     checksum = lineChecksum(line->len, line->offset, line->type, line->data);
212     
213     if (checksum != line->checksum)
214     {
215         fprintf(stderr, "packihx: invalid checksum %X (want %X) @ line %d\n", 
216                 (unsigned)checksum, (unsigned)(line->checksum),
217                 lineno);
218         return -1;      
219     }
220     
221     return 0;
222 }
223
224 /* Write a single record line. */
225 int writeRecord(unsigned len, unsigned offset, unsigned type,
226                 const Uint8 *data)
227 {
228     unsigned i;
229     
230     if (printf(":%02X%04X%02X", len, offset, type) == EOF)
231     {
232         return -1;
233     }
234     
235     for (i = 0; i < len; i++)
236     {
237         if (printf("%02X", data[i]) == EOF)
238         {
239             return -1;
240         }
241     }
242     
243     if (printf("%02X\n", lineChecksum(len, offset, type, data)) == EOF)
244     {
245         return -1;
246     }
247     outlineno++;
248     return 0;
249 }
250
251 #define OUTPUT_CHUNK 16         
252 static unsigned pendingLen = 0;
253 static unsigned pendingOffset = 0;
254 static char     pending[MAX_INPUT_COOKED + OUTPUT_CHUNK];
255
256 /* Buffer up a data record. */
257 int bufferOutput(Line *line)
258 {
259    unsigned offset = 0;
260    int      rc = 0;
261    
262    /* Stick the data onto any pending data. */
263    assert(pendingLen < OUTPUT_CHUNK);
264    memcpy(&pending[pendingLen], line->data, line->len);
265    pendingLen += line->len;
266    
267    /* Write it out untill we have less than an OUTPUT_CHUNK left. */
268    while (!rc && pendingLen >= OUTPUT_CHUNK)
269    {
270        rc = writeRecord(OUTPUT_CHUNK, pendingOffset, 0, &pending[offset]);
271        offset += OUTPUT_CHUNK;
272        pendingOffset += OUTPUT_CHUNK;
273        pendingLen -= OUTPUT_CHUNK;
274    }
275    
276    /* Copy any remaining bits back to the beginning of the buffer. */
277    if (pendingLen)
278    {
279         memmove(pending, &pending[offset], pendingLen);
280    }
281    return rc;
282 }
283
284 /* Write out any pending data. */
285 int flushPendingData(void)
286 {
287     int rc = 0;
288     
289     assert(pendingLen < OUTPUT_CHUNK);
290     
291     if (pendingLen)
292     {
293         rc =  writeRecord(pendingLen, pendingOffset, 0, pending);
294         pendingLen = pendingOffset = 0;
295     }
296     return rc;
297 }
298
299 /* Write an arbitrary line of output (buffering if possible) */
300 int writeLine(Line *line)
301 {
302     static Uint16 lastExtendedOffset = 0;
303     int       rc;
304     
305     if (line->type)
306     {
307         /* Not a data record. */
308         if (line->type == 4)
309         {
310             Uint16 offset;
311             
312             /* Extended offset record. */
313             if (line->len != 2)
314             {
315                 fprintf(stderr, 
316                         "packihx: invalid extended offset record @ line %d\n",
317                         lineno);
318                 return -1;
319             }
320             
321             offset = (line->data[0] << 8) + line->data[1];
322             
323             if (offset == lastExtendedOffset)
324             {
325                 /* We can simply skip this line. */
326                 return 0;
327             }
328             else
329             {
330                 lastExtendedOffset = offset;
331             }
332         }
333         
334         if (flushPendingData())
335         {
336             return -1;
337         }
338         
339         /* Write the line as is. */
340         rc = writeRecord(line->len, line->offset, line->type, line->data);
341     }
342     else
343     {
344         if (pendingOffset + pendingLen != (unsigned)line->offset)
345         {
346             /* This line is not contigous with the last one. Dump pending. */
347             if (flushPendingData())
348             {
349                 return -1;
350             }
351             pendingOffset = line->offset;
352         }
353         rc = bufferOutput(line);
354     }
355     return rc;
356 }
357
358 int main(int argc, char *argv[])
359 {
360     FILE *inFile;
361     Line *line;
362     int  closeFile;
363     int  rc = 0;
364     
365     if (argc > 1)
366     {
367         inFile = fopen(argv[1], "rt");
368         if (!inFile)
369         {
370             fprintf(stderr, "packihx: cannot open %s\n", 
371                      argv[1]);
372             return 1;
373         }
374         closeFile = 1;
375     }
376     else
377     {
378         inFile = stdin;
379         closeFile = 0;
380     }
381     
382     while (!rc && ((line = readLine(inFile)) != NULL))
383     {
384         rc = validateChecksum(line);
385         
386         if (!rc)
387         {
388             rc = writeLine(line);
389         }
390         
391         free(line->data);
392         free(line);
393     }
394     
395     if (!rc && !feof(inFile))
396     {
397         /* readLine must have failed for some reason. */
398         fprintf(stderr, "packihx: aborting after %d lines.\n", lineno);
399         rc = 1;
400     }
401     
402     if (!rc)
403     {
404         /* Just in case there's something still pending. */
405         rc = flushPendingData();
406     }
407     
408     if (!rc)
409     {
410         fprintf(stderr, "packihx: read %d lines, wrote %d: OK.\n", lineno, outlineno);
411     }
412
413     if (closeFile)
414     {
415         fclose(inFile);
416     }    
417     return rc;
418 }