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