(Fsave_restriction): Doc fix.
[bpt/emacs.git] / src / editfns.c
index bfee732..f1f11cf 100644 (file)
@@ -490,13 +490,28 @@ See also `gap-position'.")
 }
 
 DEFUN ("position-bytes", Fposition_bytes, Sposition_bytes, 1, 1, 0,
-  "Return the byte position for character position POSITION.")
+  "Return the byte position for character position POSITION.\n\
+If POSITION is out of range, the value is nil.")
   (position)
      Lisp_Object position;
 {
   CHECK_NUMBER_COERCE_MARKER (position, 1);
+  if (XINT (position) < BEG || XINT (position) > Z)
+    return Qnil;
   return make_number (CHAR_TO_BYTE (XINT (position)));
 }
+
+DEFUN ("byte-to-position", Fbyte_to_position, Sbyte_to_position, 1, 1, 0,
+  "Return the character position for byte position BYTEPOS.\n\
+If BYTEPOS is out of range, the value is nil.")
+  (bytepos)
+     Lisp_Object bytepos;
+{
+  CHECK_NUMBER (bytepos, 1);
+  if (XINT (bytepos) < BEG_BYTE || XINT (bytepos) > Z_BYTE)
+    return Qnil;
+  return make_number (BYTE_TO_CHAR (XINT (bytepos)));
+}
 \f
 DEFUN ("following-char", Ffollowing_char, Sfollowing_char, 0, 0, 0,
   "Return the character following point, as a number.\n\
@@ -578,10 +593,7 @@ DEFUN ("eolp", Feolp, Seolp, 0, 0, 0,
 DEFUN ("char-after", Fchar_after, Schar_after, 0, 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.\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.")
+If POS is out of range, the value is nil.")
   (pos)
      Lisp_Object pos;
 {
@@ -589,8 +601,12 @@ If `enable-multibyte-characters' is nil or POS is not at character boundary,\n\
   register Lisp_Object val;
 
   if (NILP (pos))
