Index: trunk/bind-import
===================================================================
--- trunk/bind-import	(revision 811)
+++ trunk/bind-import	(revision 812)
@@ -62,9 +62,13 @@
 
 # need to spin up a full state machine-ish thing, because BIND zone files are all about context
+# see ch4, p56-72 in the grasshopper book
+my $prevlabel = '';
+my $curlabel = '';
+
 while (<>) {
   chomp;
   next if /^\s*$/;
   next if /^\s*;/;
-  if (my ($macro,$mdetail) = (/^\s*\$(TTL|ORIGIN|INCLUDE)\s+(.+)/) ) {
+  if (my ($macro,$mdetail) = (/^\s*\$(TTL|ORIGIN|INCLUDE|GENERATE)\s+(.+)/) ) {
     # macro sort of thing;  $TTL and $ORIGIN most common.  $INCLUDE is a thing, expect it to be rare in live use tho
     if ($macro eq 'TTL') {
@@ -89,4 +93,5 @@
       }
     }
+    # not handling $INCLUDE or $GENERATE (altho the latter seems to be mostly a less-flexible version of the template types)
     next;
   }
@@ -94,5 +99,6 @@
   next if /^ip-192-168-1(12|20)-\d+/;
   next if /ip.add.re.\d+\s*$/;
-  my ($name) = /([\w_.-]+)\s/;
+  # records must begin in the first column, no leading whitespace
+  my ($name) = /^([\w\@_.-]+)\s/;
   # append zone name to record name if missing AND not dot-terminated;
   # this happens automagically for forward zones, but not reverse because Reasons.  (fixme?)
@@ -102,16 +108,7 @@
 $name = $zname if /^\s*IN/;
 $name = $zname if /^\@/;
-  s/([\w\@_.-]+)\s+//;
-  my ($class) = /(IN|CS|CH|HS)\s/;
-  if ($class) {
-    if ($class ne 'IN') {
-      print "Non-Internet class records not supported, you weirdo\n";
-      next;
-    }
-    s/(IN|CS|CH|HS)\s+//;
-  } else {
-    $class = 'IN' if !$class;
-  }
-  my ($ttl) = /(\d+)?\s/;
+  s/^([\w\@_.-]+)\s+//;
+# by convention the optional TTL leads the optional class, but they're apparently swappable.
+  my ($ttl) = /^(\d+)?\s/;
   if (defined $ttl) {
     # TTL may be zero
@@ -121,4 +118,17 @@
     $ttl = $zonettl;
   }
+  my ($class) = /^(IN|CS|CH|HS|\d+)\s/;
+  if (defined $class) {
+    if ($class =~ /\d+/) {
+      
+    }
+    if ($class ne 'IN') {
+      print "Non-Internet class records not supported, you weirdo\n";
+      next;
+    }
+    s/(IN|CS|CH|HS)\s+//;
+  } else {
+    $class = 'IN';
+  }
   my ($type) = /([A-Z-]+)\s/;
   if (!$reverse_typemap{$type}) {
@@ -130,4 +140,28 @@
   chomp;
   my $rdata = $_;
+
+  # SOA is the only type that may span multiple lines.  Probably.  Note even AXFRed zones write multiline SOA records:
+  #@       IN      SOA     test.example.invalid.   test.example.invalid.   (2020082500 7200 900 604800 3600)
+  #        IN      NS      olddns.example.com.
+  #        IN      MX      1 fred.foo.bar.invalid.
+  #foo     IN      A       192.168.16.45
+  # AXFR'ed zone file gets written as
+  #$ORIGIN .
+  #$TTL 3600       ; 1 hour
+  #example.invalid         IN SOA  test.example.invalid. test.example.invalid. (
+  #                                2020082500 ; serial
+  #                                7200       ; refresh (2 hours)
+  #                                900        ; retry (15 minutes)
+  #                                604800     ; expire (1 week)
+  #                                3600       ; minimum (1 hour)
+  #                                )
+  #                        NS      olddns.example.com.
+  #                        MX      1 fred.foo.bar.invalid.
+  #$ORIGIN example.invalid.
+  #foo                     A       192.168.16.45
+
+  if ($type eq 'SOA') {
+
+  }
 
   # Quotes may arguably be syntactically required, but they're not actually part of the record data
