changeset 17031:c612a2cdd83b

Include charset.h. (Fchar_to_string, Fstring_to_char): Handle multibyte characters. (Fsref): New function. (Fgoto_char): Force point to be at a character boundary. (Ffollowing_char, Fpreceding_char): Handle multibyte characters. (Fchar_after): Handle multibyte characters. (Fchar_before): New function. (general_insert_function): New function. (Finsert, Finsert_and_inherit, Finsert_before_markers): Use it. (Finsert_char): Doc-string refer to markers of before-insertion-type. Handle multibyte characters. (Fsubst_char_in_region): Handle multibyte characters. (Fchar_equal): Don't consider `case' of multibyte characters. (syms_of_editfns): Handle the new function `char-before'.
author Karl Heuer <kwzh@gnu.org>
date Thu, 20 Feb 1997 06:48:37 +0000
parents 42d758739319
children 814257735e7d
files src/editfns.c
diffstat 1 files changed, 232 insertions(+), 132 deletions(-) [+]
line wrap: on
line diff
--- a/src/editfns.c	Thu Feb 20 06:48:14 1997 +0000
+++ b/src/editfns.c	Thu Feb 20 06:48:37 1997 +0000
@@ -32,6 +32,7 @@
 #include "lisp.h"
 #include "intervals.h"
 #include "buffer.h"
+#include "charset.h"
 #include "window.h"
 
 #include "systime.h"
@@ -114,33 +115,61 @@
 }
 
 DEFUN ("char-to-string", Fchar_to_string, Schar_to_string, 1, 1, 0,
-  "Convert arg CHARACTER to a one-character string containing that character.")
+  "Convert arg CHAR to a string containing multi-byte form of that character.")
   (character)
      Lisp_Object character;
 {
-  char c;
+  int len;
+  char workbuf[4], *str;
+
   CHECK_NUMBER (character, 0);
 
-  c = XINT (character);
-  return make_string (&c, 1);
+  len = CHAR_STRING (XFASTINT (character), workbuf, str);
+  return make_string (str, len);
 }
 
 DEFUN ("string-to-char", Fstring_to_char, Sstring_to_char, 1, 1, 0,
-  "Convert arg STRING to a character, the first character of that string.")
+  "Convert arg STRING to a character, the first character of that string.\n\
+A multibyte character is handled correctly.")
   (string)
      register Lisp_Object string;
 {
   register Lisp_Object val;
   register struct Lisp_String *p;
   CHECK_STRING (string, 0);
-
   p = XSTRING (string);
   if (p->size)
-    XSETFASTINT (val, ((unsigned char *) p->data)[0]);
+    XSETFASTINT (val, STRING_CHAR (p->data, p->size));
   else
     XSETFASTINT (val, 0);
   return val;
 }
+
+DEFUN ("sref", Fsref, Ssref, 2, 2, 0,
+  "Return the character in STRING at INDEX.  INDEX starts at 0.\n\
+A multibyte character is handled correctly.\n\
+INDEX not pointing at character boundary is an error.")
+  (str, idx)
+     Lisp_Object str, idx;
+{
+  register int idxval, len;
+  register unsigned char *p;
+  register Lisp_Object val;
+
+  CHECK_STRING (str, 0);
+  CHECK_NUMBER (idx, 1);
+  idxval = XINT (idx);
+  if (idxval < 0 || idxval >= (len = XVECTOR (str)->size))
+    args_out_of_range (str, idx);
+  p = XSTRING (str)->data + idxval;
+  if (!CHAR_HEAD_P (p))
+    error ("Not character boundary");
+
+  len = XSTRING (str)->size - idxval;
+  XSETFASTINT (val, STRING_CHAR (p, len));
+  return val;
+}
+
 
 static Lisp_Object
 buildmark (val)
@@ -183,13 +212,41 @@
 
 DEFUN ("goto-char", Fgoto_char, Sgoto_char, 1, 1, "NGoto char: ",
   "Set point to POSITION, a number or marker.\n\
-Beginning of buffer is position (point-min), end is (point-max).")
+Beginning of buffer is position (point-min), end is (point-max).\n\
+If the position is in the middle of a multibyte form,\n\
+the actual point is set at the head of the multibyte form\n\
+except in the case that `enable-multibyte-characters' is nil.")
   (position)
      register Lisp_Object position;
 {
+  int pos;
+  unsigned char *p;
+
   CHECK_NUMBER_COERCE_MARKER (position, 0);
 
-  SET_PT (clip_to_bounds (BEGV, XINT (position), ZV));
+  pos = clip_to_bounds (BEGV, XINT (position), ZV);
+  /* If POS is in a middle of multi-byte form (i.e. *P >= 0xA0), we
+     must decrement POS until it points the head of the multi-byte
+     form.  */
+  if (!NILP (current_buffer->enable_multibyte_characters)
+      && *(p = POS_ADDR (pos)) >= 0xA0
+      && pos > BEGV)
+    {
+      /* Since a multi-byte form does not contain the gap, POS should
+         not stride over the gap while it is being decreased.  So, we
+         set the limit as below.  */
+      unsigned char *p_min = pos < GPT ? BEG_ADDR : GAP_END_ADDR;
+      unsigned int saved_pos = pos;
+
+      do {
+	p--, pos--;
+      } while (p > p_min && *p >= 0xA0);
+      if (*p < 0x80)
+	/* This was an invalid multi-byte form.  */
+	pos = saved_pos;
+      XSETFASTINT (position, pos);
+    }
+  SET_PT (pos);
   return position;
 }
 
