dump: Successfully parse all dimensions.
[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_value(int level)
124 {
125   for (int i = 0; i <= level; i++)
126     printf ("    ");
127
128   if (match_byte (3))
129     {
130       get_string();
131       if (match_byte (0x31))
132         {
133           match_u32 (1);
134           printf("(footnote %d) ", get_u32());
135           match_byte_assert (0);
136           match_byte_assert (0);
137           int subn = get_u32 ();
138           printf ("nested %d bytes", subn);
139           pos += subn;
140         }
141       else
142         match_byte_assert (0x58);
143       get_string();
144       printf("string \"%s\"", get_string());
145       match_byte (0);
146       match_byte_assert (1);
147       match_byte (0);
148       match_byte (0);
149       match_byte (0);
150       match_byte (1);
151     }
152   else if (match_byte (5))
153     {
154       match_byte_assert (0x58);
155       printf ("variable \"%s\"", get_string());
156       get_string();
157       if (!match_byte (3))
158         match_byte_assert (2);
159       match_byte (0);
160       match_byte (0);
161       match_byte (0);
162       match_byte (0);
163     }
164   else if (match_byte (2))
165     {
166       unsigned int format;
167       char *var, *vallab;
168       double value;
169
170       match_byte_assert (0x58);
171       format = get_u32 ();
172       value = get_double ();
173       var = get_string ();
174       vallab = get_string ();
175       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
176               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
177       if (!match_u32 (3))
178         match_u32_assert (2);
179       match_byte (0);
180     }
181   else if (match_byte (1))
182     {
183       unsigned int format;
184       double value;
185
186       match_byte_assert (0x58);
187       format = get_u32 ();
188       value = get_double ();
189       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
190       match_byte (1);
191       match_byte (0);
192       match_byte (0);
193       match_byte (0);
194       match_byte (1);
195     }
196   else
197     {
198       int subn;
199       int total_subs = 1;
200
201       match_byte (0);
202       match_byte_assert (0x31);
203       match_u32_assert (0);
204       match_u32_assert (0);
205       subn = get_u32 ();
206       printf ("nested %d bytes", subn);
207       pos += subn;
208       printf ("; \"%s\", substitutions:", get_string());
209       fprintf (stderr, "substitutions:");
210       for (;;)
211         {
212           int n_subst = get_u32();
213           if (!n_subst)
214             break;
215           printf (" %d", n_subst);
216           fprintf (stderr, " %d", n_subst);
217           total_subs *= n_subst;
218         }
219       putc ('\n', stderr);
220
221       for (int i = 0; i < total_subs; i++)
222         {
223           putc ('\n', stdout);
224           dump_value (level + 1);
225         }
226     }
227 }
228
229 static void
230 dump_category(int level)
231 {
232   match_byte (0);
233   match_byte (0);
234   match_byte (0);
235   match_byte (0);
236   dump_value (level);
237
238   if (match_u32 (2))
239     get_u32 ();
240   else if (match_u32 (1))
241     {
242       match_byte (0);
243       match_byte (0);
244       match_byte (0);
245       get_u32 ();
246     }
247   else
248     {
249       match_u32_assert (0);
250       get_u32 ();
251     }
252
253   int n_categories = get_u32();
254   if (n_categories > 0)
255     printf (", %d subcategories:", n_categories);
256   printf("\n");
257   for (int i = 0; i < n_categories; i++)
258     dump_category (level + 1);
259 }
260
261 static void
262 dump_dim(void)
263 {
264   int n_categories;
265   if (match_byte(3))
266     {
267       get_string();
268       match_byte_assert(0x58);
269       get_string();
270       printf("string \"%s\": ", get_string());
271       match_byte_assert(1);
272     }
273   else if (match_byte(5))
274     {
275       match_byte_assert(0x58);
276       printf("variable \"%s\": ", get_string());
277       get_string();
278       if (!match_byte(2))
279         match_byte_assert(3);
280     }
281   else
282     {
283       fprintf(stderr, "%08x: unexpected byte\n", pos);
284       exit(1);
285     }
286
287   match_byte_assert(0);
288   if (!match_byte(0) && !match_byte(1))
289     match_byte_assert(2);
290   match_u32_assert(2);
291   if (!match_byte(0))
292     match_byte_assert(1);
293   match_byte(0);
294   match_byte(0);
295   match_byte(0);
296   match_byte(0);
297   get_u32();
298   match_byte(0);
299   match_byte(0);
300   match_byte(0);
301   match_byte(0);
302   n_categories = get_u32();
303   printf("%d nested categories\n", n_categories);
304   for (int i = 0; i < n_categories; i++)
305     dump_category (0);
306 }
307
308 static void
309 dump_dims(void)
310 {
311   int n_dims = get_u32();
312
313   printf ("%u dimensions\n", n_dims);
314   for (int i = 0; i < n_dims; i++)
315     {
316       printf("\n");
317       dump_dim ();
318     }
319 }
320
321 int
322 main(int argc, char *argv[])
323 {
324   size_t start;
325   struct stat s;
326
327   if (isatty(STDIN_FILENO))
328     {
329       fprintf(stderr, "redirect stdin from a .bin file\n");
330       exit(1);
331     }
332   if (fstat(STDIN_FILENO, &s))
333     {
334       perror("fstat");
335       exit(1);
336     }
337   n = s.st_size;
338   data = malloc(n);
339   if (!data)
340     {
341       perror("malloc");
342       exit(1);
343     }
344   if (read(STDIN_FILENO, data, n) != n)
345     {
346       perror("read");
347       exit(1);
348     }
349
350   if (argc > 1)
351     {
352       if (!strcmp(argv[1], "title0"))
353         {
354           pos = 0x27;
355           if (match_byte (0x03)
356               || (match_byte (0x05) && match_byte (0x58)))
357             printf ("%s\n", get_string());
358           else
359             printf ("<unknown>\n");
360           return 0;
361         }
362       if (!strcmp(argv[1], "title"))
363         {
364           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
365           start = 0x27;
366           n = find(fonts, sizeof fonts - 1);
367         }
368       else if (!strcmp(argv[1], "fonts"))
369         {
370           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
371           const char styles[] = "\xf0\0\0\0";
372           start = find(fonts, sizeof fonts - 1);
373           n = find(styles, sizeof styles - 1);
374         }
375       else if (!strcmp(argv[1], "styles"))
376         {
377           const char styles[] = "\xf0\0\0\0";
378           const char dimensions[] = "-,,,.\0";
379           start = find(styles, sizeof styles - 1);
380           n = find(dimensions, sizeof dimensions - 1) + sizeof dimensions - 1;
381         }
382       else if (!strcmp(argv[1], "dimensions"))
383         {
384           const char dimensions[] = "-,,,.\0";
385           start = find(dimensions, sizeof dimensions - 1) + sizeof dimensions - 1;
386           pos = start;
387           dump_dims ();
388           return 0;
389         }
390       else
391         {
392           fprintf (stderr, "unknown section %s\n", argv[1]);
393           exit(1);
394         }
395     }
396   else
397     start = 0x27;
398
399   for (size_t i = start; i < n; )
400     {
401       if (i + 5 <= n
402           && data[i]
403           && !data[i + 1]
404           && !data[i + 2]
405           && !data[i + 3]
406           && i + 4 + data[i] <= n
407           && all_ascii(&data[i + 4], data[i]))
408         {
409           fputs("\n\"", stdout);
410           fwrite(&data[i + 4], 1, data[i], stdout);
411           fputs("\" ", stdout);
412
413           i += 4 + data[i];
414         }
415       else if (i + 12 <= n
416                && data[i + 1] == 40
417                && data[i + 2] == 5
418                && data[i + 3] == 0)
419         {
420           double d;
421
422           memcpy (&d, &data[i + 4], 8);
423           printf ("F40.%d(%.*f)\n", data[i], data[i], d);
424           i += 12;
425         }
426       else if (i + 12 <= n
427                && data[i + 1] == 40
428                && data[i + 2] == 31
429                && data[i + 3] == 0)
430         {
431           double d;
432
433           memcpy (&d, &data[i + 4], 8);
434           printf ("PCT40.%d(%.*f)\n", data[i], data[i], d);
435           i += 12;
436         }
437       else if (i + 4 <= n
438                && (data[i] && data[i] != 88 && data[i] != 0x41)
439                && !data[i + 1]
440                && !data[i + 2]
441                && !data[i + 3])
442         {
443           printf ("i%d ", data[i]);
444           i += 4;
445         }
446       else
447         {
448           printf("%02x ", data[i]);
449           i++;
450         }
451     }
452
453   return 0;
454 }