unilbrk/u*-possible-linebreaks: Fix undefined behaviour.
authorBruno Haible <bruno@clisp.org>
Fri, 8 Mar 2019 23:27:19 +0000 (00:27 +0100)
committerBruno Haible <bruno@clisp.org>
Fri, 8 Mar 2019 23:27:19 +0000 (00:27 +0100)
Reported by Jeffrey Walton <noloader@gmail.com>.

* lib/unilbrk/u8-possible-linebreaks.c (u8_possible_linebreaks): Don't
invoke memset with a zero size.
* lib/unilbrk/u16-possible-linebreaks.c (u16_possible_linebreaks):
Likewise.
* lib/unilbrk/u32-possible-linebreaks.c (u32_possible_linebreaks):
Adjust accordingly.

ChangeLog
lib/unilbrk/u16-possible-linebreaks.c
lib/unilbrk/u32-possible-linebreaks.c
lib/unilbrk/u8-possible-linebreaks.c

index 687357e6147fd79977a0e596a34ee2168cd3a3d5..a67ed92ce863761d81a830bc7330fb4eb7dfb864 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2019-03-08  Bruno Haible  <bruno@clisp.org>
+
+       unilbrk/u*-possible-linebreaks: Fix undefined behaviour.
+       Reported by Jeffrey Walton <noloader@gmail.com>.
+       * lib/unilbrk/u8-possible-linebreaks.c (u8_possible_linebreaks): Don't
+       invoke memset with a zero size.
+       * lib/unilbrk/u16-possible-linebreaks.c (u16_possible_linebreaks):
+       Likewise.
+       * lib/unilbrk/u32-possible-linebreaks.c (u32_possible_linebreaks):
+       Adjust accordingly.
+
 2019-03-08  Bruno Haible  <bruno@clisp.org>
 
        unistr/*, uniconv/*: Fix undefined behaviour.
index 3ee91ad02f8a021f12f7bb26c6f4a22c6566e71d..0a913683abcc41e71f531e168d76b14f56961deb 100644 (file)
 void
 u16_possible_linebreaks (const uint16_t *s, size_t n, const char *encoding, char *p)
 {
-  int LBP_AI_REPLACEMENT = (is_cjk_encoding (encoding) ? LBP_ID : LBP_AL);
-  const uint16_t *s_end = s + n;
-  int last_prop = LBP_BK; /* line break property of last non-space character */
-  char *seen_space = NULL; /* Was a space seen after the last non-space character? */
-  char *seen_space2 = NULL; /* At least two spaces after the last non-space? */
-
-  /* Don't break inside multibyte characters.  */
-  memset (p, UC_BREAK_PROHIBITED, n);
-
-  while (s < s_end)
+  if (n > 0)
     {
-      ucs4_t uc;
-      int count = u16_mbtouc_unsafe (&uc, s, s_end - s);
-      int prop = unilbrkprop_lookup (uc);
+      int LBP_AI_REPLACEMENT = (is_cjk_encoding (encoding) ? LBP_ID : LBP_AL);
+      const uint16_t *s_end = s + n;
+      int last_prop = LBP_BK; /* line break property of last non-space character */
+      char *seen_space = NULL; /* Was a space seen after the last non-space character? */
+      char *seen_space2 = NULL; /* At least two spaces after the last non-space? */
 
-      if (prop == LBP_BK)
-        {
-          /* Mandatory break.  */
-          *p = UC_BREAK_MANDATORY;
-          last_prop = LBP_BK;
-          seen_space = NULL;
-          seen_space2 = NULL;
-        }
-      else
-        {
-          char *q;
+      /* Don't break inside multibyte characters.  */
+      memset (p, UC_BREAK_PROHIBITED, n);
 
-          /* Resolve property values whose behaviour is not fixed.  */
-          switch (prop)
-            {
-            case LBP_AI:
-              /* Resolve ambiguous.  */
-              prop = LBP_AI_REPLACEMENT;
-              break;
-            case LBP_CB:
-              /* This is arbitrary.  */
-              prop = LBP_ID;
-              break;
-            case LBP_SA:
-              /* We don't handle complex scripts yet.
-                 Treat LBP_SA like LBP_XX.  */
-            case LBP_XX:
-              /* This is arbitrary.  */
-              prop = LBP_AL;
-              break;
-            }
+      do
+        {
+          ucs4_t uc;
+          int count = u16_mbtouc_unsafe (&uc, s, s_end - s);
+          int prop = unilbrkprop_lookup (uc);
 
-          /* Deal with spaces and combining characters.  */
-          q = p;
-          if (prop == LBP_SP)
-            {
-              /* Don't break just before a space.  */
-              *p = UC_BREAK_PROHIBITED;
-              seen_space2 = seen_space;
-              seen_space = p;
-            }
-          else if (prop == LBP_ZW)
+          if (prop == LBP_BK)
             {
-              /* Don't break just before a zero-width space.  */
-              *p = UC_BREAK_PROHIBITED;
-              last_prop = LBP_ZW;
+              /* Mandatory break.  */
+              *p = UC_BREAK_MANDATORY;
+              last_prop = LBP_BK;
               seen_space = NULL;
               seen_space2 = NULL;
             }
-          else if (prop == LBP_CM)
+          else
             {
-              /* Don't break just before a combining character, except immediately after a
-                 zero-width space.  */
-              if (last_prop == LBP_ZW)
+              char *q;
+
+              /* Resolve property values whose behaviour is not fixed.  */
+              switch (prop)
                 {
-                  /* Break after zero-width space.  */
-                  *p = UC_BREAK_POSSIBLE;
-                  /* A combining character turns a preceding space into LBP_ID.  */
-                  last_prop = LBP_ID;
+                case LBP_AI:
+                  /* Resolve ambiguous.  */
+                  prop = LBP_AI_REPLACEMENT;
+                  break;
+                case LBP_CB:
+                  /* This is arbitrary.  */
+                  prop = LBP_ID;
+                  break;
+                case LBP_SA:
+                  /* We don't handle complex scripts yet.
+                     Treat LBP_SA like LBP_XX.  */
+                case LBP_XX:
+                  /* This is arbitrary.  */
+                  prop = LBP_AL;
+                  break;
                 }
-              else
+
+              /* Deal with spaces and combining characters.  */
+              q = p;
+              if (prop == LBP_SP)
                 {
+                  /* Don't break just before a space.  */
                   *p = UC_BREAK_PROHIBITED;
-                  /* A combining character turns a preceding space into LBP_ID.  */
-                  if (seen_space != NULL)
-                    {
-                      q = seen_space;
-                      seen_space = seen_space2;
-                      prop = LBP_ID;
-                      goto lookup_via_table;
-                    }
+                  seen_space2 = seen_space;
+                  seen_space = p;
                 }
-            }
-          else
-            {
-             lookup_via_table:
-              /* prop must be usable as an index for table 7.3 of UTR #14.  */
-              if (!(prop >= 0 && prop < sizeof (unilbrk_table) / sizeof (unilbrk_table[0])))
-                abort ();
-
-              if (last_prop == LBP_BK)
+              else if (prop == LBP_ZW)
                 {
-                  /* Don't break at the beginning of a line.  */
-                  *q = UC_BREAK_PROHIBITED;
+                  /* Don't break just before a zero-width space.  */
+                  *p = UC_BREAK_PROHIBITED;
+                  last_prop = LBP_ZW;
+                  seen_space = NULL;
+                  seen_space2 = NULL;
                 }
-              else if (last_prop == LBP_ZW)
+              else if (prop == LBP_CM)
                 {
-                  /* Break after zero-width space.  */
-                  *q = UC_BREAK_POSSIBLE;
+                  /* Don't break just before a combining character, except immediately
+                     after a zero-width space.  */
+                  if (last_prop == LBP_ZW)
+                    {
+                      /* Break after zero-width space.  */
+                      *p = UC_BREAK_POSSIBLE;
+                      /* A combining character turns a preceding space into LBP_ID.  */
+                      last_prop = LBP_ID;
+                    }
+                  else
+                    {
+                      *p = UC_BREAK_PROHIBITED;
+                      /* A combining character turns a preceding space into LBP_ID.  */
+                      if (seen_space != NULL)
+                        {
+                          q = seen_space;
+                          seen_space = seen_space2;
+                          prop = LBP_ID;
+                          goto lookup_via_table;
+                        }
+                    }
                 }
               else
                 {
-                  switch (unilbrk_table [last_prop] [prop])
+                 lookup_via_table:
+                  /* prop must be usable as an index for table 7.3 of UTR #14.  */
+                  if (!(prop >= 0 && prop < sizeof (unilbrk_table) / sizeof (unilbrk_table[0])))
+                    abort ();
+
+                  if (last_prop == LBP_BK)
                     {
-                    case D:
-                      *q = UC_BREAK_POSSIBLE;
-                      break;
-                    case I:
-                      *q = (seen_space != NULL ? UC_BREAK_POSSIBLE : UC_BREAK_PROHIBITED);
-                      break;
-                    case P:
+                      /* Don't break at the beginning of a line.  */
                       *q = UC_BREAK_PROHIBITED;
-                      break;
-                    default:
-                      abort ();
                     }
+                  else if (last_prop == LBP_ZW)
+                    {
+                      /* Break after zero-width space.  */
+                      *q = UC_BREAK_POSSIBLE;
+                    }
+                  else
+                    {
+                      switch (unilbrk_table [last_prop] [prop])
+                        {
+                        case D:
+                          *q = UC_BREAK_POSSIBLE;
+                          break;
+                        case I:
+                          *q = (seen_space != NULL ? UC_BREAK_POSSIBLE : UC_BREAK_PROHIBITED);
+                          break;
+                        case P:
+                          *q = UC_BREAK_PROHIBITED;
+                          break;
+                        default:
+                          abort ();
+                        }
+                    }
+                  last_prop = prop;
+                  seen_space = NULL;
+                  seen_space2 = NULL;
                 }
-              last_prop = prop;
-              seen_space = NULL;
-              seen_space2 = NULL;
             }
-        }
 
-      s += count;
-      p += count;
+          s += count;
+          p += count;
+        }
+      while (s < s_end);
     }
 }
