emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Emacs-diffs] master 1a722e8 2/2: Fix translation-region bug with MAX_CH


From: Paul Eggert
Subject: [Emacs-diffs] master 1a722e8 2/2: Fix translation-region bug with MAX_CHAR
Date: Sun, 13 Jan 2019 18:46:50 -0500 (EST)

branch: master
commit 1a722e888454a0cb24dffc35455467688b4b4c60
Author: Paul Eggert <address@hidden>
Commit: Paul Eggert <address@hidden>

    Fix translation-region bug with MAX_CHAR
    
    Also, clean up the code a bit.
    Actually I discovered the bug while cleaning up the code.
    * src/editfns.c (Fsubst_char_in_region)
    (Ftranslate_region_internal): Use bool for booleans.
    (Ftranslate_region_internal): Fix off-by-1 bug when a
    translation table translates the maximum char.  Assume C99
    decl-after-statement, similar minor cleanups.
    * test/src/editfns-tests.el (test-translate-region-internal):
    New test.
---
 src/editfns.c             | 82 ++++++++++++++++++++---------------------------
 test/src/editfns-tests.el |  9 ++++++
 2 files changed, 44 insertions(+), 47 deletions(-)

diff --git a/src/editfns.c b/src/editfns.c
index c6ad4c0..5512701 100644
--- a/src/editfns.c
+++ b/src/editfns.c
@@ -2322,7 +2322,7 @@ Both characters must have the same length of multi-byte 
form.  */)
              /* replace_range is less efficient, because it moves the gap,
                 but it handles combining correctly.  */
              replace_range (pos, pos + 1, string,
-                            0, 0, 1, 0);
+                            false, false, true, false);
              pos_byte_next = CHAR_TO_BYTE (pos);
              if (pos_byte_next > pos_byte)
                /* Before combining happened.  We should not increment
@@ -2433,60 +2433,53 @@ From START to END, translate characters according to 
TABLE.
 TABLE is a string or a char-table; the Nth character in it is the
 mapping for the character with code N.
 It returns the number of characters changed.  */)
