dump: Title section sometimes uses font size 14, not just 12.
[pspp] / dump.c
1 #include <float.h>
2 #include <stdbool.h>
3 #include <stdint.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <sys/stat.h>
8 #include <unistd.h>
9
10 static uint8_t *data;
11 static size_t n;
12
13 static bool
14 all_ascii(const uint8_t *p, size_t n)
15 {
16   for (size_t i = 0; i < n; i++)
17     if (p[i] < 32 || p[i] > 126)
18       return false;
19   return true;
20 }
21
22 static size_t
23 try_find(const char *target, size_t target_len)
24 {
25   const uint8_t *pos = (const uint8_t *) memmem (data, n, target, target_len);
26   return pos ? pos - data : 0;
27 }
28
29 static size_t
30 find(const char *target, size_t target_len)
31 {
32   size_t pos = try_find(target, target_len);
33   if (!pos)
34     {
35       fprintf (stderr, "not found\n");
36       exit(1);
37     }
38   return pos;
39 }
40
41 size_t pos;
42
43 #define XSTR(x) #x
44 #define STR(x) XSTR(x)
45 #define WHERE __FILE__":" STR(__LINE__)
46
47 static unsigned int
48 get_u32(void)
49 {
50   uint32_t x;
51   memcpy(&x, &data[pos], 4);
52   pos += 4;
53   return x;
54 }
55
56 static double
57 get_double(void)
58 {
59   double x;
60   memcpy(&x, &data[pos], 8);
61   pos += 8;
62   return x;
63 }
64
65 static bool
66 match_u32(uint32_t x)
67 {
68   if (get_u32() == x)
69     return true;
70   pos -= 4;
71   return false;
72 }
73
74 static void
75 match_u32_assert(uint32_t x, const char *where)
76 {
77   unsigned int y = get_u32();
78   if (x != y)
79     {
80       fprintf(stderr, "%s: 0x%x: expected i%u, got i%u\n", where, pos - 4, x, y);
81       exit(1);
82     }
83 }
84 #define match_u32_assert(x) match_u32_assert(x, WHERE)
85
86 static bool
87 match_byte(uint8_t b)
88 {
89   if (pos < n && data[pos] == b)
90     {
91       pos++;
92       return true;
93     }
94   else
95     return false;
96 }
97
98 static void
99 match_byte_assert(uint8_t b, const char *where)
100 {
101   if (!match_byte(b))
102     {
103       fprintf(stderr, "%s: 0x%x: expected %02x, got %02x\n", where, pos, b, data[pos]);
104       exit(1);
105     }
106 }
107 #define match_byte_assert(b) match_byte_assert(b, WHERE)
108
109 static void
110 newline(FILE *stream, int pos)
111 {
112   fprintf(stream, "\n%08x: ", pos);
113 }
114
115 static void
116 dump_raw(FILE *stream, int start, int end)
117 {
118   for (size_t i = start; i < end; )
119     {
120       if (i + 5 <= n
121           && data[i]
122           //&& !data[i + 1]
123           && !data[i + 2]
124           && !data[i + 3]
125           && i + 4 + data[i] + data[i + 1] * 256 <= end
126           && all_ascii(&data[i + 4], data[i] + data[i + 1] * 256))
127         {
128           newline(stream, i);
129           fprintf(stream, "\"");
130           fwrite(&data[i + 4], 1, data[i] + data[i + 1] * 256, stream);
131           fputs("\" ", stream);
132
133           i += 4 + data[i] + data[i + 1] * 256;
134         }
135       else if (i + 12 <= end
136                && data[i + 1] == 40
137                && data[i + 2] == 5
138                && data[i + 3] == 0)
139         {
140           double d;
141
142           memcpy (&d, &data[i + 4], 8);
143           fprintf (stream, "F40.%d(%.*f)", data[i], data[i], d);
144           i += 12;
145           newline (stream, i);
146         }
147       else if (i + 12 <= end
148                && data[i + 1] == 40
149                && data[i + 2] == 31
150                && data[i + 3] == 0)
151         {
152           double d;
153
154           memcpy (&d, &data[i + 4], 8);
155           fprintf (stream, "PCT40.%d(%.*f)", data[i], data[i], d);
156           i += 12;
157           newline(stream, i);
158         }
159       else if (i + 4 <= end
160                && (data[i] && data[i] != 88 && data[i] != 0x41)
161                && !data[i + 1]
162                && !data[i + 2]
163                && !data[i + 3])
164         {
165           fprintf (stream, "i%d ", data[i]);
166           i += 4;
167         }
168       else
169         {
170           fprintf(stream, "%02x ", data[i]);
171           i++;
172         }
173     }
174
175
176 }
177
178 static char *
179 get_string(const char *where)
180 {
181   if (1
182       /*data[pos + 1] == 0 && data[pos + 2] == 0 && data[pos + 3] == 0*/
183       /*&& all_ascii(&data[pos + 4], data[pos])*/)
184     {
185       int len = data[pos] + data[pos + 1] * 256;
186       char *s = malloc(len + 1);
187
188       memcpy(s, &data[pos + 4], len);
189       s[len] = 0;
190       pos += 4 + len;
191       return s;
192     }
193   else
194     {
195       fprintf(stderr, "%s: 0x%x: expected string\n", where, pos);
196       exit(1);
197     }
198 }
199 #define get_string() get_string(WHERE)
200
201 static char *
202 dump_nested_string(void)
203 {
204   char *s = NULL;
205
206   match_byte_assert (0);
207   match_byte_assert (0);
208   int outer_end = pos + get_u32();
209   int inner_end = pos + get_u32();
210   if (pos != inner_end)
211     {
212       match_u32_assert(0);
213       if (match_byte(0x31))
214         s = get_string();
215       else
216         match_byte_assert(0x58);
217       if (pos != inner_end)
218         {
219           fprintf(stderr, "inner end discrepancy\n");
220           exit(1);
221         }
222     }
223   match_byte_assert(0x58);
224   match_byte_assert(0x58);
225   if (pos != outer_end)
226     {
227       fprintf(stderr, "outer end discrepancy\n");
228       exit(1);
229     }
230
231   return s;
232 }
233
234 static void
235 dump_value_31(FILE *stream)
236 {
237   if (match_byte (0x31))
238     {
239       if (match_u32 (0))
240         {
241           if (match_u32 (1))
242             {
243               /* Only "a" observed as a sample value (although it appears 44 times in the corpus). */
244               get_string();
245             }
246           else
247             match_u32_assert (0);
248
249           int outer_end = pos + get_u32();
250           int inner_end = pos + get_u32();
251           match_u32_assert(0);
252           if (match_byte(0x31))
253             {
254               /* Appears to be a template string, e.g. '^1 cells (^2) expf < 5. Min exp = ^3...'.
255                  Probably doesn't actually appear in output because many examples look unpolished,
256                  e.g. 'partial list cases value ^1 shown upper...' */
257               get_string();
258             }
259           else
260             match_byte_assert(0x58);
261           if (pos != inner_end)
262             {
263               fprintf(stderr, "inner end discrepancy\n");
264               exit(1);
265             }
266
267           if (match_byte(0x31))
268             {
269               /* Only one example in the corpus. */
270               match_byte(0);
271               match_byte(0);
272               match_byte(0);
273               match_byte_assert(1);
274               get_string();     /* foreground */
275               get_string();     /* background */
276               get_string();     /* font */
277               if (!match_byte(14))
278                 match_byte_assert(12); /* size? */
279             }
280           else
281             match_byte_assert(0x58);
282           match_byte_assert(0x58);
283           if (pos != outer_end)
284             {
285               fprintf(stderr, "outer end discrepancy\n");
286               exit(1);
287             }
288         }
289       else if (match_u32 (1))
290         {
291           fprintf(stream, "(footnote %d) ", get_u32());
292           dump_nested_string();
293         }
294       else if (match_u32 (2))
295         {
296           fprintf(stream, "(special 2)");
297           match_byte_assert(0);
298           match_byte_assert(0);
299           if (!match_u32 (2))
300             match_u32_assert(1);
301           dump_nested_string(); /* Our corpus doesn't contain any examples with strings though. */
302         }
303       else
304         {
305           match_u32_assert(3);
306           fprintf(stream, "(special 3)");
307           match_byte_assert(0);
308           match_byte_assert(0);
309           match_byte_assert(1);
310           match_byte_assert(0);
311           match_u32_assert(2);
312           dump_nested_string(); /* Our corpus doesn't contain any examples with strings though. */
313         }
314     }
315   else
316     match_byte_assert (0x58);
317 }
318
319 static const char *
320 format_to_string (int type)
321 {
322   static char tmp[16];
323   switch (type)
324     {
325     case 1: return "A";
326     case 2: return "AHEX";
327     case 3: return "COMMA";
328     case 4: return "DOLLAR";
329     case 5: case 40: return "F";
330     case 6: return "IB";
331     case 7: return "PIBHEX";
332     case 8: return "P";
333     case 9: return "PIB";
334     case 10: return "PK";
335     case 11: return "RB";
336     case 12: return "RBHEX";
337     case 15: return "Z";
338     case 16: return "N";
339     case 17: return "E";
340     case 20: return "DATE";
341     case 21: return "TIME";
342     case 22: return "DATETIME";
343     case 23: return "ADATE";
344     case 24: return "JDATE";
345     case 25: return "DTIME";
346     case 26: return "WKDAY";
347     case 27: return "MONTH";
348     case 28: return "MOYR";
349     case 29: return "QYR";
350     case 30: return "WKYR";
351     case 31: return "PCT";
352     case 32: return "DOT";
353     case 33: return "CCA";
354     case 34: return "CCB";
355     case 35: return "CCC";
356     case 36: return "CCD";
357     case 37: return "CCE";
358     case 38: return "EDATE";
359     case 39: return "SDATE";
360     default:
361       abort();
362       sprintf(tmp, "<%d>", type);
363       return tmp;
364     }
365 }
366
367 static void
368 dump_value(FILE *stream, int level, bool match1)
369 {
370   match_byte(0);
371   match_byte(0);
372   match_byte(0);
373   match_byte(0);
374
375   for (int i = 0; i <= level; i++)
376     fprintf (stream, "    ");
377
378   if (match_byte (3))
379     {
380       char *text = get_string();
381       dump_value_31(stream);
382       char *identifier = get_string();
383       char *text_eng = get_string();
384       fprintf (stream, "<string c=\"%s\"", text_eng);
385       if (identifier[0])
386         fprintf (stream, " identifier=\"%s\"", identifier);
387       if (strcmp(text_eng, text))
388         fprintf (stream, " local=\"%s\"", text);
389       fprintf (stream, "/>\n");
390       if (!match_byte (0))
391         match_byte_assert(1);
392       if (match1)
393         match_byte (1);
394     }
395   else if (match_byte (5))
396     {
397       dump_value_31(stream);
398       char *name = get_string ();
399       char *label = get_string ();
400       fprintf (stream, "<variable name=\"%s\"", name);
401       if (label[0])
402         fprintf (stream, " label=\"%s\"", label);
403       fprintf (stream, "/>\n");
404       if (!match_byte(1) && !match_byte(2))
405         match_byte_assert(3);
406     }
407   else if (match_byte (2))
408     {
409       unsigned int format;
410       char *var, *vallab;
411       double value;
412
413       match_byte_assert (0x58);
414       format = get_u32 ();
415       value = get_double ();
416       var = get_string ();
417       vallab = get_string ();
418       fprintf (stream, "<numeric-datum value=\"%.*g\" format=\"%s%d.%d\"",
419               DBL_DIG, value, format_to_string(format >> 16), (format >> 8) & 0xff, format & 0xff);
420       if (var[0])
421         fprintf (stream, " variable=\"%s\"", var);
422       if (vallab[0])
423         fprintf (stream, " label=\"%s\"/>\n", vallab);
424       fprintf (stream, "/>\n");
425       if (!match_byte (1) && !match_byte(2))
426         match_byte_assert (3);
427     }
428   else if (match_byte (4))
429     {
430       unsigned int format;
431       char *var, *vallab, *value;
432
433       match_byte_assert (0x58);
434       format = get_u32 ();
435       vallab = get_string ();
436       var = get_string ();
437       if (!match_byte(1) && !match_byte(2))
438         match_byte_assert (3);
439       value = get_string ();
440       fprintf (stream, "<string-datum value=\"%s\" format=\"%s%d.%d\"",
441               value, format_to_string(format >> 16), (format >> 8) & 0xff, format & 0xff);
442       if (var[0])
443         fprintf (stream, " variable=\"%s\"", var);
444       if (vallab[0])
445         fprintf (stream, " label=\"%s\"/>\n", vallab);
446       fprintf (stream, "/>\n");
447     }
448   else if (match_byte (1))
449     {
450       unsigned int format;
451       double value;
452
453       dump_value_31(stream);
454       format = get_u32 ();
455       value = get_double ();
456       fprintf (stream, "<number value=\"%.*g\" format=\"%s%d.%d\"/>\n",
457                DBL_DIG, value, format_to_string(format >> 16), (format >> 8) & 0xff, format & 0xff);
458       if (match1)
459         match_byte (1);
460     }
461   else
462     {
463       dump_value_31(stream);
464
465       char *base = get_string();
466       int x = get_u32();
467       fprintf (stream, "<template format=\"%s\">\n", base);
468       for (int i = 0; i < x; i++)
469         {
470           int y = get_u32();
471           if (!y)
472             y = 1;
473           else
474             match_u32_assert(0);
475           for (int j = 0; j <= level + 1; j++)
476             fprintf (stream, "    ");
477           fprintf (stream, "<substitution index=\"%d\">\n", i + 1);
478           for (int j = 0; j < y; j++)
479             dump_value (stream, level + 2, false);
480           for (int j = 0; j <= level + 1; j++)
481             fprintf (stream, "    ");
482           fprintf (stream, "</substitution>\n");
483         }
484       for (int j = 0; j <= level; j++)
485         fprintf (stream, "    ");
486       fprintf (stream, "</template>\n");
487     }
488 }
489
490 static int
491 compare_int(const void *a_, const void *b_)
492 {
493   const int *a = a_;
494   const int *b = b_;
495   return *a < *b ? -1 : *a > *b;
496 }
497
498 static void
499 check_permutation(int *a, int n, const char *name)
500 {
501   int b[n];
502   memcpy(b, a, n * sizeof *a);
503   qsort(b, n, sizeof *b, compare_int);
504   for (int i = 0; i < n; i++)
505     if (b[i] != i)
506       {
507         fprintf(stderr, "bad %s permutation:", name);
508         for (int i = 0; i < n; i++)
509           fprintf(stderr, " %d", a[i]);
510         putc('\n', stderr);
511         exit(1);
512       }
513 }
514
515 static void
516 dump_category(int level, int *indexes, int *n_indexes)
517 {
518   for (int i = 0; i <= level; i++)
519     fprintf (stdout, "    ");
520   printf ("<category>\n");
521   dump_value (stdout, level + 1, true);
522   match_byte(0);
523   match_byte(0);
524   match_byte(0);
525
526   if (match_u32 (1))
527     match_byte (0);
528   else if (match_byte (1))
529     {
530       match_byte (0);
531       if (!match_u32 (2))
532         match_u32_assert (1);
533       match_byte (0);
534     }
535   else if (!match_u32(2))
536     match_u32_assert (0);
537
538   int indx = get_u32();
539   int n_categories = get_u32();
540   if (indx != -1)
541     {
542       if (n_categories != 0)
543         {
544           fprintf(stderr, "index not -1 but subcategories\n");
545           exit(1);
546         }
547       indexes[(*n_indexes)++] = indx;
548     }
549   if (n_categories == 0)
550     {
551       for (int i = 0; i <= level + 1; i++)
552         fprintf (stdout, "    ");
553       fprintf (stdout, "<category-index>%d</category-index>\n", indx);
554     }
555   for (int i = 0; i < n_categories; i++)
556     dump_category (level + 1, indexes, n_indexes);
557   for (int i = 0; i <= level; i++)
558     fprintf (stdout, "    ");
559   printf ("</category>\n");
560 }
561
562 static void
563 dump_dim(int indx)
564 {
565   int n_categories;
566
567   printf ("<dimension index=\"%d\">\n", indx);
568   dump_value (stdout, 0, false);
569
570   /* This byte is usually 0x02 but 0x00 and 0x75 (!) have also been spotted. */
571   pos++;
572
573   if (!match_byte(0) && !match_byte(1))
574     match_byte_assert(2);
575   if (!match_u32(0))
576     match_u32_assert(2);
577   if (!match_byte(0))
578     match_byte_assert(1);
579   if (!match_byte(0))
580     match_byte_assert(1);
581   match_byte_assert(1);
582   match_u32_assert(indx);
583   n_categories = get_u32();
584
585   int indexes[1024];
586   int n_indexes = 0;
587   for (int i = 0; i < n_categories; i++)
588     dump_category (0, indexes, &n_indexes);
589   check_permutation(indexes, n_indexes, "categories");
590
591   fprintf (stdout, "</dimension>\n");
592 }
593
594 int n_dims;
595 static void
596 dump_dims(void)
597 {
598   n_dims = get_u32();
599   for (int i = 0; i < n_dims; i++)
600     dump_dim (i);
601 }
602
603 static void
604 dump_data(void)
605 {
606   /* The first three numbers add to the number of dimensions. */
607   int t = get_u32();
608   t += get_u32();
609   match_u32_assert(n_dims - t);
610
611   /* The next n_dims numbers are a permutation of the dimension numbers. */
612   int a[n_dims];
613   for (int i = 0; i < n_dims; i++)
614     a[i] = get_u32();
615   check_permutation(a, n_dims, "dimensions");
616
617   int x = get_u32();
618   printf ("<data>\n");
619   for (int i = 0; i < x; i++)
620     {
621       printf ("    <datum index=\"%d\">\n", get_u32());
622       match_u32_assert(0);
623       dump_value(stdout, 1, false);
624       fprintf (stdout, "    </datum>\n");
625     }
626   printf ("</data>\n");
627 }
628
629 static void
630 dump_title(void)
631 {
632   pos = 0x27;
633   printf ("<title-local>\n");
634   dump_value(stdout, 0, true);
635   printf ("</title-local>\n");
636
637   printf ("<subtype>\n");
638   dump_value(stdout, 0, true);
639   printf ("</subtype>\n");
640
641   match_byte_assert(0x31);
642
643   printf ("<title-c>\n");
644   dump_value(stdout, 0, true);
645   printf ("</title-c>\n");
646
647   match_byte(0);
648   match_byte_assert(0x58);
649   if (match_byte(0x31))
650     {
651       printf ("<caption>\n");
652       dump_value(stdout, 0, false);
653       printf ("</caption>\n");
654     }
655   else
656     match_byte_assert(0x58);
657
658
659   int n_footnotes = get_u32();
660   for (int i = 0; i < n_footnotes; i++)
661     {
662       printf ("<footnote index=\"%d\">\n", i);
663       dump_value(stdout, 0, false);
664       if (match_byte (0x31))
665         {
666           /* Custom footnote marker string. */
667           match_byte_assert(3);
668           get_string();
669           match_byte_assert(0x58);
670           match_u32_assert(0);
671           get_string();
672         }
673       else
674         match_byte_assert (0x58);
675       printf("(%d)\n", get_u32());
676       printf ("</footnote>\n");
677     }
678 }
679
680 static void
681 dump_fonts(void)
682 {
683   match_byte(0);
684   for (int i = 1; i <= 8; i++)
685     {
686       printf ("<style index=\"%d\"", i);
687       match_byte_assert(i);
688       match_byte_assert(0x31);
689       printf(" font=\"%s\"", get_string());
690       match_byte_assert(0);
691       match_byte_assert(0);
692       if (!match_byte(0x40) && !match_byte(0x20) && !match_byte(0x80) && !match_byte(0x10))
693         match_byte_assert(0x50);
694       if (!match_byte(0x41))
695         match_byte_assert(0x51);
696       if (!match_u32(0))
697         match_u32_assert(1);
698       match_byte_assert(0);
699
700       /* OK, this seems really unlikely to be totally correct, but it matches my corpus... */
701       if (!match_u32(0) && !match_u32(2))
702         match_u32_assert(0xfaad);
703
704       if (!match_u32(0) && !match_u32(1) && !match_u32(2))
705         match_u32_assert(3);
706       printf (" fgcolor=\"%s\"", get_string());
707       printf (" bgcolor=\"%s\"", get_string());
708       match_u32_assert(0);
709       match_u32_assert(0);
710       match_byte_assert(0);
711
712       /* These seem unlikely to be correct too. */
713       if (i != 3)
714         {
715           match_u32_assert(8);
716           if (!match_u32(10))
717             match_u32_assert(11);
718           match_u32_assert(1);
719         }
720       else
721         {
722           get_u32();
723           if (!match_u32(-1) && !match_u32(8))
724             match_u32_assert(24);
725           if (!match_u32(-1) && !match_u32(2))
726             match_u32_assert(3);
727         }
728
729       /* Who knows? Ranges from -1 to 8 with no obvious pattern. */
730       get_u32();
731
732       printf ("/>\n");
733     }
734
735   match_u32_assert(240);
736   pos += 240;
737
738   match_u32_assert(18);
739   pos += 18;
740
741   if (match_u32(117))
742     pos += 117;
743   else
744     {
745       match_u32_assert(142);
746       pos += 142;
747     }
748
749   int count = get_u32();
750   pos += 4 * count;
751
752   printf ("<encoding>%s</encoding>\n", get_string ());
753
754   if (!match_u32(0))
755     match_u32_assert(UINT32_MAX);
756   if (!match_byte(0))
757     match_byte_assert(1);
758   match_byte_assert(0);
759   if (!match_byte(0))
760     match_byte_assert(1);
761   if (!match_byte(0x97) && !match_byte(0x98) && !match_byte(0x99))
762     match_byte_assert(0x9a);
763   match_byte_assert(7);
764   match_byte_assert(0);
765   match_byte_assert(0);
766   if (match_byte('.'))
767     match_byte_assert(',');
768   else
769     {
770       match_byte_assert(',');
771       if (!match_byte('.'))
772         match_byte_assert(' ');
773     }
774   match_u32_assert(5);
775   for (int i = 0; i < 5; i++)
776     get_string();
777   pos += get_u32();
778 }
779
780 int
781 main(int argc, char *argv[])
782 {
783   size_t start;
784   struct stat s;
785
786   if (isatty(STDIN_FILENO))
787     {
788       fprintf(stderr, "redirect stdin from a .bin file\n");
789       exit(1);
790     }
791   if (fstat(STDIN_FILENO, &s))
792     {
793       perror("fstat");
794       exit(1);
795     }
796   n = s.st_size;
797   data = malloc(n);
798   if (!data)
799     {
800       perror("malloc");
801       exit(1);
802     }
803   if (read(STDIN_FILENO, data, n) != n)
804     {
805       perror("read");
806       exit(1);
807     }
808
809   if (argc > 1)
810     {
811       if (!strcmp(argv[1], "title0"))
812         {
813           pos = 0x27;
814           if (match_byte (0x03)
815               || (match_byte (0x05) && match_byte (0x58)))
816             printf ("%s\n", get_string());
817           else
818             printf ("<unknown>\n");
819           return 0;
820         }
821       else if (!strcmp(argv[1], "title"))
822         {
823           dump_title();
824           exit(0);
825         }
826       else if (!strcmp(argv[1], "titleraw"))
827         {
828           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
829           start = 0x27;
830           n = find(fonts, sizeof fonts - 1);
831         }
832       else if (!strcmp(argv[1], "fonts"))
833         {
834           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
835           const char styles[] = "\xf0\0\0\0";
836           start = find(fonts, sizeof fonts - 1);
837           n = find(styles, sizeof styles - 1);
838         }
839       else if (!strcmp(argv[1], "styles"))
840         {
841           const char styles[] = "\xf0\0\0\0";
842           const char dimensions[] = "-,,,.\0";
843           start = find(styles, sizeof styles - 1);
844           n = find(dimensions, sizeof dimensions - 1) + sizeof dimensions - 1;
845         }
846       else if (!strcmp(argv[1], "dimensions") || !strcmp(argv[1], "all"))
847         {
848           pos = 0;
849           match_byte_assert(1);
850           match_byte_assert(0);
851           match_u32_assert(3);
852           match_byte_assert(1);
853           if (!match_byte(0))
854             match_byte_assert(1);
855           match_byte_assert(0);
856           match_byte_assert(0);
857           if (!match_byte(0))
858             match_byte_assert(1);
859           pos++;
860           match_byte_assert(0);
861           match_byte_assert(0);
862           match_byte_assert(0);
863           dump_title ();
864           dump_fonts();
865           dump_dims ();
866           dump_data ();
867           match_byte (1);
868           if (pos != n)
869             {
870               fprintf (stderr, "%x / %x\n", pos, n);
871               exit(1);
872             }
873           exit(0);
874         }
875       else
876         {
877           fprintf (stderr, "unknown section %s\n", argv[1]);
878           exit(1);
879         }
880     }
881   else
882     start = 0x27;
883
884   dump_raw(stdout, start, n);
885
886   return 0;
887 }