+static size_t n, pos;
+
+#define XSTR(x) #x
+#define STR(x) XSTR(x)
+#define WHERE __FILE__":" STR(__LINE__)
+
+static unsigned int
+get_u32(void)
+{
+ uint32_t x;
+ memcpy(&x, &data[pos], 4);
+ pos += 4;
+ return x;
+}
+
+static double
+get_double(void)
+{
+ double x;
+ memcpy(&x, &data[pos], 8);
+ pos += 8;
+ return x;
+}
+
+static bool
+match_u32(uint32_t x)
+{
+ if (get_u32() == x)
+ return true;
+ pos -= 4;
+ return false;
+}
+
+static void
+match_u32_assert(uint32_t x, const char *where)
+{
+ unsigned int y = get_u32();
+ if (x != y)
+ {
+ fprintf(stderr, "%s: 0x%x: expected i%u, got i%u\n", where, pos - 4, x, y);
+ exit(1);
+ }
+}
+#define match_u32_assert(x) match_u32_assert(x, WHERE)
+
+static bool
+match_byte(uint8_t b)
+{
+ if (pos < n && data[pos] == b)
+ {
+ pos++;
+ return true;
+ }
+ else
+ return false;
+}
+
+static void
+match_byte_assert(uint8_t b, const char *where)
+{
+ if (!match_byte(b))
+ {
+ fprintf(stderr, "%s: 0x%x: expected %02x, got %02x\n", where, pos, b, data[pos]);
+ exit(1);
+ }
+}
+#define match_byte_assert(b) match_byte_assert(b, WHERE)
+
+static bool
+all_ascii(const uint8_t *p)
+{
+ for (; *p; p++)
+ if (*p < 32 || *p > 126)
+ return false;
+ return true;
+}
+
+static char *
+get_fixed_string(int len, const char *where)
+{
+ if (pos + len > n || !memchr(&data[pos], 0, len) || !all_ascii(&data[pos]))
+ {
+ fprintf(stderr, "%s: 0x%x: bad fixed-width string\n", where, pos);
+ exit(1);
+ }
+ char *s = (char *) &data[pos];
+ pos += len;
+ return s;
+}
+#define get_fixed_string(len) get_fixed_string(len, WHERE)
+
+static bool
+all_ascii2(const uint8_t *p, size_t n)
+{
+ for (size_t i = 0; i < n; i++)
+ if (p[i] < 32 || p[i] > 126)
+ return false;
+ return true;
+}
+
+static char *
+get_string(const char *where)
+{
+ if (1
+ /*data[pos + 1] == 0 && data[pos + 2] == 0 && data[pos + 3] == 0*/
+ /*&& all_ascii(&data[pos + 4], data[pos])*/)
+ {
+ int len = data[pos] + data[pos + 1] * 256;
+ char *s = malloc(len + 1);
+
+ memcpy(s, &data[pos + 4], len);
+ s[len] = 0;
+ pos += 4 + len;
+ return s;
+ }
+ else
+ {
+ fprintf(stderr, "%s: 0x%x: expected string\n", where, pos);
+ exit(1);
+ }
+}
+#define get_string() get_string(WHERE)
+
+static void
+dump_raw(FILE *stream, int start, int end, const char *separator)
+{
+ for (size_t i = start; i < end; )
+ {
+ if (i + 5 <= n
+ && data[i] > 0
+ //&& !data[i + 1]
+ && !data[i + 2]
+ && !data[i + 3]
+ && i + 4 + data[i] + data[i + 1] * 256 <= end
+ && all_ascii2(&data[i + 4], data[i] + data[i + 1] * 256))
+ {
+ fprintf(stream, "%s\"", separator);
+ fwrite(&data[i + 4], 1, data[i] + data[i + 1] * 256, stream);
+ fputs("\" ", stream);
+
+ i += 4 + data[i] + data[i + 1] * 256;
+ }
+ else if (i + 12 <= end
+ && data[i + 1] == 40
+ && data[i + 2] == 5
+ && data[i + 3] == 0)
+ {
+ double d;
+
+ memcpy (&d, &data[i + 4], 8);
+ fprintf (stream, "F40.%d(%.*f)%s", data[i], data[i], d, separator);
+ i += 12;
+ }
+ else if (i + 12 <= end
+ && data[i + 1] == 40
+ && data[i + 2] == 31
+ && data[i + 3] == 0)
+ {
+ double d;
+
+ memcpy (&d, &data[i + 4], 8);
+ fprintf (stream, "PCT40.%d(%.*f)%s", data[i], data[i], d, separator);
+ i += 12;
+ }
+ else if (i + 4 <= end
+ && !data[i + 1]
+ && !data[i + 2]
+ && !data[i + 3])
+ {
+ fprintf (stream, "i%d ", data[i]);
+ i += 4;
+ }
+ else
+ {
+ fprintf(stream, "%02x ", data[i]);
+ i++;
+ }
+ }
+}
+
+static void
+dump_source(int end, int count, int n_series)
+{
+ const union
+ {
+ uint8_t b[8];
+ double d;
+ }
+ sysmis = {.b = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff}};
+ int n_sysmis = 0;
+ for (int i = 0; i < n_series; i++)
+ {
+ printf (" series %d: \"%s\"\n ", i, get_fixed_string(288));
+ for (int i = 0; i < count; i++)
+ {
+ double d = get_double();
+ if (d == sysmis.d)
+ {
+ printf (" .");
+ n_sysmis++;
+ }
+ else
+ printf (" %.*g", DBL_DIG, d);
+ }
+ printf ("\n");
+ }
+
+ if (pos >= end)
+ return;
+
+ printf ("\n %08x: (%d sysmis)", pos, n_sysmis);
+ printf (" %d", get_u32());
+ printf (", \"%s\"\n", get_string());
+
+ printf ("\n %08x:", pos);
+ int n_more_series = get_u32();
+ printf (" %d series to come\n", n_more_series);
+
+ for (int i = 0; i < n_more_series; i++)
+ {
+ printf ("%08x:", pos);
+ printf (" \"%s\"", get_string());
+ int n_pairs = get_u32();
+ for (int j = 0; j < n_pairs; j++)
+ {
+ int x = get_u32();
+ int y = get_u32();
+ printf (" (%d,%d)", x, y);
+ }
+ printf ("\n");
+ }
+
+ printf ("\n%08x:", pos);
+ int n_strings = get_u32();
+ printf (" %d strings\n", n_strings);
+ for (int i = 0; i < n_strings; i++)
+ {
+ int x = get_u32();
+ char *s = get_string();
+ printf ("%d: \"%s\" (%d)\n", i, s, x);
+ }
+ printf ("\n");
+}