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