f105a09eda
* While technically Git can support $ID keywords, in practice they are not used. * GCC now give a warning about ''rcsid' defined but not used' for some of those. Let's just remove the unused rcsid.
567 lines
14 KiB
C
567 lines
14 KiB
C
/*
|
|
* Written by Oron Peled <oron@actcom.co.il>
|
|
* Copyright (C) 2006, 2007, 2008, Xorcom
|
|
*
|
|
* All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
#include <ctype.h>
|
|
#include "hexfile.h"
|
|
|
|
static parse_hexfile_report_func_t report_func = NULL;
|
|
|
|
parse_hexfile_report_func_t parse_hexfile_set_reporting(parse_hexfile_report_func_t rf)
|
|
{
|
|
parse_hexfile_report_func_t old_rf = report_func;
|
|
report_func = rf;
|
|
return old_rf;
|
|
}
|
|
|
|
static void chomp(char buf[])
|
|
{
|
|
size_t last = strlen(buf) - 1;
|
|
while(last >= 0 && isspace(buf[last]))
|
|
buf[last--] = '\0';
|
|
}
|
|
|
|
static int hexline_checksum(struct hexline *hexline)
|
|
{
|
|
unsigned int i;
|
|
unsigned int chksm = 0;
|
|
int ll = hexline->d.content.header.ll;
|
|
|
|
for(i = 0; i <= sizeof(hexline->d.content.header) + ll; i++) {
|
|
chksm += hexline->d.raw[i];
|
|
}
|
|
return chksm & 0xFF;
|
|
}
|
|
|
|
int dump_hexline(int recordno, struct hexline *line, FILE *fp)
|
|
{
|
|
uint8_t ll;
|
|
uint16_t offset;
|
|
uint8_t tt;
|
|
uint8_t old_chksum;
|
|
uint8_t new_chksum;
|
|
uint8_t *data;
|
|
unsigned int i;
|
|
|
|
ll = line->d.content.header.ll;
|
|
offset = line->d.content.header.offset;
|
|
tt = line->d.content.header.tt;
|
|
fprintf(fp, ":%02X%04X%02X", ll, offset, tt);
|
|
data = line->d.content.tt_data.data;
|
|
for(i = 0; i < ll; i++) {
|
|
fprintf(fp, "%02X", data[i]);
|
|
}
|
|
old_chksum = data[ll];
|
|
data[ll] = 0;
|
|
new_chksum = 0xFF - hexline_checksum(line) + 1;
|
|
data[ll] = old_chksum;
|
|
fprintf(fp, "%02X\n", new_chksum);
|
|
if(new_chksum != old_chksum) {
|
|
if(report_func)
|
|
report_func(LOG_ERR, "record #%d: new_chksum(%02X) != old_chksum(%02X)\n",
|
|
recordno, new_chksum, old_chksum);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
struct hexline *new_hexline(uint8_t datalen, uint16_t offset, uint8_t tt)
|
|
{
|
|
struct hexline *hexline;
|
|
size_t allocsize;
|
|
|
|
allocsize = sizeof(struct hexline) + datalen + 1; /* checksum byte */
|
|
if((hexline = malloc(allocsize)) == NULL) {
|
|
if(report_func)
|
|
report_func(LOG_ERR, "No more memory\n");
|
|
return NULL;
|
|
}
|
|
memset(hexline, 0, allocsize);
|
|
hexline->d.content.header.ll = datalen;
|
|
hexline->d.content.header.offset = offset;
|
|
hexline->d.content.header.tt = tt;
|
|
return hexline;
|
|
}
|
|
|
|
static int append_hexline(struct hexdata *hexdata, char *buf)
|
|
{
|
|
int ret;
|
|
unsigned int ll, offset, tt;
|
|
char *p;
|
|
struct hexline *hexline;
|
|
unsigned int i;
|
|
|
|
if(hexdata->got_eof) {
|
|
if(report_func)
|
|
report_func(LOG_ERR, "Extranous data after EOF record\n");
|
|
return -EINVAL;
|
|
}
|
|
if(hexdata->last_line >= hexdata->maxlines) {
|
|
if(report_func)
|
|
report_func(LOG_ERR, "Hexfile too large (maxline %d)\n", hexdata->maxlines);
|
|
return -ENOMEM;
|
|
}
|
|
ret = sscanf(buf, "%02X%04X%02X", &ll, &offset, &tt);
|
|
if(ret != 3) {
|
|
if(report_func)
|
|
report_func(LOG_ERR, "Bad line header (only %d items out of 3 parsed)\n", ret);
|
|
return -EINVAL;
|
|
}
|
|
switch(tt) {
|
|
case TT_DATA:
|
|
break;
|
|
case TT_EOF:
|
|
if(ll != 0) {
|
|
if(report_func)
|
|
report_func(LOG_ERR,
|
|
"%d: Record %d(EOF): Bad len = %d\n",
|
|
hexdata->last_line, tt, ll);
|
|
return -EINVAL;
|
|
}
|
|
if(offset != 0) {
|
|
if(report_func)
|
|
report_func(LOG_ERR,
|
|
"%d: Record %d(EOF): Bad offset = %d\n",
|
|
hexdata->last_line, tt, offset);
|
|
return -EINVAL;
|
|
}
|
|
hexdata->got_eof = 1;
|
|
break;
|
|
case TT_EXT_SEG:
|
|
if(ll != 2) {
|
|
if(report_func)
|
|
report_func(LOG_ERR,
|
|
"%d: Record %d(EXT_SEG): Bad len = %d\n",
|
|
hexdata->last_line, tt, ll);
|
|
return -EINVAL;
|
|
}
|
|
if(offset != 0) {
|
|
if(report_func)
|
|
report_func(LOG_ERR,
|
|
"%d: Record %d(EXT_SEG): Bad offset = %d\n",
|
|
hexdata->last_line, tt, offset);
|
|
return -EINVAL;
|
|
}
|
|
break;
|
|
case TT_START_SEG:
|
|
if(ll != 4) {
|
|
if(report_func)
|
|
report_func(LOG_ERR,
|
|
"%d: Record %d(START_SEG): Bad len = %d\n",
|
|
hexdata->last_line, tt, ll);
|
|
return -EINVAL;
|
|
}
|
|
if(offset != 0) {
|
|
if(report_func)
|
|
report_func(LOG_ERR,
|
|
"%d: Record %d(START_SEG): Bad offset = %d\n",
|
|
hexdata->last_line, tt, offset);
|
|
return -EINVAL;
|
|
}
|
|
break;
|
|
case TT_EXT_LIN:
|
|
if(ll != 2) {
|
|
if(report_func)
|
|
report_func(LOG_ERR,
|
|
"%d: Record %d(EXT_LIN): Bad len = %d\n",
|
|
hexdata->last_line, tt, ll);
|
|
return -EINVAL;
|
|
}
|
|
if(offset != 0) {
|
|
if(report_func)
|
|
report_func(LOG_ERR,
|
|
"%d: Record %d(EXT_LIN): Bad offset = %d\n",
|
|
hexdata->last_line, tt, ll);
|
|
return -EINVAL;
|
|
}
|
|
break;
|
|
case TT_START_LIN: /* Unimplemented */
|
|
if(ll != 4) {
|
|
if(report_func)
|
|
report_func(LOG_ERR,
|
|
"%d: Record %d(EXT_LIN): Bad len = %d\n",
|
|
hexdata->last_line, tt, ll);
|
|
return -EINVAL;
|
|
}
|
|
if(offset != 0) {
|
|
if(report_func)
|
|
report_func(LOG_ERR,
|
|
"%d: Record %d(EXT_LIN): Bad offset = %d\n",
|
|
hexdata->last_line, tt, ll);
|
|
return -EINVAL;
|
|
}
|
|
break;
|
|
default:
|
|
if(report_func)
|
|
report_func(LOG_ERR, "%d: Unimplemented record type %d: %s\n",
|
|
hexdata->last_line, tt, buf);
|
|
return -EINVAL;
|
|
}
|
|
buf += 8; /* Skip header */
|
|
if((hexline = new_hexline(ll, offset, tt)) == NULL) {
|
|
if(report_func)
|
|
report_func(LOG_ERR, "No more memory for hexfile lines\n");
|
|
return -EINVAL;
|
|
}
|
|
p = buf;
|
|
for(i = 0; i < ll + 1; i++) { /* include checksum */
|
|
unsigned int val;
|
|
|
|
if((*p == '\0') || (*(p+1) == '\0')) {
|
|
if(report_func)
|
|
report_func(LOG_ERR, "Short data string '%s'\n", buf);
|
|
return -EINVAL;
|
|
}
|
|
ret = sscanf(p, "%02X", &val);
|
|
if(ret != 1) {
|
|
if(report_func)
|
|
report_func(LOG_ERR, "Bad data byte #%d\n", i);
|
|
return -EINVAL;
|
|
}
|
|
hexline->d.content.tt_data.data[i] = val;
|
|
p += 2;
|
|
}
|
|
if(hexline_checksum(hexline) != 0) {
|
|
if(report_func) {
|
|
report_func(LOG_ERR, "Bad checksum (%d instead of 0)\n",
|
|
hexline_checksum(hexline));
|
|
dump_hexline(hexdata->last_line, hexline, stderr);
|
|
}
|
|
return -EINVAL;
|
|
}
|
|
hexdata->lines[hexdata->last_line] = hexline;
|
|
if(hexdata->got_eof)
|
|
return 0;
|
|
hexdata->last_line++;
|
|
return 1;
|
|
}
|
|
|
|
void free_hexdata(struct hexdata *hexdata)
|
|
{
|
|
if(hexdata) {
|
|
unsigned int i;
|
|
|
|
for(i = 0; i < hexdata->maxlines; i++)
|
|
if(hexdata->lines[i] != NULL)
|
|
free(hexdata->lines[i]);
|
|
free(hexdata);
|
|
}
|
|
}
|
|
|
|
int dump_hexfile(struct hexdata *hexdata, const char *outfile)
|
|
{
|
|
FILE *fp;
|
|
unsigned int i;
|
|
|
|
if(report_func)
|
|
report_func(LOG_INFO, "Dumping hex data into '%s'\n", outfile);
|
|
if(!outfile || strcmp(outfile, "-") == 0)
|
|
fp = stdout;
|
|
else if((fp = fopen(outfile, "w")) == NULL) {
|
|
perror(outfile);
|
|
exit(1);
|
|
}
|
|
for(i = 0; i <= hexdata->last_line; i++) {
|
|
struct hexline *line = hexdata->lines[i];
|
|
if(!line) {
|
|
if(report_func)
|
|
report_func(LOG_ERR, "Missing line at #%d\n", i);
|
|
return -EINVAL;
|
|
}
|
|
if(!dump_hexline(i, line, fp))
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int dump_hexfile2(struct hexdata *hexdata, const char *outfile, uint8_t maxwidth)
|
|
{
|
|
FILE *fp;
|
|
uint8_t tt;
|
|
unsigned int i;
|
|
struct hexline *line;
|
|
|
|
if(report_func)
|
|
report_func(LOG_INFO,
|
|
"Dumping hex data into '%s' (maxwidth=%d)\n",
|
|
outfile, maxwidth);
|
|
if(!outfile || strcmp(outfile, "-") == 0)
|
|
fp = stdout;
|
|
else if((fp = fopen(outfile, "w")) == NULL) {
|
|
perror(outfile);
|
|
exit(1);
|
|
}
|
|
if(maxwidth == 0)
|
|
maxwidth = UINT8_MAX;
|
|
for(i = 0; i <= hexdata->last_line; i++) {
|
|
int bytesleft = 0;
|
|
int extra_offset = 0;
|
|
int base_offset;
|
|
uint8_t *base_data;
|
|
|
|
line = hexdata->lines[i];
|
|
if(!line) {
|
|
if(report_func)
|
|
report_func(LOG_ERR, "Missing line at #%d\n", i);
|
|
return -EINVAL;
|
|
}
|
|
bytesleft = line->d.content.header.ll;
|
|
/* split the line into several lines */
|
|
tt = line->d.content.header.tt;
|
|
base_offset = line->d.content.header.offset;
|
|
base_data = line->d.content.tt_data.data;
|
|
while (bytesleft > 0) {
|
|
struct hexline *extraline;
|
|
uint8_t new_chksum;
|
|
unsigned int curr_bytes = (bytesleft >= maxwidth) ? maxwidth : bytesleft;
|
|
|
|
/* generate the new line */
|
|
if((extraline = new_hexline(curr_bytes, base_offset + extra_offset, tt)) == NULL) {
|
|
if(report_func)
|
|
report_func(LOG_ERR, "No more memory for hexfile lines\n");
|
|
return -EINVAL;
|
|
}
|
|
memcpy(extraline->d.content.tt_data.data, base_data + extra_offset, curr_bytes);
|
|
new_chksum = 0xFF - hexline_checksum(extraline) + 1;
|
|
extraline->d.content.tt_data.data[curr_bytes] = new_chksum;
|
|
/* print it */
|
|
dump_hexline(i, extraline, fp);
|
|
/* cleanups */
|
|
free(extraline);
|
|
extra_offset += curr_bytes;
|
|
bytesleft -= curr_bytes;
|
|
}
|
|
}
|
|
if(tt != TT_EOF) {
|
|
if(report_func)
|
|
report_func(LOG_ERR, "Missing EOF record\n");
|
|
return -EINVAL;
|
|
}
|
|
dump_hexline(i, line, fp);
|
|
return 0;
|
|
}
|
|
|
|
void process_comment(struct hexdata *hexdata, char buf[])
|
|
{
|
|
char *dollar_start;
|
|
char *dollar_end;
|
|
const char id_prefix[] = "Id: ";
|
|
char tmp[BUFSIZ];
|
|
char *p;
|
|
int len;
|
|
|
|
if(report_func)
|
|
report_func(LOG_INFO, "Comment: %s\n", buf + 1);
|
|
/* Search for RCS keywords */
|
|
if((dollar_start = strchr(buf, '$')) == NULL)
|
|
return;
|
|
if((dollar_end = strchr(dollar_start + 1, '$')) == NULL)
|
|
return;
|
|
/* Crop the '$' signs */
|
|
len = dollar_end - dollar_start;
|
|
len -= 2;
|
|
memcpy(tmp, dollar_start + 1, len);
|
|
tmp[len] = '\0';
|
|
p = tmp;
|
|
if(strstr(tmp, id_prefix) == NULL)
|
|
return;
|
|
p += strlen(id_prefix);
|
|
if((p = strchr(p, ' ')) == NULL)
|
|
return;
|
|
p++;
|
|
snprintf(hexdata->version_info, BUFSIZ, "%s", p);
|
|
if((p = strchr(hexdata->version_info, ' ')) != NULL)
|
|
*p = '\0';
|
|
}
|
|
|
|
struct hexdata *parse_hexfile(const char *fname, unsigned int maxlines)
|
|
{
|
|
FILE *fp;
|
|
struct hexdata *hexdata = NULL;
|
|
int datasize;
|
|
char buf[BUFSIZ];
|
|
int line;
|
|
int dos_eof = 0;
|
|
int ret;
|
|
|
|
assert(fname != NULL);
|
|
if(report_func)
|
|
report_func(LOG_INFO, "Parsing %s\n", fname);
|
|
datasize = sizeof(struct hexdata) + maxlines * sizeof(char *);
|
|
hexdata = (struct hexdata *)malloc(datasize);
|
|
if(!hexdata) {
|
|
if(report_func)
|
|
report_func(LOG_ERR, "Failed to allocate %d bytes for hexfile contents\n", datasize);
|
|
goto err;
|
|
}
|
|
memset(hexdata, 0, datasize);
|
|
hexdata->maxlines = maxlines;
|
|
if((fp = fopen(fname, "r")) == NULL) {
|
|
if(report_func)
|
|
report_func(LOG_ERR, "Failed to open hexfile '%s'\n", fname);
|
|
goto err;
|
|
}
|
|
snprintf(hexdata->fname, PATH_MAX, "%s", fname);
|
|
for(line = 1; fgets(buf, BUFSIZ, fp); line++) {
|
|
if(dos_eof) {
|
|
if(report_func)
|
|
report_func(LOG_ERR, "%s:%d - Got DOS EOF character before true EOF\n", fname, line);
|
|
goto err;
|
|
}
|
|
if(buf[0] == 0x1A && buf[1] == '\0') { /* DOS EOF char */
|
|
dos_eof = 1;
|
|
continue;
|
|
}
|
|
chomp(buf);
|
|
if(buf[0] == '\0') {
|
|
if(report_func)
|
|
report_func(LOG_ERR, "%s:%d - Short line\n", fname, line);
|
|
goto err;
|
|
}
|
|
if(buf[0] == '#') {
|
|
process_comment(hexdata, buf);
|
|
continue;
|
|
}
|
|
if(buf[0] != ':') {
|
|
if(report_func)
|
|
report_func(LOG_ERR, "%s:%d - Line begins with 0x%X\n", fname, line, buf[0]);
|
|
goto err;
|
|
}
|
|
if((ret = append_hexline(hexdata, buf + 1)) < 0) {
|
|
if(report_func)
|
|
report_func(LOG_ERR, "%s:%d - Failed parsing.\n", fname, line);
|
|
goto err;
|
|
}
|
|
}
|
|
fclose(fp);
|
|
if(report_func)
|
|
report_func(LOG_INFO, "%s parsed OK\n", fname);
|
|
return hexdata;
|
|
err:
|
|
free_hexdata(hexdata);
|
|
return NULL;
|
|
}
|
|
|
|
void dump_binary(struct hexdata *hexdata, const char *outfile)
|
|
{
|
|
FILE *fp;
|
|
unsigned int i;
|
|
size_t len;
|
|
|
|
if(report_func)
|
|
report_func(LOG_INFO, "Dumping binary data into '%s'\n", outfile);
|
|
if((fp = fopen(outfile, "w")) == NULL) {
|
|
perror(outfile);
|
|
exit(1);
|
|
}
|
|
for(i = 0; i < hexdata->maxlines; i++) {
|
|
struct hexline *hexline = hexdata->lines[i];
|
|
|
|
if(!hexline)
|
|
break;
|
|
switch(hexline->d.content.header.tt) {
|
|
case TT_EOF:
|
|
if(report_func)
|
|
report_func(LOG_INFO, "\ndump: good EOF record");
|
|
break;
|
|
case TT_DATA:
|
|
if(report_func)
|
|
report_func(LOG_INFO, "dump: %6d\r", i);
|
|
len = hexline->d.content.header.ll;
|
|
if(fwrite(hexline->d.content.tt_data.data, 1, len, fp) != len) {
|
|
perror("write");
|
|
exit(1);
|
|
}
|
|
break;
|
|
case TT_EXT_SEG:
|
|
case TT_START_SEG:
|
|
case TT_EXT_LIN:
|
|
case TT_START_LIN:
|
|
if(report_func)
|
|
report_func(LOG_INFO,
|
|
"\ndump(%d): ignored record type %d",
|
|
i, hexline->d.content.header.tt);
|
|
break;
|
|
default:
|
|
if(report_func)
|
|
report_func(LOG_ERR, "dump: Unknown record type %d\n",
|
|
hexline->d.content.header.tt);
|
|
exit(1);
|
|
}
|
|
}
|
|
if(report_func)
|
|
report_func(LOG_INFO, "\nDump finished\n");
|
|
fclose(fp);
|
|
}
|
|
|
|
void gen_hexline(const uint8_t *data, uint16_t addr, size_t len, FILE *output)
|
|
{
|
|
struct hexline *hexline;
|
|
|
|
if(!data) {
|
|
fprintf(output, ":%02X%04X%02XFF\n", 0, 0, TT_EOF);
|
|
return;
|
|
}
|
|
if((hexline = new_hexline(len, addr, (!data) ? TT_EOF : TT_DATA)) == NULL) {
|
|
if(report_func)
|
|
report_func(LOG_ERR, "No more memory\n");
|
|
return;
|
|
}
|
|
if(data)
|
|
memcpy(&hexline->d.content.tt_data, data, len);
|
|
dump_hexline(0, hexline, output);
|
|
free(hexline);
|
|
}
|
|
|
|
/*
|
|
* Algorithm lifted of sum(1) implementation from coreutils.
|
|
* We chose the default algorithm (BSD style).
|
|
*/
|
|
int bsd_checksum(struct hexdata *hexdata)
|
|
{
|
|
unsigned int i;
|
|
size_t len;
|
|
int ck = 0;
|
|
|
|
for(i = 0; i < hexdata->maxlines; i++) {
|
|
struct hexline *hexline = hexdata->lines[i];
|
|
unsigned char *p;
|
|
|
|
if(!hexline)
|
|
break;
|
|
if(hexline->d.content.header.tt == TT_EOF)
|
|
continue;
|
|
len = hexline->d.content.header.ll;
|
|
p = hexline->d.content.tt_data.data;
|
|
for(; len; p++, len--) {
|
|
ck = (ck >> 1) + ((ck & 1) << 15);
|
|
ck += *p;
|
|
ck &= 0xffff; /* Keep it within bounds. */
|
|
}
|
|
}
|
|
return ck;
|
|
}
|