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