git.haldean.org ana / 4e054d6
work on bringing xd into tree Haldean Brown 1 year, 5 months ago
3 changed file(s) with 418 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 xd.1
1 Makefile
2 index.html
3 images
4 log.txt
5 xd
6 xd.o
0 include_rules
1
2 : xd.c |> cl %f /c /Z7 /MD /Fo%o |> %B.o
3 : xd.o |> link %f /OUT:%o |> xd ^.*AppData.*
0 /*
1
2 Extended dump and load utility
3
4 by John Walker
5 http://www.fourmilab.ch/
6
7 This program is in the public domain.
8
9 */
10
11 #define Version \
12 "1.4 -- October 2017"
13
14
15 #include <stdio.h>
16 #include <ctype.h>
17 #include <string.h>
18 #ifdef _WIN32
19 #include <fcntl.h>
20 #include <io.h>
21 #endif
22
23 #define FALSE 0
24 #define TRUE 1
25
26 #define EOS '\0'
27
28 static char addrformat[80] = "%6X";
29 static char scanaddr[80] = "%lx%c";
30
31 static char dataformat1[80] = "%02X";
32 static char scandata[80] = "%x%n%c";
33
34 static int bytesperline = 16, doublechar = FALSE,
35 dflen = 2, loading = FALSE, streaming = FALSE;
36 static unsigned long fileaddr;
37 static unsigned char lineecho[32];
38
39 /* OUTLINE -- Edit a line of binary data into the selected output
40 format. */
41
42 static void outline(out, dat, len)
43 FILE *out;
44 unsigned char *dat;
45 int len;
46 {
47 char oline[132];
48 int i;
49
50 sprintf(oline, addrformat, fileaddr);
51 strcat(oline, ":");
52 for (i = 0; i < len; i++) {
53 char outedit[80];
54
55 sprintf(outedit, dataformat1, dat[i]);
56 strcat(oline, (i == (bytesperline / 2)) ? " " : " ");
57 strcat(oline, outedit);
58 }
59
60 if (doublechar) {
61 char oc[2];
62 int shortfall = ((bytesperline - len) * (dflen + 1)) +
63 (len <= (bytesperline / 2) ? 1 : 0);
64
65 while (shortfall-- > 0) {
66 strcat(oline, " ");
67 }
68 oc[1] = EOS;
69 strcat(oline, " | ");
70 for (i = 0; i < len; i++) {
71 int b = dat[i];
72
73 /* Map non-printing characters to "." according to the
74 definitions for ISO 8859/1 Latin-1. */
75
76 if ((b < ' ') || (b > '~' && b < 160)) {
77 b = '.';
78 }
79
80 /* Many existing systems which support Latin-1 lack
81 a definition for character 160, the non-breaking
82 space. Translate this to a space to avoid
83 confusion. */
84
85 if (b == 160) {
86 b = ' ';
87 }
88 oc[0] = (char) b;
89 strcat(oline, oc);
90 }
91 }
92 strcat(oline, "\n");
93 fputs(oline, out);
94 }
95
96 /* INTERPLINE -- Interpret a line of input. */
97
98 static int interpline(line, lineno, out)
99 char *line;
100 int lineno;
101 FILE *out;
102 {
103 char *cp = line;
104 char c;
105 unsigned long lfaddr;
106 int gfaddr = FALSE;
107
108 /* Scan the line for a possible alternative format information
109 following a vertical bar and delete it. */
110
111 while ((c = *cp++) != EOS) {
112 if (c == '|') {
113 cp[-1] = EOS;
114 break;
115 }
116 }
117
118 /* Scan the line for a file address terminated by a colon. Save
119 the file address. */
120
121 cp = line;
122
123 while ((c = *cp++) != EOS) {
124 if (c == ':') {
125 int sa;
126 char tchar;
127
128 cp[-1] = EOS;
129 sa = sscanf(line, scanaddr, &lfaddr, &tchar);
130 if (sa == 0 || (sa > 1 && tchar != EOS)) {
131 fprintf(stderr,
132 "Bad file address \"%s\" on line %d:\n",
133 line, lineno);
134 return FALSE;
135 }
136 gfaddr = TRUE;
137 cp[-1] = ':';
138 break;
139 }
140 }
141 if (!gfaddr) {
142 cp = line;
143 }
144 if (!streaming) {
145 if (!gfaddr) {
146 fprintf(stderr, "File address missing on line %d:\n", lineno);
147 fprintf(stderr, "%s\n", line);
148 return FALSE;
149 }
150 if (lfaddr != fileaddr) {
151 fprintf(stderr, "File address sequence error on line %d.\n",
152 lineno);
153 fprintf(stderr, " Expected ");
154 fprintf(stderr, addrformat, fileaddr);
155 fprintf(stderr, ", received ");
156 fprintf(stderr, addrformat, lfaddr);
157 fprintf(stderr, ".\n");
158 fprintf(stderr, "%s\n", line);
159 return FALSE;
160 }
161 }
162
163 while ((c = *cp++) != EOS) {
164 if (!isspace(c)) {
165 int scanl, nscan, dvalue;
166 char termchar;
167
168 if (((scanl = sscanf(cp - 1, scandata, &dvalue, &nscan, &termchar)) == 0) ||
169 (dvalue < 0) || (dvalue > 255) ||
170 (scanl == 2 && !isspace(termchar))) {
171 fprintf(stderr, "Improper value, \"%s\" on line %d:\n",
172 cp - 1, lineno);
173 fprintf(stderr, "%s\n", line);
174 fprintf(stderr, "Bytes must be specified as digits separated by white space.\n");
175 return FALSE;
176 }
177 putc((char) dvalue, out);
178 fileaddr++;
179 cp += nscan;
180 }
181 }
182 return TRUE;
183 }
184
185 /* Main program */
186
187 int main(argc, argv)
188 int argc; char *argv[];
189 {
190 int i, b, bp, cdata = FALSE, f = 0;
191 char *cp, *clabel, opt;
192 FILE *in = stdin, *out = stdout;
193
194 for (i = 1; i < argc; i++) {
195 cp = argv[i];
196 if (*cp == '-') {
197 opt = *(++cp);
198 if (islower(opt)) {
199 opt = (char) toupper(opt);
200 }
201 switch (opt) {
202
203 case 'A': /* -Af -- Set address format */
204 opt = cp[1];
205 if (islower(opt)) {
206 opt = (char) toupper(opt);
207 }
208 switch (opt) {
209 case 'D':
210 strcpy(addrformat, "%8d");
211 strcpy(scanaddr, "%ld%c");
212 break;
213
214 case 'H':
215 case 'X':
216 strcpy(addrformat, "%6X");
217 strcpy(scanaddr, "%lx%c");
218 break;
219
220 case 'O':
221 strcpy(addrformat, "%8o");
222 strcpy(scanaddr, "%lo%c");
223 break;
224
225 default:
226 fprintf(stderr,
227 "Invalid address format '%c'. Must be D, H, or O.\n", cp[1]);
228 return 2;
229 }
230 break;
231
232 case 'C':
233 doublechar = TRUE;
234 break;
235
236 case 'D':
237 cdata = TRUE;
238 clabel = cp + 1;
239 break;
240
241 case 'L':
242 loading = TRUE;
243 break;
244
245 case 'N': /* -Nf -- Set numeric dump format */
246 opt = cp[1];
247 if (islower(opt)) {
248 opt = (char) toupper(opt);
249 }
250 switch (opt) {
251 case 'D':
252 strcpy(dataformat1, "%3d");
253 strcpy(scandata, "%d%n%c");
254 break;
255
256 case 'H':
257 case 'X':
258 strcpy(dataformat1, "%02X");
259 strcpy(scandata, "%x%n%c");
260 break;
261
262 case 'O':
263 strcpy(dataformat1, "%03o");
264 strcpy(scandata, "%o%n%c");
265 break;
266
267 default:
268 fprintf(stderr,
269 "Invalid numeric dump format '%c'. Must be D, H, or O.\n", cp[1]);
270 return 2;
271 }
272 break;
273
274 case 'S':
275 streaming = TRUE;
276 break;
277
278 case '?':
279 case 'H':
280 case 'U':
281 fprintf(stderr, "XD -- Extended dump. Call with xd [input [output]]\n");
282 fprintf(stderr, "\n");
283 fprintf(stderr, " Options:\n");
284 fprintf(stderr, " -af Print addresses in f = Decimal, Hex, or Octal\n");
285 fprintf(stderr, " -c Dump as ISO characters\n");
286 fprintf(stderr, " -dlabel Dump as a C data declaration\n");
287 fprintf(stderr, " -l Load file from hex dump\n");
288 fprintf(stderr, " -nf Numeric dump in f = Decimal, Hex, or Octal\n");
289 fprintf(stderr, " -s Stream load (don't check file addresses)\n");
290 fprintf(stderr, " -u Print this message\n");
291 fprintf(stderr, "\nBy John Walker, http://www.fourmilab.ch/\n");
292 fprintf(stderr,"Version %s\n", Version);
293 return 0;
294 }
295 } else {
296 switch (f) {
297 case 0:
298
299 /** Warning! On systems which distinguish text mode and
300 binary I/O (MS-DOS, Macintosh, etc.) the modes in these
301 open statements will have to be made conditional based
302 upon whether an encode or decode is being done, which
303 will have to be specified earlier. But it's worse: if
304 input or output is from standard input or output, the
305 mode will have to be changed on the fly, which is
306 generally system and compiler dependent. 'Twasn't me
307 who couldn't conform to Unix CR/LF convention, so
308 don't ask me to write the code to work around
309 Apple and Microsoft's incompatible standards.
310
311 This file contains code, conditional on _WIN32, which
312 sets binary mode using the method prescribed by
313 Microsoft Visual C 1.52 ("Monkey C"); this may
314 require modification if you're using a different
315 compiler or release of Monkey C. */
316
317 if ((in = fopen(cp, loading ? "r" : "rb")) == NULL) {
318 fprintf(stderr, "Cannot open input file %s\n", cp);
319 return 2;
320 }
321 f++;
322 break;
323
324 case 1:
325 if ((out = fopen(cp, loading ? "wb" : "w")) == NULL) {
326 fprintf(stderr, "Cannot open output file %s\n", cp);
327 return 2;
328 }
329 f++;
330 break;
331
332 default:
333 fprintf(stderr, "Too many file names specified.\n");
334 }
335 }
336 }
337
338 #ifdef _WIN32
339
340 /* If input is from standard input and we aren't loading
341 from a dump file, set the input file mode to binary. */
342
343 if ((in == stdin) && (!loading)) {
344 _setmode(_fileno(in), _O_BINARY);
345 }
346
347 /* If output is to standard output and we're loading a
348 binary file from a dump, set the output file mode to
349 binary. */
350
351 if ((out == stdout) && (loading)) {
352 _setmode(_fileno(out), _O_BINARY);
353 }
354 #endif
355
356 bp = 0;
357 fileaddr = 0;
358
359 if (loading) {
360 char in_line[256];
361 int lineno = 0;
362
363 while (fgets(in_line, (sizeof in_line) - 2, in)) {
364 lineno++;
365 if (!interpline(in_line, lineno, out)) {
366 fclose(out);
367 return 2;
368 }
369 }
370 } else {
371 if (cdata) {
372 char cout[80];
373
374 fprintf(out, "unsigned char %s[] = {\n",
375 clabel[0] == EOS ? "xd_data" : clabel);
376 strcpy(cout, " ");
377
378 while ((b = getc(in)) != EOF) {
379 if (strlen(cout) > 72) {
380 fprintf(out, "%s\n", cout);
381 strcpy(cout, " ");
382 }
383 sprintf(cout + strlen(cout), "%d,", b);
384 }
385 if (strlen(cout) > 4) {
386 cout[strlen(cout) - 1] = EOS; /* Strip trailing comma */
387 fprintf(out, "%s\n", cout);
388 }
389 fprintf(out, "};\n");
390 } else {
391 while ((b = getc(in)) != EOF) {
392 if (bp >= bytesperline) {
393 outline(out, lineecho, bp);
394 bp = 0;
395 fileaddr += bytesperline;
396 }
397 lineecho[bp++] = (char) b;
398 }
399
400 if (bp > 0) {
401 outline(out, lineecho, bp);
402 }
403 }
404 }
405 return 0;
406 }