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