Bring pageSetup into what we look at.
[pspp] / dump2.c
1 #include <assert.h>
2 #include <float.h>
3 #include <stdbool.h>
4 #include <stdint.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <sys/stat.h>
9 #include <unistd.h>
10
11 static uint8_t *data;
12 static size_t n, pos;
13
14 #define XSTR(x) #x
15 #define STR(x) XSTR(x)
16 #define WHERE __FILE__":" STR(__LINE__)
17
18 static unsigned int
19 get_u32(void)
20 {
21   uint32_t x;
22   memcpy(&x, &data[pos], 4);
23   pos += 4;
24   return x;
25 }
26
27 static double
28 get_double(void)
29 {
30   double x;
31   memcpy(&x, &data[pos], 8);
32   pos += 8;
33   return x;
34 }
35
36 static bool
37 match_u32(uint32_t x)
38 {
39   if (get_u32() == x)
40     return true;
41   pos -= 4;
42   return false;
43 }
44
45 static void
46 match_u32_assert(uint32_t x, const char *where)
47 {
48   unsigned int y = get_u32();
49   if (x != y)
50     {
51       fprintf(stderr, "%s: 0x%x: expected i%u, got i%u\n", where, pos - 4, x, y);
52       exit(1);
53     }
54 }
55 #define match_u32_assert(x) match_u32_assert(x, WHERE)
56
57 static bool
58 match_byte(uint8_t b)
59 {
60   if (pos < n && data[pos] == b)
61     {
62       pos++;
63       return true;
64     }
65   else
66     return false;
67 }
68
69 static void
70 match_byte_assert(uint8_t b, const char *where)
71 {
72   if (!match_byte(b))
73     {
74       fprintf(stderr, "%s: 0x%x: expected %02x, got %02x\n", where, pos, b, data[pos]);
75       exit(1);
76     }
77 }
78 #define match_byte_assert(b) match_byte_assert(b, WHERE)
79
80 static bool
81 all_ascii(const uint8_t *p)
82 {
83   for (; *p; p++)
84     if (*p < 32 || *p > 126)
85       return false;
86   return true;
87 }
88
89 static char *
90 get_fixed_string(int len, const char *where)
91 {
92   if (pos + len > n || !memchr(&data[pos], 0, len) || !all_ascii(&data[pos]))
93     {
94       fprintf(stderr, "%s: 0x%x: bad fixed-width string\n", where, pos);
95       exit(1);
96     }
97   char *s = (char *) &data[pos];
98   pos += len;
99   return s;
100 }
101 #define get_fixed_string(len) get_fixed_string(len, WHERE)
102
103 static bool
104 all_ascii2(const uint8_t *p, size_t n)
105 {
106   for (size_t i = 0; i < n; i++)
107     if (p[i] < 32 || p[i] > 126)
108       return false;
109   return true;
110 }
111
112 static char *
113 get_string(const char *where)
114 {
115   if (1
116       /*data[pos + 1] == 0 && data[pos + 2] == 0 && data[pos + 3] == 0*/
117       /*&& all_ascii(&data[pos + 4], data[pos])*/)
118     {
119       int len = data[pos] + data[pos + 1] * 256;
120       char *s = malloc(len + 1);
121
122       memcpy(s, &data[pos + 4], len);
123       s[len] = 0;
124       pos += 4 + len;
125       return s;
126     }
127   else
128     {
129       fprintf(stderr, "%s: 0x%x: expected string\n", where, pos);
130       exit(1);
131     }
132 }
133 #define get_string() get_string(WHERE)
134
135 static void
136 dump_raw(FILE *stream, int start, int end, const char *separator)
137 {
138   for (size_t i = start; i < end; )
139     {
140       if (i + 5 <= n
141           && data[i] > 0
142           //&& !data[i + 1]
143           && !data[i + 2]
144           && !data[i + 3]
145           && i + 4 + data[i] + data[i + 1] * 256 <= end
146           && all_ascii2(&data[i + 4], data[i] + data[i + 1] * 256))
147         {
148           fprintf(stream, "%s\"", separator);
149           fwrite(&data[i + 4], 1, data[i] + data[i + 1] * 256, stream);
150           fputs("\" ", stream);
151
152           i += 4 + data[i] + data[i + 1] * 256;
153         }
154       else if (i + 12 <= end
155                && data[i + 1] == 40
156                && data[i + 2] == 5
157                && data[i + 3] == 0)
158         {
159           double d;
160
161           memcpy (&d, &data[i + 4], 8);
162           fprintf (stream, "F40.%d(%.*f)%s", data[i], data[i], d, separator);
163           i += 12;
164         }
165       else if (i + 12 <= end
166                && data[i + 1] == 40
167                && data[i + 2] == 31
168                && data[i + 3] == 0)
169         {
170           double d;
171
172           memcpy (&d, &data[i + 4], 8);
173           fprintf (stream, "PCT40.%d(%.*f)%s", data[i], data[i], d, separator);
174           i += 12;
175         }
176       else if (i + 4 <= end
177                && !data[i + 1]
178                && !data[i + 2]
179                && !data[i + 3])
180         {
181           fprintf (stream, "i%d ", data[i]);
182           i += 4;
183         }
184       else
185         {
186           fprintf(stream, "%02x ", data[i]);
187           i++;
188         }
189     }
190 }
191
192 static void
193 dump_source(int end, int count, int n_series)
194 {
195   const union
196     {
197       uint8_t b[8];
198       double d;
199     }
200   sysmis = {.b = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff}};
201   int n_sysmis = 0;
202   for (int i = 0; i < n_series; i++)
203     {
204       printf ("    series %d: \"%s\"\n        ", i, get_fixed_string(288));
205       for (int i = 0; i < count; i++)
206         {
207           double d = get_double();
208           if (d == sysmis.d)
209             {
210               printf (" .");
211               n_sysmis++;
212             }
213           else
214             printf (" %.*g", DBL_DIG, d);
215         }
216       printf ("\n");
217     }
218
219   if (pos >= end)
220     return;
221
222   printf ("\n    %08x: (%d sysmis)", pos, n_sysmis);
223   printf (" %d", get_u32());
224   printf (", \"%s\"\n", get_string());
225
226   printf ("\n    %08x:", pos);
227   int n_more_series = get_u32();
228   printf (" %d series to come\n", n_more_series);
229
230   for (int i = 0; i < n_more_series; i++)
231     {
232       printf ("%08x:", pos);
233       printf (" \"%s\"", get_string());
234       int n_pairs = get_u32();
235       for (int j = 0; j < n_pairs; j++)
236         {
237           int x = get_u32();
238           int y = get_u32();
239           printf (" (%d,%d)", x, y);
240         }
241       printf ("\n");
242     }
243
244   printf ("\n%08x:", pos);
245   int n_strings = get_u32();
246   printf (" %d strings\n", n_strings);
247   for (int i = 0; i < n_strings; i++)
248     {
249       int x = get_u32();
250       char *s = get_string();
251       printf ("%d: \"%s\" (%d)\n", i, s, x);
252     }
253   printf ("\n");
254 }
255
256 int
257 main(int argc, char **argv)
258 {
259   size_t start;
260   struct stat s;
261
262   if (isatty(STDIN_FILENO))
263     {
264       fprintf(stderr, "redirect stdin from a .bin file\n");
265       exit(1);
266     }
267   if (fstat(STDIN_FILENO, &s))
268     {
269       perror("fstat");
270       exit(1);
271     }
272   n = s.st_size;
273   data = malloc(n);
274   if (!data)
275     {
276       perror("malloc");
277       exit(1);
278     }
279   if (read(STDIN_FILENO, data, n) != n)
280     {
281       perror("read");
282       exit(1);
283     }
284
285   pos = 0;
286   match_byte_assert(0);
287   int version = data[pos];
288   if (!match_byte(0xaf))
289     match_byte_assert(0xb0);
290   int n_sources = data[pos++];
291   match_byte_assert(0);
292
293   match_u32_assert(s.st_size);
294   printf ("%d sources\n", n_sources);
295
296   struct source
297     {
298       int offset, count, n_series;
299     }
300   sources[n_sources];
301   for (int i = 0; i < n_sources; i++)
302     {
303       int count = get_u32();
304       int n_series = get_u32();
305       int offset = get_u32();
306       char *name = get_fixed_string(version == 0xb0 ? 64 : 28);
307       int dunno = version == 0xb0 ? get_u32() : 0;
308       printf ("source %d: %d series, %d observations per series, offset %08x, \"%s\", %x\n",
309               i, n_series, count, offset, name, dunno);
310       sources[i].offset = offset;
311       sources[i].count = count;
312       sources[i].n_series = n_series;
313     }
314
315   for (int i = 0; i < n_sources; i++)
316     {
317       if (pos != sources[i].offset)
318         {
319           fprintf (stderr, "pos=0x%x expected=0x%x reading source %d\n", pos, sources[i].offset, i);
320           exit(1);
321         }
322       dump_source(i + 1 >= n_sources ? n : sources[i + 1].offset, sources[i].count, sources[i].n_series);
323     }
324   assert(pos == n);
325
326   return 0;
327 }