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