Some progress on interpreting 31s and understanding footnotes.
[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 find(const char *target, size_t target_len)
23 {
24   const uint8_t *pos = (const uint8_t *) memmem (data, n, target, target_len);
25   if (!pos)
26     {
27       fprintf (stderr, "not found\n");
28       exit(1);
29     }
30   return pos - data;
31 }
32
33 size_t pos;
34
35 #define XSTR(x) #x
36 #define STR(x) XSTR(x)
37 #define WHERE __FILE__":" STR(__LINE__)
38
39 static unsigned int
40 get_u32(void)
41 {
42   uint32_t x;
43   memcpy(&x, &data[pos], 4);
44   pos += 4;
45   return x;
46 }
47
48 static double
49 get_double(void)
50 {
51   double x;
52   memcpy(&x, &data[pos], 8);
53   pos += 8;
54   return x;
55 }
56
57 static bool
58 match_u32(uint32_t x)
59 {
60   if (get_u32() == x)
61     return true;
62   pos -= 4;
63   return false;
64 }
65
66 static void
67 match_u32_assert(uint32_t x, const char *where)
68 {
69   unsigned int y = get_u32();
70   if (x != y)
71     {
72       fprintf(stderr, "%s: 0x%x: expected i%u, got i%u\n", where, pos - 4, x, y);
73       exit(1);
74     }
75 }
76 #define match_u32_assert(x) match_u32_assert(x, WHERE)
77
78 static bool
79 match_byte(uint8_t b)
80 {
81   if (data[pos] == b)
82     {
83       pos++;
84       return true;
85     }
86   else
87     return false;
88 }
89
90 static void
91 match_byte_assert(uint8_t b, const char *where)
92 {
93   if (!match_byte(b))
94     {
95       fprintf(stderr, "%s: 0x%x: expected %02x, got %02x\n", where, pos, b, data[pos]);
96       exit(1);
97     }
98 }
99 #define match_byte_assert(b) match_byte_assert(b, WHERE)
100
101 static char *
102 get_string(void)
103 {
104   if (data[pos + 1] == 0 && data[pos + 2] == 0 && data[pos + 3] == 0
105       && all_ascii(&data[pos + 4], data[pos]))
106     {
107       int len = data[pos];
108       char *s = malloc(len + 1);
109
110       memcpy(s, &data[pos + 4], len);
111       s[len] = 0;
112       pos += 4 + data[pos];
113       return s;
114     }
115   else
116     {
117       fprintf(stderr, "0x%x: expected string\n", pos);
118       exit(1);
119     }
120 }
121
122 static void
123 dump_category(int level)
124 {
125   for (int i = 0; i <= level; i++)
126     printf ("    ");
127
128   match_byte (0);
129   match_byte (0);
130   match_byte (0);
131   match_byte (0);
132   if (match_byte (3))
133     {
134       get_string();
135       match_byte_assert (0x58);
136       get_string();
137       printf("string \"%s\"", get_string());
138       match_byte (0);
139       match_byte_assert (1);
140       match_byte (0);
141       match_byte (0);
142       match_byte (0);
143       match_byte (1);
144     }
145   else if (match_byte (5))
146     {
147       match_byte_assert (0x58);
148       printf ("variable \"%s\"", get_string());
149       get_string();
150       if (!match_byte (3))
151         match_byte_assert (2);
152       match_byte (0);
153       match_byte (0);
154       match_byte (0);
155     }
156   else if (match_byte (2))
157     {
158       unsigned int format;
159       char *var, *vallab;
160       double value;
161
162       match_byte_assert (0x58);
163       format = get_u32 ();
164       value = get_double ();
165       var = get_string ();
166       vallab = get_string ();
167       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
168               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
169       if (!match_u32 (3))
170         match_u32_assert (2);
171     }
172   else if (match_byte (1))
173     {
174       unsigned int format;
175       double value;
176
177       match_byte_assert (0x58);
178       format = get_u32 ();
179       value = get_double ();
180       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
181       match_byte (1);
182       match_byte (0);
183       match_byte (0);
184       match_byte (0);
185       match_byte (1);
186     }
187   else
188     {
189       int subn;
190
191       match_byte_assert (0x31);
192       match_u32_assert (0);
193       match_u32_assert (0);
194       subn = get_u32 ();
195       printf ("nested %d bytes", subn);
196       pos += subn;
197       printf ("; \"%s\"", get_string());
198       fprintf (stderr, "got %02x\n", data[pos]);
199       match_byte (1);
200       match_byte (0);
201       match_byte (0);
202       match_byte (0);
203       goto next;
204     }
205
206   if (match_u32 (2))
207     get_u32 ();
208   else if (match_u32 (1))
209     {
210       match_byte (0);
211       match_byte (0);
212       match_byte (0);
213       get_u32 ();
214     }
215   else
216     {
217       match_u32_assert (0);
218       get_u32 ();
219     }
220 next:;
221   int n_categories = get_u32();
222   if (n_categories > 0)
223     printf (", %d subcategories:", n_categories);
224   printf("\n");
225   for (int i = 0; i < n_categories; i++)
226     dump_category (level + 1);
227 }
228
229 static void
230 dump_dim(void)
231 {
232   int n_categories;
233   if (match_byte(3))
234     {
235       get_string();
236       match_byte_assert(0x58);
237       get_string();
238       printf("string \"%s\": ", get_string());
239       match_byte_assert(1);
240     }
241   else if (match_byte(5))
242     {
243       match_byte_assert(0x58);
244       printf("variable \"%s\": ", get_string());
245       get_string();
246       if (!match_byte(2))
247         match_byte_assert(3);
248     }
249   else
250     {
251       fprintf(stderr, "%08x: unexpected byte\n", pos);
252       exit(1);
253     }
254
255   match_byte_assert(0);
256   if (!match_byte(0) && !match_byte(1))
257     match_byte_assert(2);
258   match_u32_assert(2);
259   if (!match_byte(0))
260     match_byte_assert(1);
261   match_byte(0);
262   match_byte(0);
263   match_byte(0);
264   match_byte(0);
265   get_u32();
266   match_byte(0);
267   match_byte(0);
268   match_byte(0);
269   match_byte(0);
270   n_categories = get_u32();
271   printf("%d nested categories\n", n_categories);
272   for (int i = 0; i < n_categories; i++)
273     dump_category (0);
274 }
275
276 static void
277 dump_dims(void)
278 {
279   int n_dims = get_u32();
280
281   printf ("%u dimensions\n", n_dims);
282   for (int i = 0; i < n_dims; i++)
283     {
284       printf("\n");
285       dump_dim ();
286     }
287 }
288
289 int
290 main(int argc, char *argv[])
291 {
292   size_t start;
293   struct stat s;
294
295   if (isatty(STDIN_FILENO))
296     {
297       fprintf(stderr, "redirect stdin from a .bin file\n");
298       exit(1);
299     }
300   if (fstat(STDIN_FILENO, &s))
301     {
302       perror("fstat");
303       exit(1);
304     }
305   n = s.st_size;
306   data = malloc(n);
307   if (!data)
308     {
309       perror("malloc");
310       exit(1);
311     }
312   if (read(STDIN_FILENO, data, n) != n)
313     {
314       perror("read");
315       exit(1);
316     }
317
318   if (argc > 1)
319     {
320       if (!strcmp(argv[1], "title"))
321         {
322           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
323           start = 0x27;
324           n = find(fonts, sizeof fonts - 1);
325         }
326       else if (!strcmp(argv[1], "fonts"))
327         {
328           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
329           const char styles[] = "\xf0\0\0\0";
330           start = find(fonts, sizeof fonts - 1);
331           n = find(styles, sizeof styles - 1);
332         }
333       else if (!strcmp(argv[1], "styles"))
334         {
335           const char styles[] = "\xf0\0\0\0";
336           const char dimensions[] = "-,,,.\0";
337           start = find(styles, sizeof styles - 1);
338           n = find(dimensions, sizeof dimensions - 1) + sizeof dimensions - 1;
339         }
340       else if (!strcmp(argv[1], "dimensions"))
341         {
342           const char dimensions[] = "-,,,.\0";
343           start = find(dimensions, sizeof dimensions - 1) + sizeof dimensions - 1;
344           pos = start;
345           dump_dims ();
346           return 0;
347         }
348       else
349         {
350           fprintf (stderr, "unknown section %s\n", argv[1]);
351           exit(1);
352         }
353     }
354   else
355     start = 0x27;
356
357   for (size_t i = start; i < n; )
358     {
359       if (i + 5 <= n
360           && data[i]
361           && !data[i + 1]
362           && !data[i + 2]
363           && !data[i + 3]
364           && i + 4 + data[i] <= n
365           && all_ascii(&data[i + 4], data[i]))
366         {
367           fputs("\n\"", stdout);
368           fwrite(&data[i + 4], 1, data[i], stdout);
369           fputs("\" ", stdout);
370
371           i += 4 + data[i];
372         }
373       else if (i + 12 <= n
374                && data[i + 1] == 40
375                && data[i + 2] == 5
376                && data[i + 3] == 0)
377         {
378           double d;
379
380           memcpy (&d, &data[i + 4], 8);
381           printf ("F40.%d(%.*f)\n", data[i], data[i], d);
382           i += 12;
383         }
384       else if (i + 12 <= n
385                && data[i + 1] == 40
386                && data[i + 2] == 31
387                && data[i + 3] == 0)
388         {
389           double d;
390
391           memcpy (&d, &data[i + 4], 8);
392           printf ("PCT40.%d(%.*f)\n", data[i], data[i], d);
393           i += 12;
394         }
395       else if (i + 4 <= n
396                && (data[i] && data[i] != 88 && data[i] != 0x41)
397                && !data[i + 1]
398                && !data[i + 2]
399                && !data[i + 3])
400         {
401           printf ("i%d ", data[i]);
402           i += 4;
403         }
404       else
405         {
406           printf("%02x ", data[i]);
407           i++;
408         }
409     }
410
411   return 0;
412 }