start moving beyond PMModelItemInfo
[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 #include "u8-mbtouc.h"
11
12 static uint8_t *data;
13 static size_t n, pos;
14
15 #define XSTR(x) #x
16 #define STR(x) XSTR(x)
17 #define WHERE __FILE__":" STR(__LINE__)
18
19 bool ok = true;
20
21 static unsigned int
22 get_u32(void)
23 {
24   uint32_t x;
25   memcpy(&x, &data[pos], 4);
26   pos += 4;
27   return x;
28 }
29
30 static double
31 get_double(void)
32 {
33   double x;
34   memcpy(&x, &data[pos], 8);
35   pos += 8;
36   return x;
37 }
38
39 static bool
40 match_u32(uint32_t x)
41 {
42   if (get_u32() == x)
43     return true;
44   pos -= 4;
45   return false;
46 }
47
48 static void
49 match_u32_assert(uint32_t x, const char *where)
50 {
51   unsigned int y = get_u32();
52   if (x != y)
53     {
54       fprintf(stderr, "%s: 0x%x: expected i%u, got i%u\n", where, pos - 4, x, y);
55       exit(1);
56     }
57 }
58 #define match_u32_assert(x) match_u32_assert(x, WHERE)
59
60 static bool
61 match_byte(uint8_t b)
62 {
63   if (pos < n && data[pos] == b)
64     {
65       pos++;
66       return true;
67     }
68   else
69     return false;
70 }
71
72 static void
73 match_byte_assert(uint8_t b, const char *where)
74 {
75   if (!match_byte(b))
76     {
77       fprintf(stderr, "%s: 0x%x: expected %02x, got %02x\n", where, pos, b, data[pos]);
78       exit(1);
79     }
80 }
81 #define match_byte_assert(b) match_byte_assert(b, WHERE)
82
83 static bool
84 all_ascii(const uint8_t *p)
85 {
86   for (; *p; p++)
87     if (*p < 32 || *p > 126)
88       return false;
89   return true;
90 }
91
92 static bool
93 all_utf8(const uint8_t *p)
94 {
95   size_t len = strlen ((char *) p);
96   for (size_t ofs = 0, mblen; ofs < len; ofs += mblen)
97     {
98       ucs4_t uc;
99
100       mblen = u8_mbtouc (&uc, p + ofs, len - ofs);
101       if (uc < 32 || uc == 127 || uc == 0xfffd)
102         return false;
103     }
104   return true;
105 }
106
107 static char *
108 get_fixed_string(int len, const char *where)
109 {
110   size_t i;
111   for (i = 0; ; i++)
112     {
113       if (!data[pos + i])
114         break;
115       if (i >= len)
116         {
117           fprintf(stderr, "%s: 0x%x: unterminated fixed-width string\n", where, pos);
118           exit(1);
119         }
120     }
121   if (!all_utf8(&data[pos]))
122     {
123       fprintf(stderr, "%s: 0x%x: bad fixed-width string\n", where, pos);
124       exit(1);
125     }
126   while (++i < len)
127     {
128       if (data[pos + i])
129         {
130           fprintf(stderr, "%s: 0x%x: text in middle of fixed-width string\n", where, pos);
131           //exit(1);
132           break;
133         }
134     }
135   char *s = (char *) &data[pos];
136   pos += len;
137   return s;
138 }
139 #define get_fixed_string(len) get_fixed_string(len, WHERE)
140
141 static bool
142 all_ascii2(const uint8_t *p, size_t n)
143 {
144   for (size_t i = 0; i < n; i++)
145     if (p[i] < 32 || p[i] > 126)
146       return false;
147   return true;
148 }
149
150 static char *
151 get_string(const char *where)
152 {
153   if (pos + 4 <= n
154       /*data[pos + 1] == 0 && data[pos + 2] == 0 && data[pos + 3] == 0*/
155       /*&& all_ascii(&data[pos + 4], data[pos])*/)
156     {
157       int len = data[pos] + data[pos + 1] * 256;
158       char *s = malloc(len + 1);
159
160       memcpy(s, &data[pos + 4], len);
161       s[len] = 0;
162       pos += 4 + len;
163       return s;
164     }
165   else
166     {
167       fprintf(stderr, "%s: 0x%x: expected string\n", where, pos);
168       exit(1);
169     }
170 }
171 #define get_string() get_string(WHERE)
172
173 static void
174 dump_raw(FILE *stream, int start, int end, const char *separator)
175 {
176   for (size_t i = start; i < end; )
177     {
178       if (i + 5 <= n
179           && data[i] > 1
180           //&& !data[i + 1]
181           && !data[i + 2]
182           && !data[i + 3]
183           && i + 4 + data[i] + data[i + 1] * 256 <= end
184           && all_ascii2(&data[i + 4], data[i] + data[i + 1] * 256))
185         {
186           fprintf(stream, "%s\"", separator);
187           fwrite(&data[i + 4], 1, data[i] + data[i + 1] * 256, stream);
188           fputs("\" ", stream);
189
190           i += 4 + data[i] + data[i + 1] * 256;
191         }
192       else if (i + 12 <= end
193                && data[i + 1] == 40
194                && data[i + 2] == 5
195                && data[i + 3] == 0)
196         {
197           double d;
198
199           memcpy (&d, &data[i + 4], 8);
200           fprintf (stream, "F40.%d(%.*f)%s", data[i], data[i], d, separator);
201           i += 12;
202         }
203       else if (i + 12 <= end
204                && data[i + 1] == 40
205                && data[i + 2] == 31
206                && data[i + 3] == 0)
207         {
208           double d;
209
210           memcpy (&d, &data[i + 4], 8);
211           fprintf (stream, "PCT40.%d(%.*f)%s", data[i], data[i], d, separator);
212           i += 12;
213         }
214       else if (i + 4 <= end
215                && !data[i + 1]
216                && !data[i + 2]
217                && !data[i + 3])
218         {
219           fprintf (stream, "i%d ", data[i]);
220           i += 4;
221         }
222       else
223         {
224           fprintf(stream, "%02x ", data[i]);
225           i++;
226         }
227     }
228 }
229
230 static void
231 dump_source(int end, int count, int n_series, const char *name)
232 {
233   const union
234     {
235       uint8_t b[8];
236       double d;
237     }
238   sysmis = {.b = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff}};
239   int n_sysmis = 0;
240   for (int i = 0; i < n_series; i++)
241     {
242       printf ("    %08x: series %d: \"%s\", %d values:\n        ",
243               pos, i, get_fixed_string(288), count);
244       for (int i = 0; i < count; i++)
245         {
246           double d = get_double();
247           if (d == sysmis.d)
248             {
249               printf (" .");
250               n_sysmis++;
251             }
252           else
253             printf (" %.*g", DBL_DIG, d);
254         }
255       printf ("\n");
256     }
257 }
258
259 static void
260 dump_strings(void)
261
262 {
263   if (pos >= n)
264     return;
265
266   int start = pos;
267   int offset = pos;
268   int n_maps = get_u32();
269   int max1 = -1;
270   for (int k = 0; k < n_maps; k++)
271     {
272       char *source_name = get_string();
273       printf ("%08x: %s\n", offset, source_name);
274
275       int n_series = get_u32();
276       for (int i = 0; i < n_series; i++)
277         {
278           printf ("%08x:", pos);
279           printf (" \"%s\"", get_string());
280           int n_pairs = get_u32();
281           for (int j = 0; j < n_pairs; j++)
282             {
283               int x = get_u32();
284               int y = get_u32();
285               printf (" (%d, %d)", x, y);
286               if (y > max1)
287                 max1 = y;
288             }
289           printf ("\n");
290         }
291     }
292   printf ("\n%08x:", pos);
293   int n_strings = get_u32();
294   if (n_strings != max1 + 1)
295     {
296       fprintf (stderr, "n_strings=%d max1+1=%d (-s %#x -n %u)\n", n_strings, max1 + 1, start, n - start);
297       dump_raw (stderr, start, n, "\n");
298       assert(n_strings == max1 + 1);
299     }
300   printf (" %d strings\n", n_strings);
301
302   char **strings = malloc((max1 + 1) * sizeof *strings);
303   for (int i = 0; i <= max1; i++)
304     {
305       int frequency = get_u32();
306       char *s = get_string();
307       printf ("%d: \"%s\" (%d)\n", i, s, frequency);
308       strings[i] = s;
309     }
310   printf ("\n");
311
312   assert (pos == n);
313 #if 0
314   pos = ofs;
315   printf("Strings:\n");
316   for (int i = 0; i < n_more_series; i++)
317     {
318       printf ("  \"%s\"\n", get_string());
319       int n_pairs = get_u32();
320       for (int j = 0; j < n_pairs; j++)
321         {
322           int x = get_u32();
323           //assert (x == j);
324           int y = get_u32();
325           printf ("    %d: \"%s\"\n", x, strings[y]);
326         }
327       printf ("\n");
328     }
329 #endif
330 }
331
332 int
333 main(int argc, char **argv)
334 {
335   size_t start;
336   struct stat s;
337
338   if (isatty(STDIN_FILENO))
339     {
340       fprintf(stderr, "redirect stdin from a .bin file\n");
341       exit(1);
342     }
343   if (fstat(STDIN_FILENO, &s))
344     {
345       perror("fstat");
346       exit(1);
347     }
348   n = s.st_size;
349   data = malloc(n);
350   if (!data)
351     {
352       perror("malloc");
353       exit(1);
354     }
355   if (read(STDIN_FILENO, data, n) != n)
356     {
357       perror("read");
358       exit(1);
359     }
360
361   pos = 0;
362   match_byte_assert(0);
363   int version = data[pos];
364   if (!match_byte(0xaf))
365     match_byte_assert(0xb0);
366   int n_sources = data[pos++];
367   match_byte_assert(0);
368
369   match_u32_assert(s.st_size);
370   printf ("%d sources\n", n_sources);
371
372   struct source
373     {
374       int offset, count, n_series;
375       char *name;
376     }
377   sources[n_sources];
378   for (int i = 0; i < n_sources; i++)
379     {
380       int count = get_u32();
381       int n_series = get_u32();
382       int offset = get_u32();
383       char *name = get_fixed_string(version == 0xb0 ? 64 : 28);
384       int dunno = version == 0xb0 ? get_u32() : 0;
385       printf ("source %d: %d series, %d observations per series, offset %08x, \"%s\", %x\n",
386               i, n_series, count, offset, name, dunno);
387       sources[i].offset = offset;
388       sources[i].count = count;
389       sources[i].n_series = n_series;
390       sources[i].name = name;
391     }
392
393   for (int i = 0; i < n_sources; i++)
394     {
395       if (pos != sources[i].offset)
396         {
397           fprintf (stderr, "pos=0x%x expected=0x%x reading source %d\n", pos, sources[i].offset, i);
398           //exit(1);
399         }
400       printf ("source %d:\n", i);
401       pos = sources[i].offset;
402       dump_source(i + 1 >= n_sources ? n : sources[i + 1].offset, sources[i].count, sources[i].n_series, sources[i].name);
403     }
404   dump_strings();
405 #if 0
406   if (pos != n)
407     {
408       fprintf (stderr, "consumed %zu bytes, file has %zu bytes\n", pos, n);
409       ok = false;
410     }
411   assert(pos == n);
412 #endif
413   
414   return ok ? EXIT_SUCCESS : EXIT_FAILURE;
415 }