Make type-03 cases more uniform.
[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               match_byte(0);
596               if (match_byte(3))
597                 {
598                   char *a = get_string();
599                   match_byte_assert(0x58);
600                   char *b = get_string();
601                   char *c = get_string();
602                   for (int k = 0; k <= level + 1; k++)
603                     printf ("    ");
604                   printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
605                   if (!match_byte(0))
606                     match_byte_assert(1);
607                 }
608               else
609                 dump_title_value (level+1);
610               putchar('\n');
611             }
612         }
613     }
614 }
615
616 static void
617 dump_footnote_value(int level)
618 {
619   for (int i = 0; i <= level; i++)
620     printf ("    ");
621
622   match_byte (0);
623   match_byte (0);
624   match_byte (0);
625   match_byte (0);
626   if (data[pos] == 2 || data[pos] == 4)
627     dump_value(level);
628   else if (match_byte (3))
629     {
630       get_string();
631       dump_value_31();
632       get_string();
633       printf("string \"%s\"", get_string());
634       if (!match_byte (0))
635         match_byte_assert (1);
636     }
637   else if (match_byte (5))
638     {
639       match_byte_assert (0x58);
640       printf ("variable \"%s\"", get_string());
641       get_string();
642       if (!match_byte(1) && !match_byte(2))
643         match_byte_assert(3);
644     }
645   else if (match_byte (1))
646     {
647       unsigned int format;
648       double value;
649
650       dump_value_31();
651       format = get_u32 ();
652       value = get_double ();
653       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
654     }
655   else
656     {
657       dump_value_31();
658       char *base = get_string();
659       int x = get_u32();
660       printf ("\"%s\"; %d variables:\n", base, x);
661       for (int i = 0; i < x; i++)
662         {
663           int y = get_u32();
664           if (!y)
665             y = 1;
666           else
667             match_u32_assert(0);
668           for (int j = 0; j <= level; j++)
669             printf ("    ");
670           printf("variable %d has %d values:\n", i, y);
671           for (int j = 0; j < y; j++)
672             {
673               dump_footnote_value (level+1);
674               putchar('\n');
675             }
676         }
677     }
678 }
679
680 static void
681 dump_title(void)
682 {
683   pos = 0x27;
684   dump_title_value(0); putchar('\n');
685   dump_title_value(0); putchar('\n');
686   match_byte_assert(0x31);
687   dump_title_value(0); putchar('\n');
688   match_byte(0);
689   match_byte_assert(0x58);
690   if (match_byte(0x31))
691     {
692       dump_footnote_value(0); putchar('\n');
693     }
694   else
695     match_byte_assert(0x58);
696
697
698   int n_footnotes = get_u32();
699   if (n_footnotes >= 20)
700     {
701       fprintf(stderr, "%08x: %d footnotes\n", pos - 4, n_footnotes);
702       exit(1);
703     }
704
705   printf("------\n%d footnotes\n", n_footnotes);
706   if (n_footnotes < 20)
707     {
708       for (int i = 0; i < n_footnotes; i++)
709         {
710           printf("footnote %d:\n", i);
711           dump_footnote_value(0);
712           match_byte(0);
713           match_byte(0);
714           match_byte(0);
715           match_byte(0);
716           if (match_byte (0x31))
717             {
718               /* Custom footnote marker string. */
719               match_byte_assert(3);
720               get_string();
721               match_byte_assert(0x58);
722               match_u32_assert(0);
723               get_string();
724             }
725           else
726             match_byte_assert (0x58);
727           printf("(%d)\n", get_u32());
728         }
729     }
730 }
731
732 static int
733 find_dimensions(void)
734 {
735   {
736     const char dimensions[] = "-,,,.\0";
737     int x = try_find_tail(dimensions, sizeof dimensions - 1);
738     if (x)
739       return x;
740   }
741
742   const char dimensions[] = "-,,, .\0";
743   return find_tail(dimensions, sizeof dimensions - 1);
744 }
745
746 static void
747 dump_fonts(void)
748 {
749   printf("fonts: offset=%08x\n", pos);
750   match_byte(0);
751   for (int i = 1; i <= 8; i++)
752     {
753       printf("%08x: font %d, ", pos, i);
754       match_byte_assert(i);
755       match_byte_assert(0x31);
756       printf("%s, ", get_string());
757       match_byte_assert(0);
758       match_byte_assert(0);
759       if (!match_byte(0x40) && !match_byte(0x20) && !match_byte(0x80) && !match_byte(0x10))
760         match_byte_assert(0x50);
761       if (!match_byte(0x41))
762         match_byte_assert(0x51);
763       pos += 13;
764       printf ("%s, ", get_string());
765       printf ("%s, ", get_string());
766       match_u32_assert(0);
767       match_u32_assert(0);
768       pos++;
769       get_u32();
770       get_u32();
771       get_u32();
772       get_u32();
773       putchar('\n');
774     }
775
776   match_u32_assert(240);
777   pos += 240;
778
779   match_u32_assert(18);
780   pos += 18;
781
782   if (match_u32(117))
783     pos += 117;
784   else
785     {
786       match_u32_assert(142);
787       pos += 142;
788     }
789
790   int count = get_u32();
791   pos += 4 * count;
792
793   char *encoding = get_string();
794   printf("encoding=%s\n", encoding);
795
796   if (!match_u32(0))
797     match_u32_assert(UINT32_MAX);
798   if (!match_byte(0))
799     match_byte_assert(1);
800   match_byte_assert(0);
801   if (!match_byte(0))
802     match_byte_assert(1);
803   if (!match_byte(0x99) && !match_byte(0x98))
804     match_byte_assert(0x97);
805   match_byte_assert(7);
806   match_byte_assert(0);
807   match_byte_assert(0);
808   if (match_byte('.'))
809     match_byte_assert(',');
810   else
811     {
812       match_byte_assert(',');
813       if (!match_byte('.'))
814         match_byte_assert(' ');
815     }
816   match_u32_assert(5);
817   for (int i = 0; i < 5; i++)
818     get_string();
819   pos += get_u32();
820   if (pos != find_dimensions())
821     fprintf (stderr, "%08x / %08x\n", pos, find_dimensions());
822 }
823
824 int
825 main(int argc, char *argv[])
826 {
827   size_t start;
828   struct stat s;
829
830   if (isatty(STDIN_FILENO))
831     {
832       fprintf(stderr, "redirect stdin from a .bin file\n");
833       exit(1);
834     }
835   if (fstat(STDIN_FILENO, &s))
836     {
837       perror("fstat");
838       exit(1);
839     }
840   n = s.st_size;
841   data = malloc(n);
842   if (!data)
843     {
844       perror("malloc");
845       exit(1);
846     }
847   if (read(STDIN_FILENO, data, n) != n)
848     {
849       perror("read");
850       exit(1);
851     }
852
853   if (argc > 1)
854     {
855       if (!strcmp(argv[1], "title0"))
856         {
857           pos = 0x27;
858           if (match_byte (0x03)
859               || (match_byte (0x05) && match_byte (0x58)))
860             printf ("%s\n", get_string());
861           else
862             printf ("<unknown>\n");
863           return 0;
864         }
865       else if (!strcmp(argv[1], "title"))
866         {
867           dump_title();
868           exit(0);
869         }
870       else if (!strcmp(argv[1], "titleraw"))
871         {
872           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
873           start = 0x27;
874           n = find(fonts, sizeof fonts - 1);
875         }
876       else if (!strcmp(argv[1], "fonts"))
877         {
878           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
879           const char styles[] = "\xf0\0\0\0";
880           start = find(fonts, sizeof fonts - 1);
881           n = find(styles, sizeof styles - 1);
882         }
883       else if (!strcmp(argv[1], "styles"))
884         {
885           const char styles[] = "\xf0\0\0\0";
886           const char dimensions[] = "-,,,.\0";
887           start = find(styles, sizeof styles - 1);
888           n = find(dimensions, sizeof dimensions - 1) + sizeof dimensions - 1;
889         }
890       else if (!strcmp(argv[1], "dimensions") || !strcmp(argv[1], "all"))
891         {
892           pos = 0;
893           match_byte_assert(1);
894           match_byte_assert(0);
895           match_u32_assert(3);
896           match_byte_assert(1);
897           if (!match_byte(0))
898             match_byte_assert(1);
899           match_byte_assert(0);
900           match_byte_assert(0);
901           if (!match_byte(0))
902             match_byte_assert(1);
903           pos++;
904           match_byte_assert(0);
905           match_byte_assert(0);
906           match_byte_assert(0);
907           dump_title ();
908           dump_fonts();
909           dump_dims ();
910           printf("\n\ndata:\n");
911           dump_data ();
912           if (pos == n - 1)
913             match_byte_assert (1);
914           if (pos != n)
915             {
916               fprintf (stderr, "%x / %x\n", pos, n);
917               exit(1);
918             }
919           exit(0);
920         }
921       else
922         {
923           fprintf (stderr, "unknown section %s\n", argv[1]);
924           exit(1);
925         }
926     }
927   else
928     start = 0x27;
929
930   for (size_t i = start; i < n; )
931     {
932       if (i + 5 <= n
933           && data[i]
934           //&& !data[i + 1]
935           && !data[i + 2]
936           && !data[i + 3]
937           && i + 4 + data[i] + data[i + 1] * 256 <= n
938           && all_ascii(&data[i + 4], data[i] + data[i + 1] * 256))
939         {
940           fputs("\n\"", stdout);
941           fwrite(&data[i + 4], 1, data[i] + data[i + 1] * 256, stdout);
942           fputs("\" ", stdout);
943
944           i += 4 + data[i] + data[i + 1] * 256;
945         }
946       else if (i + 12 <= n
947                && data[i + 1] == 40
948                && data[i + 2] == 5
949                && data[i + 3] == 0)
950         {
951           double d;
952
953           memcpy (&d, &data[i + 4], 8);
954           printf ("F40.%d(%.*f)\n", data[i], data[i], d);
955           i += 12;
956         }
957       else if (i + 12 <= n
958                && data[i + 1] == 40
959                && data[i + 2] == 31
960                && data[i + 3] == 0)
961         {
962           double d;
963
964           memcpy (&d, &data[i + 4], 8);
965           printf ("PCT40.%d(%.*f)\n", data[i], data[i], d);
966           i += 12;
967         }
968       else if (i + 4 <= n
969                && (data[i] && data[i] != 88 && data[i] != 0x41)
970                && !data[i + 1]
971                && !data[i + 2]
972                && !data[i + 3])
973         {
974           printf ("i%d ", data[i]);
975           i += 4;
976         }
977       else
978         {
979           printf("%02x ", data[i]);
980           i++;
981         }
982     }
983
984   return 0;
985 }