progress?
[pspp] / dump-spo2.c
1 #include <assert.h>
2 #include <errno.h>
3 #include <fcntl.h>
4 #include <float.h>
5 #include <inttypes.h>
6 #include <stdbool.h>
7 #include <stdint.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <sys/stat.h>
12 #include <time.h>
13 #include <unistd.h>
14 #include "u8-mbtouc.h"
15
16 static const char *filename;
17 static uint8_t *data;
18 static size_t n;
19
20 int version;
21
22 unsigned int pos;
23
24 #define XSTR(x) #x
25 #define STR(x) XSTR(x)
26 #define WHERE __FILE__":" STR(__LINE__)
27
28 static void __attribute__((unused))
29 hex_dump(FILE *stream, int ofs, int n);
30
31 static uint8_t
32 get_byte(void)
33 {
34   return data[pos++];
35 }
36
37 static unsigned int
38 get_u32(void)
39 {
40   uint32_t x;
41   memcpy(&x, &data[pos], 4);
42   pos += 4;
43   return x;
44 }
45
46 static unsigned long long int
47 get_u64(void)
48 {
49   uint64_t x;
50   memcpy(&x, &data[pos], 8);
51   pos += 8;
52   return x;
53 }
54
55 static unsigned int
56 get_be32(void)
57 {
58   uint32_t x;
59   x = (data[pos] << 24) | (data[pos + 1] << 16) | (data[pos + 2] << 8) | data[pos + 3];
60   pos += 4;
61   return x;
62 }
63
64 static unsigned int
65 get_u16(void)
66 {
67   uint16_t x;
68   memcpy(&x, &data[pos], 2);
69   pos += 2;
70   return x;
71 }
72
73 static double
74 get_double(void)
75 {
76   double x;
77   memcpy(&x, &data[pos], 8);
78   pos += 8;
79   return x;
80 }
81
82 static double __attribute__((unused))
83 get_float(void)
84 {
85   float x;
86   memcpy(&x, &data[pos], 4);
87   pos += 4;
88   return x;
89 }
90
91 static bool
92 match_u32(uint32_t x)
93 {
94   if (get_u32() == x)
95     return true;
96   pos -= 4;
97   return false;
98 }
99
100 bool
101 match_u16(uint16_t x)
102 {
103   if (get_u16() == x)
104     return true;
105   pos -= 2;
106   return false;
107 }
108
109 static void
110 match_u32_assert(uint32_t x, const char *where)
111 {
112   unsigned int y = get_u32();
113   if (x != y)
114     {
115       fprintf(stderr, "%s: 0x%x: expected i%u, got i%u: ", where, pos - 4, x, y);
116       hex_dump(stderr, pos - 4, 64);
117       exit(1);
118     }
119 }
120 #define match_u32_assert(x) match_u32_assert(x, WHERE)
121
122 static void
123 match_u16_assert(uint16_t x, const char *where)
124 {
125   unsigned int y = get_u16();
126   if (x != y)
127     {
128       fprintf(stderr, "%s: 0x%x: expected u16:%u, got u16:%u: ", where, pos - 2, x, y);
129       hex_dump(stderr, pos - 2, 64);
130       exit(1);
131     }
132 }
133 #define match_u16_assert(x) match_u16_assert(x, WHERE)
134
135 static bool __attribute__((unused))
136 match_u64(uint64_t x)
137 {
138   if (get_u64() == x)
139     return true;
140   pos -= 8;
141   return false;
142 }
143
144 static void __attribute__((unused))
145 match_u64_assert(uint64_t x, const char *where)
146 {
147   unsigned long long int y = get_u64();
148   if (x != y)
149     {
150       fprintf(stderr, "%s: 0x%x: expected u64:%lu, got u64:%llu\n", where, pos - 8, x, y);
151       exit(1);
152     }
153 }
154 #define match_u64_assert(x) match_u64_assert(x, WHERE)
155
156 static bool __attribute__((unused))
157 match_be32(uint32_t x)
158 {
159   if (get_be32() == x)
160     return true;
161   pos -= 4;
162   return false;
163 }
164
165 static void
166 match_be32_assert(uint32_t x, const char *where)
167 {
168   unsigned int y = get_be32();
169   if (x != y)
170     {
171       fprintf(stderr, "%s: 0x%x: expected be%u, got be%u\n", where, pos - 4, x, y);
172       exit(1);
173     }
174 }
175 #define match_be32_assert(x) match_be32_assert(x, WHERE)
176
177 static bool
178 match_byte(uint8_t b)
179 {
180   if (pos < n && data[pos] == b)
181     {
182       pos++;
183       return true;
184     }
185   else
186     return false;
187 }
188
189 static void
190 match_byte_assert(uint8_t b, const char *where)
191 {
192   if (!match_byte(b))
193     {
194       fprintf(stderr, "%s: 0x%x: expected %02x, got %02x: ", where, pos, b, data[pos]);
195       hex_dump(stderr, pos, 64);
196       exit(1);
197     }
198 }
199 #define match_byte_assert(b) match_byte_assert(b, WHERE)
200
201 static bool
202 match_bytes(int start, const int *bytes, size_t n_bytes)
203 {
204   for (size_t i = 0; i < n_bytes; i++)
205     if (bytes[i] >= 0 && data[start + i] != bytes[i])
206       return false;
207   return true;
208 }
209
210 static char *
211 xmemdup0(const void *p, size_t n)
212 {
213   char *s = malloc(n + 1);
214   memcpy(s, p, n);
215   s[n] = 0;
216   return s;
217 }
218
219 static bool
220 get_bool(void)
221 {
222   if (match_byte(0))
223     return false;
224   match_byte_assert(1);
225   return true;
226 }
227
228 static bool __attribute__((unused))
229 is_ascii(uint8_t p)
230 {
231   return (p >= ' ' && p < 127) || p == '\r' || p == '\n' || p == '\t';
232 }
233
234 static int
235 count_zeros(const uint8_t *p)
236 {
237   size_t n = 0;
238   while (p[n] == 0)
239     n++;
240   return n;
241 }
242
243 static bool __attribute__((unused))
244 all_utf8(const char *p_, size_t len)
245 {
246   const uint8_t *p = (const uint8_t *) p_;
247   for (size_t ofs = 0, mblen; ofs < len; ofs += mblen)
248     {
249       ucs4_t uc;
250
251       mblen = u8_mbtouc (&uc, p + ofs, len - ofs);
252       if ((uc < 32 && uc != '\n') || uc == 127 || uc == 0xfffd)
253         return false;
254     }
255   return true;
256 }
257
258 static char *
259 get_string2(void)
260 {
261   int len = data[pos] + data[pos + 1] * 256;
262   char *s = xmemdup0(&data[pos + 2], len);
263   pos += 2 + len;
264   return s;
265 }
266
267 static char *
268 get_string1(void)
269 {
270   int len = data[pos++];
271   if (len == 0xff)
272     return get_string2();
273   else
274     {
275       char *s = xmemdup0(&data[pos], len);
276       pos += len;
277       return s;
278     }
279 }
280
281 static void
282 match_string1_assert(const char *exp, const char *where)
283 {
284   int start = pos;
285   char *act = get_string1();
286   if (strcmp(act, exp)) 
287     {
288       fprintf(stderr, "%s: 0x%x: expected \"%s\", got \"%s\"\n",
289               where, start, exp, act);
290       exit(1);
291     }
292 }
293 #define match_string1_assert(x) match_string1_assert(x, WHERE)
294
295 static void
296 match_string2_assert(const char *exp, const char *where)
297 {
298   int start = pos;
299   char *act = get_string2();
300   if (strcmp(act, exp)) 
301     {
302       fprintf(stderr, "%s: 0x%x: expected \"%s\", got \"%s\"\n",
303               where, start, exp, act);
304       exit(1);
305     }
306 }
307 #define match_string2_assert(x) match_string2_assert(x, WHERE)
308
309 static char *
310 get_string4(const char *where)
311 {
312   if (1
313       /*data[pos + 1] == 0 && data[pos + 2] == 0 && data[pos + 3] == 0*/
314       /*&& all_ascii(&data[pos + 4], data[pos])*/)
315     {
316       assert(data[pos + 3] == 0);
317       int len = data[pos] + data[pos + 1] * 256 + data[pos + 2] * 65536;
318       char *s = malloc(len + 1);
319
320       memcpy(s, &data[pos + 4], len);
321       s[len] = 0;
322       pos += 4 + len;
323       return s;
324     }
325   else
326     {
327       fprintf(stderr, "%s: 0x%x: expected string\n", where, pos);
328       exit(1);
329     }
330 }
331 #define get_string4() get_string4(WHERE)
332
333 static char *
334 get_padded_string(int len)
335 {
336   char *s = xmemdup0(&data[pos], len);
337   pos += len;
338   return s;
339 }
340
341 static char *
342 get_string_be(const char *where)
343 {
344   if (1
345       /*data[pos + 1] == 0 && data[pos + 2] == 0 && data[pos + 3] == 0*/
346       /*&& all_ascii(&data[pos + 4], data[pos])*/)
347     {
348       int len = data[pos + 2] * 256 + data[pos + 3];
349       char *s = malloc(len + 1);
350
351       memcpy(s, &data[pos + 4], len);
352       s[len] = 0;
353       pos += 4 + len;
354       return s;
355     }
356   else
357     {
358       fprintf(stderr, "%s: 0x%x: expected string\n", where, pos);
359       exit(1);
360     }
361 }
362 #define get_string_be() get_string_be(WHERE)
363
364 static int
365 get_end(void)
366 {
367   int len = get_u32();
368   return pos + len;
369 }
370
371 static void __attribute__((unused))
372 hex_dump(FILE *stream, int ofs, int n)
373 {
374   int n_ascii = 0;
375   for (int i = 0; i < n; i++)
376     {
377       int c = data[ofs + i];
378       n_ascii += is_ascii(c);
379       fprintf(stream, " %02x", c);
380     }
381   if (n_ascii >= 3)
382     {
383       putc(' ', stream);
384       for (int i = 0; i < n; i++)
385         {
386           int c = data[ofs + i];
387           putc(c >= 32 && c < 127 ? c : '.', stream);
388         }
389     }
390   putc('\n', stream);
391 }
392
393 static void __attribute__((unused))
394 char_dump(FILE *stream, int ofs, int n)
395 {
396   for (int i = 0; i < n; i++)
397     {
398       int c = data[ofs + i];
399       putc(c >= 32 && c < 127 ? c : '.', stream);
400     }
401   putc('\n', stream);
402 }
403
404
405 static int
406 compare_int(const void *a_, const void *b_)
407 {
408   const int *a = a_;
409   const int *b = b_;
410   return *a < *b ? -1 : *a > *b;
411 }
412
413
414 static const char *
415 format_name (int format, char *buf)
416 {
417   switch (format)
418     {
419     case 1: return "A";
420     case 2: return "AHEX";
421     case 3: return "COMMA";
422     case 4: return "DOLLAR";
423     case 5: case 40: return "F";
424     case 6: return "IB";
425     case 7: return "PIBHEX";
426     case 8: return "P";
427     case 9: return "PIB";
428     case 10: return "PK";
429     case 11: return "RB";
430     case 12: return "RBHEX";
431     case 15: return "Z";
432     case 16: return "N";
433     case 17: return "E";
434     case 20: return "DATE";
435     case 21: return "TIME";
436     case 22: return "DATETIME";
437     case 23: return "ADATE";
438     case 24: return "JDATE";
439     case 25: return "DTIME";
440     case 26: return "WKDAY";
441     case 27: return "MONTH";
442     case 28: return "MOYR";
443     case 29: return "QYR";
444     case 30: return "WKYR";
445     case 31: return "PCT";
446     case 32: return "DOT";
447     case 33: return "CCA";
448     case 34: return "CCB";
449     case 35: return "CCC";
450     case 36: return "CCD";
451     case 37: return "CCE";
452     case 38: return "EDATE";
453     case 39: return "SDATE";
454     default: sprintf(buf, "(%d)", format); return buf;
455     }
456 }
457
458 static void
459 parse_format(void)
460 {
461   int d = data[pos++];
462   int w = data[pos++];
463   int fmt = data[pos++];
464   char buf[32];
465   printf ("%s%d.%d", format_name(fmt, buf), w, d);
466 }
467
468 static void
469 parse_heading(const char *name)
470 {
471   match_u16_assert(0xffff);
472   match_u16_assert(0);
473   match_string2_assert(name);
474 }
475
476 static void
477 match_zeros_assert(int count, const char *where)
478 {
479   for (int i = 0; i < count; i++)
480     if (data[pos + i])
481       {
482         fprintf (stderr,
483                  "%s: %#x: expected %d zeros here but offset %d is %#"PRIx8": ",
484                  where, pos, count, i, data[pos + i]);
485         hex_dump (stderr, pos, 64);
486         exit (1);
487       }
488   pos += count;
489 }
490 #define match_zeros_assert(count) match_zeros_assert(count, WHERE)
491
492 static void
493 put_safe(const char *s)
494 {
495   while (*s)
496     {
497       if (*s == '\n')
498         printf ("\\n");
499       else if (*s == '\r')
500         printf ("\\r");
501       else if (*s < 0x20 || *s > 0x7e)
502         printf ("\\x%02"PRIx8, (uint8_t) *s);
503       else
504         putchar (*s);
505       s++;
506     }
507 }
508
509 static void parse_flexible(void);
510
511 static void
512 parse_DspString(void)
513 {
514   printf("%#x: DspString(", pos);
515   if (match_byte(2))
516     {
517       printf("%f, \"", get_double());
518       printf("%s\")\n", get_string1());
519     }
520   else
521     {
522       match_byte_assert(1);
523       parse_format();
524       printf(" \"");
525       match_byte_assert(0);
526       match_byte_assert(1);
527       put_safe(get_string1());
528       printf("\")\n");
529     }
530 }
531
532 static void
533 match_DspString(void)
534 {                               /* 05 80 */
535   match_byte_assert(5);
536   match_byte_assert(0x80);
537   parse_DspString();
538 }
539
540 static void
541 match_DspSimpleText(void)
542 {                               /* 03 80 */
543   match_byte_assert(3);
544   match_byte_assert(0x80);
545   match_byte_assert(0);
546   if (match_byte(0))
547     {
548       match_zeros_assert(3);
549       if (!match_byte(0x10))
550         match_byte_assert(0);
551       match_zeros_assert(4);
552     }
553 }
554
555 static void
556 parse_weirdness(void)
557 {
558   match_byte_assert(1);
559   get_u32();
560   match_zeros_assert(12);
561   pos++;                        /* 90 or BC */
562   if (!match_byte(2))
563     match_byte_assert(1);
564   match_zeros_assert(5);
565   pos++;
566   match_zeros_assert(3);
567   puts(get_padded_string(32));
568 }
569
570 static void
571 match_NavTreeViewItem(void)
572 {                               /* 07 80 */
573   match_byte_assert(7);
574   match_byte_assert(0x80);
575   match_zeros_assert(1);
576   if (!match_byte(0) && !match_byte(7) && !match_byte(2))
577     match_byte_assert(8);
578   match_zeros_assert(3);
579   pos++;
580   match_byte_assert(0);
581   match_byte_assert(1);
582   match_byte_assert(0);
583   if (match_byte(0))
584     {
585       match_byte_assert(0);
586       if (!match_byte(1))
587         match_byte_assert(0);
588       match_zeros_assert(5);
589       if (!match_byte(0))
590         match_byte_assert(1);
591       match_zeros_assert(5);
592       get_string1();
593       if (match_byte(1))
594         {
595           if (data[pos] == 1)
596             {
597               parse_weirdness();
598               match_byte_assert(0);
599               pos++;
600               match_zeros_assert(11);
601               match_byte_assert(1);
602               match_zeros_assert(3);
603               get_string4();
604               match_byte_assert(0);
605               if (match_byte(0))
606                 {
607                   match_zeros_assert(2);
608                   if (match_u32(8500))
609                     match_u32_assert(11000);
610                   else
611                     {
612                       match_u32_assert(11000);
613                       match_u32_assert(8500);
614                     }
615                   pos += 32;
616                   get_string1();
617                   if (!match_byte(0))
618                     match_byte_assert(1);
619                   pos++;
620                   pos++;
621                   pos++;
622                   pos++;
623                   get_string4();                /* page title */
624                   match_byte_assert(1);
625                   match_byte_assert(1);
626                   match_zeros_assert(3);
627                   get_string4();                /* page number */
628                   match_byte_assert(0);
629                   pos += 2;
630                   match_u16_assert(2);
631                 }
632               parse_flexible();
633             }
634         }
635       else
636         match_zeros_assert(3);
637     }
638   //fprintf(stderr, "%#x ", pos - 16);
639 }
640
641 static void
642 parse_DspNumber(void)
643 {
644   match_byte_assert(1);
645   printf("DspNumber(");
646   parse_format();
647   match_byte_assert(0x80);
648   match_byte(2);
649   printf (" %f", get_double());
650   printf (" \"%s\")\n", get_string1());
651 }
652
653 static void
654 match_DspNumber(void)
655 {
656   match_byte_assert(0x2a);
657   match_byte_assert(0x80);
658   parse_DspNumber();
659 }
660
661 static void
662 parse_DspCell(void)
663 {
664   match_byte_assert(0);
665   match_DspSimpleText();
666   parse_flexible();             /* DspString or DspNumber. */
667 }
668
669 static void
670 match_DspCell(void)
671 {                               /* 27 80 */
672   match_byte_assert(0x27);
673   match_byte_assert(0x80);
674   parse_DspCell();
675 }
676
677 static void
678 parse_NavLog(void)
679 {
680   match_byte_assert(2);
681   pos += 32;
682 }
683
684 static void
685 match_NavLog(void)
686 {                               /* 09 80 */
687   match_byte_assert(9);
688   match_byte_assert(0x80);
689   parse_NavLog();
690 }
691
692 static void
693 parse_PMModelItemInfo(void)
694 {                               /* 54 80 */
695   match_byte_assert(0);
696   pos += 1;                     /* Counter */
697   match_zeros_assert(7);
698   pos += 3;
699   if (!match_byte(0))
700     match_byte_assert(0xe);
701   match_byte_assert(0);
702 }
703
704 static void
705 match_PMModelItemInfo(void)
706 {                               /* 54 80 */
707   match_byte_assert(0x54);
708   match_byte_assert(0x80);
709   parse_PMModelItemInfo();
710   match_DspSimpleText();
711   match_DspString();
712 }
713
714 static void
715 match_PMPivotItemTree(void)
716 {                               /* 52 80 */
717   match_byte_assert(0x52);
718   match_byte_assert(0x80);
719   match_byte_assert(0);
720   match_PMModelItemInfo();
721 }
722
723 static void
724 parse_NavHead(void)
725 {
726   match_byte_assert(2);
727   match_zeros_assert(24);
728   match_byte_assert(1);
729   match_zeros_assert(3);
730   if (!match_byte(1))
731     match_byte_assert(0);
732   match_zeros_assert(3);
733   match_DspSimpleText();
734   parse_flexible();
735 }
736
737 static void
738 parse_NavOleItem(void)
739 {
740   match_byte_assert(0);
741   match_byte_assert(1);
742   match_zeros_assert(2);
743   pos++;
744   match_zeros_assert(9);
745   match_byte_assert(1);
746   match_zeros_assert(10);
747   match_byte_assert(1);
748   match_zeros_assert(5);
749   get_string1();
750   match_byte_assert(1);
751   parse_weirdness();
752   match_byte_assert(0);
753   pos++;
754   match_zeros_assert(11);
755   match_byte_assert(1);
756   match_zeros_assert(3);
757   get_string4();
758   match_byte_assert(0);
759 }
760
761 static void
762 match_NavOleItem(void)
763 {                               /* 0e 80 or 12 80*/
764   if (!match_byte(0x12))
765     match_byte_assert(0x0e);
766   match_byte_assert(0x80);
767   parse_NavOleItem();
768 }
769
770 static void
771 parse_NavTitle(void)
772 {
773   match_byte_assert(2);
774   match_zeros_assert(8);
775   match_u32_assert(24);
776   get_u32();
777   pos++;
778   if (!match_byte(3))
779     match_byte_assert(4);
780   match_zeros_assert(2);
781   get_u32();
782   match_u32_assert(2);
783   if (!match_u32(2))
784     match_u32_assert(1);
785 }
786
787 static void
788 parse_NavNote(void)
789 {
790   match_byte_assert(2);
791   match_zeros_assert(8);
792   match_u32_assert(24);
793   if (!match_u32(0) && !match_u32(0xffffff4b))
794     match_u32_assert(-40);
795   pos += 8;
796   match_u32_assert(2);
797   if (!match_u32(2))
798     match_u32_assert(1);
799 }
800
801 static void
802 parse_PTPivotController(void)
803 {
804   match_byte_assert(2);
805   pos += 8;
806   match_u32_assert(100);
807   match_u32_assert(100);
808   match_u32_assert(100);
809   match_u32_assert(100);
810 }
811
812 static void
813 parse_PVPivotView(void)
814 {
815   match_byte_assert(5);
816   match_zeros_assert(4);
817 }
818
819 static void
820 parse_NDimensional__DspCell(void)
821 {
822   match_byte_assert(0);
823   match_u32_assert(1);
824 }
825
826 static void
827 parse_IndexedCollection(void)
828 {
829   match_byte_assert(0);
830   if (match_byte(0))
831     {
832       match_zeros_assert(12);
833     }
834   else
835     {
836       get_u32();
837       match_u16_assert(1);
838     }
839 }
840
841 static void
842 parse_flexible(void)
843 {
844   int start = pos;
845   if (data[pos] == 0xff && data[pos + 1] == 0xff)
846     {
847       match_u16_assert(0xffff);
848       match_u16_assert(0);
849       char *heading = get_string2();
850       if (!strcmp(heading, "DspCell"))
851         parse_DspCell();
852       else if (!strcmp(heading, "DspNumber"))
853         parse_DspNumber();
854       else if (!strcmp(heading, "DspString"))
855         parse_DspString();
856       else if (!strcmp(heading, "NavHead"))
857         parse_NavHead();
858       else if (!strcmp(heading, "IndexedCollection"))
859         parse_IndexedCollection();
860       else if (!strcmp(heading, "NavOleItem"))
861         parse_NavOleItem();
862       else if (!strcmp(heading, "NavTitle"))
863         parse_NavTitle();
864       else if (!strcmp(heading, "NavNote"))
865         parse_NavNote();
866       else if (!strcmp(heading, "PTPivotController"))
867         parse_PTPivotController();
868       else if (!strcmp(heading, "PVPivotView"))
869         parse_PVPivotView();
870       else if (!strcmp(heading, "PMPivotModel"))
871         match_byte_assert(3);
872       else if (!strcmp(heading, "NDimensional__DspCell"))
873         parse_NDimensional__DspCell();
874       else if (!strcmp(heading, "PMPivotItemTree"))
875         match_byte_assert(0);
876       else if (!strcmp(heading, "PMModelItemInfo"))
877         parse_PMModelItemInfo();
878       else if (!strcmp(heading, "AbstractTreeBranch"))
879         match_byte_assert(0);
880       else
881         {
882           fprintf(stderr, "don't know %s at offset 0x%x: ", heading, start);
883           hex_dump(stderr, pos, 64);
884           assert(0);
885         }
886     }
887   else if (data[pos + 1] == 0x80)
888     {
889       if (data[pos] == 0x2a && data[pos + 1] == 0x80)
890         match_DspNumber();
891       else if (data[pos] == 0x27 && data[pos + 1] == 0x80)
892         match_DspCell();
893       else if (data[pos] == 0x5 && data[pos + 1] == 0x80)
894         match_DspString();
895       else if (data[pos] == 0x7 && data[pos + 1] == 0x80)
896         match_NavTreeViewItem();
897       else if (data[pos] == 0x3 && data[pos + 1] == 0x80)
898         match_DspSimpleText();
899       else if ((data[pos] == 0x3c || data[pos] == 0x39)
900                && data[pos + 1] == 0x80)
901         {
902           /* 3c 80 */
903           /* 39 80 */
904           pos += 2;
905           parse_format();
906 /*      match_byte_assert(0x01);
907         match_byte_assert(0x02);
908         match_byte_assert(0x0d); */
909         }
910       else if (data[pos] == 0x15 && data[pos + 1] == 0x80)
911         {
912           /* 15 80 */
913           pos += 2;
914           if (match_byte(2))
915             {
916               printf ("15 80(%f", get_double());
917               printf (" %s)\n", get_string1());
918               if (match_byte(1))
919                 {
920                   match_byte_assert(0);
921                   get_string1();
922                   if (!match_byte(2) && !match_byte(3))
923                     match_byte_assert(0);
924                   match_zeros_assert(3);
925                   get_string1();
926                   match_byte_assert(0);
927                   match_byte_assert(1);
928                   match_zeros_assert(3);
929                   match_byte_assert(1);
930                   match_byte_assert(0);
931                 }
932             }
933           else
934             {
935               match_byte_assert(0);
936               if (match_u32(0xc))
937                 match_u16_assert(1);
938               else
939                 match_zeros_assert(13);
940             }
941         }
942       else if (data[pos] == 0x9 && data[pos + 1] == 0x80)
943         {
944           match_NavLog();
945         }
946       else if (data[pos] == 0xe || data[pos] == 0x12)
947         match_NavOleItem();
948       else if (data[pos] == 0x11 || data[pos] == 0x13)
949         {
950           pos += 2;
951           match_byte_assert(0);
952           if (match_u32(0xc) || match_u32(0xd))
953             match_u16_assert(1);
954           else
955             match_zeros_assert(13);
956         }
957       else if (data[pos] == 0x29 ||
958                data[pos] == 0x2b ||
959                data[pos] == 0x2d ||
960                data[pos] == 0x31 ||
961                data[pos] == 0x32 ||
962                data[pos] == 0x4a ||
963                data[pos] == 0x4c ||
964                data[pos] == 0x4f ||
965                data[pos] == 0x4d ||
966                data[pos] == 0x50 ||
967                data[pos] == 0x36 ||
968                data[pos] == 0x52 ||
969                data[pos] == 0x53 ||
970                data[pos] == 0x54 ||
971                data[pos] == 0x55 ||
972                data[pos] == 0x57 ||
973                data[pos] == 0x56 ||
974                data[pos] == 0x58 ||
975                data[pos] == 0x5c ||
976                data[pos] == 0x5b ||
977                data[pos] == 0x5e ||
978                data[pos] == 0x62 ||
979                data[pos] == 0x64 ||
980                data[pos] == 0x4e ||
981                data[pos] == 0x51 ||
982                data[pos] == 0x59 ||
983                data[pos] == 0x5a ||
984                data[pos] == 0x5d ||
985                data[pos] == 0x66 ||
986                data[pos] == 0x60 ||
987                data[pos] == 0x68 ||
988                data[pos] == 0x48 ||
989                data[pos] == 0x6a ||
990                data[pos] == 0x37)
991         {
992           pos += 2;
993           match_byte_assert(0);
994         }
995       else if (data[pos] == 0x2c ||
996                data[pos] == 0x2e ||
997                data[pos] == 0x30 ||
998                data[pos] == 0x34 ||
999                data[pos] == 0x3d ||
1000                data[pos] == 0x40 ||
1001                data[pos] == 0x3f ||
1002                data[pos] == 0x42 ||
1003                data[pos] == 0x43 ||
1004                data[pos] == 0x44 ||
1005                data[pos] == 0x49 ||
1006                data[pos] == 0x3e ||
1007                data[pos] == 0x46)
1008         {
1009           pos += 2;
1010           parse_format();
1011         }
1012       else
1013         {
1014 #if 0
1015           fprintf (stderr, "bad record 0x%02x at offset %x: ",
1016                    data[pos], pos);
1017 #endif
1018           hex_dump (stderr, pos, 64);
1019           assert(0);
1020         }
1021     }
1022   else if (match_byte(0xa)) 
1023     {
1024       match_byte_assert(0);
1025       if (match_u16(0x0e74))
1026         match_byte_assert(0);
1027       else
1028         {
1029           match_zeros_assert(4);
1030           assert(pos == n);
1031           exit (0);
1032         }
1033     }
1034   else if (match_byte(1))
1035     {
1036       match_byte_assert(0);
1037       get_string1();
1038       if (!match_byte(2))
1039         match_byte_assert(0);
1040       match_zeros_assert(3);
1041       get_string1();
1042       if (match_byte(0x08))
1043         {
1044           match_byte_assert(0);
1045           match_u16_assert(0x0e74);
1046           match_byte_assert(0);
1047         }
1048       else if (match_byte(3))
1049         {
1050           match_byte_assert(0);
1051           if (match_u16(0x0e74))
1052             match_byte_assert(0);
1053           else
1054             {
1055               match_zeros_assert(6);
1056               if (!match_byte(0xe) && !match_byte(0x11))
1057                 match_byte_assert(0);
1058               match_byte_assert(0);
1059               if (!match_u16(0x0e74))
1060                 match_u16_assert(0);
1061               match_byte_assert(0);
1062             }
1063         }
1064       else
1065         {
1066           match_byte_assert(0);
1067           match_byte_assert(1);
1068           match_zeros_assert(3);
1069           match_byte_assert(1);
1070           match_byte_assert(0);
1071         }
1072     }
1073   else if (match_byte(7) || match_byte(4) || match_byte(5) || match_byte(6) || match_byte(8) || match_byte(9) || match_byte(0xb) || match_byte(0xc))
1074     {
1075       match_byte_assert(0);
1076       if (!match_u16(0x0e74))
1077         match_byte_assert(0);
1078       match_byte_assert(0);
1079     }
1080   else if (match_byte(2) || match_byte(3))
1081     {
1082       match_byte_assert(0);
1083       if (!match_u16(0x0e74))
1084         {
1085           match_zeros_assert(6);
1086           if (match_byte(0))
1087             match_zeros_assert(4);
1088           else
1089             {
1090               pos++;
1091               match_byte(0);
1092               match_u16_assert(0x0e74);
1093             }
1094         }
1095       //match_byte_assert(0);
1096     }
1097   else
1098     {
1099       //fprintf (stderr, "bad record start at offset %x: ", pos);
1100       hex_dump (stderr, pos, 64);
1101       assert(0);
1102     }
1103 }
1104
1105
1106
1107 int
1108 main(int argc, char *argv[])
1109 {
1110   bool print_offsets = false;
1111   for (;;)
1112     {
1113       int c = getopt (argc, argv, "o");
1114       if (c == -1)
1115         break;
1116
1117       switch (c)
1118         {
1119         case 'o':
1120           print_offsets = true;
1121           break;
1122
1123         case '?':
1124           exit (-1);
1125         }
1126     }
1127   if (argc - optind != 1)
1128     {
1129       fprintf (stderr, "usage: %s FILE.bin", argv[0]);
1130       exit (1);
1131     }
1132
1133   const char *filename = argv[optind];
1134   int fd = open(filename, O_RDONLY);
1135   if (fd < 0)
1136     {
1137       fprintf (stderr, "%s: open failed (%s)", filename, strerror (errno));
1138       exit (1);
1139     }
1140
1141   struct stat s;
1142   if (fstat(fd, &s))
1143     {
1144       perror("fstat");
1145       exit(1);
1146     }
1147   n = s.st_size;
1148   data = malloc(n + 256);
1149   if (!data)
1150     {
1151       perror("malloc");
1152       exit(1);
1153     }
1154   if (read(fd, data, n) != n)
1155     {
1156       perror("read");
1157       exit(1);
1158     }
1159   for (int i = 0; i < 256; i++)
1160     data[n + i] = i % 2 ? 0xaa : 0x55;
1161   close(fd);
1162
1163   setvbuf (stdout, NULL, _IOLBF, 0);
1164
1165   match_byte_assert(4);
1166   match_u32_assert(0);
1167   match_string1_assert("SPSS Output Document");
1168   match_u32_assert(1);
1169   match_byte_assert(0x63);
1170
1171   parse_heading("NavRoot");
1172   match_byte_assert(2);
1173   match_zeros_assert(32);
1174
1175   parse_heading("DspSimpleText");
1176   match_zeros_assert(10);
1177
1178   parse_heading("DspString");
1179   parse_DspString();
1180
1181   parse_heading("NavTreeViewItem");
1182   match_byte_assert(0);
1183   if (!match_u32(1))
1184     match_u32_assert(0);
1185   match_byte_assert(2);
1186   match_byte_assert(0);
1187   match_byte_assert(1);
1188   match_zeros_assert(9);
1189   match_u32_assert(1);
1190
1191   match_u32_assert(0);
1192   match_u32_assert(0x18);
1193   if (!match_u32(0))
1194     match_u32_assert(0xffffffd8);
1195   match_u32_assert(0xffffffde);
1196   match_u32_assert(0x18);
1197   if (!match_u32(0))
1198     match_u32_assert(0xffffffd8);
1199   match_u32_assert(0x28);
1200   match_u32_assert(0x28);
1201   pos += 8;
1202   if (data[pos] == 0)
1203     {
1204       match_zeros_assert(5);
1205
1206       if (match_u32(8500))
1207         match_u32_assert(11000);
1208       else
1209         {
1210           match_u32_assert(11000);
1211           match_u32_assert(8500);
1212         }
1213       pos += 32;
1214       get_string1();
1215       if (!match_byte(0))
1216         match_byte_assert(1);
1217       pos++;
1218       pos++;
1219       pos++;
1220       pos++;
1221       get_string4();                /* page title */
1222       match_byte_assert(1);
1223       match_byte_assert(1);
1224       match_zeros_assert(3);
1225       get_string4();                /* page number */
1226       match_byte_assert(0);
1227       pos += 2;
1228       match_u16_assert(2);
1229     }
1230
1231   if (data[pos + 9] != 'L')
1232     exit(0);
1233   parse_heading("NavLog");
1234   parse_NavLog();
1235   for (;;)
1236     {
1237       if (data[pos] == 0)
1238         pos++;
1239       else
1240         parse_flexible();
1241     }
1242   exit(0);
1243   puts(get_padded_string(32));
1244   if (!match_u32(80))
1245     match_u32_assert(132);
1246   match_zeros_assert(8);
1247   match_u32_assert(1);
1248   printf ("0x%x\n", pos);
1249   get_string4();
1250   match_byte_assert(0);
1251
1252   parse_heading("NavHead");
1253   parse_NavHead();
1254   match_NavTreeViewItem();
1255   match_zeros_assert(3);
1256
1257   parse_heading("NavTitle");
1258   pos += 33;
1259   match_DspSimpleText();
1260   match_DspString();
1261   match_NavTreeViewItem();
1262
1263   match_byte_assert(1);
1264   match_byte_assert(1);
1265   match_u32_assert(-19);
1266   match_zeros_assert(12);
1267   match_byte_assert(0xbc);
1268   match_byte_assert(2);
1269   match_zeros_assert(9);
1270   match_byte_assert(0x22);
1271   puts(get_padded_string(32));
1272   match_u32_assert(80);
1273   match_zeros_assert(8);
1274   match_u32_assert(1);
1275   get_string4();
1276   match_byte_assert(0);
1277
1278   parse_heading("NavNote");
1279   match_byte_assert(2);
1280   match_zeros_assert(8);
1281   match_u32_assert(24);
1282   if (!match_u32(0))
1283     match_u32_assert(-40);
1284   pos += 8;
1285   match_u32_assert(2);
1286   match_u32_assert(1);
1287   match_DspSimpleText();
1288   match_DspString();
1289   match_NavTreeViewItem();
1290   match_byte_assert(1);
1291
1292   parse_heading("PTPivotController");
1293   match_byte_assert(2);
1294   pos += 8;
1295   match_u32_assert(100);
1296   match_u32_assert(100);
1297   match_u32_assert(100);
1298   match_u32_assert(100);
1299
1300   parse_heading("PVPivotView");
1301   match_u32_assert(5);
1302   match_byte_assert(0);
1303
1304   parse_heading("PMPivotModel");
1305   match_byte_assert(3);
1306
1307   parse_heading("NDimensional__DspCell");
1308   match_byte_assert(0);
1309   match_u32_assert(1);
1310
1311   parse_heading("IndexedCollection");
1312   match_byte_assert(0);
1313   pos++;
1314   match_zeros_assert(3);
1315   match_byte_assert(1);
1316   match_byte_assert(0);
1317   match_zeros_assert(7);
1318
1319   while (data[pos] != 1)
1320     {
1321       if (data[pos] == 0)
1322         pos++;
1323       else
1324         parse_flexible();
1325     }
1326
1327   match_byte_assert(1);
1328   match_byte_assert(0);
1329   puts(get_string1());
1330   if (!match_u32(0))
1331     match_u32_assert(2);
1332   puts(get_string1());
1333
1334   match_byte_assert(0);
1335   match_byte_assert(1);
1336   match_byte_assert(0);
1337   match_byte_assert(0);
1338   match_byte_assert(0);
1339   match_byte_assert(1);
1340   match_byte_assert(0);
1341
1342   exit (0);
1343
1344   parse_heading("PMPivotItemTree");
1345   match_byte_assert(0);
1346
1347   parse_heading("AbstractTreeBranch");
1348   match_byte_assert(0);
1349
1350   parse_heading("PMModelItemInfo");
1351   parse_PMModelItemInfo();
1352   match_DspSimpleText();
1353   match_DspString();
1354
1355   match_u32_assert(7);
1356   match_PMPivotItemTree();
1357
1358   match_u32_assert(0);
1359   match_PMPivotItemTree();
1360
1361   match_u32_assert(0);
1362   match_PMPivotItemTree();
1363
1364   match_u32_assert(6);
1365   match_PMPivotItemTree();
1366
1367   match_u32_assert(0);
1368   match_PMPivotItemTree();
1369
1370   match_u32_assert(0);
1371   match_PMPivotItemTree();
1372
1373   match_u32_assert(0);
1374   match_PMPivotItemTree();
1375
1376   match_u32_assert(0);
1377   match_PMPivotItemTree();
1378
1379   match_u32_assert(0);
1380   match_PMPivotItemTree();
1381
1382   match_u32_assert(0);
1383   match_PMPivotItemTree();
1384
1385   match_u32_assert(2);
1386   match_PMPivotItemTree();
1387
1388   match_u32_assert(0);
1389   match_PMPivotItemTree();
1390
1391   match_u32_assert(0);
1392   match_PMPivotItemTree();
1393
1394   match_u32_assert(0);
1395   match_PMPivotItemTree();
1396
1397   match_u32_assert(0);
1398   match_PMPivotItemTree();
1399
1400   match_u32_assert(2);
1401   match_PMPivotItemTree();
1402
1403   match_u32_assert(0);
1404   match_PMPivotItemTree();
1405
1406   match_u32_assert(0);
1407
1408   /* ...unknown... */
1409
1410   while (data[pos] != 0xff || data[pos + 1] != 0xff)
1411     pos++;
1412   parse_heading("PVViewDimension");
1413
1414   int i;
1415   for (i = 0; data[pos + i] != 0xff || data[pos + i + 1] != 0xff; i++)
1416     ;
1417   hex_dump(stdout, pos, i);
1418
1419   printf ("%#x: end of successful parse\n", pos);
1420
1421   return 0;
1422 }