Fixed problem in SDCCalloc.h & fixed problem allocation
[fw/sdcc] / packihx / packihx.c
1 /*-----------------------------------------------------------------------
2  * packihx.c:
3  *
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.
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     /* Hack - always allocate something, even if len is zero.
149      * Avoids special case for len == 0. */
150     line->data = (Uint8 *)malloc(line->len ? line->len : 1);
151     if (!line->data)
152     {
153         free(line);
154         fprintf(stderr, "packihx: no memory!\n");
155         return NULL;
156     }
157     
158     for (i = 0; i < (unsigned)line->len; i++)
159     {
160         if (getHexByte(bp, &(line->data[i])))
161         {
162             fprintf(stderr, 
163                     "packihx: can't read data byte %u of %u @ line %d\n", 
164                     i, (unsigned) line->len, lineno);
165             free(line->data);
166             free(line);
167             return NULL; 
168         }
169         bp += 2; /* Two digits consumed. */
170     }
171     
172     if (getHexByte(bp, &line->checksum))
173     {
174         fprintf(stderr, "packihx: can't read checksum @ line %d\n", 
175                 lineno);
176         free(line->data);
177         free(line);
178         return NULL;
179     }
180     /* bp += 2; */    /* Two digits consumed. */
181         
182     return line;
183 }
184
185 /* Compute the checksum of a line. */
186 Uint16 lineChecksum(unsigned len, unsigned offset, unsigned type, 
187                     const Uint8 *data)
188 {
189     Uint16  checksum;
190     unsigned    i;
191      
192     checksum = len + type + (offset >> 8) + 
193                (offset & 0xff);
194                 
195     for (i = 0; i < len; i++)
196     {
197         checksum += data[i];       
198     }
199     
200     checksum &= 0xff;
201     if (checksum)
202     {
203        checksum = 0x100 - checksum;
204     }
205     return checksum;
206 }
207
208 /* Ensure that the checksum of a line matches the expected value. */
209 int validateChecksum(Line *line)
210 {
211     Uint16 checksum;
212     
213     checksum = lineChecksum(line->len, line->offset, line->type, line->data);
214     
215     if (checksum != line->checksum)
216     {
217         fprintf(stderr, "packihx: invalid checksum %X (want %X) @ line %d\n", 
218                 (unsigned)checksum, (unsigned)(line->checksum),
219                 lineno);
220         return -1;      
221     }
222     
223     return 0;
224 }
225
226 /* Write a single record line. */
227 int writeRecord(unsigned len, unsigned offset, unsigned type,
228                 const Uint8 *data)
229 {
230     unsigned i;
231     
232     if (printf(":%02X%04X%02X", len, offset, type) == EOF)
233     {
234         return -1;
235     }
236     
237     for (i = 0; i < len; i++)
238     {
239         if (printf("%02X", data[i]) == EOF)
240         {
241             return -1;
242         }
243     }
244     
245     if (printf("%02X\n", lineChecksum(len, offset, type, data)) == EOF)
246     {
247         return -1;
248     }
249     outlineno++;
250     return 0;
251 }
252
253 #define OUTPUT_CHUNK 16         
254 static unsigned pendingLen = 0;
255 static unsigned pendingOffset = 0;
256 static char     pending[MAX_INPUT_COOKED + OUTPUT_CHUNK];
257
258 /* Buffer up a data record. */
259 int bufferOutput(Line *line)
260 {
261    unsigned offset = 0;
262    int      rc = 0;
263    
264    /* Stick the data onto any pending data. */
265    assert(pendingLen < OUTPUT_CHUNK);
266    memcpy(&pending[pendingLen], line->data, line->len);
267    pendingLen += line->len;
268    
269    /* Write it out untill we have less than an OUTPUT_CHUNK left. */
270    while (!rc && pendingLen >= OUTPUT_CHUNK)
271    {
272        rc = writeRecord(OUTPUT_CHUNK, pendingOffset, 0, &pending[offset]);
273        offset += OUTPUT_CHUNK;
274        pendingOffset += OUTPUT_CHUNK;
275        pendingLen -= OUTPUT_CHUNK;
276    }
277    
278    /* Copy any remaining bits back to the beginning of the buffer. */
279    if (pendingLen)
280    {
281         memmove(pending, &pending[offset], pendingLen);
282    }
283    return rc;
284 }
285
286 /* Write out any pending data. */
287 int flushPendingData(void)
288 {
289     int rc = 0;
290     
291     assert(pendingLen < OUTPUT_CHUNK);
292     
293     if (pendingLen)
294     {
295         rc =  writeRecord(pendingLen, pendingOffset, 0, pending);
296         pendingLen = pendingOffset = 0;
297     }
298     return rc;
299 }
300
301 /* Write an arbitrary line of output (buffering if possible) */
302 int writeLine(Line *line)
303 {
304     static Uint16 lastExtendedOffset = 0;
305     int       rc;
306     
307     if (line->type)
308     {
309         /* Not a data record. */
310         if (line->type == 4)
311         {
312             Uint16 offset;
313             
314             /* Extended offset record. */
315             if (line->len != 2)
316             {
317                 fprintf(stderr, 
318                         "packihx: invalid extended offset record @ line %d\n",
319                         lineno);
320                 return -1;
321             }
322             
323             offset = (line->data[0] << 8) + line->data[1];
324             
325             if (offset == lastExtendedOffset)
326             {
327                 /* We can simply skip this line. */
328                 return 0;
329             }
330             else
331             {
332                 lastExtendedOffset = offset;
333             }
334         }
335         
336         if (flushPendingData())
337         {
338             return -1;
339         }
340         
341         /* Write the line as is. */
342         rc = writeRecord(line->len, line->offset, line->type, line->data);
343     }
344     else
345     {
346         if (pendingOffset + pendingLen != (unsigned)line->offset)
347         {
348             /* This line is not contigous with the last one. Dump pending. */
349             if (flushPendingData())
350             {
351                 return -1;
352             }
353             pendingOffset = line->offset;
354         }
355         rc = bufferOutput(line);
356     }
357     return rc;
358 }
359
360 int main(int argc, char *argv[])
361 {
362     FILE *inFile;
363     Line *line;
364     int  closeFile;
365     int  rc = 0;
366     
367     if (argc > 1)
368     {
369         inFile = fopen(argv[1], "rt");
370         if (!inFile)
371         {
372             fprintf(stderr, "packihx: cannot open %s\n", 
373                      argv[1]);
374             return 1;
375         }
376         closeFile = 1;
377     }
378     else
379     {
380         inFile = stdin;
381         closeFile = 0;
382     }
383     
384     while (!rc && ((line = readLine(inFile)) != NULL))
385     {
386         rc = validateChecksum(line);
387         
388         if (!rc)
389         {
390             rc = writeLine(line);
391         }
392         
393         free(line->data);
394         free(line);
395     }
396     
397     if (!rc && !feof(inFile))
398     {
399         /* readLine must have failed for some reason. */
400         fprintf(stderr, "packihx: aborting after %d lines.\n", lineno);
401         rc = 1;
402     }
403     
404     if (!rc)
405     {
406         /* Just in case there's something still pending. */
407         rc = flushPendingData();
408     }
409     
410     if (!rc)
411     {
412         fprintf(stderr, "packihx: read %d lines, wrote %d: OK.\n", lineno, outlineno);
413     }
414
415     if (closeFile)
416     {
417         fclose(inFile);
418     }    
419     return rc;
420 }