@@ -426,7 +483,10 @@
 
 DEFUN ("following-char", Ffollowing_char, Sfollowing_char, 0, 0, 0,
   "Return the character following point, as a number.\n\
-At the end of the buffer or accessible region, return 0.")
+At the end of the buffer or accessible region, return 0.\n\
+If `enable-multibyte-characters' is nil or point is not\n\
+ at character boundary,  multibyte form is ignored,\n\
+ and only one byte following point is returned as a character.")
   ()
 {
   Lisp_Object temp;
@@ -439,14 +499,23 @@
 
 DEFUN ("preceding-char", Fprevious_char, Sprevious_char, 0, 0, 0,
   "Return the character preceding point, as a number.\n\
-At the beginning of the buffer or accessible region, return 0.")
+At the beginning of the buffer or accessible region, return 0.\n\
+If `enable-multibyte-characters' is nil or point is not\n\
+ at character boundary, multi-byte form is ignored,\n\
+ and only one byte preceding point is returned as a character.")
   ()
 {
   Lisp_Object temp;
   if (PT <= BEGV)
     XSETFASTINT (temp, 0);
+  else if (!NILP (current_buffer->enable_multibyte_characters))
+    {
+      int pos = PT;
+      DEC_POS (pos);
+      XSETFASTINT (temp, FETCH_CHAR (pos));
+    }
   else
-    XSETFASTINT (temp, FETCH_CHAR (PT - 1));
+    XSETFASTINT (temp, FETCH_BYTE (point - 1));
   return temp;
 }
 
@@ -474,7 +543,7 @@
   "Return T if point is at the beginning of a line.")
   ()
 {
-  if (PT == BEGV || FETCH_CHAR (PT - 1) == '\n')
+  if (PT == BEGV || FETCH_BYTE (PT - 1) == '\n')
     return Qt;
   return Qnil;
 }
@@ -484,7 +553,7 @@
 `End of a line' includes point being at the end of the buffer.")
   ()
 {
-  if (PT == ZV || FETCH_CHAR (PT) == '\n')
+  if (PT == ZV || FETCH_BYTE (PT) == '\n')
     return Qt;
   return Qnil;
 }
@@ -492,7 +561,10 @@
 DEFUN ("char-after", Fchar_after, Schar_after, 1, 1, 0,
   "Return character in current buffer at position POS.\n\
 POS is an integer or a buffer pointer.\n\
-If POS is out of range, the value is nil.")
+If POS is out of range, the value is nil.\n\
+If `enable-multibyte-characters' is nil or POS is not at character boundary,\n\
+ multi-byte form is ignored, and only one byte at POS\n\
+ is returned as a character.")
   (pos)
      Lisp_Object pos;
 {
@@ -507,6 +579,37 @@
   XSETFASTINT (val, FETCH_CHAR (n));
   return val;
 }
+
+DEFUN ("char-before", Fchar_before, Schar_before, 1, 1, 0,
+  "Return character in current buffer preceding position POS.\n\
+POS is an integer or a buffer pointer.\n\
+If POS is out of range, the value is nil.\n\
+If `enable-multibyte-characters' is nil or POS is not at character boundary,\n\
+multi-byte form is ignored, and only one byte preceding POS\n\
+is returned as a character.")
+  (pos)
+     Lisp_Object pos;
+{
+  register Lisp_Object val;
+  register int n;
+
+  CHECK_NUMBER_COERCE_MARKER (pos, 0);
+
+  n = XINT (pos);
+  if (n <= BEGV || n > ZV) return Qnil;
+
+  if (!NILP (current_buffer->enable_multibyte_characters))
+    {
+      DEC_POS (pos);
+      XSETFASTINT (val, FETCH_CHAR (pos));
+    }
+  else
+    {
+      pos--;
+      XSETFASTINT (val, FETCH_BYTE (pos));
+    }
+   return val;
+}
 
 DEFUN ("user-login-name", Fuser_login_name, Suser_login_name, 0, 1, 0,
   "Return the name under which the user logged in, as a string.\n\
@@ -1114,6 +1217,47 @@
 #endif
 }
 
