14a84720429b9e1a8b54b61440924d6be0b26dbb
[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 (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(int level)
152 {
153   for (int i = 0; i <= level; i++)
154     printf ("    ");
155
156   match_byte (0);
157   match_byte (0);
158   match_byte (0);
159   match_byte (0);
160   if (match_byte (3))
161     {
162       get_string();
163       if (match_byte (0x31))
164         {
165           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       get_string();
205       printf("string \"%s\"", get_string());
206       match_byte (0);
207       match_byte (0);
208       match_byte (0);
209       match_byte (1);
210       match_byte (1);
211       match_byte (0);
212       match_byte (0);
213       match_byte (0);
214       match_byte (1);
215     }
216   else if (match_byte (5))
217     {
218       match_byte_assert (0x58);
219       printf ("variable \"%s\"", get_string());
220       get_string();
221       if (!match_byte(1) && !match_byte(2))
222         match_byte_assert(3);
223       match_byte (0);
224       match_byte (0);
225       match_byte (0);
226       match_byte (0);
227     }
228   else if (match_byte (2))
229     {
230       unsigned int format;
231       char *var, *vallab;
232       double value;
233
234       match_byte_assert (0x58);
235       format = get_u32 ();
236       value = get_double ();
237       var = get_string ();
238       vallab = get_string ();
239       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
240               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
241       if (!match_byte (1) && !match_byte(2))
242         match_byte_assert (3);
243       match_byte (0);
244       match_byte (0);
245       match_byte (0);
246       match_byte (0);
247       match_byte (0);
248       match_byte (0);
249       match_byte (0);
250     }
251   else if (match_byte (4))
252     {
253       unsigned int format;
254       char *var, *vallab, *value;
255
256       match_byte_assert (0x58);
257       format = get_u32 ();
258       vallab = get_string ();
259       var = get_string ();
260       if (!match_byte(1) && !match_byte(2))
261         match_byte_assert (3);
262       value = get_string ();
263       printf ("value \"%s\" format %d(%d.%d) var \"%s\" vallab \"%s\"",
264               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
265       match_byte (0);
266       match_byte (0);
267       match_byte (0);
268       match_byte (0);
269     }
270   else if (match_byte (1))
271     {
272       unsigned int format;
273       double value;
274
275       if (match_byte (0x31))
276         {
277           if (match_u32 (1))
278             {
279               printf("(footnote %d) ", get_u32());
280               match_byte_assert (0);
281               match_byte_assert (0);
282               int subn = get_u32 ();
283               printf ("nested %d bytes", subn);
284               pos += subn;
285             }
286         }
287       else
288         match_byte_assert (0x58);
289       format = get_u32 ();
290       value = get_double ();
291       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
292       match_byte (1);
293       match_byte (0);
294       match_byte (0);
295       match_byte (0);
296       match_byte (1);
297     }
298   else if (match_byte (0x31))
299     {
300       if (match_u32 (1))
301         {
302           printf("(footnote %d) ", get_u32());
303           match_byte_assert (0);
304           match_byte_assert (0);
305           int subn = get_u32 ();
306           printf ("nested %d bytes", subn);
307           pos += subn;
308         }
309       else
310         {
311           match_u32_assert (0);
312           match_u32_assert (0);
313           int subn = get_u32 ();
314           printf ("nested %d bytes", subn);
315           pos += subn;
316         }
317       char *base = get_string();
318       int x = get_u32();
319       printf ("\"%s\"; %d variables:\n", base, x);
320       if (match_u32(0))
321         {
322           for (int i = 0; i < x; i++)
323             {
324               dump_value (level+1);
325               putchar('\n');
326             }
327         }
328       else
329         {
330           for (int i = 0; i < x; i++)
331             {
332               int y = get_u32();
333               match_u32_assert(0);
334               for (int j = 0; j <= level; j++)
335                 printf ("    ");
336               printf("variable %d has %d values:\n", i, y);
337               for (int j = 0; j < y; j++)
338                 {
339                   if (match_byte(3))
340                     {
341                       char *a = get_string();
342                       match_byte_assert(0x58);
343                       char *b = get_string();
344                       char *c = get_string();
345                       for (int k = 0; k <= level + 1; k++)
346                         printf ("    ");
347                       printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
348                       match_byte(0);
349                       match_byte(0);
350                       match_byte(0);
351                       match_byte(0);
352                       match_byte(0);
353                     }
354                   else
355                     dump_value (level+1);
356                   putchar('\n');
357                 }
358             }
359         }
360     }
361   else
362     {
363
364       match_byte_assert (0x58);
365       char *base = get_string();
366       int x = get_u32();
367       printf ("\"%s\" with %d variables:\n", base, x);
368       if (match_u32(0))
369         {
370           for (int i = 0; i < x; i++)
371             {
372               dump_value (level+1);
373               putchar('\n');
374             }
375         }
376       else
377         {
378           for (int i = 0; i < x; i++)
379             {
380               int y = get_u32();
381               match_u32_assert(0);
382               for (int j = 0; j <= level; j++)
383                 printf ("    ");
384               printf("variable %d has %d values:\n", i, y);
385               for (int j = 0; j < y; j++)
386                 {
387                   if (match_byte(3))
388                     {
389                       char *a = get_string();
390                       match_byte_assert(0x58);
391                       char *b = get_string();
392                       char *c = get_string();
393                       for (int k = 0; k <= level + 1; k++)
394                         printf ("    ");
395                       printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
396                       match_byte(0);
397                       match_byte(0);
398                       match_byte(0);
399                       match_byte(0);
400                       match_byte(0);
401                     }
402                   else
403                     dump_value (level+1);
404                   putchar('\n');
405                 }
406             }
407         }
408     }
409 }
410
411 static void
412 dump_dim_value(int level)
413 {
414   for (int i = 0; i <= level; i++)
415     printf ("    ");
416
417   if (match_byte (3))
418     {
419       get_string();
420       if (match_byte (0x31))
421         {
422           match_u32 (1);
423           printf("(footnote %d) ", get_u32());
424           match_byte_assert (0);
425           match_byte_assert (0);
426           int subn = get_u32 ();
427           printf ("nested %d bytes", subn);
428           pos += subn;
429         }
430       else
431         match_byte_assert (0x58);
432       get_string();
433       printf("string \"%s\"", get_string());
434       match_byte (0);
435       match_byte_assert (1);
436       match_byte (0);
437       match_byte (0);
438       match_byte (0);
439       match_byte (1);
440     }
441   else if (match_byte (5))
442     {
443       match_byte_assert (0x58);
444       printf ("variable \"%s\"", get_string());
445       get_string();
446       match_byte_assert (2);
447     }
448   else if (match_byte (2))
449     {
450       unsigned int format;
451       char *var, *vallab;
452       double value;
453
454       match_byte_assert (0x58);
455       format = get_u32 ();
456       value = get_double ();
457       var = get_string ();
458       vallab = get_string ();
459       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
460               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
461       if (!match_u32 (3))
462         match_u32_assert (2);
463       match_byte (0);
464     }
465   else if (match_byte (1))
466     {
467       unsigned int format;
468       double value;
469
470       match_byte_assert (0x58);
471       format = get_u32 ();
472       value = get_double ();
473       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
474       match_byte (1);
475       match_byte (0);
476       match_byte (0);
477       match_byte (0);
478       match_byte (1);
479     }
480   else
481     {
482       int subn;
483
484       match_byte (0);
485       match_byte_assert (0x31);
486       match_u32_assert (0);
487       match_u32_assert (0);
488       subn = get_u32 ();
489       printf ("nested %d bytes", subn);
490       pos += subn;
491       printf ("; \"%s\", substitutions:", get_string());
492       int total_subs = get_u32();
493       int x = get_u32();
494       if (x)
495         {
496           total_subs = (total_subs - 1) + x;
497           match_u32_assert (0);
498         }
499       printf (" (total %d)", total_subs);
500
501       for (int i = 0; i < total_subs; i++)
502         {
503           putc ('\n', stdout);
504           dump_value (level + 1);
505         }
506     }
507 }
508
509 static void
510 dump_category(int level)
511 {
512   match_byte (0);
513   match_byte (0);
514   match_byte (0);
515   match_byte (0);
516   dump_value (level);
517
518   if (match_u32 (2))
519     get_u32 ();
520   else if (match_u32 (1))
521     {
522       match_byte (0);
523       match_byte (0);
524       match_byte (0);
525       get_u32 ();
526     }
527   else if (match_byte (1))
528     {
529       match_byte (0);
530       if (!match_u32 (2))
531         match_u32_assert (1);
532       match_byte (0);
533       get_u32();
534     }
535   else
536     {
537       match_u32_assert (0);
538       get_u32 ();
539     }
540
541   int n_categories = get_u32();
542   if (n_categories > 0)
543     printf (", %d subcategories:", n_categories);
544   printf("\n");
545   for (int i = 0; i < n_categories; i++)
546     dump_category (level + 1);
547 }
548
549 static void
550 dump_dim(void)
551 {
552   int n_categories;
553   printf("next dim\n");
554   match_byte(0);
555   if (match_byte(3))
556     {
557       get_string();
558       match_byte_assert(0x58);
559       get_string();
560       printf("string \"%s\": ", get_string());
561       match_byte(1) || match_byte(0);
562     }
563   else if (match_byte(5)) 
564     {
565       match_byte_assert(0x58);
566       printf("variable \"%s\": ", get_string());
567       get_string();
568       if (!match_byte(2))
569         match_byte_assert(3);
570     }
571   else if (match_byte(0x31))
572     {
573       int subn;
574       int total_subs = 1;
575
576       match_u32_assert (0);
577       match_u32_assert (0);
578       subn = get_u32 ();
579       printf ("nested %d bytes", subn);
580       pos += subn;
581       printf ("; \"%s\", substitutions:", get_string());
582       for (;;)
583         {
584           int n_subst = get_u32();
585           if (!n_subst)
586             break;
587           printf (" %d", n_subst);
588           total_subs *= n_subst;
589         }
590
591       for (int i = 0; i < total_subs; i++)
592         {
593           putc ('\n', stdout);
594           dump_dim_value (0);
595         }
596     }
597   else
598     {
599       int total_subs = 1;
600
601       match_byte_assert (0x58);
602       printf ("\"%s\" with substitutions:", get_string());
603       for (;;)
604         {
605           int n_subst = get_u32();
606           if (!n_subst)
607             break;
608           printf (" %d", n_subst);
609           total_subs *= n_subst;
610         }
611
612       for (int i = 0; i < total_subs; i++)
613         {
614           putc ('\n', stdout);
615           dump_dim_value (0);
616         }
617     }
618
619   /* This byte is usually 0x02 but 0x00 and 0x75 (!) have also been spotted. */
620   pos++;
621
622   if (!match_byte(0) && !match_byte(1))
623     match_byte_assert(2);
624   if (!match_u32(0))
625     match_u32_assert(2);
626   if (!match_byte(0))
627     match_byte_assert(1);
628   match_byte(0);
629   match_byte(0);
630   match_byte(0);
631   match_byte(0);
632   get_u32();
633   match_byte(0);
634   match_byte(0);
635   match_byte(0);
636   match_byte(0);
637   n_categories = get_u32();
638   printf("%d nested categories\n", n_categories);
639   for (int i = 0; i < n_categories; i++)
640     dump_category (0);
641 }
642
643 int n_dims;
644 static void
645 dump_dims(void)
646 {
647   n_dims = get_u32();
648   printf ("%u dimensions\n", n_dims);
649   for (int i = 0; i < n_dims; i++)
650     {
651       printf("\n");
652       dump_dim ();
653     }
654 }
655
656 static void
657 dump_data(void)
658 {
659 #if 1
660   int a[16];
661   for (int i = 0; i < 3 + n_dims; i++)
662     a[i] = get_u32();
663   printf ("data intro:");
664   for (int i = 0; i < 3 + n_dims; i++)
665     printf(" %d", a[i]);
666   printf("\n");
667 #else
668   fprintf (stderr,"data intro (%d dims):", n_dims);
669   for (int i = 0; i < 3+n_dims; i++)
670     fprintf (stderr," %d", get_u32());
671   fprintf(stderr,"\n");
672 #endif
673   int x = get_u32();
674   printf ("%d data values, starting at %08x\n", x, pos);
675   for (int i = 0; i < x; i++)
676     {
677       printf("%08x, index %d:\n", pos, get_u32());
678       match_u32_assert(0);
679       match_byte(0);
680       match_byte(0);
681       match_byte(0);
682       match_byte(0);
683       if (match_byte (1))
684         {
685           unsigned int format;
686           double value;
687
688           if (match_byte (0x31))
689             {
690               if (match_u32 (0))
691                 {
692                   match_u32_assert (0);
693                   int subn = get_u32 ();
694                   printf ("nested %d bytes", subn);
695                   pos += subn;
696                 }
697               else if (match_u32 (1))
698                 {
699                   printf("(footnote %d) ", get_u32());
700                   match_byte_assert (0);
701                   match_byte_assert (0);
702                   int subn = get_u32 ();
703                   printf ("nested %d bytes", subn);
704                   pos += subn;
705                 }
706               else
707                 {
708                   match_u32_assert(2);
709                   printf("(special 2)");
710                   match_byte_assert(0);
711                   match_byte_assert(0);
712                   match_u32_assert(1);
713                   match_byte_assert(0);
714                   match_byte_assert(0);
715                   int subn = get_u32 ();
716                   printf ("nested %d bytes", subn);
717                   pos += subn;
718                 }
719             }
720           else
721             match_byte_assert (0x58);
722           format = get_u32 ();
723           value = get_double ();
724           printf ("    value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
725         }
726       else if (match_byte (3))
727         {
728           get_string();
729           if (match_byte (0x31))
730             {
731               if (match_u32 (0))
732                 {
733                   match_u32_assert (1);
734                   int subn = get_u32 ();
735                   printf ("nested %d bytes", subn);
736                   pos += subn;
737                 }
738               else if (match_u32 (1))
739                 {
740                   printf("(footnote %d) ", get_u32());
741                   match_byte_assert (0);
742                   match_byte_assert (0);
743                   int subn = get_u32 ();
744                   printf ("nested %d bytes", subn);
745                   pos += subn;
746                 }
747               else if (match_u32 (2))
748                 {
749                   printf("(special 2)");
750                   match_byte_assert(0);
751                   match_byte_assert(0);
752                   match_u32_assert(1);
753                   match_byte_assert(0);
754                   match_byte_assert(0);
755                   int subn = get_u32 ();
756                   printf ("nested %d bytes", subn);
757                   pos += subn;
758                 }
759               else
760                 {
761                   match_u32_assert(3);
762                   printf("(special 3)");
763                   match_byte_assert(0);
764                   match_byte_assert(0);
765                   match_byte_assert(1);
766                   match_byte_assert(0);
767                   int subn = get_u32 ();
768                   printf ("nested %d bytes, ", subn);
769                   pos += subn;
770                   subn = get_u32 ();
771                   printf ("nested %d bytes, ", subn);
772                   pos += subn;
773                 }
774             }
775           else
776             match_byte_assert (0x58);
777           get_string();
778           printf("string \"%s\"", get_string());
779           match_byte (0);
780         }
781       else if (match_byte (2))
782         {
783           unsigned int format;
784           char *var, *vallab;
785           double value;
786
787           match_byte_assert (0x58);
788           format = get_u32 ();
789           value = get_double ();
790           var = get_string ();
791           vallab = get_string ();
792           printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
793                   value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
794           if (!match_byte (1) && !match_byte(2))
795             match_byte_assert (3);
796         }
797       else if (match_byte (4))
798         {
799           unsigned int format;
800           char *var, *vallab, *value;
801
802           match_byte_assert (0x58);
803           format = get_u32 ();
804           vallab = get_string ();
805           var = get_string ();
806           if (!match_byte(1) && !match_byte(2))
807             match_byte_assert (3);
808           value = get_string ();
809           printf ("value \"%s\" format %d(%d.%d) var \"%s\" vallab \"%s\"",
810                   value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
811         }
812       else if (match_byte (5))
813         {
814           match_byte_assert (0x58);
815           printf ("variable \"%s\"", get_string());
816           get_string();
817           if (!match_byte(1) && !match_byte(2))
818             match_byte_assert(3);
819           match_byte (0);
820           match_byte (0);
821           match_byte (0);
822           match_byte (0);
823         }
824       else if (match_byte(0x31))
825         {
826           if (match_u32 (1))
827             {
828               printf("(footnote %d) ", get_u32());
829               match_byte_assert (0);
830               match_byte_assert (0);
831               int subn = get_u32 ();
832               printf ("nested %d bytes", subn);
833               pos += subn;
834             }
835           else
836             {
837               match_u32_assert (0);
838               match_u32_assert (0);
839               int subn = get_u32 ();
840               printf ("nested %d bytes", subn);
841               pos += subn;
842             }
843           char *base = get_string();
844           int x = get_u32();
845           printf ("\"%s\"; %d variables:\n", base, x);
846           if (match_u32(0))
847             {
848               for (int i = 0; i < x; i++)
849                 {
850                   dump_value (0);
851                   putchar('\n');
852                 }
853             }
854           else
855             {
856               for (int i = 0; i < x; i++)
857                 {
858                   int y = get_u32();
859                   match_u32_assert(0);
860                   for (int j = 0; j <= 0; j++)
861                     printf ("    ");
862                   printf("variable %d has %d values:\n", i, y);
863                   for (int j = 0; j < y; j++)
864                     {
865                       if (match_byte(3))
866                         {
867                           char *a = get_string();
868                           match_byte_assert(0x58);
869                           char *b = get_string();
870                           char *c = get_string();
871                           for (int k = 0; k <= 1; k++)
872                             printf ("    ");
873                           printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
874                           match_byte(0);
875                           match_byte(0);
876                           match_byte(0);
877                           match_byte(0);
878                           match_byte(0);
879                         }
880                       else
881                         dump_value (0);
882                       putchar('\n');
883                     }
884                 }
885             }
886         }
887       else
888         {
889           match_byte_assert (0x58);
890           char *base = get_string();
891           int x = get_u32();
892           printf ("\"%s\" with %d variables:\n", base, x);
893           if (match_u32(0))
894             {
895               for (int i = 0; i < x; i++)
896                 {
897                   dump_value (1);
898                   putchar('\n');
899                 }
900             }
901           else
902             {
903               for (int i = 0; i < x; i++)
904                 {
905                   int y = get_u32();
906                   match_u32_assert(0);
907                   for (int j = 0; j <= 0; j++)
908                     printf ("    ");
909                   printf("variable %d has %d values:\n", i, y);
910                   for (int j = 0; j < y; j++)
911                     {
912                       if (match_byte(3))
913                         {
914                           char *a = get_string();
915                           match_byte_assert(0x58);
916                           char *b = get_string();
917                           char *c = get_string();
918                           for (int k = 0; k <= 1; k++)
919                             printf ("    ");
920                           printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
921                           match_byte(0);
922                           match_byte(0);
923                           match_byte(0);
924                           match_byte(0);
925                           match_byte(0);
926                         }
927                       else
928                         dump_value (1);
929                       putchar('\n');
930                     }
931                 }
932             }
933         }
934       putchar('\n');
935     }
936 }
937
938 int
939 main(int argc, char *argv[])
940 {
941   size_t start;
942   struct stat s;
943
944   if (isatty(STDIN_FILENO))
945     {
946       fprintf(stderr, "redirect stdin from a .bin file\n");
947       exit(1);
948     }
949   if (fstat(STDIN_FILENO, &s))
950     {
951       perror("fstat");
952       exit(1);
953     }
954   n = s.st_size;
955   data = malloc(n);
956   if (!data)
957     {
958       perror("malloc");
959       exit(1);
960     }
961   if (read(STDIN_FILENO, data, n) != n)
962     {
963       perror("read");
964       exit(1);
965     }
966
967   if (argc > 1)
968     {
969       if (!strcmp(argv[1], "title0"))
970         {
971           pos = 0x27;
972           if (match_byte (0x03)
973               || (match_byte (0x05) && match_byte (0x58)))
974             printf ("%s\n", get_string());
975           else
976             printf ("<unknown>\n");
977           return 0;
978         }
979       if (!strcmp(argv[1], "title"))
980         {
981           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
982           start = 0x27;
983           n = find(fonts, sizeof fonts - 1);
984         }
985       else if (!strcmp(argv[1], "fonts"))
986         {
987           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
988           const char styles[] = "\xf0\0\0\0";
989           start = find(fonts, sizeof fonts - 1);
990           n = find(styles, sizeof styles - 1);
991         }
992       else if (!strcmp(argv[1], "styles"))
993         {
994           const char styles[] = "\xf0\0\0\0";
995           const char dimensions[] = "-,,,.\0";
996           start = find(styles, sizeof styles - 1);
997           n = find(dimensions, sizeof dimensions - 1) + sizeof dimensions - 1;
998         }
999       else if (!strcmp(argv[1], "dimensions"))
1000         {
1001           {
1002             const char dimensions[] = "-,,,.\0";
1003             start = try_find_tail(dimensions, sizeof dimensions - 1);
1004           }
1005
1006           if (!start)
1007             {
1008               const char dimensions[] = "-,,, .\0";
1009               start = find_tail(dimensions, sizeof dimensions - 1);
1010             }
1011
1012           pos = start;
1013           dump_dims ();
1014           dump_data ();
1015           start = pos;
1016         }
1017       else
1018         {
1019           fprintf (stderr, "unknown section %s\n", argv[1]);
1020           exit(1);
1021         }
1022     }
1023   else
1024     start = 0x27;
1025
1026   for (size_t i = start; i < n; )
1027     {
1028       if (i + 5 <= n
1029           && data[i]
1030           //&& !data[i + 1]
1031           && !data[i + 2]
1032           && !data[i + 3]
1033           && i + 4 + data[i] + data[i + 1] * 256 <= n
1034           && all_ascii(&data[i + 4], data[i] + data[i + 1] * 256))
1035         {
1036           fputs("\n\"", stdout);
1037           fwrite(&data[i + 4], 1, data[i] + data[i + 1] * 256, stdout);
1038           fputs("\" ", stdout);
1039
1040           i += 4 + data[i] + data[i + 1] * 256;
1041         }
1042       else if (i + 12 <= n
1043                && data[i + 1] == 40
1044                && data[i + 2] == 5
1045                && data[i + 3] == 0)
1046         {
1047           double d;
1048
1049           memcpy (&d, &data[i + 4], 8);
1050           printf ("F40.%d(%.*f)\n", data[i], data[i], d);
1051           i += 12;
1052         }
1053       else if (i + 12 <= n
1054                && data[i + 1] == 40
1055                && data[i + 2] == 31
1056                && data[i + 3] == 0)
1057         {
1058           double d;
1059
1060           memcpy (&d, &data[i + 4], 8);
1061           printf ("PCT40.%d(%.*f)\n", data[i], data[i], d);
1062           i += 12;
1063         }
1064       else if (i + 4 <= n
1065                && (data[i] && data[i] != 88 && data[i] != 0x41)
1066                && !data[i + 1]
1067                && !data[i + 2]
1068                && !data[i + 3])
1069         {
1070           printf ("i%d ", data[i]);
1071           i += 4;
1072         }
1073       else
1074         {
1075           printf("%02x ", data[i]);
1076           i++;
1077         }
1078     }
1079
1080   return 0;
1081 }