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