-    pos_byte = PT_BYTE;
-  else if (MARKERP (pos))
+    {
+      pos_byte = PT_BYTE;
+      pos = PT;
+    }
+
+  if (MARKERP (pos))
     {
       pos_byte = marker_byte_position (pos);
       if (pos_byte < BEGV_BYTE || pos_byte >= ZV_BYTE)
@@ -611,10 +627,7 @@ If `enable-multibyte-characters' is nil or POS is not at character boundary,\n\
 DEFUN ("char-before", Fchar_before, Schar_before, 0, 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.")
+If POS is out of range, the value is nil.")
   (pos)
      Lisp_Object pos;
 {
@@ -622,8 +635,12 @@ is returned as a character.")
   register int pos_byte;
 
   if (NILP (pos))
-    pos_byte = PT_BYTE;
-  else if (MARKERP (pos))
+    {
+      pos_byte = PT_BYTE;
+      pos = PT;
+    }
+
+  if (MARKERP (pos))
     {
       pos_byte = marker_byte_position (pos);
 
@@ -830,6 +847,58 @@ lisp_time_argument (specified_time, result)
     }
 }
 
+/* Write information into buffer S of size MAXSIZE, according to the
+   FORMAT of length FORMAT_LEN, using time information taken from *TP.
+   Return the number of bytes written, not including the terminating
+   '\0'.  If S is NULL, nothing will be written anywhere; so to
+   determine how many bytes would be written, use NULL for S and
+   ((size_t) -1) for MAXSIZE.
+
+   This function behaves like emacs_strftime, except it allows null
+   bytes in FORMAT.  */
+static size_t
+emacs_memftime (s, maxsize, format, format_len, tp)
+      char *s;
+      size_t maxsize;
+      const char *format;
+      size_t format_len;
+      const struct tm *tp;
+{
+  size_t total = 0;
+
+  /* Loop through all the null-terminated strings in the format
+     argument.  Normally there's just one null-terminated string, but
+     there can be arbitrarily many, concatenated together, if the
+     format contains '\0' bytes.  emacs_strftime stops at the first
+     '\0' byte so we must invoke it separately for each such string.  */
+  for (;;)
+    {
+      size_t len;
+      size_t result;
+
+      if (s)
+       s[0] = '\1';
+
+      result = emacs_strftime (s, maxsize, format, tp);
+
+      if (s)
+       {
+         if (result == 0 && s[0] != '\0')
+           return 0;
+         s += result + 1;
+       }
+
+      maxsize -= result + 1;
+      total += result;
+      len = strlen (format);
+      if (len == format_len)
+       return total;
+      total++;
+      format += len + 1;
+      format_len -= len + 1;
+    }
+}
+
 /*
 DEFUN ("format-time-string", Fformat_time_string, Sformat_time_string, 1, 3, 0,
   "Use FORMAT-STRING to format the time TIME, or now if omitted.\n\
@@ -888,6 +957,7 @@ DEFUN ("format-time-string", Fformat_time_string, Sformat_time_string, 1, 3, 0,
 {
   time_t value;
   int size;
+  struct tm *tm;
 
   CHECK_STRING (format_string, 1);
 
@@ -897,22 +967,27 @@ DEFUN ("format-time-string", Fformat_time_string, Sformat_time_string, 1, 3, 0,
   /* This is probably enough.  */
   size = STRING_BYTES (XSTRING (format_string)) * 6 + 50;
 
+  tm = NILP (universal) ? localtime (&value) : gmtime (&value);
+  if (! tm)
+    error ("Specified time is not representable");
+
   while (1)
     {
       char *buf = (char *) alloca (size + 1);
       int result;
 
       buf[0] = '\1';
-      result = emacs_strftime (buf, size, XSTRING (format_string)->data,
-                              (NILP (universal) ? localtime (&value)
-                               : gmtime (&value)));
+      result = emacs_memftime (buf, size, XSTRING (format_string)->data,
+                              STRING_BYTES (XSTRING (format_string)),
+                              tm);
       if ((result > 0 && result < size) || (result == 0 && buf[0] == '\0'))
-       return build_string (buf);
+       return make_string (buf, result);
 
       /* If buffer was too small, make it bigger and try again.  */
-      result = emacs_strftime (NULL, 0x7fffffff, XSTRING (format_string)->data,
-                              (NILP (universal) ? localtime (&value)
-                               : gmtime (&value)));
+      result = emacs_memftime (NULL, (size_t) -1,
+                              XSTRING (format_string)->data,
+                              STRING_BYTES (XSTRING (format_string)),
+                              tm);
       size = result + 1;
     }
 }
@@ -942,6 +1017,8 @@ ZONE is an integer indicating the number of seconds east of Greenwich.\n\
     error ("Invalid time specification");
 
   decoded_time = localtime (&time_spec);
+  if (! decoded_time)
+    error ("Specified time is not representable");
   XSETFASTINT (list_args[0], decoded_time->tm_sec);
   XSETFASTINT (list_args[1], decoded_time->tm_min);
   XSETFASTINT (list_args[2], decoded_time->tm_hour);
@@ -1126,18 +1203,15 @@ the data it can't find.")
 {
   time_t value;
   struct tm *t;
+  struct tm gmt;
 
   if (lisp_time_argument (specified_time, &value)
-      && (t = gmtime (&value)) != 0)
+      && (t = gmtime (&value)) != 0
+      && (gmt = *t, t = localtime (&value)) != 0)
     {
-      struct tm gmt;
-      int offset;
-      char *s, buf[6];
-
-      gmt = *t;                /* Make a copy, in case localtime modifies *t.  */
-      t = localtime (&value);
-      offset = tm_diff (t, &gmt);
-      s = 0;
+      int offset = tm_diff (t, &gmt);
+      char *s = 0;
+      char buf[6];
 #ifdef HAVE_TM_ZONE
       if (t->tm_zone)
        s = (char *)t->tm_zone;
@@ -1315,7 +1389,13 @@ general_insert_function (insert_func, insert_from_string_func,
          if (!NILP (current_buffer->enable_multibyte_characters))
            len = CHAR_STRING (XFASTINT (val), workbuf, str);
          else
-           workbuf[0] = XINT (val), str = workbuf, len = 1;
+           {
+             workbuf[0] = (SINGLE_BYTE_CHAR_P (XINT (val))
+                           ? XINT (val)
+                           : multibyte_char_to_unibyte (XINT (val), Qnil));
+             str = workbuf;
+             len = 1;
+           }
          (*insert_func) (str, len);
        }
       else if (STRINGP (val))
@@ -1354,7 +1434,7 @@ Any other markers at the point of insertion remain before the text.\n\
 \n\
 If the current buffer is multibyte, unibyte strings are converted\n\
 to multibyte for insertion (see `unibyte-char-to-multibyte').\n\
-If the current buffer is unibyte, multiibyte strings are converted\n\
+If the current buffer is unibyte, multibyte strings are converted\n\
 to unibyte for insertion.")
   (nargs, args)
      int nargs;
@@ -1373,7 +1453,7 @@ Any other markers at the point of insertion remain before the text.\n\
 \n\
 If the current buffer is multibyte, unibyte strings are converted\n\
 to multibyte for insertion (see `unibyte-char-to-multibyte').\n\
-If the current buffer is unibyte, multiibyte strings are converted\n\
+If the current buffer is unibyte, multibyte strings are converted\n\
 to unibyte for insertion.")
   (nargs, args)
      int nargs;
@@ -1390,7 +1470,7 @@ Point and markers move forward to end up after the inserted text.\n\
 \n\
 If the current buffer is multibyte, unibyte strings are converted\n\
 to multibyte for insertion (see `unibyte-char-to-multibyte').\n\
-If the current buffer is unibyte, multiibyte strings are converted\n\
+If the current buffer is unibyte, multibyte strings are converted\n\
 to unibyte for insertion.")
   (nargs, args)
      int nargs;
@@ -1409,7 +1489,7 @@ Point and markers move forward to end up after the inserted text.\n\
 \n\
 If the current buffer is multibyte, unibyte strings are converted\n\
 to multibyte for insertion (see `unibyte-char-to-multibyte').\n\
-If the current buffer is unibyte, multiibyte strings are converted\n\
+If the current buffer is unibyte, multibyte strings are converted\n\
 to unibyte for insertion.")
   (nargs, args)
      int nargs;
@@ -1423,8 +1503,8 @@ to unibyte for insertion.")
 \f
 DEFUN ("insert-char", Finsert_char, Sinsert_char, 2, 3, 0,
   "Insert COUNT (second arg) copies of CHARACTER (first arg).\n\
-Point and before-insertion-markers are affected as in the function `insert'.\n\
 Both arguments are required.\n\
+Point, and before-insertion markers, are relocated as in the function `insert'.\n\
 The optional third arg INHERIT, if non-nil, says to inherit text properties\n\
 from adjoining text, if those properties are sticky.")
   (character, count, inherit)
@@ -1927,9 +2007,44 @@ Both characters must have the same length of multi-byte form.")
              changed = 1;
            }
 
-         if (NILP (noundo))
-           record_change (pos, 1);
-         for (i = 0; i < len; i++) *p++ = tostr[i];
+         /* Take care of the case where the new character
+            combines with neighboring bytes.  */ 
+         if (len == 1
+             && ((! CHAR_HEAD_P (tostr[0])
+                  && pos_byte > BEGV_BYTE
+                  && ! ASCII_BYTE_P (FETCH_BYTE (pos_byte - 1)))
+                 ||
+                 (! ASCII_BYTE_P (tostr[0])
+                  && pos_byte + 1 < ZV_BYTE
+                  && ! CHAR_HEAD_P (FETCH_BYTE (pos_byte + 1)))))
+           {
+             Lisp_Object tem, string;
+
+             struct gcpro gcpro1;
+
+             tem = current_buffer->undo_list;
+             GCPRO1 (tem);
+
+             /* Make a multibyte string containing this
+                single-byte character.  */
+             string = Fmake_string (make_number (1),
+                                    make_number (tochar));
+             SET_STRING_BYTES (XSTRING (string), 1);
+             /* replace_range is less efficient, because it moves the gap,
+                but it handles combining correctly.  */
+             replace_range (pos, pos + 1, string,
+                            0, 0, 1);
+             if (! NILP (noundo))
+               current_buffer->undo_list = tem;
+
+             UNGCPRO;
+           }
+         else
+           {
+             if (NILP (noundo))
+               record_change (pos, 1);
+             for (i = 0; i < len; i++) *p++ = tostr[i];
+           }
        }
       INC_BOTH (pos, pos_byte);
     }
