Some more details of styles.
[pspp] / dump2.c
diff --git a/dump2.c b/dump2.c
index 9eb0f5b2ac7b7a425c313fd4dd9670213cc0f35d..d7f8ad3d68abbcff43539cae1f56ef4515aa2e06 100644 (file)
--- a/dump2.c
+++ b/dump2.c
@@ -1,3 +1,5 @@
+#include <assert.h>
+#include <float.h>
 #include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -5,9 +7,267 @@
 #include <string.h>
 #include <sys/stat.h>
 #include <unistd.h>
+#include "u8-mbtouc.h"
 
 static uint8_t *data;
-static size_t n;
+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 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)
+{
+  if (pos + len > n || !memchr(&data[pos], 0, len) || !all_utf8(&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");
+}
 
 int
 main(int argc, char **argv)
@@ -38,19 +298,46 @@ main(int argc, char **argv)
       exit(1);
     }
 
-  if (argc < 3)
+  pos = 0;
+  match_byte_assert(0);
+  int version = data[pos];
+  if (!match_byte(0xaf))
+    match_byte_assert(0xb0);
+  int n_sources = data[pos++];
+  match_byte_assert(0);
+
+  match_u32_assert(s.st_size);
+  printf ("%d sources\n", n_sources);
+
+  struct source
     {
-      fprintf(stderr, "bad arguments");
-      exit(1);
+      int offset, count, n_series;
+    }
+  sources[n_sources];
+  for (int i = 0; i < n_sources; i++)
+    {
+      int count = get_u32();
+      int n_series = get_u32();
+      int offset = get_u32();
+      char *name = get_fixed_string(version == 0xb0 ? 64 : 28);
+      int dunno = version == 0xb0 ? get_u32() : 0;
+      printf ("source %d: %d series, %d observations per series, offset %08x, \"%s\", %x\n",
+              i, n_series, count, offset, name, dunno);
+      sources[i].offset = offset;
+      sources[i].count = count;
+      sources[i].n_series = n_series;
     }
 
-  start = strtol(argv[1], NULL, 0);
-  int end = strtol(argv[2], NULL, 0);
-  for (int pos = start; pos < end; pos += 8)
+  for (int i = 0; i < n_sources; i++)
     {
-      double d;
-      memcpy(&d, &data[pos], sizeof d);
-      printf("%g\n", d);
+      if (pos != sources[i].offset)
+        {
+          fprintf (stderr, "pos=0x%x expected=0x%x reading source %d\n", pos, sources[i].offset, i);
+          exit(1);
+        }
+      dump_source(i + 1 >= n_sources ? n : sources[i + 1].offset, sources[i].count, sources[i].n_series);
     }
+  assert(pos == n);
+
   return 0;
 }