diff --git a/CMarkGFM.hsc b/CMarkGFM.hsc
--- a/CMarkGFM.hsc
+++ b/CMarkGFM.hsc
@@ -45,8 +45,8 @@
 import Data.Typeable (Typeable)
 import Data.Text (Text, empty)
 import qualified Data.Text.Foreign as TF
-import qualified Data.ByteString as B
-import Data.Text.Encoding (encodeUtf8)
+import Data.ByteString.Unsafe (unsafePackMallocCString)
+import Data.Text.Encoding (decodeUtf8)
 import Control.Applicative ((<$>), (<*>))
 
 #include <cmark-gfm.h>
@@ -153,7 +153,7 @@
   fptr <- newForeignPtr c_cmark_node_free nptr
   withForeignPtr fptr $ \ptr -> do
     cstr <- renderer ptr (combineOptions opts) (fromMaybe 0 mbWidth)
-    TF.peekCStringLen (cstr, c_strlen cstr)
+    decodeUtf8 <$> unsafePackMallocCString cstr
 
 commonmarkToX :: Renderer
               -> [CMarkOption]
@@ -173,8 +173,7 @@
     fptr <- newForeignPtr c_cmark_node_free nptr
     withForeignPtr fptr $ \p -> do
       str <- renderer p opts' (fromMaybe 0 mbWidth)
-      t <- TF.peekCStringLen $! (str, c_strlen str)
-      return t
+      decodeUtf8 <$> unsafePackMallocCString str
 
 data ParserPhantom
 type ParserPtr = Ptr ParserPhantom
