OSDN Git Service

2013.10.24
[uclinux-h8/uClinux-dist.git] / user / boottools / xloader.c
1 /*
2  * xloader
3  *
4  * A replacement for ramloader and flashloader, since the loading mechanism
5  * is identical and the difference is just in which call to make to the
6  * uCbootloader
7  *
8  * (c) 2002-2003 Arcturus Networks Inc. 
9  *     by Michael Leslie <mleslie@arcturusnetworks.com>,
10  *
11  * Change log:
12  *  August  2003 - Oleksandr Zhadan <oleks@arcturusnetworks.com>
13  *                 load_image_4k() is fixed.
14  *                 Was wrong link number calculation.
15  */
16
17 /* With uClibc 0.9.26, malloc() is malloc_standard, which uses the slab
18  * allocator. This means that malloc(4096) takes 4 KB of memory, not 8 KB, so
19  * using load_image_4k() should not incur a penalty of 100% overhead.
20  *
21  * FIXME: Make load_image4k() recognize opt_stdin.
22  */
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <errno.h>
27 #include <sys/stat.h>
28 #include <string.h>
29 #include <unistd.h>
30 #include <byteswap.h>
31 #include <asm/uCbootstrap.h>
32 #include "md5.h"
33 #include "flash.h"
34
35 /****** data declarations: **************************************************/
36
37 /* Max image size should come at least from (a platform specific
38  * part of) uCbootstrap.h, and at best from a uCbootstrap system call.
39  * For now we will hard-define it here to be 4M - 128K: */
40 #define MAX_IMAGE_SIZE (0x400000 - 0x20000)
41
42
43 int   opt_ramloader = 0;    /* invoke ramloader */
44 int   opt_flashloader = 0;  /* invoke flashloader */
45 int   opt_quiet = 0;        /* do not print anything to the screen */
46 int   opt_debug = 0;        /* print debug info here and in uCbootloader */
47 #define DBG(a1, a2...) if (opt_debug) fprintf(stderr, a1, ##a2)
48 char *opt_filename = NULL;  /* filename to load */
49 char *opt_partition = NULL; /* (optional) partition specification to program */
50
51 int   opt_4k = 0;           /* Don't use old 4K-based version by default */
52
53 int   opt_noop        = 0;  /* no bootloader operation */
54 int   opt_stdin       = 0;  /* read image from stdin instead of filesystem */
55
56 int   opt_program_all = 1;  /* Use the old bsc_program call to program all
57                              * of user flash, rather than partition by partition */
58 int   opt_watchdog    = 0;  /* watchdog must be serviced at key points */
59
60 int   flags;
61 mnode_t m;
62
63 FILE *watchdog;
64
65 /* memory data structures: */
66
67 uCimage_header header;
68 int header_present = 0;
69
70 #define BUFFERSIZE 4096
71 void **chain;
72 void **chain_tmp;
73 int    links;
74
75 #define CARSIZE (65536 - 128)
76 void **train = NULL;
77 int    cars  = 0;
78
79 /****** endian handling for uCimage header: *********************************/
80
81 #if (BYTE_ORDER == BIG_ENDIAN)
82 /* #  warning This target is BIG_ENDIAN */
83   /* note "ltoh" = little-endian to host */
84 #  define ltoh16(x)  bswap_16(x)
85 #  define ltoh32(x)  __bswap_constant_32(x)
86 #elif (BYTE_ORDER == LITTLE_ENDIAN)
87 /* #warning This target is LITTLE_ENDIAN */
88   /* note "ltoh" = little-endian to host */
89 #  define ltoh16(x)  (x)
90 #  define ltoh32(x)  (x)
91 #endif
92
93 /****** function prototypes: ************************************************/
94
95 int parse_args(int argc, char *argv[]);
96 void usage(void);
97
98 _bsc2(int,program,void *,a1,int,a2)
99 _bsc3(int,program2,void *,a1,int,a2,char *,partition)
100 _bsc2(int,ramload,void *,a1,int,a2)
101 _bsc1(int, setbenv, char *, a)
102
103 int load_image (char *filename);     /* malloc, sort, and read 64K blocks */
104 int load_image_4k (char *filename);  /* malloc and read 4K blocks */
105 int sort_pointers (void **train, int cars);
106 void deallocate_all (void);          /* safely deallocate all major structures */
107
108 int check_uCimage (uCimage_header *header, FILE *handle);
109
110
111 /****** main(): *************************************************************/
112
113 int main (int argc, char *argv[])
114 {
115
116         if (parse_args (argc, argv))
117                 if (!opt_quiet)
118                 usage();
119
120         if (opt_watchdog) {
121                 fprintf (stderr, "Opening /dev/watchdog.\n");
122                 watchdog = fopen ("/dev/watchdog", "w");
123                 if (watchdog == NULL) {
124                         perror ("Error opening watchdog device file");
125                         exit(-1);
126                 }
127                 fputc('c', watchdog);
128                 fflush (watchdog);
129         }
130
131         if (!opt_4k) {
132                 if (load_image (opt_filename))
133                         exit (-1);
134         } else {
135                 if (load_image_4k (opt_filename))
136                         exit (-1);
137         }
138
139         if (opt_watchdog) {
140                 fputc('c', watchdog);
141                 fflush (watchdog);
142         }
143
144         flags = (opt_debug?(PGM_DEBUG):0);
145
146         if (opt_noop) {
147                 deallocate_all();
148                 exit (1);
149         }
150
151         fflush (stdout);
152         fflush (stderr);
153         sleep (1);
154
155         if (opt_ramloader)
156                 ramload(&m, flags | PGM_EXEC_AFTER);
157         else if (opt_flashloader) {
158                 if (opt_program_all)
159                         program(&m, flags | PGM_ERASE_FIRST | PGM_RESET_AFTER);
160                 else {
161                         if (opt_partition)
162                                 program2(&m, flags | PGM_ERASE_FIRST | PGM_RESET_AFTER, opt_partition);
163                 else
164                                 program2(&m, flags | PGM_ERASE_FIRST | PGM_RESET_AFTER, "0");
165                 }
166         }
167
168         /* not reached:
169          * PGM_EXEC_AFTER starts the new kernel,
170          * PGM_RESET_AFTER resets the board.
171          */
172         exit (-1);
173
174 }
175
176
177
178 /****** function declarations: **********************************************/
179
180 /*
181  * parse_args(int argc, char *argv[])
182  *
183  * Parse command line arguments and set corresponding
184  * opt_xxx variables.
185  *
186  */
187 int parse_args(int argc, char *argv[])
188 {
189         char *c;
190         int i;
191         int err = 0;
192         char * argvp;
193
194         /* fprintf (stderr, "argv[0] = \"%s\"\n", argv[0]); */
195         c = (char *)strrchr (argv[0], '/');
196         if (c == NULL) c = argv[0];
197         else           c++;
198
199         if (argc < 2)
200                 return (1);
201
202         if (!strcmp (c, "ramloader"))
203                 opt_ramloader = 1;
204         else if (!strcmp (c, "flashloader"))
205                 opt_flashloader = 1;
206
207         for (i=1;i<argc;i++) {
208                 if (argv[i][0] == '-'){
209                         argvp = argv[i] + 1;
210
211                         if (!*argvp) {
212                                 if (i < argc-1)
213                                 return 1; /* no option */
214                                 else {
215                                         opt_stdin = 1;
216                                         opt_filename = "-";
217                                 }
218                         }
219
220                         while(*argvp) {
221                                 switch (*argvp++)
222                                 {
223                                 case 'd': opt_debug       = 1; break;
224                                 case 'r': opt_ramloader   = 1; break;
225                                 case 'f': opt_flashloader = 1; break;
226                                         case 'F': opt_flashloader = 1;
227                                                   opt_program_all = 1;
228                                                   break;
229                                 case '4': opt_4k          = 1; break;
230                                         case 'n': opt_noop        = 1; break;
231                                 case 'q': opt_quiet       = 1; break;
232                                         case 's':
233                                                 opt_stdin    = 1;
234                                                 opt_filename = "-";
235                                                 break;
236                                         case 'w': opt_watchdog    = 1; break;
237                                 case 'h': return 1;
238
239                                 default:
240                                                 if (!opt_quiet)
241                                         fprintf (stderr,
242                                                                          "Error: Unknown option \"%s\" - Aborting.\n\n",
243                                                                          argv[i]);
244                                         return 1;
245                                 }
246                         }
247
248                 } else if (opt_filename) {
249                         /* If the previous arg was a filename (ie the first non '-' arg, then
250                          * this must be a partition spec: "X:"
251                          */
252                         c = strchr(argv[i], ':');
253                         if (c != NULL) {
254                                 *c = 0; /* strip final ':' from partition name */
255                                 opt_partition = argv[i];
256                                 if (opt_program_all) {
257                                         fprintf (stderr, "Error: There is a conflict between programming"
258                                                 " all of flash and partition mode.\n\n");
259                                         return 1;
260                                 }
261                         } else {
262                         fprintf (stderr, "Error: Only one image is allowed - Aborting.\n\n");
263                         return 1;
264                 }
265
266                 } else
267                         opt_filename = argv[i];
268         }
269
270         /* print out options if debug enabled: */
271         DBG("argv[0] = \"%s\"\n", c);
272         DBG("opt_ramloader   = %d;\n", opt_ramloader);
273         DBG("opt_flashloader = %d;\n", opt_flashloader);
274         DBG("opt_program_all = %d;\n", opt_program_all);
275         DBG("opt_quiet       = %d;\n", opt_quiet);
276         DBG("opt_debug       = %d;\n", opt_debug);
277         DBG("opt_filename    = %s\n",  opt_filename);
278         DBG("opt_partition   = %s\n",  opt_partition);
279         DBG("opt_4k          = %d\n",  opt_4k);
280         DBG("opt_noop        = %d\n",  opt_noop);
281         DBG("opt_stdin       = %d\n",  opt_stdin);
282         DBG("opt_watchdog    = %d\n",  opt_watchdog);
283
284         /* check the option */
285         if(opt_ramloader && opt_flashloader)
286         {
287                 if (!opt_quiet)
288                 fprintf(stderr, "Error: You cannot use both -r and -f options.\n");
289                 err = 1;        
290         }
291         if(opt_ramloader && opt_partition)
292         {
293                 if (!opt_quiet)
294                         fprintf(stderr, "Error: specifying a partition to the ramloader makes no sense.\n");
295                 err = 1;        
296         }
297         if(!opt_filename)
298         {
299                 if (!opt_quiet)
300                 fprintf(stderr, "Error: No image given.\n");
301                 err = 1;        
302         }
303
304         if (!opt_ramloader && !opt_flashloader) {
305                 if (!opt_quiet) {
306                 fprintf (stderr, 
307                                  "Error: neither ramloader (-r) nor flashloader (-f)\n");
308                 fprintf (stderr,
309                                  "       selected. Aborting.\n");
310                 }
311                 err = 1;        
312         }
313         if (err) return 1;
314
315         if (!opt_quiet) {
316                 fprintf (stderr, "Load image file: \"%s\" to %s",
317                          opt_filename, opt_ramloader?"ram":"flash");
318
319                 if (opt_partition)
320                         fprintf (stderr, " partition %s\n", opt_partition);
321                 else
322                         fprintf (stderr, "\n");
323         }
324
325         return (0);
326 }
327
328
329 void usage()
330 {
331         fprintf (stderr,
332 "usage: xloader | ramloader | flashloader\n"
333 "\n"
334 "       Invoked as \"ramloader\" or \"xloader -r\", this program will\n"
335 "       load a kernel image into RAM and pass it to uCbootloader for\n"
336 "       execution.\n"
337 "       Invoked as \"flashloader\" or \"xloader -f\", it will load a\n"
338 "       cramfs image and pass it to uCbootloader to be written into\n"
339 "       flash memory.\n"
340 "       In both cases, this program *will not return*. Once uCbootloader\n"
341 "       has been given control, interrupts are disabled, and the new\n"
342 "       image is booted.\n"
343 );
344         fprintf(stderr,
345 "Options:\n"
346 "\t-2\twrite to second flash device(default is the first flash device)\n"
347 "\t-4\tfor 4k-based block, default 64k-based\n"
348 "\t-d\tprint debugging message\n"
349 "\t-f\tinvoke flashloader\n"
350 "\t-F\tinvoke flashloader; use all of user flash\n"
351 "\t-h\tthis help information\n"
352 "\t-n\tDo everything except call the bootloader (noop)\n"
353 "\t-r\tinvoke ramloader\n"
354 "\t-s\tRead new image file from stdin\n"
355 "\t-w\tRefresh /dev/watchdog at key points\n"
356 "\t-q\tdo it quietly, no output to the screen\n\n"
357 );
358         exit(1);
359 }
360
361
362
363 int load_image (char *filename)
364 {
365         FILE *image;
366         struct stat statbuf;
367         int filesize, i, j, n;
368         int bytes_read = 0;
369         int links_per_car, links_over;
370         int percent;
371         int ferror_image;
372         struct MD5Context  md5c;
373         unsigned char      digest[16];
374
375         if (opt_stdin) {
376                 image = stdin;
377         } else {
378                 /* open image file: */
379                 image = fopen (filename, "r");
380                 if (image == NULL) {
381                         perror ("Error opening image file");
382                         return (errno);
383                 }
384         }
385
386
387         /* Check for the presence of a uCimage file header: */
388         /* read header: */
389         fread (&header, sizeof(header), 1, image);
390         check_uCimage (&header, image);
391
392         if (header_present) {
393                 filesize = header.data_size;
394                 MD5Init(&md5c); /* Initialize MD5 module */
395
396         } else {
397                 /* no uCimage header present: */
398                 if (opt_stdin) {
399                         /* allocate the maximum size, since we do not know
400                          * how big the image will be: */
401                         filesize = MAX_IMAGE_SIZE;
402                 } else {
403         /* stat input file */
404         if (stat (filename, &statbuf)) {
405                 perror ("Error stat()ing image file");
406                 return (errno);
407         }
408         /* otherwise, all is still OK: */
409         filesize = statbuf.st_size;     
410                 }
411         }
412
413
414         /* build buffer chain: */
415         links = (int) ((filesize + BUFFERSIZE -1) / BUFFERSIZE);
416         /* chain = (void *)malloc (links * sizeof (void *)); */
417
418         /* build link train: */
419         links_per_car = CARSIZE / BUFFERSIZE;
420         cars = 1 + links / links_per_car;
421
422         /* Can we fit the chain into the last car? */
423         /* How many links in the last car? */
424         links_over = links - (cars - 1) * links_per_car;
425         if ((CARSIZE - links_over*BUFFERSIZE) <
426                 links * sizeof (void *)) {
427                 /* then the chain can not be placed in the last car;
428                  * allocate one more.*/
429                 cars++;
430                 links_over = links - (cars - 1) * links_per_car;
431                 if (links_over < 0) links_over = 0;
432         }
433
434         /* allocate the array of cars: */
435         /* note: this array can be discarded once the chain of links
436          * has been mapped onto the actual buffers */
437         train = (void *)malloc(cars * sizeof (void *));
438         if ( train == NULL) {
439                 if (!opt_quiet)
440                 fprintf (stderr, "Error allocating train\n");
441                 return (errno);
442         } else
443                 memset (train, 0, cars * sizeof (void *));
444
445         /* allocate the cars: */
446         for (i=0;i<cars;i++) {
447                 train[i] = (void *)malloc(CARSIZE);
448                 if (train[i] == NULL) {
449                         if (!opt_quiet)
450                         fprintf (stderr, "Error allocating car %d\n", i);
451                         /* before we return, free all allocated memories */
452                         deallocate_all();
453                         return (errno);
454                 }
455                 DBG("train[%d] = %p\n", i, train[i]);
456         }
457
458         /* sort the cars */
459         sort_pointers (train, cars);
460
461         /* map the chain into the last car: */
462         chain = (void *)(train[cars-1]) + (links_over * BUFFERSIZE);
463
464         /* allocate links into the cars: */
465         for (i=0;i<cars;i++,j++) {
466                 DBG("\ntrain[%d] = %p cars:\n", i, train[i]);
467                 for (j=0;j<links_per_car;j++) {
468                         if (i*links_per_car+j >= links)
469                                 break;
470                         chain[i*links_per_car+j] = train[i] + (BUFFERSIZE * j);
471                         DBG("  0x%08x", (unsigned int)chain[i*links_per_car+j]);
472                 }
473         }
474
475         DBG("\nfilesize = %d, links = %d\n", filesize, links);
476         DBG("cars = %d, links_per_car = %d, links_over = %d\n", cars, links_per_car, links_over);
477         DBG("car[%d] = %p, chain = %p\n", cars-1, train[cars-1], chain);
478
479
480
481         /* populate chain with image file: */
482         for (i=0;i<cars;i++) {
483                 if ((i+1)*links_per_car <= links)
484                         j = links_per_car;
485                 else
486                         j = links_over;
487
488                 n = fread (train[i], 1, BUFFERSIZE * j, image);
489
490                 if (opt_debug)
491                         fprintf(stderr, "fread %d bytes to car[%d] = %p\n",
492                                         n, i, train[i]);
493                 else {
494                         percent = (((i*links_per_car+j)+1) * 100)/links;
495                         /* if (percent%10 == 0) */
496                         if (!opt_quiet)
497                         fprintf (stderr, "\r%d%%", percent);
498                 }
499
500                 /* Compute MD5 as we read the file: */
501                 if (header_present)
502                         MD5Update (&md5c, (char *)(train[i]), n);
503
504
505                 bytes_read += n;
506                         
507                 if (n < BUFFERSIZE * j) {
508                         if (opt_stdin && !header_present) {
509                                 /* assume the transmission has finished */
510                                 break;
511                         } else {
512                                 if (bytes_read != filesize) {
513                                         ferror_image = ferror (image);
514                                         if (!opt_quiet)
515                         fprintf (stderr, "Error #%d reading from image file\n",
516                                                          ferror_image);
517                         fclose (image);
518                                         return ferror_image;
519                                 }
520                         }
521                 }
522         }
523
524         if (!opt_debug && !opt_quiet)
525                 fprintf (stderr, "\n");
526
527         if (opt_stdin && !feof(image)) {
528                 if (bytes_read != filesize) {
529                         if (!opt_quiet)
530                                 fprintf (stderr, "Image too big, max %d\n", filesize);
531         fclose (image);
532                         deallocate_all();
533                         return -1;
534                 }
535         }
536
537         fclose (image);
538         /* free(train); */
539
540         if (opt_stdin) {
541                 filesize = bytes_read;
542                 DBG("size of received file = %d bytes\n", filesize);
543         }
544
545         /* NOTE THAT we should have some kind of header and / or footer to verify
546          * that our image is error free. For starters, make sure it's not empty.
547          */
548
549         if (filesize == 0) {
550                 if (!opt_quiet)
551                         fprintf(stderr, "Image is empty.\n");
552                 deallocate_all();
553                 return -1;
554         }
555
556         if (opt_watchdog) {
557                 fputc('c', watchdog);
558                 fflush (watchdog);
559         }
560
561
562         if (header_present) {
563                 char buf[42], ascii_md5[34];
564
565                 /* save MD5: */
566                 MD5Final (&(digest[0]), &md5c);
567
568                 if (!opt_quiet)
569                         printf ("Checking MD5 signature... ");
570
571                 /* print the signature to a string: */
572                 for (i=0;i<16;i++)
573                         sprintf (&(ascii_md5[i*2]), "%02x", digest[i]);
574
575                 /* Compare with given digest: */
576                 for (i=0;i<16;i++) {
577                         if (digest[i] != header.md5sum[i]) {
578                                 printf ("ERROR: MD5 digest mismatch\n");
579                                 printf ("MD5 digest calculated as:  %s\n", ascii_md5);
580                                 printf ("failed to match given MD5: ");
581                                 for (i=0;i<16;i++)
582                                         printf ("%02x", header.md5sum[i]);
583                                 printf ("\n");
584                                 return (1);
585                         }
586                 }
587
588                 if (!opt_quiet)
589                         printf ("verified\n");
590
591                 if (opt_watchdog) {
592                         fputc('c', watchdog);
593                         fflush (watchdog);
594                 }
595
596                 if (opt_flashloader) {
597                         /* write uCbootstrap envars for the image: */
598                         if (opt_partition) {
599                                 sprintf (buf, "p_%s_SIZE=%d", opt_partition, header.data_size);
600                                 setbenv(buf);
601
602                                 sprintf (buf, "p_%s_MD5=%s", opt_partition, ascii_md5);
603                                 setbenv(buf);
604                         }
605                 }
606                 
607         } /* endif header_present */
608
609
610
611         /* set uCbootloader arguments: */
612         m.len = filesize;
613         m.offset = (void *)chain;
614
615         return (0);
616 }
617
618
619
620
621
622
623 int sort_pointers (void **pointer, int N)
624 {
625         int i, j;
626         void *p;
627
628         /* sort pointers */
629         for (i=0;i<N;i++) {
630                 p = pointer[i];
631                 for (j=i+1;j<N;j++) {
632                         if ((unsigned long int)pointer[j] < (unsigned long int)p) {
633                                 p          = pointer[j];
634                                 pointer[j] = pointer[i];
635                                 pointer[i] = p;
636                         }
637                 }
638         }
639         return (0);
640 }
641
642
643
644 int load_image_4k (char *filename)
645 {
646         FILE *image;
647         struct stat statbuf;
648         int filesize, i, n;
649         int percent;
650         int ferror_image;
651
652         /* stat input file */
653         if (stat (filename, &statbuf)) {
654                 perror ("Error stat()ing image file");
655                 return (errno);
656         }
657
658         /* otherwise, all is still OK: */
659         filesize = statbuf.st_size;     
660
661         /* build buffer chain: */
662         DBG("links: ");
663
664         links = filesize / BUFFERSIZE;
665         if ( filesize % BUFFERSIZE )
666             links++;
667
668         DBG("  %d  \n", links );
669         DBG("pointer chain: ");
670         chain_tmp = (void *)malloc (links * sizeof (void *));       /* Temporary chain */
671         DBG("  0x%08x\n", (unsigned int)chain_tmp);
672
673         for (i=0;i<links;i++) {
674             chain_tmp[i] = (void *)malloc (BUFFERSIZE);
675             DBG("  0x%08x", (unsigned int)chain_tmp[i]);
676             if (chain_tmp[i] == NULL) {
677                         fprintf (stderr, "Error allocating chain link %d\n", i);
678                         return (errno);
679                 }
680         }
681         DBG("\npointer chain: ");
682         chain = (void *)malloc (links * sizeof (void *));       /* Link pointers have to be at the end */
683         DBG("  0x%08x\n", (unsigned int)chain);
684
685         for (i=0; i<links; i++)                                 /* Dup the chain */
686             chain[i] = chain_tmp[i];
687
688         DBG("filesize = %d\n", filesize);
689
690         /* open image file: */
691         image = fopen (filename, "r");
692         if (image == NULL) {
693                 perror ("Error opening image file");
694                 return (errno);
695         }
696
697         /* populate chain with image file: */
698         for (i=0;i<links;i++) {
699                 n = fread (chain[i], 1, BUFFERSIZE, image);
700
701                 if (opt_debug)
702                         fprintf(stderr,
703                                         "fread %d bytes to chain[%d] = %p\n", n, i,chain[i]);
704                 else {
705                         percent = ((i+1) * 100)/links;
706                         if (percent%10 == 0)
707                                 fprintf (stderr, "\r%d%%", percent);
708                 }
709
710                 if ((n < BUFFERSIZE) && (BUFFERSIZE*i + n < filesize)) {
711                         ferror_image = ferror (image);
712                         fprintf (stderr, "Error #%d reading from image file\n",
713                                          ferror_image);
714                         fclose (image);
715                         return ferror_image;
716                 }
717         }
718         if (!opt_debug) fprintf (stderr, "\n");
719
720         fclose (image);
721
722         /* set uCbootloader arguments: */
723         m.len = filesize;
724         m.offset = (void *)chain;
725
726         return (0);
727 }
728
729
730 int check_uCimage (uCimage_header *header, FILE *handle)
731 {
732         int i;
733
734         /* Check magic in header */
735         for (i=0;i<sizeof(header->magic);i++) {
736                 if (header->magic[i] != UCHEADER_MAGIC[i]) {
737                         if (opt_debug)
738                                 fprintf (stderr, "Header magic[%d] not: \"%s\" instead: \"%s\"\n",
739                                          i, UCHEADER_MAGIC, header->magic);
740                         fprintf (stderr, "uCimage header not detected.\n");
741                         rewind (handle);
742                         return (1); /* header not found */
743                 }
744         }
745
746         header_present = 1;
747
748         /* TODO: check reported header size, seek to data */
749
750         /* Convert from little-endian to host byte order *in place*
751          * in the header: */
752
753         header->header_size = ltoh32(header->header_size);
754         DBG ("header_size reported as: %10d\n", header->header_size);
755
756         /* image size: */
757         header->data_size   = ltoh32(header->data_size);
758         fprintf (stdout, "data_size reported as:   %10d\n", header->data_size);
759
760         /* header date code */
761         fprintf (stdout, "date code reported as:    \"%s\"\n", header->datecode);
762
763         /* header name */
764         fprintf (stdout, "name reported as:         \"%s\"\n", header->name);
765
766         /* MD5: */
767         fprintf (stdout, "MD5 digest reported as:   ");
768         for (i=0;i<16;i++)
769                 fprintf (stdout, "%02x", header->md5sum[i]);
770         fprintf (stdout, "\n");
771
772 #if 0
773         /* read image and do MD5: */
774         while (!feof(infile)) {
775                 n = fread (buf, 1, BUFFERSIZE, infile);
776                 size += n;
777                 MD5Update (&md5c, buf, n);
778         }
779         /* save MD5: */
780         MD5Final (digest, &md5c);
781 #endif
782
783         return (0);
784 }
785
786
787 /****************************************************************************/
788
789 /* safely deallocate all major structures */
790 void deallocate_all (void)
791 {
792         int i;
793
794         if (train != NULL) {
795                 for (i=0;i<cars;i++) {
796                         if (train[i] != NULL) {
797                                 DBG("deallocate_all(): train[%d] == %p\n", i, train[i]);
798                                 free (train[i]);
799                         }
800                 }
801                 free (train);
802         }
803 }
804
805 /****************************************************************************/
806
807 /*
808  * Local variables:
809  *  c-indent-level: 4
810  *  c-basic-offset: 4
811  *  tab-width: 4
812  * End:
813  */