Merge branch 'minbpc'
diff --git a/expat/lib/xmltok_impl.c b/expat/lib/xmltok_impl.c
index 647843d..fd0ee22 100644
--- a/expat/lib/xmltok_impl.c
+++ b/expat/lib/xmltok_impl.c
@@ -87,27 +87,45 @@
 #define PREFIX(ident) ident
 #endif
 
+
+#define HAS_CHARS(enc, ptr, end, count) \
+    (end - ptr >= count * MINBPC(enc))
+
+#define HAS_CHAR(enc, ptr, end) \
+    HAS_CHARS(enc, ptr, end, 1)
+
+#define REQUIRE_CHARS(enc, ptr, end, count) \
+    { \
+      if (! HAS_CHARS(enc, ptr, end, count)) { \
+        return XML_TOK_PARTIAL; \
+      } \
+    }
+
+#define REQUIRE_CHAR(enc, ptr, end) \
+    REQUIRE_CHARS(enc, ptr, end, 1)
+
+
 /* ptr points to character following "<!-" */
 
 static int PTRCALL
 PREFIX(scanComment)(const ENCODING *enc, const char *ptr,
                     const char *end, const char **nextTokPtr)
 {
-  if (ptr < end) {
+  if (HAS_CHAR(enc, ptr, end)) {
     if (!CHAR_MATCHES(enc, ptr, ASCII_MINUS)) {
       *nextTokPtr = ptr;
       return XML_TOK_INVALID;
     }
     ptr += MINBPC(enc);
-    while (ptr < end) {
+    while (HAS_CHAR(enc, ptr, end)) {
       switch (BYTE_TYPE(enc, ptr)) {
       INVALID_CASES(ptr, nextTokPtr)
       case BT_MINUS:
-        if ((ptr += MINBPC(enc)) == end)
-          return XML_TOK_PARTIAL;
+        ptr += MINBPC(enc);
+        REQUIRE_CHAR(enc, ptr, end);
         if (CHAR_MATCHES(enc, ptr, ASCII_MINUS)) {
-          if ((ptr += MINBPC(enc)) == end)
-            return XML_TOK_PARTIAL;
+          ptr += MINBPC(enc);
+          REQUIRE_CHAR(enc, ptr, end);
           if (!CHAR_MATCHES(enc, ptr, ASCII_GT)) {
             *nextTokPtr = ptr;
             return XML_TOK_INVALID;
@@ -131,8 +149,7 @@
 PREFIX(scanDecl)(const ENCODING *enc, const char *ptr,
                  const char *end, const char **nextTokPtr)
 {
-  if (ptr == end)
-    return XML_TOK_PARTIAL;
+  REQUIRE_CHAR(enc, ptr, end);
   switch (BYTE_TYPE(enc, ptr)) {
   case BT_MINUS:
     return PREFIX(scanComment)(enc, ptr + MINBPC(enc), end, nextTokPtr);
@@ -147,11 +164,10 @@
     *nextTokPtr = ptr;
     return XML_TOK_INVALID;
   }
-  while (ptr < end) {
+  while (HAS_CHAR(enc, ptr, end)) {
     switch (BYTE_TYPE(enc, ptr)) {
     case BT_PERCNT:
-      if (ptr + MINBPC(enc) == end)
-        return XML_TOK_PARTIAL;
+      REQUIRE_CHARS(enc, ptr, end, 2);
       /* don't allow <!ENTITY% foo "whatever"> */
       switch (BYTE_TYPE(enc, ptr + MINBPC(enc))) {
       case BT_S: case BT_CR: case BT_LF: case BT_PERCNT:
@@ -225,15 +241,14 @@
 {
   int tok;
   const char *target = ptr;
-  if (ptr == end)
-    return XML_TOK_PARTIAL;
+  REQUIRE_CHAR(enc, ptr, end);
   switch (BYTE_TYPE(enc, ptr)) {
   CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
   default:
     *nextTokPtr = ptr;
     return XML_TOK_INVALID;
   }
-  while (ptr < end) {
+  while (HAS_CHAR(enc, ptr, end)) {
     switch (BYTE_TYPE(enc, ptr)) {
     CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
     case BT_S: case BT_CR: case BT_LF:
@@ -242,13 +257,12 @@
         return XML_TOK_INVALID;
       }
       ptr += MINBPC(enc);
-      while (ptr < end) {
+      while (HAS_CHAR(enc, ptr, end)) {
         switch (BYTE_TYPE(enc, ptr)) {
         INVALID_CASES(ptr, nextTokPtr)
         case BT_QUEST:
           ptr += MINBPC(enc);
-          if (ptr == end)
-            return XML_TOK_PARTIAL;
+          REQUIRE_CHAR(enc, ptr, end);
           if (CHAR_MATCHES(enc, ptr, ASCII_GT)) {
             *nextTokPtr = ptr + MINBPC(enc);
             return tok;
@@ -266,8 +280,7 @@
         return XML_TOK_INVALID;
       }
       ptr += MINBPC(enc);
-      if (ptr == end)
-        return XML_TOK_PARTIAL;
+      REQUIRE_CHAR(enc, ptr, end);
       if (CHAR_MATCHES(enc, ptr, ASCII_GT)) {
         *nextTokPtr = ptr + MINBPC(enc);
         return tok;
@@ -289,8 +302,7 @@
                                      ASCII_T, ASCII_A, ASCII_LSQB };
   int i;
   /* CDATA[ */
-  if (end - ptr < 6 * MINBPC(enc))
-    return XML_TOK_PARTIAL;
+  REQUIRE_CHARS(enc, ptr, end, 6);
   for (i = 0; i < 6; i++, ptr += MINBPC(enc)) {
     if (!CHAR_MATCHES(enc, ptr, CDATA_LSQB[i])) {
       *nextTokPtr = ptr;
@@ -319,13 +331,11 @@
   switch (BYTE_TYPE(enc, ptr)) {
   case BT_RSQB:
     ptr += MINBPC(enc);
-    if (ptr == end)
-      return XML_TOK_PARTIAL;
+    REQUIRE_CHAR(enc, ptr, end);
     if (!CHAR_MATCHES(enc, ptr, ASCII_RSQB))
       break;
     ptr += MINBPC(enc);
-    if (ptr == end)
-      return XML_TOK_PARTIAL;
+    REQUIRE_CHAR(enc, ptr, end);
     if (!CHAR_MATCHES(enc, ptr, ASCII_GT)) {
       ptr -= MINBPC(enc);
       break;
@@ -334,8 +344,7 @@
     return XML_TOK_CDATA_SECT_CLOSE;
   case BT_CR:
     ptr += MINBPC(enc);
-    if (ptr == end)
-      return XML_TOK_PARTIAL;
+    REQUIRE_CHAR(enc, ptr, end);
     if (BYTE_TYPE(enc, ptr) == BT_LF)
       ptr += MINBPC(enc);
     *nextTokPtr = ptr;
@@ -348,7 +357,7 @@
     ptr += MINBPC(enc);
     break;
   }
-  while (ptr < end) {
+  while (HAS_CHAR(enc, ptr, end)) {
     switch (BYTE_TYPE(enc, ptr)) {
 #define LEAD_CASE(n) \
     case BT_LEAD ## n: \
@@ -383,19 +392,18 @@
 PREFIX(scanEndTag)(const ENCODING *enc, const char *ptr,
                    const char *end, const char **nextTokPtr)
 {
-  if (ptr == end)
-    return XML_TOK_PARTIAL;
+  REQUIRE_CHAR(enc, ptr, end);
   switch (BYTE_TYPE(enc, ptr)) {
   CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
   default:
     *nextTokPtr = ptr;
     return XML_TOK_INVALID;
   }
-  while (ptr < end) {
+  while (HAS_CHAR(enc, ptr, end)) {
     switch (BYTE_TYPE(enc, ptr)) {
     CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
     case BT_S: case BT_CR: case BT_LF:
-      for (ptr += MINBPC(enc); ptr < end; ptr += MINBPC(enc)) {
+      for (ptr += MINBPC(enc); HAS_CHAR(enc, ptr, end); ptr += MINBPC(enc)) {
         switch (BYTE_TYPE(enc, ptr)) {
         case BT_S: case BT_CR: case BT_LF:
           break;
@@ -432,7 +440,7 @@
 PREFIX(scanHexCharRef)(const ENCODING *enc, const char *ptr,
                        const char *end, const char **nextTokPtr)
 {
-  if (ptr < end) {
+  if (HAS_CHAR(enc, ptr, end)) {
     switch (BYTE_TYPE(enc, ptr)) {
     case BT_DIGIT:
     case BT_HEX:
@@ -441,7 +449,7 @@
       *nextTokPtr = ptr;
       return XML_TOK_INVALID;
     }
-    for (ptr += MINBPC(enc); ptr < end; ptr += MINBPC(enc)) {
+    for (ptr += MINBPC(enc); HAS_CHAR(enc, ptr, end); ptr += MINBPC(enc)) {
       switch (BYTE_TYPE(enc, ptr)) {
       case BT_DIGIT:
       case BT_HEX:
@@ -464,7 +472,7 @@
 PREFIX(scanCharRef)(const ENCODING *enc, const char *ptr,
                     const char *end, const char **nextTokPtr)
 {
-  if (ptr < end) {
+  if (HAS_CHAR(enc, ptr, end)) {
     if (CHAR_MATCHES(enc, ptr, ASCII_x))
       return PREFIX(scanHexCharRef)(enc, ptr + MINBPC(enc), end, nextTokPtr);
     switch (BYTE_TYPE(enc, ptr)) {
@@ -474,7 +482,7 @@
       *nextTokPtr = ptr;
       return XML_TOK_INVALID;
     }
-    for (ptr += MINBPC(enc); ptr < end; ptr += MINBPC(enc)) {
+    for (ptr += MINBPC(enc); HAS_CHAR(enc, ptr, end); ptr += MINBPC(enc)) {
       switch (BYTE_TYPE(enc, ptr)) {
       case BT_DIGIT:
         break;
@@ -496,8 +504,7 @@
 PREFIX(scanRef)(const ENCODING *enc, const char *ptr, const char *end,
                 const char **nextTokPtr)
 {
-  if (ptr == end)
-    return XML_TOK_PARTIAL;
+  REQUIRE_CHAR(enc, ptr, end);
   switch (BYTE_TYPE(enc, ptr)) {
   CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
   case BT_NUM:
@@ -506,7 +513,7 @@
     *nextTokPtr = ptr;
     return XML_TOK_INVALID;
   }
-  while (ptr < end) {
+  while (HAS_CHAR(enc, ptr, end)) {
     switch (BYTE_TYPE(enc, ptr)) {
     CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
     case BT_SEMI:
@@ -529,7 +536,7 @@
 #ifdef XML_NS
   int hadColon = 0;
 #endif
-  while (ptr < end) {
+  while (HAS_CHAR(enc, ptr, end)) {
     switch (BYTE_TYPE(enc, ptr)) {
     CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
 #ifdef XML_NS
@@ -540,8 +547,7 @@
       }
       hadColon = 1;
       ptr += MINBPC(enc);
-      if (ptr == end)
-        return XML_TOK_PARTIAL;
+      REQUIRE_CHAR(enc, ptr, end);
       switch (BYTE_TYPE(enc, ptr)) {
       CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
       default:
@@ -555,8 +561,7 @@
         int t;
 
         ptr += MINBPC(enc);
-        if (ptr == end)
-          return XML_TOK_PARTIAL;
+        REQUIRE_CHAR(enc, ptr, end);
         t = BYTE_TYPE(enc, ptr);
         if (t == BT_EQUALS)
           break;
@@ -579,8 +584,7 @@
 #endif
         for (;;) {
           ptr += MINBPC(enc);
-          if (ptr == end)
-            return XML_TOK_PARTIAL;
+          REQUIRE_CHAR(enc, ptr, end);
           open = BYTE_TYPE(enc, ptr);
           if (open == BT_QUOT || open == BT_APOS)
             break;
@@ -598,8 +602,7 @@
         /* in attribute value */
         for (;;) {
           int t;
-          if (ptr == end)
-            return XML_TOK_PARTIAL;
+          REQUIRE_CHAR(enc, ptr, end);
           t = BYTE_TYPE(enc, ptr);
           if (t == open)
             break;
@@ -624,8 +627,7 @@
           }
         }
         ptr += MINBPC(enc);
-        if (ptr == end)
-          return XML_TOK_PARTIAL;
+        REQUIRE_CHAR(enc, ptr, end);
         switch (BYTE_TYPE(enc, ptr)) {
         case BT_S:
         case BT_CR:
@@ -642,8 +644,7 @@
         /* ptr points to closing quote */
         for (;;) {
           ptr += MINBPC(enc);
-          if (ptr == end)
-            return XML_TOK_PARTIAL;
+          REQUIRE_CHAR(enc, ptr, end);
           switch (BYTE_TYPE(enc, ptr)) {
           CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
           case BT_S: case BT_CR: case BT_LF:
@@ -655,8 +656,7 @@
           case BT_SOL:
           sol:
             ptr += MINBPC(enc);
-            if (ptr == end)
-              return XML_TOK_PARTIAL;
+            REQUIRE_CHAR(enc, ptr, end);
             if (!CHAR_MATCHES(enc, ptr, ASCII_GT)) {
               *nextTokPtr = ptr;
               return XML_TOK_INVALID;
@@ -688,13 +688,12 @@
 #ifdef XML_NS
   int hadColon;
 #endif
-  if (ptr == end)
-    return XML_TOK_PARTIAL;
+  REQUIRE_CHAR(enc, ptr, end);
   switch (BYTE_TYPE(enc, ptr)) {
   CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
   case BT_EXCL:
-    if ((ptr += MINBPC(enc)) == end)
-      return XML_TOK_PARTIAL;
+    ptr += MINBPC(enc);
+    REQUIRE_CHAR(enc, ptr, end);
     switch (BYTE_TYPE(enc, ptr)) {
     case BT_MINUS:
       return PREFIX(scanComment)(enc, ptr + MINBPC(enc), end, nextTokPtr);
@@ -716,7 +715,7 @@
   hadColon = 0;
 #endif
   /* we have a start-tag */
-  while (ptr < end) {
+  while (HAS_CHAR(enc, ptr, end)) {
     switch (BYTE_TYPE(enc, ptr)) {
     CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
 #ifdef XML_NS
@@ -727,8 +726,7 @@
       }
       hadColon = 1;
       ptr += MINBPC(enc);
-      if (ptr == end)
-        return XML_TOK_PARTIAL;
+      REQUIRE_CHAR(enc, ptr, end);
       switch (BYTE_TYPE(enc, ptr)) {
       CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
       default:
@@ -740,7 +738,7 @@
     case BT_S: case BT_CR: case BT_LF:
       {
         ptr += MINBPC(enc);
-        while (ptr < end) {
+        while (HAS_CHAR(enc, ptr, end)) {
           switch (BYTE_TYPE(enc, ptr)) {
           CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
           case BT_GT:
@@ -765,8 +763,7 @@
     case BT_SOL:
     sol:
       ptr += MINBPC(enc);
-      if (ptr == end)
-        return XML_TOK_PARTIAL;
+      REQUIRE_CHAR(enc, ptr, end);
       if (!CHAR_MATCHES(enc, ptr, ASCII_GT)) {
         *nextTokPtr = ptr;
         return XML_TOK_INVALID;
@@ -803,7 +800,7 @@
     return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr);
   case BT_CR:
     ptr += MINBPC(enc);
-    if (ptr == end)
+    if (! HAS_CHAR(enc, ptr, end))
       return XML_TOK_TRAILING_CR;
     if (BYTE_TYPE(enc, ptr) == BT_LF)
       ptr += MINBPC(enc);
@@ -814,12 +811,12 @@
     return XML_TOK_DATA_NEWLINE;
   case BT_RSQB:
     ptr += MINBPC(enc);
-    if (ptr == end)
+    if (! HAS_CHAR(enc, ptr, end))
       return XML_TOK_TRAILING_RSQB;
     if (!CHAR_MATCHES(enc, ptr, ASCII_RSQB))
       break;
     ptr += MINBPC(enc);
-    if (ptr == end)
+    if (! HAS_CHAR(enc, ptr, end))
       return XML_TOK_TRAILING_RSQB;
     if (!CHAR_MATCHES(enc, ptr, ASCII_GT)) {
       ptr -= MINBPC(enc);
@@ -832,7 +829,7 @@
     ptr += MINBPC(enc);
     break;
   }
-  while (ptr < end) {
+  while (HAS_CHAR(enc, ptr, end)) {
     switch (BYTE_TYPE(enc, ptr)) {
 #define LEAD_CASE(n) \
     case BT_LEAD ## n: \
@@ -845,12 +842,12 @@
     LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4)
 #undef LEAD_CASE
     case BT_RSQB:
-      if (ptr + MINBPC(enc) != end) {
+      if (HAS_CHARS(enc, ptr, end, 2)) {
          if (!CHAR_MATCHES(enc, ptr + MINBPC(enc), ASCII_RSQB)) {
            ptr += MINBPC(enc);
            break;
          }
-         if (ptr + 2*MINBPC(enc) != end) {
+         if (HAS_CHARS(enc, ptr, end, 3)) {
            if (!CHAR_MATCHES(enc, ptr + 2*MINBPC(enc), ASCII_GT)) {
              ptr += MINBPC(enc);
              break;
@@ -884,8 +881,7 @@
 PREFIX(scanPercent)(const ENCODING *enc, const char *ptr, const char *end,
                     const char **nextTokPtr)
 {
-  if (ptr == end)
-    return XML_TOK_PARTIAL;
+  REQUIRE_CHAR(enc, ptr, end);
   switch (BYTE_TYPE(enc, ptr)) {
   CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
   case BT_S: case BT_LF: case BT_CR: case BT_PERCNT:
@@ -895,7 +891,7 @@
     *nextTokPtr = ptr;
     return XML_TOK_INVALID;
   }
-  while (ptr < end) {
+  while (HAS_CHAR(enc, ptr, end)) {
     switch (BYTE_TYPE(enc, ptr)) {
     CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
     case BT_SEMI:
@@ -913,15 +909,14 @@
 PREFIX(scanPoundName)(const ENCODING *enc, const char *ptr, const char *end,
                       const char **nextTokPtr)
 {
-  if (ptr == end)
-    return XML_TOK_PARTIAL;
+  REQUIRE_CHAR(enc, ptr, end);
   switch (BYTE_TYPE(enc, ptr)) {
   CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
   default:
     *nextTokPtr = ptr;
     return XML_TOK_INVALID;
   }
-  while (ptr < end) {
+  while (HAS_CHAR(enc, ptr, end)) {
     switch (BYTE_TYPE(enc, ptr)) {
     CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
     case BT_CR: case BT_LF: case BT_S:
@@ -941,7 +936,7 @@
                 const char *ptr, const char *end,
                 const char **nextTokPtr)
 {
-  while (ptr < end) {
+  while (HAS_CHAR(enc, ptr, end)) {
     int t = BYTE_TYPE(enc, ptr);
     switch (t) {
     INVALID_CASES(ptr, nextTokPtr)
@@ -950,7 +945,7 @@
       ptr += MINBPC(enc);
       if (t != open)
         break;
-      if (ptr == end)
+      if (! HAS_CHAR(enc, ptr, end))
         return -XML_TOK_LITERAL;
       *nextTokPtr = ptr;
       switch (BYTE_TYPE(enc, ptr)) {
@@ -992,8 +987,7 @@
   case BT_LT:
     {
       ptr += MINBPC(enc);
-      if (ptr == end)
-        return XML_TOK_PARTIAL;
+      REQUIRE_CHAR(enc, ptr, end);
       switch (BYTE_TYPE(enc, ptr)) {
       case BT_EXCL:
         return PREFIX(scanDecl)(enc, ptr + MINBPC(enc), end, nextTokPtr);
@@ -1021,7 +1015,7 @@
   case BT_S: case BT_LF:
     for (;;) {
       ptr += MINBPC(enc);
-      if (ptr == end)
+      if (! HAS_CHAR(enc, ptr, end))
         break;
       switch (BYTE_TYPE(enc, ptr)) {
       case BT_S: case BT_LF:
@@ -1048,11 +1042,10 @@
     return XML_TOK_OPEN_BRACKET;
   case BT_RSQB:
     ptr += MINBPC(enc);
-    if (ptr == end)
+    if (! HAS_CHAR(enc, ptr, end))
       return -XML_TOK_CLOSE_BRACKET;
     if (CHAR_MATCHES(enc, ptr, ASCII_RSQB)) {
-      if (ptr + MINBPC(enc) == end)
-        return XML_TOK_PARTIAL;
+      REQUIRE_CHARS(enc, ptr, end, 2);
       if (CHAR_MATCHES(enc, ptr + MINBPC(enc), ASCII_GT)) {
         *nextTokPtr = ptr + 2*MINBPC(enc);
         return XML_TOK_COND_SECT_CLOSE;
@@ -1065,7 +1058,7 @@
     return XML_TOK_OPEN_PAREN;
   case BT_RPAR:
     ptr += MINBPC(enc);
-    if (ptr == end)
+    if (! HAS_CHAR(enc, ptr, end))
       return -XML_TOK_CLOSE_PAREN;
     switch (BYTE_TYPE(enc, ptr)) {
     case BT_AST:
@@ -1141,7 +1134,7 @@
     *nextTokPtr = ptr;
     return XML_TOK_INVALID;
   }
-  while (ptr < end) {
+  while (HAS_CHAR(enc, ptr, end)) {
     switch (BYTE_TYPE(enc, ptr)) {
     CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
     case BT_GT: case BT_RPAR: case BT_COMMA:
@@ -1154,8 +1147,7 @@
       ptr += MINBPC(enc);
       switch (tok) {
       case XML_TOK_NAME:
-        if (ptr == end)
-          return XML_TOK_PARTIAL;
+        REQUIRE_CHAR(enc, ptr, end);
         tok = XML_TOK_PREFIXED_NAME;
         switch (BYTE_TYPE(enc, ptr)) {
         CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
@@ -1207,7 +1199,7 @@
   if (ptr >= end)
     return XML_TOK_NONE;
   start = ptr;
-  while (ptr < end) {
+  while (HAS_CHAR(enc, ptr, end)) {
     switch (BYTE_TYPE(enc, ptr)) {
 #define LEAD_CASE(n) \
     case BT_LEAD ## n: ptr += n; break;
@@ -1232,7 +1224,7 @@
     case BT_CR:
       if (ptr == start) {
         ptr += MINBPC(enc);
-        if (ptr == end)
+        if (! HAS_CHAR(enc, ptr, end))
           return XML_TOK_TRAILING_CR;
         if (BYTE_TYPE(enc, ptr) == BT_LF)
           ptr += MINBPC(enc);
@@ -1265,7 +1257,7 @@
   if (ptr >= end)
     return XML_TOK_NONE;
   start = ptr;
-  while (ptr < end) {
+  while (HAS_CHAR(enc, ptr, end)) {
     switch (BYTE_TYPE(enc, ptr)) {
 #define LEAD_CASE(n) \
     case BT_LEAD ## n: ptr += n; break;
@@ -1294,7 +1286,7 @@
     case BT_CR:
       if (ptr == start) {
         ptr += MINBPC(enc);
-        if (ptr == end)
+        if (! HAS_CHAR(enc, ptr, end))
           return XML_TOK_TRAILING_CR;
         if (BYTE_TYPE(enc, ptr) == BT_LF)
           ptr += MINBPC(enc);
@@ -1326,15 +1318,15 @@
       end = ptr + n;
     }
   }
-  while (ptr < end) {
+  while (HAS_CHAR(enc, ptr, end)) {
     switch (BYTE_TYPE(enc, ptr)) {
     INVALID_CASES(ptr, nextTokPtr)
     case BT_LT:
-      if ((ptr += MINBPC(enc)) == end)
-        return XML_TOK_PARTIAL;
+      ptr += MINBPC(enc);
+      REQUIRE_CHAR(enc, ptr, end);
       if (CHAR_MATCHES(enc, ptr, ASCII_EXCL)) {
-        if ((ptr += MINBPC(enc)) == end)
-          return XML_TOK_PARTIAL;
+        ptr += MINBPC(enc);
+        REQUIRE_CHAR(enc, ptr, end);
         if (CHAR_MATCHES(enc, ptr, ASCII_LSQB)) {
           ++level;
           ptr += MINBPC(enc);
@@ -1342,11 +1334,11 @@
       }
       break;
     case BT_RSQB:
-      if ((ptr += MINBPC(enc)) == end)
-        return XML_TOK_PARTIAL;
+      ptr += MINBPC(enc);
+      REQUIRE_CHAR(enc, ptr, end);
       if (CHAR_MATCHES(enc, ptr, ASCII_RSQB)) {
-        if ((ptr += MINBPC(enc)) == end)
-          return XML_TOK_PARTIAL;
+        ptr += MINBPC(enc);
+        REQUIRE_CHAR(enc, ptr, end);
         if (CHAR_MATCHES(enc, ptr, ASCII_GT)) {
           ptr += MINBPC(enc);
           if (level == 0) {
@@ -1373,7 +1365,7 @@
 {
   ptr += MINBPC(enc);
   end -= MINBPC(enc);
-  for (; ptr < end; ptr += MINBPC(enc)) {
+  for (; HAS_CHAR(enc, ptr, end); ptr += MINBPC(enc)) {
     switch (BYTE_TYPE(enc, ptr)) {
     case BT_DIGIT:
     case BT_HEX:
@@ -1687,7 +1679,7 @@
                          const char *end1, const char *ptr2)
 {
   for (; *ptr2; ptr1 += MINBPC(enc), ptr2++) {
-    if (ptr1 == end1)
+    if (end1 - ptr1 < MINBPC(enc))
       return 0;
     if (!CHAR_MATCHES(enc, ptr1, *ptr2))
       return 0;
@@ -1744,7 +1736,7 @@
                        const char *end,
                        POSITION *pos)
 {
-  while (ptr < end) {
+  while (HAS_CHAR(enc, ptr, end)) {
     switch (BYTE_TYPE(enc, ptr)) {
 #define LEAD_CASE(n) \
     case BT_LEAD ## n: \
@@ -1760,7 +1752,7 @@
     case BT_CR:
       pos->lineNumber++;
       ptr += MINBPC(enc);
-      if (ptr < end && BYTE_TYPE(enc, ptr) == BT_LF)
+      if (HAS_CHAR(enc, ptr, end) && BYTE_TYPE(enc, ptr) == BT_LF)
         ptr += MINBPC(enc);
       pos->columnNumber = (XML_Size)-1;
       break;
diff --git a/expat/qa.sh b/expat/qa.sh
index 070993a..0dce871 100755
--- a/expat/qa.sh
+++ b/expat/qa.sh
@@ -9,14 +9,29 @@
 : ${CLANG_CC:=clang}
 : ${CLANG_CXX:=clang++}
 
+: ${AR:=ar}
 : ${CC:="${CLANG_CC}"}
 : ${CXX:="${CLANG_CXX}"}
+: ${LD:=ld}
 : ${MAKE:=make}
 
 : ${BASE_FLAGS:="-pipe -Wall -Wextra -pedantic -Wno-overlength-strings"}
 
+RUN() {
+    local open='\e[1m'
+    local close='\e[0m'
+
+    echo -e -n "${open}"
+    echo -n "# $*"
+    echo -e "${close}"
+
+    env "$@"
+}
+
 main() {
     local mode="${1:-}"
+    shift
+
     local RUNENV
     local BASE_FLAGS="${BASE_FLAGS}"
 
@@ -36,6 +51,13 @@
         # http://clang.llvm.org/docs/MemorySanitizer.html
         BASE_FLAGS+=" -fsanitize=memory -fno-omit-frame-pointer -g -O2"
         ;;
+    ncc)
+        # http://students.ceid.upatras.gr/~sxanth/ncc/
+        local CC="ncc -ncgcc -ncld -ncfabs"
+        local AR=nccar
+        local LD=nccld
+        BASE_FLAGS+=" -fPIC"
+        ;;
     undefined)
         # http://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html
         BASE_FLAGS+=" -fsanitize=undefined"
@@ -43,30 +65,42 @@
         ;;
     *)
         echo "Usage:" 1>&2
-        echo "  ${0##*/} (address|coverage|memory|undefined)" 1>&2
+        echo "  ${0##*/} (address|coverage|memory|ncc|undefined)" 1>&2
         exit 1
         ;;
     esac
 
-    CFLAGS="-std=c89 ${BASE_FLAGS}"
-    CXXFLAGS="-std=c++98 ${BASE_FLAGS}"
+    local CFLAGS="-std=c89 ${BASE_FLAGS} ${CFLAGS:-}"
+    local CXXFLAGS="-std=c++98 ${BASE_FLAGS} ${CXXFLAGS:-}"
 
     (
-        PS4='# '
         set -e
-        set -x
 
-        CC="${CC}" CFLAGS="${CFLAGS}" \
-            CXX="${CXX}" CXXFLAGS="${CXXFLAGS}" \
-            ./configure
+        RUN CC="${CC}" CFLAGS="${CFLAGS}" \
+                CXX="${CXX}" CXXFLAGS="${CXXFLAGS}" \
+                AR="${AR}" \
+                LD="${LD}" \
+                ./configure "$@"
 
-        "${MAKE}" clean all
-        "${MAKE}" check run-xmltest
+        RUN "${MAKE}" clean all
+
+        case "${mode}" in
+        ncc)
+            ;;
+        *)
+            RUN "${MAKE}" check run-xmltest
+            ;;
+        esac
     ) || exit 1
 
-    if [[ "${mode}" = coverage ]]; then
+    case "${mode}" in
+    coverage)
         find -name '*.gcda' | sort | xargs gcov
-    fi
+        ;;
+    ncc)
+        RUN nccnav ./.libs/libexpat.a.nccout
+        ;;
+    esac
 }
 
 main "$@"