@@ -1984,9 +2099,33 @@ It returns the number of characters changed.")
          nc = tt[oc];
          if (nc != oc)
            {
-             record_change (pos, 1);
-             *p = nc;
-             signal_after_change (pos, 1, 1);
+             /* Take care of the case where the new character
+                combines with neighboring bytes.  */ 
+             if ((! CHAR_HEAD_P (nc)
+                  && pos_byte > BEGV_BYTE
+                  && ! ASCII_BYTE_P (FETCH_BYTE (pos_byte - 1)))
+                 ||
+                 (! ASCII_BYTE_P (nc)
+                  && pos_byte + 1 < ZV_BYTE
+                  && ! CHAR_HEAD_P (FETCH_BYTE (pos_byte + 1))))
+               {
+                 Lisp_Object string;
+
+                 string = Fmake_string (make_number (1),
+                                        make_number (nc));
+                 SET_STRING_BYTES (XSTRING (string), 1);
+
+                 /* This is less efficient, because it moves the gap,
+                    but it handles combining correctly.  */
+                 replace_range (pos, pos + 1, string,
+                                1, 0, 0);
+               }
+             else
+               {
+                 record_change (pos, 1);
+                 *p = nc;
+                 signal_after_change (pos, 1, 1);
+               }
              ++cnt;
            }
        }