+/* Insert NARGS Lisp objects in the array ARGS by calling INSERT_FUNC
+   (if a type of object is Lisp_Int) or INSERT_FROM_STRING_FUNC (if a
+   type of object is Lisp_String).  INHERIT is passed to
+   INSERT_FROM_STRING_FUNC as the last argument.  */
+
+general_insert_function (insert_func, insert_from_string_func,
+			 inherit, nargs, args)
+     int (*insert_func)(), (*insert_from_string_func)();
+     int inherit, nargs;
+     register Lisp_Object *args;
+{
+  register int argnum;
+  register Lisp_Object val;
+
+  for (argnum = 0; argnum < nargs; argnum++)
+    {
+      val = args[argnum];
+    retry:
+      if (INTEGERP (val))
+	{
+	  char workbuf[4], *str;
+	  int len;
+
+	  if (!NILP (current_buffer->enable_multibyte_characters))
+	    len = CHAR_STRING (XFASTINT (val), workbuf, str);
+	  else
+	    workbuf[0] = XINT (val), str = workbuf, len = 1;
+	  (*insert_func) (str, len);
+	}
+      else if (STRINGP (val))
+	{
+	  (*insert_from_string_func) (val, 0, XSTRING (val)->size, inherit);
+	}
+      else
+	{
+	  val = wrong_type_argument (Qchar_or_string_p, val);
+	  goto retry;
+	}
+    }
+}
+
 void
 insert1 (arg)
      Lisp_Object arg;
@@ -1129,107 +1273,44 @@
 
 DEFUN ("insert", Finsert, Sinsert, 0, MANY, 0,
   "Insert the arguments, either strings or characters, at point.\n\
-Point moves forward so that it ends up after the inserted text.\n\
+Point and before-insertion-markers move forward so that it ends up\n\
+ after the inserted text.\n\
 Any other markers at the point of insertion remain before the text.")
   (nargs, args)
      int nargs;
      register Lisp_Object *args;
 {
-  register int argnum;
-  register Lisp_Object tem;
-  char str[1];
-
-  for (argnum = 0; argnum < nargs; argnum++)
-    {
-      tem = args[argnum];
-    retry:
-      if (INTEGERP (tem))
-	{
-	  str[0] = XINT (tem);
-	  insert (str, 1);
-	}
-      else if (STRINGP (tem))
-	{
-	  insert_from_string (tem, 0, XSTRING (tem)->size, 0);
-	}
-      else
-	{
-	  tem = wrong_type_argument (Qchar_or_string_p, tem);
-	  goto retry;
-	}
-    }
-
+  general_insert_function (insert, insert_from_string, 0, nargs, args);
   return Qnil;
 }
 
 DEFUN ("insert-and-inherit", Finsert_and_inherit, Sinsert_and_inherit,
    0, MANY, 0,
   "Insert the arguments at point, inheriting properties from adjoining text.\n\
-Point moves forward so that it ends up after the inserted text.\n\
+Point and before-insertion-markers move forward so that it ends up\n\
+ after the inserted text.\n\
 Any other markers at the point of insertion remain before the text.")
   (nargs, args)
      int nargs;
      register Lisp_Object *args;
 {
-  register int argnum;
-  register Lisp_Object tem;
-  char str[1];
-
-  for (argnum = 0; argnum < nargs; argnum++)
-    {
-      tem = args[argnum];
-    retry:
-      if (INTEGERP (tem))
-	{
-	  str[0] = XINT (tem);
-	  insert_and_inherit (str, 1);
-	}
-      else if (STRINGP (tem))
-	{
-	  insert_from_string (tem, 0, XSTRING (tem)->size, 1);
-	}
-      else
-	{
-	  tem = wrong_type_argument (Qchar_or_string_p, tem);
-	  goto retry;
-	}
-    }
-
+  general_insert_function (insert_and_inherit, insert_from_string, 1,
+			   nargs, args);
   return Qnil;
 }
 
 DEFUN ("insert-before-markers", Finsert_before_markers, Sinsert_before_markers, 0, MANY, 0,
   "Insert strings or characters at point, relocating markers after the text.\n\
-Point moves forward so that it ends up after the inserted text.\n\
+Point and before-insertion-markers move forward so that it ends up\n\
+ after the inserted text.\n\
 Any other markers at the point of insertion also end up after the text.")
   (nargs, args)
      int nargs;
      register Lisp_Object *args;
 {
-  register int argnum;
-  register Lisp_Object tem;
-  char str[1];
-
-  for (argnum = 0; argnum < nargs; argnum++)
-    {
-      tem = args[argnum];
-    retry:
-      if (INTEGERP (tem))
-	{
-	  str[0] = XINT (tem);
-	  insert_before_markers (str, 1);
-	}
-      else if (STRINGP (tem))
-	{
-	  insert_from_string_before_markers (tem, 0, XSTRING (tem)->size, 0);
-	}
-      else
-	{
-	  tem = wrong_type_argument (Qchar_or_string_p, tem);
-	  goto retry;
-	}
-    }
-
+  general_insert_function (insert_before_markers,
+			   insert_from_string_before_markers, 0,
+			   nargs, args);
   return Qnil;
 }
 
