Drop dump_dim_value_31() and dump_data_value_31() in favor of dump_value_31().
[pspp] / dump.c
1 #include <stdbool.h>
2 #include <stdint.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <sys/stat.h>
7 #include <unistd.h>
8
9 static uint8_t *data;
10 static size_t n;
11
12 static bool
13 all_ascii(const uint8_t *p, size_t n)
14 {
15   for (size_t i = 0; i < n; i++)
16     if (p[i] < 32 || p[i] > 126)
17       return false;
18   return true;
19 }
20
21 static size_t
22 try_find(const char *target, size_t target_len)
23 {
24   const uint8_t *pos = (const uint8_t *) memmem (data, n, target, target_len);
25   return pos ? pos - data : 0;
26 }
27
28 static size_t
29 try_find_tail(const char *target, size_t target_len)
30 {
31   size_t pos = try_find(target, target_len);
32   return pos ? pos + target_len : 0;
33 }
34
35 static size_t
36 find(const char *target, size_t target_len)
37 {
38   size_t pos = try_find(target, target_len);
39   if (!pos)
40     {
41       fprintf (stderr, "not found\n");
42       exit(1);
43     }
44   return pos;
45 }
46
47 static size_t
48 find_tail(const char *target, size_t target_len)
49 {
50   size_t pos = try_find_tail(target, target_len);
51   if (!pos)
52     {
53       fprintf (stderr, "not found\n");
54       exit(1);
55     }
56   return pos;
57 }
58
59 size_t pos;
60
61 #define XSTR(x) #x
62 #define STR(x) XSTR(x)
63 #define WHERE __FILE__":" STR(__LINE__)
64
65 static unsigned int
66 get_u32(void)
67 {
68   uint32_t x;
69   memcpy(&x, &data[pos], 4);
70   pos += 4;
71   return x;
72 }
73
74 static double
75 get_double(void)
76 {
77   double x;
78   memcpy(&x, &data[pos], 8);
79   pos += 8;
80   return x;
81 }
82
83 static bool
84 match_u32(uint32_t x)
85 {
86   if (get_u32() == x)
87     return true;
88   pos -= 4;
89   return false;
90 }
91
92 static void
93 match_u32_assert(uint32_t x, const char *where)
94 {
95   unsigned int y = get_u32();
96   if (x != y)
97     {
98       fprintf(stderr, "%s: 0x%x: expected i%u, got i%u\n", where, pos - 4, x, y);
99       exit(1);
100     }
101 }
102 #define match_u32_assert(x) match_u32_assert(x, WHERE)
103
104 static bool
105 match_byte(uint8_t b)
106 {
107   if (pos < n && data[pos] == b)
108     {
109       pos++;
110       return true;
111     }
112   else
113     return false;
114 }
115
116 static void
117 match_byte_assert(uint8_t b, const char *where)
118 {
119   if (!match_byte(b))
120     {
121       fprintf(stderr, "%s: 0x%x: expected %02x, got %02x\n", where, pos, b, data[pos]);
122       exit(1);
123     }
124 }
125 #define match_byte_assert(b) match_byte_assert(b, WHERE)
126
127 static char *
128 get_string(const char *where)
129 {
130   if (1
131       /*data[pos + 1] == 0 && data[pos + 2] == 0 && data[pos + 3] == 0*/
132       /*&& all_ascii(&data[pos + 4], data[pos])*/)
133     {
134       int len = data[pos] + data[pos + 1] * 256;
135       char *s = malloc(len + 1);
136
137       memcpy(s, &data[pos + 4], len);
138       s[len] = 0;
139       pos += 4 + len;
140       return s;
141     }
142   else
143     {
144       fprintf(stderr, "%s: 0x%x: expected string\n", where, pos);
145       exit(1);
146     }
147 }
148 #define get_string() get_string(WHERE)
149
150 static void
151 dump_value_31(void)
152 {
153   if (match_byte (0x31))
154     {
155       if (match_u32 (0))
156         {
157           if (match_u32 (1))
158             get_string();
159           else
160             match_u32_assert (0);
161           int subn = get_u32 ();
162           printf ("nested %d bytes", subn);
163           pos += subn;
164         }
165       else if (match_u32 (1))
166         {
167           printf("(footnote %d) ", get_u32());
168           match_byte_assert (0);
169           match_byte_assert (0);
170           int subn = get_u32 ();
171           printf ("nested %d bytes", subn);
172           pos += subn;
173         }
174       else if (match_u32 (2))
175         {
176           printf("(special 2)");
177           match_byte_assert(0);
178           match_byte_assert(0);
179           match_u32_assert(1);
180           match_byte_assert(0);
181           match_byte_assert(0);
182           int subn = get_u32 ();
183           printf ("nested %d bytes", subn);
184           pos += subn;
185         }
186       else
187         {
188           match_u32_assert(3);
189           printf("(special 3)");
190           match_byte_assert(0);
191           match_byte_assert(0);
192           match_byte_assert(1);
193           match_byte_assert(0);
194           int subn = get_u32 ();
195           printf ("nested %d bytes, ", subn);
196           pos += subn;
197           subn = get_u32 ();
198           printf ("nested %d bytes, ", subn);
199           pos += subn;
200         }
201     }
202   else
203     match_byte_assert (0x58);
204 }
205
206 static void
207 dump_value(int level)
208 {
209   for (int i = 0; i <= level; i++)
210     printf ("    ");
211
212   match_byte (0);
213   match_byte (0);
214   match_byte (0);
215   match_byte (0);
216   if (match_byte (3))
217     {
218       char *s1 = get_string();
219       dump_value_31();
220       char *s2 = get_string();
221       char *s3 = get_string();
222       if (strcmp(s1, s3))
223         printf("strings \"%s\", \"%s\" and \"%s\"", s1, s2, s3);
224       else
225         printf("string \"%s\" and \"%s\"", s1, s2);
226       match_byte (0);
227       match_byte (1);
228       match_byte (1);
229       match_byte (0);
230       match_byte (0);
231       match_byte (0);
232     }
233   else if (match_byte (5))
234     {
235       match_byte_assert (0x58);
236       printf ("variable \"%s\"", get_string());
237       get_string();
238       if (!match_byte(1) && !match_byte(2))
239         match_byte_assert(3);
240       match_byte (0);
241       match_byte (0);
242       match_byte (0);
243     }
244   else if (match_byte (2))
245     {
246       unsigned int format;
247       char *var, *vallab;
248       double value;
249
250       match_byte_assert (0x58);
251       format = get_u32 ();
252       value = get_double ();
253       var = get_string ();
254       vallab = get_string ();
255       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
256               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
257       if (!match_byte (1) && !match_byte(2))
258         match_byte_assert (3);
259       match_byte (0);
260       match_byte (0);
261       match_byte (0);
262     }
263   else if (match_byte (4))
264     {
265       unsigned int format;
266       char *var, *vallab, *value;
267
268       match_byte_assert (0x58);
269       format = get_u32 ();
270       vallab = get_string ();
271       var = get_string ();
272       if (!match_byte(1) && !match_byte(2))
273         match_byte_assert (3);
274       value = get_string ();
275       printf ("value \"%s\" format %d(%d.%d) var \"%s\" vallab \"%s\"",
276               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
277       match_byte (0);
278       match_byte (0);
279       match_byte (0);
280     }
281   else if (match_byte (1))
282     {
283       unsigned int format;
284       double value;
285
286       dump_value_31();
287       format = get_u32 ();
288       value = get_double ();
289       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
290       match_byte (1);
291       match_byte (0);
292       match_byte (0);
293       match_byte (0);
294     }
295   else
296     {
297       dump_value_31();
298       char *base = get_string();
299       int x = get_u32();
300       printf ("\"%s\" with %d variables:\n", base, x);
301       if (match_u32(0))
302         {
303           for (int i = 0; i < x; i++)
304             {
305               dump_value (level+1);
306               putchar('\n');
307             }
308         }
309       else
310         {
311           for (int i = 0; i < x; i++)
312             {
313               int y = get_u32();
314               match_u32_assert(0);
315               for (int j = 0; j <= level; j++)
316                 printf ("    ");
317               printf("variable %d has %d values:\n", i, y);
318               for (int j = 0; j < y; j++)
319                 {
320                   if (match_byte(3))
321                     {
322                       char *a = get_string();
323                       match_byte_assert(0x58);
324                       char *b = get_string();
325                       char *c = get_string();
326                       for (int k = 0; k <= level + 1; k++)
327                         printf ("    ");
328                       printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
329                       match_byte(0);
330                       match_byte(0);
331                       match_byte(0);
332                       match_byte(0);
333                       match_byte(0);
334                     }
335                   else
336                     dump_value (level+1);
337                   putchar('\n');
338                 }
339             }
340         }
341     }
342 }
343
344 static void
345 dump_dim_value(int level)
346 {
347   for (int i = 0; i <= level; i++)
348     printf ("    ");
349
350   if (match_byte (3))
351     {
352       get_string();
353       dump_value_31();
354       get_string();
355       printf("string \"%s\"", get_string());
356       match_byte (0);
357       match_byte (1);
358     }
359   else if (match_byte (5))
360     {
361       match_byte_assert (0x58);
362       printf ("variable \"%s\"", get_string());
363       get_string();
364       if (!match_byte (2))
365         match_byte_assert(3);
366     }
367   else if (match_byte (2))
368     {
369       unsigned int format;
370       char *var, *vallab;
371       double value;
372
373       match_byte_assert (0x58);
374       format = get_u32 ();
375       value = get_double ();
376       var = get_string ();
377       vallab = get_string ();
378       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
379               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
380       if (!match_u32 (3))
381         match_u32_assert (2);
382       match_byte (0);
383     }
384   else if (match_byte (1))
385     {
386       unsigned int format;
387       double value;
388
389       match_byte_assert (0x58);
390       format = get_u32 ();
391       value = get_double ();
392       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
393       match_byte (1);
394       match_byte (0);
395       match_byte (0);
396       match_byte (0);
397       match_byte (1);
398     }
399   else
400     {
401       match_byte (0);
402       dump_value_31();
403       printf ("; \"%s\", substitutions:", get_string());
404       int total_subs = get_u32();
405       int x = get_u32();
406       if (x)
407         {
408           total_subs = (total_subs - 1) + x;
409           match_u32_assert (0);
410         }
411       printf (" (total %d)", total_subs);
412
413       for (int i = 0; i < total_subs; i++)
414         {
415           putc ('\n', stdout);
416           dump_value (level + 1);
417         }
418     }
419 }
420
421 static void
422 dump_category(int level)
423 {
424   match_byte (0);
425   match_byte (0);
426   match_byte (0);
427   match_byte (0);
428   dump_value (level);
429
430   if (match_u32 (2))
431     get_u32 ();
432   else if (match_u32 (1))
433     {
434       match_byte (0);
435       match_byte (0);
436       match_byte (0);
437       get_u32 ();
438     }
439   else if (match_byte (1))
440     {
441       match_byte (0);
442       if (!match_u32 (2))
443         match_u32_assert (1);
444       match_byte (0);
445       get_u32();
446     }
447   else
448     {
449       match_u32_assert (0);
450       get_u32 ();
451     }
452
453   int n_categories = get_u32();
454   if (n_categories > 0)
455     printf (", %d subcategories:", n_categories);
456   printf("\n");
457   for (int i = 0; i < n_categories; i++)
458     dump_category (level + 1);
459 }
460
461 static void
462 dump_dim(void)
463 {
464   int n_categories;
465   printf("next dim\n");
466   match_byte(0);
467   dump_dim_value(0);
468
469   /* This byte is usually 0x02 but 0x00 and 0x75 (!) have also been spotted. */
470   pos++;
471
472   if (!match_byte(0) && !match_byte(1))
473     match_byte_assert(2);
474   if (!match_u32(0))
475     match_u32_assert(2);
476   if (!match_byte(0))
477     match_byte_assert(1);
478   match_byte(0);
479   match_byte(0);
480   match_byte(0);
481   match_byte(0);
482   get_u32();
483   match_byte(0);
484   match_byte(0);
485   match_byte(0);
486   match_byte(0);
487   n_categories = get_u32();
488   printf("%d nested categories\n", n_categories);
489   for (int i = 0; i < n_categories; i++)
490     dump_category (0);
491 }
492
493 int n_dims;
494 static void
495 dump_dims(void)
496 {
497   n_dims = get_u32();
498   printf ("%u dimensions\n", n_dims);
499   for (int i = 0; i < n_dims; i++)
500     {
501       printf("\n");
502       dump_dim ();
503     }
504 }
505
506 static void
507 dump_data_value(void)
508 {
509   match_byte(0);
510   match_byte(0);
511   match_byte(0);
512   match_byte(0);
513   if (match_byte (1))
514     {
515       unsigned int format;
516       double value;
517
518       dump_value_31();
519       format = get_u32 ();
520       value = get_double ();
521       printf ("    value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
522     }
523   else if (match_byte (3))
524     {
525       get_string();
526       dump_value_31();
527       get_string();
528       printf("string \"%s\"", get_string());
529       match_byte (0);
530     }
531   else if (match_byte (2))
532     {
533       unsigned int format;
534       char *var, *vallab;
535       double value;
536
537       match_byte_assert (0x58);
538       format = get_u32 ();
539       value = get_double ();
540       var = get_string ();
541       vallab = get_string ();
542       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
543               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
544       if (!match_byte (1) && !match_byte(2))
545         match_byte_assert (3);
546     }
547   else if (match_byte (4))
548     {
549       unsigned int format;
550       char *var, *vallab, *value;
551
552       match_byte_assert (0x58);
553       format = get_u32 ();
554       vallab = get_string ();
555       var = get_string ();
556       if (!match_byte(1) && !match_byte(2))
557         match_byte_assert (3);
558       value = get_string ();
559       printf ("value \"%s\" format %d(%d.%d) var \"%s\" vallab \"%s\"",
560               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
561     }
562   else if (match_byte (5))
563     {
564       match_byte_assert (0x58);
565       printf ("variable \"%s\"", get_string());
566       get_string();
567       if (!match_byte(1) && !match_byte(2))
568         match_byte_assert(3);
569       match_byte (0);
570       match_byte (0);
571       match_byte (0);
572       match_byte (0);
573     }
574   else
575     {
576       dump_value_31();
577       char *base = get_string();
578       int x = get_u32();
579       printf ("\"%s\"; %d variables:\n", base, x);
580       for (int i = 0; i < x; i++)
581         {
582           int y = get_u32();
583           if (!y)
584             y = 1;
585           else
586             match_u32_assert(0);
587           for (int j = 0; j <= 0; j++)
588             printf ("    ");
589           printf("variable %d has %d values:\n", i, y);
590           for (int j = 0; j < y; j++)
591             dump_data_value();
592         }
593     }
594 }
595
596 static void
597 dump_data(void)
598 {
599 #if 1
600   int a[16];
601   for (int i = 0; i < 3 + n_dims; i++)
602     a[i] = get_u32();
603   printf ("data intro:");
604   for (int i = 0; i < 3 + n_dims; i++)
605     printf(" %d", a[i]);
606   printf("\n");
607 #else
608   fprintf (stderr,"data intro (%d dims):", n_dims);
609   for (int i = 0; i < 3+n_dims; i++)
610     fprintf (stderr," %d", get_u32());
611   fprintf(stderr,"\n");
612 #endif
613   int x = get_u32();
614   printf ("%d data values, starting at %08x\n", x, pos);
615   for (int i = 0; i < x; i++)
616     {
617       printf("%08x, index %d:\n", pos, get_u32());
618       match_u32_assert(0);
619       dump_data_value();
620       putchar('\n');
621     }
622 }
623
624 static void
625 dump_title_value_31(int level)
626 {
627   if (match_byte (0x31))
628     {
629       if (match_u32 (0))
630         {
631           match_u32_assert (0);
632           int subn = get_u32 ();
633           printf ("nested %d bytes", subn);
634           pos += subn;
635         }
636       else if (match_u32 (1))
637         {
638           printf("(footnote %d) ", get_u32());
639           match_byte_assert (0);
640           match_byte_assert (0);
641           int subn = get_u32 ();
642           printf ("nested %d bytes", subn);
643           pos += subn;
644         }
645       else if (match_u32 (2))
646         {
647           printf("(special 2)");
648           match_byte_assert(0);
649           match_byte_assert(0);
650           if (!match_u32(2))
651             match_u32_assert(1);
652           match_byte_assert(0);
653           match_byte_assert(0);
654           int subn = get_u32 ();
655           printf ("nested %d bytes", subn);
656           pos += subn;
657         }
658       else
659         {
660           match_u32_assert(3);
661           printf("(special 3)");
662           match_byte_assert(0);
663           match_byte_assert(0);
664           match_byte_assert(1);
665           match_byte_assert(0);
666           int subn = get_u32 ();
667           printf ("nested %d bytes, ", subn);
668           pos += subn;
669           subn = get_u32 ();
670           printf ("nested %d bytes, ", subn);
671           pos += subn;
672         }
673     }
674   else
675     match_byte_assert (0x58);
676 }
677
678 static void
679 dump_title_value(int level)
680 {
681   for (int i = 0; i <= level; i++)
682     printf ("    ");
683
684   match_byte (0);
685   match_byte (0);
686   match_byte (0);
687   match_byte (0);
688   match_byte (0);
689   if (match_byte (3))
690     {
691       get_string();
692       dump_title_value_31(level);
693       get_string();
694       printf("string \"%s\"", get_string());
695       match_byte (0);
696       match_byte (0);
697       match_byte (0);
698       match_byte (1);
699       match_byte (1);
700       match_byte (0);
701       match_byte (0);
702       match_byte (0);
703       match_byte (1);
704     }
705   else if (match_byte (5))
706     {
707       dump_title_value_31(level);
708       printf ("variable \"%s\"", get_string());
709       get_string();
710       if (!match_byte(1) && !match_byte(2))
711         match_byte_assert(3);
712     }
713   else if (match_byte (2))
714     {
715       unsigned int format;
716       char *var, *vallab;
717       double value;
718
719       match_byte_assert (0x58);
720       format = get_u32 ();
721       value = get_double ();
722       var = get_string ();
723       vallab = get_string ();
724       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
725               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
726       if (!match_byte (1) && !match_byte(2))
727         match_byte_assert (3);
728       match_byte (0);
729       match_byte (0);
730       match_byte (0);
731       match_byte (0);
732       match_byte (0);
733       match_byte (0);
734       match_byte (0);
735     }
736   else if (match_byte (4))
737     {
738       unsigned int format;
739       char *var, *vallab, *value;
740
741       match_byte_assert (0x58);
742       format = get_u32 ();
743       vallab = get_string ();
744       var = get_string ();
745       if (!match_byte(1) && !match_byte(2))
746         match_byte_assert (3);
747       value = get_string ();
748       printf ("value \"%s\" format %d(%d.%d) var \"%s\" vallab \"%s\"",
749               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
750       match_byte (0);
751       match_byte (0);
752       match_byte (0);
753       match_byte (0);
754     }
755   else if (match_byte (1))
756     {
757       unsigned int format;
758       double value;
759
760       dump_title_value_31(level);
761       format = get_u32 ();
762       value = get_double ();
763       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
764       match_byte (1);
765       match_byte (0);
766       match_byte (0);
767       match_byte (0);
768       match_byte (1);
769     }
770   else
771     {
772       dump_title_value_31(level);
773
774       char *base = get_string();
775       int x = get_u32();
776       printf ("\"%s\" with %d variables:\n", base, x);
777       for (int i = 0; i < x; i++)
778         {
779           int y = get_u32();
780           if (!y)
781             y = 1;
782           else
783             match_u32_assert(0);
784           for (int j = 0; j <= level; j++)
785             printf ("    ");
786           printf("variable %d has %d values:\n", i, y);
787           for (int j = 0; j < y; j++)
788             {
789               match_byte(0);
790               if (match_byte(3))
791                 {
792                   char *a = get_string();
793                   match_byte_assert(0x58);
794                   char *b = get_string();
795                   char *c = get_string();
796                   for (int k = 0; k <= level + 1; k++)
797                     printf ("    ");
798                   printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
799                 }
800               else
801                 dump_title_value (level+1);
802               putchar('\n');
803             }
804         }
805     }
806 }
807
808 static void
809 dump_footnote_value_31(void)
810 {
811   if (match_byte (0x31))
812     {
813       if (match_u32 (0))
814         {
815
816           match_u32_assert (0);
817           int subn = get_u32 ();
818           printf ("nested %d bytes", subn);
819           pos += subn;
820         }
821       else if (match_u32 (1))
822         {
823           printf("(footnote %d) ", get_u32());
824           match_byte_assert (0);
825           match_byte_assert (0);
826           int subn = get_u32 ();
827           printf ("nested %d bytes", subn);
828           pos += subn;
829         }
830       else if (match_u32 (2))
831         {
832           printf("(special 2)");
833           match_byte_assert(0);
834           match_byte_assert(0);
835           match_u32_assert(1);
836           match_byte_assert(0);
837           match_byte_assert(0);
838           int subn = get_u32 ();
839           printf ("nested %d bytes", subn);
840           pos += subn;
841         }
842       else
843         {
844           match_u32_assert(3);
845           printf("(special 3)");
846           match_byte_assert(0);
847           match_byte_assert(0);
848           match_byte_assert(1);
849           match_byte_assert(0);
850           int subn = get_u32 ();
851           printf ("nested %d bytes, ", subn);
852           pos += subn;
853           subn = get_u32 ();
854           printf ("nested %d bytes, ", subn);
855           pos += subn;
856         }
857     }
858   else
859     match_byte_assert (0x58);
860 }
861
862 static void
863 dump_footnote_value(int level)
864 {
865   for (int i = 0; i <= level; i++)
866     printf ("    ");
867
868   match_byte (0);
869   match_byte (0);
870   match_byte (0);
871   match_byte (0);
872   if (match_byte (3))
873     {
874       get_string();
875       dump_footnote_value_31();
876       get_string();
877       printf("string \"%s\"", get_string());
878       if (!match_byte (0))
879         match_byte_assert (1);
880     }
881   else if (match_byte (5))
882     {
883       match_byte_assert (0x58);
884       printf ("variable \"%s\"", get_string());
885       get_string();
886       if (!match_byte(1) && !match_byte(2))
887         match_byte_assert(3);
888     }
889   else if (match_byte (2))
890     {
891       unsigned int format;
892       char *var, *vallab;
893       double value;
894
895       match_byte_assert (0x58);
896       format = get_u32 ();
897       value = get_double ();
898       var = get_string ();
899       vallab = get_string ();
900       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
901               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
902       if (!match_byte (1) && !match_byte(2))
903         match_byte_assert (3);
904       match_byte (0);
905       match_byte (0);
906       match_byte (0);
907       match_byte (0);
908       match_byte (0);
909       match_byte (0);
910       match_byte (0);
911     }
912   else if (match_byte (4))
913     {
914       unsigned int format;
915       char *var, *vallab, *value;
916
917       match_byte_assert (0x58);
918       format = get_u32 ();
919       vallab = get_string ();
920       var = get_string ();
921       if (!match_byte(1) && !match_byte(2))
922         match_byte_assert (3);
923       value = get_string ();
924       printf ("value \"%s\" format %d(%d.%d) var \"%s\" vallab \"%s\"",
925               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
926       match_byte (0);
927       match_byte (0);
928       match_byte (0);
929       match_byte (0);
930     }
931   else if (match_byte (1))
932     {
933       unsigned int format;
934       double value;
935
936       dump_footnote_value_31();
937       format = get_u32 ();
938       value = get_double ();
939       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
940     }
941   else
942     {
943       dump_footnote_value_31();
944       char *base = get_string();
945       int x = get_u32();
946       printf ("\"%s\"; %d variables:\n", base, x);
947       for (int i = 0; i < x; i++)
948         {
949           int y = get_u32();
950           if (!y)
951             y = 1;
952           else
953             match_u32_assert(0);
954           for (int j = 0; j <= level; j++)
955             printf ("    ");
956           printf("variable %d has %d values:\n", i, y);
957           for (int j = 0; j < y; j++)
958             {
959               if (match_byte(3))
960                 {
961                   char *a = get_string();
962                   match_byte_assert(0x58);
963                   char *b = get_string();
964                   char *c = get_string();
965                   for (int k = 0; k <= level + 1; k++)
966                     printf ("    ");
967                   printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
968                   if (!match_byte(1))
969                     match_byte_assert(0);
970                 }
971               else
972                 dump_footnote_value (level+1);
973               putchar('\n');
974             }
975         }
976     }
977 }
978
979 static void
980 dump_title(void)
981 {
982   pos = 0x27;
983   dump_title_value(0); putchar('\n');
984   dump_title_value(0); putchar('\n');
985   match_byte_assert(0x31);
986   dump_title_value(0); putchar('\n');
987   match_byte(0);
988   match_byte_assert(0x58);
989   if (match_byte(0x31))
990     {
991       dump_footnote_value(0); putchar('\n');
992     }
993   else
994     match_byte_assert(0x58);
995
996
997   int n_footnotes = get_u32();
998   if (n_footnotes >= 20)
999     {
1000       fprintf(stderr, "%08x: %d footnotes\n", pos - 4, n_footnotes);
1001       exit(1);
1002     }
1003
1004   printf("------\n%d footnotes\n", n_footnotes);
1005   if (n_footnotes < 20)
1006     {
1007       for (int i = 0; i < n_footnotes; i++)
1008         {
1009           printf("footnote %d:\n", i);
1010           dump_footnote_value(0);
1011           match_byte(0);
1012           match_byte(0);
1013           match_byte(0);
1014           match_byte(0);
1015           if (match_byte (0x31))
1016             {
1017               /* Custom footnote marker string. */
1018               match_byte_assert(3);
1019               get_string();
1020               match_byte_assert(0x58);
1021               match_u32_assert(0);
1022               get_string();
1023             }
1024           else
1025             match_byte_assert (0x58);
1026           printf("(%d)\n", get_u32());
1027         }
1028     }
1029 }
1030
1031 static int
1032 find_dimensions(void)
1033 {
1034   {
1035     const char dimensions[] = "-,,,.\0";
1036     int x = try_find_tail(dimensions, sizeof dimensions - 1);
1037     if (x)
1038       return x;
1039   }
1040
1041   const char dimensions[] = "-,,, .\0";
1042   return find_tail(dimensions, sizeof dimensions - 1);
1043 }
1044
1045 static void
1046 dump_fonts(void)
1047 {
1048   printf("fonts: offset=%08x\n", pos);
1049   match_byte(0);
1050   for (int i = 1; i <= 8; i++)
1051     {
1052       printf("%08x: font %d, ", pos, i);
1053       match_byte_assert(i);
1054       match_byte_assert(0x31);
1055       printf("%s, ", get_string());
1056       match_byte_assert(0);
1057       match_byte_assert(0);
1058       if (!match_byte(0x40) && !match_byte(0x20) && !match_byte(0x80) && !match_byte(0x10))
1059         match_byte_assert(0x50);
1060       if (!match_byte(0x41))
1061         match_byte_assert(0x51);
1062       pos += 13;
1063       printf ("%s, ", get_string());
1064       printf ("%s, ", get_string());
1065       match_u32_assert(0);
1066       match_u32_assert(0);
1067       pos++;
1068       get_u32();
1069       get_u32();
1070       get_u32();
1071       get_u32();
1072       putchar('\n');
1073     }
1074
1075   match_u32_assert(240);
1076   pos += 240;
1077
1078   match_u32_assert(18);
1079   pos += 18;
1080
1081   if (match_u32(117))
1082     pos += 117;
1083   else
1084     {
1085       match_u32_assert(142);
1086       pos += 142;
1087     }
1088
1089   int count = get_u32();
1090   pos += 4 * count;
1091
1092   char *encoding = get_string();
1093   printf("encoding=%s\n", encoding);
1094
1095   if (!match_u32(0))
1096     match_u32_assert(UINT32_MAX);
1097   if (!match_byte(0))
1098     match_byte_assert(1);
1099   match_byte_assert(0);
1100   if (!match_byte(0))
1101     match_byte_assert(1);
1102   if (!match_byte(0x99) && !match_byte(0x98))
1103     match_byte_assert(0x97);
1104   match_byte_assert(7);
1105   match_byte_assert(0);
1106   match_byte_assert(0);
1107   if (match_byte('.'))
1108     match_byte_assert(',');
1109   else
1110     {
1111       match_byte_assert(',');
1112       if (!match_byte('.'))
1113         match_byte_assert(' ');
1114     }
1115   match_u32_assert(5);
1116   for (int i = 0; i < 5; i++)
1117     get_string();
1118   pos += get_u32();
1119   if (pos != find_dimensions())
1120     fprintf (stderr, "%08x / %08x\n", pos, find_dimensions());
1121 }
1122
1123 int
1124 main(int argc, char *argv[])
1125 {
1126   size_t start;
1127   struct stat s;
1128
1129   if (isatty(STDIN_FILENO))
1130     {
1131       fprintf(stderr, "redirect stdin from a .bin file\n");
1132       exit(1);
1133     }
1134   if (fstat(STDIN_FILENO, &s))
1135     {
1136       perror("fstat");
1137       exit(1);
1138     }
1139   n = s.st_size;
1140   data = malloc(n);
1141   if (!data)
1142     {
1143       perror("malloc");
1144       exit(1);
1145     }
1146   if (read(STDIN_FILENO, data, n) != n)
1147     {
1148       perror("read");
1149       exit(1);
1150     }
1151
1152   if (argc > 1)
1153     {
1154       if (!strcmp(argv[1], "title0"))
1155         {
1156           pos = 0x27;
1157           if (match_byte (0x03)
1158               || (match_byte (0x05) && match_byte (0x58)))
1159             printf ("%s\n", get_string());
1160           else
1161             printf ("<unknown>\n");
1162           return 0;
1163         }
1164       else if (!strcmp(argv[1], "title"))
1165         {
1166           dump_title();
1167           exit(0);
1168         }
1169       else if (!strcmp(argv[1], "titleraw"))
1170         {
1171           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
1172           start = 0x27;
1173           n = find(fonts, sizeof fonts - 1);
1174         }
1175       else if (!strcmp(argv[1], "fonts"))
1176         {
1177           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
1178           const char styles[] = "\xf0\0\0\0";
1179           start = find(fonts, sizeof fonts - 1);
1180           n = find(styles, sizeof styles - 1);
1181         }
1182       else if (!strcmp(argv[1], "styles"))
1183         {
1184           const char styles[] = "\xf0\0\0\0";
1185           const char dimensions[] = "-,,,.\0";
1186           start = find(styles, sizeof styles - 1);
1187           n = find(dimensions, sizeof dimensions - 1) + sizeof dimensions - 1;
1188         }
1189       else if (!strcmp(argv[1], "dimensions") || !strcmp(argv[1], "all"))
1190         {
1191           pos = 0;
1192           match_byte_assert(1);
1193           match_byte_assert(0);
1194           match_u32_assert(3);
1195           match_byte_assert(1);
1196           if (!match_byte(0))
1197             match_byte_assert(1);
1198           match_byte_assert(0);
1199           match_byte_assert(0);
1200           if (!match_byte(0))
1201             match_byte_assert(1);
1202           pos++;
1203           match_byte_assert(0);
1204           match_byte_assert(0);
1205           match_byte_assert(0);
1206           dump_title ();
1207           dump_fonts();
1208           dump_dims ();
1209           printf("\n\ndata:\n");
1210           dump_data ();
1211           if (pos == n - 1)
1212             match_byte_assert (1);
1213           if (pos != n)
1214             {
1215               fprintf (stderr, "%x / %x\n", pos, n);
1216               exit(1);
1217             }
1218           exit(0);
1219         }
1220       else
1221         {
1222           fprintf (stderr, "unknown section %s\n", argv[1]);
1223           exit(1);
1224         }
1225     }
1226   else
1227     start = 0x27;
1228
1229   for (size_t i = start; i < n; )
1230     {
1231       if (i + 5 <= n
1232           && data[i]
1233           //&& !data[i + 1]
1234           && !data[i + 2]
1235           && !data[i + 3]
1236           && i + 4 + data[i] + data[i + 1] * 256 <= n
1237           && all_ascii(&data[i + 4], data[i] + data[i + 1] * 256))
1238         {
1239           fputs("\n\"", stdout);
1240           fwrite(&data[i + 4], 1, data[i] + data[i + 1] * 256, stdout);
1241           fputs("\" ", stdout);
1242
1243           i += 4 + data[i] + data[i + 1] * 256;
1244         }
1245       else if (i + 12 <= n
1246                && data[i + 1] == 40
1247                && data[i + 2] == 5
1248                && data[i + 3] == 0)
1249         {
1250           double d;
1251
1252           memcpy (&d, &data[i + 4], 8);
1253           printf ("F40.%d(%.*f)\n", data[i], data[i], d);
1254           i += 12;
1255         }
1256       else if (i + 12 <= n
1257                && data[i + 1] == 40
1258                && data[i + 2] == 31
1259                && data[i + 3] == 0)
1260         {
1261           double d;
1262
1263           memcpy (&d, &data[i + 4], 8);
1264           printf ("PCT40.%d(%.*f)\n", data[i], data[i], d);
1265           i += 12;
1266         }
1267       else if (i + 4 <= n
1268                && (data[i] && data[i] != 88 && data[i] != 0x41)
1269                && !data[i + 1]
1270                && !data[i + 2]
1271                && !data[i + 3])
1272         {
1273           printf ("i%d ", data[i]);
1274           i += 4;
1275         }
1276       else
1277         {
1278           printf("%02x ", data[i]);
1279           i++;
1280         }
1281     }
1282
1283   return 0;
1284 }