+static size_t n, pos;
+
+#define XSTR(x) #x
+#define STR(x) XSTR(x)
+#define WHERE __FILE__":" STR(__LINE__)
+
+bool ok = true;
+
+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 bool
+all_utf8(const uint8_t *p)
+{
+ size_t len = strlen ((char *) p);
+ for (size_t ofs = 0, mblen; ofs < len; ofs += mblen)
+ {
+ ucs4_t uc;
+
+ mblen = u8_mbtouc (&uc, p + ofs, len - ofs);
+ if (uc < 32 || uc == 127 || uc == 0xfffd)
+ return false;
+ }
+ return true;
+}
+
+static char *
+get_fixed_string(int len, const char *where)
+{
+ size_t i;
+ for (i = 0; ; i++)
+ {
+ if (!data[pos + i])
+ break;
+ if (i >= len)
+ {
+ fprintf(stderr, "%s: 0x%x: unterminated fixed-width string\n", where, pos);
+ exit(1);
+ }
+ }
+ if (!all_utf8(&data[pos]))
+ {
+ fprintf(stderr, "%s: 0x%x: bad fixed-width string\n", where, pos);
+ exit(1);
+ }
+ while (++i < len)
+ {
+ if (data[pos + i])
+ {
+ fprintf(stderr, "%s: 0x%x: text in middle of fixed-width string\n", where, pos);
+ //exit(1);
+ break;
+ }
+ }
+ 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 (pos + 4 <= n
+ /*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] > 1
+ //&& !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 char *name)
+{
+ 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 (" %08x: series %d: \"%s\", %d values:\n ",
+ pos, i, get_fixed_string(288), count);
+ 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");
+ }
+}
+
+static void
+dump_strings(void)
+
+{
+ if (pos >= n)
+ return;
+
+ int start = pos;
+ int offset = pos;
+ int n_maps = get_u32();
+ int max1 = -1;
+ for (int k = 0; k < n_maps; k++)
+ {
+ char *source_name = get_string();
+ printf ("%08x: %s\n", offset, source_name);
+
+ int n_series = get_u32();
+ for (int i = 0; i < n_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);
+ if (y > max1)
+ max1 = y;
+ }
+ printf ("\n");
+ }
+ }
+ printf ("\n%08x:", pos);
+ int n_strings = get_u32();
+ if (n_strings != max1 + 1)
+ {
+ fprintf (stderr, "n_strings=%d max1+1=%d (-s %#x -n %u)\n", n_strings, max1 + 1, start, n - start);
+ dump_raw (stderr, start, n, "\n");
+ assert(n_strings == max1 + 1);
+ }
+ printf (" %d strings\n", n_strings);
+
+ char **strings = malloc((max1 + 1) * sizeof *strings);
+ for (int i = 0; i <= max1; i++)
+ {
+ int frequency = get_u32();
+ char *s = get_string();
+ printf ("%d: \"%s\" (%d)\n", i, s, frequency);
+ strings[i] = s;
+ }
+ printf ("\n");
+
+ assert (pos == n);
+#if 0
+ pos = ofs;
+ printf("Strings:\n");
+ for (int i = 0; i < n_more_series; i++)
+ {
+ printf (" \"%s\"\n", get_string());
+ int n_pairs = get_u32();
+ for (int j = 0; j < n_pairs; j++)
+ {
+ int x = get_u32();
+ //assert (x == j);
+ int y = get_u32();
+ printf (" %d: \"%s\"\n", x, strings[y]);
+ }
+ printf ("\n");
+ }
+#endif
+}