@@ -1242,36 +1323,15 @@
      int nargs;
      register Lisp_Object *args;
 {
-  register int argnum;
-  register Lisp_Object tem;
-  char str[1];
-
-  for (argnum = 0; argnum < nargs; argnum++)
-    {
-      tem = args[argnum];
-    retry:
-      if (INTEGERP (tem))
-	{
-	  str[0] = XINT (tem);
-	  insert_before_markers_and_inherit (str, 1);
-	}
-      else if (STRINGP (tem))
-	{
-	  insert_from_string_before_markers (tem, 0, XSTRING (tem)->size, 1);
-	}
-      else
-	{
-	  tem = wrong_type_argument (Qchar_or_string_p, tem);
-	  goto retry;
-	}
-    }
-
+  general_insert_function (insert_before_markers_and_inherit,
+			   insert_from_string_before_markers, 1,
+			   nargs, args);
   return Qnil;
 }
 
 DEFUN ("insert-char", Finsert_char, Sinsert_char, 2, 3, 0,
   "Insert COUNT (second arg) copies of CHARACTER (first arg).\n\
-Point and all markers are affected as in the function `insert'.\n\
+Point and before-insertion-markers are affected as in the function `insert'.\n\
 Both arguments are required.\n\
 The optional third arg INHERIT, if non-nil, says to inherit text properties\n\
 from adjoining text, if those properties are sticky.")
@@ -1281,17 +1341,23 @@
   register unsigned char *string;
   register int strlen;
   register int i, n;
+  int len;
+  unsigned char workbuf[4], *str;
 
   CHECK_NUMBER (character, 0);
   CHECK_NUMBER (count, 1);
 
-  n = XINT (count);
+  if (!NILP (current_buffer->enable_multibyte_characters))
+    len = CHAR_STRING (XFASTINT (character), workbuf, str);
+  else
+    workbuf[0] = XFASTINT (character), str = workbuf, len = 1;
+  n = XINT (count) * len;
   if (n <= 0)
     return Qnil;
-  strlen = min (n, 256);
+  strlen = min (n, 256 * len);
   string = (unsigned char *) alloca (strlen);
   for (i = 0; i < strlen; i++)
