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