0eade561af61b036296e761e71280d201f0b6b0e
[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               int subn = get_u32 ();
167               printf ("nested %d bytes", subn);
168               pos += subn;
169             }
170           else if (match_u32 (2))
171             {
172               printf("(special 2)");
173               match_byte_assert(0);
174               match_byte_assert(0);
175               match_u32_assert(1);
176               match_byte_assert(0);
177               match_byte_assert(0);
178               int subn = get_u32 ();
179               printf ("nested %d bytes", subn);
180               pos += subn;
181             }
182           else
183             {
184               match_u32_assert(3);
185               printf("(special 3)");
186               match_byte_assert(0);
187               match_byte_assert(0);
188               match_byte_assert(1);
189               match_byte_assert(0);
190               int subn = get_u32 ();
191               printf ("nested %d bytes, ", subn);
192               pos += subn;
193               subn = get_u32 ();
194               printf ("nested %d bytes, ", subn);
195               pos += subn;
196             }
197         }
198       else
199         match_byte_assert (0x58);
200       get_string();
201       printf("string \"%s\"", get_string());
202       match_byte (0);
203       match_byte (0);
204       match_byte (0);
205       match_byte (1);
206       match_byte (1);
207       match_byte (0);
208       match_byte (0);
209       match_byte (0);
210       match_byte (1);
211     }
212   else if (match_byte (5))
213     {
214       match_byte_assert (0x58);
215       printf ("variable \"%s\"", get_string());
216       get_string();
217       if (!match_byte (3))
218         match_byte_assert (2);
219       match_byte (0);
220       match_byte (0);
221       match_byte (0);
222       match_byte (0);
223     }
224   else if (match_byte (2))
225     {
226       unsigned int format;
227       char *var, *vallab;
228       double value;
229
230       match_byte_assert (0x58);
231       format = get_u32 ();
232       value = get_double ();
233       var = get_string ();
234       vallab = get_string ();
235       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
236               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
237       if (!match_u32 (3))
238         match_u32_assert (2);
239       match_byte (0);
240       match_byte (0);
241       match_byte (0);
242       match_byte (0);
243     }
244   else if (match_byte (4))
245     {
246       unsigned int format;
247       char *var, *vallab, *value;
248
249       match_byte_assert (0x58);
250       format = get_u32 ();
251       vallab = get_string ();
252       var = get_string ();
253       match_byte_assert (2);
254       value = get_string ();
255       printf ("value \"%s\" format %d(%d.%d) var \"%s\" vallab \"%s\"",
256               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
257       match_byte (0);
258       match_byte (0);
259       match_byte (0);
260       match_byte (0);
261     }
262   else if (match_byte (1))
263     {
264       unsigned int format;
265       double value;
266
267       match_byte_assert (0x58);
268       format = get_u32 ();
269       value = get_double ();
270       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
271       match_byte (1);
272       match_byte (0);
273       match_byte (0);
274       match_byte (0);
275       match_byte (1);
276     }
277   else if (match_byte (0x31))
278     {
279       int subn;
280       int total_subs = 1;
281
282       match_u32_assert (0);
283       match_u32_assert (0);
284       subn = get_u32 ();
285       printf ("nested %d bytes", subn);
286       pos += subn;
287       printf ("; \"%s\", substitutions:", get_string());
288       for (;;)
289         {
290           int n_subst = get_u32();
291           if (!n_subst)
292             break;
293           printf (" %d", n_subst);
294           total_subs *= n_subst;
295         }
296
297       for (int i = 0; i < total_subs; i++)
298         {
299           putc ('\n', stdout);
300           dump_value (level + 1);
301         }
302     }
303   else
304     {
305       int total_subs = 1;
306
307       match_byte_assert (0x58);
308       printf ("\"%s\" with substitutions:", get_string());
309       for (;;)
310         {
311           int n_subst = get_u32();
312           if (!n_subst)
313             break;
314           printf (" %d", n_subst);
315           total_subs *= n_subst;
316         }
317
318       for (int i = 0; i < total_subs; i++)
319         {
320           putc ('\n', stdout);
321           dump_value (level + 1);
322         }
323     }
324 }
325
326 static void
327 dump_dim_value(int level)
328 {
329   for (int i = 0; i <= level; i++)
330     printf ("    ");
331
332   if (match_byte (3))
333     {
334       get_string();
335       if (match_byte (0x31))
336         {
337           match_u32 (1);
338           printf("(footnote %d) ", get_u32());
339           match_byte_assert (0);
340           match_byte_assert (0);
341           int subn = get_u32 ();
342           printf ("nested %d bytes", subn);
343           pos += subn;
344         }
345       else
346         match_byte_assert (0x58);
347       get_string();
348       printf("string \"%s\"", get_string());
349       match_byte (0);
350       match_byte_assert (1);
351       match_byte (0);
352       match_byte (0);
353       match_byte (0);
354       match_byte (1);
355     }
356   else if (match_byte (5))
357     {
358       match_byte_assert (0x58);
359       printf ("variable \"%s\"", get_string());
360       get_string();
361       match_byte_assert (2);
362     }
363   else if (match_byte (2))
364     {
365       unsigned int format;
366       char *var, *vallab;
367       double value;
368
369       match_byte_assert (0x58);
370       format = get_u32 ();
371       value = get_double ();
372       var = get_string ();
373       vallab = get_string ();
374       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
375               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
376       if (!match_u32 (3))
377         match_u32_assert (2);
378       match_byte (0);
379     }
380   else if (match_byte (1))
381     {
382       unsigned int format;
383       double value;
384
385       match_byte_assert (0x58);
386       format = get_u32 ();
387       value = get_double ();
388       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
389       match_byte (1);
390       match_byte (0);
391       match_byte (0);
392       match_byte (0);
393       match_byte (1);
394     }
395   else
396     {
397       int subn;
398       int total_subs = 1;
399
400       match_byte (0);
401       match_byte_assert (0x31);
402       match_u32_assert (0);
403       match_u32_assert (0);
404       subn = get_u32 ();
405       printf ("nested %d bytes", subn);
406       pos += subn;
407       printf ("; \"%s\", substitutions:", get_string());
408       for (;;)
409         {
410           int n_subst = get_u32();
411           if (!n_subst)
412             break;
413           printf (" %d", n_subst);
414           total_subs *= n_subst;
415         }
416
417       for (int i = 0; i < total_subs; i++)
418         {
419           putc ('\n', stdout);
420           dump_value (level + 1);
421         }
422     }
423 }
424
425 static void
426 dump_category(int level)
427 {
428   match_byte (0);
429   match_byte (0);
430   match_byte (0);
431   match_byte (0);
432   dump_value (level);
433
434   if (match_u32 (2))
435     get_u32 ();
436   else if (match_u32 (1))
437     {
438       match_byte (0);
439       match_byte (0);
440       match_byte (0);
441       get_u32 ();
442     }
443   else if (match_byte (1))
444     {
445       match_byte (0);
446       match_u32_assert (1);
447       match_byte (0);
448       get_u32();
449     }
450   else
451     {
452       match_u32_assert (0);
453       get_u32 ();
454     }
455
456   int n_categories = get_u32();
457   if (n_categories > 0)
458     printf (", %d subcategories:", n_categories);
459   printf("\n");
460   for (int i = 0; i < n_categories; i++)
461     dump_category (level + 1);
462 }
463
464 static void
465 dump_dim(void)
466 {
467   int n_categories;
468   printf("next dim\n");
469   match_byte(0);
470   if (match_byte(3))
471     {
472       get_string();
473       match_byte_assert(0x58);
474       get_string();
475       printf("string \"%s\": ", get_string());
476       match_byte_assert(1);
477     }
478   else if (match_byte(5)) 
479     {
480       match_byte_assert(0x58);
481       printf("variable \"%s\": ", get_string());
482       get_string();
483       if (!match_byte(2))
484         match_byte_assert(3);
485     }
486   else
487     {
488       int subn;
489       int total_subs = 1;
490
491       match_byte_assert(0x31);
492       match_u32_assert (0);
493       match_u32_assert (0);
494       subn = get_u32 ();
495       printf ("nested %d bytes", subn);
496       pos += subn;
497       printf ("; \"%s\", substitutions:", get_string());
498       for (;;)
499         {
500           int n_subst = get_u32();
501           if (!n_subst)
502             break;
503           printf (" %d", n_subst);
504           total_subs *= n_subst;
505         }
506
507       for (int i = 0; i < total_subs; i++)
508         {
509           putc ('\n', stdout);
510           dump_dim_value (0);
511         }
512     }
513
514   match_byte_assert(0);
515   if (!match_byte(0) && !match_byte(1))
516     match_byte_assert(2);
517   match_u32_assert(2);
518   if (!match_byte(0))
519     match_byte_assert(1);
520   match_byte(0);
521   match_byte(0);
522   match_byte(0);
523   match_byte(0);
524   get_u32();
525   match_byte(0);
526   match_byte(0);
527   match_byte(0);
528   match_byte(0);
529   n_categories = get_u32();
530   printf("%d nested categories\n", n_categories);
531   for (int i = 0; i < n_categories; i++)
532     dump_category (0);
533 }
534
535 static void
536 dump_dims(void)
537 {
538   int n_dims = get_u32();
539
540   printf ("%u dimensions\n", n_dims);
541   for (int i = 0; i < n_dims; i++)
542     {
543       printf("\n");
544       dump_dim ();
545     }
546 }
547
548 int
549 main(int argc, char *argv[])
550 {
551   size_t start;
552   struct stat s;
553
554   if (isatty(STDIN_FILENO))
555     {
556       fprintf(stderr, "redirect stdin from a .bin file\n");
557       exit(1);
558     }
559   if (fstat(STDIN_FILENO, &s))
560     {
561       perror("fstat");
562       exit(1);
563     }
564   n = s.st_size;
565   data = malloc(n);
566   if (!data)
567     {
568       perror("malloc");
569       exit(1);
570     }
571   if (read(STDIN_FILENO, data, n) != n)
572     {
573       perror("read");
574       exit(1);
575     }
576
577   if (argc > 1)
578     {
579       if (!strcmp(argv[1], "title0"))
580         {
581           pos = 0x27;
582           if (match_byte (0x03)
583               || (match_byte (0x05) && match_byte (0x58)))
584             printf ("%s\n", get_string());
585           else
586             printf ("<unknown>\n");
587           return 0;
588         }
589       if (!strcmp(argv[1], "title"))
590         {
591           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
592           start = 0x27;
593           n = find(fonts, sizeof fonts - 1);
594         }
595       else if (!strcmp(argv[1], "fonts"))
596         {
597           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
598           const char styles[] = "\xf0\0\0\0";
599           start = find(fonts, sizeof fonts - 1);
600           n = find(styles, sizeof styles - 1);
601         }
602       else if (!strcmp(argv[1], "styles"))
603         {
604           const char styles[] = "\xf0\0\0\0";
605           const char dimensions[] = "-,,,.\0";
606           start = find(styles, sizeof styles - 1);
607           n = find(dimensions, sizeof dimensions - 1) + sizeof dimensions - 1;
608         }
609       else if (!strcmp(argv[1], "dimensions"))
610         {
611           {
612             const char dimensions[] = "-,,,.\0";
613             start = try_find_tail(dimensions, sizeof dimensions - 1);
614           }
615
616           if (!start)
617             {
618               const char dimensions[] = "-,,, .\0";
619               start = find_tail(dimensions, sizeof dimensions - 1);
620             }
621
622           pos = start;
623           dump_dims ();
624           return 0;
625         }
626       else
627         {
628           fprintf (stderr, "unknown section %s\n", argv[1]);
629           exit(1);
630         }
631     }
632   else
633     start = 0x27;
634
635   for (size_t i = start; i < n; )
636     {
637       if (i + 5 <= n
638           && data[i]
639           && !data[i + 1]
640           && !data[i + 2]
641           && !data[i + 3]
642           && i + 4 + data[i] <= n
643           && all_ascii(&data[i + 4], data[i]))
644         {
645           fputs("\n\"", stdout);
646           fwrite(&data[i + 4], 1, data[i], stdout);
647           fputs("\" ", stdout);
648
649           i += 4 + data[i];
650         }
651       else if (i + 12 <= n
652                && data[i + 1] == 40
653                && data[i + 2] == 5
654                && data[i + 3] == 0)
655         {
656           double d;
657
658           memcpy (&d, &data[i + 4], 8);
659           printf ("F40.%d(%.*f)\n", data[i], data[i], d);
660           i += 12;
661         }
662       else if (i + 12 <= n
663                && data[i + 1] == 40
664                && data[i + 2] == 31
665                && data[i + 3] == 0)
666         {
667           double d;
668
669           memcpy (&d, &data[i + 4], 8);
670           printf ("PCT40.%d(%.*f)\n", data[i], data[i], d);
671           i += 12;
672         }
673       else if (i + 4 <= n
674                && (data[i] && data[i] != 88 && data[i] != 0x41)
675                && !data[i + 1]
676                && !data[i + 2]
677                && !data[i + 3])
678         {
679           printf ("i%d ", data[i]);
680           i += 4;
681         }
682       else
683         {
684           printf("%02x ", data[i]);
685           i++;
686         }
687     }
688
689   return 0;
690 }