-  (Lisp_Object start, Lisp_Object end, register Lisp_Object table)
+  (Lisp_Object start, Lisp_Object end, Lisp_Object table)
 {
-  register unsigned char *tt;  /* Trans table. */
-  register int nc;             /* New character. */
-  ptrdiff_t cnt;               /* Number of changes made. */
-  ptrdiff_t size;              /* Size of translate table. */
-  ptrdiff_t pos, pos_byte, end_pos;
+  int translatable_chars = MAX_CHAR + 1;
   bool multibyte = !NILP (BVAR (current_buffer, enable_multibyte_characters));
   bool string_multibyte UNINIT;
 
   validate_region (&start, &end);
-  if (CHAR_TABLE_P (table))
+  if (STRINGP (table))
     {
-      if (! EQ (XCHAR_TABLE (table)->purpose, Qtranslation_table))
-       error ("Not a translation table");
-      size = MAX_CHAR;
-      tt = NULL;
-    }
-  else
-    {
-      CHECK_STRING (table);
-
-      if (! multibyte && (SCHARS (table) < SBYTES (table)))
+      if (! multibyte)
        table = string_make_unibyte (table);
-      string_multibyte = SCHARS (table) < SBYTES (table);
-      size = SBYTES (table);
-      tt = SDATA (table);
+      translatable_chars = min (translatable_chars, SBYTES (table));
+      string_multibyte = STRING_MULTIBYTE (table);
     }
+  else if (! (CHAR_TABLE_P (table)
+             && EQ (XCHAR_TABLE (table)->purpose, Qtranslation_table)))
+    error ("Not a translation table");
 
-  pos = XFIXNUM (start);
-  pos_byte = CHAR_TO_BYTE (pos);
-  end_pos = XFIXNUM (end);
+  ptrdiff_t pos = XFIXNUM (start);
+  ptrdiff_t pos_byte = CHAR_TO_BYTE (pos);
+  ptrdiff_t end_pos = XFIXNUM (end);
   modify_text (pos, end_pos);
 
-  cnt = 0;
-  for (; pos < end_pos; )
+  ptrdiff_t characters_changed = 0;
+
+  while (pos < end_pos)
     {
       unsigned char *p = BYTE_POS_ADDR (pos_byte);
       unsigned char *str UNINIT;
       unsigned char buf[MAX_MULTIBYTE_LENGTH];
-      int len, str_len;
-      int oc;
-      Lisp_Object val;
+      int len, oc;
 
       if (multibyte)
        oc = STRING_CHAR_AND_LENGTH (p, len);
       else
        oc = *p, len = 1;
-      if (oc < size)
+      if (oc < translatable_chars)
        {
-         if (tt)
+         int nc; /* New character.  */
+         int str_len;
+         Lisp_Object val;
+
+         if (STRINGP (table))
            {
              /* Reload as signal_after_change in last iteration may GC.  */
-             tt = SDATA (table);
+             unsigned char *tt = SDATA (table);
+
              if (string_multibyte)
                {
                  str = tt + string_char_to_byte (table, oc);
@@ -2535,7 +2528,8 @@ It returns the number of characters changed.  */)
                  /* This is less efficient, because it moves the gap,
                     but it should handle multibyte characters correctly.  */
                  string = make_multibyte_string ((char *) str, 1, str_len);
-                 replace_range (pos, pos + 1, string, 1, 0, 1, 0);
+                 replace_range (pos, pos + 1, string,
+                                true, false, true, false);
                  len = str_len;
                }
              else
@@ -2546,12 +2540,10 @@ It returns the number of characters changed.  */)
                  signal_after_change (pos, 1, 1);
                  update_compositions (pos, pos + 1, CHECK_BORDER);
                }
-             ++cnt;
+             characters_changed++;
            }
          else if (nc < 0)
            {
-             Lisp_Object string;
-
              if (CONSP (val))
                {
                  val = check_translation (pos, pos_byte, end_pos, val);
@@ -2568,18 +2560,14 @@ It returns the number of characters changed.  */)
              else
                len = 1;
 
-             if (VECTORP (val))
-               {
-                 string = Fconcat (1, &val);
-               }
-             else
-               {
-                 string = Fmake_string (make_fixnum (1), val, Qnil);
-               }
-             replace_range (pos, pos + len, string, 1, 0, 1, 0);
+             Lisp_Object string
+               = (VECTORP (val)
+                  ? Fconcat (1, &val)
+                  : Fmake_string (make_fixnum (1), val, Qnil));
+             replace_range (pos, pos + len, string, true, false, true, false);
              pos_byte += SBYTES (string);
              pos += SCHARS (string);
-             cnt += SCHARS (string);
+             characters_changed += SCHARS (string);
              end_pos += SCHARS (string) - len;
              continue;
            }
@@ -2588,7 +2576,7 @@ It returns the number of characters changed.  */)
       pos++;
     }
 
-  return make_fixnum (cnt);
+  return make_fixnum (characters_changed);
 }
 
 DEFUN ("delete-region", Fdelete_region, Sdelete_region, 2, 2, "r",
diff --git a/test/src/editfns-tests.el b/test/src/editfns-tests.el
index d3b0a11..a01dc4a 100644
--- a/test/src/editfns-tests.el
+++ b/test/src/editfns-tests.el
@@ -373,4 +373,13 @@
               ((eq stat 2)
                (should-not name)))))))))
 
+(ert-deftest test-translate-region-internal ()
+  (with-temp-buffer
+    (let ((max-char #16r3FFFFF)
+          (tt (make-char-table 'translation-table)))
+      (aset tt max-char ?*)
+      (insert max-char)
+      (translate-region-internal (point-min) (point-max) tt)
+      (should (string-equal (buffer-string) "*")))))
+
 ;;; editfns-tests.el ends here



reply via email to

[Prev in Thread] Current Thread [Next in Thread]