-    string[i] = XFASTINT (character);
+    string[i] = str[i % len];
   while (n >= strlen)
     {
       if (!NILP (inherit))
@@ -1337,7 +1403,7 @@
     move_gap (start);
 
   result = make_uninit_string (end - start);
-  bcopy (&FETCH_CHAR (start), XSTRING (result)->data, end - start);
+  bcopy (POS_ADDR (start), XSTRING (result)->data, end - start);
 
   /* If desired, update and copy the text properties.  */
 #ifdef USE_TEXT_PROPERTIES
@@ -1627,21 +1693,35 @@
   Ssubst_char_in_region, 4, 5, 0,
   "From START to END, replace FROMCHAR with TOCHAR each time it occurs.\n\
 If optional arg NOUNDO is non-nil, don't record this change for undo\n\
-and don't mark the buffer as really changed.")
+and don't mark the buffer as really changed.\n\
+Both characters must have the same length of multi-byte form.")
   (start, end, fromchar, tochar, noundo)
      Lisp_Object start, end, fromchar, tochar, noundo;
 {
-  register int pos, stop, look;
+  register int pos, stop, i, len;
   int changed = 0;
+  unsigned char fromwork[4], *fromstr, towork[4], *tostr, *p;
   int count = specpdl_ptr - specpdl;
 
   validate_region (&start, &end);
   CHECK_NUMBER (fromchar, 2);
   CHECK_NUMBER (tochar, 3);
 
+  if (! NILP (current_buffer->enable_multibyte_characters))
+    {
+      len = CHAR_STRING (XFASTINT (fromchar), fromwork, fromstr);
+      if (CHAR_STRING (XFASTINT (tochar), towork, tostr) != len)
+	error ("Characters in subst-char-in-region have different byte-lengths");
+    }
+  else
+    {
+      len = 1;
+      fromwork[0] = XFASTINT (fromchar), fromstr = fromwork;
+      towork[0] = XFASTINT (tochar), tostr = towork;
+    }
+
   pos = XINT (start);
   stop = XINT (end);
-  look = XINT (fromchar);
 
   /* If we don't want undo, turn off putting stuff on the list.
      That's faster than getting rid of things,
@@ -1658,13 +1738,26 @@
       current_buffer->filename = Qnil;
     }
 
-  while (pos < stop)
+  if (pos < GPT)
+    stop = min(stop, GPT);
+  p = POS_ADDR (pos);
+  while (1)
     {
-      if (FETCH_CHAR (pos) == look)
+      if (pos >= stop)
+	{
+	  if (pos >= XINT (end)) break;
+	  stop = XINT (end);
+	  p = POS_ADDR (pos);
+	}
+      if (p[0] == fromstr[0]
+	  && (len == 1
+	      || (p[1] == fromstr[1]
+		  && (len == 2 || (p[2] == fromstr[2]
+				 && (len == 3 || p[3] == fromstr[3]))))))
 	{
 	  if (! changed)
 	    {
-	      modify_region (current_buffer, XINT (start), stop);
+	      modify_region (current_buffer, XINT (start), XINT (end));
 
 	      if (! NILP (noundo))
 		{
@@ -1674,14 +1767,16 @@
 		    current_buffer->auto_save_modified++;
 		}
 
- 	      changed = 1;
+	      changed = 1;
 	    }
 
 	  if (NILP (noundo))
-	    record_change (pos, 1);
-	  FETCH_CHAR (pos) = XINT (tochar);
+	    record_change (pos, len);
+	  for (i = 0; i < len; i++) *p++ = tostr[i];
+	  pos += len;
 	}
-      pos++;
+      else
+	pos++, p++;
     }
 
   if (changed)
@@ -1722,14 +1817,14 @@
   cnt = 0;
   for (; pos < stop; ++pos)
     {
-      oc = FETCH_CHAR (pos);
+      oc = FETCH_BYTE (pos);
       if (oc < size)
 	{
 	  nc = tt[oc];
 	  if (nc != oc)
 	    {
 	      record_change (pos, 1);
-	      FETCH_CHAR (pos) = nc;
+	      *(POS_ADDR (pos)) = nc;
 	      signal_after_change (pos, 1, 1);
 	      ++cnt;
 	    }
@@ -2200,7 +2295,10 @@
   CHECK_NUMBER (c1, 0);
   CHECK_NUMBER (c2, 1);
 
-  if (!NILP (current_buffer->case_fold_search)
+  if ((!NILP (current_buffer->case_fold_search)
+       && SINGLE_BYTE_CHAR_P (c1) /* For the moment, downcase table is */
+       && SINGLE_BYTE_CHAR_P (c2) /* implemented only for ASCII characters.  */
+       )
       ? ((XINT (downcase[0xff & XFASTINT (c1)])
 	  == XINT (downcase[0xff & XFASTINT (c2)]))
 	 && (XFASTINT (c1) & ~0xff) == (XFASTINT (c2) & ~0xff))
@@ -2591,6 +2689,7 @@
   defsubr (&Sgoto_char);
   defsubr (&Sstring_to_char);
   defsubr (&Schar_to_string);
+  defsubr (&Ssref);
   defsubr (&Sbuffer_substring);
   defsubr (&Sbuffer_substring_no_properties);
   defsubr (&Sbuffer_string);
@@ -2621,6 +2720,7 @@
   defsubr (&Sfollowing_char);
   defsubr (&Sprevious_char);
   defsubr (&Schar_after);
+  defsubr (&Schar_before);
   defsubr (&Sinsert);
   defsubr (&Sinsert_before_markers);
   defsubr (&Sinsert_and_inherit);