@@ -2130,6 +2269,7 @@ The value returned is the value of the last form in BODY.\n\
 \n\
 `save-restriction' can get confused if, within the BODY, you widen\n\
 and then make changes outside the area within the saved restrictions.\n\
+See Info node `(elisp)Narrowing' for details and an appropriate technique.\n\
 \n\
 Note: if you are using both `save-excursion' and `save-restriction',\n\
 use `save-excursion' outermost:\n\
@@ -2309,6 +2449,11 @@ Use %% to put a single % into the output.")
   /* Nonzero if the output should be a multibyte string,
      which is true if any of the inputs is one.  */
   int multibyte = 0;
+  /* When we make a multibyte string, we must pay attention to the
+     byte combining problem, i.e., a byte may be combined with a
+     multibyte charcter of the previous string.  This flag tells if we
+     must consider such a situation or not.  */
+  int maybe_combine_byte;
   unsigned char *this_format;
   int longest_format;
   Lisp_Object val;
@@ -2360,6 +2505,8 @@ Use %% to put a single % into the output.")
        if (format - this_format_start + 1 > longest_format)
          longest_format = format - this_format_start + 1;
 
+       if (format == end)
+         error ("Format string ends in middle of format specifier");
        if (*format == '%')
          format++;
        else if (++n >= nargs)
@@ -2391,7 +2538,7 @@ Use %% to put a single % into the output.")
          {
          string:
            if (*format != 's' && *format != 'S')
-             error ("format specifier doesn't match argument type");
+             error ("Format specifier doesn't match argument type");
            thissize = CONVERTED_BYTE_SIZE (multibyte, args[n]);
          }
        /* Would get MPV otherwise, since Lisp_Int's `point' to low memory.  */
@@ -2455,9 +2602,9 @@ Use %% to put a single % into the output.")
   /* Allocate the space for the result.
      Note that TOTAL is an overestimate.  */
   if (total < 1000)
-    buf = (unsigned char *) alloca (total + 1);
+    buf = (char *) alloca (total + 1);
   else
-    buf = (unsigned char *) xmalloc (total + 1);
+    buf = (char *) xmalloc (total + 1);
 
   p = buf;
   nchars = 0;
@@ -2465,6 +2612,7 @@ Use %% to put a single % into the output.")
 
   /* Scan the format and store result in BUF.  */
   format = XSTRING (args[0])->data;
+  maybe_combine_byte = 0;
   while (format != end)
     {
       if (*format == '%')
@@ -2508,6 +2656,12 @@ Use %% to put a single % into the output.")
                    nchars++;
                  }
 
+             if (p > buf
+                 && multibyte
+                 && !ASCII_BYTE_P (*((unsigned char *) p - 1))
+                 && STRING_MULTIBYTE (args[n])
+                 && !CHAR_HEAD_P (XSTRING (args[n])->data[0]))
+               maybe_combine_byte = 1;
              nbytes = copy_text (XSTRING (args[n])->data, p,
                                  STRING_BYTES (XSTRING (args[n])),
                                  STRING_MULTIBYTE (args[n]), multibyte);
@@ -2534,6 +2688,11 @@ Use %% to put a single % into the output.")
              else
                sprintf (p, this_format, XFLOAT (args[n])->data);
 
+             if (p > buf
+                 && multibyte
+                 && !ASCII_BYTE_P (*((unsigned char *) p - 1))
+                 && !CHAR_HEAD_P (*((unsigned char *) p)))
+               maybe_combine_byte = 1;
              this_nchars = strlen (p);
              p += this_nchars;
              nchars += this_nchars;
@@ -2542,6 +2701,11 @@ Use %% to put a single % into the output.")
       else if (STRING_MULTIBYTE (args[0]))
        {
          /* Copy a whole multibyte character.  */
+         if (p > buf
+             && multibyte
+             && !ASCII_BYTE_P (*((unsigned char *) p - 1))
+             && !CHAR_HEAD_P (*format))
+           maybe_combine_byte = 1;
          *p++ = *format++;
          while (! CHAR_HEAD_P (*format)) *p++ = *format++;
          nchars++;
@@ -2559,6 +2723,8 @@ Use %% to put a single % into the output.")
        *p++ = *format++, nchars++;
     }
 
+  if (maybe_combine_byte)
+    nchars = multibyte_chars_in_text (buf, p - buf);
   val = make_specified_string (buf, nchars, p - buf, multibyte);
 
   /* If we allocated BUF with malloc, free it too.  */
