1ca9f0a03f3fda520956e510c9db883a84767d9c
[pspp] / dump-spo.c
1 #include <assert.h>
2 #include <errno.h>
3 #include <fcntl.h>
4 #include <float.h>
5 #include <stdbool.h>
6 #include <stdint.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <sys/stat.h>
11 #include <time.h>
12 #include <unistd.h>
13 #include "u8-mbtouc.h"
14
15 static const char *filename;
16 static uint8_t *data;
17 static size_t n;
18
19 int version;
20
21 unsigned int pos;
22
23 #define XSTR(x) #x
24 #define STR(x) XSTR(x)
25 #define WHERE __FILE__":" STR(__LINE__)
26
27 static uint8_t
28 get_byte(void)
29 {
30   return data[pos++];
31 }
32
33 static unsigned int
34 get_u32(void)
35 {
36   uint32_t x;
37   memcpy(&x, &data[pos], 4);
38   pos += 4;
39   return x;
40 }
41
42 static unsigned long long int
43 get_u64(void)
44 {
45   uint64_t x;
46   memcpy(&x, &data[pos], 8);
47   pos += 8;
48   return x;
49 }
50
51 static unsigned int
52 get_be32(void)
53 {
54   uint32_t x;
55   x = (data[pos] << 24) | (data[pos + 1] << 16) | (data[pos + 2] << 8) | data[pos + 3];
56   pos += 4;
57   return x;
58 }
59
60 static unsigned int
61 get_u16(void)
62 {
63   uint16_t x;
64   memcpy(&x, &data[pos], 2);
65   pos += 2;
66   return x;
67 }
68
69 static double
70 get_double(void)
71 {
72   double x;
73   memcpy(&x, &data[pos], 8);
74   pos += 8;
75   return x;
76 }
77
78 static double __attribute__((unused))
79 get_float(void)
80 {
81   float x;
82   memcpy(&x, &data[pos], 4);
83   pos += 4;
84   return x;
85 }
86
87 static bool
88 match_u32(uint32_t x)
89 {
90   if (get_u32() == x)
91     return true;
92   pos -= 4;
93   return false;
94 }
95
96 bool
97 match_u16(uint16_t x)
98 {
99   if (get_u16() == x)
100     return true;
101   pos -= 2;
102   return false;
103 }
104
105 static void
106 match_u32_assert(uint32_t x, const char *where)
107 {
108   unsigned int y = get_u32();
109   if (x != y)
110     {
111       fprintf(stderr, "%s: 0x%x: expected i%u, got i%u\n", where, pos - 4, x, y);
112       exit(1);
113     }
114 }
115 #define match_u32_assert(x) match_u32_assert(x, WHERE)
116
117 static void
118 match_u16_assert(uint16_t x, const char *where)
119 {
120   unsigned int y = get_u16();
121   if (x != y)
122     {
123       fprintf(stderr, "%s: 0x%x: expected u16:%u, got u16:%u\n", where, pos - 2, x, y);
124       exit(1);
125     }
126 }
127 #define match_u16_assert(x) match_u16_assert(x, WHERE)
128
129 static bool __attribute__((unused))
130 match_u64(uint64_t x)
131 {
132   if (get_u64() == x)
133     return true;
134   pos -= 8;
135   return false;
136 }
137
138 static void __attribute__((unused))
139 match_u64_assert(uint64_t x, const char *where)
140 {
141   unsigned long long int y = get_u64();
142   if (x != y)
143     {
144       fprintf(stderr, "%s: 0x%x: expected u64:%lu, got u64:%llu\n", where, pos - 8, x, y);
145       exit(1);
146     }
147 }
148 #define match_u64_assert(x) match_u64_assert(x, WHERE)
149
150 static bool __attribute__((unused))
151 match_be32(uint32_t x)
152 {
153   if (get_be32() == x)
154     return true;
155   pos -= 4;
156   return false;
157 }
158
159 static void
160 match_be32_assert(uint32_t x, const char *where)
161 {
162   unsigned int y = get_be32();
163   if (x != y)
164     {
165       fprintf(stderr, "%s: 0x%x: expected be%u, got be%u\n", where, pos - 4, x, y);
166       exit(1);
167     }
168 }
169 #define match_be32_assert(x) match_be32_assert(x, WHERE)
170
171 static bool
172 match_byte(uint8_t b)
173 {
174   if (pos < n && data[pos] == b)
175     {
176       pos++;
177       return true;
178     }
179   else
180     return false;
181 }
182
183 static void
184 match_byte_assert(uint8_t b, const char *where)
185 {
186   if (!match_byte(b))
187     {
188       fprintf(stderr, "%s: 0x%x: expected %02x, got %02x\n", where, pos, b, data[pos]);
189       exit(1);
190     }
191 }
192 #define match_byte_assert(b) match_byte_assert(b, WHERE)
193
194 static bool
195 match_bytes(int start, const int *bytes, size_t n_bytes)
196 {
197   for (size_t i = 0; i < n_bytes; i++)
198     if (bytes[i] >= 0 && data[start + i] != bytes[i])
199       return false;
200   return true;
201 }
202
203 static char *
204 xmemdup0(const void *p, size_t n)
205 {
206   char *s = malloc(n + 1);
207   memcpy(s, p, n);
208   s[n] = 0;
209   return s;
210 }
211
212 static bool
213 get_bool(void)
214 {
215   if (match_byte(0))
216     return false;
217   match_byte_assert(1);
218   return true;
219 }
220
221 static bool __attribute__((unused))
222 is_ascii(uint8_t p)
223 {
224   return (p >= ' ' && p < 127) || p == '\r' || p == '\n' || p == '\t';
225 }
226
227 static int
228 count_zeros(const uint8_t *p)
229 {
230   size_t n = 0;
231   while (p[n] == 0)
232     n++;
233   return n;
234 }
235
236 static bool __attribute__((unused))
237 all_utf8(const char *p_, size_t len)
238 {
239   const uint8_t *p = (const uint8_t *) p_;
240   for (size_t ofs = 0, mblen; ofs < len; ofs += mblen)
241     {
242       ucs4_t uc;
243
244       mblen = u8_mbtouc (&uc, p + ofs, len - ofs);
245       if ((uc < 32 && uc != '\n') || uc == 127 || uc == 0xfffd)
246         return false;
247     }
248   return true;
249 }
250
251 static char *
252 get_string1(void)
253 {
254   int len = data[pos++];
255   char *s = xmemdup0(&data[pos], len);
256   pos += len;
257   return s;
258 }
259
260 static char *
261 get_string2(void)
262 {
263   int len = data[pos] + data[pos + 1] * 256;
264   char *s = xmemdup0(&data[pos + 2], len);
265   pos += 2 + len;
266   return s;
267 }
268
269 static char *
270 get_string(const char *where)
271 {
272   if (1
273       /*data[pos + 1] == 0 && data[pos + 2] == 0 && data[pos + 3] == 0*/
274       /*&& all_ascii(&data[pos + 4], data[pos])*/)
275     {
276       int len = data[pos] + data[pos + 1] * 256;
277       char *s = malloc(len + 1);
278
279       memcpy(s, &data[pos + 4], len);
280       s[len] = 0;
281       pos += 4 + len;
282       return s;
283     }
284   else
285     {
286       fprintf(stderr, "%s: 0x%x: expected string\n", where, pos);
287       exit(1);
288     }
289 }
290 #define get_string() get_string(WHERE)
291
292 static char *
293 get_string_be(const char *where)
294 {
295   if (1
296       /*data[pos + 1] == 0 && data[pos + 2] == 0 && data[pos + 3] == 0*/
297       /*&& all_ascii(&data[pos + 4], data[pos])*/)
298     {
299       int len = data[pos + 2] * 256 + data[pos + 3];
300       char *s = malloc(len + 1);
301
302       memcpy(s, &data[pos + 4], len);
303       s[len] = 0;
304       pos += 4 + len;
305       return s;
306     }
307   else
308     {
309       fprintf(stderr, "%s: 0x%x: expected string\n", where, pos);
310       exit(1);
311     }
312 }
313 #define get_string_be() get_string_be(WHERE)
314
315 static int
316 get_end(void)
317 {
318   int len = get_u32();
319   return pos + len;
320 }
321
322 static void __attribute__((unused))
323 hex_dump(FILE *stream, int ofs, int n)
324 {
325   int n_ascii = 0;
326   for (int i = 0; i < n; i++)
327     {
328       int c = data[ofs + i];
329       n_ascii += is_ascii(c);
330       fprintf(stream, " %02x", c);
331     }
332   if (n_ascii >= 3)
333     {
334       putc(' ', stream);
335       for (int i = 0; i < n; i++)
336         {
337           int c = data[ofs + i];
338           putc(c >= 32 && c < 127 ? c : '.', stream);
339         }
340     }
341   putc('\n', stream);
342 }
343
344 static void __attribute__((unused))
345 char_dump(FILE *stream, int ofs, int n)
346 {
347   for (int i = 0; i < n; i++)
348     {
349       int c = data[ofs + i];
350       putc(c >= 32 && c < 127 ? c : '.', stream);
351     }
352   putc('\n', stream);
353 }
354
355
356 static int
357 compare_int(const void *a_, const void *b_)
358 {
359   const int *a = a_;
360   const int *b = b_;
361   return *a < *b ? -1 : *a > *b;
362 }
363
364
365 static const char *
366 format_name (int format, char *buf)
367 {
368   switch (format)
369     {
370     case 1: return "A";
371     case 2: return "AHEX";
372     case 3: return "COMMA";
373     case 4: return "DOLLAR";
374     case 5: case 40: return "F";
375     case 6: return "IB";
376     case 7: return "PIBHEX";
377     case 8: return "P";
378     case 9: return "PIB";
379     case 10: return "PK";
380     case 11: return "RB";
381     case 12: return "RBHEX";
382     case 15: return "Z";
383     case 16: return "N";
384     case 17: return "E";
385     case 20: return "DATE";
386     case 21: return "TIME";
387     case 22: return "DATETIME";
388     case 23: return "ADATE";
389     case 24: return "JDATE";
390     case 25: return "DTIME";
391     case 26: return "WKDAY";
392     case 27: return "MONTH";
393     case 28: return "MOYR";
394     case 29: return "QYR";
395     case 30: return "WKYR";
396     case 31: return "PCT";
397     case 32: return "DOT";
398     case 33: return "CCA";
399     case 34: return "CCB";
400     case 35: return "CCC";
401     case 36: return "CCD";
402     case 37: return "CCE";
403     case 38: return "EDATE";
404     case 39: return "SDATE";
405     default: sprintf(buf, "(%d)", format); return buf;
406     }
407 }
408
409 static void
410 dump_DspNumber(void)
411 {
412   match_byte_assert(1);
413   int d = get_byte();
414   int w = get_byte();
415   int fmt = get_byte();
416   char buf[64];
417   printf ("%s%d.%d ", format_name (fmt, buf), w, d);
418
419   match_byte_assert(0x80);
420   match_byte_assert(2);
421   printf ("%f ", get_double ());
422   printf ("\"%s\"\n", get_string1 ());
423
424   for (;;)
425     {
426       if (data[pos] == 0xff)
427         {
428           printf ("\nff exit");
429           break;
430         }
431
432       if (data[pos] == 0x80 && data[pos + 1] == 1)
433         {
434           pos += 2;
435           int d = get_byte();
436           int w = get_byte();
437           int fmt = get_byte();
438           char buf[64];
439           printf ("\n%% %s%d.%d\n", format_name (fmt, buf), w, d);
440         }
441       else if (data[pos] == 0x80 && data[pos + 1] == 2)
442         {
443           pos += 2;
444           printf ("\n%f ", get_double ());
445           printf ("'%s'\n", get_string1 ());
446         }
447       else if (data[pos] == 0x80 && data[pos + 1] == 0 && data[pos + 2] == 3)
448         pos += 3;
449       else if (data[pos] == 0x80 && count_zeros(&data[pos + 1]) == 10)
450         pos += 11;
451       else if (data[pos] == 0x1 && data[pos + 1] == 0xff)
452         {
453           pos += 2;
454           printf ("\n\"%s\"\n", get_string2 ());
455         }
456       else if (data[pos] == 0x1 && data[pos + 1])
457         {
458           pos += 1;
459           printf ("\n\"%s\"\n", get_string1 ());
460         }
461       else
462         printf ("%02x ", get_byte());
463     }
464 }
465
466 static void
467 dump_cell(void)
468 {
469   static const int cell_prefix[] = {
470     0x00, 0x03, 0x80,
471     0x00, 0x00, 0x00, 0x00, 0x00, -1 /* 00 or 10 */, 0x00, 0x00, 0x00, 0x00,
472
473     /*13  14    15  16  17  18  19 */
474     -1, 0x80, 0x01, -1, -1, -1, -1,
475   };
476   size_t cell_prefix_len = sizeof cell_prefix / sizeof *cell_prefix;
477   if (!match_bytes(pos, cell_prefix, cell_prefix_len))
478     {
479       printf ("match failed at %x\n", pos);
480       return;
481     }
482
483   char buf[64];
484   printf ("cell %s%d.%d ",
485           format_name (data[pos + 18], buf),
486           data[pos + 17],
487           data[pos + 16]);
488
489   int len = cell_prefix_len;
490   if (data[pos + 19] == 0)
491     {
492       assert (data[pos + 13] == 5);
493       if (data[pos + 20] == 0)
494         {
495           int count = (data[pos + 22]);
496           printf ("%d %d \"%.*s\"",
497                   data[pos + 21], data[pos + 22],
498                   count, &data[pos + 23]);
499           len = 23 + count;
500         }
501       else if (data[pos + 20] == 1
502                && data[pos + 21] == 0xff
503                && data[pos + 22] == 0xff)
504         {
505           int count = 255;
506           printf ("%d \"%.*s\"", count, data[pos + 23],
507                   &data[pos + 24]);
508           len = 23 + count;
509         }
510       else if (data[pos + 20] == 1 && data[pos + 21] == 255)
511         {
512           int count = data[pos + 22] + (data[pos + 23] << 8);
513           printf ("\"%.*s\"",
514                   count, &data[pos + 24]);
515           len = 24 + count;
516         }
517       else if (data[pos + 20] == 1)
518         {
519           int count = (data[pos + 21]);
520           printf ("\"%.*s\"",
521                   count, &data[pos + 22]);
522           len = 22 + count;
523         }
524       else
525         assert (false);
526     }
527   else if (data[pos + 19] == 128 && data[pos + 20] == 2)
528     {
529       /* pos + 13 is usually 22...53, and it's 3 more than the
530          " xx 80" separator between cells  */
531       printf ("xxx%x ", data[pos + 13]);
532       double d = *(double *) &data[pos + 21];
533       len = 29;
534       const union
535         {
536           uint8_t b[8];
537           double d;
538         }
539       sysmis = {.b = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff}};
540       if (d == sysmis.d)
541         printf ("sysmis");
542       else
543         printf ("%f", d);
544
545       if (data[pos + 29] < 0xff
546           && all_utf8((char *) &data[pos + 30], data[pos + 29]))
547         {
548           printf (" \"%.*s\"", (int) data[pos + 29],
549                   &data[pos + 30]);
550           len += data[pos + 29] + 1;
551         }
552       else
553         assert (false);
554     }
555   else if (data[pos + 19] == 128 && data[pos + 20] == 1 &&
556            data[pos + 21] == 0)
557     {
558       if (data[pos + 23] < 0xff
559           && all_utf8((char *) &data[pos + 24], data[pos + 23]))
560         {
561           printf (" \"%.*s\"", (int) data[pos + 23],
562                   &data[pos + 24]);
563           len = 24 + data[pos + 23];
564         }
565       else
566         assert (false);
567     }
568   else
569     {
570       printf ("xxx%d %d %d %d",
571               data[pos + 19], data[pos + 20],
572               data[pos + 21], data[pos + 22]);
573       assert(false);
574     }
575   pos += len;
576 }
577
578 static void
579 dump_category (int level, uint8_t *catstart, bool *have_catstart)
580 {
581   int cat_index = get_u32();
582   assert (cat_index < 256);
583
584   if (!match_u32 (0))
585     match_u32_assert (1);
586
587   get_u16();
588
589   if (!match_u16(0xe74) && !match_u16(0xffff))
590     match_u16_assert(0);
591
592   for (int i = 0; i < level; i++)
593     printf ("  ");
594
595   for (int i = 0; ; i++, pos++)
596     if (data[pos] == 5 && data[pos + 1] == 0x80)
597       break;
598     else if (i >= 100)
599       assert(false);
600
601   match_byte_assert (5);
602   match_byte_assert (0x80);
603   if (match_byte(2))
604     get_double ();
605   else
606     {
607       match_byte_assert (1);
608       match_byte_assert (2);
609       match_byte_assert (0x28);
610       match_byte_assert (5);
611       match_byte_assert (0);
612       match_byte_assert (1);
613     }
614   printf (" \"%s\"", get_string1());
615
616   int n_children = get_u32();
617   assert (n_children < 256);
618   if (n_children)
619     printf (" (group with %d children)", n_children);
620   else
621     printf (" (category #%d)", cat_index);
622
623   printf (" %02x %02x %02x %02x %02x %02x\n",
624           data[pos], data[pos + 1], data[pos + 2],
625           data[pos + 3], data[pos + 4], data[pos + 5]);
626   if (!*have_catstart)
627     {
628       *have_catstart = true;
629       memcpy(catstart, &data[pos], 6);
630     }
631   pos += 6;
632
633   for (int i = 0; i < n_children; i++)
634     dump_category (level + 1, catstart, have_catstart);
635 }
636
637 static void
638 dump_PMModelItemInfo(int ndims)
639 {
640   return;
641 #if 0
642   if (data[pos + 9] && data[pos + 9] != 0xff)//count_zeros (&data[pos + 9]) < 4)
643     return;
644 #endif
645   
646   match_byte_assert (0);
647
648   uint8_t catstart[6];
649   bool have_catstart = false;
650   dump_category (0, catstart, &have_catstart);
651   assert(have_catstart);
652
653   for (int i = 1; i < ndims; i++)
654     {
655       for (int j = 0; ; j++, pos++)
656         {
657           assert (j <= 1000);
658           if (!memcmp(&data[pos], catstart, 6))
659             break;
660         }
661       pos += 6;
662
663       dump_category (0, catstart, &have_catstart);
664     }
665 }
666
667 int
668 main(int argc, char *argv[])
669 {
670   bool print_offsets = false;
671   for (;;)
672     {
673       int c = getopt (argc, argv, "o");
674       if (c == -1)
675         break;
676
677       switch (c)
678         {
679         case 'o':
680           print_offsets = true;
681           break;
682
683         case '?':
684           exit (-1);
685         }
686     }
687   if (argc - optind != 1)
688     {
689       fprintf (stderr, "usage: %s FILE.bin", argv[0]);
690       exit (1);
691     }
692
693   const char *filename = argv[optind];
694   int fd = open(filename, O_RDONLY);
695   if (fd < 0)
696     {
697       fprintf (stderr, "%s: open failed (%s)", filename, strerror (errno));
698       exit (1);
699     }
700
701   struct stat s;
702   if (fstat(fd, &s))
703     {
704       perror("fstat");
705       exit(1);
706     }
707   n = s.st_size;
708   data = malloc(n);
709   if (!data)
710     {
711       perror("malloc");
712       exit(1);
713     }
714   if (read(fd, data, n) != n)
715     {
716       perror("read");
717       exit(1);
718     }
719   close(fd);
720
721   setvbuf (stdout, NULL, _IOLBF, 0);
722
723   int sum = 0;
724   
725 #if 0
726   unsigned int prev_end = 0;
727   for (pos = 0; pos + 50 < n; pos++)
728     {
729       if (data[pos + 0] == 0xff &&
730           data[pos + 1] == 0xff &&
731           data[pos + 2] == 0 &&
732           data[pos + 3] == 0)
733         {
734           int len = data[pos + 4] + (data[pos + 5] << 8);
735           if (len < 3 || pos + len + 6 >= n || !all_utf8 ((char *) &data[pos + 6], len))
736             continue;
737
738           printf ("+%04x %04x...%04x: %-25.*s\n",
739                   pos - prev_end, pos, pos + 6 + len,
740                   len < 50 ? (int) len : 50, &data[pos + 6]);
741           prev_end = pos + 6 + len;
742         }
743     }
744 #endif
745 #if 0
746   for (pos = 0; pos + 50 < n; pos++)
747     {
748       if (data[pos + 0] == 'L' &&
749           data[pos + 1] == 'o' &&
750           data[pos + 2] == 'g' &&
751           !all_utf8((char *) &data[pos + 3], 1) &&
752           data[pos - 1] != 'v')
753         {
754           if (print_offsets)
755             printf ("%04x: ", pos);
756           unsigned int p = pos;
757           while (all_utf8 ((char *) &data[p], 1))
758             p--;
759           hex_dump (stdout, p - 28, 38);
760         }
761     }
762 #endif
763   unsigned int prev_end = 0;
764   char *title = "";
765   int ndims = 0;
766   for (pos = 2; pos + 50 < n; )
767     {
768       static const int cell_prefix[] = {
769         0x00, 0x03,
770         0x80, 0x00, 0x00, 0x00, 0x00, 0x00, -1 /* 00 or 10 */, 0x00, 0x00, 0x00, 0x00, -1,
771
772         /*14    15  16  17  18  19 */
773         0x80, 0x01, -1, -1, -1, -1,
774       };
775       size_t cell_prefix_len = sizeof cell_prefix / sizeof *cell_prefix;
776       if (match_bytes(pos, cell_prefix, cell_prefix_len))
777         {
778           if (prev_end != pos)
779             {
780               if (print_offsets)
781                 printf ("%04x ", prev_end);
782               hex_dump (stdout, prev_end, pos - prev_end);
783             }
784
785           char buf[64];
786           printf ("cell %s%d.%d ",
787                   format_name (data[pos + 18], buf),
788                   data[pos + 17],
789                   data[pos + 16]);
790
791           int len = cell_prefix_len;
792           if (data[pos + 19] == 0)
793             {
794               assert (data[pos + 13] == 5);
795               if (data[pos + 20] == 0)
796                 {
797                   int count = (data[pos + 22]);
798                   printf ("%d %d \"%.*s\"\n",
799                           data[pos + 21], data[pos + 22],
800                           count, &data[pos + 23]);
801                   len = 23 + count;
802                 }
803               else if (data[pos + 20] == 1
804                        && data[pos + 21] == 0xff
805                        && data[pos + 22] == 0xff)
806                 {
807                   int count = 255;
808                   printf ("%d \"%.*s\"\n", count, data[pos + 23],
809                           &data[pos + 24]);
810                   len = 23 + count;
811                 }
812               else if (data[pos + 20] == 1 && data[pos + 21] == 255)
813                 {
814                   int count = data[pos + 22] + (data[pos + 23] << 8);
815                   printf ("\"%.*s\"\n",
816                           count, &data[pos + 24]);
817                   len = 24 + count;
818                 }
819               else if (data[pos + 20] == 1)
820                 {
821                   int count = (data[pos + 21]);
822                   printf ("\"%.*s\"\n",
823                           count, &data[pos + 22]);
824                   len = 22 + count;
825                 }
826               else
827                 assert (false);
828             }
829           else if (data[pos + 19] == 128 && data[pos + 20] == 2)
830             {
831               /* pos + 13 is usually 22...53, and it's 3 more than the
832                  " xx 80" separator between cells  */
833               printf ("xxx%x ", data[pos + 13]);
834               double d = *(double *) &data[pos + 21];
835               len = 29;
836               const union
837                 {
838                   uint8_t b[8];
839                   double d;
840                 }
841               sysmis = {.b = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff}};
842               if (d == sysmis.d)
843                 printf ("sysmis");
844               else
845                 printf ("%f", d);
846
847               if (data[pos + 29] < 0xff
848                   && all_utf8((char *) &data[pos + 30], data[pos + 29]))
849                 {
850                   printf (" \"%.*s\"", (int) data[pos + 29],
851                           &data[pos + 30]);
852                   len += data[pos + 29] + 1;
853                 }
854               else
855                 assert (false);
856
857               putchar ('\n');
858             }
859           else if (data[pos + 19] == 128 && data[pos + 20] == 1 &&
860                    data[pos + 21] == 0)
861             {
862               if (data[pos + 23] < 0xff
863                   && all_utf8((char *) &data[pos + 24], data[pos + 23]))
864                 {
865                   printf (" \"%.*s\"\n", (int) data[pos + 23],
866                           &data[pos + 24]);
867                   len = 24 + data[pos + 23];
868                 }
869               else
870                 assert (false);
871             }
872           else
873             {
874               printf ("xxx%d %d %d %d\n",
875                       data[pos + 19], data[pos + 20],
876                       data[pos + 21], data[pos + 22]);
877               assert(false);
878             }
879           pos += len;
880           prev_end = pos;
881           continue;
882         }
883
884       static const int record_prefix[] = {
885         0xff, 0xff, 0x00, 0x00,
886       };
887       size_t record_prefix_len = sizeof record_prefix / sizeof *record_prefix;
888       if (match_bytes(pos, record_prefix, record_prefix_len))
889         {
890           int len = record_prefix_len;
891           int slen = data[pos + 4] + (data[pos + 5] << 8);
892           if (slen >= 2 && slen < 256 && all_utf8((char *) &data[pos + 6], slen))
893             {
894               if (prev_end != pos)
895                 {
896                   if (print_offsets)
897                     printf ("%04x ", prev_end);
898                   hex_dump (stdout, prev_end, pos - prev_end);
899                 }
900
901               putchar ('\n');
902
903               printf ("rec:%-20.*s ", slen, &data[pos + 6]);
904               len = slen + 6;
905               title = xmemdup0(&data[pos + 6], slen);
906               sum += data[pos+len];
907
908               pos += len;
909
910               if (!strcmp(title, "DspNumber"))
911                 dump_DspNumber();
912               else if (!strcmp(title, "PMModelItemInfo"))
913                 {
914                   assert(ndims);
915                   dump_PMModelItemInfo(ndims);
916                 }
917               else if (!strcmp(title, "NDimensional__DspCell"))
918                 {
919                   match_byte_assert(0);
920                   ndims = get_u32();
921                   assert(ndims < 10);
922                 }
923               prev_end = pos;
924               continue;
925             }
926         }
927
928       static const int number_prefix[] = {
929         0x80, 0x02
930       };
931       size_t number_prefix_len = sizeof number_prefix / sizeof *number_prefix;
932       if (match_bytes(pos, number_prefix, number_prefix_len))
933         {
934           if (prev_end != pos)
935             {
936               if (print_offsets)
937                 printf ("%04x ", prev_end);
938               hex_dump (stdout, prev_end, pos - prev_end);
939             }
940           prev_end = pos;
941
942           double d = *(double *) &data[pos + number_prefix_len];
943           printf ("float %f\n", d);
944
945           pos += 10;
946           prev_end = pos;
947           continue;
948         }
949
950       if (!memcmp (&data[pos + 4], "{\\rtf", 5))
951         {
952           int len = data[pos] + (data[pos + 1] << 8) + (data[pos + 2] << 16)
953             + (data[pos + 3] << 24);
954           if (len < n - pos - 4)
955             {
956               if (prev_end != pos)
957                 {
958                   if (print_offsets)
959                     printf ("%04x ", prev_end);
960                   hex_dump (stdout, prev_end, pos - prev_end);
961                 }
962               prev_end = pos;
963
964               printf ("rtf\n");
965               pos += 4 + len;
966               prev_end = pos;
967               continue;
968             }
969         }
970
971       if (data[pos] && data[pos + 1] && data[pos + 2] >= 0xfe
972           && data[pos + 3] == 0xff && data[pos + 4] && data[pos + 4] != 0xff)
973         {
974           if (prev_end != pos)
975             {
976               if (print_offsets)
977                 printf ("%04x ", prev_end);
978               hex_dump (stdout, prev_end, pos - prev_end);
979             }
980           prev_end = pos;
981
982           static int prev_num;
983           int32_t num = data[pos] + (data[pos + 1] << 8)
984             + (data[pos + 2] << 16) + (data[pos + 3] << 24);
985           printf ("%d (%+d) ", num, num - prev_num);
986           prev_num = num;
987           pos += 4;
988           prev_end = pos;
989           continue;
990         }
991
992       static const int font_prefix[] = 
993         {
994           0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x41, 0x72, 0x69, 0x61, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
995         };
996       size_t font_prefix_len = sizeof font_prefix / sizeof *font_prefix;
997       if (match_bytes(pos, font_prefix, font_prefix_len))
998         {
999           if (prev_end != pos)
1000             {
1001               if (print_offsets)
1002                 printf ("%04x ", prev_end);
1003               hex_dump (stdout, prev_end, pos - prev_end);
1004             }
1005           prev_end = pos;
1006
1007           printf ("font\n");
1008
1009           pos += font_prefix_len;
1010           prev_end = pos;
1011           continue;
1012         }
1013
1014       static const int string_prefix[] = {
1015         0x05, 0x80, 0x01, 0x02, 0x28, 0x05, 0x00, 0x01
1016       };
1017       size_t string_prefix_len = sizeof string_prefix / sizeof *string_prefix;
1018       if (match_bytes(pos, string_prefix, string_prefix_len) && data[pos + string_prefix_len] != 255)
1019         {
1020           if (prev_end != pos)
1021             {
1022               if (print_offsets)
1023                 printf ("%04x ", prev_end);
1024               hex_dump (stdout, prev_end, pos - prev_end);
1025             }
1026           prev_end = pos;
1027
1028           int len = data[pos + 8];
1029           printf ("string %.*s\n", len, &data[pos + 9]);
1030           pos += 8 + len;
1031           prev_end = pos;
1032           continue;
1033         }
1034       if (match_bytes(pos, string_prefix, string_prefix_len) && data[pos + string_prefix_len] == 255)
1035         {
1036           if (prev_end != pos)
1037             {
1038               if (print_offsets)
1039                 printf ("%04x ", prev_end);
1040               hex_dump (stdout, prev_end, pos - prev_end);
1041             }
1042           prev_end = pos;
1043
1044           int len = data[pos + 9] + (data[pos + 10] << 8);
1045           printf ("\nlongstring %.*s\n", len, &data[pos + 11]);
1046           pos += 11 + len;
1047           prev_end = pos;
1048           continue;
1049         }
1050
1051       
1052
1053
1054       if (!is_ascii(data[pos]))
1055         {
1056           pos++;
1057           continue;
1058         }
1059
1060       unsigned int start = pos;
1061       unsigned int end = pos + 1;
1062       while (is_ascii(data[end]))
1063         end++;
1064
1065       unsigned int len = end - start;
1066       if (len < 3)
1067         {
1068           pos++;
1069           continue;
1070         }
1071
1072       unsigned int len2 = data[start - 2] + (data[start - 1] << 8);
1073       unsigned int len3 = data[start - 1];
1074       int length_bytes;
1075       if (len2 && len2 <= len)
1076         {
1077           length_bytes = 2;
1078           len = len2;
1079         }
1080       else if (len3 && len3 <= len)
1081         {
1082           length_bytes = 1;
1083           len = len3;
1084         }
1085       else
1086         {
1087           pos++;
1088           continue;
1089         }
1090       if (len < 3)
1091         {
1092           pos++;
1093           continue;
1094         }
1095       end = start + len;
1096
1097       unsigned real_start = start - length_bytes;
1098       if (prev_end != real_start)
1099         {
1100           if (print_offsets)
1101             printf ("%04x ", prev_end);
1102           hex_dump (stdout, prev_end, real_start - prev_end);
1103         }
1104       if (print_offsets)
1105         printf ("%04x ", real_start);
1106       printf ("\"%.*s\"\n", 
1107               (int) end - start, (char *) &data[start]);
1108       prev_end = end;
1109       pos = end;
1110     }
1111
1112   exit(0);
1113
1114   return 0;
1115 }