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