}
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\
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))
- 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)
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 = PT_BYTE;
+ pos = PT;
+ }
+
+ if (MARKERP (pos))
{
pos_byte = marker_byte_position (pos);
}
}
+/* 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);
/* 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;
}
}
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))
\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;
\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;
\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;
\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;
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
{
stop = min (stop, GPT_BYTE);
while (1)
{
+ int pos_byte_next = pos_byte;
+
if (pos_byte >= stop)
{
if (pos_byte >= end_byte) break;
stop = end_byte;
}
p = BYTE_POS_ADDR (pos_byte);
- if (p[0] == fromstr[0]
+ 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, 1);
- for (i = 0; i < len; i++) *p++ = tostr[i];
+ /* 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_byte = pos_byte_next;
+ pos++;
}
- INC_BOTH (pos, pos_byte);
}
if (changed)
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\
/* 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;
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)
{
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
+ 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]))
{
if (! (*format == 'e' || *format == 'f' || *format == 'g'))
args[n] = Ftruncate (args[n], Qnil);
- thissize = 60;
+ thissize = 200;
}
#endif
else
/* 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 == '%')
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);
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 (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++;
*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. */
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;
/* 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 (&Sgap_position);
defsubr (&Sgap_size);
defsubr (&Sposition_bytes);
+ defsubr (&Sbyte_to_position);
defsubr (&Sbobp);
defsubr (&Seobp);