-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathsrec2bin.c
224 lines (212 loc) · 7.38 KB
/
srec2bin.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
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
/*
* srec2bin.c: Read Motorola SREC 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 SREC files produced by compilers/etc have data beginning
* at an address greater than zero, potentially causing initial padding
* in the output (up to gigabytes of unnecessary length). The command-line
* option `-a` can be used to specify an offset, i.e., the value will be
* subtracted from the SREC 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) 2015 Kimmo Kulovesi, http://arkku.com
* Provided with absolutely no warranty, use at your own risk only.
* Distribute freely, mark modified copies as such.
*/
#include "kk_srec.h"
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#define AUTODETECT_ADDRESS (~(srec_address_t)0)
static FILE *outfile;
static unsigned long line_number = 1UL;
static srec_address_t file_position = 0L;
static srec_address_t address_offset = 0;
static bool debug_enabled = 0;
int
main (int argc, char *argv[]) {
struct srec_state srec;
FILE *infile = stdin;
srec_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 = (srec_address_t) strtol(*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_srec " KK_SREC_VERSION
" - Copyright (c) 2015 Kimmo Kulovesi\n");
(void) fprintf(stderr, "Usage: srec2bin ([-a <address_offset>]|[-A])"
" [-o <out.bin>] [-i <in.srec>] [-v]\n");
return arg ? EXIT_FAILURE : EXIT_SUCCESS;
argument_error:
perror(*argv);
return EXIT_FAILURE;
}
srec_begin_read(&srec);
while (fgets(buf, sizeof(buf), infile)) {
count = (srec_count_t) strlen(buf);
srec_read_bytes(&srec, buf, count);
line_number += (count && buf[count - 1] == '\n');
}
srec_end_read(&srec);
if (infile != stdin) {
(void) fclose(infile);
}
return EXIT_SUCCESS;
}
void
srec_data_read(struct srec_state *srec,
srec_record_number_t record_type,
srec_address_t address,
uint8_t *data, srec_count_t length,
srec_bool_t error) {
if (srec->length != srec->byte_count) {
if (srec->byte_count > SREC_LINE_MAX_BYTE_COUNT) {
(void) fprintf(stderr, "Error: Byte count too high on line %lu\n",
line_number);
} else {
(void) fprintf(stderr, "Error: Byte count mismatch on line %lu\n",
line_number);
}
exit(EXIT_FAILURE);
}
if (error) {
(void) fprintf(stderr, "Error: Checksum mismatch on line %lu\n",
line_number);
exit(EXIT_FAILURE);
}
if (SREC_IS_DATA(record_type)) {
if (!outfile) {
(void) fprintf(stderr, "Error: Excess data after termination\n");
exit(EXIT_FAILURE);
}
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",
(unsigned long) 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",
(unsigned long) file_position,
(unsigned long) 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(data, (size_t) length, 1, outfile)) {
perror("fwrite");
exit(EXIT_FAILURE);
}
file_position += (srec_address_t) length;
} else if (SREC_IS_TERMINATION(record_type)) {
if (address || debug_enabled) {
(void) fprintf(stderr, "Program start address: 0x%0*lx\n",
SREC_ADDRESS_BYTE_COUNT(record_type) * 2,
(unsigned long) address);
}
if (debug_enabled) {
(void) fprintf(stderr, "%lu bytes written\n",
(unsigned long) file_position);
}
if (outfile != stdout) {
(void) fclose(outfile);
}
outfile = NULL;
} else if (record_type == 0) {
// Print the header record unless it contains control characters
srec_bool_t is_printable = 1;
srec_count_t i;
for (i = 0; i < length && data[i]; ++i) {
if (iscntrl(data[i])) {
is_printable = 0;
break;
}
}
if (is_printable) {
data[length] = '\0';
(void) fprintf(stderr, "Header on line %lu: %s\n",
line_number, data);
}
}
}