@@ -421,17 +420,17 @@
             BLOCK_QUOTE -> c_cmark_node_new (#const CMARK_NODE_BLOCK_QUOTE)
             HTML_BLOCK literal -> do
                      n <- c_cmark_node_new (#const CMARK_NODE_HTML_BLOCK)
-                     c_cmark_node_set_literal n =<< fromtext literal
+                     withtext literal (c_cmark_node_set_literal n)
                      return n
             CUSTOM_BLOCK onEnter onExit -> do
                      n <- c_cmark_node_new (#const CMARK_NODE_CUSTOM_BLOCK)
-                     c_cmark_node_set_on_enter n =<< fromtext onEnter
-                     c_cmark_node_set_on_exit  n =<< fromtext onExit
+                     withtext onEnter (c_cmark_node_set_on_enter n)
+                     withtext onExit  (c_cmark_node_set_on_exit  n)
                      return n
             CODE_BLOCK info literal -> do
                      n <- c_cmark_node_new (#const CMARK_NODE_CODE_BLOCK)
-                     c_cmark_node_set_literal n =<< fromtext literal
-                     c_cmark_node_set_fence_info n =<< fromtext info
+                     withtext literal (c_cmark_node_set_literal n)
+                     withtext info (c_cmark_node_set_fence_info n)
                      return n
             LIST attr   -> do
                      n <- c_cmark_node_new (#const CMARK_NODE_LIST)
@@ -453,30 +452,30 @@
             STRONG      -> c_cmark_node_new (#const CMARK_NODE_STRONG)
             LINK url title -> do
                      n <- c_cmark_node_new (#const CMARK_NODE_LINK)
-                     c_cmark_node_set_url n =<< fromtext url
-                     c_cmark_node_set_title n =<< fromtext title
+                     withtext url (c_cmark_node_set_url n)
+                     withtext title (c_cmark_node_set_title n)
                      return n
             IMAGE url title -> do
                      n <- c_cmark_node_new (#const CMARK_NODE_IMAGE)
-                     c_cmark_node_set_url n =<< fromtext url
-                     c_cmark_node_set_title n =<< fromtext title
+                     withtext url (c_cmark_node_set_url n)
+                     withtext title (c_cmark_node_set_title n)
                      return n
             TEXT literal -> do
                      n <- c_cmark_node_new (#const CMARK_NODE_TEXT)
-                     c_cmark_node_set_literal n =<< fromtext literal
+                     withtext literal (c_cmark_node_set_literal n)
                      return n
             CODE literal -> do
                      n <- c_cmark_node_new (#const CMARK_NODE_CODE)
-                     c_cmark_node_set_literal n =<< fromtext literal
+                     withtext literal (c_cmark_node_set_literal n)
                      return n
             HTML_INLINE literal -> do
                      n <- c_cmark_node_new (#const CMARK_NODE_HTML_INLINE)
-                     c_cmark_node_set_literal n =<< fromtext literal
+                     withtext literal (c_cmark_node_set_literal n)
                      return n
             CUSTOM_INLINE onEnter onExit -> do
                      n <- c_cmark_node_new (#const CMARK_NODE_CUSTOM_INLINE)
-                     c_cmark_node_set_on_enter n =<< fromtext onEnter
-                     c_cmark_node_set_on_exit  n =<< fromtext onExit
+                     withtext onEnter (c_cmark_node_set_on_enter n)
+                     withtext onExit  (c_cmark_node_set_on_exit  n)
                      return n
             SOFTBREAK   -> c_cmark_node_new (#const CMARK_NODE_SOFTBREAK)
             LINEBREAK   -> c_cmark_node_new (#const CMARK_NODE_LINEBREAK)
@@ -492,8 +491,8 @@
   | str == nullPtr = return empty
   | otherwise      = TF.peekCStringLen (str, c_strlen str)
 
-fromtext :: Text -> IO CString
-fromtext t = B.useAsCString (encodeUtf8 t) return
+withtext :: Text -> (CString -> IO a) -> IO a
+withtext t f = TF.withCStringLen t (f . fst)
 
 foreign import ccall "string.h strlen"
     c_strlen :: CString -> Int
diff --git a/cbits/autolink.c b/cbits/autolink.c
--- a/cbits/autolink.c
+++ b/cbits/autolink.c
@@ -269,7 +269,11 @@
   // inline was finished in inlines.c.
 }
 
-static void postprocess_text(cmark_parser *parser, cmark_node *text, int offset) {
+static void postprocess_text(cmark_parser *parser, cmark_node *text, int offset, int depth) {
+  // postprocess_text can recurse very deeply if there is a very long line of
+  // '@' only.  Stop at a reasonable depth to ensure it cannot crash.
+  if (depth > 1000) return;
+
   size_t link_end;
   uint8_t *data = text->as.literal.data,
     *at;
@@ -307,7 +311,7 @@
   }
 
   if (rewind == 0 || ns > 0) {
-    postprocess_text(parser, text, max_rewind + 1 + offset);
+    postprocess_text(parser, text, max_rewind + 1 + offset, depth + 1);
     return;
   }
 
@@ -327,14 +331,14 @@
 
   if (link_end < 2 || nb != 1 || np == 0 ||
       (!cmark_isalpha(data[link_end - 1]) && data[link_end - 1] != '.')) {
-    postprocess_text(parser, text, max_rewind + 1 + offset);
+    postprocess_text(parser, text, max_rewind + 1 + offset, depth + 1);
     return;
   }
 
   link_end = autolink_delim(data, link_end);
 
   if (link_end == 0) {
-    postprocess_text(parser, text, max_rewind + 1 + offset);
+    postprocess_text(parser, text, max_rewind + 1 + offset, depth + 1);
     return;
   }
 
@@ -369,7 +373,7 @@
   text->as.literal.len = offset + max_rewind - rewind;
   text->as.literal.data[text->as.literal.len] = 0;
 
-  postprocess_text(parser, post, 0);
+  postprocess_text(parser, post, 0, depth + 1);
 }
 
 static cmark_node *postprocess(cmark_syntax_extension *ext, cmark_parser *parser, cmark_node *root) {
@@ -396,7 +400,7 @@
     }
 
     if (ev == CMARK_EVENT_ENTER && node->type == CMARK_NODE_TEXT) {
-      postprocess_text(parser, node, 0);
+      postprocess_text(parser, node, 0, /*depth*/0);
     }
   }
 
diff --git a/cbits/cmark-gfm-core-extensions.h b/cbits/cmark-gfm-core-extensions.h
--- a/cbits/cmark-gfm-core-extensions.h
+++ b/cbits/cmark-gfm-core-extensions.h
@@ -21,6 +21,9 @@
 CMARK_GFM_EXTENSIONS_EXPORT
 int cmark_gfm_extensions_get_table_row_is_header(cmark_node *node);
 
+CMARK_GFM_EXTENSIONS_EXPORT
+char *cmark_gfm_extensions_get_tasklist_state(cmark_node *node);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/cbits/cmark-gfm_version.h b/cbits/cmark-gfm_version.h
--- a/cbits/cmark-gfm_version.h
+++ b/cbits/cmark-gfm_version.h
@@ -1,7 +1,7 @@
 #ifndef CMARK_GFM_VERSION_H
 #define CMARK_GFM_VERSION_H
 
-#define CMARK_GFM_VERSION ((0 << 24) | (28 << 16) | (3 << 8) | 18)
-#define CMARK_GFM_VERSION_STRING "0.28.3.gfm.18"
+#define CMARK_GFM_VERSION ((0 << 24) | (28 << 16) | (3 << 8) | 20)
+#define CMARK_GFM_VERSION_STRING "0.28.3.gfm.20"
 
 #endif
diff --git a/cbits/commonmark.c b/cbits/commonmark.c
--- a/cbits/commonmark.c
+++ b/cbits/commonmark.c
@@ -172,6 +172,7 @@
   int i;
   bool entering = (ev_type == CMARK_EVENT_ENTER);
   const char *info, *code, *title;
+  char fencechar[2] = {'\0', '\0'};
   size_t info_len, code_len;
   char listmarker[LISTMARKER_SIZE];
   char *emph_delim;
@@ -284,6 +285,7 @@
     }
     info = cmark_node_get_fence_info(node);
     info_len = strlen(info);
+    fencechar[0] = strchr(info, '`') == NULL ? '`' : '~';
     code = cmark_node_get_literal(node);
     code_len = strlen(code);
     // use indented form if no info, and code doesn't
@@ -303,7 +305,7 @@
         numticks = 3;
       }
       for (i = 0; i < numticks; i++) {
-        LIT("`");
+        LIT(fencechar);
       }
       LIT(" ");
       OUT(info, false, LITERAL);
@@ -311,7 +313,7 @@
       OUT(cmark_node_get_literal(node), false, LITERAL);
       CR();
       for (i = 0; i < numticks; i++) {
-        LIT("`");
+        LIT(fencechar);
       }
     }
     BLANKLINE();
diff --git a/cbits/core-extensions.c b/cbits/core-extensions.c
--- a/cbits/core-extensions.c
+++ b/cbits/core-extensions.c
@@ -3,6 +3,7 @@
 #include "strikethrough.h"
 #include "table.h"
 #include "tagfilter.h"
+#include "tasklist.h"
 #include "registry.h"
 #include "plugin.h"
 
@@ -12,6 +13,7 @@
                                          create_strikethrough_extension());
   cmark_plugin_register_syntax_extension(plugin, create_autolink_extension());
   cmark_plugin_register_syntax_extension(plugin, create_tagfilter_extension());
+  cmark_plugin_register_syntax_extension(plugin, create_tasklist_extension());
   return 1;
 }
 
diff --git a/cbits/ext_scanners.c b/cbits/ext_scanners.c
--- a/cbits/ext_scanners.c
+++ b/cbits/ext_scanners.c
@@ -1,941 +1,1159 @@
-/* Generated by re2c 0.15.3 */
-#include <stdlib.h>
-#include "ext_scanners.h"
-
-bufsize_t _ext_scan_at(bufsize_t (*scanner)(const unsigned char *),
-                       unsigned char *ptr, int len, bufsize_t offset) {
-  bufsize_t res;
-
-  if (ptr == NULL || offset > len) {
-    return 0;
-  } else {
-    unsigned char lim = ptr[len];
-
-    ptr[len] = '\0';
-    res = scanner(ptr + offset);
-    ptr[len] = lim;
-  }
-
-  return res;
-}
-
-bufsize_t _scan_table_start(const unsigned char *p) {
-  const unsigned char *marker = NULL;
-  const unsigned char *start = p;
-
-  {
-    unsigned char yych;
-    static const unsigned char yybm[] = {
-        0, 0,   0, 0, 0, 0, 0, 0, 0, 64, 0,  64, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-        0, 0,   0, 0, 0, 0, 0, 0, 0, 0,  64, 0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,
-        0, 128, 0, 0, 0, 0, 0, 0, 0, 0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,
-        0, 0,   0, 0, 0, 0, 0, 0, 0, 0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,
-        0, 0,   0, 0, 0, 0, 0, 0, 0, 0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,
-        0, 0,   0, 0, 0, 0, 0, 0, 0, 0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,
-        0, 0,   0, 0, 0, 0, 0, 0, 0, 0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,
-        0, 0,   0, 0, 0, 0, 0, 0, 0, 0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,
-        0, 0,   0, 0, 0, 0, 0, 0, 0, 0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,
-        0, 0,   0, 0, 0, 0, 0, 0, 0, 0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,
-        0, 0,   0, 0, 0, 0, 0, 0, 0, 0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,
-        0, 0,   0, 0, 0, 0, 0, 0, 0, 0,  0,  0,  0,  0,
-    };
-    yych = *(marker = p);
-    if (yych <= '{') {
-      if (yych <= 0x1F) {
-        if (yych <= '\t') {
-          if (yych <= 0x08)
-            goto yy6;
-          goto yy3;
-        } else {
-          if (yych <= '\n')
-            goto yy2;
-          if (yych <= '\f')
-            goto yy3;
-          goto yy6;
-        }
-      } else {
-        if (yych <= '-') {
-          if (yych <= ' ')
-            goto yy3;
-          if (yych <= ',')
-            goto yy6;
-          goto yy5;
-        } else {
-          if (yych == ':')
-            goto yy4;
-          goto yy6;
-        }
-      }
-    } else {
-      if (yych <= 0xEC) {
-        if (yych <= 0xC1) {
-          if (yych <= '|')
-            goto yy3;
-          if (yych <= 0x7F)
-            goto yy6;
-        } else {
-          if (yych <= 0xDF)
-            goto yy7;
-          if (yych <= 0xE0)
-            goto yy9;
-          goto yy10;
-        }
-      } else {
-        if (yych <= 0xF0) {
-          if (yych <= 0xED)
-            goto yy14;
-          if (yych <= 0xEF)
-            goto yy10;
-          goto yy11;
-        } else {
-          if (yych <= 0xF3)
-            goto yy12;
-          if (yych <= 0xF4)
-            goto yy13;
-        }
-      }
-    }
-  yy2 : { return 0; }
-  yy3:
-    yych = *(marker = ++p);
-    if (yybm[0 + yych] & 128) {
-      goto yy22;
-    }
-    if (yych <= '\f') {
-      if (yych == '\t')
-        goto yy29;
-      if (yych <= '\n')
-        goto yy2;
-      goto yy29;
-    } else {
-      if (yych <= ' ') {
-        if (yych <= 0x1F)
-          goto yy2;
-        goto yy29;
-      } else {
-        if (yych == ':')
-          goto yy31;
-        goto yy2;
-      }
-    }
-  yy4:
-    yych = *(marker = ++p);
-    if (yybm[0 + yych] & 128) {
-      goto yy22;
-    }
-    goto yy2;
-  yy5:
-    yych = *(marker = ++p);
-    if (yybm[0 + yych] & 128) {
-      goto yy22;
-    }
-    if (yych <= ' ') {
-      if (yych <= 0x08)
-        goto yy2;
-      if (yych <= '\r')
-        goto yy16;
-      if (yych <= 0x1F)
-        goto yy2;
-      goto yy16;
-    } else {
-      if (yych <= ':') {
-        if (yych <= '9')
-          goto yy2;
-        goto yy15;
-      } else {
-        if (yych == '|')
-          goto yy16;
-        goto yy2;
-      }
-    }
-  yy6:
-    yych = *++p;
-    goto yy2;
-  yy7:
-    yych = *++p;
-    if (yych <= 0x7F)
-      goto yy8;
-    if (yych <= 0xBF)
-      goto yy6;
-  yy8:
-    p = marker;
-    goto yy2;
-  yy9:
-    yych = *++p;
-    if (yych <= 0x9F)
-      goto yy8;
-    if (yych <= 0xBF)
-      goto yy7;
-    goto yy8;
-  yy10:
-    yych = *++p;
-    if (yych <= 0x7F)
-      goto yy8;
-    if (yych <= 0xBF)
-      goto yy7;
-    goto yy8;
-  yy11:
-    yych = *++p;
-    if (yych <= 0x8F)
-      goto yy8;
-    if (yych <= 0xBF)
-      goto yy10;
-    goto yy8;
-  yy12:
-    yych = *++p;
-    if (yych <= 0x7F)
-      goto yy8;
-    if (yych <= 0xBF)
-      goto yy10;
-    goto yy8;
-  yy13:
-    yych = *++p;
-    if (yych <= 0x7F)
-      goto yy8;
-    if (yych <= 0x8F)
-      goto yy10;
-    goto yy8;
-  yy14:
-    yych = *++p;
-    if (yych <= 0x7F)
-      goto yy8;
-    if (yych <= 0x9F)
-      goto yy7;
-    goto yy8;
-  yy15:
-    ++p;
-    yych = *p;
-  yy16:
-    if (yybm[0 + yych] & 64) {
-      goto yy15;
-    }
-    if (yych <= '\r') {
-      if (yych <= 0x08)
-        goto yy8;
-      if (yych <= '\n')
-        goto yy20;
-      goto yy19;
-    } else {
-      if (yych != '|')
-        goto yy8;
-    }
-  yy17:
-    ++p;
-    yych = *p;
-    if (yych <= 0x1F) {
-      if (yych <= '\n') {
-        if (yych <= 0x08)
-          goto yy8;
-        if (yych <= '\t')
-          goto yy17;
-        goto yy20;
-      } else {
-        if (yych <= '\f')
-          goto yy17;
-        if (yych >= 0x0E)
-          goto yy8;
-      }
-    } else {
-      if (yych <= '-') {
-        if (yych <= ' ')
-          goto yy17;
-        if (yych <= ',')
-          goto yy8;
-        goto yy25;
-      } else {
-        if (yych == ':')
-          goto yy24;
-        goto yy8;
-      }
-    }
-  yy19:
-    yych = *++p;
-    if (yych != '\n')
-      goto yy8;
-  yy20:
-    ++p;
-    { return (bufsize_t)(p - start); }
-  yy22:
-    ++p;
-    yych = *p;
-    if (yybm[0 + yych] & 128) {
-      goto yy22;
-    }
-    if (yych <= 0x1F) {
-      if (yych <= '\n') {
-        if (yych <= 0x08)
-          goto yy8;
-        if (yych <= '\t')
-          goto yy15;
-        goto yy20;
-      } else {
-        if (yych <= '\f')
-          goto yy15;
-        if (yych <= '\r')
-          goto yy19;
-        goto yy8;
-      }
-    } else {
-      if (yych <= ':') {
-        if (yych <= ' ')
-          goto yy15;
-        if (yych <= '9')
-          goto yy8;
-        goto yy15;
-      } else {
-        if (yych == '|')
-          goto yy17;
-        goto yy8;
-      }
-    }
-  yy24:
-    ++p;
-    yych = *p;
-    if (yych != '-')
-      goto yy8;
-  yy25:
-    ++p;
-    yych = *p;
-    if (yych <= ' ') {
-      if (yych <= '\n') {
-        if (yych <= 0x08)
-          goto yy8;
-        if (yych >= '\n')
-          goto yy20;
-      } else {
-        if (yych <= '\f')
-          goto yy27;
-        if (yych <= '\r')
-          goto yy19;
-        if (yych <= 0x1F)
-          goto yy8;
-      }
-    } else {
-      if (yych <= '9') {
-        if (yych == '-')
-          goto yy25;
-        goto yy8;
-      } else {
-        if (yych <= ':')
-          goto yy27;
-        if (yych == '|')
-          goto yy17;
-        goto yy8;
-      }
-    }
-  yy27:
-    ++p;
-    yych = *p;
-    if (yych <= '\r') {
-      if (yych <= '\t') {
-        if (yych <= 0x08)
-          goto yy8;
-        goto yy27;
-      } else {
-        if (yych <= '\n')
-          goto yy20;
-        if (yych <= '\f')
-          goto yy27;
-        goto yy19;
-      }
-    } else {
-      if (yych <= ' ') {
-        if (yych <= 0x1F)
-          goto yy8;
-        goto yy27;
-      } else {
-        if (yych == '|')
-          goto yy17;
-        goto yy8;
-      }
-    }
-  yy29:
-    ++p;
-    yych = *p;
-    if (yybm[0 + yych] & 128) {
-      goto yy22;
-    }
-    if (yych <= '\f') {
-      if (yych == '\t')
-        goto yy29;
-      if (yych <= '\n')
-        goto yy8;
-      goto yy29;
-    } else {
-      if (yych <= ' ') {
-        if (yych <= 0x1F)
-          goto yy8;
-        goto yy29;
-      } else {
-        if (yych != ':')
-          goto yy8;
-      }
-    }
-  yy31:
-    ++p;
-    if (yybm[0 + (yych = *p)] & 128) {
-      goto yy22;
-    }
-    goto yy8;
-  }
-}
-
-bufsize_t _scan_table_cell(const unsigned char *p) {
-  const unsigned char *marker = NULL;
-  const unsigned char *start = p;
-
-  {
-    unsigned char yych;
-    static const unsigned char yybm[] = {
-        128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 0,   128, 128, 0,
-        128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
-        128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
-        128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
-        128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
-        128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
-        128, 128, 128, 128, 128, 128, 128, 128, 64,  128, 128, 128, 128, 128,
-        128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
-        128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 0,   128,
-        128, 128, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-        0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-        0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-        0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-        0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-        0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-        0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-        0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-        0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-        0,   0,   0,   0,
-    };
-    yych = *(marker = p);
-    if (yych <= 0x7F) {
-      if (yych <= '\r') {
-        if (yych == '\n')
-          goto yy34;
-        if (yych <= '\f')
-          goto yy36;
-        goto yy45;
-      } else {
-        if (yych <= '\\') {
-          if (yych <= '[')
-            goto yy36;
-          goto yy35;
-        } else {
-          if (yych == '|')
-            goto yy45;
-          goto yy36;
-        }
-      }
-    } else {
-      if (yych <= 0xED) {
-        if (yych <= 0xDF) {
-          if (yych >= 0xC2)
-            goto yy37;
-        } else {
-          if (yych <= 0xE0)
-            goto yy39;
-          if (yych <= 0xEC)
-            goto yy40;
-          goto yy44;
-        }
-      } else {
-        if (yych <= 0xF0) {
-          if (yych <= 0xEF)
-            goto yy40;
-          goto yy41;
-        } else {
-          if (yych <= 0xF3)
-            goto yy42;
-          if (yych <= 0xF4)
-            goto yy43;
-        }
-      }
-    }
-  yy34 : { return (bufsize_t)(p - start); }
-  yy35:
-    yych = *(marker = ++p);
-    if (yych == '|')
-      goto yy49;
-    goto yy50;
-  yy36:
-    yych = *(marker = ++p);
-    goto yy50;
-  yy37:
-    yych = *++p;
-    if (yych <= 0x7F)
-      goto yy38;
-    if (yych <= 0xBF)
-      goto yy36;
-  yy38:
-    p = marker;
-    goto yy34;
-  yy39:
-    yych = *++p;
-    if (yych <= 0x9F)
-      goto yy38;
-    if (yych <= 0xBF)
-      goto yy37;
-    goto yy38;
-  yy40:
-    yych = *++p;
-    if (yych <= 0x7F)
-      goto yy38;
-    if (yych <= 0xBF)
-      goto yy37;
-    goto yy38;
-  yy41:
-    yych = *++p;
-    if (yych <= 0x8F)
-      goto yy38;
-    if (yych <= 0xBF)
-      goto yy40;
-    goto yy38;
-  yy42:
-    yych = *++p;
-    if (yych <= 0x7F)
-      goto yy38;
-    if (yych <= 0xBF)
-      goto yy40;
-    goto yy38;
-  yy43:
-    yych = *++p;
-    if (yych <= 0x7F)
-      goto yy38;
-    if (yych <= 0x8F)
-      goto yy40;
-    goto yy38;
-  yy44:
-    yych = *++p;
-    if (yych <= 0x7F)
-      goto yy38;
-    if (yych <= 0x9F)
-      goto yy37;
-    goto yy38;
-  yy45:
-    ++p;
-    { return 0; }
-  yy47:
-    marker = ++p;
-    yych = *p;
-    if (yybm[0 + yych] & 64) {
-      goto yy47;
-    }
-    if (yych <= 0xDF) {
-      if (yych <= '\f') {
-        if (yych == '\n')
-          goto yy34;
-      } else {
-        if (yych <= '\r')
-          goto yy34;
-        if (yych <= 0x7F)
-          goto yy49;
-        if (yych <= 0xC1)
-          goto yy34;
-        goto yy51;
-      }
-    } else {
-      if (yych <= 0xEF) {
-        if (yych <= 0xE0)
-          goto yy52;
-        if (yych == 0xED)
-          goto yy57;
-        goto yy53;
-      } else {
-        if (yych <= 0xF0)
-          goto yy54;
-        if (yych <= 0xF3)
-          goto yy55;
-        if (yych <= 0xF4)
-          goto yy56;
-        goto yy34;
-      }
-    }
-  yy49:
-    marker = ++p;
-    yych = *p;
-  yy50:
-    if (yybm[0 + yych] & 128) {
-      goto yy49;
-    }
-    if (yych <= 0xEC) {
-      if (yych <= 0xC1) {
-        if (yych <= '\r')
-          goto yy34;
-        if (yych <= '\\')
-          goto yy47;
-        goto yy34;
-      } else {
-        if (yych <= 0xDF)
-          goto yy51;
-        if (yych <= 0xE0)
-          goto yy52;
-        goto yy53;
-      }
-    } else {
-      if (yych <= 0xF0) {
-        if (yych <= 0xED)
-          goto yy57;
-        if (yych <= 0xEF)
-          goto yy53;
-        goto yy54;
-      } else {
-        if (yych <= 0xF3)
-          goto yy55;
-        if (yych <= 0xF4)
-          goto yy56;
-        goto yy34;
-      }
-    }
-  yy51:
-    ++p;
-    yych = *p;
-    if (yych <= 0x7F)
-      goto yy38;
-    if (yych <= 0xBF)
-      goto yy49;
-    goto yy38;
-  yy52:
-    ++p;
-    yych = *p;
-    if (yych <= 0x9F)
-      goto yy38;
-    if (yych <= 0xBF)
-      goto yy51;
-    goto yy38;
-  yy53:
-    ++p;
-    yych = *p;
-    if (yych <= 0x7F)
-      goto yy38;
-    if (yych <= 0xBF)
-      goto yy51;
-    goto yy38;
-  yy54:
-    ++p;
-    yych = *p;
-    if (yych <= 0x8F)
-      goto yy38;
-    if (yych <= 0xBF)
-      goto yy53;
-    goto yy38;
-  yy55:
-    ++p;
-    yych = *p;
-    if (yych <= 0x7F)
-      goto yy38;
-    if (yych <= 0xBF)
-      goto yy53;
-    goto yy38;
-  yy56:
-    ++p;
-    yych = *p;
-    if (yych <= 0x7F)
-      goto yy38;
-    if (yych <= 0x8F)
-      goto yy53;
-    goto yy38;
-  yy57:
-    ++p;
-    yych = *p;
-    if (yych <= 0x7F)
-      goto yy38;
-    if (yych <= 0x9F)
-      goto yy51;
-    goto yy38;
-  }
-}
-
-bufsize_t _scan_table_cell_end(const unsigned char *p) {
-  const unsigned char *marker = NULL;
-  const unsigned char *start = p;
-
-  {
-    unsigned char yych;
-    unsigned int yyaccept = 0;
-    static const unsigned char yybm[] = {
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 128, 128, 0, 0, 0, 0, 0, 0, 0,
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   128, 0, 0, 0, 0, 0, 0, 0,
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   0,   0, 0, 0, 0, 0, 0, 0,
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   0,   0, 0, 0, 0, 0, 0, 0,
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   0,   0, 0, 0, 0, 0, 0, 0,
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   0,   0, 0, 0, 0, 0, 0, 0,
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   0,   0, 0, 0, 0, 0, 0, 0,
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   0,   0, 0, 0, 0, 0, 0, 0,
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   0,   0, 0, 0, 0, 0, 0, 0,
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   0,   0, 0, 0, 0, 0, 0, 0,
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   0,   0, 0, 0, 0, 0, 0, 0,
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   0,   0, 0, 0, 0, 0, 0, 0,
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   0,   0, 0, 0,
-    };
-    yych = *(marker = p);
-    if (yych <= 0xDF) {
-      if (yych <= '{') {
-        if (yych != '\n')
-          goto yy63;
-      } else {
-        if (yych <= '|')
-          goto yy61;
-        if (yych <= 0x7F)
-          goto yy63;
-        if (yych >= 0xC2)
-          goto yy64;
-      }
-    } else {
-      if (yych <= 0xEF) {
-        if (yych <= 0xE0)
-          goto yy66;
-        if (yych == 0xED)
-          goto yy71;
-        goto yy67;
-      } else {
-        if (yych <= 0xF0)
-          goto yy68;
-        if (yych <= 0xF3)
-          goto yy69;
-        if (yych <= 0xF4)
-          goto yy70;
-      }
-    }
-  yy60 : { return 0; }
-  yy61:
-    yyaccept = 1;
-    yych = *(marker = ++p);
-    goto yy73;
-  yy62 : { return (bufsize_t)(p - start); }
-  yy63:
-    yych = *++p;
-    goto yy60;
-  yy64:
-    yych = *++p;
-    if (yych <= 0x7F)
-      goto yy65;
-    if (yych <= 0xBF)
-      goto yy63;
-  yy65:
-    p = marker;
-    if (yyaccept == 0) {
-      goto yy60;
-    } else {
-      goto yy62;
-    }
-  yy66:
-    yych = *++p;
-    if (yych <= 0x9F)
-      goto yy65;
-    if (yych <= 0xBF)
-      goto yy64;
-    goto yy65;
-  yy67:
-    yych = *++p;
-    if (yych <= 0x7F)
-      goto yy65;
-    if (yych <= 0xBF)
-      goto yy64;
-    goto yy65;
-  yy68:
-    yych = *++p;
-    if (yych <= 0x8F)
-      goto yy65;
-    if (yych <= 0xBF)
-      goto yy67;
-    goto yy65;
-  yy69:
-    yych = *++p;
-    if (yych <= 0x7F)
-      goto yy65;
-    if (yych <= 0xBF)
-      goto yy67;
-    goto yy65;
-  yy70:
-    yych = *++p;
-    if (yych <= 0x7F)
-      goto yy65;
-    if (yych <= 0x8F)
-      goto yy67;
-    goto yy65;
-  yy71:
-    yych = *++p;
-    if (yych <= 0x7F)
-      goto yy65;
-    if (yych <= 0x9F)
-      goto yy64;
-    goto yy65;
-  yy72:
-    yyaccept = 1;
-    marker = ++p;
-    yych = *p;
-  yy73:
-    if (yybm[0 + yych] & 128) {
-      goto yy72;
-    }
-    if (yych <= 0x08)
-      goto yy62;
-    if (yych <= '\n')
-      goto yy75;
-    if (yych >= 0x0E)
-      goto yy62;
-    yych = *++p;
-    if (yych != '\n')
-      goto yy65;
-  yy75:
-    ++p;
-    yych = *p;
-    goto yy62;
-  }
-}
-
-bufsize_t _scan_table_row_end(const unsigned char *p) {
-  const unsigned char *marker = NULL;
-  const unsigned char *start = p;
-
-  {
-    unsigned char yych;
-    static const unsigned char yybm[] = {
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 128, 128, 0, 0, 0, 0, 0, 0, 0,
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   128, 0, 0, 0, 0, 0, 0, 0,
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   0,   0, 0, 0, 0, 0, 0, 0,
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   0,   0, 0, 0, 0, 0, 0, 0,
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   0,   0, 0, 0, 0, 0, 0, 0,
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   0,   0, 0, 0, 0, 0, 0, 0,
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   0,   0, 0, 0, 0, 0, 0, 0,
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   0,   0, 0, 0, 0, 0, 0, 0,
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   0,   0, 0, 0, 0, 0, 0, 0,
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   0,   0, 0, 0, 0, 0, 0, 0,
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   0,   0, 0, 0, 0, 0, 0, 0,
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   0,   0, 0, 0, 0, 0, 0, 0,
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   0,   0, 0, 0,
-    };
-    yych = *(marker = p);
-    if (yych <= 0xC1) {
-      if (yych <= '\f') {
-        if (yych <= 0x08)
-          goto yy83;
-        if (yych == '\n')
-          goto yy81;
-        goto yy79;
-      } else {
-        if (yych <= 0x1F) {
-          if (yych <= '\r')
-            goto yy80;
-          goto yy83;
-        } else {
-          if (yych <= ' ')
-            goto yy79;
-          if (yych <= 0x7F)
-            goto yy83;
-        }
-      }
-    } else {
-      if (yych <= 0xED) {
-        if (yych <= 0xDF)
-          goto yy84;
-        if (yych <= 0xE0)
-          goto yy86;
-        if (yych <= 0xEC)
-          goto yy87;
-        goto yy91;
-      } else {
-        if (yych <= 0xF0) {
-          if (yych <= 0xEF)
-            goto yy87;
-          goto yy88;
-        } else {
-          if (yych <= 0xF3)
-            goto yy89;
-          if (yych <= 0xF4)
-            goto yy90;
-        }
-      }
-    }
-  yy78 : { return 0; }
-  yy79:
-    yych = *(marker = ++p);
-    if (yych <= 0x08)
-      goto yy78;
-    if (yych <= '\r')
-      goto yy94;
-    if (yych == ' ')
-      goto yy94;
-    goto yy78;
-  yy80:
-    yych = *++p;
-    if (yych != '\n')
-      goto yy78;
-  yy81:
-    ++p;
-    { return (bufsize_t)(p - start); }
-  yy83:
-    yych = *++p;
-    goto yy78;
-  yy84:
-    yych = *++p;
-    if (yych <= 0x7F)
-      goto yy85;
-    if (yych <= 0xBF)
-      goto yy83;
-  yy85:
-    p = marker;
-    goto yy78;
-  yy86:
-    yych = *++p;
-    if (yych <= 0x9F)
-      goto yy85;
-    if (yych <= 0xBF)
-      goto yy84;
-    goto yy85;
-  yy87:
-    yych = *++p;
-    if (yych <= 0x7F)
-      goto yy85;
-    if (yych <= 0xBF)
-      goto yy84;
-    goto yy85;
-  yy88:
-    yych = *++p;
-    if (yych <= 0x8F)
-      goto yy85;
-    if (yych <= 0xBF)
-      goto yy87;
-    goto yy85;
-  yy89:
-    yych = *++p;
-    if (yych <= 0x7F)
-      goto yy85;
-    if (yych <= 0xBF)
-      goto yy87;
-    goto yy85;
-  yy90:
-    yych = *++p;
-    if (yych <= 0x7F)
-      goto yy85;
-    if (yych <= 0x8F)
-      goto yy87;
-    goto yy85;
-  yy91:
-    yych = *++p;
-    if (yych <= 0x7F)
-      goto yy85;
-    if (yych <= 0x9F)
-      goto yy84;
-    goto yy85;
-  yy92:
-    yych = *++p;
-    if (yych == '\n')
-      goto yy81;
-    goto yy85;
-  yy93:
-    ++p;
-    yych = *p;
-  yy94:
-    if (yybm[0 + yych] & 128) {
-      goto yy93;
-    }
-    if (yych <= 0x08)
-      goto yy85;
-    if (yych <= '\n')
-      goto yy81;
-    if (yych <= '\r')
-      goto yy92;
-    goto yy85;
+/* Generated by re2c 1.1.1 */
+#include "ext_scanners.h"
+#include <stdlib.h>
+
+bufsize_t _ext_scan_at(bufsize_t (*scanner)(const unsigned char *),
+                       unsigned char *ptr, int len, bufsize_t offset) {
+  bufsize_t res;
+
+  if (ptr == NULL || offset >= len) {
+    return 0;
+  } else {
+    unsigned char lim = ptr[len];
+
+    ptr[len] = '\0';
+    res = scanner(ptr + offset);
+    ptr[len] = lim;
+  }
+
+  return res;
+}
+
+bufsize_t _scan_table_start(const unsigned char *p) {
+  const unsigned char *marker = NULL;
+  const unsigned char *start = p;
+
+  {
+    unsigned char yych;
+    static const unsigned char yybm[] = {
+        0, 0,   0, 0, 0, 0, 0, 0, 0, 64, 0,  64, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0,   0, 0, 0, 0, 0, 0, 0, 0,  64, 0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 128, 0, 0, 0, 0, 0, 0, 0, 0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0,   0, 0, 0, 0, 0, 0, 0, 0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0,   0, 0, 0, 0, 0, 0, 0, 0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0,   0, 0, 0, 0, 0, 0, 0, 0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0,   0, 0, 0, 0, 0, 0, 0, 0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0,   0, 0, 0, 0, 0, 0, 0, 0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0,   0, 0, 0, 0, 0, 0, 0, 0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0,   0, 0, 0, 0, 0, 0, 0, 0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0,   0, 0, 0, 0, 0, 0, 0, 0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0,   0, 0, 0, 0, 0, 0, 0, 0,  0,  0,  0,  0,
+    };
+    yych = *(marker = p);
+    if (yych <= '{') {
+      if (yych <= 0x1F) {
+        if (yych <= '\t') {
+          if (yych <= 0x08)
+            goto yy3;
+          goto yy4;
+        } else {
+          if (yych <= '\n')
+            goto yy2;
+          if (yych <= '\f')
+            goto yy4;
+          goto yy3;
+        }
+      } else {
+        if (yych <= '-') {
+          if (yych <= ' ')
+            goto yy4;
+          if (yych <= ',')
+            goto yy3;
+          goto yy5;
+        } else {
+          if (yych == ':')
+            goto yy6;
+          goto yy3;
+        }
+      }
+    } else {
+      if (yych <= 0xEC) {
+        if (yych <= 0xC1) {
+          if (yych <= '|')
+            goto yy4;
+          if (yych <= 0x7F)
+            goto yy3;
+        } else {
+          if (yych <= 0xDF)
+            goto yy7;
+          if (yych <= 0xE0)
+            goto yy9;
+          goto yy10;
+        }
+      } else {
+        if (yych <= 0xF0) {
+          if (yych <= 0xED)
+            goto yy11;
+          if (yych <= 0xEF)
+            goto yy10;
+          goto yy12;
+        } else {
+          if (yych <= 0xF3)
+            goto yy13;
+          if (yych <= 0xF4)
+            goto yy14;
+        }
+      }
+    }
+  yy2 : { return 0; }
+  yy3:
+    ++p;
+    goto yy2;
+  yy4:
+    yych = *(marker = ++p);
+    if (yybm[0 + yych] & 64) {
+      goto yy15;
+    }
+    if (yych == '-')
+      goto yy17;
+    if (yych == ':')
+      goto yy19;
+    goto yy2;
+  yy5:
+    yych = *(marker = ++p);
+    if (yybm[0 + yych] & 128) {
+      goto yy17;
+    }
+    if (yych <= ' ') {
+      if (yych <= 0x08)
+        goto yy2;
+      if (yych <= '\r')
+        goto yy21;
+      if (yych <= 0x1F)
+        goto yy2;
+      goto yy21;
+    } else {
+      if (yych <= ':') {
+        if (yych <= '9')
+          goto yy2;
+        goto yy20;
+      } else {
+        if (yych == '|')
+          goto yy21;
+        goto yy2;
+      }
+    }
+  yy6:
+    yych = *(marker = ++p);
+    if (yybm[0 + yych] & 128) {
+      goto yy17;
+    }
+    goto yy2;
+  yy7:
+    yych = *++p;
+    if (yych <= 0x7F)
+      goto yy8;
+    if (yych <= 0xBF)
+      goto yy3;
+  yy8:
+    p = marker;
+    goto yy2;
+  yy9:
+    yych = *++p;
+    if (yych <= 0x9F)
+      goto yy8;
+    if (yych <= 0xBF)
+      goto yy7;
+    goto yy8;
+  yy10:
+    yych = *++p;
+    if (yych <= 0x7F)
+      goto yy8;
+    if (yych <= 0xBF)
+      goto yy7;
+    goto yy8;
+  yy11:
+    yych = *++p;
+    if (yych <= 0x7F)
+      goto yy8;
+    if (yych <= 0x9F)
+      goto yy7;
+    goto yy8;
+  yy12:
+    yych = *++p;
+    if (yych <= 0x8F)
+      goto yy8;
+    if (yych <= 0xBF)
+      goto yy10;
+    goto yy8;
+  yy13:
+    yych = *++p;
+    if (yych <= 0x7F)
+      goto yy8;
+    if (yych <= 0xBF)
+      goto yy10;
+    goto yy8;
+  yy14:
+    yych = *++p;
+    if (yych <= 0x7F)
+      goto yy8;
+    if (yych <= 0x8F)
+      goto yy10;
+    goto yy8;
+  yy15:
+    yych = *++p;
+    if (yybm[0 + yych] & 64) {
+      goto yy15;
+    }
+    if (yych == '-')
+      goto yy17;
+    if (yych == ':')
+      goto yy19;
+    goto yy8;
+  yy17:
+    yych = *++p;
+    if (yybm[0 + yych] & 128) {
+      goto yy17;
+    }
+    if (yych <= 0x1F) {
+      if (yych <= '\n') {
+        if (yych <= 0x08)
+          goto yy8;
+        if (yych <= '\t')
+          goto yy20;
+        goto yy22;
+      } else {
+        if (yych <= '\f')
+          goto yy20;
+        if (yych <= '\r')
+          goto yy24;
+        goto yy8;
+      }
+    } else {
+      if (yych <= ':') {
+        if (yych <= ' ')
+          goto yy20;
+        if (yych <= '9')
+          goto yy8;
+        goto yy20;
+      } else {
+        if (yych == '|')
+          goto yy25;
+        goto yy8;
+      }
+    }
+  yy19:
+    yych = *++p;
+    if (yybm[0 + yych] & 128) {
+      goto yy17;
+    }
+    goto yy8;
+  yy20:
+    yych = *++p;
+  yy21:
+    if (yych <= '\r') {
+      if (yych <= '\t') {
+        if (yych <= 0x08)
+          goto yy8;
+        goto yy20;
+      } else {
+        if (yych <= '\n')
+          goto yy22;
+        if (yych <= '\f')
+          goto yy20;
+        goto yy24;
+      }
+    } else {
+      if (yych <= ' ') {
+        if (yych <= 0x1F)
+          goto yy8;
+        goto yy20;
+      } else {
+        if (yych == '|')
+          goto yy25;
+        goto yy8;
+      }
+    }
+  yy22:
+    ++p;
+    { return (bufsize_t)(p - start); }
+  yy24:
+    yych = *++p;
+    if (yych == '\n')
+      goto yy22;
+    goto yy8;
+  yy25:
+    yych = *++p;
+    if (yybm[0 + yych] & 128) {
+      goto yy17;
+    }
+    if (yych <= '\r') {
+      if (yych <= '\t') {
+        if (yych <= 0x08)
+          goto yy8;
+        goto yy25;
+      } else {
+        if (yych <= '\n')
+          goto yy22;
+        if (yych <= '\f')
+          goto yy25;
+        goto yy24;
+      }
+    } else {
+      if (yych <= ' ') {
+        if (yych <= 0x1F)
+          goto yy8;
+        goto yy25;
+      } else {
+        if (yych == ':')
+          goto yy19;
+        goto yy8;
+      }
+    }
+  }
+}
+
+bufsize_t _scan_table_cell(const unsigned char *p) {
+  const unsigned char *marker = NULL;
+  const unsigned char *start = p;
+
+  {
+    unsigned char yych;
+    static const unsigned char yybm[] = {
+        64, 64, 64,  64, 64, 64, 64, 64, 64, 64, 0,  64, 64, 0,  64, 64, 64, 64,
+        64, 64, 64,  64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+        64, 64, 64,  64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+        64, 64, 64,  64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+        64, 64, 64,  64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+        64, 64, 128, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+        64, 64, 64,  64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 0,  64,
+        64, 64, 0,   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+        0,  0,  0,   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+        0,  0,  0,   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+        0,  0,  0,   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+        0,  0,  0,   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+        0,  0,  0,   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+        0,  0,  0,   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+        0,  0,  0,   0,
+    };
+    yych = *(marker = p);
+    if (yybm[0 + yych] & 64) {
+      goto yy30;
+    }
+    if (yych <= 0xE0) {
+      if (yych <= '\\') {
+        if (yych <= '\n')
+          goto yy29;
+        if (yych <= '\r')
+          goto yy32;
+        goto yy34;
+      } else {
+        if (yych <= '|')
+          goto yy32;
+        if (yych <= 0xC1)
+          goto yy29;
+        if (yych <= 0xDF)
+          goto yy36;
+        goto yy38;
+      }
+    } else {
+      if (yych <= 0xEF) {
+        if (yych == 0xED)
+          goto yy40;
+        goto yy39;
+      } else {
+        if (yych <= 0xF0)
+          goto yy41;
+        if (yych <= 0xF3)
+          goto yy42;
+        if (yych <= 0xF4)
+          goto yy43;
+      }
+    }
+  yy29 : { return (bufsize_t)(p - start); }
+  yy30:
+    yych = *(marker = ++p);
+    if (yybm[0 + yych] & 64) {
+      goto yy30;
+    }
+    if (yych <= 0xEC) {
+      if (yych <= 0xC1) {
+        if (yych <= '\r')
+          goto yy29;
+        if (yych <= '\\')
+          goto yy34;
+        goto yy29;
+      } else {
+        if (yych <= 0xDF)
+          goto yy36;
+        if (yych <= 0xE0)
+          goto yy38;
+        goto yy39;
+      }
+    } else {
+      if (yych <= 0xF0) {
+        if (yych <= 0xED)
+          goto yy40;
+        if (yych <= 0xEF)
+          goto yy39;
+        goto yy41;
+      } else {
+        if (yych <= 0xF3)
+          goto yy42;
+        if (yych <= 0xF4)
+          goto yy43;
+        goto yy29;
+      }
+    }
+  yy32:
+    ++p;
+    { return 0; }
+  yy34:
+    yych = *(marker = ++p);
+    if (yybm[0 + yych] & 128) {
+      goto yy34;
+    }
+    if (yych <= 0xDF) {
+      if (yych <= '\f') {
+        if (yych == '\n')
+          goto yy29;
+        goto yy30;
+      } else {
+        if (yych <= '\r')
+          goto yy29;
+        if (yych <= 0x7F)
+          goto yy30;
+        if (yych <= 0xC1)
+          goto yy29;
+      }
+    } else {
+      if (yych <= 0xEF) {
+        if (yych <= 0xE0)
+          goto yy38;
+        if (yych == 0xED)
+          goto yy40;
+        goto yy39;
+      } else {
+        if (yych <= 0xF0)
+          goto yy41;
+        if (yych <= 0xF3)
+          goto yy42;
+        if (yych <= 0xF4)
+          goto yy43;
+        goto yy29;
+      }
+    }
+  yy36:
+    yych = *++p;
+    if (yych <= 0x7F)
+      goto yy37;
+    if (yych <= 0xBF)
+      goto yy30;
+  yy37:
+    p = marker;
+    goto yy29;
+  yy38:
+    yych = *++p;
+    if (yych <= 0x9F)
+      goto yy37;
+    if (yych <= 0xBF)
+      goto yy36;
+    goto yy37;
+  yy39:
+    yych = *++p;
+    if (yych <= 0x7F)
+      goto yy37;
+    if (yych <= 0xBF)
+      goto yy36;
+    goto yy37;
+  yy40:
+    yych = *++p;
+    if (yych <= 0x7F)
+      goto yy37;
+    if (yych <= 0x9F)
+      goto yy36;
+    goto yy37;
+  yy41:
+    yych = *++p;
+    if (yych <= 0x8F)
+      goto yy37;
+    if (yych <= 0xBF)
+      goto yy39;
+    goto yy37;
+  yy42:
+    yych = *++p;
+    if (yych <= 0x7F)
+      goto yy37;
+    if (yych <= 0xBF)
+      goto yy39;
+    goto yy37;
+  yy43:
+    yych = *++p;
+    if (yych <= 0x7F)
+      goto yy37;
+    if (yych <= 0x8F)
+      goto yy39;
+    goto yy37;
+  }
+}
+
+bufsize_t _scan_table_cell_end(const unsigned char *p) {
+  const unsigned char *marker = NULL;
+  const unsigned char *start = p;
+
+  {
+    unsigned char yych;
+    unsigned int yyaccept = 0;
+    static const unsigned char yybm[] = {
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 128, 128, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   128, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   0,   0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   0,   0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   0,   0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   0,   0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   0,   0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   0,   0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   0,   0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   0,   0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   0,   0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   0,   0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   0,   0, 0, 0,
+    };
+    yych = *(marker = p);
+    if (yych <= 0xDF) {
+      if (yych <= '{') {
+        if (yych != '\n')
+          goto yy47;
+      } else {
+        if (yych <= '|')
+          goto yy48;
+        if (yych <= 0x7F)
+          goto yy47;
+        if (yych >= 0xC2)
+          goto yy51;
+      }
+    } else {
+      if (yych <= 0xEF) {
+        if (yych <= 0xE0)
+          goto yy53;
+        if (yych == 0xED)
+          goto yy55;
+        goto yy54;
+      } else {
+        if (yych <= 0xF0)
+          goto yy56;
+        if (yych <= 0xF3)
+          goto yy57;
+        if (yych <= 0xF4)
+          goto yy58;
+      }
+    }
+  yy46 : { return 0; }
+  yy47:
+    ++p;
+    goto yy46;
+  yy48:
+    yyaccept = 1;
+    yych = *(marker = ++p);
+    if (yybm[0 + yych] & 128) {
+      goto yy48;
+    }
+    if (yych <= 0x08)
+      goto yy50;
+    if (yych <= '\n')
+      goto yy59;
+    if (yych <= '\r')
+      goto yy60;
+  yy50 : { return (bufsize_t)(p - start); }
+  yy51:
+    yych = *++p;
+    if (yych <= 0x7F)
+      goto yy52;
+    if (yych <= 0xBF)
+      goto yy47;
+  yy52:
+    p = marker;
+    if (yyaccept == 0) {
+      goto yy46;
+    } else {
+      goto yy50;
+    }
+  yy53:
+    yych = *++p;
+    if (yych <= 0x9F)
+      goto yy52;
+    if (yych <= 0xBF)
+      goto yy51;
+    goto yy52;
+  yy54:
+    yych = *++p;
+    if (yych <= 0x7F)
+      goto yy52;
+    if (yych <= 0xBF)
+      goto yy51;
+    goto yy52;
+  yy55:
+    yych = *++p;
+    if (yych <= 0x7F)
+      goto yy52;
+    if (yych <= 0x9F)
+      goto yy51;
+    goto yy52;
+  yy56:
+    yych = *++p;
+    if (yych <= 0x8F)
+      goto yy52;
+    if (yych <= 0xBF)
+      goto yy54;
+    goto yy52;
+  yy57:
+    yych = *++p;
+    if (yych <= 0x7F)
+      goto yy52;
+    if (yych <= 0xBF)
+      goto yy54;
+    goto yy52;
+  yy58:
+    yych = *++p;
+    if (yych <= 0x7F)
+      goto yy52;
+    if (yych <= 0x8F)
+      goto yy54;
+    goto yy52;
+  yy59:
+    ++p;
+    goto yy50;
+  yy60:
+    yych = *++p;
+    if (yych == '\n')
+      goto yy59;
+    goto yy52;
+  }
+}
+
+bufsize_t _scan_table_row_end(const unsigned char *p) {
+  const unsigned char *marker = NULL;
+  const unsigned char *start = p;
+
+  {
+    unsigned char yych;
+    static const unsigned char yybm[] = {
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 128, 128, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   128, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   0,   0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   0,   0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   0,   0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   0,   0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   0,   0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   0,   0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   0,   0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   0,   0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   0,   0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   0,   0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0,   0,   0, 0, 0,
+    };
+    yych = *(marker = p);
+    if (yych <= 0xC1) {
+      if (yych <= '\f') {
+        if (yych <= 0x08)
+          goto yy64;
+        if (yych == '\n')
+          goto yy66;
+        goto yy65;
+      } else {
+        if (yych <= 0x1F) {
+          if (yych <= '\r')
+            goto yy68;
+          goto yy64;
+        } else {
+          if (yych <= ' ')
+            goto yy65;
+          if (yych <= 0x7F)
+            goto yy64;
+        }
+      }
+    } else {
+      if (yych <= 0xED) {
+        if (yych <= 0xDF)
+          goto yy69;
+        if (yych <= 0xE0)
+          goto yy71;
+        if (yych <= 0xEC)
+          goto yy72;
+        goto yy73;
+      } else {
+        if (yych <= 0xF0) {
+          if (yych <= 0xEF)
+            goto yy72;
+          goto yy74;
+        } else {
+          if (yych <= 0xF3)
+            goto yy75;
+          if (yych <= 0xF4)
+            goto yy76;
+        }
+      }
+    }
+  yy63 : { return 0; }
+  yy64:
+    ++p;
+    goto yy63;
+  yy65:
+    yych = *(marker = ++p);
+    if (yych <= 0x08)
+      goto yy63;
+    if (yych <= '\r')
+      goto yy78;
+    if (yych == ' ')
+      goto yy78;
+    goto yy63;
+  yy66:
+    ++p;
+    { return (bufsize_t)(p - start); }
+  yy68:
+    yych = *++p;
+    if (yych == '\n')
+      goto yy66;
+    goto yy63;
+  yy69:
+    yych = *++p;
+    if (yych <= 0x7F)
+      goto yy70;
+    if (yych <= 0xBF)
+      goto yy64;
+  yy70:
+    p = marker;
+    goto yy63;
+  yy71:
+    yych = *++p;
+    if (yych <= 0x9F)
+      goto yy70;
+    if (yych <= 0xBF)
+      goto yy69;
+    goto yy70;
+  yy72:
+    yych = *++p;
+    if (yych <= 0x7F)
+      goto yy70;
+    if (yych <= 0xBF)
+      goto yy69;
+    goto yy70;
+  yy73:
+    yych = *++p;
+    if (yych <= 0x7F)
+      goto yy70;
+    if (yych <= 0x9F)
+      goto yy69;
+    goto yy70;
+  yy74:
+    yych = *++p;
+    if (yych <= 0x8F)
+      goto yy70;
+    if (yych <= 0xBF)
+      goto yy72;
+    goto yy70;
+  yy75:
+    yych = *++p;
+    if (yych <= 0x7F)
+      goto yy70;
+    if (yych <= 0xBF)
+      goto yy72;
+    goto yy70;
+  yy76:
+    yych = *++p;
+    if (yych <= 0x7F)
+      goto yy70;
+    if (yych <= 0x8F)
+      goto yy72;
+    goto yy70;
+  yy77:
+    yych = *++p;
+  yy78:
+    if (yybm[0 + yych] & 128) {
+      goto yy77;
+    }
+    if (yych <= 0x08)
+      goto yy70;
+    if (yych <= '\n')
+      goto yy66;
+    if (yych >= 0x0E)
+      goto yy70;
+    yych = *++p;
+    if (yych == '\n')
+      goto yy66;
+    goto yy70;
+  }
+}
+bufsize_t _scan_tasklist(const unsigned char *p) {
+  const unsigned char *marker = NULL;
+  const unsigned char *start = p;
+
+  {
+    unsigned char yych;
+    static const unsigned char yybm[] = {
+        0,   0,   0,   0,   0,   0,   0,   0,   0,   64,  0, 64, 64, 0, 0, 0,
+        0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 0,  0,  0, 0, 0,
+        64,  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 0,  0,  0, 0, 0,
+        128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 0, 0,  0,  0, 0, 0,
+        0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 0,  0,  0, 0, 0,
+        0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 0,  0,  0, 0, 0,
+        0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 0,  0,  0, 0, 0,
+        0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 0,  0,  0, 0, 0,
+        0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 0,  0,  0, 0, 0,
+        0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 0,  0,  0, 0, 0,
+        0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 0,  0,  0, 0, 0,
+        0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 0,  0,  0, 0, 0,
+        0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 0,  0,  0, 0, 0,
+        0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 0,  0,  0, 0, 0,
+        0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 0,  0,  0, 0, 0,
+        0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 0,  0,  0, 0, 0,
+    };
+    yych = *(marker = p);
+    if (yych <= '/') {
+      if (yych <= 0x1F) {
+        if (yych <= '\t') {
+          if (yych <= 0x08)
+            goto yy83;
+          goto yy84;
+        } else {
+          if (yych <= '\n')
+            goto yy82;
+          if (yych <= '\f')
+            goto yy84;
+          goto yy83;
+        }
+      } else {
+        if (yych <= '+') {
+          if (yych <= ' ')
+            goto yy84;
+          if (yych <= ')')
+            goto yy83;
+          goto yy85;
+        } else {
+          if (yych == '-')
+            goto yy85;
+          goto yy83;
+        }
+      }
+    } else {
+      if (yych <= 0xEC) {
+        if (yych <= 0xC1) {
+          if (yych <= '9')
+            goto yy86;
+          if (yych <= 0x7F)
+            goto yy83;
+        } else {
+          if (yych <= 0xDF)
+            goto yy87;
+          if (yych <= 0xE0)
+            goto yy89;
+          goto yy90;
+        }
+      } else {
+        if (yych <= 0xF0) {
+          if (yych <= 0xED)
+            goto yy91;
+          if (yych <= 0xEF)
+            goto yy90;
+          goto yy92;
+        } else {
+          if (yych <= 0xF3)
+            goto yy93;
+          if (yych <= 0xF4)
+            goto yy94;
+        }
+      }
+    }
+  yy82 : { return 0; }
+  yy83:
+    ++p;
+    goto yy82;
+  yy84:
+    yych = *(marker = ++p);
+    if (yybm[0 + yych] & 64) {
+      goto yy95;
+    }
+    if (yych <= ',') {
+      if (yych <= ')')
+        goto yy82;
+      if (yych <= '+')
+        goto yy97;
+      goto yy82;
+    } else {
+      if (yych <= '-')
+        goto yy97;
+      if (yych <= '/')
+        goto yy82;
+      if (yych <= '9')
+        goto yy98;
+      goto yy82;
+    }
+  yy85:
+    yych = *(marker = ++p);
+    if (yych <= '\n') {
+      if (yych == '\t')
+        goto yy99;
+      goto yy82;
+    } else {
+      if (yych <= '\f')
+        goto yy99;
+      if (yych == ' ')
+        goto yy99;
+      goto yy82;
+    }
+  yy86:
+    yych = *(marker = ++p);
+    if (yych <= 0x1F) {
+      if (yych <= '\t') {
+        if (yych <= 0x08)
+          goto yy102;
+        goto yy97;
+      } else {
+        if (yych <= '\n')
+          goto yy82;
+        if (yych <= '\f')
+          goto yy97;
+        goto yy102;
+      }
+    } else {
+      if (yych <= 0x7F) {
+        if (yych <= ' ')
+          goto yy97;
+        goto yy102;
+      } else {
+        if (yych <= 0xC1)
+          goto yy82;
+        if (yych <= 0xF4)
+          goto yy102;
+        goto yy82;
+      }
+    }
+  yy87:
+    yych = *++p;
+    if (yych <= 0x7F)
+      goto yy88;
+    if (yych <= 0xBF)
+      goto yy83;
+  yy88:
+    p = marker;
+    goto yy82;
+  yy89:
+    yych = *++p;
+    if (yych <= 0x9F)
+      goto yy88;
+    if (yych <= 0xBF)
+      goto yy87;
+    goto yy88;
+  yy90:
+    yych = *++p;
+    if (yych <= 0x7F)
+      goto yy88;
+    if (yych <= 0xBF)
+      goto yy87;
+    goto yy88;
+  yy91:
+    yych = *++p;
+    if (yych <= 0x7F)
+      goto yy88;
+    if (yych <= 0x9F)
+      goto yy87;
+    goto yy88;
+  yy92:
+    yych = *++p;
+    if (yych <= 0x8F)
+      goto yy88;
+    if (yych <= 0xBF)
+      goto yy90;
+    goto yy88;
+  yy93:
+    yych = *++p;
+    if (yych <= 0x7F)
+      goto yy88;
+    if (yych <= 0xBF)
+      goto yy90;
+    goto yy88;
+  yy94:
+    yych = *++p;
+    if (yych <= 0x7F)
+      goto yy88;
+    if (yych <= 0x8F)
+      goto yy90;
+    goto yy88;
+  yy95:
+    yych = *++p;
+    if (yybm[0 + yych] & 64) {
+      goto yy95;
+    }
+    if (yych <= ',') {
+      if (yych <= ')')
+        goto yy88;
+      if (yych >= ',')
+        goto yy88;
+    } else {
+      if (yych <= '-')
+        goto yy97;
+      if (yych <= '/')
+        goto yy88;
+      if (yych <= '9')
+        goto yy98;
+      goto yy88;
+    }
+  yy97:
+    yych = *++p;
+    if (yych == '[')
+      goto yy88;
+    goto yy100;
+  yy98:
+    yych = *++p;
+    if (yych <= '\n') {
+      if (yych == '\t')
+        goto yy97;
+      goto yy102;
+    } else {
+      if (yych <= '\f')
+        goto yy97;
+      if (yych == ' ')
+        goto yy97;
+      goto yy102;
+    }
+  yy99:
+    yych = *++p;
+  yy100:
+    if (yych <= '\f') {
+      if (yych == '\t')
+        goto yy99;
+      if (yych <= '\n')
+        goto yy88;
+      goto yy99;
+    } else {
+      if (yych <= ' ') {
+        if (yych <= 0x1F)
+          goto yy88;
+        goto yy99;
+      } else {
+        if (yych == '[')
+          goto yy110;
+        goto yy88;
+      }
+    }
+  yy101:
+    yych = *++p;
+  yy102:
+    if (yybm[0 + yych] & 128) {
+      goto yy101;
+    }
+    if (yych <= 0xC1) {
+      if (yych <= '\f') {
+        if (yych <= 0x08)
+          goto yy97;
+        if (yych == '\n')
+          goto yy88;
+        goto yy99;
+      } else {
+        if (yych == ' ')
+          goto yy99;
+        if (yych <= 0x7F)
+          goto yy97;
+        goto yy88;
+      }
+    } else {
+      if (yych <= 0xED) {
+        if (yych <= 0xDF)
+          goto yy103;
+        if (yych <= 0xE0)
+          goto yy104;
+        if (yych <= 0xEC)
+          goto yy105;
+        goto yy106;
+      } else {
+        if (yych <= 0xF0) {
+          if (yych <= 0xEF)
+            goto yy105;
+          goto yy107;
+        } else {
+          if (yych <= 0xF3)
+            goto yy108;
+          if (yych <= 0xF4)
+            goto yy109;
+          goto yy88;
+        }
+      }
+    }
+  yy103:
+    yych = *++p;
+    if (yych <= 0x7F)
+      goto yy88;
+    if (yych <= 0xBF)
+      goto yy97;
+    goto yy88;
+  yy104:
+    yych = *++p;
+    if (yych <= 0x9F)
+      goto yy88;
+    if (yych <= 0xBF)
+      goto yy103;
+    goto yy88;
+  yy105:
+    yych = *++p;
+    if (yych <= 0x7F)
+      goto yy88;
+    if (yych <= 0xBF)
+      goto yy103;
+    goto yy88;
+  yy106:
+    yych = *++p;
+    if (yych <= 0x7F)
+      goto yy88;
+    if (yych <= 0x9F)
+      goto yy103;
+    goto yy88;
+  yy107:
+    yych = *++p;
+    if (yych <= 0x8F)
+      goto yy88;
+    if (yych <= 0xBF)
+      goto yy105;
+    goto yy88;
+  yy108:
+    yych = *++p;
+    if (yych <= 0x7F)
+      goto yy88;
+    if (yych <= 0xBF)
+      goto yy105;
+    goto yy88;
+  yy109:
+    yych = *++p;
+    if (yych <= 0x7F)
+      goto yy88;
+    if (yych <= 0x8F)
+      goto yy105;
+    goto yy88;
+  yy110:
+    yych = *++p;
+    if (yych <= 'W') {
+      if (yych != ' ')
+        goto yy88;
+    } else {
+      if (yych <= 'X')
+        goto yy111;
+      if (yych != 'x')
+        goto yy88;
+    }
+  yy111:
+    yych = *++p;
+    if (yych != ']')
+      goto yy88;
+    yych = *++p;
+    if (yych <= '\n') {
+      if (yych != '\t')
+        goto yy88;
+    } else {
+      if (yych <= '\f')
+        goto yy113;
+      if (yych != ' ')
+        goto yy88;
+    }
+  yy113:
+    yych = *++p;
+    if (yych <= '\n') {
+      if (yych == '\t')
+        goto yy113;
+    } else {
+      if (yych <= '\f')
+        goto yy113;
+      if (yych == ' ')
+        goto yy113;
+    }
+    { return (bufsize_t)(p - start); }
   }
 }
diff --git a/cbits/ext_scanners.h b/cbits/ext_scanners.h
--- a/cbits/ext_scanners.h
+++ b/cbits/ext_scanners.h
@@ -11,11 +11,13 @@
 bufsize_t _scan_table_cell(const unsigned char *p);
 bufsize_t _scan_table_cell_end(const unsigned char *p);
 bufsize_t _scan_table_row_end(const unsigned char *p);
+bufsize_t _scan_tasklist(const unsigned char *p);
 
 #define scan_table_start(c, l, n) _ext_scan_at(&_scan_table_start, c, l, n)
 #define scan_table_cell(c, l, n) _ext_scan_at(&_scan_table_cell, c, l, n)
 #define scan_table_cell_end(c, l, n) _ext_scan_at(&_scan_table_cell_end, c, l, n)
 #define scan_table_row_end(c, l, n) _ext_scan_at(&_scan_table_row_end, c, l, n)
+#define scan_tasklist(c, l, n) _ext_scan_at(&_scan_tasklist, c, l, n)
 
 #ifdef __cplusplus
 }
diff --git a/cbits/houdini_href_e.c b/cbits/houdini_href_e.c
--- a/cbits/houdini_href_e.c
+++ b/cbits/houdini_href_e.c
@@ -15,7 +15,7 @@
  *	- The characters which are *not* safe to be in
  *	an URL because they are RESERVED characters.
  *
- * We asume (lazily) that any RESERVED char that
+ * We assume (lazily) that any RESERVED char that
  * appears inside an URL is actually meant to
  * have its native function (i.e. as an URL
  * component/separator) and hence needs no escaping.
diff --git a/cbits/inlines.c b/cbits/inlines.c
--- a/cbits/inlines.c
+++ b/cbits/inlines.c
@@ -760,9 +760,10 @@
   }
   cmark_node_insert_after(opener_inl, emph);
 
-  emph->start_line = emph->end_line = subj->line;
-  emph->start_column = opener_inl->start_column + subj->column_offset;
-  emph->end_column = closer_inl->end_column + subj->column_offset;
+  emph->start_line = opener_inl->start_line;
+  emph->end_line = closer_inl->end_line;
+  emph->start_column = opener_inl->start_column;
+  emph->end_column = closer_inl->end_column;
 
   // if opener has 0 characters, remove it and its associated inline
   if (opener_num_chars == 0) {
@@ -1551,7 +1552,7 @@
     }
   }
 
-  while (peek_char(parser) == c && numdelims <= max_delims) {
+  while (peek_char(parser) == c && numdelims < max_delims) {
     numdelims++;
     advance(parser);
   }
diff --git a/cbits/plaintext.c b/cbits/plaintext.c
--- a/cbits/plaintext.c
+++ b/cbits/plaintext.c
@@ -191,6 +191,28 @@
   case CMARK_NODE_IMAGE:
     break;
 
+  case CMARK_NODE_FOOTNOTE_REFERENCE:
+    if (entering) {
+      LIT("[^");
+      OUT(cmark_chunk_to_cstr(renderer->mem, &node->as.literal), false, LITERAL);
+      LIT("]");
+    }
+    break;
+
+  case CMARK_NODE_FOOTNOTE_DEFINITION:
+    if (entering) {
+      renderer->footnote_ix += 1;
+      LIT("[^");
+      char n[32];
+      snprintf(n, sizeof(n), "%d", renderer->footnote_ix);
+      OUT(n, false, LITERAL);
+      LIT("]: ");
+
+      cmark_strbuf_puts(renderer->prefix, "    ");
+    } else {
+      cmark_strbuf_truncate(renderer->prefix, renderer->prefix->size - 4);
+    }
+    break;
   default:
     assert(false);
     break;
diff --git a/cbits/render.c b/cbits/render.c
--- a/cbits/render.c
+++ b/cbits/render.c
@@ -57,6 +57,7 @@
       }
     }
     renderer->column = 0;
+    renderer->last_breakable = 0;
     renderer->begin_line = true;
     renderer->begin_content = true;
     renderer->need_cr -= 1;
@@ -188,7 +189,7 @@
   }
 
   // ensure final newline
-  if (renderer.buffer->ptr[renderer.buffer->size - 1] != '\n') {
+  if (renderer.buffer->size == 0 || renderer.buffer->ptr[renderer.buffer->size - 1] != '\n') {
     cmark_strbuf_putc(renderer.buffer, '\n');
   }
 
diff --git a/cbits/scanners.c b/cbits/scanners.c
--- a/cbits/scanners.c
+++ b/cbits/scanners.c
@@ -1,4 +1,4 @@
-/* Generated by re2c 1.0.3 */
+/* Generated by re2c 1.1.1 */
 #include <stdlib.h>
 #include "chunk.h"
 #include "scanners.h"
@@ -9226,7 +9226,7 @@
 		144, 192, 192, 192, 192, 192, 192, 192, 
 		192, 192, 192, 192, 192, 192, 192, 192, 
 		192, 192, 192, 192, 192, 192, 192, 192, 
-		192, 192, 192, 192, 192, 192,  96, 192, 
+		192, 192, 192, 192, 192, 192, 224, 192, 
 		  0,   0,   0,   0,   0,   0,   0,   0, 
 		  0,   0,   0,   0,   0,   0,   0,   0, 
 		  0,   0,   0,   0,   0,   0,   0,   0, 
diff --git a/cbits/table.c b/cbits/table.c
--- a/cbits/table.c
+++ b/cbits/table.c
@@ -114,7 +114,7 @@
                                   cmark_parser *parser, unsigned char *string,
                                   int len) {
   table_row *row = NULL;
-  bufsize_t cell_matched, pipe_matched, offset;
+  bufsize_t cell_matched = 1, pipe_matched = 1, offset;
 
   row = (table_row *)parser->mem->calloc(1, sizeof(table_row));
   row->n_columns = 0;
@@ -122,7 +122,9 @@
 
   offset = scan_table_cell_end(string, len, 0);
 
-  do {
+  // Parse the cells of the row. Stop if we reach the end of the input, or if we
+  // cannot detect any more cells.
+  while (offset < len && (cell_matched || pipe_matched)) {
     cell_matched = scan_table_cell(string, len, offset);
     pipe_matched = scan_table_cell_end(string, len, offset + cell_matched);
 
@@ -149,7 +151,7 @@
       pipe_matched = scan_table_row_end(string, len, offset);
       offset += pipe_matched;
     }
-  } while ((cell_matched || pipe_matched) && offset < len);
+  }
 
   if (offset != len || !row->n_columns) {
     free_table_row(parser->mem, row);
diff --git a/cbits/tasklist.c b/cbits/tasklist.c
new file mode 100644
--- /dev/null
+++ b/cbits/tasklist.c
@@ -0,0 +1,135 @@
+#include "tasklist.h"
+#include <parser.h>
+#include <render.h>
+#include <html.h>
+#include "ext_scanners.h"
+
+typedef enum {
+  CMARK_TASKLIST_NOCHECKED,
+  CMARK_TASKLIST_CHECKED,
+} cmark_tasklist_type;
+
+static const char *get_type_string(cmark_syntax_extension *extension, cmark_node *node) {
+  return "tasklist";
+}
+
+char *cmark_gfm_extensions_get_tasklist_state(cmark_node *node) {
+  if (!node || ((int)node->as.opaque != CMARK_TASKLIST_CHECKED && (int)node->as.opaque != CMARK_TASKLIST_NOCHECKED))
+    return 0;
+
+  if ((int)node->as.opaque != CMARK_TASKLIST_CHECKED) {
+    return "checked";
+  }
+  else {
+    return "unchecked";
+  }
+}
+
+static bool parse_node_item_prefix(cmark_parser *parser, const char *input,
+                                   cmark_node *container) {
+  bool res = false;
+
+  if (parser->indent >=
+      container->as.list.marker_offset + container->as.list.padding) {
+    cmark_parser_advance_offset(parser, input, container->as.list.marker_offset +
+                                        container->as.list.padding,
+                     true);
+    res = true;
+  } else if (parser->blank && container->first_child != NULL) {
+    // if container->first_child is NULL, then the opening line
+    // of the list item was blank after the list marker; in this
+    // case, we are done with the list item.
+    cmark_parser_advance_offset(parser, input, parser->first_nonspace - parser->offset,
+                     false);
+    res = true;
+  }
+  return res;
+}
+
+static int matches(cmark_syntax_extension *self, cmark_parser *parser,
+                   unsigned char *input, int len,
+                   cmark_node *parent_container) {
+  return parse_node_item_prefix(parser, (const char*)input, parent_container);
+}
+
+static int can_contain(cmark_syntax_extension *extension, cmark_node *node,
+                       cmark_node_type child_type) {
+  return (node->type == CMARK_NODE_ITEM) ? 1 : 0;
+}
+
+static cmark_node *open_tasklist_item(cmark_syntax_extension *self,
+                                      int indented, cmark_parser *parser,
+                                      cmark_node *parent_container,
+                                      unsigned char *input, int len) {
+  cmark_node_type node_type = cmark_node_get_type(parent_container);
+  if (node_type != CMARK_NODE_ITEM) {
+    return NULL;
+  }
+
+  bufsize_t matched = scan_tasklist(input, len, 0);
+  if (!matched) {
+    return NULL;
+  }
+
+  cmark_node_set_syntax_extension(parent_container, self);
+  cmark_parser_advance_offset(parser, (char *)input, 3, false);
+
+  if (strstr((char*)input, "[x]")) {
+    parent_container->as.opaque = (void *)CMARK_TASKLIST_CHECKED;
+  } else {
+    parent_container->as.opaque = (void *)CMARK_TASKLIST_NOCHECKED;
+  }
+
+  return NULL;
+}
+
+static void commonmark_render(cmark_syntax_extension *extension,
+                              cmark_renderer *renderer, cmark_node *node,
+                              cmark_event_type ev_type, int options) {
+  bool entering = (ev_type == CMARK_EVENT_ENTER);
+  if (entering) {
+    renderer->cr(renderer);
+    if ((int)node->as.opaque == CMARK_TASKLIST_CHECKED) {
+      renderer->out(renderer, node, "- [x] ", false, LITERAL);
+    } else {
+      renderer->out(renderer, node, "- [ ] ", false, LITERAL);
+    }
+    cmark_strbuf_puts(renderer->prefix, "  ");
+  } else {
+    cmark_strbuf_truncate(renderer->prefix, renderer->prefix->size - 2);
+    renderer->cr(renderer);
+  }
+}
+
+static void html_render(cmark_syntax_extension *extension,
+                        cmark_html_renderer *renderer, cmark_node *node,
+                        cmark_event_type ev_type, int options) {
+  bool entering = (ev_type == CMARK_EVENT_ENTER);
+  if (entering) {
+    cmark_html_render_cr(renderer->html);
+    cmark_strbuf_puts(renderer->html, "<li");
+    cmark_html_render_sourcepos(node, renderer->html, options);
+    cmark_strbuf_putc(renderer->html, '>');
+    if ((int)node->as.opaque == CMARK_TASKLIST_CHECKED) {
+      cmark_strbuf_puts(renderer->html, "<input type=\"checkbox\" checked=\"\" disabled=\"\" /> ");
+    } else {
+      cmark_strbuf_puts(renderer->html, "<input type=\"checkbox\" disabled=\"\" /> ");
+    }
+  } else {
+    cmark_strbuf_puts(renderer->html, "</li>\n");
+  }
+}
+
+cmark_syntax_extension *create_tasklist_extension(void) {
+  cmark_syntax_extension *ext = cmark_syntax_extension_new("tasklist");
+
+  cmark_syntax_extension_set_match_block_func(ext, matches);
+  cmark_syntax_extension_set_get_type_string_func(ext, get_type_string);
+  cmark_syntax_extension_set_open_block_func(ext, open_tasklist_item);
+  cmark_syntax_extension_set_can_contain_func(ext, can_contain);
+  cmark_syntax_extension_set_commonmark_render_func(ext, commonmark_render);
+  cmark_syntax_extension_set_plaintext_render_func(ext, commonmark_render);
+  cmark_syntax_extension_set_html_render_func(ext, html_render);
+
+  return ext;
+}
diff --git a/cbits/tasklist.h b/cbits/tasklist.h
new file mode 100644
--- /dev/null
+++ b/cbits/tasklist.h
@@ -0,0 +1,8 @@
+#ifndef TASKLIST_H
+#define TASKLIST_H
+
+#include "cmark-gfm-core-extensions.h"
+
+cmark_syntax_extension *create_tasklist_extension(void);
+
+#endif
diff --git a/changelog b/changelog
--- a/changelog
+++ b/changelog
@@ -1,3 +1,21 @@
+cmark-gfm 0.1.7 (13 Mar 2019)
+
+  * Update to cmark-gfm 0.28.3.gfm.20.
+
+cmark 0.5.6.1 (11 Mar 2019)
+
+  * Fix CString memory leaks (Anders Kaseorg).  The renderer functions return
+    a malloc’d string which the caller is expected to free.
+
+  * Fix use-after-free bugs (Anders Kaseorg).  `useAsCString` frees the
+    CString after the IO action finishes executing, so passing `return` as that
+    action can never be correct.  To make sure the CString gets freed at the
+    right time, we need a “with”-style abstraction, not just a conversion
+    function.  While we’re here replace this with `withCStringLen`, which also
+    makes fewer copies.
+
+  * Show node type on error for unknown node type.
+
 cmark-gfm 0.1.6 (17 Oct 2018)
 
   * Update to cmark-gfm 0.28.3.gfm.18.
@@ -31,7 +49,7 @@
   * Update to cmark-gfm (between revisions).
   * Add extension support.
 
-cmark 0.5.5.1 (02 Aug 2017)
+cmark 0.5.6 (02 Aug 2017)
 
   * Update to cmark 0.28.0.
 
diff --git a/cmark-gfm.cabal b/cmark-gfm.cabal
--- a/cmark-gfm.cabal
+++ b/cmark-gfm.cabal
@@ -1,9 +1,9 @@
 name:                cmark-gfm
-version:             0.1.6
+version:             0.1.7
 synopsis:            Fast, accurate GitHub Flavored Markdown parser and renderer
 description:
   This package provides Haskell bindings for
-  <https://github.com/github/cmark libcmark-gfm>, the reference
+  <https://github.com/github/cmark-gfm libcmark-gfm>, the reference
   parser for <https://github.github.com/gfm/ GitHub Flavored Markdown>, a fully
   specified variant of Markdown. It includes sources for
   libcmark-gfm (0.28.0) and does not require prior installation of the
@@ -51,6 +51,7 @@
                      cbits/strikethrough.h
                      cbits/table.h
                      cbits/tagfilter.h
+                     cbits/tasklist.h
                      cbits/cmark-gfm-extensions_export.h
                      cbits/map.h
                      cbits/footnotes.h
@@ -112,6 +113,7 @@
                        cbits/strikethrough.c
                        cbits/table.c
                        cbits/tagfilter.c
+                       cbits/tasklist.c
                        cbits/footnotes.c
                        cbits/map.c
 
