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