/* Lisp functions pertaining to editing.
- Copyright (C) 1985,86,87,89,93,94,95,96,97 Free Software Foundation, Inc.
+ Copyright (C) 1985,86,87,89,93,94,95,96,97,98 Free Software Foundation, Inc.
This file is part of GNU Emacs.
#include <pwd.h>
#endif
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
#include "lisp.h"
#include "intervals.h"
#include "buffer.h"
}
\f
DEFUN ("char-to-string", Fchar_to_string, Schar_to_string, 1, 1, 0,
- "Convert arg CHAR to a string containing multi-byte form of that character.")
+ "Convert arg CHAR to a string containing that character.")
(character)
Lisp_Object character;
{
CHECK_NUMBER (character, 0);
len = CHAR_STRING (XFASTINT (character), workbuf, str);
- return make_multibyte_string (str, 1, len);
+ return make_string_from_bytes (str, 1, len);
}
DEFUN ("string-to-char", Fstring_to_char, Sstring_to_char, 1, 1, 0,
CHECK_STRING (string, 0);
p = XSTRING (string);
if (p->size)
- XSETFASTINT (val, STRING_CHAR (p->data, p->size));
+ XSETFASTINT (val, STRING_CHAR (p->data, STRING_BYTES (p)));
else
XSETFASTINT (val, 0);
return val;
int pos;
unsigned char *p;
- if (MARKERP (position))
+ if (MARKERP (position)
+ && current_buffer == XMARKER (position)->buffer)
{
pos = marker_position (position);
if (pos < BEGV)
Executes BODY just like `progn'.\n\
The values of point, mark and the current buffer are restored\n\
even in case of abnormal exit (throw or error).\n\
-The state of activation of the mark is also restored.")
+The state of activation of the mark is also restored.\n\
+\n\
+This construct does not save `deactivate-mark', and therefore\n\
+functions that change the buffer will still cause deactivation\n\
+of the mark at the end of the command. To prevent that, bind\n\
+`deactivate-mark' with `let'.")
(args)
Lisp_Object args;
{
register Lisp_Object val;
int count = specpdl_ptr - specpdl;
- record_unwind_protect (Fset_buffer, Fcurrent_buffer ());
+ record_unwind_protect (set_buffer_if_live, Fcurrent_buffer ());
val = Fprogn (args);
return unbind_to (count, val);
return buildmark (ZV, ZV_BYTE);
}
+DEFUN ("gap-position", Fgap_position, Sgap_position, 0, 0, 0,
+ "Return the position of the gap, in the current buffer.\n\
+See also `gap-size'.")
+ ()
+{
+ Lisp_Object temp;
+ XSETFASTINT (temp, GPT);
+ return temp;
+}
+
+DEFUN ("gap-size", Fgap_size, Sgap_size, 0, 0, 0,
+ "Return the size of the current buffer's gap.\n\
+See also `gap-position'.")
+ ()
+{
+ Lisp_Object temp;
+ XSETFASTINT (temp, GAP_SIZE);
+ return temp;
+}
+
+DEFUN ("position-bytes", Fposition_bytes, Sposition_bytes, 1, 1, 0,
+ "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\
At the end of the buffer or accessible region, return 0.\n\
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;
{
register Lisp_Object val;
if (NILP (pos))
- return make_number (FETCH_CHAR (PT_BYTE));
+ {
+ pos_byte = PT_BYTE;
+ pos = PT;
+ }
if (MARKERP (pos))
- pos_byte = marker_byte_position (pos);
+ {
+ pos_byte = marker_byte_position (pos);
+ if (pos_byte < BEGV_BYTE || pos_byte >= ZV_BYTE)
+ return Qnil;
+ }
else
{
CHECK_NUMBER_COERCE_MARKER (pos, 0);
+ if (XINT (pos) < BEGV || XINT (pos) >= ZV)
+ return Qnil;
pos_byte = CHAR_TO_BYTE (XINT (pos));
}
- if (pos_byte < BEGV_BYTE || pos_byte >= ZV_BYTE)
- return Qnil;
-
return make_number (FETCH_CHAR (pos_byte));
}
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;
{
register int pos_byte;
if (NILP (pos))
- pos_byte = PT_BYTE;
- else if (MARKERP (pos))
- pos_byte = marker_byte_position (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)
+ return Qnil;
+ }
else
{
CHECK_NUMBER_COERCE_MARKER (pos, 0);
+ if (XINT (pos) <= BEGV || XINT (pos) > ZV)
+ return Qnil;
+
pos_byte = CHAR_TO_BYTE (XINT (pos));
}
- if (pos_byte <= BEGV_BYTE || pos_byte > ZV_BYTE)
- return Qnil;
-
if (!NILP (current_buffer->enable_multibyte_characters))
{
DEC_POS (pos_byte);
}
}
+/* 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\
{
time_t value;
int size;
+ struct tm *tm;
CHECK_STRING (format_string, 1);
error ("Invalid time specification");
/* This is probably enough. */
- size = XSTRING (format_string)->size_byte * 6 + 50;
+ 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)
{
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;
}
}
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);
{
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;
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))
{
(*insert_from_string_func) (val, 0, 0,
XSTRING (val)->size,
- XSTRING (val)->size_byte,
+ STRING_BYTES (XSTRING (val)),
inherit);
}
else
DEFUN ("insert", Finsert, Sinsert, 0, MANY, 0,
"Insert the arguments, either strings or characters, at point.\n\
-Point and before-insertion-markers move forward so that it ends up\n\
+Point and before-insertion markers move forward to end up\n\
after the inserted text.\n\
-Any other markers at the point of insertion remain before the text.")
+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, multibyte strings are converted\n\
+to unibyte for insertion.")
(nargs, args)
int nargs;
register Lisp_Object *args;
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 and before-insertion-markers move forward so that it ends up\n\
+Point and before-insertion markers move forward to end up\n\
after the inserted text.\n\
-Any other markers at the point of insertion remain before the text.")
+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, multibyte strings are converted\n\
+to unibyte for insertion.")
(nargs, args)
int nargs;
register Lisp_Object *args;
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 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.")
+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, multibyte strings are converted\n\
+to unibyte for insertion.")
(nargs, args)
int nargs;
register Lisp_Object *args;
DEFUN ("insert-before-markers-and-inherit", Finsert_and_inherit_before_markers,
Sinsert_and_inherit_before_markers, 0, MANY, 0,
"Insert text at point, relocating markers and inheriting properties.\n\
-Point moves forward so that it ends up after the inserted text.\n\
-Any other markers at the point of insertion also end up after the text.")
+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, multibyte strings are converted\n\
+to unibyte for insertion.")
(nargs, args)
int nargs;
register Lisp_Object *args;
\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)
int start, end;
int props;
{
- Lisp_Object result, tem, tem1;
int start_byte = CHAR_TO_BYTE (start);
int end_byte = CHAR_TO_BYTE (end);
+ return make_buffer_string_both (start, start_byte, end, end_byte, props);
+}
+
+/* Return a Lisp_String containing the text of the current buffer from
+ START / START_BYTE to END / END_BYTE.
+
+ If text properties are in use and the current buffer
+ has properties in the range specified, the resulting string will also
+ have them, if PROPS is nonzero.
+
+ We don't want to use plain old make_string here, because it calls
+ make_uninit_string, which can cause the buffer arena to be
+ compacted. make_string has no way of knowing that the data has
+ been moved, and thus copies the wrong data into the string. This
+ doesn't effect most of the other users of make_string, so it should
+ be left as is. But we should use this function when conjuring
+ buffer substrings. */
+
+Lisp_Object
+make_buffer_string_both (start, start_byte, end, end_byte, props)
+ int start, start_byte, end, end_byte;
+ int props;
+{
+ Lisp_Object result, tem, tem1;
+
if (start < GPT && GPT < end)
move_gap (start);
- result = make_uninit_multibyte_string (end - start, end_byte - start_byte);
+ if (! NILP (current_buffer->enable_multibyte_characters))
+ result = make_uninit_multibyte_string (end - start, end_byte - start_byte);
+ else
+ result = make_uninit_string (end - start);
bcopy (BYTE_POS_ADDR (start_byte), XSTRING (result)->data,
end_byte - start_byte);
DEFUN ("buffer-substring", Fbuffer_substring, Sbuffer_substring, 2, 2, 0,
"Return the contents of part of the current buffer as a string.\n\
The two arguments START and END are character positions;\n\
-they can be in either order.")
+they can be in either order.\n\
+The string returned is multibyte if the buffer is multibyte.")
(start, end)
Lisp_Object start, end;
{
(buffer1, start1, end1, buffer2, start2, end2)
Lisp_Object buffer1, start1, end1, buffer2, start2, end2;
{
- register int begp1, endp1, begp2, endp2, temp, len1, len2, length, i;
+ register int begp1, endp1, begp2, endp2, temp;
register struct buffer *bp1, *bp2;
register Lisp_Object *trt
= (!NILP (current_buffer->case_fold_search)
? XCHAR_TABLE (current_buffer->case_canon_table)->contents : 0);
int chars = 0;
- int beg1_byte, beg2_byte;
+ int i1, i2, i1_byte, i2_byte;
/* Find the first buffer and its substring. */
&& endp2 <= BUF_ZV (bp2)))
args_out_of_range (start2, end2);
- beg1_byte = buf_charpos_to_bytepos (bp1, begp1);
- beg2_byte = buf_charpos_to_bytepos (bp2, begp2);
- len1 = buf_charpos_to_bytepos (bp1, endp1) - begp1;
- len2 = buf_charpos_to_bytepos (bp2, endp2) - begp2;
- length = len1;
- if (len2 < length)
- length = len2;
+ i1 = begp1;
+ i2 = begp2;
+ i1_byte = buf_charpos_to_bytepos (bp1, i1);
+ i2_byte = buf_charpos_to_bytepos (bp2, i2);
- for (i = 0; i < length; i++)
+ while (i1 < endp1 && i2 < endp2)
{
- unsigned char *p1 = BUF_BYTE_ADDRESS (bp1, beg1_byte + i);
- int c1 = *p1;
- int c2 = *BUF_BYTE_ADDRESS (bp2, beg2_byte + i);
+ /* When we find a mismatch, we must compare the
+ characters, not just the bytes. */
+ int c1, c2;
+
+ if (! NILP (bp1->enable_multibyte_characters))
+ {
+ c1 = BUF_FETCH_MULTIBYTE_CHAR (bp1, i1_byte);
+ BUF_INC_POS (bp1, i1_byte);
+ i1++;
+ }
+ else
+ {
+ c1 = BUF_FETCH_BYTE (bp1, i1);
+ c1 = unibyte_char_to_multibyte (c1);
+ i1++;
+ }
- /* If a character begins here,
- count the previous character now. */
- if (i > 0
- && (NILP (current_buffer->enable_multibyte_characters)
- || CHAR_HEAD_P (*p1)))
- chars++;
+ if (! NILP (bp2->enable_multibyte_characters))
+ {
+ c2 = BUF_FETCH_MULTIBYTE_CHAR (bp2, i2_byte);
+ BUF_INC_POS (bp2, i2_byte);
+ i2++;
+ }
+ else
+ {
+ c2 = BUF_FETCH_BYTE (bp2, i2);
+ c2 = unibyte_char_to_multibyte (c2);
+ i2++;
+ }
if (trt)
{
return make_number (- 1 - chars);
if (c1 > c2)
return make_number (chars + 1);
+
+ chars++;
}
/* The strings match as far as they go.
If one is shorter, that one is less. */
- if (length < len1)
+ if (chars < endp1 - begp1)
return make_number (chars + 1);
- else if (length < len2)
+ else if (chars < endp2 - begp2)
return make_number (- chars - 1);
/* Same length too => they are equal. */
(start, end, fromchar, tochar, noundo)
Lisp_Object start, end, fromchar, tochar, noundo;
{
- register int pos, stop, i, len, end_byte;
+ register int pos, pos_byte, stop, i, len, end_byte;
int changed = 0;
unsigned char fromwork[4], *fromstr, towork[4], *tostr, *p;
int count = specpdl_ptr - specpdl;
+ int maybe_byte_combining = 0;
validate_region (&start, &end);
CHECK_NUMBER (fromchar, 2);
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");
+ if (len == 1)
+ /* If *TOSTR is in the range 0x80..0x9F, it may be combined
+ with the after bytes. If it is in the range 0xA0..0xFF, it
+ may be combined with the before bytes. */
+ maybe_byte_combining = !ASCII_BYTE_P (*tostr);
}
else
{
towork[0] = XFASTINT (tochar), tostr = towork;
}
- pos = CHAR_TO_BYTE (XINT (start));
+ pos = XINT (start);
+ pos_byte = CHAR_TO_BYTE (pos);
stop = CHAR_TO_BYTE (XINT (end));
end_byte = stop;
current_buffer->filename = Qnil;
}
- if (pos < GPT_BYTE)
+ if (pos_byte < GPT_BYTE)
stop = min (stop, GPT_BYTE);
- p = BYTE_POS_ADDR (pos);
while (1)
{
- if (pos >= stop)
+ int pos_byte_next = pos_byte;
+
+ if (pos_byte >= stop)
{
- if (pos >= end_byte) break;
+ if (pos_byte >= end_byte) break;
stop = end_byte;
- p = BYTE_POS_ADDR (pos);
}
- if (p[0] == fromstr[0]
+ p = BYTE_POS_ADDR (pos_byte);
+ INC_POS (pos_byte_next);
+ if (pos_byte_next - pos_byte == len
+ && p[0] == fromstr[0]
&& (len == 1
|| (p[1] == fromstr[1]
&& (len == 2 || (p[2] == fromstr[2]
changed = 1;
}
- if (NILP (noundo))
- record_change (pos, len);
- for (i = 0; i < len; i++) *p++ = tostr[i];
- pos += len;
+ /* Take care of the case where the new character
+ combines with neighboring bytes. */
+ if (maybe_byte_combining
+ && (CHAR_HEAD_P (*tostr)
+ ? ! CHAR_HEAD_P (FETCH_BYTE (pos_byte + 1))
+ : (pos_byte > BEGV_BYTE
+ && ! ASCII_BYTE_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 = make_multibyte_string (tostr, 1, 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);
+ pos_byte_next = CHAR_TO_BYTE (pos);
+ if (pos_byte_next > pos_byte)
+ /* Before combining happened. We should not increment
+ POS because now it points the next character. */
+ pos_byte = pos_byte_next;
+ else
+ {
+ pos++;
+ INC_POS (pos_byte_next);
+ }
+
+ 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];
+ pos_byte = pos_byte_next;
+ pos++;
+ }
}
else
- pos++, p++;
+ {
+ pos_byte = pos_byte_next;
+ pos++;
+ }
}
if (changed)
signal_after_change (XINT (start),
- stop - XINT (start), stop - XINT (start));
+ XINT (end) - XINT (start), XINT (end) - XINT (start));
unbind_to (count, Qnil);
return Qnil;
validate_region (&start, &end);
CHECK_STRING (table, 2);
- size = XSTRING (table)->size_byte;
+ size = STRING_BYTES (XSTRING (table));
tt = XSTRING (table)->data;
pos_byte = CHAR_TO_BYTE (XINT (start));
register unsigned char *p = BYTE_POS_ADDR (pos_byte);
int len;
int oc;
+ int pos_byte_next;
oc = STRING_CHAR_AND_LENGTH (p, stop - pos_byte, len);
+ pos_byte_next = pos_byte + len;
if (oc < size && len == 1)
{
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 (!ASCII_BYTE_P (nc)
+ && (CHAR_HEAD_P (nc)
+ ? ! CHAR_HEAD_P (FETCH_BYTE (pos_byte + 1))
+ : (pos_byte > BEGV_BYTE
+ && ! ASCII_BYTE_P (FETCH_BYTE (pos_byte - 1)))))
+ {
+ Lisp_Object string;
+
+ string = make_multibyte_string (tt + oc, 1, 1);
+ /* This is less efficient, because it moves the gap,
+ but it handles combining correctly. */
+ replace_range (pos, pos + 1, string,
+ 1, 0, 1);
+ pos_byte_next = CHAR_TO_BYTE (pos);
+ if (pos_byte_next > pos_byte)
+ /* Before combining happened. We should not
+ increment POS because now it points the next
+ character. */
+ pos_byte = pos_byte_next;
+ else
+ {
+ pos++;
+ INC_POS (pos_byte_next);
+ }
+ }
+ else
+ {
+ record_change (pos, 1);
+ *p = nc;
+ signal_after_change (pos, 1, 1);
+ pos_byte++;
+ pos++;
+ }
++cnt;
}
+ else
+ {
+ pos_byte++;
+ pos++;
+ }
+ }
+ else
+ {
+ pos_byte += len;
+ pos++;
}
- pos_byte += len;
- pos++;
}
return make_number (cnt);
\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\
message_text = (char *)xmalloc (80);
message_length = 80;
}
- if (XSTRING (val)->size > message_length)
+ if (STRING_BYTES (XSTRING (val)) > message_length)
{
- message_length = XSTRING (val)->size_byte;
+ message_length = STRING_BYTES (XSTRING (val));
message_text = (char *)xrealloc (message_text, message_length);
}
- bcopy (XSTRING (val)->data, message_text, XSTRING (val)->size_byte);
- message2 (message_text, XSTRING (val)->size_byte,
+ bcopy (XSTRING (val)->data, message_text, STRING_BYTES (XSTRING (val)));
+ message2 (message_text, STRING_BYTES (XSTRING (val)),
STRING_MULTIBYTE (val));
return val;
}
message_text = (char *)xmalloc (80);
message_length = 80;
}
- if (XSTRING (val)->size_byte > message_length)
+ if (STRING_BYTES (XSTRING (val)) > message_length)
{
- message_length = XSTRING (val)->size_byte;
+ message_length = STRING_BYTES (XSTRING (val));
message_text = (char *)xrealloc (message_text, message_length);
}
- bcopy (XSTRING (val)->data, message_text, XSTRING (val)->size_byte);
- message2 (message_text, XSTRING (val)->size_byte);
+ bcopy (XSTRING (val)->data, message_text, STRING_BYTES (XSTRING (val)));
+ message2 (message_text, STRING_BYTES (XSTRING (val)),
+ STRING_MULTIBYTE (val));
return val;
#endif /* not HAVE_MENUS */
}
#define CONVERTED_BYTE_SIZE(MULTIBYTE, STRING) \
(((MULTIBYTE) && ! STRING_MULTIBYTE (STRING)) \
- ? XSTRING (STRING)->size_byte \
- : count_size_as_multibyte (XSTRING (STRING)->data, \
- XSTRING (STRING)->size_byte))
+ ? count_size_as_multibyte (XSTRING (STRING)->data, \
+ STRING_BYTES (XSTRING (STRING))) \
+ : STRING_BYTES (XSTRING (STRING)))
DEFUN ("format", Fformat, Sformat, 1, MANY, 0,
"Format a string out of a control-string and arguments.\n\
register Lisp_Object *args;
{
register int n; /* The number of the next arg to substitute */
- register int total = 5; /* An estimate of the final length */
+ register int total; /* An estimate of the final length */
char *buf, *p;
register unsigned char *format, *end;
int length, nchars;
/* 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 = 0;
+ int longest_format;
+ Lisp_Object val;
extern char *index ();
/* It should not be necessary to GCPRO ARGS, because
the caller in the interpreter should take care of that. */
+ /* Try to determine whether the result should be multibyte.
+ This is not always right; sometimes the result needs to be multibyte
+ because of an object that we will pass through prin1,
+ and in that case, we won't know it here. */
for (n = 0; n < nargs; n++)
if (STRINGP (args[n]) && STRING_MULTIBYTE (args[n]))
multibyte = 1;
CHECK_STRING (args[0], 0);
+
+ /* If we start out planning a unibyte result,
+ and later find it has to be multibyte, we jump back to retry. */
+ retry:
+
format = XSTRING (args[0])->data;
- end = format + XSTRING (args[0])->size_byte;
+ end = format + STRING_BYTES (XSTRING (args[0]));
+ longest_format = 0;
/* Make room in result for all the non-%-codes in the control string. */
- total += CONVERTED_BYTE_SIZE (multibyte, args[0]);
+ total = 5 + CONVERTED_BYTE_SIZE (multibyte, args[0]);
/* Add to TOTAL enough space to hold the converted arguments. */
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)
/* For `S', prin1 the argument and then treat like a string. */
register Lisp_Object tem;
tem = Fprin1_to_string (args[n], Qnil);
+ if (STRING_MULTIBYTE (tem) && ! multibyte)
+ {
+ multibyte = 1;
+ goto retry;
+ }
args[n] = tem;
goto string;
}
else if (SYMBOLP (args[n]))
{
XSETSTRING (args[n], XSYMBOL (args[n])->name);
+ if (STRING_MULTIBYTE (args[n]) && ! multibyte)
+ {
+ multibyte = 1;
+ goto retry;
+ }
goto string;
}
else if (STRINGP (args[n]))
{
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. */
be a double. */
if (*format == 'e' || *format == 'f' || *format == 'g')
args[n] = Ffloat (args[n]);
+ else
#endif
- thissize = 30;
+ if (*format != 'd' && *format != 'o' && *format != 'x'
+ && *format != 'X' && *format != 'c')
+ error ("Invalid format operation %%%c", *format);
+
+ thissize = 30;
+ if (*format == 'c'
+ && (! SINGLE_BYTE_CHAR_P (XINT (args[n]))
+ || XINT (args[n]) == 0))
+ {
+ if (! multibyte)
+ {
+ multibyte = 1;
+ goto retry;
+ }
+ args[n] = Fchar_to_string (args[n]);
+ thissize = STRING_BYTES (XSTRING (args[n]));
+ }
}
#ifdef LISP_FLOAT_TYPE
else if (FLOATP (args[n]) && *format != 's')
{
if (! (*format == 'e' || *format == 'f' || *format == 'g'))
args[n] = Ftruncate (args[n], Qnil);
- thissize = 60;
+ thissize = 200;
}
#endif
else
/* Anything but a string, convert to a string using princ. */
register Lisp_Object tem;
tem = Fprin1_to_string (args[n], Qt);
+ if (STRING_MULTIBYTE (tem) & ! multibyte)
+ {
+ multibyte = 1;
+ goto retry;
+ }
args[n] = tem;
goto string;
}
total += thissize + 4;
}
+ /* Now we can no longer jump to retry.
+ TOTAL and LONGEST_FORMAT are known for certain. */
+
this_format = (unsigned char *) alloca (longest_format + 1);
/* 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;
/* Scan the format and store result in BUF. */
format = XSTRING (args[0])->data;
+ maybe_combine_byte = 0;
while (format != end)
{
if (*format == '%')
{
int minlen;
+ int negative = 0;
unsigned char *this_format_start = format;
format++;
/* Process a numeric arg and skip it. */
minlen = atoi (format);
if (minlen < 0)
- minlen = - minlen;
+ minlen = - minlen, negative = 1;
while ((*format >= '0' && *format <= '9')
|| *format == '-' || *format == ' ' || *format == '.')
if (STRINGP (args[n]))
{
int padding, nbytes;
+ int width = strwidth (XSTRING (args[n])->data,
+ STRING_BYTES (XSTRING (args[n])));
+ /* If spec requires it, pad on right with spaces. */
+ padding = minlen - width;
+ if (! negative)
+ while (padding-- > 0)
+ {
+ *p++ = ' ';
+ 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,
- XSTRING (args[n])->size_byte,
+ STRING_BYTES (XSTRING (args[n])),
STRING_MULTIBYTE (args[n]), multibyte);
p += nbytes;
nchars += XSTRING (args[n])->size;
- /* If spec requires it, pad on right with spaces. */
- padding = minlen - XSTRING (args[n])->size;
- while (padding-- > 0)
- {
- *p++ = ' ';
- nchars++;
- }
+ if (negative)
+ while (padding-- > 0)
+ {
+ *p++ = ' ';
+ nchars++;
+ }
}
else if (INTEGERP (args[n]) || FLOATP (args[n]))
{
format - this_format_start);
this_format[format - this_format_start] = 0;
- sprintf (p, this_format, XINT (args[n]));
+ if (INTEGERP (args[n]))
+ sprintf (p, this_format, XINT (args[n]));
+ 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;
}
}
- else if (multibyte && !STRING_MULTIBYTE (args[0]))
+ 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++;
+ }
+ else if (multibyte)
{
/* Convert a single-byte character to multibyte. */
int len = copy_text (format, p, 1, 0, 1);
*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. */
if (total >= 1000)
xfree (buf);
- return make_multibyte_string (buf, nchars, p - buf);
+ return val;
}
/* VARARGS 1 */
args[2] = arg2;
args[3] = arg3;
args[4] = arg4;
- doprnt (buf, sizeof buf, string1, (char *)0, 5, args);
+ doprnt (buf, sizeof buf, string1, (char *)0, 5, (char **) args);
#else
doprnt (buf, sizeof buf, string1, (char *)0, 5, &string1 + 1);
#endif
int start1_byte, start2_byte, len1_byte, len2_byte;
int gap, len1, len_mid, len2;
unsigned char *start1_addr, *start2_addr, *temp;
+ int combined_before_bytes_1, combined_after_bytes_1;
+ int combined_before_bytes_2, combined_after_bytes_2;
+ struct gcpro gcpro1, gcpro2;
#ifdef USE_TEXT_PROPERTIES
INTERVAL cur_intv, tmp_interval1, tmp_interval_mid, tmp_interval2;
len2 = end2 - start2;
if (start2 < end1)
- error ("Transposed regions not properly ordered");
+ error ("Transposed regions overlap");
else if (start1 == end1 || start2 == end2)
- error ("Transposed region may not be of length 0");
+ error ("Transposed region has length 0");
/* The possibilities are:
1. Adjacent (contiguous) regions, or separate but equal regions
start2_byte = CHAR_TO_BYTE (start2);
len1_byte = CHAR_TO_BYTE (end1) - start1_byte;
len2_byte = CHAR_TO_BYTE (end2) - start2_byte;
+
+ if (end1 == start2)
+ {
+ combined_before_bytes_2
+ = count_combining_before (BYTE_POS_ADDR (start2_byte),
+ len2_byte, start1, start1_byte);
+ combined_before_bytes_1
+ = count_combining_before (BYTE_POS_ADDR (start1_byte),
+ len1_byte, end2, start2_byte + len2_byte);
+ combined_after_bytes_1
+ = count_combining_after (BYTE_POS_ADDR (start1_byte),
+ len1_byte, end2, start2_byte + len2_byte);
+ combined_after_bytes_2 = 0;
+ }
+ else
+ {
+ combined_before_bytes_2
+ = count_combining_before (BYTE_POS_ADDR (start2_byte),
+ len2_byte, start1, start1_byte);
+ combined_before_bytes_1
+ = count_combining_before (BYTE_POS_ADDR (start1_byte),
+ len1_byte, start2, start2_byte);
+ combined_after_bytes_2
+ = count_combining_after (BYTE_POS_ADDR (start2_byte),
+ len2_byte, end1, start1_byte + len1_byte);
+ combined_after_bytes_1
+ = count_combining_after (BYTE_POS_ADDR (start1_byte),
+ len1_byte, end2, start2_byte + len2_byte);
+ }
+
+ /* If any combining is going to happen, do this the stupid way,
+ because replace handles combining properly. */
+ if (combined_before_bytes_1 || combined_before_bytes_2
+ || combined_after_bytes_1 || combined_after_bytes_2)
+ {
+ Lisp_Object text1, text2;
+
+ text1 = text2 = Qnil;
+ GCPRO2 (text1, text2);
+
+ text1 = make_buffer_string_both (start1, start1_byte,
+ end1, start1_byte + len1_byte, 1);
+ text2 = make_buffer_string_both (start2, start2_byte,
+ end2, start2_byte + len2_byte, 1);
+
+ transpose_markers (start1, end1, start2, end2,
+ start1_byte, start1_byte + len1_byte,
+ start2_byte, start2_byte + len2_byte);
+
+ replace_range (start2, end2, text1, 1, 0, 0);
+ replace_range (start1, end1, text2, 1, 0, 0);
+
+ UNGCPRO;
+ return Qnil;
+ }
/* Hmmm... how about checking to see if the gap is large
enough to use as the temporary storage? That would avoid an
/* 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);
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);
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);
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);
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);
defsubr (&Spoint);
defsubr (&Sregion_beginning);
defsubr (&Sregion_end);
+
+ defsubr (&Sline_beginning_position);
+ defsubr (&Sline_end_position);
+
/* defsubr (&Smark); */
/* defsubr (&Sset_mark); */
defsubr (&Ssave_excursion);
defsubr (&Spoint_min);
defsubr (&Spoint_min_marker);
defsubr (&Spoint_max_marker);
-
- defsubr (&Sline_beginning_position);
- defsubr (&Sline_end_position);
+ defsubr (&Sgap_position);
+ defsubr (&Sgap_size);
+ defsubr (&Sposition_bytes);
+ defsubr (&Sbyte_to_position);
defsubr (&Sbobp);
defsubr (&Seobp);