index cdd00c8989b58e27b548ff184b0ef849130f674a..9a0bafa920d8b66bf994233e068b3d8c9071b9e7 100644 (file)
 void
 u32_possible_linebreaks (const uint32_t *s, size_t n, const char *encoding, char *p)
 {
-  int LBP_AI_REPLACEMENT = (is_cjk_encoding (encoding) ? LBP_ID : LBP_AL);
-  const uint32_t *s_end = s + n;
-  int last_prop = LBP_BK; /* line break property of last non-space character */
-  char *seen_space = NULL; /* Was a space seen after the last non-space character? */
-  char *seen_space2 = NULL; /* At least two spaces after the last non-space? */
-
-  while (s < s_end)
+  if (n > 0)
     {
-      ucs4_t uc = *s;
-      int prop = unilbrkprop_lookup (uc);
+      int LBP_AI_REPLACEMENT = (is_cjk_encoding (encoding) ? LBP_ID : LBP_AL);
+      const uint32_t *s_end = s + n;
+      int last_prop = LBP_BK; /* line break property of last non-space character */
+      char *seen_space = NULL; /* Was a space seen after the last non-space character? */
+      char *seen_space2 = NULL; /* At least two spaces after the last non-space? */
 
-      if (prop == LBP_BK)
-        {
-          /* Mandatory break.  */
-          *p = UC_BREAK_MANDATORY;
-          last_prop = LBP_BK;
-          seen_space = NULL;
-          seen_space2 = NULL;
-        }
-      else
+      do
         {
-          char *q;
+          ucs4_t uc = *s;
+          int prop = unilbrkprop_lookup (uc);
 
-          /* Resolve property values whose behaviour is not fixed.  */
-          switch (prop)
+          if (prop == LBP_BK)
             {
-            case LBP_AI:
-              /* Resolve ambiguous.  */
-              prop = LBP_AI_REPLACEMENT;
-              break;
-            case LBP_CB:
-              /* This is arbitrary.  */
-              prop = LBP_ID;
-              break;
-            case LBP_SA:
-              /* We don't handle complex scripts yet.
-                 Treat LBP_SA like LBP_XX.  */
-            case LBP_XX:
-              /* This is arbitrary.  */
-              prop = LBP_AL;
-              break;
-            }
-
-          /* Deal with spaces and combining characters.  */
-          q = p;
-          if (prop == LBP_SP)
-            {
-              /* Don't break just before a space.  */
-              *p = UC_BREAK_PROHIBITED;
-              seen_space2 = seen_space;
-              seen_space = p;
-            }
-          else if (prop == LBP_ZW)
-            {
-              /* Don't break just before a zero-width space.  */
-              *p = UC_BREAK_PROHIBITED;
-              last_prop = LBP_ZW;
+              /* Mandatory break.  */
+              *p = UC_BREAK_MANDATORY;
+              last_prop = LBP_BK;
               seen_space = NULL;
               seen_space2 = NULL;
             }
-          else if (prop == LBP_CM)
+          else
             {
-              /* Don't break just before a combining character, except immediately after a
-                 zero-width space.  */
-              if (last_prop == LBP_ZW)
+              char *q;
+
+              /* Resolve property values whose behaviour is not fixed.  */
+              switch (prop)
                 {
-                  /* Break after zero-width space.  */
-                  *p = UC_BREAK_POSSIBLE;
-                  /* A combining character turns a preceding space into LBP_ID.  */
-                  last_prop = LBP_ID;
+                case LBP_AI:
+                  /* Resolve ambiguous.  */
+                  prop = LBP_AI_REPLACEMENT;
+                  break;
+                case LBP_CB:
+                  /* This is arbitrary.  */
+                  prop = LBP_ID;
+                  break;
+                case LBP_SA:
+                  /* We don't handle complex scripts yet.
+                     Treat LBP_SA like LBP_XX.  */
+                case LBP_XX:
+                  /* This is arbitrary.  */
+                  prop = LBP_AL;
+                  break;
                 }
-              else
+
+              /* Deal with spaces and combining characters.  */
+              q = p;
+              if (prop == LBP_SP)
                 {
+                  /* Don't break just before a space.  */
                   *p = UC_BREAK_PROHIBITED;
-                  /* A combining character turns a preceding space into LBP_ID.  */
-                  if (seen_space != NULL)
-                    {
-                      q = seen_space;
-                      seen_space = seen_space2;
-                      prop = LBP_ID;
-                      goto lookup_via_table;
-                    }
+                  seen_space2 = seen_space;
+                  seen_space = p;
                 }
-            }
-          else
-            {
-             lookup_via_table:
-              /* prop must be usable as an index for table 7.3 of UTR #14.  */
-              if (!(prop >= 0 && prop < sizeof (unilbrk_table) / sizeof (unilbrk_table[0])))
-                abort ();
-
-              if (last_prop == LBP_BK)
+              else if (prop == LBP_ZW)
                 {
-                  /* Don't break at the beginning of a line.  */
-                  *q = UC_BREAK_PROHIBITED;
+                  /* Don't break just before a zero-width space.  */
+                  *p = UC_BREAK_PROHIBITED;
+                  last_prop = LBP_ZW;
+                  seen_space = NULL;
+                  seen_space2 = NULL;
                 }
-              else if (last_prop == LBP_ZW)
+              else if (prop == LBP_CM)
                 {
-                  /* Break after zero-width space.  */
-                  *q = UC_BREAK_POSSIBLE;
+                  /* Don't break just before a combining character, except immediately
+                     after a zero-width space.  */
+                  if (last_prop == LBP_ZW)
+                    {
+                      /* Break after zero-width space.  */
+                      *p = UC_BREAK_POSSIBLE;
+                      /* A combining character turns a preceding space into LBP_ID.  */
+                      last_prop = LBP_ID;
+                    }
+                  else
+                    {
+                      *p = UC_BREAK_PROHIBITED;
+                      /* A combining character turns a preceding space into LBP_ID.  */
+                      if (seen_space != NULL)
+                        {
+                          q = seen_space;
+                          seen_space = seen_space2;
+                          prop = LBP_ID;
+                          goto lookup_via_table;
+                        }
+                    }
                 }
               else
                 {
-                  switch (unilbrk_table [last_prop] [prop])
+                 lookup_via_table:
+                  /* prop must be usable as an index for table 7.3 of UTR #14.  */
+                  if (!(prop >= 0 && prop < sizeof (unilbrk_table) / sizeof (unilbrk_table[0])))
+                    abort ();
+
+                  if (last_prop == LBP_BK)
                     {
-                    case D:
-                      *q = UC_BREAK_POSSIBLE;
-                      break;
-                    case I:
-                      *q = (seen_space != NULL ? UC_BREAK_POSSIBLE : UC_BREAK_PROHIBITED);
-                      break;
-                    case P:
+                      /* Don't break at the beginning of a line.  */
                       *q = UC_BREAK_PROHIBITED;
-                      break;
-                    default:
-                      abort ();
                     }
+                  else if (last_prop == LBP_ZW)
+                    {
+                      /* Break after zero-width space.  */
+                      *q = UC_BREAK_POSSIBLE;
+                    }
+                  else
+                    {
+                      switch (unilbrk_table [last_prop] [prop])
+                        {
+                        case D:
+                          *q = UC_BREAK_POSSIBLE;
+                          break;
+                        case I:
+                          *q = (seen_space != NULL ? UC_BREAK_POSSIBLE : UC_BREAK_PROHIBITED);
+                          break;
+                        case P:
+                          *q = UC_BREAK_PROHIBITED;
+                          break;
+                        default:
+                          abort ();
+                        }
+                    }
+                  last_prop = prop;
+                  seen_space = NULL;
+                  seen_space2 = NULL;
                 }
-              last_prop = prop;
-              seen_space = NULL;
-              seen_space2 = NULL;
             }
-        }
 
-      s++;
-      p++;
+          s++;
+          p++;
+        }
+      while (s < s_end);
     }
 }
