Remove special case in dump_title_value().
[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           if (!match_u32 (2))
180             match_u32_assert(1);
181           match_byte_assert(0);
182           match_byte_assert(0);
183           int subn = get_u32 ();
184           printf ("nested %d bytes", subn);
185           pos += subn;
186         }
187       else
188         {
189           match_u32_assert(3);
190           printf("(special 3)");
191           match_byte_assert(0);
192           match_byte_assert(0);
193           match_byte_assert(1);
194           match_byte_assert(0);
195           int subn = get_u32 ();
196           printf ("nested %d bytes, ", subn);
197           pos += subn;
198           subn = get_u32 ();
199           printf ("nested %d bytes, ", subn);
200           pos += subn;
201         }
202     }
203   else
204     match_byte_assert (0x58);
205 }
206
207 static void
208 dump_value(int level)
209 {
210   for (int i = 0; i <= level; i++)
211     printf ("    ");
212
213   match_byte (0);
214   match_byte (0);
215   match_byte (0);
216   match_byte (0);
217   if (match_byte (3))
218     {
219       char *s1 = get_string();
220       dump_value_31();
221       char *s2 = get_string();
222       char *s3 = get_string();
223       if (strcmp(s1, s3))
224         printf("strings \"%s\", \"%s\" and \"%s\"", s1, s2, s3);
225       else
226         printf("string \"%s\" and \"%s\"", s1, s2);
227       if (!match_byte (0))
228         match_byte_assert(1);
229       match_byte (1);
230     }
231   else if (match_byte (5))
232     {
233       dump_value_31();
234       printf ("variable \"%s\"", get_string());
235       get_string();
236       if (!match_byte(1) && !match_byte(2))
237         match_byte_assert(3);
238     }
239   else if (match_byte (2))
240     {
241       unsigned int format;
242       char *var, *vallab;
243       double value;
244
245       match_byte_assert (0x58);
246       format = get_u32 ();
247       value = get_double ();
248       var = get_string ();
249       vallab = get_string ();
250       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
251               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
252       if (!match_byte (1) && !match_byte(2))
253         match_byte_assert (3);
254     }
255   else if (match_byte (4))
256     {
257       unsigned int format;
258       char *var, *vallab, *value;
259
260       match_byte_assert (0x58);
261       format = get_u32 ();
262       vallab = get_string ();
263       var = get_string ();
264       if (!match_byte(1) && !match_byte(2))
265         match_byte_assert (3);
266       value = get_string ();
267       printf ("value \"%s\" format %d(%d.%d) var \"%s\" vallab \"%s\"",
268               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
269     }
270   else if (match_byte (1))
271     {
272       unsigned int format;
273       double value;
274
275       dump_value_31();
276       format = get_u32 ();
277       value = get_double ();
278       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
279       match_byte (1);
280     }
281   else
282     {
283       dump_value_31();
284       char *base = get_string();
285
286       int x = get_u32();
287       printf ("\"%s\" with %d variables:\n", base, x);
288       if (match_u32(0))
289         {
290           for (int i = 0; i < x; i++)
291             {
292               dump_value (level+1);
293               putchar('\n');
294             }
295         }
296       else
297         {
298           for (int i = 0; i < x; i++)
299             {
300               int y = get_u32();
301               match_u32_assert(0);
302               for (int j = 0; j <= level; j++)
303                 printf ("    ");
304               printf("variable %d has %d values:\n", i, y);
305               for (int j = 0; j < y; j++)
306                 {
307                   match_byte(0);
308                   if (match_byte(3))
309                     {
310                       char *a = get_string();
311                       match_byte_assert(0x58);
312                       char *b = get_string();
313                       char *c = get_string();
314                       for (int k = 0; k <= level + 1; k++)
315                         printf ("    ");
316                       printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
317                     }
318                   else
319                     dump_value (level+1);
320
321                   match_byte(0);
322                   match_byte(0);
323                   match_byte(0);
324                   match_byte(0);
325                   putchar('\n');
326                 }
327             }
328         }
329     }
330   match_byte(0);
331   match_byte(0);
332   match_byte(0);
333 }
334
335 static void
336 dump_dim_value(int level)
337 {
338   for (int i = 0; i <= level; i++)
339     printf ("    ");
340
341   if (match_byte (3))
342     {
343       get_string();
344       dump_value_31();
345       get_string();
346       printf("string \"%s\"", get_string());
347       if (!match_byte (0))
348         match_byte_assert(1);
349     }
350   else if (match_byte (5))
351     {
352       match_byte_assert (0x58);
353       printf ("variable \"%s\"", get_string());
354       get_string();
355       if (!match_byte (1) && !match_byte (2))
356         match_byte_assert(3);
357     }
358   else
359     dump_value(level);
360 }
361
362 static void
363 dump_category(int level)
364 {
365   match_byte (0);
366   match_byte (0);
367   match_byte (0);
368   match_byte (0);
369   dump_value (level);
370
371   if (match_u32 (2))
372     get_u32 ();
373   else if (match_u32 (1))
374     {
375       match_byte (0);
376       match_byte (0);
377       match_byte (0);
378       get_u32 ();
379     }
380   else if (match_byte (1))
381     {
382       match_byte (0);
383       if (!match_u32 (2))
384         match_u32_assert (1);
385       match_byte (0);
386       get_u32();
387     }
388   else
389     {
390       match_u32_assert (0);
391       get_u32 ();
392     }
393
394   int n_categories = get_u32();
395   if (n_categories > 0)
396     printf (", %d subcategories:", n_categories);
397   printf("\n");
398   for (int i = 0; i < n_categories; i++)
399     dump_category (level + 1);
400 }
401
402 static void
403 dump_dim(void)
404 {
405   int n_categories;
406   printf("next dim\n");
407   match_byte(0);
408   dump_dim_value(0);
409
410   /* This byte is usually 0x02 but 0x00 and 0x75 (!) have also been spotted. */
411   pos++;
412
413   if (!match_byte(0) && !match_byte(1))
414     match_byte_assert(2);
415   if (!match_u32(0))
416     match_u32_assert(2);
417   if (!match_byte(0))
418     match_byte_assert(1);
419   match_byte(0);
420   match_byte(0);
421   match_byte(0);
422   match_byte(0);
423   get_u32();
424   match_byte(0);
425   match_byte(0);
426   match_byte(0);
427   match_byte(0);
428   n_categories = get_u32();
429   printf("%d nested categories\n", n_categories);
430   for (int i = 0; i < n_categories; i++)
431     dump_category (0);
432 }
433
434 int n_dims;
435 static void
436 dump_dims(void)
437 {
438   n_dims = get_u32();
439   printf ("%u dimensions\n", n_dims);
440   for (int i = 0; i < n_dims; i++)
441     {
442       printf("\n");
443       dump_dim ();
444     }
445 }
446
447 static void
448 dump_data_value(void)
449 {
450   match_byte(0);
451   match_byte(0);
452   match_byte(0);
453   match_byte(0);
454   if (match_byte (1))
455     {
456       unsigned int format;
457       double value;
458
459       dump_value_31();
460       format = get_u32 ();
461       value = get_double ();
462       printf ("    value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
463     }
464   else if (match_byte (3))
465     {
466       get_string();
467       dump_value_31();
468       get_string();
469       printf("string \"%s\"", get_string());
470       if (!match_byte (0))
471         match_byte_assert(1);
472     }
473   else if (match_byte (2))
474     {
475       unsigned int format;
476       char *var, *vallab;
477       double value;
478
479       match_byte_assert (0x58);
480       format = get_u32 ();
481       value = get_double ();
482       var = get_string ();
483       vallab = get_string ();
484       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
485               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
486       if (!match_byte (1) && !match_byte(2))
487         match_byte_assert (3);
488     }
489   else if (match_byte (4))
490     {
491       unsigned int format;
492       char *var, *vallab, *value;
493
494       match_byte_assert (0x58);
495       format = get_u32 ();
496       vallab = get_string ();
497       var = get_string ();
498       if (!match_byte(1) && !match_byte(2))
499         match_byte_assert (3);
500       value = get_string ();
501       printf ("value \"%s\" format %d(%d.%d) var \"%s\" vallab \"%s\"",
502               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
503     }
504   else if (data[pos] == 5)
505     dump_value (0);
506   else
507     {
508       dump_value_31();
509       char *base = get_string();
510       int x = get_u32();
511       printf ("\"%s\"; %d variables:\n", base, x);
512       for (int i = 0; i < x; i++)
513         {
514           int y = get_u32();
515           if (!y)
516             y = 1;
517           else
518             match_u32_assert(0);
519           for (int j = 0; j <= 0; j++)
520             printf ("    ");
521           printf("variable %d has %d values:\n", i, y);
522           for (int j = 0; j < y; j++)
523             dump_data_value();
524         }
525     }
526 }
527
528 static void
529 dump_data(void)
530 {
531 #if 1
532   int a[16];
533   for (int i = 0; i < 3 + n_dims; i++)
534     a[i] = get_u32();
535   printf ("data intro:");
536   for (int i = 0; i < 3 + n_dims; i++)
537     printf(" %d", a[i]);
538   printf("\n");
539 #else
540   fprintf (stderr,"data intro (%d dims):", n_dims);
541   for (int i = 0; i < 3+n_dims; i++)
542     fprintf (stderr," %d", get_u32());
543   fprintf(stderr,"\n");
544 #endif
545   int x = get_u32();
546   printf ("%d data values, starting at %08x\n", x, pos);
547   for (int i = 0; i < x; i++)
548     {
549       printf("%08x, index %d:\n", pos, get_u32());
550       match_u32_assert(0);
551       dump_data_value();
552       putchar('\n');
553     }
554 }
555
556 static void
557 dump_title_value(int level)
558 {
559   for (int i = 0; i <= level; i++)
560     printf ("    ");
561
562   match_byte (0);
563   match_byte (0);
564   match_byte (0);
565   match_byte (0);
566   if (data[pos] == 3 || data[pos] == 2 || data[pos] == 4 || data[pos] == 1)
567     dump_value(level);
568   else if (match_byte (5))
569     {
570       dump_value_31();
571       printf ("variable \"%s\"", get_string());
572       get_string();
573       if (!match_byte(1) && !match_byte(2))
574         match_byte_assert(3);
575     }
576   else
577     {
578       dump_value_31();
579
580       char *base = get_string();
581       int x = get_u32();
582       printf ("\"%s\" with %d variables:\n", base, x);
583       for (int i = 0; i < x; i++)
584         {
585           int y = get_u32();
586           if (!y)
587             y = 1;
588           else
589             match_u32_assert(0);
590           for (int j = 0; j <= level; j++)
591             printf ("    ");
592           printf("variable %d has %d values:\n", i, y);
593           for (int j = 0; j < y; j++)
594             {
595               dump_title_value (level+1);
596               putchar('\n');
597             }
598         }
599     }
600 }
601
602 static void
603 dump_footnote_value(int level)
604 {
605   for (int i = 0; i <= level; i++)
606     printf ("    ");
607
608   match_byte (0);
609   match_byte (0);
610   match_byte (0);
611   match_byte (0);
612   if (data[pos] == 2 || data[pos] == 4)
613     dump_value(level);
614   else if (match_byte (3))
615     {
616       get_string();
617       dump_value_31();
618       get_string();
619       printf("string \"%s\"", get_string());
620       if (!match_byte (0))
621         match_byte_assert (1);
622     }
623   else if (match_byte (5))
624     {
625       match_byte_assert (0x58);
626       printf ("variable \"%s\"", get_string());
627       get_string();
628       if (!match_byte(1) && !match_byte(2))
629         match_byte_assert(3);
630     }
631   else if (match_byte (1))
632     {
633       unsigned int format;
634       double value;
635
636       dump_value_31();
637       format = get_u32 ();
638       value = get_double ();
639       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
640     }
641   else
642     {
643       dump_value_31();
644       char *base = get_string();
645       int x = get_u32();
646       printf ("\"%s\"; %d variables:\n", base, x);
647       for (int i = 0; i < x; i++)
648         {
649           int y = get_u32();
650           if (!y)
651             y = 1;
652           else
653             match_u32_assert(0);
654           for (int j = 0; j <= level; j++)
655             printf ("    ");
656           printf("variable %d has %d values:\n", i, y);
657           for (int j = 0; j < y; j++)
658             {
659               dump_footnote_value (level+1);
660               putchar('\n');
661             }
662         }
663     }
664 }
665
666 static void
667 dump_title(void)
668 {
669   pos = 0x27;
670   dump_title_value(0); putchar('\n');
671   dump_title_value(0); putchar('\n');
672   match_byte_assert(0x31);
673   dump_title_value(0); putchar('\n');
674   match_byte(0);
675   match_byte_assert(0x58);
676   if (match_byte(0x31))
677     {
678       dump_footnote_value(0); putchar('\n');
679     }
680   else
681     match_byte_assert(0x58);
682
683
684   int n_footnotes = get_u32();
685   if (n_footnotes >= 20)
686     {
687       fprintf(stderr, "%08x: %d footnotes\n", pos - 4, n_footnotes);
688       exit(1);
689     }
690
691   printf("------\n%d footnotes\n", n_footnotes);
692   if (n_footnotes < 20)
693     {
694       for (int i = 0; i < n_footnotes; i++)
695         {
696           printf("footnote %d:\n", i);
697           dump_footnote_value(0);
698           match_byte(0);
699           match_byte(0);
700           match_byte(0);
701           match_byte(0);
702           if (match_byte (0x31))
703             {
704               /* Custom footnote marker string. */
705               match_byte_assert(3);
706               get_string();
707               match_byte_assert(0x58);
708               match_u32_assert(0);
709               get_string();
710             }
711           else
712             match_byte_assert (0x58);
713           printf("(%d)\n", get_u32());
714         }
715     }
716 }
717
718 static int
719 find_dimensions(void)
720 {
721   {
722     const char dimensions[] = "-,,,.\0";
723     int x = try_find_tail(dimensions, sizeof dimensions - 1);
724     if (x)
725       return x;
726   }
727
728   const char dimensions[] = "-,,, .\0";
729   return find_tail(dimensions, sizeof dimensions - 1);
730 }
731
732 static void
733 dump_fonts(void)
734 {
735   printf("fonts: offset=%08x\n", pos);
736   match_byte(0);
737   for (int i = 1; i <= 8; i++)
738     {
739       printf("%08x: font %d, ", pos, i);
740       match_byte_assert(i);
741       match_byte_assert(0x31);
742       printf("%s, ", get_string());
743       match_byte_assert(0);
744       match_byte_assert(0);
745       if (!match_byte(0x40) && !match_byte(0x20) && !match_byte(0x80) && !match_byte(0x10))
746         match_byte_assert(0x50);
747       if (!match_byte(0x41))
748         match_byte_assert(0x51);
749       pos += 13;
750       printf ("%s, ", get_string());
751       printf ("%s, ", get_string());
752       match_u32_assert(0);
753       match_u32_assert(0);
754       pos++;
755       get_u32();
756       get_u32();
757       get_u32();
758       get_u32();
759       putchar('\n');
760     }
761
762   match_u32_assert(240);
763   pos += 240;
764
765   match_u32_assert(18);
766   pos += 18;
767
768   if (match_u32(117))
769     pos += 117;
770   else
771     {
772       match_u32_assert(142);
773       pos += 142;
774     }
775
776   int count = get_u32();
777   pos += 4 * count;
778
779   char *encoding = get_string();
780   printf("encoding=%s\n", encoding);
781
782   if (!match_u32(0))
783     match_u32_assert(UINT32_MAX);
784   if (!match_byte(0))
785     match_byte_assert(1);
786   match_byte_assert(0);
787   if (!match_byte(0))
788     match_byte_assert(1);
789   if (!match_byte(0x99) && !match_byte(0x98))
790     match_byte_assert(0x97);
791   match_byte_assert(7);
792   match_byte_assert(0);
793   match_byte_assert(0);
794   if (match_byte('.'))
795     match_byte_assert(',');
796   else
797     {
798       match_byte_assert(',');
799       if (!match_byte('.'))
800         match_byte_assert(' ');
801     }
802   match_u32_assert(5);
803   for (int i = 0; i < 5; i++)
804     get_string();
805   pos += get_u32();
806   if (pos != find_dimensions())
807     fprintf (stderr, "%08x / %08x\n", pos, find_dimensions());
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           match_u32_assert(3);
882           match_byte_assert(1);
883           if (!match_byte(0))
884             match_byte_assert(1);
885           match_byte_assert(0);
886           match_byte_assert(0);
887           if (!match_byte(0))
888             match_byte_assert(1);
889           pos++;
890           match_byte_assert(0);
891           match_byte_assert(0);
892           match_byte_assert(0);
893           dump_title ();
894           dump_fonts();
895           dump_dims ();
896           printf("\n\ndata:\n");
897           dump_data ();
898           if (pos == n - 1)
899             match_byte_assert (1);
900           if (pos != n)
901             {
902               fprintf (stderr, "%x / %x\n", pos, n);
903               exit(1);
904             }
905           exit(0);
906         }
907       else
908         {
909           fprintf (stderr, "unknown section %s\n", argv[1]);
910           exit(1);
911         }
912     }
913   else
914     start = 0x27;
915
916   for (size_t i = start; i < n; )
917     {
918       if (i + 5 <= n
919           && data[i]
920           //&& !data[i + 1]
921           && !data[i + 2]
922           && !data[i + 3]
923           && i + 4 + data[i] + data[i + 1] * 256 <= n
924           && all_ascii(&data[i + 4], data[i] + data[i + 1] * 256))
925         {
926           fputs("\n\"", stdout);
927           fwrite(&data[i + 4], 1, data[i] + data[i + 1] * 256, stdout);
928           fputs("\" ", stdout);
929
930           i += 4 + data[i] + data[i + 1] * 256;
931         }
932       else if (i + 12 <= n
933                && data[i + 1] == 40
934                && data[i + 2] == 5
935                && data[i + 3] == 0)
936         {
937           double d;
938
939           memcpy (&d, &data[i + 4], 8);
940           printf ("F40.%d(%.*f)\n", data[i], data[i], d);
941           i += 12;
942         }
943       else if (i + 12 <= n
944                && data[i + 1] == 40
945                && data[i + 2] == 31
946                && data[i + 3] == 0)
947         {
948           double d;
949
950           memcpy (&d, &data[i + 4], 8);
951           printf ("PCT40.%d(%.*f)\n", data[i], data[i], d);
952           i += 12;
953         }
954       else if (i + 4 <= n
955                && (data[i] && data[i] != 88 && data[i] != 0x41)
956                && !data[i + 1]
957                && !data[i + 2]
958                && !data[i + 3])
959         {
960           printf ("i%d ", data[i]);
961           i += 4;
962         }
963       else
964         {
965           printf("%02x ", data[i]);
966           i++;
967         }
968     }
969
970   return 0;
971 }