xpp: safer compilation
[dahdi/tools.git] / xpp / hexfile.c
1 /*
2  * Written by Oron Peled <oron@actcom.co.il>
3  * Copyright (C) 2006, 2007, 2008, Xorcom
4  *
5  * All rights reserved.
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  *
21  */
22
23 #include <stdio.h>
24 #include <assert.h>
25 #include <string.h>
26 #include <stdlib.h>
27 #include <errno.h>
28 #include <ctype.h>
29 #include "hexfile.h"
30
31 static const char rcsid[] = "$Id$";
32
33 static parse_hexfile_report_func_t      report_func = NULL;
34
35 parse_hexfile_report_func_t parse_hexfile_set_reporting(parse_hexfile_report_func_t rf)
36 {
37         parse_hexfile_report_func_t     old_rf = report_func;
38         report_func = rf;
39         return old_rf;
40 }
41
42 static void chomp(char buf[])
43 {
44         size_t  last = strlen(buf) - 1;
45         while(last >= 0 && isspace(buf[last]))
46                 buf[last--] = '\0';
47 }
48
49 static int hexline_checksum(struct hexline *hexline)
50 {
51         unsigned int    i;
52         unsigned int    chksm = 0;
53         int             ll = hexline->d.content.header.ll;
54
55         for(i = 0; i <= sizeof(hexline->d.content.header) + ll; i++) {
56                 chksm += hexline->d.raw[i];
57         }
58         return chksm & 0xFF;
59 }
60
61 int dump_hexline(int recordno, struct hexline *line, FILE *fp)
62 {
63         uint8_t         ll;
64         uint16_t        offset;
65         uint8_t         tt;
66         uint8_t         old_chksum;
67         uint8_t         new_chksum;
68         uint8_t         *data;
69         unsigned int    i;
70
71         ll = line->d.content.header.ll;
72         offset = line->d.content.header.offset;
73         tt = line->d.content.header.tt;
74         fprintf(fp, ":%02X%04X%02X", ll, offset, tt);
75         data = line->d.content.tt_data.data;
76         for(i = 0; i < ll; i++) {
77                 fprintf(fp, "%02X", data[i]);
78         }
79         old_chksum = data[ll];
80         data[ll] = 0;
81         new_chksum = 0xFF - hexline_checksum(line) + 1;
82         data[ll] = old_chksum;
83         fprintf(fp, "%02X\n", new_chksum);
84         if(new_chksum != old_chksum) {
85                 if(report_func)
86                         report_func(LOG_ERR, "record #%d: new_chksum(%02X) != old_chksum(%02X)\n",
87                                         recordno, new_chksum, old_chksum);
88                 return 0;
89         }
90         return 1;
91 }
92
93 struct hexline  *new_hexline(uint8_t datalen, uint16_t offset, uint8_t tt)
94 {
95         struct hexline  *hexline;
96         size_t          allocsize;
97
98         allocsize = sizeof(struct hexline) + datalen + 1; /* checksum byte */
99         if((hexline = malloc(allocsize)) == NULL) {
100                 if(report_func)
101                         report_func(LOG_ERR, "No more memory\n");
102                 return NULL;
103         }
104         memset(hexline, 0, allocsize);
105         hexline->d.content.header.ll = datalen;
106         hexline->d.content.header.offset = offset;
107         hexline->d.content.header.tt = tt;
108         return hexline;
109 }
110
111 static int append_hexline(struct hexdata *hexdata, char *buf)
112 {
113         int             ret;
114         unsigned int    ll, offset, tt;
115         char            *p;
116         struct hexline  *hexline;
117         unsigned int    i;
118
119         if(hexdata->got_eof) {
120                 if(report_func)
121                         report_func(LOG_ERR, "Extranous data after EOF record\n");
122                 return -EINVAL;
123         }
124         if(hexdata->last_line >= hexdata->maxlines) {
125                 if(report_func)
126                         report_func(LOG_ERR, "Hexfile too large (maxline %d)\n", hexdata->maxlines);
127                 return -ENOMEM;
128         }
129         ret = sscanf(buf, "%02X%04X%02X", &ll, &offset, &tt);
130         if(ret != 3) {
131                 if(report_func)
132                         report_func(LOG_ERR, "Bad line header (only %d items out of 3 parsed)\n", ret);
133                 return -EINVAL;
134         }
135         switch(tt) {
136                 case TT_DATA:
137                         break;
138                 case TT_EOF:
139                         if(ll != 0) {
140                                 if(report_func)
141                                         report_func(LOG_ERR,
142                                                 "%d: Record %d(EOF): Bad len = %d\n",
143                                                 hexdata->last_line, tt, ll);
144                                 return -EINVAL;
145                         }
146                         if(offset != 0) {
147                                 if(report_func)
148                                         report_func(LOG_ERR,
149                                                 "%d: Record %d(EOF): Bad offset = %d\n",
150                                                 hexdata->last_line, tt, offset);
151                                 return -EINVAL;
152                         }
153                         hexdata->got_eof = 1;
154                         break;
155                 case TT_EXT_SEG:
156                         if(ll != 2) {
157                                 if(report_func)
158                                         report_func(LOG_ERR,
159                                                 "%d: Record %d(EXT_SEG): Bad len = %d\n",
160                                                 hexdata->last_line, tt, ll);
161                                 return -EINVAL;
162                         }
163                         if(offset != 0) {
164                                 if(report_func)
165                                         report_func(LOG_ERR,
166                                                 "%d: Record %d(EXT_SEG): Bad offset = %d\n",
167                                                 hexdata->last_line, tt, offset);
168                                 return -EINVAL;
169                         }
170                         break;
171                 case TT_START_SEG:
172                         if(ll != 4) {
173                                 if(report_func)
174                                         report_func(LOG_ERR,
175                                                 "%d: Record %d(START_SEG): Bad len = %d\n",
176                                                 hexdata->last_line, tt, ll);
177                                 return -EINVAL;
178                         }
179                         if(offset != 0) {
180                                 if(report_func)
181                                         report_func(LOG_ERR,
182                                                 "%d: Record %d(START_SEG): Bad offset = %d\n",
183                                                 hexdata->last_line, tt, offset);
184                                 return -EINVAL;
185                         }
186                         break;
187                 case TT_EXT_LIN:
188                         if(ll != 2) {
189                                 if(report_func)
190                                         report_func(LOG_ERR,
191                                                 "%d: Record %d(EXT_LIN): Bad len = %d\n",
192                                                 hexdata->last_line, tt, ll);
193                                 return -EINVAL;
194                         }
195                         if(offset != 0) {
196                                 if(report_func)
197                                         report_func(LOG_ERR,
198                                                 "%d: Record %d(EXT_LIN): Bad offset = %d\n",
199                                                 hexdata->last_line, tt, ll);
200                                 return -EINVAL;
201                         }
202                         break;
203                 case TT_START_LIN:      /* Unimplemented */
204                         if(ll != 4) {
205                                 if(report_func)
206                                         report_func(LOG_ERR,
207                                                 "%d: Record %d(EXT_LIN): Bad len = %d\n",
208                                                 hexdata->last_line, tt, ll);
209                                 return -EINVAL;
210                         }
211                         if(offset != 0) {
212                                 if(report_func)
213                                         report_func(LOG_ERR,
214                                                 "%d: Record %d(EXT_LIN): Bad offset = %d\n",
215                                                 hexdata->last_line, tt, ll);
216                                 return -EINVAL;
217                         }
218                         break;
219                 default:
220                         if(report_func)
221                                 report_func(LOG_ERR, "%d: Unimplemented record type %d: %s\n",
222                                         hexdata->last_line, tt, buf);
223                         return -EINVAL;
224         }
225         buf += 8;       /* Skip header */
226         if((hexline = new_hexline(ll, offset, tt)) == NULL) {
227                 if(report_func)
228                         report_func(LOG_ERR, "No more memory for hexfile lines\n");
229                 return -EINVAL;
230         }
231         p = buf;
232         for(i = 0; i < ll + 1; i++) {   /* include checksum */
233                 unsigned int    val;
234
235                 if((*p == '\0') || (*(p+1) == '\0')) {
236                         if(report_func)
237                                 report_func(LOG_ERR, "Short data string '%s'\n", buf);
238                         return -EINVAL;
239                 }
240                 ret = sscanf(p, "%02X", &val);
241                 if(ret != 1) {
242                         if(report_func)
243                                 report_func(LOG_ERR, "Bad data byte #%d\n", i);
244                         return -EINVAL;
245                 }
246                 hexline->d.content.tt_data.data[i] = val;
247                 p += 2;
248         }
249         if(hexline_checksum(hexline) != 0) {
250                 if(report_func) {
251                         report_func(LOG_ERR, "Bad checksum (%d instead of 0)\n",
252                                 hexline_checksum(hexline));
253                         dump_hexline(hexdata->last_line, hexline, stderr);
254                 }
255                 return -EINVAL;
256         }
257         hexdata->lines[hexdata->last_line] = hexline;
258         if(hexdata->got_eof)
259                 return 0;
260         hexdata->last_line++;
261         return 1;
262 }
263
264 void free_hexdata(struct hexdata *hexdata)
265 {
266         if(hexdata) {
267                 unsigned int    i;
268
269                 for(i = 0; i < hexdata->maxlines; i++)
270                         if(hexdata->lines[i] != NULL)
271                                 free(hexdata->lines[i]);
272                 free(hexdata);
273         }
274 }
275
276 int dump_hexfile(struct hexdata *hexdata, const char *outfile)
277 {
278         FILE            *fp;
279         unsigned int    i;
280
281         if(report_func)
282                 report_func(LOG_INFO, "Dumping hex data into '%s'\n", outfile);
283         if(!outfile || strcmp(outfile, "-") == 0)
284                 fp = stdout;
285         else if((fp = fopen(outfile, "w")) == NULL) {
286                 perror(outfile);
287                 exit(1);
288         }
289         for(i = 0; i <= hexdata->last_line; i++) {
290                 struct hexline  *line = hexdata->lines[i];
291                 if(!line) {
292                         if(report_func)
293                                 report_func(LOG_ERR, "Missing line at #%d\n", i);
294                         return -EINVAL;
295                 }
296                 if(!dump_hexline(i, line, fp))
297                         return -EINVAL;
298         }
299         return 0;
300 }
301
302 int dump_hexfile2(struct hexdata *hexdata, const char *outfile, uint8_t maxwidth)
303 {
304         FILE            *fp;
305         uint8_t         tt;
306         unsigned int    i;
307         struct hexline  *line;
308
309         if(report_func)
310                 report_func(LOG_INFO,
311                         "Dumping hex data into '%s' (maxwidth=%d)\n",
312                         outfile, maxwidth);
313         if(!outfile || strcmp(outfile, "-") == 0)
314                 fp = stdout;
315         else if((fp = fopen(outfile, "w")) == NULL) {
316                 perror(outfile);
317                 exit(1);
318         }
319         if(maxwidth == 0)
320                 maxwidth = UINT8_MAX;
321         for(i = 0; i <= hexdata->last_line; i++) {
322                 int             bytesleft = 0;
323                 int             extra_offset = 0;
324                 int             base_offset;
325                 uint8_t         *base_data;
326                 
327                 line = hexdata->lines[i];
328                 if(!line) {
329                         if(report_func)
330                                 report_func(LOG_ERR, "Missing line at #%d\n", i);
331                         return -EINVAL;
332                 }
333                 bytesleft = line->d.content.header.ll;
334                 /* split the line into several lines */
335                 tt = line->d.content.header.tt;
336                 base_offset = line->d.content.header.offset;
337                 base_data = line->d.content.tt_data.data;
338                 while (bytesleft > 0) {
339                         struct hexline  *extraline;
340                         uint8_t         new_chksum;
341                         unsigned int    curr_bytes = (bytesleft >= maxwidth) ? maxwidth : bytesleft;
342
343                         /* generate the new line */
344                         if((extraline = new_hexline(curr_bytes, base_offset + extra_offset, tt)) == NULL) {
345                                 if(report_func)
346                                         report_func(LOG_ERR, "No more memory for hexfile lines\n");
347                                 return -EINVAL;
348                         }
349                         memcpy(extraline->d.content.tt_data.data, base_data + extra_offset, curr_bytes);
350                         new_chksum = 0xFF - hexline_checksum(extraline) + 1;
351                         extraline->d.content.tt_data.data[curr_bytes] = new_chksum;
352                         /* print it */
353                         dump_hexline(i, extraline, fp);
354                         /* cleanups */
355                         free(extraline);
356                         extra_offset += curr_bytes;
357                         bytesleft -= curr_bytes;
358                 }
359         }
360         if(tt != TT_EOF) {
361                 if(report_func)
362                         report_func(LOG_ERR, "Missing EOF record\n");
363                 return -EINVAL;
364         }
365         dump_hexline(i, line, fp);
366         return 0;
367 }
368
369 void process_comment(struct hexdata *hexdata, char buf[])
370 {
371         char            *dollar_start;
372         char            *dollar_end;
373         const char      id_prefix[] = "Id: ";
374         char            tmp[BUFSIZ];
375         char            *p;
376         int             len;
377
378         if(report_func)
379                 report_func(LOG_INFO, "Comment: %s\n", buf + 1);
380         /* Search for RCS keywords */
381         if((dollar_start = strchr(buf, '$')) == NULL)
382                 return;
383         if((dollar_end = strchr(dollar_start + 1, '$')) == NULL)
384                 return;
385         /* Crop the '$' signs */
386         len = dollar_end - dollar_start;
387         len -= 2;
388         memcpy(tmp, dollar_start + 1, len);
389         tmp[len] = '\0';
390         p = tmp;
391         if(strstr(tmp, id_prefix) == NULL)
392                 return;
393         p += strlen(id_prefix);
394         if((p = strchr(p, ' ')) == NULL)
395                 return;
396         p++;
397         snprintf(hexdata->version_info, BUFSIZ, "%s", p);
398         if((p = strchr(hexdata->version_info, ' ')) != NULL)
399                 *p = '\0';
400 }
401
402 struct hexdata *parse_hexfile(const char *fname, unsigned int maxlines)
403 {
404         FILE            *fp;
405         struct hexdata  *hexdata = NULL;
406         int             datasize;
407         char            buf[BUFSIZ];
408         int             line;
409         int             dos_eof = 0;
410         int             ret;
411
412         assert(fname != NULL);
413         if(report_func)
414                 report_func(LOG_INFO, "Parsing %s\n", fname);
415         datasize = sizeof(struct hexdata) + maxlines * sizeof(char *);
416         hexdata = (struct hexdata *)malloc(datasize);
417         if(!hexdata) {
418                 if(report_func)
419                         report_func(LOG_ERR, "Failed to allocate %d bytes for hexfile contents\n", datasize);
420                 goto err;
421         }
422         memset(hexdata, 0, datasize);
423         hexdata->maxlines = maxlines;
424         if((fp = fopen(fname, "r")) == NULL) {
425                 if(report_func)
426                         report_func(LOG_ERR, "Failed to open hexfile '%s'\n", fname);
427                 goto err;
428         }
429         snprintf(hexdata->fname, PATH_MAX, "%s", fname);
430         for(line = 1; fgets(buf, BUFSIZ, fp); line++) {
431                 if(dos_eof) {
432                         if(report_func)
433                                 report_func(LOG_ERR, "%s:%d - Got DOS EOF character before true EOF\n", fname, line);
434                         goto err;
435                 }
436                 if(buf[0] == 0x1A && buf[1] == '\0') { /* DOS EOF char */
437                         dos_eof = 1;
438                         continue;
439                 }
440                 chomp(buf);
441                 if(buf[0] == '\0') {
442                                 if(report_func)
443                                         report_func(LOG_ERR, "%s:%d - Short line\n", fname, line);
444                                 goto err;
445                 }
446                 if(buf[0] == '#') {
447                         process_comment(hexdata, buf);
448                         continue;
449                 }
450                 if(buf[0] != ':') {
451                         if(report_func)
452                                 report_func(LOG_ERR, "%s:%d - Line begins with 0x%X\n", fname, line, buf[0]);
453                         goto err;
454                 }
455                 if((ret = append_hexline(hexdata, buf + 1)) < 0) {
456                         if(report_func)
457                                 report_func(LOG_ERR, "%s:%d - Failed parsing.\n", fname, line);
458                         goto err;
459                 }
460         }
461         fclose(fp);
462         if(report_func)
463                 report_func(LOG_INFO, "%s parsed OK\n", fname);
464         return hexdata;
465 err:
466         free_hexdata(hexdata);
467         return NULL;
468 }
469
470 void dump_binary(struct hexdata *hexdata, const char *outfile)
471 {
472         FILE            *fp;
473         unsigned int    i;
474         size_t          len;
475
476         if(report_func)
477                 report_func(LOG_INFO, "Dumping binary data into '%s'\n", outfile);
478         if((fp = fopen(outfile, "w")) == NULL) {
479                 perror(outfile);
480                 exit(1);
481         }
482         for(i = 0; i < hexdata->maxlines; i++) {
483                 struct hexline  *hexline = hexdata->lines[i];
484
485                 if(!hexline)
486                         break;
487                 switch(hexline->d.content.header.tt) {
488                 case TT_EOF:
489                         if(report_func)
490                                 report_func(LOG_INFO, "\ndump: good EOF record");
491                         break;
492                 case TT_DATA:
493                         if(report_func)
494                                 report_func(LOG_INFO, "dump: %6d\r", i);
495                         len = hexline->d.content.header.ll;
496                         if(fwrite(hexline->d.content.tt_data.data, 1, len, fp) != len) {
497                                 perror("write");
498                                 exit(1);
499                         }
500                         break;
501                 case TT_EXT_SEG:
502                 case TT_START_SEG:
503                 case TT_EXT_LIN:
504                 case TT_START_LIN:
505                         if(report_func)
506                                 report_func(LOG_INFO,
507                                         "\ndump(%d): ignored record type %d",
508                                         i, hexline->d.content.header.tt);
509                         break;
510                 default:
511                         if(report_func)
512                                 report_func(LOG_ERR, "dump: Unknown record type %d\n",
513                                         hexline->d.content.header.tt);
514                         exit(1);
515                 }
516         }
517         if(report_func)
518                 report_func(LOG_INFO, "\nDump finished\n");
519         fclose(fp);
520 }
521
522 void gen_hexline(const uint8_t *data, uint16_t addr, size_t len, FILE *output)
523 {
524         struct hexline  *hexline;
525
526         if(!data) {
527                 fprintf(output, ":%02X%04X%02XFF\n", 0, 0, TT_EOF);
528                 return;
529         }
530         if((hexline = new_hexline(len, addr, (!data) ? TT_EOF : TT_DATA)) == NULL) {
531                 if(report_func)
532                         report_func(LOG_ERR, "No more memory\n");
533                 return;
534         }
535         if(data)
536                 memcpy(&hexline->d.content.tt_data, data, len);
537         dump_hexline(0, hexline, output);
538         free(hexline);
539 }
540
541 /*
542  * Algorithm lifted of sum(1) implementation from coreutils.
543  * We chose the default algorithm (BSD style).
544  */
545 int bsd_checksum(struct hexdata *hexdata)
546 {
547         unsigned int    i;
548         size_t          len;
549         int             ck = 0;
550
551         for(i = 0; i < hexdata->maxlines; i++) {
552                 struct hexline  *hexline = hexdata->lines[i];
553                 unsigned char   *p;
554
555                 if(!hexline)
556                         break;
557                 if(hexline->d.content.header.tt == TT_EOF)
558                         continue;
559                 len = hexline->d.content.header.ll;
560                 p = hexline->d.content.tt_data.data;
561                 for(; len; p++, len--) {
562                         ck = (ck >> 1) + ((ck & 1) << 15);
563                         ck += *p;
564                         ck &= 0xffff;   /* Keep it within bounds. */
565                 }
566         }
567         return ck;
568 }