Factor dump_footnote_value() in terms of dump_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       match_byte (0);
228       match_byte (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       match_byte (0);
348       match_byte (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       match_byte (0);
471     }
472   else if (match_byte (2))
473     {
474       unsigned int format;
475       char *var, *vallab;
476       double value;
477
478       match_byte_assert (0x58);
479       format = get_u32 ();
480       value = get_double ();
481       var = get_string ();
482       vallab = get_string ();
483       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
484               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
485       if (!match_byte (1) && !match_byte(2))
486         match_byte_assert (3);
487     }
488   else if (match_byte (4))
489     {
490       unsigned int format;
491       char *var, *vallab, *value;
492
493       match_byte_assert (0x58);
494       format = get_u32 ();
495       vallab = get_string ();
496       var = get_string ();
497       if (!match_byte(1) && !match_byte(2))
498         match_byte_assert (3);
499       value = get_string ();
500       printf ("value \"%s\" format %d(%d.%d) var \"%s\" vallab \"%s\"",
501               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
502     }
503   else if (data[pos] == 5)
504     dump_value (0);
505   else
506     {
507       dump_value_31();
508       char *base = get_string();
509       int x = get_u32();
510       printf ("\"%s\"; %d variables:\n", base, x);
511       for (int i = 0; i < x; i++)
512         {
513           int y = get_u32();
514           if (!y)
515             y = 1;
516           else
517             match_u32_assert(0);
518           for (int j = 0; j <= 0; j++)
519             printf ("    ");
520           printf("variable %d has %d values:\n", i, y);
521           for (int j = 0; j < y; j++)
522             dump_data_value();
523         }
524     }
525 }
526
527 static void
528 dump_data(void)
529 {
530 #if 1
531   int a[16];
532   for (int i = 0; i < 3 + n_dims; i++)
533     a[i] = get_u32();
534   printf ("data intro:");
535   for (int i = 0; i < 3 + n_dims; i++)
536     printf(" %d", a[i]);
537   printf("\n");
538 #else
539   fprintf (stderr,"data intro (%d dims):", n_dims);
540   for (int i = 0; i < 3+n_dims; i++)
541     fprintf (stderr," %d", get_u32());
542   fprintf(stderr,"\n");
543 #endif
544   int x = get_u32();
545   printf ("%d data values, starting at %08x\n", x, pos);
546   for (int i = 0; i < x; i++)
547     {
548       printf("%08x, index %d:\n", pos, get_u32());
549       match_u32_assert(0);
550       dump_data_value();
551       putchar('\n');
552     }
553 }
554
555 static void
556 dump_title_value(int level)
557 {
558   for (int i = 0; i <= level; i++)
559     printf ("    ");
560
561   match_byte (0);
562   match_byte (0);
563   match_byte (0);
564   match_byte (0);
565   if (data[pos] == 3 || data[pos] == 2 || data[pos] == 4 || data[pos] == 1)
566     dump_value(level);
567   else if (match_byte (5))
568     {
569       dump_value_31();
570       printf ("variable \"%s\"", get_string());
571       get_string();
572       if (!match_byte(1) && !match_byte(2))
573         match_byte_assert(3);
574     }
575   else
576     {
577       dump_value_31();
578
579       char *base = get_string();
580       int x = get_u32();
581       printf ("\"%s\" with %d variables:\n", base, x);
582       for (int i = 0; i < x; i++)
583         {
584           int y = get_u32();
585           if (!y)
586             y = 1;
587           else
588             match_u32_assert(0);
589           for (int j = 0; j <= level; j++)
590             printf ("    ");
591           printf("variable %d has %d values:\n", i, y);
592           for (int j = 0; j < y; j++)
593             {
594               match_byte(0);
595               if (match_byte(3))
596                 {
597                   char *a = get_string();
598                   match_byte_assert(0x58);
599                   char *b = get_string();
600                   char *c = get_string();
601                   for (int k = 0; k <= level + 1; k++)
602                     printf ("    ");
603                   printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
604                 }
605               else
606                 dump_title_value (level+1);
607               putchar('\n');
608             }
609         }
610     }
611 }
612
613 static void
614 dump_footnote_value(int level)
615 {
616   for (int i = 0; i <= level; i++)
617     printf ("    ");
618
619   match_byte (0);
620   match_byte (0);
621   match_byte (0);
622   match_byte (0);
623   if (data[pos] == 2 || data[pos] == 4)
624     dump_value(level);
625   else if (match_byte (3))
626     {
627       get_string();
628       dump_value_31();
629       get_string();
630       printf("string \"%s\"", get_string());
631       if (!match_byte (0))
632         match_byte_assert (1);
633     }
634   else if (match_byte (5))
635     {
636       match_byte_assert (0x58);
637       printf ("variable \"%s\"", get_string());
638       get_string();
639       if (!match_byte(1) && !match_byte(2))
640         match_byte_assert(3);
641     }
642   else if (match_byte (1))
643     {
644       unsigned int format;
645       double value;
646
647       dump_value_31();
648       format = get_u32 ();
649       value = get_double ();
650       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
651     }
652   else
653     {
654       dump_value_31();
655       char *base = get_string();
656       int x = get_u32();
657       printf ("\"%s\"; %d variables:\n", base, x);
658       for (int i = 0; i < x; i++)
659         {
660           int y = get_u32();
661           if (!y)
662             y = 1;
663           else
664             match_u32_assert(0);
665           for (int j = 0; j <= level; j++)
666             printf ("    ");
667           printf("variable %d has %d values:\n", i, y);
668           for (int j = 0; j < y; j++)
669             {
670               dump_footnote_value (level+1);
671               putchar('\n');
672             }
673         }
674     }
675 }
676
677 static void
678 dump_title(void)
679 {
680   pos = 0x27;
681   dump_title_value(0); putchar('\n');
682   dump_title_value(0); putchar('\n');
683   match_byte_assert(0x31);
684   dump_title_value(0); putchar('\n');
685   match_byte(0);
686   match_byte_assert(0x58);
687   if (match_byte(0x31))
688     {
689       dump_footnote_value(0); putchar('\n');
690     }
691   else
692     match_byte_assert(0x58);
693
694
695   int n_footnotes = get_u32();
696   if (n_footnotes >= 20)
697     {
698       fprintf(stderr, "%08x: %d footnotes\n", pos - 4, n_footnotes);
699       exit(1);
700     }
701
702   printf("------\n%d footnotes\n", n_footnotes);
703   if (n_footnotes < 20)
704     {
705       for (int i = 0; i < n_footnotes; i++)
706         {
707           printf("footnote %d:\n", i);
708           dump_footnote_value(0);
709           match_byte(0);
710           match_byte(0);
711           match_byte(0);
712           match_byte(0);
713           if (match_byte (0x31))
714             {
715               /* Custom footnote marker string. */
716               match_byte_assert(3);
717               get_string();
718               match_byte_assert(0x58);
719               match_u32_assert(0);
720               get_string();
721             }
722           else
723             match_byte_assert (0x58);
724           printf("(%d)\n", get_u32());
725         }
726     }
727 }
728
729 static int
730 find_dimensions(void)
731 {
732   {
733     const char dimensions[] = "-,,,.\0";
734     int x = try_find_tail(dimensions, sizeof dimensions - 1);
735     if (x)
736       return x;
737   }
738
739   const char dimensions[] = "-,,, .\0";
740   return find_tail(dimensions, sizeof dimensions - 1);
741 }
742
743 static void
744 dump_fonts(void)
745 {
746   printf("fonts: offset=%08x\n", pos);
747   match_byte(0);
748   for (int i = 1; i <= 8; i++)
749     {
750       printf("%08x: font %d, ", pos, i);
751       match_byte_assert(i);
752       match_byte_assert(0x31);
753       printf("%s, ", get_string());
754       match_byte_assert(0);
755       match_byte_assert(0);
756       if (!match_byte(0x40) && !match_byte(0x20) && !match_byte(0x80) && !match_byte(0x10))
757         match_byte_assert(0x50);
758       if (!match_byte(0x41))
759         match_byte_assert(0x51);
760       pos += 13;
761       printf ("%s, ", get_string());
762       printf ("%s, ", get_string());
763       match_u32_assert(0);
764       match_u32_assert(0);
765       pos++;
766       get_u32();
767       get_u32();
768       get_u32();
769       get_u32();
770       putchar('\n');
771     }
772
773   match_u32_assert(240);
774   pos += 240;
775
776   match_u32_assert(18);
777   pos += 18;
778
779   if (match_u32(117))
780     pos += 117;
781   else
782     {
783       match_u32_assert(142);
784       pos += 142;
785     }
786
787   int count = get_u32();
788   pos += 4 * count;
789
790   char *encoding = get_string();
791   printf("encoding=%s\n", encoding);
792
793   if (!match_u32(0))
794     match_u32_assert(UINT32_MAX);
795   if (!match_byte(0))
796     match_byte_assert(1);
797   match_byte_assert(0);
798   if (!match_byte(0))
799     match_byte_assert(1);
800   if (!match_byte(0x99) && !match_byte(0x98))
801     match_byte_assert(0x97);
802   match_byte_assert(7);
803   match_byte_assert(0);
804   match_byte_assert(0);
805   if (match_byte('.'))
806     match_byte_assert(',');
807   else
808     {
809       match_byte_assert(',');
810       if (!match_byte('.'))
811         match_byte_assert(' ');
812     }
813   match_u32_assert(5);
814   for (int i = 0; i < 5; i++)
815     get_string();
816   pos += get_u32();
817   if (pos != find_dimensions())
818     fprintf (stderr, "%08x / %08x\n", pos, find_dimensions());
819 }
820
821 int
822 main(int argc, char *argv[])
823 {
824   size_t start;
825   struct stat s;
826
827   if (isatty(STDIN_FILENO))
828     {
829       fprintf(stderr, "redirect stdin from a .bin file\n");
830       exit(1);
831     }
832   if (fstat(STDIN_FILENO, &s))
833     {
834       perror("fstat");
835       exit(1);
836     }
837   n = s.st_size;
838   data = malloc(n);
839   if (!data)
840     {
841       perror("malloc");
842       exit(1);
843     }
844   if (read(STDIN_FILENO, data, n) != n)
845     {
846       perror("read");
847       exit(1);
848     }
849
850   if (argc > 1)
851     {
852       if (!strcmp(argv[1], "title0"))
853         {
854           pos = 0x27;
855           if (match_byte (0x03)
856               || (match_byte (0x05) && match_byte (0x58)))
857             printf ("%s\n", get_string());
858           else
859             printf ("<unknown>\n");
860           return 0;
861         }
862       else if (!strcmp(argv[1], "title"))
863         {
864           dump_title();
865           exit(0);
866         }
867       else if (!strcmp(argv[1], "titleraw"))
868         {
869           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
870           start = 0x27;
871           n = find(fonts, sizeof fonts - 1);
872         }
873       else if (!strcmp(argv[1], "fonts"))
874         {
875           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
876           const char styles[] = "\xf0\0\0\0";
877           start = find(fonts, sizeof fonts - 1);
878           n = find(styles, sizeof styles - 1);
879         }
880       else if (!strcmp(argv[1], "styles"))
881         {
882           const char styles[] = "\xf0\0\0\0";
883           const char dimensions[] = "-,,,.\0";
884           start = find(styles, sizeof styles - 1);
885           n = find(dimensions, sizeof dimensions - 1) + sizeof dimensions - 1;
886         }
887       else if (!strcmp(argv[1], "dimensions") || !strcmp(argv[1], "all"))
888         {
889           pos = 0;
890           match_byte_assert(1);
891           match_byte_assert(0);
892           match_u32_assert(3);
893           match_byte_assert(1);
894           if (!match_byte(0))
895             match_byte_assert(1);
896           match_byte_assert(0);
897           match_byte_assert(0);
898           if (!match_byte(0))
899             match_byte_assert(1);
900           pos++;
901           match_byte_assert(0);
902           match_byte_assert(0);
903           match_byte_assert(0);
904           dump_title ();
905           dump_fonts();
906           dump_dims ();
907           printf("\n\ndata:\n");
908           dump_data ();
909           if (pos == n - 1)
910             match_byte_assert (1);
911           if (pos != n)
912             {
913               fprintf (stderr, "%x / %x\n", pos, n);
914               exit(1);
915             }
916           exit(0);
917         }
918       else
919         {
920           fprintf (stderr, "unknown section %s\n", argv[1]);
921           exit(1);
922         }
923     }
924   else
925     start = 0x27;
926
927   for (size_t i = start; i < n; )
928     {
929       if (i + 5 <= n
930           && data[i]
931           //&& !data[i + 1]
932           && !data[i + 2]
933           && !data[i + 3]
934           && i + 4 + data[i] + data[i + 1] * 256 <= n
935           && all_ascii(&data[i + 4], data[i] + data[i + 1] * 256))
936         {
937           fputs("\n\"", stdout);
938           fwrite(&data[i + 4], 1, data[i] + data[i + 1] * 256, stdout);
939           fputs("\" ", stdout);
940
941           i += 4 + data[i] + data[i + 1] * 256;
942         }
943       else if (i + 12 <= n
944                && data[i + 1] == 40
945                && data[i + 2] == 5
946                && data[i + 3] == 0)
947         {
948           double d;
949
950           memcpy (&d, &data[i + 4], 8);
951           printf ("F40.%d(%.*f)\n", data[i], data[i], d);
952           i += 12;
953         }
954       else if (i + 12 <= n
955                && data[i + 1] == 40
956                && data[i + 2] == 31
957                && data[i + 3] == 0)
958         {
959           double d;
960
961           memcpy (&d, &data[i + 4], 8);
962           printf ("PCT40.%d(%.*f)\n", data[i], data[i], d);
963           i += 12;
964         }
965       else if (i + 4 <= n
966                && (data[i] && data[i] != 88 && data[i] != 0x41)
967                && !data[i + 1]
968                && !data[i + 2]
969                && !data[i + 3])
970         {
971           printf ("i%d ", data[i]);
972           i += 4;
973         }
974       else
975         {
976           printf("%02x ", data[i]);
977           i++;
978         }
979     }
980
981   return 0;
982 }