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