(HAVE_INDEX, HAVE_RINDEX, HAVE_STRINGS_H): Add undefs.
[bpt/emacs.git] / src / data.c
index b06d8cd..0e393bf 100644 (file)
@@ -1835,6 +1835,11 @@ or a byte-code object.  IDX starts at 0.")
     }
 }
 
+/* Don't use alloca for relocating string data larger than this, lest
+   we overflow their stack.  The value is the same as what used in
+   fns.c for base64 handling.  */
+#define MAX_ALLOCA 16*1024
+
 DEFUN ("aset", Faset, Saset, 3, 3, 0,
   "Store into the element of ARRAY at index IDX the value NEWELT.\n\
 ARRAY may be a vector, a string, a char-table or a bool-vector.\n\
@@ -1916,41 +1921,81 @@ IDX starts at 0.")
     }
   else if (STRING_MULTIBYTE (array))
     {
-      int idxval_byte, new_len, actual_len;
-      int prev_byte;
-      unsigned char *p, workbuf[MAX_MULTIBYTE_LENGTH], *str = workbuf;
+      int idxval_byte, prev_bytes, new_bytes;
+      unsigned char workbuf[MAX_MULTIBYTE_LENGTH], *p0 = workbuf, *p1;
 
       if (idxval < 0 || idxval >= XSTRING (array)->size)
        args_out_of_range (array, idx);
+      CHECK_NUMBER (newelt, 2);
 
       idxval_byte = string_char_to_byte (array, idxval);
-      p = &XSTRING (array)->data[idxval_byte];
-
-      actual_len = MULTIBYTE_FORM_LENGTH (p, STRING_BYTES (XSTRING (array)));
-      CHECK_NUMBER (newelt, 2);
-      new_len = CHAR_STRING (XINT (newelt), str);
-      if (actual_len != new_len)
-       error ("Attempt to change byte length of a string");
-
-      /* We can't accept a change causing byte combining.  */
-      if (!ASCII_BYTE_P (*str)
-         && ((idxval > 0 && !CHAR_HEAD_P (*str)
-              && (prev_byte = string_char_to_byte (array, idxval - 1),
-                  BYTES_BY_CHAR_HEAD (XSTRING (array)->data[prev_byte])
-                  > idxval_byte - prev_byte))
-             || (idxval < XSTRING (array)->size - 1
-                 && !CHAR_HEAD_P (p[actual_len])
-                 && new_len < BYTES_BY_CHAR_HEAD (*str))))
-       error ("Attempt to change char length of a string");
-      while (new_len--)
-       *p++ = *str++;
+      p1 = &XSTRING (array)->data[idxval_byte];
+      PARSE_MULTIBYTE_SEQ (p1, nbytes - idxval_byte, prev_bytes);
+      new_bytes = CHAR_STRING (XINT (newelt), p0);
+      if (prev_bytes != new_bytes)
+       {
+         /* We must relocate the string data.  */
+         int nchars = XSTRING (array)->size;
+         int nbytes = STRING_BYTES (XSTRING (array));
+         unsigned char *str;
+
+         str = (nbytes <= MAX_ALLOCA
+                ? (unsigned char *) alloca (nbytes)
+                : (unsigned char *) xmalloc (nbytes));
+         bcopy (XSTRING (array)->data, str, nbytes);
+         allocate_string_data (XSTRING (array), nchars,
+                               nbytes + new_bytes - prev_bytes);
+         bcopy (str, XSTRING (array)->data, idxval_byte);
+         p1 = XSTRING (array)->data + idxval_byte;
+         bcopy (str + idxval_byte + prev_bytes, p1 + new_bytes,
+                nbytes - (idxval_byte + prev_bytes));
+         if (nbytes > MAX_ALLOCA)
+           xfree (str);
+         clear_string_char_byte_cache ();
+       }
+      while (new_bytes--)
+       *p1++ = *p0++;
     }
   else
     {
       if (idxval < 0 || idxval >= XSTRING (array)->size)
        args_out_of_range (array, idx);
       CHECK_NUMBER (newelt, 2);
-      XSTRING (array)->data[idxval] = XINT (newelt);
+
+      if (XINT (newelt) < 0 || SINGLE_BYTE_CHAR_P (XINT (newelt)))
+       XSTRING (array)->data[idxval] = XINT (newelt);
+      else
+       {
+         /* We must relocate the string data while converting it to
+            multibyte.  */
+         int idxval_byte, prev_bytes, new_bytes;
+         unsigned char workbuf[MAX_MULTIBYTE_LENGTH], *p0 = workbuf, *p1;
+         unsigned char *origstr = XSTRING (array)->data, *str;
+         int nchars, nbytes;
+
+         nchars = XSTRING (array)->size;
+         nbytes = idxval_byte = count_size_as_multibyte (origstr, idxval);
+         nbytes += count_size_as_multibyte (origstr + idxval,
+                                            nchars - idxval);
+         str = (nbytes <= MAX_ALLOCA
+                ? (unsigned char *) alloca (nbytes)
+                : (unsigned char *) xmalloc (nbytes));
+         copy_text (XSTRING (array)->data, str, nchars, 0, 1);
+         PARSE_MULTIBYTE_SEQ (str + idxval_byte, nbytes - idxval_byte,
+                              prev_bytes);
+         new_bytes = CHAR_STRING (XINT (newelt), p0);
+         allocate_string_data (XSTRING (array), nchars,
+                               nbytes + new_bytes - prev_bytes);
+         bcopy (str, XSTRING (array)->data, idxval_byte);
+         p1 = XSTRING (array)->data + idxval_byte;
+         while (new_bytes--)
+           *p1++ = *p0++;
+         bcopy (str + idxval_byte + prev_bytes, p1,
+                nbytes - (idxval_byte + prev_bytes));
+         if (nbytes > MAX_ALLOCA)
+           xfree (str);
+         clear_string_char_byte_cache ();
+       }
     }
 
   return newelt;