successfully parse trhough the model on descriptives-only, correlations-only
[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 j)
707 {
708   get_u16(); match_byte_assert(0);
709   get_u16(); match_byte_assert(0);
710   match_u32_assert(j);
711   match_u32_assert(0);
712   if (get_u16() == 0xffff)
713     match_u16_assert(0xffff);
714   else
715     match_u16_assert(0x0e74);
716   match_byte_assert(0);
717   match_DspSimpleText();
718   match_DspString();
719   match_u32_assert(0);
720 }
721
722 static void
723 parse_dimension(int i)
724 {
725   printf ("%#x: dimension %d\n", pos, i);
726   if (i == 0)
727     {
728       match_zeros_assert(5);
729       match_u32_assert(1);
730       get_u16(); match_u16_assert(0x0e74); match_byte_assert(0);
731       match_DspSimpleText();
732       match_DspString();
733
734       int n_categories = get_u32();
735       for (int j = 0; j < n_categories; j++)
736         parse_category(j);
737     }
738   else
739     {
740       match_zeros_assert(6);
741
742       int n_units16 = get_u32();
743       match_u16_assert(1);
744       for (int j = 0; j < n_units16; j++)
745         get_u16();
746
747       match_byte_assert(0);
748
749       int n_units32 = get_u32();
750       match_u16_assert(0);
751       for (int j = 0; j < n_units32; j++)
752         get_u32();
753
754       get_u16(); match_byte_assert(0);
755
756       get_u16(); match_byte_assert(0);
757       get_u16(); match_byte_assert(0);
758       match_u32_assert(0);
759       match_u32_assert(1);
760
761       get_u16(); match_u16_assert(0x0e74); match_byte_assert(0);
762       match_DspSimpleText();
763       match_DspString();
764
765       int n_categories = get_u32();
766       for (int j = 0; j < n_categories; j++)
767         parse_category(j);
768     }
769 }
770
771 static void
772 parse_PMModelItemInfo(void)
773 {
774   for (int i = 0; i < n_dims; i++)
775     parse_dimension(i);
776   printf("%#x: end of model\n", pos);
777   exit(0);
778 }
779
780 static void
781 match_PMModelItemInfo(void)
782 {                               /* 54 80 */
783   match_byte_assert(0x54);
784   match_byte_assert(0x80);
785   parse_PMModelItemInfo();
786   /* DspSimpleText */
787   /* DspString */
788 }
789
790 static void
791 match_PMPivotItemTree(void)
792 {                               /* 52 80 */
793   match_byte_assert(0x52);
794   match_byte_assert(0x80);
795   match_byte_assert(0);
796   match_PMModelItemInfo();
797 }
798
799 static void
800 parse_NavHead(void)
801 {
802   match_byte_assert(2);
803   match_zeros_assert(24);
804   match_byte_assert(1);
805   match_zeros_assert(3);
806   if (!match_byte(1))
807     match_byte_assert(0);
808   match_zeros_assert(3);
809   /* DspSimpleText */
810 }
811
812 static void
813 parse_NavOleItem(void)
814 {
815   match_byte_assert(0);
816   match_byte_assert(1);
817   match_zeros_assert(2);
818   pos++;
819   match_zeros_assert(9);
820   match_byte_assert(1);
821   match_zeros_assert(10);
822   match_byte_assert(1);
823   match_zeros_assert(5);
824   get_string1();
825   match_byte_assert(1);
826   parse_weirdness();
827   match_byte_assert(0);
828   pos++;
829   match_zeros_assert(11);
830   match_byte_assert(1);
831   match_zeros_assert(3);
832   get_string4();
833   match_byte_assert(0);
834 }
835
836 static void
837 match_NavOleItem(void)
838 {                               /* 0e 80 or 12 80*/
839   if (!match_byte(0x12))
840     match_byte_assert(0x0e);
841   match_byte_assert(0x80);
842   parse_NavOleItem();
843 }
844
845 static void
846 parse_NavTitle(void)
847 {
848   match_byte_assert(2);
849   match_zeros_assert(8);
850   match_u32_assert(24);
851   get_u32();
852   pos++;
853   if (!match_byte(3))
854     match_byte_assert(4);
855   match_zeros_assert(2);
856   get_u32();
857   match_u32_assert(2);
858   if (!match_u32(2))
859     match_u32_assert(1);
860 }
861
862 static void
863 parse_NavNote(void)
864 {
865   match_byte_assert(2);
866   match_zeros_assert(8);
867   match_u32_assert(24);
868   if (!match_u32(0) && !match_u32(0xffffff4b))
869     match_u32_assert(-40);
870   pos += 8;
871   match_u32_assert(2);
872   if (!match_u32(2))
873     match_u32_assert(1);
874 }
875
876 static void
877 parse_PTPivotController(void)
878 {
879   match_byte_assert(2);
880   pos += 8;
881   match_u32_assert(100);
882   match_u32_assert(100);
883   match_u32_assert(100);
884   match_u32_assert(100);
885 }
886
887 static void
888 parse_PVPivotView(void)
889 {
890   match_byte_assert(5);
891   printf ("PVPivotView(%d)\n", get_u32());
892 }
893
894 static void
895 parse_NDimensional__DspCell(void)
896 {
897   match_byte_assert(0);
898   n_dims = get_u32();
899   printf ("NDimensional__DspCell(n_dims=%d)\n", n_dims);
900 }
901
902 static void
903 parse_IndexedCollection(void)
904 {
905   printf("IndexedCollection");
906   for (size_t i = 0; ; i++)
907     {
908       match_byte_assert(0);
909       printf("%c%d", i ? 'x' : '(', get_u32());
910       match_u16_assert(1);
911       if (!match_u16(0x8011))
912         break;
913     }
914   printf(")\n");
915 }
916
917 static void
918 parse_PTTableLook(void)
919 {
920   match_byte_assert(2);
921   match_byte_assert(2);
922   match_zeros_assert(7);
923   match_u32_assert(0x36);
924   match_u32_assert(0x12);
925 }
926
927 static void
928 parse_PVViewDimension(void)
929 {
930   while (data[pos + 1] != 0x80
931          && (data[pos] != 0xff || data[pos + 1] != 0xff))
932     {
933       assert(pos < n);
934       pos++;
935     }
936 }
937
938 static void
939 parse_PVSeparatorStyle(void)
940 {
941   match_byte_assert(0);
942   match_byte_assert(1);
943   match_zeros_assert(15);
944   pos++;
945   match_byte_assert(0x80);
946   match_byte_assert(0);
947
948   match_byte_assert(1);
949   match_zeros_assert(9);
950   while (data[pos + 1] != 0x80
951          && (data[pos] != 0xff || data[pos + 1] != 0xff))
952     {
953       assert(pos < n);
954       pos++;
955     }
956 }
957
958 static void
959 parse_PVCellStyle(void)
960 {
961   match_byte_assert(0);
962   match_byte_assert(1);
963   match_zeros_assert(5);
964   match_u32_assert(0xffffff);
965   match_zeros_assert(2);
966 }
967
968 static void
969 skip_item(const char *name)
970 {
971   int start_pos = pos;
972   printf("%#x: skipping %s bytes...", pos, name);
973   while (data[pos + 1] != 0x80
974          && !(data[pos] == 0xff && data[pos + 1] == 0xff
975               && data[pos + 2] == 0 && data[pos + 3] == 0))
976     {
977       assert(pos < n);
978       pos++;
979     }
980   printf("until %#x:", pos);
981   hex_dump(stdout, start_pos, pos - start_pos);
982 }
983
984 static void
985 parse_flexible(void)
986 {
987   int start = pos;
988   if (match_u16(0xffff))
989     {
990       match_u16_assert(0);
991       char *heading = get_string2();
992       printf("%#x: %s\n", pos, heading);
993       if (!strcmp(heading, "NavRoot"))
994         {
995           match_byte_assert(2);
996           match_zeros_assert(32);
997         }
998       else if (!strcmp(heading, "NavPivot"))
999         {
1000           hex_dump(stdout, pos, 021);
1001           pos += 0x21;
1002         }
1003       else if (!strcmp(heading, "DspCell"))
1004         parse_DspCell();
1005       else if (!strcmp(heading, "DspSimpleText"))
1006         parse_DspSimpleText();
1007       else if (!strcmp(heading, "DspNumber"))
1008         parse_DspNumber();
1009       else if (!strcmp(heading, "DspString"))
1010         parse_DspString();
1011       else if (!strcmp(heading, "NavHead"))
1012         parse_NavHead();
1013       else if (!strcmp(heading, "NavTreeViewItem"))
1014         {
1015           if (0)
1016             parse_NavTreeViewItem();
1017           else
1018             skip_item(heading);
1019         }
1020       else if (!strcmp(heading, "IndexedCollection"))
1021         parse_IndexedCollection();
1022       else if (!strcmp(heading, "NavOleItem"))
1023         parse_NavOleItem();
1024       else if (!strcmp(heading, "NavTitle"))
1025         parse_NavTitle();
1026       else if (!strcmp(heading, "NavNote"))
1027         parse_NavNote();
1028       else if (!strcmp(heading, "PTPivotController"))
1029         parse_PTPivotController();
1030       else if (!strcmp(heading, "PVPivotView"))
1031         parse_PVPivotView();
1032       else if (!strcmp(heading, "PMPivotModel"))
1033         match_byte_assert(3);
1034       else if (!strcmp(heading, "NDimensional__DspCell"))
1035         parse_NDimensional__DspCell();
1036       else if (!strcmp(heading, "PMPivotItemTree"))
1037         match_byte_assert(0);
1038       else if (!strcmp(heading, "PMModelItemInfo"))
1039         parse_PMModelItemInfo();
1040       else if (!strcmp(heading, "AbstractTreeBranch"))
1041         match_byte_assert(0);
1042       else if (!strcmp(heading, "PTTableLook"))
1043         parse_PTTableLook();
1044       else if (!strcmp(heading, "PVViewDimension"))
1045         parse_PVViewDimension();
1046       else if (!strcmp(heading, "PVSeparatorStyle"))
1047         parse_PVSeparatorStyle();
1048       else if (!strcmp(heading, "PVCellStyle"))
1049         parse_PVCellStyle();
1050       else if (!strcmp(heading, "PVTextStyle"))
1051         exit(0);
1052       else
1053         {
1054           fprintf(stderr, "don't know %s at offset 0x%x: ", heading, start);
1055           hex_dump(stderr, pos, 128);
1056           assert(0);
1057         }
1058     }
1059   else if (data[pos + 1] == 0x80)
1060     {
1061       if ((data[pos] == 0x2a || data[pos] == 0x18 || data[pos] == 0x19) && data[pos + 1] == 0x80)
1062         match_DspNumber();
1063       else if (data[pos] == 0x27 && data[pos + 1] == 0x80)
1064         match_DspCell();
1065       else if (data[pos] == 0x5 && data[pos + 1] == 0x80)
1066         match_DspString();
1067       else if (data[pos] == 0x7 && data[pos + 1] == 0x80)
1068         match_NavTreeViewItem();
1069       else if (data[pos] == 0x3 && data[pos + 1] == 0x80)
1070         match_DspSimpleText();
1071       else if ((data[pos] == 0x3c || data[pos] == 0x39)
1072                && data[pos + 1] == 0x80)
1073         {
1074           /* 3c 80 */
1075           /* 39 80 */
1076           printf("%#x: %02x %02x ", pos, data[pos], data[pos + 1]);
1077           pos += 2;
1078           parse_format();
1079           printf ("\n");
1080 /*      match_byte_assert(0x01);
1081         match_byte_assert(0x02);
1082         match_byte_assert(0x0d); */
1083         }
1084       else if (data[pos] == 0x15 && data[pos + 1] == 0x80)
1085         {
1086           /* 15 80 */
1087           pos += 2;
1088           if (match_byte(2))
1089             {
1090               printf ("15 80(%f", get_double());
1091               printf (" \"%s\")\n", get_string1());
1092               if (match_byte(1))
1093                 {
1094                   match_byte_assert(0);
1095                   get_string1();
1096                   if (!match_byte(2) && !match_byte(3))
1097                     match_byte_assert(0);
1098                   match_zeros_assert(3);
1099                   get_string1();
1100                   match_byte_assert(0);
1101                   match_byte_assert(1);
1102                   match_zeros_assert(3);
1103                   match_byte_assert(1);
1104                   match_byte_assert(0);
1105                 }
1106             }
1107           else
1108             {
1109               match_byte_assert(0);
1110               if (match_u32(0xc))
1111                 match_u16_assert(1);
1112             }
1113         }
1114       else if (data[pos] == 0x9 && data[pos + 1] == 0x80)
1115         {
1116           match_NavLog();
1117         }
1118       else if (data[pos] == 0xe || data[pos] == 0x12)
1119         match_NavOleItem();
1120       else if (data[pos] == 0x11 || data[pos] == 0x13)
1121         {
1122           int type = data[pos];
1123           pos += 2;
1124           match_byte_assert(0);
1125           if (data[pos] != 0)
1126             {
1127               int x = get_u32();
1128               int y = get_u16();
1129               if (y == 0)
1130                 {
1131                   int index = get_u32();
1132                   printf("%02x 80(footnote %d)\n", type, index);
1133                 }
1134               else
1135                 printf("%02x 80(%d %d)\n", type, x, y);
1136             }
1137           else
1138             match_zeros_assert(13);
1139         }
1140       else if (data[pos] == 0x29 ||
1141                data[pos] == 0x2b ||
1142                data[pos] == 0x2d ||
1143                data[pos] == 0x31 ||
1144                data[pos] == 0x32 ||
1145                data[pos] == 0x4a ||
1146                data[pos] == 0x4c ||
1147                data[pos] == 0x4f ||
1148                data[pos] == 0x4d ||
1149                data[pos] == 0x50 ||
1150                data[pos] == 0x36 ||
1151                data[pos] == 0x52 ||
1152                data[pos] == 0x53 ||
1153                data[pos] == 0x54 ||
1154                data[pos] == 0x55 ||
1155                data[pos] == 0x57 ||
1156                data[pos] == 0x56 ||
1157                data[pos] == 0x58 ||
1158                data[pos] == 0x5c ||
1159                data[pos] == 0x5b ||
1160                data[pos] == 0x5e ||
1161                data[pos] == 0x62 ||
1162                data[pos] == 0x64 ||
1163                data[pos] == 0x4e ||
1164                data[pos] == 0x51 ||
1165                data[pos] == 0x59 ||
1166                data[pos] == 0x5a ||
1167                data[pos] == 0x5d ||
1168                data[pos] == 0x66 ||
1169                data[pos] == 0x60 ||
1170                data[pos] == 0x68 ||
1171                data[pos] == 0x48 ||
1172                data[pos] == 0x6a ||
1173                data[pos] == 0x37)
1174         {
1175           pos += 2;
1176           match_byte_assert(0);
1177         }
1178       else if (data[pos] == 0x2c ||
1179                data[pos] == 0x2e ||
1180                data[pos] == 0x30 ||
1181                data[pos] == 0x34 ||
1182                data[pos] == 0x3d ||
1183                data[pos] == 0x40 ||
1184                data[pos] == 0x3f ||
1185                data[pos] == 0x42 ||
1186                data[pos] == 0x43 ||
1187                data[pos] == 0x44 ||
1188                data[pos] == 0x49 ||
1189                data[pos] == 0x3e ||
1190                data[pos] == 0x46)
1191         {
1192           printf ("%#x: %02x %02x(%02x %02x %02x)\n",
1193                   pos, data[pos], data[pos + 1],
1194                   data[pos + 2], data[pos + 3], data[pos + 4]);
1195           pos += 2;
1196           pos += 3;
1197         }
1198       else
1199         {
1200 #if 0
1201           fprintf (stderr, "bad record 0x%02x at offset %x: ",
1202                    data[pos], pos);
1203 #endif
1204           hex_dump (stderr, pos, 64);
1205           assert(0);
1206         }
1207     }
1208   else if (match_byte(0xa)) 
1209     {
1210       if (!match_byte(7))
1211         match_byte_assert(0);
1212       if (match_u16(0x0e74))
1213         match_byte_assert(0);
1214       else
1215         {
1216           match_zeros_assert(4);
1217           if (pos == n)
1218             exit (0);
1219           match_zeros_assert (2);
1220         }
1221     }
1222 #if 0
1223   else if (match_byte(1))
1224     {
1225       match_byte_assert(0);
1226       get_string1();
1227       if (!match_byte(2))
1228         match_byte_assert(0);
1229       if (match_byte(0))
1230         {
1231           match_zeros_assert(2);
1232           get_string1();
1233           if (match_byte(0x08))
1234             {
1235               match_byte_assert(0);
1236               match_u16_assert(0x0e74);
1237               match_byte_assert(0);
1238             }
1239           else if (match_byte(3))
1240             {
1241               match_byte_assert(0);
1242               if (match_u16(0x0e74))
1243                 match_byte_assert(0);
1244               else
1245                 {
1246                   match_zeros_assert(6);
1247                   if (!match_byte(0xe) && !match_byte(0x11))
1248                     match_byte_assert(0);
1249                   match_byte_assert(0);
1250                   if (!match_u16(0x0e74))
1251                     match_u16_assert(0);
1252                   match_byte_assert(0);
1253                 }
1254             }
1255           else
1256             {
1257               match_byte_assert(0);
1258               match_byte_assert(1);
1259               match_zeros_assert(3);
1260               match_byte_assert(1);
1261               match_byte_assert(0);
1262             }
1263         }
1264     }
1265 #endif
1266   else if (match_u16(1))
1267     {
1268       int start_pos = pos;
1269       char *title = get_string1();
1270       printf("%#x: title(\"%s\", ", start_pos, title);
1271       match_u32_assert(0);
1272       char *id = get_string1();
1273       printf("\"%s\")\n", id);
1274       match_byte_assert(0);
1275       match_u32_assert(3);
1276       match_u16_assert(1);
1277     }
1278   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))
1279     skip_item("unknown");
1280 #if 0
1281   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))
1282     {
1283       if (!match_byte(7))
1284         match_byte_assert(0);
1285       if (!match_u16(0x0e74))
1286         match_byte_assert(0);
1287       match_byte_assert(0);
1288     }
1289   else if (match_byte(2) || match_byte(3))
1290     {
1291       match_byte_assert(0);
1292       if (!match_u16(0x0e74))
1293         {
1294           match_zeros_assert(2);
1295           if (match_byte(0))
1296             {
1297               match_zeros_assert(3);
1298               if (match_byte(0))
1299                 match_zeros_assert(4);
1300               else
1301                 {
1302                   pos++;
1303                   match_byte(0);
1304                   match_u16_assert(0x0e74);
1305                 }
1306             }
1307         }
1308       //match_byte_assert(0);
1309     }
1310   else if (match_byte(0xd) || match_byte(0xe) || match_byte(0xf)
1311            || match_byte(0x11) || match_byte(0x12) || match_byte(0x13)
1312            || match_byte(0x14) || match_byte(0x1b))
1313     {
1314       if (!match_byte(0x07))
1315         match_byte_assert(0);
1316       if (!match_u16(0x0e74))
1317         match_zeros_assert(11);
1318       else
1319         match_byte_assert(0);
1320     }
1321   else if (match_byte(0xe3) || match_byte(0xdb) || match_byte(0xd8) || match_byte(0xe9) || match_byte(0xf3))
1322     {
1323       match_byte_assert(0x0e);
1324       match_byte_assert(0x74);
1325       match_byte_assert(0x0e);
1326       match_byte_assert(0);
1327     }
1328   else if (match_byte(0x9d) || match_byte(0x9e) || match_byte(0x9c))
1329     match_u32_assert(0x000e741a);
1330   else if (match_byte(0x10))
1331     {
1332       match_byte_assert(0);
1333       if (match_byte(0))
1334         match_zeros_assert(10);
1335       else
1336         {
1337           match_u16_assert(0x0e74);
1338           match_byte_assert(0);
1339         }
1340     }
1341   else if (match_byte(0x39) || match_byte(0x3a) || match_byte(0x3b))
1342     match_u32_assert(0x000e7409);
1343   else
1344     {
1345       //fprintf (stderr, "bad record start at offset %x: ", pos);
1346       hex_dump (stderr, pos, 64);
1347       assert(0);
1348     }
1349 #endif
1350 }
1351
1352
1353
1354 int
1355 main(int argc, char *argv[])
1356 {
1357   bool print_offsets = false;
1358   for (;;)
1359     {
1360       int c = getopt (argc, argv, "o");
1361       if (c == -1)
1362         break;
1363
1364       switch (c)
1365         {
1366         case 'o':
1367           print_offsets = true;
1368           break;
1369
1370         case '?':
1371           exit (-1);
1372         }
1373     }
1374   if (argc - optind != 1)
1375     {
1376       fprintf (stderr, "usage: %s FILE.bin", argv[0]);
1377       exit (1);
1378     }
1379
1380   const char *filename = argv[optind];
1381   int fd = open(filename, O_RDONLY);
1382   if (fd < 0)
1383     {
1384       fprintf (stderr, "%s: open failed (%s)", filename, strerror (errno));
1385       exit (1);
1386     }
1387
1388   struct stat s;
1389   if (fstat(fd, &s))
1390     {
1391       perror("fstat");
1392       exit(1);
1393     }
1394   n = s.st_size;
1395   data = malloc(n + 256);
1396   if (!data)
1397     {
1398       perror("malloc");
1399       exit(1);
1400     }
1401   if (read(fd, data, n) != n)
1402     {
1403       perror("read");
1404       exit(1);
1405     }
1406   for (int i = 0; i < 256; i++)
1407     data[n + i] = i % 2 ? 0xaa : 0x55;
1408   close(fd);
1409
1410   setvbuf (stdout, NULL, _IONBF, 0);
1411
1412   match_byte_assert(4);
1413   match_u32_assert(0);
1414   match_string1_assert("SPSS Output Document");
1415   match_u32_assert(1);
1416   match_byte_assert(0x63);
1417
1418   for (;;)
1419     {
1420       if (data[pos] == 0)
1421         {
1422           //printf("zero\n");
1423           pos++;
1424         }
1425       else
1426         parse_flexible();
1427     }
1428   exit(0);
1429
1430   parse_heading("NavRoot");
1431   match_byte_assert(2);
1432   match_zeros_assert(32);
1433
1434   parse_heading("DspSimpleText");
1435   match_zeros_assert(10);
1436
1437   parse_heading("DspString");
1438   parse_DspString();
1439
1440   parse_heading("NavTreeViewItem");
1441   match_byte_assert(0);
1442   if (!match_u32(1))
1443     match_u32_assert(0);
1444   match_byte_assert(2);
1445   match_byte_assert(0);
1446   match_byte_assert(1);
1447   match_zeros_assert(9);
1448   match_u32_assert(1);
1449
1450   match_u32_assert(0);
1451   match_u32_assert(0x18);
1452   if (!match_u32(0))
1453     match_u32_assert(0xffffffd8);
1454   match_u32_assert(0xffffffde);
1455   match_u32_assert(0x18);
1456   if (!match_u32(0))
1457     match_u32_assert(0xffffffd8);
1458   match_u32_assert(0x28);
1459   match_u32_assert(0x28);
1460   pos += 8;
1461   if (data[pos] == 0)
1462     {
1463       match_zeros_assert(5);
1464
1465       if (match_u32(8500))
1466         match_u32_assert(11000);
1467       else
1468         {
1469           match_u32_assert(11000);
1470           match_u32_assert(8500);
1471         }
1472       pos += 32;
1473       get_string1();
1474       if (!match_byte(0))
1475         match_byte_assert(1);
1476       pos++;
1477       pos++;
1478       pos++;
1479       pos++;
1480       get_string4();                /* page title */
1481       match_byte_assert(1);
1482       match_byte_assert(1);
1483       match_zeros_assert(3);
1484       get_string4();                /* page number */
1485       match_byte_assert(0);
1486       pos += 2;
1487       match_u16_assert(2);
1488     }
1489
1490   if (data[pos + 9] != 'L')
1491     exit(0);
1492   parse_heading("NavLog");
1493   parse_NavLog();
1494   for (;;)
1495     {
1496       if (data[pos] == 0)
1497         {
1498           //printf("zero\n");
1499           pos++;
1500         }
1501       else
1502         parse_flexible();
1503     }
1504   exit(0);
1505   puts(get_padded_string(32));
1506   if (!match_u32(80))
1507     match_u32_assert(132);
1508   match_zeros_assert(8);
1509   match_u32_assert(1);
1510   printf ("0x%x\n", pos);
1511   get_string4();
1512   match_byte_assert(0);
1513
1514   parse_heading("NavHead");
1515   parse_NavHead();
1516   match_NavTreeViewItem();
1517   match_zeros_assert(3);
1518
1519   parse_heading("NavTitle");
1520   pos += 33;
1521   match_DspSimpleText();
1522   match_DspString();
1523   match_NavTreeViewItem();
1524
1525   match_byte_assert(1);
1526   match_byte_assert(1);
1527   match_u32_assert(-19);
1528   match_zeros_assert(12);
1529   match_byte_assert(0xbc);
1530   match_byte_assert(2);
1531   match_zeros_assert(9);
1532   match_byte_assert(0x22);
1533   puts(get_padded_string(32));
1534   match_u32_assert(80);
1535   match_zeros_assert(8);
1536   match_u32_assert(1);
1537   get_string4();
1538   match_byte_assert(0);
1539
1540   parse_heading("NavNote");
1541   match_byte_assert(2);
1542   match_zeros_assert(8);
1543   match_u32_assert(24);
1544   if (!match_u32(0))
1545     match_u32_assert(-40);
1546   pos += 8;
1547   match_u32_assert(2);
1548   match_u32_assert(1);
1549   match_DspSimpleText();
1550   match_DspString();
1551   match_NavTreeViewItem();
1552   match_byte_assert(1);
1553
1554   parse_heading("PTPivotController");
1555   match_byte_assert(2);
1556   pos += 8;
1557   match_u32_assert(100);
1558   match_u32_assert(100);
1559   match_u32_assert(100);
1560   match_u32_assert(100);
1561
1562   parse_heading("PVPivotView");
1563   match_u32_assert(5);
1564   match_byte_assert(0);
1565
1566   parse_heading("PMPivotModel");
1567   match_byte_assert(3);
1568
1569   parse_heading("NDimensional__DspCell");
1570   match_byte_assert(0);
1571   match_u32_assert(1);
1572
1573   parse_heading("IndexedCollection");
1574   match_byte_assert(0);
1575   pos++;
1576   match_zeros_assert(3);
1577   match_byte_assert(1);
1578   match_byte_assert(0);
1579   match_zeros_assert(7);
1580
1581   while (data[pos] != 1)
1582     {
1583       if (data[pos] == 0)
1584         {
1585           printf("zero\n");
1586           pos++;
1587         }
1588       else
1589         parse_flexible();
1590     }
1591
1592   match_byte_assert(1);
1593   match_byte_assert(0);
1594   puts(get_string1());
1595   if (!match_u32(0))
1596     match_u32_assert(2);
1597   puts(get_string1());
1598
1599   match_byte_assert(0);
1600   match_byte_assert(1);
1601   match_byte_assert(0);
1602   match_byte_assert(0);
1603   match_byte_assert(0);
1604   match_byte_assert(1);
1605   match_byte_assert(0);
1606
1607   exit (0);
1608
1609   parse_heading("PMPivotItemTree");
1610   match_byte_assert(0);
1611
1612   parse_heading("AbstractTreeBranch");
1613   match_byte_assert(0);
1614
1615   parse_heading("PMModelItemInfo");
1616   parse_PMModelItemInfo();
1617   match_DspSimpleText();
1618   match_DspString();
1619
1620   match_u32_assert(7);
1621   match_PMPivotItemTree();
1622
1623   match_u32_assert(0);
1624   match_PMPivotItemTree();
1625
1626   match_u32_assert(0);
1627   match_PMPivotItemTree();
1628
1629   match_u32_assert(6);
1630   match_PMPivotItemTree();
1631
1632   match_u32_assert(0);
1633   match_PMPivotItemTree();
1634
1635   match_u32_assert(0);
1636   match_PMPivotItemTree();
1637
1638   match_u32_assert(0);
1639   match_PMPivotItemTree();
1640
1641   match_u32_assert(0);
1642   match_PMPivotItemTree();
1643
1644   match_u32_assert(0);
1645   match_PMPivotItemTree();
1646
1647   match_u32_assert(0);
1648   match_PMPivotItemTree();
1649
1650   match_u32_assert(2);
1651   match_PMPivotItemTree();
1652
1653   match_u32_assert(0);
1654   match_PMPivotItemTree();
1655
1656   match_u32_assert(0);
1657   match_PMPivotItemTree();
1658
1659   match_u32_assert(0);
1660   match_PMPivotItemTree();
1661
1662   match_u32_assert(0);
1663   match_PMPivotItemTree();
1664
1665   match_u32_assert(2);
1666   match_PMPivotItemTree();
1667
1668   match_u32_assert(0);
1669   match_PMPivotItemTree();
1670
1671   match_u32_assert(0);
1672
1673   /* ...unknown... */
1674
1675   while (data[pos] != 0xff || data[pos + 1] != 0xff)
1676     pos++;
1677   parse_heading("PVViewDimension");
1678
1679   int i;
1680   for (i = 0; data[pos + i] != 0xff || data[pos + i + 1] != 0xff; i++)
1681     ;
1682   hex_dump(stdout, pos, i);
1683
1684   printf ("%#x: end of successful parse\n", pos);
1685
1686   return 0;
1687 }