971fbab8eacf1b3390c5c51d6709d45b531396ee
[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 pull_string(int len, const char *where)
260 {
261   assert (len >= 0);
262   for (int i = 0; i < len - 1; i++)
263     if (!data[pos + i])
264       {
265         fprintf(stderr, "%s: %d-byte string starting at 0x%x has null byte "
266                 "at offset %d: ", where, len, pos, i);
267         hex_dump(stderr, pos, len + 64);
268         exit(1);
269       }
270
271   char *s = xmemdup0(&data[pos], len);
272   pos += len;
273   return s;
274 }
275
276 static char *
277 get_string2(const char *where)
278 {
279   return pull_string(get_u16(), where);
280 }
281 #define get_string2() get_string2(WHERE)
282
283 static char *
284 get_string1(const char *where)
285 {
286   int len = data[pos++];
287   return len == 0xff ? (get_string2)(where) : pull_string(len, where);
288 }
289 #define get_string1() get_string1(WHERE)
290
291 static void
292 match_string1_assert(const char *exp, const char *where)
293 {
294   int start = pos;
295   char *act = (get_string1)(where);
296   if (strcmp(act, exp)) 
297     {
298       fprintf(stderr, "%s: 0x%x: expected \"%s\", got \"%s\"\n",
299               where, start, exp, act);
300       exit(1);
301     }
302 }
303 #define match_string1_assert(x) match_string1_assert(x, WHERE)
304
305 static void
306 match_string2_assert(const char *exp, const char *where)
307 {
308   int start = pos;
309   char *act = (get_string2)(where);
310   if (strcmp(act, exp)) 
311     {
312       fprintf(stderr, "%s: 0x%x: expected \"%s\", got \"%s\"\n",
313               where, start, exp, act);
314       exit(1);
315     }
316 }
317 #define match_string2_assert(x) match_string2_assert(x, WHERE)
318
319 static char *
320 get_string4(const char *where)
321 {
322   assert(data[pos + 3] == 0);
323   return pull_string(get_u32(), where);
324 }
325 #define get_string4() get_string4(WHERE)
326
327 static char *
328 get_padded_string(int len)
329 {
330   char *s = xmemdup0(&data[pos], len);
331   pos += len;
332   return s;
333 }
334
335 static char *
336 get_string_be(const char *where)
337 {
338   if (1
339       /*data[pos + 1] == 0 && data[pos + 2] == 0 && data[pos + 3] == 0*/
340       /*&& all_ascii(&data[pos + 4], data[pos])*/)
341     {
342       int len = data[pos + 2] * 256 + data[pos + 3];
343       char *s = malloc(len + 1);
344
345       memcpy(s, &data[pos + 4], len);
346       s[len] = 0;
347       pos += 4 + len;
348       return s;
349     }
350   else
351     {
352       fprintf(stderr, "%s: 0x%x: expected string\n", where, pos);
353       exit(1);
354     }
355 }
356 #define get_string_be() get_string_be(WHERE)
357
358 static int
359 get_end(void)
360 {
361   int len = get_u32();
362   return pos + len;
363 }
364
365 static void __attribute__((unused))
366 hex_dump(FILE *stream, int ofs, int n)
367 {
368   int n_ascii = 0;
369   for (int i = 0; i < n; i++)
370     {
371       int c = data[ofs + i];
372       n_ascii += is_ascii(c);
373       fprintf(stream, " %02x", c);
374     }
375   if (n_ascii >= 3)
376     {
377       putc(' ', stream);
378       for (int i = 0; i < n; i++)
379         {
380           int c = data[ofs + i];
381           putc(c >= 32 && c < 127 ? c : '.', stream);
382         }
383     }
384   putc('\n', stream);
385 }
386
387 static void __attribute__((unused))
388 char_dump(FILE *stream, int ofs, int n)
389 {
390   for (int i = 0; i < n; i++)
391     {
392       int c = data[ofs + i];
393       putc(c >= 32 && c < 127 ? c : '.', stream);
394     }
395   putc('\n', stream);
396 }
397
398
399 static int
400 compare_int(const void *a_, const void *b_)
401 {
402   const int *a = a_;
403   const int *b = b_;
404   return *a < *b ? -1 : *a > *b;
405 }
406
407
408 static const char *
409 format_name (int format, char *buf)
410 {
411   switch (format)
412     {
413     case 1: return "A";
414     case 2: return "AHEX";
415     case 3: return "COMMA";
416     case 4: return "DOLLAR";
417     case 5: case 40: return "F";
418     case 6: return "IB";
419     case 7: return "PIBHEX";
420     case 8: return "P";
421     case 9: return "PIB";
422     case 10: return "PK";
423     case 11: return "RB";
424     case 12: return "RBHEX";
425     case 15: return "Z";
426     case 16: return "N";
427     case 17: return "E";
428     case 20: return "DATE";
429     case 21: return "TIME";
430     case 22: return "DATETIME";
431     case 23: return "ADATE";
432     case 24: return "JDATE";
433     case 25: return "DTIME";
434     case 26: return "WKDAY";
435     case 27: return "MONTH";
436     case 28: return "MOYR";
437     case 29: return "QYR";
438     case 30: return "WKYR";
439     case 31: return "PCT";
440     case 32: return "DOT";
441     case 33: return "CCA";
442     case 34: return "CCB";
443     case 35: return "CCC";
444     case 36: return "CCD";
445     case 37: return "CCE";
446     case 38: return "EDATE";
447     case 39: return "SDATE";
448     default: sprintf(buf, "(%d)", format); return buf;
449     }
450 }
451
452 static void
453 parse_format(void)
454 {
455   int d = data[pos++];
456   int w = data[pos++];
457   int fmt = data[pos++];
458   char buf[32];
459   printf ("%s%d.%d", format_name(fmt, buf), w, d);
460 }
461
462 static void
463 parse_heading(const char *name)
464 {
465   match_u16_assert(0xffff);
466   match_u16_assert(0);
467   match_string2_assert(name);
468 }
469
470 static void
471 match_zeros_assert(int count, const char *where)
472 {
473   for (int i = 0; i < count; i++)
474     if (data[pos + i])
475       {
476         fprintf (stderr,
477                  "%s: %#x: expected %d zeros here but offset %d is %#"PRIx8": ",
478                  where, pos, count, i, data[pos + i]);
479         hex_dump (stderr, pos, 64);
480         exit (1);
481       }
482   pos += count;
483 }
484 #define match_zeros_assert(count) match_zeros_assert(count, WHERE)
485
486 static void
487 put_safe(const char *s)
488 {
489   while (*s)
490     {
491       if (*s == '\n')
492         printf ("\\n");
493       else if (*s == '\r')
494         printf ("\\r");
495       else if (*s < 0x20 || *s > 0x7e)
496         printf ("\\x%02"PRIx8, (uint8_t) *s);
497       else
498         putchar (*s);
499       s++;
500     }
501 }
502
503 static void parse_flexible(void);
504
505 static void
506 parse_DspString(void)
507 {
508   printf("%#x: DspString(", pos);
509   if (match_byte(2))
510     {
511       printf("%f, \"", get_double());
512       printf("%s\")\n", get_string1());
513     }
514   else
515     {
516       match_byte_assert(1);
517       parse_format();
518       printf(" \"");
519       match_byte_assert(0);
520       match_byte_assert(1);
521       put_safe(get_string1());
522       printf("\")\n");
523     }
524 }
525
526 static void
527 match_DspString(void)
528 {                               /* 05 80 */
529   match_byte_assert(5);
530   match_byte_assert(0x80);
531   parse_DspString();
532 }
533
534 static void
535 match_DspSimpleText(void)
536 {                               /* 03 80 */
537   match_byte_assert(3);
538   match_byte_assert(0x80);
539   match_byte_assert(0);
540   if (match_byte(0))
541     {
542       match_zeros_assert(3);
543       if (!match_byte(0x10))
544         match_byte_assert(0);
545       match_zeros_assert(4);
546     }
547 }
548
549 static void
550 parse_weirdness(void)
551 {
552   match_byte_assert(1);
553   get_u32();
554   match_zeros_assert(12);
555   pos++;                        /* 90 or BC */
556   if (!match_byte(2))
557     match_byte_assert(1);
558   match_zeros_assert(5);
559   pos++;
560   match_zeros_assert(3);
561   puts(get_padded_string(32));
562 }
563
564 static void
565 match_NavTreeViewItem(void)
566 {                               /* 07 80 */
567   match_byte_assert(7);
568   match_byte_assert(0x80);
569   match_zeros_assert(1);
570   if (!match_byte(0) && !match_byte(7) && !match_byte(2))
571     match_byte_assert(8);
572   match_zeros_assert(3);
573   pos++;
574   match_byte_assert(0);
575   match_byte_assert(1);
576   match_byte_assert(0);
577   if (match_byte(0))
578     {
579       match_byte_assert(0);
580       if (!match_byte(1))
581         match_byte_assert(0);
582       match_zeros_assert(5);
583       if (!match_byte(0))
584         match_byte_assert(1);
585       match_zeros_assert(5);
586       get_string1();
587       if (match_byte(1))
588         {
589           if (data[pos] == 1)
590             {
591               parse_weirdness();
592               match_byte_assert(0);
593               pos++;
594               match_zeros_assert(11);
595               match_byte_assert(1);
596               match_zeros_assert(3);
597               get_string4();
598               match_byte_assert(0);
599               if (match_byte(0))
600                 {
601                   match_zeros_assert(2);
602                   if (match_u32(8500))
603                     match_u32_assert(11000);
604                   else
605                     {
606                       match_u32_assert(11000);
607                       match_u32_assert(8500);
608                     }
609                   pos += 32;
610                   get_string1();
611                   if (!match_byte(0))
612                     match_byte_assert(1);
613                   pos++;
614                   pos++;
615                   pos++;
616                   pos++;
617                   get_string4();                /* page title */
618                   match_byte_assert(1);
619                   match_byte_assert(1);
620                   match_zeros_assert(3);
621                   get_string4();                /* page number */
622                   match_byte_assert(0);
623                   pos += 2;
624                   match_u16_assert(2);
625                 }
626               parse_flexible();
627             }
628         }
629       else
630         match_zeros_assert(3);
631     }
632   //fprintf(stderr, "%#x ", pos - 16);
633 }
634
635 static void
636 parse_DspNumber(void)
637 {
638   match_byte_assert(1);
639   printf("DspNumber(");
640   parse_format();
641   match_byte_assert(0x80);
642   match_byte(2);
643   printf (" %f", get_double());
644   printf (" \"%s\")\n", get_string1());
645 }
646
647 static void
648 match_DspNumber(void)
649 {
650   match_byte_assert(0x2a);
651   match_byte_assert(0x80);
652   parse_DspNumber();
653 }
654
655 static void
656 parse_DspCell(void)
657 {
658   match_byte_assert(0);
659   match_DspSimpleText();
660   parse_flexible();             /* DspString or DspNumber. */
661 }
662
663 static void
664 match_DspCell(void)
665 {                               /* 27 80 */
666   match_byte_assert(0x27);
667   match_byte_assert(0x80);
668   parse_DspCell();
669 }
670
671 static void
672 parse_NavLog(void)
673 {
674   match_byte_assert(2);
675   pos += 32;
676 }
677
678 static void
679 match_NavLog(void)
680 {                               /* 09 80 */
681   match_byte_assert(9);
682   match_byte_assert(0x80);
683   parse_NavLog();
684 }
685
686 static void
687 parse_PMModelItemInfo(void)
688 {                               /* 54 80 */
689   match_byte_assert(0);
690   pos += 1;                     /* Counter */
691   match_zeros_assert(7);
692   pos += 3;
693   if (!match_byte(0))
694     match_byte_assert(0xe);
695   match_byte_assert(0);
696 }
697
698 static void
699 match_PMModelItemInfo(void)
700 {                               /* 54 80 */
701   match_byte_assert(0x54);
702   match_byte_assert(0x80);
703   parse_PMModelItemInfo();
704   match_DspSimpleText();
705   match_DspString();
706 }
707
708 static void
709 match_PMPivotItemTree(void)
710 {                               /* 52 80 */
711   match_byte_assert(0x52);
712   match_byte_assert(0x80);
713   match_byte_assert(0);
714   match_PMModelItemInfo();
715 }
716
717 static void
718 parse_NavHead(void)
719 {
720   match_byte_assert(2);
721   match_zeros_assert(24);
722   match_byte_assert(1);
723   match_zeros_assert(3);
724   if (!match_byte(1))
725     match_byte_assert(0);
726   match_zeros_assert(3);
727   match_DspSimpleText();
728   parse_flexible();
729 }
730
731 static void
732 parse_NavOleItem(void)
733 {
734   match_byte_assert(0);
735   match_byte_assert(1);
736   match_zeros_assert(2);
737   pos++;
738   match_zeros_assert(9);
739   match_byte_assert(1);
740   match_zeros_assert(10);
741   match_byte_assert(1);
742   match_zeros_assert(5);
743   get_string1();
744   match_byte_assert(1);
745   parse_weirdness();
746   match_byte_assert(0);
747   pos++;
748   match_zeros_assert(11);
749   match_byte_assert(1);
750   match_zeros_assert(3);
751   get_string4();
752   match_byte_assert(0);
753 }
754
755 static void
756 match_NavOleItem(void)
757 {                               /* 0e 80 or 12 80*/
758   if (!match_byte(0x12))
759     match_byte_assert(0x0e);
760   match_byte_assert(0x80);
761   parse_NavOleItem();
762 }
763
764 static void
765 parse_NavTitle(void)
766 {
767   match_byte_assert(2);
768   match_zeros_assert(8);
769   match_u32_assert(24);
770   get_u32();
771   pos++;
772   if (!match_byte(3))
773     match_byte_assert(4);
774   match_zeros_assert(2);
775   get_u32();
776   match_u32_assert(2);
777   if (!match_u32(2))
778     match_u32_assert(1);
779 }
780
781 static void
782 parse_NavNote(void)
783 {
784   match_byte_assert(2);
785   match_zeros_assert(8);
786   match_u32_assert(24);
787   if (!match_u32(0) && !match_u32(0xffffff4b))
788     match_u32_assert(-40);
789   pos += 8;
790   match_u32_assert(2);
791   if (!match_u32(2))
792     match_u32_assert(1);
793 }
794
795 static void
796 parse_PTPivotController(void)
797 {
798   match_byte_assert(2);
799   pos += 8;
800   match_u32_assert(100);
801   match_u32_assert(100);
802   match_u32_assert(100);
803   match_u32_assert(100);
804 }
805
806 static void
807 parse_PVPivotView(void)
808 {
809   match_byte_assert(5);
810   match_zeros_assert(4);
811 }
812
813 static void
814 parse_NDimensional__DspCell(void)
815 {
816   match_byte_assert(0);
817   match_u32_assert(1);
818 }
819
820 static void
821 parse_IndexedCollection(void)
822 {
823   match_byte_assert(0);
824   if (match_byte(0))
825     {
826       match_zeros_assert(12);
827     }
828   else
829     {
830       get_u32();
831       match_u16_assert(1);
832     }
833 }
834
835 static void
836 parse_PTTableLook(void)
837 {
838   match_byte_assert(2);
839   match_byte_assert(2);
840   match_zeros_assert(7);
841   match_u32_assert(0x36);
842   match_u32_assert(0x12);
843 }
844
845 static void
846 parse_PVViewDimension(void)
847 {
848   while (data[pos + 1] != 0x80
849          && (data[pos] != 0xff || data[pos + 1] != 0xff))
850     {
851       assert(pos < n);
852       pos++;
853     }
854 }
855
856 static void
857 parse_PVSeparatorStyle(void)
858 {
859   match_byte_assert(0);
860   match_byte_assert(1);
861   match_zeros_assert(15);
862   pos++;
863   match_byte_assert(0x80);
864   match_byte_assert(0);
865
866   match_byte_assert(1);
867   match_zeros_assert(9);
868   while (data[pos + 1] != 0x80
869          && (data[pos] != 0xff || data[pos + 1] != 0xff))
870     {
871       assert(pos < n);
872       pos++;
873     }
874 }
875
876 static void
877 parse_PVCellStyle(void)
878 {
879   match_byte_assert(0);
880   match_byte_assert(1);
881   match_zeros_assert(5);
882   match_u32_assert(0xffffff);
883   match_zeros_assert(2);
884 }
885
886 static void
887 parse_flexible(void)
888 {
889   int start = pos;
890   if (match_u16(0xffff))
891     {
892       match_u16_assert(0);
893       char *heading = get_string2();
894       if (!strcmp(heading, "DspCell"))
895         parse_DspCell();
896       else if (!strcmp(heading, "DspNumber"))
897         parse_DspNumber();
898       else if (!strcmp(heading, "DspString"))
899         parse_DspString();
900       else if (!strcmp(heading, "NavHead"))
901         parse_NavHead();
902       else if (!strcmp(heading, "IndexedCollection"))
903         parse_IndexedCollection();
904       else if (!strcmp(heading, "NavOleItem"))
905         parse_NavOleItem();
906       else if (!strcmp(heading, "NavTitle"))
907         parse_NavTitle();
908       else if (!strcmp(heading, "NavNote"))
909         parse_NavNote();
910       else if (!strcmp(heading, "PTPivotController"))
911         parse_PTPivotController();
912       else if (!strcmp(heading, "PVPivotView"))
913         parse_PVPivotView();
914       else if (!strcmp(heading, "PMPivotModel"))
915         match_byte_assert(3);
916       else if (!strcmp(heading, "NDimensional__DspCell"))
917         parse_NDimensional__DspCell();
918       else if (!strcmp(heading, "PMPivotItemTree"))
919         match_byte_assert(0);
920       else if (!strcmp(heading, "PMModelItemInfo"))
921         parse_PMModelItemInfo();
922       else if (!strcmp(heading, "AbstractTreeBranch"))
923         match_byte_assert(0);
924       else if (!strcmp(heading, "PTTableLook"))
925         parse_PTTableLook();
926       else if (!strcmp(heading, "PVViewDimension"))
927         parse_PVViewDimension();
928       else if (!strcmp(heading, "PVSeparatorStyle"))
929         parse_PVSeparatorStyle();
930       else if (!strcmp(heading, "PVCellStyle"))
931         parse_PVCellStyle();
932       else
933         {
934           fprintf(stderr, "don't know %s at offset 0x%x: ", heading, start);
935           hex_dump(stderr, pos, 128);
936           assert(0);
937         }
938     }
939   else if (data[pos + 1] == 0x80)
940     {
941       if (data[pos] == 0x2a && data[pos + 1] == 0x80)
942         match_DspNumber();
943       else if (data[pos] == 0x27 && data[pos + 1] == 0x80)
944         match_DspCell();
945       else if (data[pos] == 0x5 && data[pos + 1] == 0x80)
946         match_DspString();
947       else if (data[pos] == 0x7 && data[pos + 1] == 0x80)
948         match_NavTreeViewItem();
949       else if (data[pos] == 0x3 && data[pos + 1] == 0x80)
950         match_DspSimpleText();
951       else if ((data[pos] == 0x3c || data[pos] == 0x39)
952                && data[pos + 1] == 0x80)
953         {
954           /* 3c 80 */
955           /* 39 80 */
956           pos += 2;
957           parse_format();
958 /*      match_byte_assert(0x01);
959         match_byte_assert(0x02);
960         match_byte_assert(0x0d); */
961         }
962       else if (data[pos] == 0x15 && data[pos + 1] == 0x80)
963         {
964           /* 15 80 */
965           pos += 2;
966           if (match_byte(2))
967             {
968               printf ("15 80(%f", get_double());
969               printf (" %s)\n", get_string1());
970               if (match_byte(1))
971                 {
972                   match_byte_assert(0);
973                   get_string1();
974                   if (!match_byte(2) && !match_byte(3))
975                     match_byte_assert(0);
976                   match_zeros_assert(3);
977                   get_string1();
978                   match_byte_assert(0);
979                   match_byte_assert(1);
980                   match_zeros_assert(3);
981                   match_byte_assert(1);
982                   match_byte_assert(0);
983                 }
984             }
985           else
986             {
987               match_byte_assert(0);
988               if (match_u32(0xc))
989                 match_u16_assert(1);
990               else
991                 match_zeros_assert(13);
992             }
993         }
994       else if (data[pos] == 0x9 && data[pos + 1] == 0x80)
995         {
996           match_NavLog();
997         }
998       else if (data[pos] == 0xe || data[pos] == 0x12)
999         match_NavOleItem();
1000       else if (data[pos] == 0x11 || data[pos] == 0x13)
1001         {
1002           pos += 2;
1003           match_byte_assert(0);
1004           if (match_u32(0xc) || match_u32(0xd))
1005             match_u16_assert(1);
1006           else
1007             match_zeros_assert(13);
1008         }
1009       else if (data[pos] == 0x29 ||
1010                data[pos] == 0x2b ||
1011                data[pos] == 0x2d ||
1012                data[pos] == 0x31 ||
1013                data[pos] == 0x32 ||
1014                data[pos] == 0x4a ||
1015                data[pos] == 0x4c ||
1016                data[pos] == 0x4f ||
1017                data[pos] == 0x4d ||
1018                data[pos] == 0x50 ||
1019                data[pos] == 0x36 ||
1020                data[pos] == 0x52 ||
1021                data[pos] == 0x53 ||
1022                data[pos] == 0x54 ||
1023                data[pos] == 0x55 ||
1024                data[pos] == 0x57 ||
1025                data[pos] == 0x56 ||
1026                data[pos] == 0x58 ||
1027                data[pos] == 0x5c ||
1028                data[pos] == 0x5b ||
1029                data[pos] == 0x5e ||
1030                data[pos] == 0x62 ||
1031                data[pos] == 0x64 ||
1032                data[pos] == 0x4e ||
1033                data[pos] == 0x51 ||
1034                data[pos] == 0x59 ||
1035                data[pos] == 0x5a ||
1036                data[pos] == 0x5d ||
1037                data[pos] == 0x66 ||
1038                data[pos] == 0x60 ||
1039                data[pos] == 0x68 ||
1040                data[pos] == 0x48 ||
1041                data[pos] == 0x6a ||
1042                data[pos] == 0x37)
1043         {
1044           pos += 2;
1045           match_byte_assert(0);
1046         }
1047       else if (data[pos] == 0x2c ||
1048                data[pos] == 0x2e ||
1049                data[pos] == 0x30 ||
1050                data[pos] == 0x34 ||
1051                data[pos] == 0x3d ||
1052                data[pos] == 0x40 ||
1053                data[pos] == 0x3f ||
1054                data[pos] == 0x42 ||
1055                data[pos] == 0x43 ||
1056                data[pos] == 0x44 ||
1057                data[pos] == 0x49 ||
1058                data[pos] == 0x3e ||
1059                data[pos] == 0x46)
1060         {
1061           pos += 2;
1062           parse_format();
1063         }
1064       else
1065         {
1066 #if 0
1067           fprintf (stderr, "bad record 0x%02x at offset %x: ",
1068                    data[pos], pos);
1069 #endif
1070           hex_dump (stderr, pos, 64);
1071           assert(0);
1072         }
1073     }
1074   else if (match_byte(0xa)) 
1075     {
1076       if (!match_byte(7))
1077         match_byte_assert(0);
1078       if (match_u16(0x0e74))
1079         match_byte_assert(0);
1080       else
1081         {
1082           match_zeros_assert(4);
1083           if (pos == n)
1084             exit (0);
1085           match_zeros_assert (2);
1086         }
1087     }
1088 #if 0
1089   else if (match_byte(1))
1090     {
1091       match_byte_assert(0);
1092       get_string1();
1093       if (!match_byte(2))
1094         match_byte_assert(0);
1095       if (match_byte(0))
1096         {
1097           match_zeros_assert(2);
1098           get_string1();
1099           if (match_byte(0x08))
1100             {
1101               match_byte_assert(0);
1102               match_u16_assert(0x0e74);
1103               match_byte_assert(0);
1104             }
1105           else if (match_byte(3))
1106             {
1107               match_byte_assert(0);
1108               if (match_u16(0x0e74))
1109                 match_byte_assert(0);
1110               else
1111                 {
1112                   match_zeros_assert(6);
1113                   if (!match_byte(0xe) && !match_byte(0x11))
1114                     match_byte_assert(0);
1115                   match_byte_assert(0);
1116                   if (!match_u16(0x0e74))
1117                     match_u16_assert(0);
1118                   match_byte_assert(0);
1119                 }
1120             }
1121           else
1122             {
1123               match_byte_assert(0);
1124               match_byte_assert(1);
1125               match_zeros_assert(3);
1126               match_byte_assert(1);
1127               match_byte_assert(0);
1128             }
1129         }
1130     }
1131 #endif
1132   else //if (match_u16(2) || match_u16(3) || match_u16(4) || match_u16(5) || match_u16(6) || match_u16(7) || match_u16(8) || match_u16(9))
1133     {
1134       while (data[pos + 1] != 0x80
1135              && (data[pos] != 0xff || data[pos + 1] != 0xff))
1136         {
1137           assert(pos < n);
1138           pos++;
1139         }
1140     }
1141 #if 0
1142   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) || match_byte(0x15) || match_byte(0x16) || match_byte(0x17) || match_byte(0x18) || match_byte(0x1e)  || match_byte(0x1a))
1143     {
1144       if (!match_byte(7))
1145         match_byte_assert(0);
1146       if (!match_u16(0x0e74))
1147         match_byte_assert(0);
1148       match_byte_assert(0);
1149     }
1150   else if (match_byte(2) || match_byte(3))
1151     {
1152       match_byte_assert(0);
1153       if (!match_u16(0x0e74))
1154         {
1155           match_zeros_assert(2);
1156           if (match_byte(0))
1157             {
1158               match_zeros_assert(3);
1159               if (match_byte(0))
1160                 match_zeros_assert(4);
1161               else
1162                 {
1163                   pos++;
1164                   match_byte(0);
1165                   match_u16_assert(0x0e74);
1166                 }
1167             }
1168         }
1169       //match_byte_assert(0);
1170     }
1171   else if (match_byte(0xd) || match_byte(0xe) || match_byte(0xf)
1172            || match_byte(0x11) || match_byte(0x12) || match_byte(0x13)
1173            || match_byte(0x14) || match_byte(0x1b))
1174     {
1175       if (!match_byte(0x07))
1176         match_byte_assert(0);
1177       if (!match_u16(0x0e74))
1178         match_zeros_assert(11);
1179       else
1180         match_byte_assert(0);
1181     }
1182   else if (match_byte(0xe3) || match_byte(0xdb) || match_byte(0xd8) || match_byte(0xe9) || match_byte(0xf3))
1183     {
1184       match_byte_assert(0x0e);
1185       match_byte_assert(0x74);
1186       match_byte_assert(0x0e);
1187       match_byte_assert(0);
1188     }
1189   else if (match_byte(0x9d) || match_byte(0x9e) || match_byte(0x9c))
1190     match_u32_assert(0x000e741a);
1191   else if (match_byte(0x10))
1192     {
1193       match_byte_assert(0);
1194       if (match_byte(0))
1195         match_zeros_assert(10);
1196       else
1197         {
1198           match_u16_assert(0x0e74);
1199           match_byte_assert(0);
1200         }
1201     }
1202   else if (match_byte(0x39) || match_byte(0x3a) || match_byte(0x3b))
1203     match_u32_assert(0x000e7409);
1204   else
1205     {
1206       //fprintf (stderr, "bad record start at offset %x: ", pos);
1207       hex_dump (stderr, pos, 64);
1208       assert(0);
1209     }
1210 #endif
1211 }
1212
1213
1214
1215 int
1216 main(int argc, char *argv[])
1217 {
1218   bool print_offsets = false;
1219   for (;;)
1220     {
1221       int c = getopt (argc, argv, "o");
1222       if (c == -1)
1223         break;
1224
1225       switch (c)
1226         {
1227         case 'o':
1228           print_offsets = true;
1229           break;
1230
1231         case '?':
1232           exit (-1);
1233         }
1234     }
1235   if (argc - optind != 1)
1236     {
1237       fprintf (stderr, "usage: %s FILE.bin", argv[0]);
1238       exit (1);
1239     }
1240
1241   const char *filename = argv[optind];
1242   int fd = open(filename, O_RDONLY);
1243   if (fd < 0)
1244     {
1245       fprintf (stderr, "%s: open failed (%s)", filename, strerror (errno));
1246       exit (1);
1247     }
1248
1249   struct stat s;
1250   if (fstat(fd, &s))
1251     {
1252       perror("fstat");
1253       exit(1);
1254     }
1255   n = s.st_size;
1256   data = malloc(n + 256);
1257   if (!data)
1258     {
1259       perror("malloc");
1260       exit(1);
1261     }
1262   if (read(fd, data, n) != n)
1263     {
1264       perror("read");
1265       exit(1);
1266     }
1267   for (int i = 0; i < 256; i++)
1268     data[n + i] = i % 2 ? 0xaa : 0x55;
1269   close(fd);
1270
1271   setvbuf (stdout, NULL, _IOLBF, 0);
1272
1273   match_byte_assert(4);
1274   match_u32_assert(0);
1275   match_string1_assert("SPSS Output Document");
1276   match_u32_assert(1);
1277   match_byte_assert(0x63);
1278
1279   parse_heading("NavRoot");
1280   match_byte_assert(2);
1281   match_zeros_assert(32);
1282
1283   parse_heading("DspSimpleText");
1284   match_zeros_assert(10);
1285
1286   parse_heading("DspString");
1287   parse_DspString();
1288
1289   parse_heading("NavTreeViewItem");
1290   match_byte_assert(0);
1291   if (!match_u32(1))
1292     match_u32_assert(0);
1293   match_byte_assert(2);
1294   match_byte_assert(0);
1295   match_byte_assert(1);
1296   match_zeros_assert(9);
1297   match_u32_assert(1);
1298
1299   match_u32_assert(0);
1300   match_u32_assert(0x18);
1301   if (!match_u32(0))
1302     match_u32_assert(0xffffffd8);
1303   match_u32_assert(0xffffffde);
1304   match_u32_assert(0x18);
1305   if (!match_u32(0))
1306     match_u32_assert(0xffffffd8);
1307   match_u32_assert(0x28);
1308   match_u32_assert(0x28);
1309   pos += 8;
1310   if (data[pos] == 0)
1311     {
1312       match_zeros_assert(5);
1313
1314       if (match_u32(8500))
1315         match_u32_assert(11000);
1316       else
1317         {
1318           match_u32_assert(11000);
1319           match_u32_assert(8500);
1320         }
1321       pos += 32;
1322       get_string1();
1323       if (!match_byte(0))
1324         match_byte_assert(1);
1325       pos++;
1326       pos++;
1327       pos++;
1328       pos++;
1329       get_string4();                /* page title */
1330       match_byte_assert(1);
1331       match_byte_assert(1);
1332       match_zeros_assert(3);
1333       get_string4();                /* page number */
1334       match_byte_assert(0);
1335       pos += 2;
1336       match_u16_assert(2);
1337     }
1338
1339   if (data[pos + 9] != 'L')
1340     exit(0);
1341   parse_heading("NavLog");
1342   parse_NavLog();
1343   for (;;)
1344     {
1345       if (data[pos] == 0)
1346         pos++;
1347       else
1348         parse_flexible();
1349     }
1350   exit(0);
1351   puts(get_padded_string(32));
1352   if (!match_u32(80))
1353     match_u32_assert(132);
1354   match_zeros_assert(8);
1355   match_u32_assert(1);
1356   printf ("0x%x\n", pos);
1357   get_string4();
1358   match_byte_assert(0);
1359
1360   parse_heading("NavHead");
1361   parse_NavHead();
1362   match_NavTreeViewItem();
1363   match_zeros_assert(3);
1364
1365   parse_heading("NavTitle");
1366   pos += 33;
1367   match_DspSimpleText();
1368   match_DspString();
1369   match_NavTreeViewItem();
1370
1371   match_byte_assert(1);
1372   match_byte_assert(1);
1373   match_u32_assert(-19);
1374   match_zeros_assert(12);
1375   match_byte_assert(0xbc);
1376   match_byte_assert(2);
1377   match_zeros_assert(9);
1378   match_byte_assert(0x22);
1379   puts(get_padded_string(32));
1380   match_u32_assert(80);
1381   match_zeros_assert(8);
1382   match_u32_assert(1);
1383   get_string4();
1384   match_byte_assert(0);
1385
1386   parse_heading("NavNote");
1387   match_byte_assert(2);
1388   match_zeros_assert(8);
1389   match_u32_assert(24);
1390   if (!match_u32(0))
1391     match_u32_assert(-40);
1392   pos += 8;
1393   match_u32_assert(2);
1394   match_u32_assert(1);
1395   match_DspSimpleText();
1396   match_DspString();
1397   match_NavTreeViewItem();
1398   match_byte_assert(1);
1399
1400   parse_heading("PTPivotController");
1401   match_byte_assert(2);
1402   pos += 8;
1403   match_u32_assert(100);
1404   match_u32_assert(100);
1405   match_u32_assert(100);
1406   match_u32_assert(100);
1407
1408   parse_heading("PVPivotView");
1409   match_u32_assert(5);
1410   match_byte_assert(0);
1411
1412   parse_heading("PMPivotModel");
1413   match_byte_assert(3);
1414
1415   parse_heading("NDimensional__DspCell");
1416   match_byte_assert(0);
1417   match_u32_assert(1);
1418
1419   parse_heading("IndexedCollection");
1420   match_byte_assert(0);
1421   pos++;
1422   match_zeros_assert(3);
1423   match_byte_assert(1);
1424   match_byte_assert(0);
1425   match_zeros_assert(7);
1426
1427   while (data[pos] != 1)
1428     {
1429       if (data[pos] == 0)
1430         pos++;
1431       else
1432         parse_flexible();
1433     }
1434
1435   match_byte_assert(1);
1436   match_byte_assert(0);
1437   puts(get_string1());
1438   if (!match_u32(0))
1439     match_u32_assert(2);
1440   puts(get_string1());
1441
1442   match_byte_assert(0);
1443   match_byte_assert(1);
1444   match_byte_assert(0);
1445   match_byte_assert(0);
1446   match_byte_assert(0);
1447   match_byte_assert(1);
1448   match_byte_assert(0);
1449
1450   exit (0);
1451
1452   parse_heading("PMPivotItemTree");
1453   match_byte_assert(0);
1454
1455   parse_heading("AbstractTreeBranch");
1456   match_byte_assert(0);
1457
1458   parse_heading("PMModelItemInfo");
1459   parse_PMModelItemInfo();
1460   match_DspSimpleText();
1461   match_DspString();
1462
1463   match_u32_assert(7);
1464   match_PMPivotItemTree();
1465
1466   match_u32_assert(0);
1467   match_PMPivotItemTree();
1468
1469   match_u32_assert(0);
1470   match_PMPivotItemTree();
1471
1472   match_u32_assert(6);
1473   match_PMPivotItemTree();
1474
1475   match_u32_assert(0);
1476   match_PMPivotItemTree();
1477
1478   match_u32_assert(0);
1479   match_PMPivotItemTree();
1480
1481   match_u32_assert(0);
1482   match_PMPivotItemTree();
1483
1484   match_u32_assert(0);
1485   match_PMPivotItemTree();
1486
1487   match_u32_assert(0);
1488   match_PMPivotItemTree();
1489
1490   match_u32_assert(0);
1491   match_PMPivotItemTree();
1492
1493   match_u32_assert(2);
1494   match_PMPivotItemTree();
1495
1496   match_u32_assert(0);
1497   match_PMPivotItemTree();
1498
1499   match_u32_assert(0);
1500   match_PMPivotItemTree();
1501
1502   match_u32_assert(0);
1503   match_PMPivotItemTree();
1504
1505   match_u32_assert(0);
1506   match_PMPivotItemTree();
1507
1508   match_u32_assert(2);
1509   match_PMPivotItemTree();
1510
1511   match_u32_assert(0);
1512   match_PMPivotItemTree();
1513
1514   match_u32_assert(0);
1515
1516   /* ...unknown... */
1517
1518   while (data[pos] != 0xff || data[pos + 1] != 0xff)
1519     pos++;
1520   parse_heading("PVViewDimension");
1521
1522   int i;
1523   for (i = 0; data[pos + i] != 0xff || data[pos + i + 1] != 0xff; i++)
1524     ;
1525   hex_dump(stdout, pos, i);
1526
1527   printf ("%#x: end of successful parse\n", pos);
1528
1529   return 0;
1530 }