@@ -2840,8 +3006,8 @@ Transposing beyond buffer boundaries is an error.")
                         start1_byte, start1_byte + len1_byte,
                         start2_byte, start2_byte + len2_byte);
 
-      replace_range (start2, end2, text1, 1, 0, 1);
-      replace_range (start1, end1, text2, 1, 0, 1);
+      replace_range (start2, end2, text1, 1, 0, 0);
+      replace_range (start1, end1, text2, 1, 0, 0);
 
       UNGCPRO;
       return Qnil;
@@ -2879,8 +3045,8 @@ Transposing beyond buffer boundaries is an error.")
          /* Don't precompute these addresses.  We have to compute them
             at the last minute, because the relocating allocator might
             have moved the buffer around during the xmalloc.  */
-         start1_addr = BUF_CHAR_ADDRESS (current_buffer, start1_byte);
-         start2_addr = BUF_CHAR_ADDRESS (current_buffer, start2_byte);
+         start1_addr = BYTE_POS_ADDR (start1_byte);
+         start2_addr = BYTE_POS_ADDR (start2_byte);
 
           bcopy (start2_addr, temp, len2_byte);
           bcopy (start1_addr, start1_addr + len2_byte, len1_byte);
@@ -2895,8 +3061,8 @@ Transposing beyond buffer boundaries is an error.")
            temp = (unsigned char *) xmalloc (len1_byte);
          else
            temp = (unsigned char *) alloca (len1_byte);
-         start1_addr = BUF_CHAR_ADDRESS (current_buffer, start1_byte);
-         start2_addr = BUF_CHAR_ADDRESS (current_buffer, start2_byte);
+         start1_addr = BYTE_POS_ADDR (start1_byte);
+         start2_addr = BYTE_POS_ADDR (start2_byte);
           bcopy (start1_addr, temp, len1_byte);
           bcopy (start2_addr, start1_addr, len2_byte);
           bcopy (temp, start1_addr + len2_byte, len1_byte);
@@ -2935,8 +3101,8 @@ Transposing beyond buffer boundaries is an error.")
            temp = (unsigned char *) xmalloc (len1_byte);
          else
            temp = (unsigned char *) alloca (len1_byte);
-         start1_addr = BUF_CHAR_ADDRESS (current_buffer, start1_byte);
-         start2_addr = BUF_CHAR_ADDRESS (current_buffer, start2_byte);
+         start1_addr = BYTE_POS_ADDR (start1_byte);
+         start2_addr = BYTE_POS_ADDR (start2_byte);
           bcopy (start1_addr, temp, len1_byte);
           bcopy (start2_addr, start1_addr, len2_byte);
           bcopy (temp, start2_addr, len1_byte);
@@ -2968,8 +3134,8 @@ Transposing beyond buffer boundaries is an error.")
            temp = (unsigned char *) xmalloc (len2_byte);
          else
            temp = (unsigned char *) alloca (len2_byte);
-         start1_addr = BUF_CHAR_ADDRESS (current_buffer, start1_byte);
-         start2_addr = BUF_CHAR_ADDRESS (current_buffer, start2_byte);
+         start1_addr = BYTE_POS_ADDR (start1_byte);
+         start2_addr = BYTE_POS_ADDR (start2_byte);
           bcopy (start2_addr, temp, len2_byte);
           bcopy (start1_addr, start1_addr + len_mid + len2_byte, len1_byte);
           safe_bcopy (start1_addr + len1_byte, start1_addr + len2_byte, len_mid);
@@ -3004,8 +3170,8 @@ Transposing beyond buffer boundaries is an error.")
            temp = (unsigned char *) xmalloc (len1_byte);
          else
            temp = (unsigned char *) alloca (len1_byte);
-         start1_addr = BUF_CHAR_ADDRESS (current_buffer, start1_byte);
-         start2_addr = BUF_CHAR_ADDRESS (current_buffer, start2_byte);
+         start1_addr = BYTE_POS_ADDR (start1_byte);
+         start2_addr = BYTE_POS_ADDR (start2_byte);
           bcopy (start1_addr, temp, len1_byte);
           bcopy (start2_addr, start1_addr, len2_byte);
           bcopy (start1_addr + len1_byte, start1_addr + len2_byte, len_mid);
@@ -3115,6 +3281,7 @@ functions if all the text being accessed has this property.");
   defsubr (&Sgap_position);
   defsubr (&Sgap_size);
   defsubr (&Sposition_bytes);
+  defsubr (&Sbyte_to_position);
 
   defsubr (&Sbobp);
   defsubr (&Seobp);