--- /dev/null
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <float.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <unistd.h>
+#include "u8-mbtouc.h"
+
+static const char *filename;
+static uint8_t *data;
+static size_t n;
+
+int version;
+
+unsigned int pos;
+
+#define XSTR(x) #x
+#define STR(x) XSTR(x)
+#define WHERE __FILE__":" STR(__LINE__)
+
+static uint8_t
+get_byte(void)
+{
+ return data[pos++];
+}
+
+static unsigned int
+get_u32(void)
+{
+ uint32_t x;
+ memcpy(&x, &data[pos], 4);
+ pos += 4;
+ return x;
+}
+
+static unsigned long long int
+get_u64(void)
+{
+ uint64_t x;
+ memcpy(&x, &data[pos], 8);
+ pos += 8;
+ return x;
+}
+
+static unsigned int
+get_be32(void)
+{
+ uint32_t x;
+ x = (data[pos] << 24) | (data[pos + 1] << 16) | (data[pos + 2] << 8) | data[pos + 3];
+ pos += 4;
+ return x;
+}
+
+static unsigned int
+get_u16(void)
+{
+ uint16_t x;
+ memcpy(&x, &data[pos], 2);
+ pos += 2;
+ return x;
+}
+
+static double
+get_double(void)
+{
+ double x;
+ memcpy(&x, &data[pos], 8);
+ pos += 8;
+ return x;
+}
+
+static double __attribute__((unused))
+get_float(void)
+{
+ float x;
+ memcpy(&x, &data[pos], 4);
+ pos += 4;
+ return x;
+}
+
+static bool
+match_u32(uint32_t x)
+{
+ if (get_u32() == x)
+ return true;
+ pos -= 4;
+ return false;
+}
+
+bool
+match_u16(uint16_t x)
+{
+ if (get_u16() == x)
+ return true;
+ pos -= 2;
+ 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 void
+match_u16_assert(uint16_t x, const char *where)
+{
+ unsigned int y = get_u16();
+ if (x != y)
+ {
+ fprintf(stderr, "%s: 0x%x: expected u16:%u, got u16:%u\n", where, pos - 2, x, y);
+ exit(1);
+ }
+}
+#define match_u16_assert(x) match_u16_assert(x, WHERE)
+
+static bool __attribute__((unused))
+match_u64(uint64_t x)
+{
+ if (get_u64() == x)
+ return true;
+ pos -= 8;
+ return false;
+}
+
+static void __attribute__((unused))
+match_u64_assert(uint64_t x, const char *where)
+{
+ unsigned long long int y = get_u64();
+ if (x != y)
+ {
+ fprintf(stderr, "%s: 0x%x: expected u64:%lu, got u64:%llu\n", where, pos - 8, x, y);
+ exit(1);
+ }
+}
+#define match_u64_assert(x) match_u64_assert(x, WHERE)
+
+static bool __attribute__((unused))
+match_be32(uint32_t x)
+{
+ if (get_be32() == x)
+ return true;
+ pos -= 4;
+ return false;
+}
+
+static void
+match_be32_assert(uint32_t x, const char *where)
+{
+ unsigned int y = get_be32();
+ if (x != y)
+ {
+ fprintf(stderr, "%s: 0x%x: expected be%u, got be%u\n", where, pos - 4, x, y);
+ exit(1);
+ }
+}
+#define match_be32_assert(x) match_be32_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
+match_bytes(int start, const int *bytes, size_t n_bytes)
+{
+ for (size_t i = 0; i < n_bytes; i++)
+ if (bytes[i] >= 0 && data[start + i] != bytes[i])
+ return false;
+ return true;
+}
+
+static char *
+xmemdup0(const void *p, size_t n)
+{
+ char *s = malloc(n + 1);
+ memcpy(s, p, n);
+ s[n] = 0;
+ return s;
+}
+
+static bool
+get_bool(void)
+{
+ if (match_byte(0))
+ return false;
+ match_byte_assert(1);
+ return true;
+}
+
+static bool __attribute__((unused))
+is_ascii(uint8_t p)
+{
+ return (p >= ' ' && p < 127) || p == '\r' || p == '\n' || p == '\t';
+}
+
+static int
+count_zeros(const uint8_t *p)
+{
+ size_t n = 0;
+ while (p[n] == 0)
+ n++;
+ return n;
+}
+
+static bool __attribute__((unused))
+all_utf8(const char *p_, size_t len)
+{
+ const uint8_t *p = (const uint8_t *) 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 != '\n') || uc == 127 || uc == 0xfffd)
+ return false;
+ }
+ return true;
+}
+
+static char *
+get_string1(void)
+{
+ int len = data[pos++];
+ char *s = xmemdup0(&data[pos], len);
+ pos += len;
+ return s;
+}
+
+static char *
+get_string2(void)
+{
+ int len = data[pos] + data[pos + 1] * 256;
+ char *s = xmemdup0(&data[pos + 2], len);
+ pos += 2 + len;
+ return s;
+}
+
+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 char *
+get_string_be(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 + 2] * 256 + data[pos + 3];
+ 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_be() get_string_be(WHERE)
+
+static int
+get_end(void)
+{
+ int len = get_u32();
+ return pos + len;
+}
+
+static void __attribute__((unused))
+hex_dump(FILE *stream, int ofs, int n)
+{
+ int n_ascii = 0;
+ for (int i = 0; i < n; i++)
+ {
+ int c = data[ofs + i];
+ n_ascii += is_ascii(c);
+ fprintf(stream, " %02x", c);
+ }
+ if (n_ascii >= 3)
+ {
+ putc(' ', stream);
+ for (int i = 0; i < n; i++)
+ {
+ int c = data[ofs + i];
+ putc(c >= 32 && c < 127 ? c : '.', stream);
+ }
+ }
+ putc('\n', stream);
+}
+
+static void __attribute__((unused))
+char_dump(FILE *stream, int ofs, int n)
+{
+ for (int i = 0; i < n; i++)
+ {
+ int c = data[ofs + i];
+ putc(c >= 32 && c < 127 ? c : '.', stream);
+ }
+ putc('\n', stream);
+}
+
+
+static int
+compare_int(const void *a_, const void *b_)
+{
+ const int *a = a_;
+ const int *b = b_;
+ return *a < *b ? -1 : *a > *b;
+}
+
+
+static const char *
+format_name (int format, char *buf)
+{
+ switch (format)
+ {
+ case 1: return "A";
+ case 2: return "AHEX";
+ case 3: return "COMMA";
+ case 4: return "DOLLAR";
+ case 5: case 40: return "F";
+ case 6: return "IB";
+ case 7: return "PIBHEX";
+ case 8: return "P";
+ case 9: return "PIB";
+ case 10: return "PK";
+ case 11: return "RB";
+ case 12: return "RBHEX";
+ case 15: return "Z";
+ case 16: return "N";
+ case 17: return "E";
+ case 20: return "DATE";
+ case 21: return "TIME";
+ case 22: return "DATETIME";
+ case 23: return "ADATE";
+ case 24: return "JDATE";
+ case 25: return "DTIME";
+ case 26: return "WKDAY";
+ case 27: return "MONTH";
+ case 28: return "MOYR";
+ case 29: return "QYR";
+ case 30: return "WKYR";
+ case 31: return "PCT";
+ case 32: return "DOT";
+ case 33: return "CCA";
+ case 34: return "CCB";
+ case 35: return "CCC";
+ case 36: return "CCD";
+ case 37: return "CCE";
+ case 38: return "EDATE";
+ case 39: return "SDATE";
+ default: sprintf(buf, "(%d)", format); return buf;
+ }
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ bool print_offsets = false;
+ for (;;)
+ {
+ int c = getopt (argc, argv, "o");
+ if (c == -1)
+ break;
+
+ switch (c)
+ {
+ case 'o':
+ print_offsets = true;
+ break;
+
+ case '?':
+ exit (-1);
+ }
+ }
+ if (argc - optind != 1)
+ {
+ fprintf (stderr, "usage: %s FILE.bin", argv[0]);
+ exit (1);
+ }
+
+ const char *filename = argv[optind];
+ int fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ {
+ fprintf (stderr, "%s: open failed (%s)", filename, strerror (errno));
+ exit (1);
+ }
+
+ struct stat s;
+ if (fstat(fd, &s))
+ {
+ perror("fstat");
+ exit(1);
+ }
+ n = s.st_size;
+ data = malloc(n);
+ if (!data)
+ {
+ perror("malloc");
+ exit(1);
+ }
+ if (read(fd, data, n) != n)
+ {
+ perror("read");
+ exit(1);
+ }
+ close(fd);
+
+ setvbuf (stdout, NULL, _IOLBF, 0);
+
+ return 0;
+}