ac53ece628894626236a24d2c2d9a91537d33b12
[pspp] / dump-spo.c
1 #include <assert.h>
2 #include <errno.h>
3 #include <fcntl.h>
4 #include <float.h>
5 #include <stdbool.h>
6 #include <stdint.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <sys/stat.h>
11 #include <time.h>
12 #include <unistd.h>
13 #include "u8-mbtouc.h"
14
15 static const char *filename;
16 static uint8_t *data;
17 static size_t n;
18
19 int version;
20
21 unsigned int pos;
22
23 #define XSTR(x) #x
24 #define STR(x) XSTR(x)
25 #define WHERE __FILE__":" STR(__LINE__)
26
27 static uint8_t
28 get_byte(void)
29 {
30   return data[pos++];
31 }
32
33 static unsigned int
34 get_u32(void)
35 {
36   uint32_t x;
37   memcpy(&x, &data[pos], 4);
38   pos += 4;
39   return x;
40 }
41
42 static unsigned long long int
43 get_u64(void)
44 {
45   uint64_t x;
46   memcpy(&x, &data[pos], 8);
47   pos += 8;
48   return x;
49 }
50
51 static unsigned int
52 get_be32(void)
53 {
54   uint32_t x;
55   x = (data[pos] << 24) | (data[pos + 1] << 16) | (data[pos + 2] << 8) | data[pos + 3];
56   pos += 4;
57   return x;
58 }
59
60 static unsigned int
61 get_u16(void)
62 {
63   uint16_t x;
64   memcpy(&x, &data[pos], 2);
65   pos += 2;
66   return x;
67 }
68
69 static double
70 get_double(void)
71 {
72   double x;
73   memcpy(&x, &data[pos], 8);
74   pos += 8;
75   return x;
76 }
77
78 static double __attribute__((unused))
79 get_float(void)
80 {
81   float x;
82   memcpy(&x, &data[pos], 4);
83   pos += 4;
84   return x;
85 }
86
87 static bool
88 match_u32(uint32_t x)
89 {
90   if (get_u32() == x)
91     return true;
92   pos -= 4;
93   return false;
94 }
95
96 static void
97 match_u32_assert(uint32_t x, const char *where)
98 {
99   unsigned int y = get_u32();
100   if (x != y)
101     {
102       fprintf(stderr, "%s: 0x%x: expected i%u, got i%u\n", where, pos - 4, x, y);
103       exit(1);
104     }
105 }
106 #define match_u32_assert(x) match_u32_assert(x, WHERE)
107
108 static bool __attribute__((unused))
109 match_u64(uint64_t x)
110 {
111   if (get_u64() == x)
112     return true;
113   pos -= 8;
114   return false;
115 }
116
117 static void __attribute__((unused))
118 match_u64_assert(uint64_t x, const char *where)
119 {
120   unsigned long long int y = get_u64();
121   if (x != y)
122     {
123       fprintf(stderr, "%s: 0x%x: expected u64:%lu, got u64:%llu\n", where, pos - 8, x, y);
124       exit(1);
125     }
126 }
127 #define match_u64_assert(x) match_u64_assert(x, WHERE)
128
129 static bool __attribute__((unused))
130 match_be32(uint32_t x)
131 {
132   if (get_be32() == x)
133     return true;
134   pos -= 4;
135   return false;
136 }
137
138 static void
139 match_be32_assert(uint32_t x, const char *where)
140 {
141   unsigned int y = get_be32();
142   if (x != y)
143     {
144       fprintf(stderr, "%s: 0x%x: expected be%u, got be%u\n", where, pos - 4, x, y);
145       exit(1);
146     }
147 }
148 #define match_be32_assert(x) match_be32_assert(x, WHERE)
149
150 static bool
151 match_byte(uint8_t b)
152 {
153   if (pos < n && data[pos] == b)
154     {
155       pos++;
156       return true;
157     }
158   else
159     return false;
160 }
161
162 static void
163 match_byte_assert(uint8_t b, const char *where)
164 {
165   if (!match_byte(b))
166     {
167       fprintf(stderr, "%s: 0x%x: expected %02x, got %02x\n", where, pos, b, data[pos]);
168       exit(1);
169     }
170 }
171 #define match_byte_assert(b) match_byte_assert(b, WHERE)
172
173 static bool
174 match_bytes(int start, const int *bytes, size_t n_bytes)
175 {
176   for (size_t i = 0; i < n_bytes; i++)
177     if (bytes[i] >= 0 && data[start + i] != bytes[i])
178       return false;
179   return true;
180 }
181
182 static bool
183 get_bool(void)
184 {
185   if (match_byte(0))
186     return false;
187   match_byte_assert(1);
188   return true;
189 }
190
191 static bool __attribute__((unused))
192 is_ascii(uint8_t p)
193 {
194   return (p >= ' ' && p < 127) || p == '\r' || p == '\n' || p == '\t';
195 }
196
197 static bool __attribute__((unused))
198 all_utf8(const char *p_, size_t len)
199 {
200   const uint8_t *p = (const uint8_t *) p_;
201   for (size_t ofs = 0, mblen; ofs < len; ofs += mblen)
202     {
203       ucs4_t uc;
204
205       mblen = u8_mbtouc (&uc, p + ofs, len - ofs);
206       if ((uc < 32 && uc != '\n') || uc == 127 || uc == 0xfffd)
207         return false;
208     }
209   return true;
210 }
211
212 static char *
213 get_string(const char *where)
214 {
215   if (1
216       /*data[pos + 1] == 0 && data[pos + 2] == 0 && data[pos + 3] == 0*/
217       /*&& all_ascii(&data[pos + 4], data[pos])*/)
218     {
219       int len = data[pos] + data[pos + 1] * 256;
220       char *s = malloc(len + 1);
221
222       memcpy(s, &data[pos + 4], len);
223       s[len] = 0;
224       pos += 4 + len;
225       return s;
226     }
227   else
228     {
229       fprintf(stderr, "%s: 0x%x: expected string\n", where, pos);
230       exit(1);
231     }
232 }
233 #define get_string() get_string(WHERE)
234
235 static char *
236 get_string_be(const char *where)
237 {
238   if (1
239       /*data[pos + 1] == 0 && data[pos + 2] == 0 && data[pos + 3] == 0*/
240       /*&& all_ascii(&data[pos + 4], data[pos])*/)
241     {
242       int len = data[pos + 2] * 256 + data[pos + 3];
243       char *s = malloc(len + 1);
244
245       memcpy(s, &data[pos + 4], len);
246       s[len] = 0;
247       pos += 4 + len;
248       return s;
249     }
250   else
251     {
252       fprintf(stderr, "%s: 0x%x: expected string\n", where, pos);
253       exit(1);
254     }
255 }
256 #define get_string_be() get_string_be(WHERE)
257
258 static int
259 get_end(void)
260 {
261   int len = get_u32();
262   return pos + len;
263 }
264
265 static void __attribute__((unused))
266 hex_dump(FILE *stream, int ofs, int n)
267 {
268   int n_ascii = 0;
269   for (int i = 0; i < n; i++)
270     {
271       int c = data[ofs + i];
272       n_ascii += is_ascii(c);
273       fprintf(stream, " %02x", c);
274     }
275   if (n_ascii >= 3)
276     {
277       putc(' ', stream);
278       for (int i = 0; i < n; i++)
279         {
280           int c = data[ofs + i];
281           putc(c >= 32 && c < 127 ? c : '.', stream);
282         }
283     }
284   putc('\n', stream);
285 }
286
287 static void __attribute__((unused))
288 char_dump(FILE *stream, int ofs, int n)
289 {
290   for (int i = 0; i < n; i++)
291     {
292       int c = data[ofs + i];
293       putc(c >= 32 && c < 127 ? c : '.', stream);
294     }
295   putc('\n', stream);
296 }
297
298 static char *
299 dump_counted_string(void)
300 {
301   int inner_end = get_end();
302   if (pos == inner_end)
303     return NULL;
304
305   if (match_u32(5))
306     {
307       match_u32_assert(0);
308       match_byte_assert(0x58);
309     }
310   else
311     match_u32_assert(0);
312
313   char *s = NULL;
314   if (match_byte(0x31))
315     s = get_string();
316   else
317     match_byte_assert(0x58);
318   if (pos != inner_end)
319     {
320       fprintf(stderr, "inner end discrepancy\n");
321       exit(1);
322     }
323   return s;
324 }
325
326 static void
327 dump_style(FILE *stream)
328 {
329   if (match_byte(0x58))
330     return;
331
332   match_byte_assert(0x31);
333   if (get_bool())
334     printf (" bold=\"yes\"");
335   if (get_bool())
336     printf (" italic=\"yes\"");
337   if (get_bool())
338     printf (" underline=\"yes\"");
339   if (!get_bool())
340     printf (" show=\"no\"");
341   char *fg = get_string();     /* foreground */
342   char *bg = get_string();     /* background */
343   char *font = get_string();     /* font */
344   int size = get_byte() * (72. / 96.);
345   fprintf(stream, " fgcolor=\"%s\" bgcolor=\"%s\" font=\"%s\" size=\"%dpt\"",
346           fg, bg, font, size);
347 }
348
349 static void
350 dump_style2(FILE *stream)
351 {
352   if (match_byte(0x58))
353     return;
354
355   match_byte_assert(0x31);
356   uint32_t halign = get_u32();
357   printf (" halign=\"%s\"",
358           halign == 0 ? "center"
359           : halign == 2 ? "left"
360           : halign == 4 ? "right"
361           : halign == 6 ? "decimal"
362           : halign == 0xffffffad ? "mixed"
363           : "<error>");
364   int valign = get_u32();
365   printf (" valign=\"%s\"",
366           valign == 0 ? "center"
367           : valign == 1 ? "top"
368           : valign == 3 ? "bottom"
369           : "<error>");
370   printf (" offset=\"%gpt\"", get_double());
371   int l = get_u16();
372   int r = get_u16();
373   int t = get_u16();
374   int b = get_u16();
375   printf (" margins=\"%d %d %d %d\"", l, r, t, b);
376 }
377
378 static char *
379 dump_nested_string(FILE *stream)
380 {
381   char *s = NULL;
382
383   match_byte_assert (0);
384   match_byte_assert (0);
385   int outer_end = get_end();
386   s = dump_counted_string();
387   if (s)
388     fprintf(stream, " \"%s\"", s);
389   dump_style(stream);
390   match_byte_assert(0x58);
391   if (pos != outer_end)
392     {
393       fprintf(stderr, "outer end discrepancy\n");
394       exit(1);
395     }
396
397   return s;
398 }
399
400 static void
401 dump_value_modifier(FILE *stream)
402 {
403   if (match_byte (0x31))
404     {
405       if (match_u32 (0))
406         {
407           fprintf(stream, "<special0");
408           if (match_u32 (1))
409             {
410               /* Corpus frequencies:
411                  124 "a"
412                  12 "b"
413                  8 "a, b"
414
415                  The given text is appended to the cell in a subscript font.
416               */
417               fprintf(stream, " subscript=\"%s\"", get_string());
418             }
419           else
420             match_u32_assert (0);
421
422           if (version == 1)
423             {
424               /* We only have one SPV file for this version (with many
425                  tables). */
426               match_byte(0);
427               if (!match_u32(1))
428                 match_u32_assert(2);
429               match_byte(0);
430               match_byte(0);
431               if (!match_u32(0) && !match_u32(1) && !match_u32(2) && !match_u32(3) && !match_u32(4) && !match_u32(5) && !match_u32(6) && !match_u32(7) && !match_u32(8) && !match_u32(9))
432                 match_u32_assert(10);
433               match_byte(0);
434               match_byte(0);
435               fprintf(stream, "/>\n");
436               return;
437             }
438
439           int outer_end = get_end();
440           
441           /* This counted-string appears to be a template string,
442              e.g. "Design\: [:^1:]1 Within Subjects Design\: [:^1:]2". */
443           char *template = dump_counted_string();
444           if (template)
445             fprintf(stream, " template=\"%s\"", template);
446
447           dump_style(stream);
448           dump_style2(stream);
449           if (pos != outer_end)
450             {
451               fprintf(stderr, "outer end discrepancy\n");
452               exit(1);
453             }
454           fprintf(stream, "/>\n");
455         }
456       else
457         {
458           int count = get_u32();
459           fprintf(stream, "<footnote-ref indexes=\"");
460           for (int i = 0; i < count; i++)
461             {
462               if (i)
463                 putc(' ', stream);
464               fprintf(stream, "%d", get_u16());
465             }
466           putc('"', stream);
467           match_byte_assert(0);
468           match_byte_assert(0);
469           dump_nested_string(stream);
470           fprintf(stream, "/>\n");
471         }
472     }
473   else
474     match_byte_assert (0x58);
475 }
476
477 static const char *
478 format_to_string (int type)
479 {
480   static char tmp[16];
481   switch (type)
482     {
483     case 1: return "A";
484     case 2: return "AHEX";
485     case 3: return "COMMA";
486     case 4: return "DOLLAR";
487     case 5: case 40: return "F";
488     case 6: return "IB";
489     case 7: return "PIBHEX";
490     case 8: return "P";
491     case 9: return "PIB";
492     case 10: return "PK";
493     case 11: return "RB";
494     case 12: return "RBHEX";
495     case 15: return "Z";
496     case 16: return "N";
497     case 17: return "E";
498     case 20: return "DATE";
499     case 21: return "TIME";
500     case 22: return "DATETIME";
501     case 23: return "ADATE";
502     case 24: return "JDATE";
503     case 25: return "DTIME";
504     case 26: return "WKDAY";
505     case 27: return "MONTH";
506     case 28: return "MOYR";
507     case 29: return "QYR";
508     case 30: return "WKYR";
509     case 31: return "PCT";
510     case 32: return "DOT";
511     case 33: return "CCA";
512     case 34: return "CCB";
513     case 35: return "CCC";
514     case 36: return "CCD";
515     case 37: return "CCE";
516     case 38: return "EDATE";
517     case 39: return "SDATE";
518     default:
519       abort();
520       sprintf(tmp, "<%d>", type);
521       return tmp;
522     }
523 }
524
525 static void
526 dump_value(FILE *stream, int level)
527 {
528   match_byte(0);
529   match_byte(0);
530   match_byte(0);
531   match_byte(0);
532
533   for (int i = 0; i <= level; i++)
534     fprintf (stream, "    ");
535
536   printf ("%02x: value (%d)\n", pos, data[pos]);
537   if (match_byte (1))
538     {
539       unsigned int format;
540       double value;
541
542       dump_value_modifier(stream);
543       format = get_u32 ();
544       value = get_double ();
545       fprintf (stream, "<number value=\"%.*g\" format=\"%s%d.%d\"/>\n",
546                DBL_DIG, value, format_to_string(format >> 16), (format >> 8) & 0xff, format & 0xff);
547     }
548   else if (match_byte (2))
549     {
550       unsigned int format;
551       char *var, *vallab;
552       double value;
553
554       dump_value_modifier (stream);
555       format = get_u32 ();
556       value = get_double ();
557       var = get_string ();
558       vallab = get_string ();
559       fprintf (stream, "<numeric-datum value=\"%.*g\" format=\"%s%d.%d\"",
560               DBL_DIG, value, format_to_string(format >> 16), (format >> 8) & 0xff, format & 0xff);
561       if (var[0])
562         fprintf (stream, " variable=\"%s\"", var);
563       if (vallab[0])
564         fprintf (stream, " label=\"%s\"", vallab);
565       fprintf (stream, "/>\n");
566       if (!match_byte (1) && !match_byte(2))
567         match_byte_assert (3);
568     }
569   else if (match_byte (3))
570     {
571       char *text =  get_string();
572       dump_value_modifier(stream);
573       char *identifier = get_string();
574       char *text_eng = get_string();
575       fprintf (stream, "<string c=\"%s\"", text_eng);
576       if (identifier[0])
577         fprintf (stream, " identifier=\"%s\"", identifier);
578       if (strcmp(text_eng, text))
579         fprintf (stream, " local=\"%s\"", text);
580       fprintf (stream, "/>\n");
581       if (!match_byte (0))
582         match_byte_assert(1);
583     }
584   else if (match_byte (4))
585     {
586       unsigned int format;
587       char *var, *vallab, *value;
588
589       dump_value_modifier(stream);
590       format = get_u32 ();
591       vallab = get_string ();
592       var = get_string ();
593       if (!match_byte(1) && !match_byte(2))
594         match_byte_assert (3);
595       value = get_string ();
596       fprintf (stream, "<string-datum value=\"%s\" format=\"%s%d.%d\"",
597               value, format_to_string(format >> 16), (format >> 8) & 0xff, format & 0xff);
598       if (var[0])
599         fprintf (stream, " variable=\"%s\"", var);
600       if (vallab[0])
601         fprintf (stream, " label=\"%s\"/>\n", vallab);
602       fprintf (stream, "/>\n");
603     }
604   else if (match_byte (5))
605     {
606       dump_value_modifier(stream);
607       char *name = get_string ();
608       char *label = get_string ();
609       fprintf (stream, "<variable name=\"%s\"", name);
610       if (label[0])
611         fprintf (stream, " label=\"%s\"", label);
612       fprintf (stream, "/>\n");
613       if (!match_byte(1) && !match_byte(2))
614         match_byte_assert(3);
615     }
616   else
617     {
618       printf ("else %#x\n", pos);
619       dump_value_modifier(stream);
620
621       char *base = get_string();
622       int x = get_u32();
623       fprintf (stream, "<template format=\"%s\">\n", base);
624       for (int i = 0; i < x; i++)
625         {
626           int y = get_u32();
627           if (!y)
628             y = 1;
629           else
630             match_u32_assert(0);
631           for (int j = 0; j <= level + 1; j++)
632             fprintf (stream, "    ");
633           fprintf (stream, "<substitution index=\"%d\">\n", i + 1);
634           for (int j = 0; j < y; j++)
635             dump_value (stream, level + 2);
636           for (int j = 0; j <= level + 1; j++)
637             fprintf (stream, "    ");
638           fprintf (stream, "</substitution>\n");
639         }
640       for (int j = 0; j <= level; j++)
641         fprintf (stream, "    ");
642       fprintf (stream, "</template>\n");
643     }
644 }
645
646 static int
647 compare_int(const void *a_, const void *b_)
648 {
649   const int *a = a_;
650   const int *b = b_;
651   return *a < *b ? -1 : *a > *b;
652 }
653
654 static void
655 check_permutation(int *a, int n, const char *name)
656 {
657   int b[n];
658   memcpy(b, a, n * sizeof *a);
659   qsort(b, n, sizeof *b, compare_int);
660   for (int i = 0; i < n; i++)
661     if (b[i] != i)
662       {
663         fprintf(stderr, "bad %s permutation:", name);
664         for (int i = 0; i < n; i++)
665           fprintf(stderr, " %d", a[i]);
666         putc('\n', stderr);
667         exit(1);
668       }
669 }
670
671 static void
672 dump_category(FILE *stream, int level, int **indexes, int *allocated_indexes,
673               int *n_indexes)
674 {
675   for (int i = 0; i <= level; i++)
676     fprintf (stream, "    ");
677   printf ("<category>\n");
678   dump_value (stream, level + 1);
679
680   bool merge = get_bool();
681   match_byte_assert (0);
682   int unindexed = get_bool();
683
684   int x = get_u32 ();
685   pos -= 4;
686   if (!match_u32 (0))
687     match_u32_assert (2);
688
689   int indx = get_u32();
690   int n_categories = get_u32();
691   if (indx == -1)
692     {
693       if (merge)
694         {
695           for (int i = 0; i <= level + 1; i++)
696             fprintf (stream, "    ");
697           fprintf (stream, "<merge/>\n");
698         }
699       assert (unindexed);
700     }
701   else
702     {
703       assert (!merge);
704       assert (!unindexed);
705       assert (x == 2);
706       assert (n_categories == 0);
707       if (*n_indexes >= *allocated_indexes)
708         {
709           *allocated_indexes = *allocated_indexes ? 2 * *allocated_indexes : 16;
710           *indexes = realloc(*indexes, *allocated_indexes * sizeof **indexes);
711         }
712       (*indexes)[(*n_indexes)++] = indx;
713     }
714
715   if (n_categories == 0)
716     {
717       for (int i = 0; i <= level + 1; i++)
718         fprintf (stream, "    ");
719       fprintf (stream, "<category-index>%d</category-index>\n", indx);
720     }
721   for (int i = 0; i < n_categories; i++)
722     dump_category (stream, level + 1, indexes, allocated_indexes, n_indexes);
723   for (int i = 0; i <= level; i++)
724     fprintf (stream, "    ");
725   printf ("</category>\n");
726 }
727
728 static int
729 dump_dim(int indx)
730 {
731   int n_categories;
732
733   printf ("<dimension index=\"%d\">\n", indx);
734   dump_value (stdout, 0);
735
736   /* This byte is usually 0 but many other values have been spotted.
737      No visible effect. */
738   pos++;
739
740   /* This byte can cause data to be oddly replicated. */
741   if (!match_byte(0) && !match_byte(1))
742     match_byte_assert(2);
743
744   if (!match_u32(0))
745     match_u32_assert(2);
746
747   bool show_dim_label = get_bool();
748   if (show_dim_label)
749     printf("  <show-dim-label/>\n");
750
751   bool hide_all_labels = get_bool();
752   if (hide_all_labels)
753     printf("  <hide-all-labels/>\n");
754
755   match_byte_assert(1);
756   if (!match_u32(UINT32_MAX))
757     match_u32_assert(indx);
758
759   n_categories = get_u32();
760
761   int *indexes = NULL;
762   int n_indexes = 0;
763   int allocated_indexes = 0;
764   for (int i = 0; i < n_categories; i++)
765     dump_category (stdout, 0, &indexes, &allocated_indexes, &n_indexes);
766   check_permutation(indexes, n_indexes, "categories");
767
768   fprintf (stdout, "</dimension>\n");
769   return n_indexes;
770 }
771
772 int n_dims;
773 static int dim_n_cats[64];
774 #define MAX_DIMS (sizeof dim_n_cats / sizeof *dim_n_cats)
775
776 static void
777 dump_dims(void)
778 {
779   n_dims = get_u32();
780   assert(n_dims < MAX_DIMS);
781   for (int i = 0; i < n_dims; i++)
782     dim_n_cats[i] = dump_dim (i);
783 }
784
785 static void
786 dump_data(void)
787 {
788   /* The first three numbers add to the number of dimensions. */
789   int l = get_u32();
790   int r = get_u32();
791   int c = n_dims - l - r;
792   match_u32_assert(c);
793
794   /* The next n_dims numbers are a permutation of the dimension numbers. */
795   int a[n_dims];
796   for (int i = 0; i < n_dims; i++)
797     {
798       int dim = get_u32();
799       a[i] = dim;
800
801       const char *name = i < l ? "layer" : i < l + r ? "row" : "column";
802       printf ("<%s dimension=\"%d\"/>\n", name, dim);
803     }
804   check_permutation(a, n_dims, "dimensions");
805
806   int x = get_u32();
807   printf ("<data>\n");
808   for (int i = 0; i < x; i++)
809     {
810       unsigned int indx = get_u32();
811       printf ("    <datum index=\"%d\" coords=", indx);
812
813       int coords[MAX_DIMS];
814       for (int i = n_dims; i-- > 0; )
815         {
816           coords[i] = indx % dim_n_cats[i];
817           indx /= dim_n_cats[i];
818         }
819       for (int i = 0; i < n_dims; i++)
820         printf("%c%d", i ? ',' : '"', coords[i]);
821
822       printf ("\">\n");
823       match_u32_assert(0);
824       if (version == 1)
825         match_byte(0);
826       dump_value(stdout, 1);
827       fprintf (stdout, "    </datum>\n");
828     }
829   printf ("</data>\n");
830 }
831
832 static void
833 dump_title(void)
834 {
835   printf ("<title-local>\n");
836   dump_value(stdout, 0);
837   match_byte(1);
838   printf ("</title-local>\n");
839
840   printf ("<subtype>\n");
841   dump_value(stdout, 0);
842   match_byte(1);
843   printf ("</subtype>\n");
844
845   match_byte_assert(0x31);
846
847   printf ("<title-c>\n");
848   dump_value(stdout, 0);
849   match_byte(1);
850   printf ("</title-c>\n");
851
852   if (match_byte(0x31))
853     {
854       printf ("<user-caption>\n");
855       dump_value(stdout, 0);
856       printf ("</user-caption>\n");
857     }
858   else
859     match_byte_assert(0x58);
860   if (match_byte(0x31))
861     {
862       printf ("<caption>\n");
863       dump_value(stdout, 0);
864       printf ("</caption>\n");
865     }
866   else
867     match_byte_assert(0x58);
868
869   int n_footnotes = get_u32();
870   for (int i = 0; i < n_footnotes; i++)
871     {
872       printf ("<footnote index=\"%d\">\n", i);
873       dump_value(stdout, 0);
874       /* Custom footnote marker string. */
875       if (match_byte (0x31))
876         dump_value(stdout, 0);
877       else
878         match_byte_assert (0x58);
879       int n = get_u32();
880       if (n >= 0)
881         {
882           /* Appears to be the number of references to a footnote. */
883           printf ("  <references n=\"%d\"/>\n", n);
884         }
885       else if (n == -2)
886         {
887           /* The user deleted the footnote references. */
888           printf ("  <deleted/>\n");
889         }
890       else
891         assert(0);
892       printf ("</footnote>\n");
893     }
894 }
895
896 static void
897 dump_fonts(void)
898 {
899   match_byte(0);
900   for (int i = 1; i <= 8; i++)
901     {
902       printf ("<style index=\"%d\"", i);
903       match_byte_assert(i);
904       match_byte_assert(0x31);
905       printf(" font=\"%s\"", get_string());
906
907       printf(" size=\"%gpt\"", get_float());
908
909       int style = get_u32();
910       if (style & 1)
911         printf(" bold=\"true\"");
912       if (style & 2)
913         printf(" italic=\"true\"");
914
915       bool underline = data[pos++];
916       if (underline)
917         printf(" underline=\"true\"");
918
919       int halign = get_u32();
920       printf(" halign=%d", halign);
921
922       int valign = get_u32();
923       printf(" valign=%d", valign);
924
925       printf (" fgcolor=\"%s\"", get_string());
926       printf (" bgcolor=\"%s\"", get_string());
927
928       if (!match_byte(0))
929         match_byte_assert(1);
930
931       char *alt_fgcolor = get_string();
932       if (alt_fgcolor[0])
933         printf (" altfg=\"%s\"", alt_fgcolor);
934       char *alt_bgcolor = get_string();
935       if (alt_bgcolor[0])
936         printf (" altbg=\"%s\"", alt_bgcolor);
937
938       if (version > 1)
939         {
940           printf(" margins=\"");
941           for (int i = 0; i < 4; i++)
942             {
943               if (i)
944                 putchar(' ');
945               printf("%d", get_u32());
946             }
947           putchar('"');
948         }
949
950       printf ("/>\n");
951     }
952
953   int x1 = get_u32();
954   int x1_end = pos + x1;
955   printf("<borders>\n");
956   match_be32_assert(1);
957   int n_borders = get_be32();
958   for (int i = 0; i < n_borders; i++)
959     {
960       int type = get_be32();
961       int stroke = get_be32();
962       int color = get_be32();
963       printf("  <border type=\"%d\" stroke=\"%s\" color=\"#%06x\"/>\n",
964              type,
965              (stroke == 0 ? "none"
966               : stroke == 1 ? "solid"
967               : stroke == 2 ? "dashed"
968               : stroke == 3 ? "thick"
969               : stroke == 4 ? "thin"
970               : stroke == 5 ? "double"
971               : "<error>"),
972              color);
973     }
974   bool grid = get_byte();
975   pos += 3;
976   printf("  <grid show=\"%s\"/>\n", grid ? "yes" : "no");
977   printf("</borders>\n");
978   assert(pos == x1_end);
979
980   int skip = get_u32();
981   assert(skip == 18 || skip == 25);
982   pos += skip;
983
984   int x3 = get_u32();
985   int x3_end = pos + x3;
986   if (version == 3)
987     {
988       match_be32_assert(1);
989       get_be32();
990       printf("<settings layer=\"%d\"", get_be32());
991       if (!get_bool())
992         printf(" skipempty=\"false\"");
993       if (!get_bool())
994         printf(" showdimensionincorner=\"false\"");
995       if (!get_bool())
996         printf(" markers=\"numeric\"");
997       if (!get_bool())
998         printf(" footnoteposition=\"subscript\"");
999       get_byte();
1000       int nbytes = get_be32();
1001       int end = pos + nbytes;
1002       printf("\n");
1003       while (pos + 4 <= end)
1004         printf(" %d", get_be32());
1005       pos = end;
1006       printf("\n");
1007       pos += nbytes;
1008       char *notes = get_string_be();
1009       if (notes[0])
1010         printf(" notes=\"%s\"", notes);
1011       char *look = get_string_be();
1012       if (look[0])
1013         printf(" look=\"%s\"", look);
1014       printf(">\n");
1015     }
1016   pos = x3_end;
1017
1018   /* Manual column widths, if present. */
1019   int count = get_u32();
1020   if (count > 0)
1021     {
1022       printf("<columnwidths>");
1023       for (int i = 0; i < count; i++)
1024         {
1025           if (i)
1026             putchar(' ');
1027           printf("%d", get_u32());
1028         }
1029       printf("</columnwidths>\n");
1030     }
1031
1032   const char *locale = get_string();
1033   printf ("<locale>%s</locale>\n", locale);
1034
1035   printf ("<layer>%d</layer>\n", get_u32());
1036   if (!match_byte(0))
1037     match_byte_assert(1);
1038   if (!match_byte(0))
1039     match_byte_assert(1);
1040   if (!match_byte(0))
1041     match_byte_assert(1);
1042   printf("<epoch>%d</epoch>\n", get_u32());
1043
1044   int decimal = data[pos];
1045   int grouping = data[pos + 1];
1046   if (match_byte('.'))
1047     {
1048       if (!match_byte(',') && !match_byte('\''))
1049         match_byte_assert(' ');
1050     }
1051   else
1052     {
1053       match_byte_assert(',');
1054       if (!match_byte('.') && !match_byte(' ') && !match_byte(','))
1055         match_byte_assert(0);
1056     }
1057   printf("<format decimal=\"%c\"", decimal);
1058   if (grouping)
1059     printf(" grouping=\"%c\"", grouping);
1060   printf("\"/>\n");
1061   if (match_u32(5))
1062     {
1063       for (int i = 0; i < 5; i++)
1064         printf("<CC%c>%s</CC%c>\n", 'A' + i, get_string(), 'A' + i);
1065     }
1066   else
1067     match_u32_assert(0);
1068
1069   /* The last chunk is an outer envelope that contains two inner envelopes.
1070      The second inner envelope has some interesting data like the encoding and
1071      the locale. */
1072   int outer_end = get_end();
1073   if (version == 3)
1074     {
1075       /* First inner envelope: byte*33 int[n] int*[n]. */
1076       int inner_len = get_u32();
1077       int inner_end = pos + inner_len;
1078       int array_start = pos + 33;
1079       match_byte_assert(0);
1080       pos++;                    /* 0, 1, 10 seen. */
1081       get_bool();
1082
1083       /* 0=en 1=de 2=es 3=it 5=ko 6=pl 8=zh-tw 10=pt_BR 11=fr */
1084       printf("lang=%d ", get_byte());
1085
1086       printf ("variable_mode=%d\n", get_byte());
1087       printf ("value_mode=%d\n", get_byte());
1088       if (!match_u64(0))
1089         match_u64_assert(UINT64_MAX);
1090       match_u32_assert(0);
1091       match_u32_assert(0);
1092       match_u32_assert(0);
1093       match_u32_assert(0);
1094       match_byte_assert(0);
1095       get_bool();
1096       match_byte_assert(1);
1097       pos = array_start;
1098
1099       assert(get_end() == inner_end);
1100       printf("<heights>");
1101       int n_heights = get_u32();
1102       for (int i = 0; i < n_heights; i++)
1103         {
1104           if (i)
1105             putchar(' ');
1106           printf("%d", get_u32());
1107         }
1108       printf("</heights>\n");
1109
1110       int n_style_map = get_u32();
1111       for (int i = 0; i < n_style_map; i++)
1112         {
1113           uint64_t cell = get_u64();
1114           int style = get_u16();
1115           printf("<style-map cell=\"%lu\" style=\"%d\"/>\n", cell, style);
1116         }
1117
1118       int n_styles = get_u32();
1119       for (int i = 0; i < n_styles; i++)
1120         {
1121           printf("<cell-style index=\"%d\"", i);
1122           dump_style(stdout);
1123           dump_style2(stdout);
1124           printf("/>\n");
1125         }
1126
1127       pos = get_end();
1128       assert(pos == inner_end);
1129
1130       /* Second inner envelope. */
1131       assert(get_end() == outer_end);
1132
1133       match_byte_assert(1);
1134       match_byte_assert(0);
1135       if (!match_byte(3) && !match_byte(4))
1136         match_byte_assert(5);
1137       match_byte_assert(0);
1138       match_byte_assert(0);
1139       match_byte_assert(0);
1140
1141       printf("<command>%s</command>\n", get_string());
1142       printf("<command-local>%s</command-local>\n", get_string());
1143       printf("<language>%s</language>\n", get_string());
1144       printf("<charset>%s</charset>\n", get_string());
1145       printf("<locale>%s</locale>\n", get_string());
1146
1147       get_bool();
1148       get_bool();
1149       get_bool();
1150       get_bool();
1151
1152       printf("<epoch2>%d</epoch2>\n", get_u32());
1153
1154       if (match_byte('.'))
1155         {
1156           if (!match_byte(',') && !match_byte('\''))
1157             match_byte_assert(' ');
1158         }
1159       else
1160         {
1161           match_byte_assert(',');
1162           if (!match_byte('.') && !match_byte(' ') && !match_byte(','))
1163             match_byte_assert(0);
1164         }
1165
1166       printf ("small: %g\n", get_double());
1167
1168       match_byte_assert(1);
1169       if (outer_end - pos > 6)
1170         {
1171           /* There might be a pair of strings representing a dataset and
1172              datafile name, or there might be a set of custom currency strings.
1173              The custom currency strings start with a pair of integers, so we
1174              can distinguish these from a string by checking for a null byte; a
1175              small 32-bit integer will always contain a null and a text string
1176              never will. */
1177           int save_pos = pos;
1178           int len = get_u32();
1179           bool has_dataset = !memchr(&data[pos], '\0', len);
1180           pos = save_pos;
1181
1182           if (has_dataset)
1183             {
1184               printf("<dataset>%s</dataset>\n", get_string());
1185               printf("<datafile>%s</datafile>\n", get_string());
1186
1187               match_u32_assert(0);
1188
1189               time_t date = get_u32();
1190               struct tm tm = *localtime(&date);
1191               char s[128];
1192               strftime(s, sizeof s, "%a, %d %b %Y %H:%M:%S %z", &tm);
1193               printf("<date>%s</date>\n", s);
1194
1195               match_u32_assert(0);
1196             }
1197         }
1198
1199       if (match_u32(5))
1200         {
1201           for (int i = 0; i < 5; i++)
1202             printf("<CC%c>%s</CC%c>\n", 'A' + i, get_string(), 'A' + i);
1203         }
1204       else
1205         match_u32_assert(0);
1206
1207       match_byte_assert('.');
1208       get_bool();
1209
1210       if (pos < outer_end)
1211         {
1212           get_u32();
1213           match_u32_assert(0);
1214         }
1215       assert(pos == outer_end);
1216
1217       pos = outer_end;
1218     }
1219   else if (outer_end != pos)
1220     {
1221       pos += 14;
1222       printf("<command>%s</command>\n", get_string());
1223       printf("<command-local>%s</command-local>\n", get_string());
1224       printf("<language>%s</command>\n", get_string());
1225       printf("<charset>%s</charset>\n", get_string());
1226       printf("<locale>%s</locale>\n", get_string());
1227       get_bool();
1228       match_byte_assert(0);
1229       get_bool();
1230       get_bool();
1231
1232       printf("<epoch2>%d</epoch2>\n", get_u32());
1233       int decimal = data[pos];
1234       int grouping = data[pos + 1];
1235       if (match_byte('.'))
1236         {
1237           if (!match_byte(',') && !match_byte('\''))
1238             match_byte_assert(' ');
1239         }
1240       else
1241         {
1242           match_byte_assert(',');
1243           if (!match_byte('.') && !match_byte(' ') && !match_byte(','))
1244             match_byte_assert(0);
1245         }
1246       printf("<format decimal=\"%c\"", decimal);
1247       if (grouping)
1248         printf(" grouping=\"%c\"", grouping);
1249       printf("\"/>\n");
1250       if (match_u32(5))
1251         {
1252           for (int i = 0; i < 5; i++)
1253             printf("<CC%c>%s</CC%c>\n", 'A' + i, get_string(), 'A' + i);
1254         }
1255       else
1256         match_u32_assert(0);
1257
1258       match_byte_assert('.');
1259       get_bool();
1260
1261       assert(pos == outer_end);
1262       pos = outer_end;
1263     }
1264 }
1265
1266 static const char *
1267 format_name (int format, char *buf)
1268 {
1269   switch (format)
1270     {
1271     case 1: return "A";
1272     case 2: return "AHEX";
1273     case 3: return "COMMA";
1274     case 4: return "DOLLAR";
1275     case 5: return "F";
1276     case 6: return "IB";
1277     case 7: return "PIBHEX";
1278     case 8: return "P";
1279     case 9: return "PIB";
1280     case 10: return "PK";
1281     case 11: return "RB";
1282     case 12: return "RBHEX";
1283     case 15: return "Z";
1284     case 16: return "N";
1285     case 17: return "E";
1286     case 20: return "DATE";
1287     case 21: return "TIME";
1288     case 22: return "DATETIME";
1289     case 23: return "ADATE";
1290     case 24: return "JDATE";
1291     case 25: return "DTIME";
1292     case 26: return "WKDAY";
1293     case 27: return "MONTH";
1294     case 28: return "MOYR";
1295     case 29: return "QYR";
1296     case 30: return "WKYR";
1297     case 31: return "PCT";
1298     case 32: return "DOT";
1299     case 33: return "CCA";
1300     case 34: return "CCB";
1301     case 35: return "CCC";
1302     case 36: return "CCD";
1303     case 37: return "CCE";
1304     case 38: return "EDATE";
1305     case 39: return "SDATE";
1306     case 40: return "MTIME";
1307     case 41: return "YMDHMS";
1308     default: sprintf(buf, "(%d)", format); return buf;
1309     }
1310 }
1311
1312 int
1313 main(int argc, char *argv[])
1314 {
1315   if (argc != 2)
1316     {
1317       fprintf (stderr, "usage: %s FILE.bin", argv[0]);
1318       exit (1);
1319     }
1320
1321   filename = argv[1];
1322   int fd = open(filename, O_RDONLY);
1323   if (fd < 0)
1324     {
1325       fprintf (stderr, "%s: open failed (%s)", filename, strerror (errno));
1326       exit (1);
1327     }
1328
1329   struct stat s;
1330   if (fstat(fd, &s))
1331     {
1332       perror("fstat");
1333       exit(1);
1334     }
1335   n = s.st_size;
1336   data = malloc(n);
1337   if (!data)
1338     {
1339       perror("malloc");
1340       exit(1);
1341     }
1342   if (read(fd, data, n) != n)
1343     {
1344       perror("read");
1345       exit(1);
1346     }
1347   close(fd);
1348
1349 #if 0
1350   unsigned int prev_end = 0;
1351   for (pos = 0; pos + 50 < n; pos++)
1352     {
1353       if (data[pos + 0] == 0xff &&
1354           data[pos + 1] == 0xff &&
1355           data[pos + 2] == 0 &&
1356           data[pos + 3] == 0)
1357         {
1358           int len = data[pos + 4] + (data[pos + 5] << 8);
1359           if (len < 3 || pos + len + 6 >= n || !all_utf8 ((char *) &data[pos + 6], len))
1360             continue;
1361
1362           printf ("+%04x %04x...%04x: %-25.*s\n",
1363                   pos - prev_end, pos, pos + 6 + len,
1364                   len < 50 ? (int) len : 50, &data[pos + 6]);
1365           prev_end = pos + 6 + len;
1366         }
1367     }
1368 #endif
1369 #if 0
1370   for (pos = 0; pos + 50 < n; pos++)
1371     {
1372       if (data[pos + 0] == 'L' &&
1373           data[pos + 1] == 'o' &&
1374           data[pos + 2] == 'g' &&
1375           !all_utf8((char *) &data[pos + 3], 1) &&
1376           data[pos - 1] != 'v')
1377         {
1378           //printf ("%04x: ", pos);
1379           unsigned int p = pos;
1380           while (all_utf8 ((char *) &data[p], 1))
1381             p--;
1382           hex_dump (stdout, p - 28, 38);
1383         }
1384     }
1385 #endif
1386   unsigned int prev_end = 0;
1387   for (pos = 2; pos + 50 < n; pos++)
1388     {
1389       static const int cell_prefix[] = {
1390         0x00, 0x03,
1391         0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -1,
1392         0x80, 0x01, -1, -1, -1, -1,
1393       };
1394       size_t cell_prefix_len = sizeof cell_prefix / sizeof *cell_prefix;
1395       if (match_bytes(pos, cell_prefix, cell_prefix_len))
1396         {
1397           if (prev_end != pos)
1398             {
1399               //printf ("%04x ", prev_end);
1400               hex_dump (stdout, prev_end, pos - prev_end);
1401             }
1402
1403           char buf[64];
1404           printf ("cell %s%d.%d ",
1405                   format_name (data[pos + 18], buf),
1406                   data[pos + 17],
1407                   data[pos + 16]);
1408
1409           int len = cell_prefix_len;
1410           if (data[pos + 19] == 0)
1411             {
1412               assert (data[pos + 13] == 5);
1413               if (data[pos + 20] == 0)
1414                 {
1415                   int count = (data[pos + 22]);
1416                   printf ("%d %d \"%.*s\"\n",
1417                           data[pos + 21], data[pos + 22],
1418                           count, &data[pos + 23]);
1419                   len = 23 + count;
1420                 }
1421               else if (data[pos + 20] == 1
1422                        && data[pos + 21] == 0xff
1423                        && data[pos + 22] == 0xff)
1424                 {
1425                   int count = 255;
1426                   printf ("%d \"%.*s\"\n", count, data[pos + 23],
1427                           &data[pos + 24]);
1428                   len = 23 + count;
1429                 }
1430               else if (data[pos + 20] == 1)
1431                 {
1432                   int count = (data[pos + 21]);
1433                   printf ("\"%.*s\"\n",
1434                           count, &data[pos + 22]);
1435                   len = 22 + count;
1436                 }
1437               else
1438                 abort ();
1439             }
1440           else if (data[pos + 19] == 128)
1441             {
1442               /* pos + 13 is usually 22...53 but also 5 or 69 */
1443               /* pos + 20 is 2 most of the time, occasionally 1 */
1444               printf ("%d %d ", data[pos + 13], data[pos + 20]);
1445               double d = *(double *) &data[pos + 21];
1446               len = 29;
1447               const union
1448                 {
1449                   uint8_t b[8];
1450                   double d;
1451                 }
1452               sysmis = {.b = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff}};
1453               if (d == sysmis.d)
1454                 printf ("sysmis\n");
1455               else
1456                 printf ("%f\n", d);
1457             }
1458           else
1459             abort ();
1460           pos += len - 1;
1461           prev_end = pos + 1;
1462           continue;
1463         }
1464
1465       static const int col_prefix[] = {
1466         0x11, 0x80, 0x00, -1, 0x00, 0x00, 0x00, 0x01, 0x00
1467       };
1468       size_t col_prefix_len = sizeof col_prefix / sizeof *col_prefix;
1469       if (match_bytes(pos, col_prefix, col_prefix_len))
1470         {
1471           if (prev_end != pos)
1472             {
1473               //printf ("%04x ", prev_end);
1474               hex_dump (stdout, prev_end, pos - prev_end);
1475             }
1476
1477           printf ("col %d\n", data[pos + 3]);
1478           pos += col_prefix_len - 1;
1479           prev_end = pos + 1;
1480           continue;
1481         }
1482
1483       static const int record_prefix[] = {
1484         0xff, 0xff, 0x00, 0x00,
1485       };
1486       size_t record_prefix_len = sizeof record_prefix / sizeof *record_prefix;
1487       if (match_bytes(pos, record_prefix, record_prefix_len))
1488         {
1489           if (prev_end != pos)
1490             {
1491               //printf ("%04x ", prev_end);
1492               hex_dump (stdout, prev_end, pos - prev_end);
1493             }
1494
1495           putchar ('\n');
1496
1497           //printf ("%d ", pos % 4);
1498           int len = record_prefix_len;
1499           int slen = data[pos + 4] + (data[pos + 5] << 8);
1500           if (slen > 2 && slen < 256 && all_utf8((char *) &data[pos + 6], slen))
1501             {
1502               printf ("%.*s ", slen, &data[pos + 6]);
1503               len = slen + 6;
1504             }
1505           else
1506             printf ("notitle ");
1507
1508           pos += len - 1;
1509           prev_end = pos + 1;
1510           continue;
1511         }
1512
1513       static const int number_prefix[] = {
1514         0x80, 0x02
1515       };
1516       size_t number_prefix_len = sizeof number_prefix / sizeof *number_prefix;
1517       if (match_bytes(pos, number_prefix, number_prefix_len))
1518         {
1519           if (prev_end != pos)
1520             {
1521               //printf ("%04x ", prev_end);
1522               hex_dump (stdout, prev_end, pos - prev_end);
1523             }
1524           prev_end = pos;
1525
1526           double d = *(double *) &data[pos + number_prefix_len];
1527           printf ("float%f ", d);
1528           pos += 10 - 9;
1529           prev_end = pos + 1;
1530           continue;
1531         }
1532
1533       static const int string_prefix[] = {
1534         0x80, 0x01, 0x02, 0x28, 0x05, 0x00, 0x01
1535       };
1536       size_t string_prefix_len = sizeof string_prefix / sizeof *string_prefix;
1537       if (match_bytes(pos, string_prefix, string_prefix_len) && data[pos + string_prefix_len] != 255)
1538         {
1539           if (prev_end != pos)
1540             {
1541               //printf ("%04x ", prev_end);
1542               hex_dump (stdout, prev_end, pos - prev_end);
1543             }
1544           prev_end = pos;
1545
1546           printf ("\nstring %.*s\n", (int) data[pos + 7], &data[pos + 8]);
1547         }
1548       if (match_bytes(pos, string_prefix, string_prefix_len) && data[pos + string_prefix_len] == 255)
1549         {
1550           if (prev_end != pos)
1551             {
1552               //printf ("%04x ", prev_end);
1553               hex_dump (stdout, prev_end, pos - prev_end);
1554             }
1555           prev_end = pos;
1556
1557           int len = data[pos + 8] + (data[pos + 9] << 8);
1558           printf ("\nlongstring %.*s\n", len, &data[pos + 10]);
1559         }
1560
1561       
1562
1563 #if 0
1564       static const int heading_prefix[] = {
1565         -1, 0x00, 0x00, 0x00, 0x50, 0x80, 0x00, 0x52, 0x80, 0x00, -1, 0x00,
1566         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00,
1567         0x03, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
1568         0x00, 0x05, 0x80, 0x01, 0x02, 0x28, 0x05, 0x00, 0x01
1569       };
1570       size_t heading_prefix_len = sizeof heading_prefix / sizeof *heading_prefix;
1571       if (match_bytes(pos, heading_prefix, heading_prefix_len))
1572         {
1573           if (prev_end != pos)
1574             {
1575               //printf ("%04x ", prev_end);
1576               hex_dump (stdout, prev_end, pos - prev_end);
1577             }
1578
1579           printf ("heading %d %d\n", data[pos],data[pos + 10]);
1580           pos += heading_prefix_len - 1;
1581           prev_end = pos + 1;
1582           continue;
1583         }
1584
1585       static const int font_prefix[] = {
1586         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -1, 0x80, 0x00, 0x01, 0x00,
1587         0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, -1,
1588         0x80, 0x00, -1, 0x00, -1, 0x00, 0xc8, 0x00, -1, -1, -1, -1, -1,
1589         0x00, -1, 0x00, 0x00, 0x00, 0x01, 0x00, -1,
1590         0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1591         0x00, 0x00, 0x00, -1, -1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1592         0x00, 0x00, -1 /* 12 or 22 */,
1593       };
1594       size_t font_prefix_len = sizeof font_prefix / sizeof *font_prefix;
1595       if (match_bytes(pos, font_prefix, font_prefix_len))
1596         {
1597           if (prev_end != pos)
1598             {
1599               //printf ("%04x", prev_end);
1600               hex_dump (stdout, prev_end, pos - prev_end);
1601             }
1602
1603           printf ("font %d %d %d %d %d %d %d %d %d %d\n",
1604                   data[pos + 24], data[pos + 26],
1605                   data[pos + 30], data[pos + 31], data[pos + 32],
1606                   data[pos + 33], data[pos + 34], data[pos + 36],
1607                   data[pos + 58], data[pos + 59]);
1608           pos += font_prefix_len - 1;
1609           prev_end = pos + 1;
1610           continue;
1611         }
1612
1613       static const int table_prefix[] = {
1614         -1 /* ed or e9 */, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
1615         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x02, 0x00, 0x00,
1616         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x41, 0x72, 0x69,
1617         0x61, 0x6c, 0x00, -1, 0x00, -1, 0x00, 0x00, 0x00, 0x00, 0x00,
1618         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1619         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00,
1620         0x00, 0x00, 0x00, 0x00, -1, 0x00, 0x00, 0x00, -1,
1621       };
1622       size_t table_prefix_len = sizeof table_prefix / sizeof *table_prefix;
1623       if (match_bytes(pos, table_prefix, table_prefix_len))
1624         {
1625           if (prev_end != pos)
1626             {
1627               //printf ("%04x", prev_end);
1628               hex_dump (stdout, prev_end, pos - prev_end);
1629             }
1630
1631           printf ("table %d\n", data[pos + 72]);
1632           pos += table_prefix_len - 1;
1633           prev_end = pos + 1;
1634           continue;
1635         }
1636 #endif
1637
1638       static const int dim_prefix[] = {
1639         0x00, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, -1,
1640         0x00, 0x00, 0x00, 0x00, -1, 0x80, 0x01, 0x02, -1,
1641         -1, -1, -1 /* 00 or 01 */,
1642       };
1643       size_t dim_prefix_len = sizeof dim_prefix / sizeof *dim_prefix;
1644       if (match_bytes(pos, dim_prefix, dim_prefix_len))
1645         {
1646           if (prev_end != pos)
1647             {
1648               //printf ("%04x", prev_end);
1649               hex_dump (stdout, prev_end, pos - prev_end);
1650             }
1651
1652           printf ("dim %d %d %d %d %d\n", data[pos + 8], data[pos + 13],
1653                   data[pos + 17], data[pos + 18], data[pos + 19]);
1654           pos += dim_prefix_len - 1;
1655           prev_end = pos + 1;
1656           continue;
1657         }
1658
1659       static const int dim2_prefix[] = {
1660         0x50, 0x80, 0x00, 0x52, 0x80, 0x00, -1, 0x00, 0x00, 0x00, -1, 0, 0, 0,
1661         -1, -1, -1, -1
1662       };
1663       size_t dim2_prefix_len = sizeof dim2_prefix / sizeof *dim2_prefix;
1664       if (match_bytes(pos, dim2_prefix, dim2_prefix_len))
1665         {
1666           if (prev_end != pos)
1667             {
1668               //printf ("%04x", prev_end);
1669               hex_dump (stdout, prev_end, pos - prev_end);
1670             }
1671
1672           int16_t x = *(int16_t *) &data[pos + 14];
1673           int16_t y = *(int16_t *) &data[pos + 16];
1674           printf ("dim2 %d %d %d %d\n", data[pos + 6], data[pos + 10], x, y);
1675           pos += dim2_prefix_len - 1;
1676           prev_end = pos + 1;
1677           continue;
1678         }
1679
1680       if (!is_ascii(data[pos]))
1681         continue;
1682
1683       unsigned int start = pos;
1684       unsigned int end = pos + 1;
1685       while (is_ascii(data[end]))
1686         end++;
1687
1688       unsigned int len = end - start;
1689       if (len < 3)
1690         continue;
1691
1692       unsigned int len2 = data[start - 2] + (data[start - 1] << 8);
1693       unsigned int len3 = data[start - 1];
1694       int length_bytes;
1695       if (len2 && len2 <= len)
1696         {
1697           length_bytes = 2;
1698           len = len2;
1699         }
1700       else if (len3 && len3 <= len)
1701         {
1702           length_bytes = 1;
1703           len = len3;
1704         }
1705       else
1706         continue;
1707       if (len < 3)
1708         continue;
1709       end = start + len;
1710
1711       unsigned real_start = start - length_bytes;
1712       if (prev_end != real_start)
1713         {
1714           //printf ("%04x ", prev_end);
1715           hex_dump (stdout, prev_end, real_start - prev_end);
1716         }
1717       //printf ("%04x ", real_start);
1718       printf ("\"%.*s\"\n", 
1719               (int) end - start, (char *) &data[start]);
1720       prev_end = end;
1721       pos = end - 1;
1722     }
1723
1724   exit(0);
1725
1726   return 0;
1727 }