-
Notifications
You must be signed in to change notification settings - Fork 36
/
Copy pathihex2bin.c
194 lines (184 loc) · 6.32 KB
/
ihex2bin.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
/*
* ihex2bin.c: Read Intel HEX format, write binary data.
*
* By default reads from stdin and writes to stdout. The command-line
* options `-i` and `-o` can be used to specify the input and output
* file, respectively. Specifying an output file allows sparse writes.
*
* NOTE: Many Intel HEX files produced by compilers/etc have data
* beginning at an address greater than zero, potentially causing very
* unnecessarily large files to be created. The command-line option
* `-a` can be used to specify the start address of the output file,
* i.e., the value will be subtracted from the IHEX addresses (the
* result must not be negative).
*
* Alternatively, the command-line option `-A` sets the address offset
* to the first address that would be written (i.e., first byte of
* data written will be at address 0).
*
* Copyright (c) 2013-2019 Kimmo Kulovesi, https://arkku.com
* Provided with absolutely no warranty, use at your own risk only.
* Distribute freely, mark modified copies as such.
*/
#include "kk_ihex_read.h"
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#define AUTODETECT_ADDRESS (~0UL)
static FILE *outfile;
static unsigned long line_number = 1L;
static unsigned long file_position = 0L;
static unsigned long address_offset = 0UL;
static bool debug_enabled = 0;
int
main (int argc, char *argv[]) {
struct ihex_state ihex;
FILE *infile = stdin;
ihex_count_t count;
char buf[256];
outfile = stdout;
while (--argc) {
char *arg = *(++argv);
if (arg[0] == '-' && arg[1] && arg[2] == '\0') {
switch (arg[1]) {
case 'a':
if (--argc == 0) {
goto invalid_argument;
}
++argv;
errno = 0;
address_offset = strtoul(*argv, &arg, 0);
if (errno || arg == *argv) {
errno = errno ? errno : EINVAL;
goto argument_error;
}
break;
case 'A':
address_offset = AUTODETECT_ADDRESS;
break;
case 'i':
if (--argc == 0) {
goto invalid_argument;
}
++argv;
if (!(infile = fopen(*argv, "r"))) {
goto argument_error;
}
break;
case 'o':
if (--argc == 0) {
goto invalid_argument;
}
++argv;
if (!(outfile = fopen(*argv, "wb"))) {
goto argument_error;
}
break;
case 'v':
debug_enabled = 1;
break;
case 'h':
case '?':
arg = NULL;
goto usage;
default:
goto invalid_argument;
}
continue;
}
invalid_argument:
(void) fprintf(stderr, "Invalid argument: %s\n", arg);
usage:
(void) fprintf(stderr, "kk_ihex " KK_IHEX_VERSION
" - Copyright (c) 2013-2015 Kimmo Kulovesi\n");
(void) fprintf(stderr, "Usage: ihex2bin ([-a <address_offset>]|[-A])"
" [-o <out.bin>] [-i <in.hex>] [-v]\n");
return arg ? EXIT_FAILURE : EXIT_SUCCESS;
argument_error:
perror(*argv);
return EXIT_FAILURE;
}
ihex_read_at_address(&ihex, (address_offset != AUTODETECT_ADDRESS) ?
(ihex_address_t) address_offset :
0);
while (fgets(buf, sizeof(buf), infile)) {
count = (ihex_count_t) strlen(buf);
ihex_read_bytes(&ihex, buf, count);
line_number += (count && buf[count - 1] == '\n');
}
ihex_end_read(&ihex);
if (infile != stdin) {
(void) fclose(infile);
}
return EXIT_SUCCESS;
}
ihex_bool_t
ihex_data_read (struct ihex_state *ihex,
ihex_record_type_t type,
ihex_bool_t error) {
if (error) {
(void) fprintf(stderr, "Checksum error on line %lu\n", line_number);
exit(EXIT_FAILURE);
}
if ((error = (ihex->length < ihex->line_length))) {
(void) fprintf(stderr, "Line length error on line %lu\n", line_number);
exit(EXIT_FAILURE);
}
if (!outfile) {
(void) fprintf(stderr, "Excess data after end of file record\n");
exit(EXIT_FAILURE);
}
if (type == IHEX_DATA_RECORD) {
unsigned long address = (unsigned long) IHEX_LINEAR_ADDRESS(ihex);
if (address < address_offset) {
if (address_offset == AUTODETECT_ADDRESS) {
// autodetect initial address
address_offset = address;
if (debug_enabled) {
(void) fprintf(stderr, "Address offset: 0x%lx\n",
address_offset);
}
} else {
(void) fprintf(stderr, "Address underflow on line %lu\n",
line_number);
exit(EXIT_FAILURE);
}
}
address -= address_offset;
if (address != file_position) {
if (debug_enabled) {
(void) fprintf(stderr,
"Seeking from 0x%lx to 0x%lx on line %lu\n",
file_position, address, line_number);
}
if (outfile == stdout || fseek(outfile, (long) address, SEEK_SET)) {
if (file_position < address) {
// "seek" forward in stdout by writing NUL bytes
do {
(void) fputc('\0', outfile);
} while (++file_position < address);
} else {
perror("fseek");
exit(EXIT_FAILURE);
}
}
file_position = address;
}
if (!fwrite(ihex->data, ihex->length, 1, outfile)) {
perror("fwrite");
exit(EXIT_FAILURE);
}
file_position += ihex->length;
} else if (type == IHEX_END_OF_FILE_RECORD) {
if (debug_enabled) {
(void) fprintf(stderr, "%lu bytes written\n", file_position);
}
if (outfile != stdout) {
(void) fclose(outfile);
}
outfile = NULL;
}
return true;
}