index 1e2224bf7f5785ebac9db5ad6f1bea2ff753e0d0..79efcb23fd72fbe24adf2deba2fa7199f60427a5 100644 (file)
 void
 u8_possible_linebreaks (const uint8_t *s, size_t n, const char *encoding, char *p)
 {
-  int LBP_AI_REPLACEMENT = (is_cjk_encoding (encoding) ? LBP_ID : LBP_AL);
-  const uint8_t *s_end = s + n;
-  int last_prop = LBP_BK; /* line break property of last non-space character */
-  char *seen_space = NULL; /* Was a space seen after the last non-space character? */
-  char *seen_space2 = NULL; /* At least two spaces after the last non-space? */
-
-  /* Don't break inside multibyte characters.  */
-  memset (p, UC_BREAK_PROHIBITED, n);
-
-  while (s < s_end)
+  if (n > 0)
     {
-      ucs4_t uc;
-      int count = u8_mbtouc_unsafe (&uc, s, s_end - s);
-      int prop = unilbrkprop_lookup (uc);
+      int LBP_AI_REPLACEMENT = (is_cjk_encoding (encoding) ? LBP_ID : LBP_AL);
+      const uint8_t *s_end = s + n;
+      int last_prop = LBP_BK; /* line break property of last non-space character */
+      char *seen_space = NULL; /* Was a space seen after the last non-space character? */
+      char *seen_space2 = NULL; /* At least two spaces after the last non-space? */
 
-      if (prop == LBP_BK)
-        {
-          /* Mandatory break.  */
-          *p = UC_BREAK_MANDATORY;
-          last_prop = LBP_BK;
-          seen_space = NULL;
-          seen_space2 = NULL;
-        }
-      else
-        {
-          char *q;
+      /* Don't break inside multibyte characters.  */
+      memset (p, UC_BREAK_PROHIBITED, n);
 
-          /* Resolve property values whose behaviour is not fixed.  */
-          switch (prop)
-            {
-            case LBP_AI:
-              /* Resolve ambiguous.  */
-              prop = LBP_AI_REPLACEMENT;
-              break;
-            case LBP_CB:
-              /* This is arbitrary.  */
-              prop = LBP_ID;
-              break;
-            case LBP_SA:
-              /* We don't handle complex scripts yet.
-                 Treat LBP_SA like LBP_XX.  */
-            case LBP_XX:
-              /* This is arbitrary.  */
-              prop = LBP_AL;
-              break;
-            }
+      do
+        {
+          ucs4_t uc;
+          int count = u8_mbtouc_unsafe (&uc, s, s_end - s);
+          int prop = unilbrkprop_lookup (uc);
 
-          /* Deal with spaces and combining characters.  */
-          q = p;
-          if (prop == LBP_SP)
+          if (prop == LBP_BK)
             {
-              /* Don't break just before a space.  */
-              *p = UC_BREAK_PROHIBITED;
-              seen_space2 = seen_space;
-              seen_space = p;
-            }
-          else if (prop == LBP_ZW)
-            {
-              /* Don't break just before a zero-width space.  */
-              *p = UC_BREAK_PROHIBITED;
-              last_prop = LBP_ZW;
+              /* Mandatory break.  */
+              *p = UC_BREAK_MANDATORY;
+              last_prop = LBP_BK;
               seen_space = NULL;
               seen_space2 = NULL;
             }
-          else if (prop == LBP_CM)
+          else
             {
-              /* Don't break just before a combining character, except immediately after a
-                 zero-width space.  */
-              if (last_prop == LBP_ZW)
+              char *q;
+
+              /* Resolve property values whose behaviour is not fixed.  */
+              switch (prop)
                 {
-                  /* Break after zero-width space.  */
-                  *p = UC_BREAK_POSSIBLE;
-                  /* A combining character turns a preceding space into LBP_ID.  */
-                  last_prop = LBP_ID;
+                case LBP_AI:
+                  /* Resolve ambiguous.  */
+                  prop = LBP_AI_REPLACEMENT;
+                  break;
+                case LBP_CB:
+                  /* This is arbitrary.  */
+                  prop = LBP_ID;
+                  break;
+                case LBP_SA:
+                  /* We don't handle complex scripts yet.
+                     Treat LBP_SA like LBP_XX.  */
+                case LBP_XX:
+                  /* This is arbitrary.  */
+                  prop = LBP_AL;
+                  break;
                 }
-              else
+
+              /* Deal with spaces and combining characters.  */
+              q = p;
+              if (prop == LBP_SP)
                 {
+                  /* Don't break just before a space.  */
                   *p = UC_BREAK_PROHIBITED;
-                  /* A combining character turns a preceding space into LBP_ID.  */
-                  if (seen_space != NULL)
-                    {
-                      q = seen_space;
-                      seen_space = seen_space2;
-                      prop = LBP_ID;
-                      goto lookup_via_table;
-                    }
+                  seen_space2 = seen_space;
+                  seen_space = p;
                 }
-            }
-          else
-            {
-             lookup_via_table:
-              /* prop must be usable as an index for table 7.3 of UTR #14.  */
-              if (!(prop >= 0 && prop < sizeof (unilbrk_table) / sizeof (unilbrk_table[0])))
-                abort ();
-
-              if (last_prop == LBP_BK)
+              else if (prop == LBP_ZW)
                 {
-                  /* Don't break at the beginning of a line.  */
-                  *q = UC_BREAK_PROHIBITED;
+                  /* Don't break just before a zero-width space.  */
+                  *p = UC_BREAK_PROHIBITED;
+                  last_prop = LBP_ZW;
+                  seen_space = NULL;
+                  seen_space2 = NULL;
                 }
-              else if (last_prop == LBP_ZW)
+              else if (prop == LBP_CM)
                 {
-                  /* Break after zero-width space.  */
-                  *q = UC_BREAK_POSSIBLE;
+                  /* Don't break just before a combining character, except immediately
+                     after a zero-width space.  */
+                  if (last_prop == LBP_ZW)
+                    {
+                      /* Break after zero-width space.  */
+                      *p = UC_BREAK_POSSIBLE;
+                      /* A combining character turns a preceding space into LBP_ID.  */
+                      last_prop = LBP_ID;
+                    }
+                  else
+                    {
+                      *p = UC_BREAK_PROHIBITED;
+                      /* A combining character turns a preceding space into LBP_ID.  */
+                      if (seen_space != NULL)
+                        {
+                          q = seen_space;
+                          seen_space = seen_space2;
+                          prop = LBP_ID;
+                          goto lookup_via_table;
+                        }
+                    }
                 }
               else
                 {
-                  switch (unilbrk_table [last_prop] [prop])
+                 lookup_via_table:
+                  /* prop must be usable as an index for table 7.3 of UTR #14.  */
+                  if (!(prop >= 0 && prop < sizeof (unilbrk_table) / sizeof (unilbrk_table[0])))
+                    abort ();
+
+                  if (last_prop == LBP_BK)
                     {
-                    case D:
-                      *q = UC_BREAK_POSSIBLE;
-                      break;
-                    case I:
-                      *q = (seen_space != NULL ? UC_BREAK_POSSIBLE : UC_BREAK_PROHIBITED);
-                      break;
-                    case P:
+                      /* Don't break at the beginning of a line.  */
                       *q = UC_BREAK_PROHIBITED;
-                      break;
-                    default:
-                      abort ();
                     }
+                  else if (last_prop == LBP_ZW)
+                    {
+                      /* Break after zero-width space.  */
+                      *q = UC_BREAK_POSSIBLE;
+                    }
+                  else
+                    {
+                      switch (unilbrk_table [last_prop] [prop])
+                        {
+                        case D:
+                          *q = UC_BREAK_POSSIBLE;
+                          break;
+                        case I:
+                          *q = (seen_space != NULL ? UC_BREAK_POSSIBLE : UC_BREAK_PROHIBITED);
+                          break;
+                        case P:
+                          *q = UC_BREAK_PROHIBITED;
+                          break;
+                        default:
+                          abort ();
+                        }
+                    }
+                  last_prop = prop;
+                  seen_space = NULL;
+                  seen_space2 = NULL;
                 }
-              last_prop = prop;
-              seen_space = NULL;
-              seen_space2 = NULL;
             }
-        }
 
-      s += count;
-      p += count;
+          s += count;
+          p += count;
+        }
+      while (s < s_end);
     }
 }