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