dump: Make raw dumps include file offsets.
[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               match_byte_assert(12); /* size? */
278             }
279           else
280             match_byte_assert(0x58);
281           match_byte_assert(0x58);
282           if (pos != outer_end)
283             {
284               fprintf(stderr, "outer end discrepancy\n");
285               exit(1);
286             }
287         }
288       else if (match_u32 (1))
289         {
290           fprintf(stream, "(footnote %d) ", get_u32());
291           dump_nested_string();
292         }
293       else if (match_u32 (2))
294         {
295           fprintf(stream, "(special 2)");
296           match_byte_assert(0);
297           match_byte_assert(0);
298           if (!match_u32 (2))
299             match_u32_assert(1);
300           dump_nested_string(); /* Our corpus doesn't contain any examples with strings though. */
301         }
302       else
303         {
304           match_u32_assert(3);
305           fprintf(stream, "(special 3)");
306           match_byte_assert(0);
307           match_byte_assert(0);
308           match_byte_assert(1);
309           match_byte_assert(0);
310           match_u32_assert(2);
311           dump_nested_string(); /* Our corpus doesn't contain any examples with strings though. */
312         }
313     }
314   else
315     match_byte_assert (0x58);
316 }
317
318 static const char *
319 format_to_string (int type)
320 {
321   static char tmp[16];
322   switch (type)
323     {
324     case 1: return "A";
325     case 2: return "AHEX";
326     case 3: return "COMMA";
327     case 4: return "DOLLAR";
328     case 5: case 40: return "F";
329     case 6: return "IB";
330     case 7: return "PIBHEX";
331     case 8: return "P";
332     case 9: return "PIB";
333     case 10: return "PK";
334     case 11: return "RB";
335     case 12: return "RBHEX";
336     case 15: return "Z";
337     case 16: return "N";
338     case 17: return "E";
339     case 20: return "DATE";
340     case 21: return "TIME";
341     case 22: return "DATETIME";
342     case 23: return "ADATE";
343     case 24: return "JDATE";
344     case 25: return "DTIME";
345     case 26: return "WKDAY";
346     case 27: return "MONTH";
347     case 28: return "MOYR";
348     case 29: return "QYR";
349     case 30: return "WKYR";
350     case 31: return "PCT";
351     case 32: return "DOT";
352     case 33: return "CCA";
353     case 34: return "CCB";
354     case 35: return "CCC";
355     case 36: return "CCD";
356     case 37: return "CCE";
357     case 38: return "EDATE";
358     case 39: return "SDATE";
359     default:
360       abort();
361       sprintf(tmp, "<%d>", type);
362       return tmp;
363     }
364 }
365
366 static void
367 dump_value(FILE *stream, int level, bool match1)
368 {
369   match_byte(0);
370   match_byte(0);
371   match_byte(0);
372   match_byte(0);
373
374   for (int i = 0; i <= level; i++)
375     fprintf (stream, "    ");
376
377   if (match_byte (3))
378     {
379       char *text = get_string();
380       dump_value_31(stream);
381       char *identifier = get_string();
382       char *text_eng = get_string();
383       fprintf (stream, "<string c=\"%s\"", text_eng);
384       if (identifier[0])
385         fprintf (stream, " identifier=\"%s\"", identifier);
386       if (strcmp(text_eng, text))
387         fprintf (stream, " local=\"%s\"", text);
388       fprintf (stream, "/>\n");
389       if (!match_byte (0))
390         match_byte_assert(1);
391       if (match1)
392         match_byte (1);
393     }
394   else if (match_byte (5))
395     {
396       dump_value_31(stream);
397       char *name = get_string ();
398       char *label = get_string ();
399       fprintf (stream, "<variable name=\"%s\"", name);
400       if (label[0])
401         fprintf (stream, " label=\"%s\"", label);
402       fprintf (stream, "/>\n");
403       if (!match_byte(1) && !match_byte(2))
404         match_byte_assert(3);
405     }
406   else if (match_byte (2))
407     {
408       unsigned int format;
409       char *var, *vallab;
410       double value;
411
412       match_byte_assert (0x58);
413       format = get_u32 ();
414       value = get_double ();
415       var = get_string ();
416       vallab = get_string ();
417       fprintf (stream, "<numeric-datum value=\"%.*g\" format=\"%s%d.%d\"",
418               DBL_DIG, value, format_to_string(format >> 16), (format >> 8) & 0xff, format & 0xff);
419       if (var[0])
420         fprintf (stream, " variable=\"%s\"", var);
421       if (vallab[0])
422         fprintf (stream, " label=\"%s\"/>\n", vallab);
423       fprintf (stream, "/>\n");
424       if (!match_byte (1) && !match_byte(2))
425         match_byte_assert (3);
426     }
427   else if (match_byte (4))
428     {
429       unsigned int format;
430       char *var, *vallab, *value;
431
432       match_byte_assert (0x58);
433       format = get_u32 ();
434       vallab = get_string ();
435       var = get_string ();
436       if (!match_byte(1) && !match_byte(2))
437         match_byte_assert (3);
438       value = get_string ();
439       fprintf (stream, "<string-datum value=\"%s\" format=\"%s%d.%d\"",
440               value, format_to_string(format >> 16), (format >> 8) & 0xff, format & 0xff);
441       if (var[0])
442         fprintf (stream, " variable=\"%s\"", var);
443       if (vallab[0])
444         fprintf (stream, " label=\"%s\"/>\n", vallab);
445       fprintf (stream, "/>\n");
446     }
447   else if (match_byte (1))
448     {
449       unsigned int format;
450       double value;
451
452       dump_value_31(stream);
453       format = get_u32 ();
454       value = get_double ();
455       fprintf (stream, "<number value=\"%.*g\" format=\"%s%d.%d\"/>\n",
456                DBL_DIG, value, format_to_string(format >> 16), (format >> 8) & 0xff, format & 0xff);
457       if (match1)
458         match_byte (1);
459     }
460   else
461     {
462       dump_value_31(stream);
463
464       char *base = get_string();
465       int x = get_u32();
466       fprintf (stream, "<template format=\"%s\">\n", base);
467       for (int i = 0; i < x; i++)
468         {
469           int y = get_u32();
470           if (!y)
471             y = 1;
472           else
473             match_u32_assert(0);
474           for (int j = 0; j <= level + 1; j++)
475             fprintf (stream, "    ");
476           fprintf (stream, "<substitution index=\"%d\">\n", i + 1);
477           for (int j = 0; j < y; j++)
478             dump_value (stream, level + 2, false);
479           for (int j = 0; j <= level + 1; j++)
480             fprintf (stream, "    ");
481           fprintf (stream, "</substitution>\n");
482         }
483       for (int j = 0; j <= level; j++)
484         fprintf (stream, "    ");
485       fprintf (stream, "</template>\n");
486     }
487 }
488
489 static int
490 compare_int(const void *a_, const void *b_)
491 {
492   const int *a = a_;
493   const int *b = b_;
494   return *a < *b ? -1 : *a > *b;
495 }
496
497 static void
498 check_permutation(int *a, int n, const char *name)
499 {
500   int b[n];
501   memcpy(b, a, n * sizeof *a);
502   qsort(b, n, sizeof *b, compare_int);
503   for (int i = 0; i < n; i++)
504     if (b[i] != i)
505       {
506         fprintf(stderr, "bad %s permutation:", name);
507         for (int i = 0; i < n; i++)
508           fprintf(stderr, " %d", a[i]);
509         putc('\n', stderr);
510         exit(1);
511       }
512 }
513
514 static void
515 dump_category(int level, int *indexes, int *n_indexes)
516 {
517   for (int i = 0; i <= level; i++)
518     fprintf (stdout, "    ");
519   printf ("<category>\n");
520   dump_value (stdout, level + 1, true);
521   match_byte(0);
522   match_byte(0);
523   match_byte(0);
524
525   if (match_u32 (1))
526     match_byte (0);
527   else if (match_byte (1))
528     {
529       match_byte (0);
530       if (!match_u32 (2))
531         match_u32_assert (1);
532       match_byte (0);
533     }
534   else if (!match_u32(2))
535     match_u32_assert (0);
536
537   int indx = get_u32();
538   int n_categories = get_u32();
539   if (indx != -1)
540     {
541       if (n_categories != 0)
542         {
543           fprintf(stderr, "index not -1 but subcategories\n");
544           exit(1);
545         }
546       indexes[(*n_indexes)++] = indx;
547     }
548   if (n_categories == 0)
549     {
550       for (int i = 0; i <= level + 1; i++)
551         fprintf (stdout, "    ");
552       fprintf (stdout, "<category-index>%d</category-index>\n", indx);
553     }
554   for (int i = 0; i < n_categories; i++)
555     dump_category (level + 1, indexes, n_indexes);
556   for (int i = 0; i <= level; i++)
557     fprintf (stdout, "    ");
558   printf ("</category>\n");
559 }
560
561 static void
562 dump_dim(int indx)
563 {
564   int n_categories;
565
566   printf ("<dimension index=\"%d\">\n", indx);
567   dump_value (stdout, 0, false);
568
569   /* This byte is usually 0x02 but 0x00 and 0x75 (!) have also been spotted. */
570   pos++;
571
572   if (!match_byte(0) && !match_byte(1))
573     match_byte_assert(2);
574   if (!match_u32(0))
575     match_u32_assert(2);
576   if (!match_byte(0))
577     match_byte_assert(1);
578   if (!match_byte(0))
579     match_byte_assert(1);
580   match_byte_assert(1);
581   match_u32_assert(indx);
582   n_categories = get_u32();
583
584   int indexes[1024];
585   int n_indexes = 0;
586   for (int i = 0; i < n_categories; i++)
587     dump_category (0, indexes, &n_indexes);
588   check_permutation(indexes, n_indexes, "categories");
589
590   fprintf (stdout, "</dimension>\n");
591 }
592
593 int n_dims;
594 static void
595 dump_dims(void)
596 {
597   n_dims = get_u32();
598   for (int i = 0; i < n_dims; i++)
599     dump_dim (i);
600 }
601
602 static void
603 dump_data(void)
604 {
605   /* The first three numbers add to the number of dimensions. */
606   int t = get_u32();
607   t += get_u32();
608   match_u32_assert(n_dims - t);
609
610   /* The next n_dims numbers are a permutation of the dimension numbers. */
611   int a[n_dims];
612   for (int i = 0; i < n_dims; i++)
613     a[i] = get_u32();
614   check_permutation(a, n_dims, "dimensions");
615
616   int x = get_u32();
617   printf ("<data>\n");
618   for (int i = 0; i < x; i++)
619     {
620       printf ("    <datum index=\"%d\">\n", get_u32());
621       match_u32_assert(0);
622       dump_value(stdout, 1, false);
623       fprintf (stdout, "    </datum>\n");
624     }
625   printf ("</data>\n");
626 }
627
628 static void
629 dump_title(void)
630 {
631   pos = 0x27;
632   printf ("<title-local>\n");
633   dump_value(stdout, 0, true);
634   printf ("</title-local>\n");
635
636   printf ("<subtype>\n");
637   dump_value(stdout, 0, true);
638   printf ("</subtype>\n");
639
640   match_byte_assert(0x31);
641
642   printf ("<title-c>\n");
643   dump_value(stdout, 0, true);
644   printf ("</title-c>\n");
645
646   match_byte(0);
647   match_byte_assert(0x58);
648   if (match_byte(0x31))
649     {
650       printf ("<caption>\n");
651       dump_value(stdout, 0, false);
652       printf ("</caption>\n");
653     }
654   else
655     match_byte_assert(0x58);
656
657
658   int n_footnotes = get_u32();
659   for (int i = 0; i < n_footnotes; i++)
660     {
661       printf ("<footnote index=\"%d\">\n", i);
662       dump_value(stdout, 0, false);
663       if (match_byte (0x31))
664         {
665           /* Custom footnote marker string. */
666           match_byte_assert(3);
667           get_string();
668           match_byte_assert(0x58);
669           match_u32_assert(0);
670           get_string();
671         }
672       else
673         match_byte_assert (0x58);
674       printf("(%d)\n", get_u32());
675       printf ("</footnote>\n");
676     }
677 }
678
679 static void
680 dump_fonts(void)
681 {
682   match_byte(0);
683   for (int i = 1; i <= 8; i++)
684     {
685       printf ("<style index=\"%d\"", i);
686       match_byte_assert(i);
687       match_byte_assert(0x31);
688       printf(" font=\"%s\"", get_string());
689       match_byte_assert(0);
690       match_byte_assert(0);
691       if (!match_byte(0x40) && !match_byte(0x20) && !match_byte(0x80) && !match_byte(0x10))
692         match_byte_assert(0x50);
693       if (!match_byte(0x41))
694         match_byte_assert(0x51);
695       if (!match_u32(0))
696         match_u32_assert(1);
697       match_byte_assert(0);
698
699       /* OK, this seems really unlikely to be totally correct, but it matches my corpus... */
700       if (!match_u32(0) && !match_u32(2))
701         match_u32_assert(0xfaad);
702
703       if (!match_u32(0) && !match_u32(1) && !match_u32(2))
704         match_u32_assert(3);
705       printf (" fgcolor=\"%s\"", get_string());
706       printf (" bgcolor=\"%s\"", get_string());
707       match_u32_assert(0);
708       match_u32_assert(0);
709       match_byte_assert(0);
710
711       /* These seem unlikely to be correct too. */
712       if (i != 3)
713         {
714           match_u32_assert(8);
715           if (!match_u32(10))
716             match_u32_assert(11);
717           match_u32_assert(1);
718         }
719       else
720         {
721           get_u32();
722           if (!match_u32(-1) && !match_u32(8))
723             match_u32_assert(24);
724           if (!match_u32(-1) && !match_u32(2))
725             match_u32_assert(3);
726         }
727
728       /* Who knows? Ranges from -1 to 8 with no obvious pattern. */
729       get_u32();
730
731       printf ("/>\n");
732     }
733
734   match_u32_assert(240);
735   pos += 240;
736
737   match_u32_assert(18);
738   pos += 18;
739
740   if (match_u32(117))
741     pos += 117;
742   else
743     {
744       match_u32_assert(142);
745       pos += 142;
746     }
747
748   int count = get_u32();
749   pos += 4 * count;
750
751   printf ("<encoding>%s</encoding>\n", get_string ());
752
753   if (!match_u32(0))
754     match_u32_assert(UINT32_MAX);
755   if (!match_byte(0))
756     match_byte_assert(1);
757   match_byte_assert(0);
758   if (!match_byte(0))
759     match_byte_assert(1);
760   if (!match_byte(0x99) && !match_byte(0x98))
761     match_byte_assert(0x97);
762   match_byte_assert(7);
763   match_byte_assert(0);
764   match_byte_assert(0);
765   if (match_byte('.'))
766     match_byte_assert(',');
767   else
768     {
769       match_byte_assert(',');
770       if (!match_byte('.'))
771         match_byte_assert(' ');
772     }
773   match_u32_assert(5);
774   for (int i = 0; i < 5; i++)
775     get_string();
776   pos += get_u32();
777 }
778
779 int
780 main(int argc, char *argv[])
781 {
782   size_t start;
783   struct stat s;
784
785   if (isatty(STDIN_FILENO))
786     {
787       fprintf(stderr, "redirect stdin from a .bin file\n");
788       exit(1);
789     }
790   if (fstat(STDIN_FILENO, &s))
791     {
792       perror("fstat");
793       exit(1);
794     }
795   n = s.st_size;
796   data = malloc(n);
797   if (!data)
798     {
799       perror("malloc");
800       exit(1);
801     }
802   if (read(STDIN_FILENO, data, n) != n)
803     {
804       perror("read");
805       exit(1);
806     }
807
808   if (argc > 1)
809     {
810       if (!strcmp(argv[1], "title0"))
811         {
812           pos = 0x27;
813           if (match_byte (0x03)
814               || (match_byte (0x05) && match_byte (0x58)))
815             printf ("%s\n", get_string());
816           else
817             printf ("<unknown>\n");
818           return 0;
819         }
820       else if (!strcmp(argv[1], "title"))
821         {
822           dump_title();
823           exit(0);
824         }
825       else if (!strcmp(argv[1], "titleraw"))
826         {
827           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
828           start = 0x27;
829           n = find(fonts, sizeof fonts - 1);
830         }
831       else if (!strcmp(argv[1], "fonts"))
832         {
833           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
834           const char styles[] = "\xf0\0\0\0";
835           start = find(fonts, sizeof fonts - 1);
836           n = find(styles, sizeof styles - 1);
837         }
838       else if (!strcmp(argv[1], "styles"))
839         {
840           const char styles[] = "\xf0\0\0\0";
841           const char dimensions[] = "-,,,.\0";
842           start = find(styles, sizeof styles - 1);
843           n = find(dimensions, sizeof dimensions - 1) + sizeof dimensions - 1;
844         }
845       else if (!strcmp(argv[1], "dimensions") || !strcmp(argv[1], "all"))
846         {
847           pos = 0;
848           match_byte_assert(1);
849           match_byte_assert(0);
850           match_u32_assert(3);
851           match_byte_assert(1);
852           if (!match_byte(0))
853             match_byte_assert(1);
854           match_byte_assert(0);
855           match_byte_assert(0);
856           if (!match_byte(0))
857             match_byte_assert(1);
858           pos++;
859           match_byte_assert(0);
860           match_byte_assert(0);
861           match_byte_assert(0);
862           dump_title ();
863           dump_fonts();
864           dump_dims ();
865           dump_data ();
866           match_byte (1);
867           if (pos != n)
868             {
869               fprintf (stderr, "%x / %x\n", pos, n);
870               exit(1);
871             }
872           exit(0);
873         }
874       else
875         {
876           fprintf (stderr, "unknown section %s\n", argv[1]);
877           exit(1);
878         }
879     }
880   else
881     start = 0x27;
882
883   dump_raw(stdout, start, n);
884
885   return 0;
886 }