0a92002672de550e728b21e251850807d6ff87f9
[pspp] / dump.c
1 #include <stdbool.h>
2 #include <stdint.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <sys/stat.h>
7 #include <unistd.h>
8
9 static uint8_t *data;
10 static size_t n;
11
12 static bool
13 all_ascii(const uint8_t *p, size_t n)
14 {
15   for (size_t i = 0; i < n; i++)
16     if (p[i] < 32 || p[i] > 126)
17       return false;
18   return true;
19 }
20
21 static size_t
22 try_find(const char *target, size_t target_len)
23 {
24   const uint8_t *pos = (const uint8_t *) memmem (data, n, target, target_len);
25   return pos ? pos - data : 0;
26 }
27
28 static size_t
29 try_find_tail(const char *target, size_t target_len)
30 {
31   size_t pos = try_find(target, target_len);
32   return pos ? pos + target_len : 0;
33 }
34
35 static size_t
36 find(const char *target, size_t target_len)
37 {
38   size_t pos = try_find(target, target_len);
39   if (!pos)
40     {
41       fprintf (stderr, "not found\n");
42       exit(1);
43     }
44   return pos;
45 }
46
47 static size_t
48 find_tail(const char *target, size_t target_len)
49 {
50   size_t pos = try_find_tail(target, target_len);
51   if (!pos)
52     {
53       fprintf (stderr, "not found\n");
54       exit(1);
55     }
56   return pos;
57 }
58
59 size_t pos;
60
61 #define XSTR(x) #x
62 #define STR(x) XSTR(x)
63 #define WHERE __FILE__":" STR(__LINE__)
64
65 static unsigned int
66 get_u32(void)
67 {
68   uint32_t x;
69   memcpy(&x, &data[pos], 4);
70   pos += 4;
71   return x;
72 }
73
74 static double
75 get_double(void)
76 {
77   double x;
78   memcpy(&x, &data[pos], 8);
79   pos += 8;
80   return x;
81 }
82
83 static bool
84 match_u32(uint32_t x)
85 {
86   if (get_u32() == x)
87     return true;
88   pos -= 4;
89   return false;
90 }
91
92 static void
93 match_u32_assert(uint32_t x, const char *where)
94 {
95   unsigned int y = get_u32();
96   if (x != y)
97     {
98       fprintf(stderr, "%s: 0x%x: expected i%u, got i%u\n", where, pos - 4, x, y);
99       exit(1);
100     }
101 }
102 #define match_u32_assert(x) match_u32_assert(x, WHERE)
103
104 static bool
105 match_byte(uint8_t b)
106 {
107   if (data[pos] == b)
108     {
109       pos++;
110       return true;
111     }
112   else
113     return false;
114 }
115
116 static void
117 match_byte_assert(uint8_t b, const char *where)
118 {
119   if (!match_byte(b))
120     {
121       fprintf(stderr, "%s: 0x%x: expected %02x, got %02x\n", where, pos, b, data[pos]);
122       exit(1);
123     }
124 }
125 #define match_byte_assert(b) match_byte_assert(b, WHERE)
126
127 static char *
128 get_string(const char *where)
129 {
130   if (data[pos + 1] == 0 && data[pos + 2] == 0 && data[pos + 3] == 0
131       /*&& all_ascii(&data[pos + 4], data[pos])*/)
132     {
133       int len = data[pos];
134       char *s = malloc(len + 1);
135
136       memcpy(s, &data[pos + 4], len);
137       s[len] = 0;
138       pos += 4 + data[pos];
139       return s;
140     }
141   else
142     {
143       fprintf(stderr, "%s: 0x%x: expected string\n", where, pos);
144       exit(1);
145     }
146 }
147 #define get_string() get_string(WHERE)
148
149 static void
150 dump_value(int level)
151 {
152   for (int i = 0; i <= level; i++)
153     printf ("    ");
154
155   match_byte (0);
156   if (match_byte (3))
157     {
158       get_string();
159       if (match_byte (0x31))
160         {
161           if (match_u32 (1))
162             {
163               printf("(footnote %d) ", get_u32());
164               match_byte_assert (0);
165               match_byte_assert (0);
166             }
167           else
168             {
169               match_u32_assert (2);
170               printf("(special 2)");
171               match_byte_assert(0);
172               match_byte_assert(0);
173               match_u32_assert(1);
174               match_byte_assert(0);
175               match_byte_assert(0);
176             }
177           int subn = get_u32 ();
178           printf ("nested %d bytes", subn);
179           pos += subn;
180         }
181       else
182         match_byte_assert (0x58);
183       get_string();
184       printf("string \"%s\"", get_string());
185       match_byte (0);
186       match_byte (0);
187       match_byte (0);
188       match_byte (1);
189       match_byte (0);
190       match_byte (0);
191       match_byte (0);
192       match_byte (1);
193     }
194   else if (match_byte (5))
195     {
196       match_byte_assert (0x58);
197       printf ("variable \"%s\"", get_string());
198       get_string();
199       if (!match_byte (3))
200         match_byte_assert (2);
201       match_byte (0);
202       match_byte (0);
203       match_byte (0);
204       match_byte (0);
205     }
206   else if (match_byte (2))
207     {
208       unsigned int format;
209       char *var, *vallab;
210       double value;
211
212       match_byte_assert (0x58);
213       format = get_u32 ();
214       value = get_double ();
215       var = get_string ();
216       vallab = get_string ();
217       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
218               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
219       if (!match_u32 (3))
220         match_u32_assert (2);
221       match_byte (0);
222       match_byte (0);
223       match_byte (0);
224       match_byte (0);
225     }
226   else if (match_byte (4))
227     {
228       unsigned int format;
229       char *var, *vallab, *value;
230
231       match_byte_assert (0x58);
232       format = get_u32 ();
233       vallab = get_string ();
234       var = get_string ();
235       match_byte_assert (2);
236       value = get_string ();
237       printf ("value \"%s\" format %d(%d.%d) var \"%s\" vallab \"%s\"",
238               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
239       match_byte (0);
240       match_byte (0);
241       match_byte (0);
242       match_byte (0);
243     }
244   else if (match_byte (1))
245     {
246       unsigned int format;
247       double value;
248
249       match_byte_assert (0x58);
250       format = get_u32 ();
251       value = get_double ();
252       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
253       match_byte (1);
254       match_byte (0);
255       match_byte (0);
256       match_byte (0);
257       match_byte (1);
258     }
259   else if (match_byte (0x31))
260     {
261       int subn;
262       int total_subs = 1;
263
264       match_u32_assert (0);
265       match_u32_assert (0);
266       subn = get_u32 ();
267       printf ("nested %d bytes", subn);
268       pos += subn;
269       printf ("; \"%s\", substitutions:", get_string());
270       for (;;)
271         {
272           int n_subst = get_u32();
273           if (!n_subst)
274             break;
275           printf (" %d", n_subst);
276           total_subs *= n_subst;
277         }
278
279       for (int i = 0; i < total_subs; i++)
280         {
281           putc ('\n', stdout);
282           dump_value (level + 1);
283         }
284     }
285   else
286     {
287       int total_subs = 1;
288
289       match_byte_assert (0x58);
290       printf ("\"%s\" with substitutions:", get_string());
291       for (;;)
292         {
293           int n_subst = get_u32();
294           if (!n_subst)
295             break;
296           printf (" %d", n_subst);
297           total_subs *= n_subst;
298         }
299
300       for (int i = 0; i < total_subs; i++)
301         {
302           putc ('\n', stdout);
303           dump_value (level + 1);
304         }
305     }
306 }
307
308 static void
309 dump_dim_value(int level)
310 {
311   for (int i = 0; i <= level; i++)
312     printf ("    ");
313
314   if (match_byte (3))
315     {
316       get_string();
317       if (match_byte (0x31))
318         {
319           match_u32 (1);
320           printf("(footnote %d) ", get_u32());
321           match_byte_assert (0);
322           match_byte_assert (0);
323           int subn = get_u32 ();
324           printf ("nested %d bytes", subn);
325           pos += subn;
326         }
327       else
328         match_byte_assert (0x58);
329       get_string();
330       printf("string \"%s\"", get_string());
331       match_byte (0);
332       match_byte_assert (1);
333       match_byte (0);
334       match_byte (0);
335       match_byte (0);
336       match_byte (1);
337     }
338   else if (match_byte (5))
339     {
340       match_byte_assert (0x58);
341       printf ("variable \"%s\"", get_string());
342       get_string();
343       match_byte_assert (2);
344     }
345   else if (match_byte (2))
346     {
347       unsigned int format;
348       char *var, *vallab;
349       double value;
350
351       match_byte_assert (0x58);
352       format = get_u32 ();
353       value = get_double ();
354       var = get_string ();
355       vallab = get_string ();
356       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
357               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
358       if (!match_u32 (3))
359         match_u32_assert (2);
360       match_byte (0);
361     }
362   else if (match_byte (1))
363     {
364       unsigned int format;
365       double value;
366
367       match_byte_assert (0x58);
368       format = get_u32 ();
369       value = get_double ();
370       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
371       match_byte (1);
372       match_byte (0);
373       match_byte (0);
374       match_byte (0);
375       match_byte (1);
376     }
377   else
378     {
379       int subn;
380       int total_subs = 1;
381
382       match_byte (0);
383       match_byte_assert (0x31);
384       match_u32_assert (0);
385       match_u32_assert (0);
386       subn = get_u32 ();
387       printf ("nested %d bytes", subn);
388       pos += subn;
389       printf ("; \"%s\", substitutions:", get_string());
390       for (;;)
391         {
392           int n_subst = get_u32();
393           if (!n_subst)
394             break;
395           printf (" %d", n_subst);
396           total_subs *= n_subst;
397         }
398
399       for (int i = 0; i < total_subs; i++)
400         {
401           putc ('\n', stdout);
402           dump_value (level + 1);
403         }
404     }
405 }
406
407 static void
408 dump_category(int level)
409 {
410   match_byte (0);
411   match_byte (0);
412   match_byte (0);
413   match_byte (0);
414   dump_value (level);
415
416   if (match_u32 (2))
417     get_u32 ();
418   else if (match_u32 (1))
419     {
420       match_byte (0);
421       match_byte (0);
422       match_byte (0);
423       get_u32 ();
424     }
425   else if (match_byte (1))
426     {
427       match_byte (0);
428       match_u32_assert (1);
429       match_byte (0);
430       get_u32();
431     }
432   else
433     {
434       match_u32_assert (0);
435       get_u32 ();
436     }
437
438   int n_categories = get_u32();
439   if (n_categories > 0)
440     printf (", %d subcategories:", n_categories);
441   printf("\n");
442   for (int i = 0; i < n_categories; i++)
443     dump_category (level + 1);
444 }
445
446 static void
447 dump_dim(void)
448 {
449   int n_categories;
450   printf("next dim\n");
451   match_byte(0);
452   if (match_byte(3))
453     {
454       get_string();
455       match_byte_assert(0x58);
456       get_string();
457       printf("string \"%s\": ", get_string());
458       match_byte_assert(1);
459     }
460   else if (match_byte(5)) 
461     {
462       match_byte_assert(0x58);
463       printf("variable \"%s\": ", get_string());
464       get_string();
465       if (!match_byte(2))
466         match_byte_assert(3);
467     }
468   else
469     {
470       int subn;
471       int total_subs = 1;
472
473       match_byte_assert(0x31);
474       match_u32_assert (0);
475       match_u32_assert (0);
476       subn = get_u32 ();
477       printf ("nested %d bytes", subn);
478       pos += subn;
479       printf ("; \"%s\", substitutions:", get_string());
480       for (;;)
481         {
482           int n_subst = get_u32();
483           if (!n_subst)
484             break;
485           printf (" %d", n_subst);
486           total_subs *= n_subst;
487         }
488
489       for (int i = 0; i < total_subs; i++)
490         {
491           putc ('\n', stdout);
492           dump_dim_value (0);
493         }
494     }
495
496   match_byte_assert(0);
497   if (!match_byte(0) && !match_byte(1))
498     match_byte_assert(2);
499   match_u32_assert(2);
500   if (!match_byte(0))
501     match_byte_assert(1);
502   match_byte(0);
503   match_byte(0);
504   match_byte(0);
505   match_byte(0);
506   get_u32();
507   match_byte(0);
508   match_byte(0);
509   match_byte(0);
510   match_byte(0);
511   n_categories = get_u32();
512   printf("%d nested categories\n", n_categories);
513   for (int i = 0; i < n_categories; i++)
514     dump_category (0);
515 }
516
517 static void
518 dump_dims(void)
519 {
520   int n_dims = get_u32();
521
522   printf ("%u dimensions\n", n_dims);
523   for (int i = 0; i < n_dims; i++)
524     {
525       printf("\n");
526       dump_dim ();
527     }
528 }
529
530 int
531 main(int argc, char *argv[])
532 {
533   size_t start;
534   struct stat s;
535
536   if (isatty(STDIN_FILENO))
537     {
538       fprintf(stderr, "redirect stdin from a .bin file\n");
539       exit(1);
540     }
541   if (fstat(STDIN_FILENO, &s))
542     {
543       perror("fstat");
544       exit(1);
545     }
546   n = s.st_size;
547   data = malloc(n);
548   if (!data)
549     {
550       perror("malloc");
551       exit(1);
552     }
553   if (read(STDIN_FILENO, data, n) != n)
554     {
555       perror("read");
556       exit(1);
557     }
558
559   if (argc > 1)
560     {
561       if (!strcmp(argv[1], "title0"))
562         {
563           pos = 0x27;
564           if (match_byte (0x03)
565               || (match_byte (0x05) && match_byte (0x58)))
566             printf ("%s\n", get_string());
567           else
568             printf ("<unknown>\n");
569           return 0;
570         }
571       if (!strcmp(argv[1], "title"))
572         {
573           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
574           start = 0x27;
575           n = find(fonts, sizeof fonts - 1);
576         }
577       else if (!strcmp(argv[1], "fonts"))
578         {
579           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
580           const char styles[] = "\xf0\0\0\0";
581           start = find(fonts, sizeof fonts - 1);
582           n = find(styles, sizeof styles - 1);
583         }
584       else if (!strcmp(argv[1], "styles"))
585         {
586           const char styles[] = "\xf0\0\0\0";
587           const char dimensions[] = "-,,,.\0";
588           start = find(styles, sizeof styles - 1);
589           n = find(dimensions, sizeof dimensions - 1) + sizeof dimensions - 1;
590         }
591       else if (!strcmp(argv[1], "dimensions"))
592         {
593           {
594             const char dimensions[] = "-,,,.\0";
595             start = try_find_tail(dimensions, sizeof dimensions - 1);
596           }
597
598           if (!start)
599             {
600               const char dimensions[] = "-,,, .\0";
601               start = find_tail(dimensions, sizeof dimensions - 1);
602             }
603
604           pos = start;
605           dump_dims ();
606           return 0;
607         }
608       else
609         {
610           fprintf (stderr, "unknown section %s\n", argv[1]);
611           exit(1);
612         }
613     }
614   else
615     start = 0x27;
616
617   for (size_t i = start; i < n; )
618     {
619       if (i + 5 <= n
620           && data[i]
621           && !data[i + 1]
622           && !data[i + 2]
623           && !data[i + 3]
624           && i + 4 + data[i] <= n
625           && all_ascii(&data[i + 4], data[i]))
626         {
627           fputs("\n\"", stdout);
628           fwrite(&data[i + 4], 1, data[i], stdout);
629           fputs("\" ", stdout);
630
631           i += 4 + data[i];
632         }
633       else if (i + 12 <= n
634                && data[i + 1] == 40
635                && data[i + 2] == 5
636                && data[i + 3] == 0)
637         {
638           double d;
639
640           memcpy (&d, &data[i + 4], 8);
641           printf ("F40.%d(%.*f)\n", data[i], data[i], d);
642           i += 12;
643         }
644       else if (i + 12 <= n
645                && data[i + 1] == 40
646                && data[i + 2] == 31
647                && data[i + 3] == 0)
648         {
649           double d;
650
651           memcpy (&d, &data[i + 4], 8);
652           printf ("PCT40.%d(%.*f)\n", data[i], data[i], d);
653           i += 12;
654         }
655       else if (i + 4 <= n
656                && (data[i] && data[i] != 88 && data[i] != 0x41)
657                && !data[i + 1]
658                && !data[i + 2]
659                && !data[i + 3])
660         {
661           printf ("i%d ", data[i]);
662           i += 4;
663         }
664       else
665         {
666           printf("%02x ", data[i]);
667           i++;
668         }
669     }
670
671   return 0;
672 }