#include <config.h>
#include "lisp.h"
#include "buffer.h"
#include "charset.h"
static int cached_charpos;
static int cached_bytepos;
static struct buffer *cached_buffer;
static int cached_modiff;
static int byte_debug_flag;
void
clear_charpos_cache (b)
struct buffer *b;
{
if (cached_buffer == b)
cached_buffer = 0;
}
#define CONSIDER(CHARPOS, BYTEPOS) \
{ \
int this_charpos = (CHARPOS); \
int changed = 0; \
\
if (this_charpos == charpos) \
{ \
int value = (BYTEPOS); \
if (byte_debug_flag) \
byte_char_debug_check (b, charpos, value); \
return value; \
} \
else if (this_charpos > charpos) \
{ \
if (this_charpos < best_above) \
{ \
best_above = this_charpos; \
best_above_byte = (BYTEPOS); \
changed = 1; \
} \
} \
else if (this_charpos > best_below) \
{ \
best_below = this_charpos; \
best_below_byte = (BYTEPOS); \
changed = 1; \
} \
\
if (changed) \
{ \
if (best_above - best_below == best_above_byte - best_below_byte) \
{ \
int value = best_below_byte + (charpos - best_below); \
if (byte_debug_flag) \
byte_char_debug_check (b, charpos, value); \
return value; \
} \
} \
}
int
byte_char_debug_check (b, charpos, bytepos)
struct buffer *b;
int charpos, bytepos;
{
int nchars = 0;
if (bytepos > BUF_GPT_BYTE (b))
{
nchars = multibyte_chars_in_text (BUF_BEG_ADDR (b),
BUF_GPT_BYTE (b) - BUF_BEG_BYTE (b));
nchars += multibyte_chars_in_text (BUF_GAP_END_ADDR (b),
bytepos - BUF_GPT_BYTE (b));
}
else
nchars = multibyte_chars_in_text (BUF_BEG_ADDR (b),
bytepos - BUF_BEG_BYTE (b));
if (charpos - 1 != nchars)
abort ();
}
int
charpos_to_bytepos (charpos)
int charpos;
{
return buf_charpos_to_bytepos (current_buffer, charpos);
}
int
buf_charpos_to_bytepos (b, charpos)
struct buffer *b;
int charpos;
{
Lisp_Object tail;
int best_above, best_above_byte;
int best_below, best_below_byte;
if (charpos < BUF_BEG (b) || charpos > BUF_Z (b))
abort ();
best_above = BUF_Z (b);
best_above_byte = BUF_Z_BYTE (b);
if (best_above == best_above_byte)
return charpos;
best_below = 1;
best_below_byte = 1;
CONSIDER (BUF_PT (b), BUF_PT_BYTE (b));
CONSIDER (BUF_GPT (b), BUF_GPT_BYTE (b));
CONSIDER (BUF_BEGV (b), BUF_BEGV_BYTE (b));
CONSIDER (BUF_ZV (b), BUF_ZV_BYTE (b));
if (b == cached_buffer && BUF_MODIFF (b) == cached_modiff)
CONSIDER (cached_charpos, cached_bytepos);
tail = BUF_MARKERS (b);
while (XSYMBOL (tail) != XSYMBOL (Qnil))
{
CONSIDER (XMARKER (tail)->charpos, XMARKER (tail)->bytepos);
if (best_above - best_below < 50)
break;
tail = XMARKER (tail)->chain;
}
if (charpos - best_below < best_above - charpos)
{
int record = charpos - best_below > 5000;
while (best_below != charpos)
{
best_below++;
BUF_INC_POS (b, best_below_byte);
}
if (record)
{
Lisp_Object marker, buffer;
marker = Fmake_marker ();
XSETBUFFER (buffer, b);
set_marker_both (marker, buffer, best_below, best_below_byte);
}
if (byte_debug_flag)
byte_char_debug_check (b, charpos, best_below_byte);
cached_buffer = b;
cached_modiff = BUF_MODIFF (b);
cached_charpos = best_below;
cached_bytepos = best_below_byte;
return best_below_byte;
}
else
{
int record = best_above - charpos > 5000;
while (best_above != charpos)
{
best_above--;
BUF_DEC_POS (b, best_above_byte);
}
if (record)
{
Lisp_Object marker, buffer;
marker = Fmake_marker ();
XSETBUFFER (buffer, b);
set_marker_both (marker, buffer, best_above, best_above_byte);
}
if (byte_debug_flag)
byte_char_debug_check (b, charpos, best_above_byte);
cached_buffer = b;
cached_modiff = BUF_MODIFF (b);
cached_charpos = best_above;
cached_bytepos = best_above_byte;
return best_above_byte;
}
}
#undef CONSIDER
#define CONSIDER(BYTEPOS, CHARPOS) \
{ \
int this_bytepos = (BYTEPOS); \
int changed = 0; \
\
if (this_bytepos == bytepos) \
{ \
int value = (CHARPOS); \
if (byte_debug_flag) \
byte_char_debug_check (b, value, bytepos); \
return value; \
} \
else if (this_bytepos > bytepos) \
{ \
if (this_bytepos < best_above_byte) \
{ \
best_above = (CHARPOS); \
best_above_byte = this_bytepos; \
changed = 1; \
} \
} \
else if (this_bytepos > best_below_byte) \
{ \
best_below = (CHARPOS); \
best_below_byte = this_bytepos; \
changed = 1; \
} \
\
if (changed) \
{ \
if (best_above - best_below == best_above_byte - best_below_byte) \
{ \
int value = best_below + (bytepos - best_below_byte); \
if (byte_debug_flag) \
byte_char_debug_check (b, value, bytepos); \
return value; \
} \
} \
}
int
bytepos_to_charpos (bytepos)
int bytepos;
{
return buf_bytepos_to_charpos (current_buffer, bytepos);
}
int
buf_bytepos_to_charpos (b, bytepos)
struct buffer *b;
int bytepos;
{
Lisp_Object tail;
int best_above, best_above_byte;
int best_below, best_below_byte;
if (bytepos < BUF_BEG_BYTE (b) || bytepos > BUF_Z_BYTE (b))
abort ();
best_above = BUF_Z (b);
best_above_byte = BUF_Z_BYTE (b);
if (best_above == best_above_byte)
return bytepos;
best_below = 1;
best_below_byte = 1;
CONSIDER (BUF_PT_BYTE (b), BUF_PT (b));
CONSIDER (BUF_GPT_BYTE (b), BUF_GPT (b));
CONSIDER (BUF_BEGV_BYTE (b), BUF_BEGV (b));
CONSIDER (BUF_ZV_BYTE (b), BUF_ZV (b));
if (b == cached_buffer && BUF_MODIFF (b) == cached_modiff)
CONSIDER (cached_bytepos, cached_charpos);
tail = BUF_MARKERS (b);
while (XSYMBOL (tail) != XSYMBOL (Qnil))
{
CONSIDER (XMARKER (tail)->bytepos, XMARKER (tail)->charpos);
if (best_above - best_below < 50)
break;
tail = XMARKER (tail)->chain;
}
if (bytepos - best_below_byte < best_above_byte - bytepos)
{
int record = bytepos - best_below_byte > 5000;
while (best_below_byte < bytepos)
{
best_below++;
BUF_INC_POS (b, best_below_byte);
}
if (record && ! NILP (BUF_MARKERS (b)))
{
Lisp_Object marker, buffer;
marker = Fmake_marker ();
XSETBUFFER (buffer, b);
set_marker_both (marker, buffer, best_below, best_below_byte);
}
if (byte_debug_flag)
byte_char_debug_check (b, best_below, bytepos);
cached_buffer = b;
cached_modiff = BUF_MODIFF (b);
cached_charpos = best_below;
cached_bytepos = best_below_byte;
return best_below;
}
else
{
int record = best_above_byte - bytepos > 5000;
while (best_above_byte > bytepos)
{
best_above--;
BUF_DEC_POS (b, best_above_byte);
}
if (record && ! NILP (BUF_MARKERS (b)))
{
Lisp_Object marker, buffer;
marker = Fmake_marker ();
XSETBUFFER (buffer, b);
set_marker_both (marker, buffer, best_above, best_above_byte);
}
if (byte_debug_flag)
byte_char_debug_check (b, best_above, bytepos);
cached_buffer = b;
cached_modiff = BUF_MODIFF (b);
cached_charpos = best_above;
cached_bytepos = best_above_byte;
return best_above;
}
}
#undef CONSIDER
DEFUN ("marker-buffer", Fmarker_buffer, Smarker_buffer, 1, 1, 0,
"Return the buffer that MARKER points into, or nil if none.\n\
Returns nil if MARKER points into a dead buffer.")
(marker)
register Lisp_Object marker;
{
register Lisp_Object buf;
CHECK_MARKER (marker, 0);
if (XMARKER (marker)->buffer)
{
XSETBUFFER (buf, XMARKER (marker)->buffer);
if (!NILP (XBUFFER (buf)->name))
return buf;
}
return Qnil;
}
DEFUN ("marker-position", Fmarker_position, Smarker_position, 1, 1, 0,
"Return the position MARKER points at, as a character number.")
(marker)
Lisp_Object marker;
{
register Lisp_Object pos;
register int i;
register struct buffer *buf;
CHECK_MARKER (marker, 0);
if (XMARKER (marker)->buffer)
return make_number (XMARKER (marker)->charpos);
return Qnil;
}
DEFUN ("set-marker", Fset_marker, Sset_marker, 2, 3, 0,
"Position MARKER before character number POSITION in BUFFER.\n\
BUFFER defaults to the current buffer.\n\
If POSITION is nil, makes marker point nowhere.\n\
Then it no longer slows down editing in any buffer.\n\
Returns MARKER.")
(marker, position, buffer)
Lisp_Object marker, position, buffer;
{
register int charno, bytepos;
register struct buffer *b;
register struct Lisp_Marker *m;
CHECK_MARKER (marker, 0);
if (NILP (position)
|| (MARKERP (position) && !XMARKER (position)->buffer))
{
unchain_marker (marker);
return marker;
}
if (NILP (buffer))
b = current_buffer;
else
{
CHECK_BUFFER (buffer, 1);
b = XBUFFER (buffer);
if (EQ (b->name, Qnil))
{
unchain_marker (marker);
return marker;
}
}
m = XMARKER (marker);
if (MARKERP (position) && b == XMARKER (position)->buffer
&& b == m->buffer)
{
m->bytepos = XMARKER (position)->bytepos;
m->charpos = XMARKER (position)->charpos;
return marker;
}
CHECK_NUMBER_COERCE_MARKER (position, 1);
charno = XINT (position);
if (charno < BUF_BEG (b))
charno = BUF_BEG (b);
if (charno > BUF_Z (b))
charno = BUF_Z (b);
bytepos = buf_charpos_to_bytepos (b, charno);
if (charno > bytepos)
abort ();
m->bytepos = bytepos;
m->charpos = charno;
if (m->buffer != b)
{
unchain_marker (marker);
m->buffer = b;
m->chain = BUF_MARKERS (b);
BUF_MARKERS (b) = marker;
}
return marker;
}
Lisp_Object
set_marker_restricted (marker, pos, buffer)
Lisp_Object marker, pos, buffer;
{
register int charno, bytepos;
register struct buffer *b;
register struct Lisp_Marker *m;
CHECK_MARKER (marker, 0);
if (NILP (pos)
|| (MARKERP (pos) && !XMARKER (pos)->buffer))
{
unchain_marker (marker);
return marker;
}
if (NILP (buffer))
b = current_buffer;
else
{
CHECK_BUFFER (buffer, 1);
b = XBUFFER (buffer);
if (EQ (b->name, Qnil))
{
unchain_marker (marker);
return marker;
}
}
m = XMARKER (marker);
if (MARKERP (pos) && b == XMARKER (pos)->buffer
&& b == m->buffer)
{
m->bytepos = XMARKER (pos)->bytepos;
m->charpos = XMARKER (pos)->charpos;
return marker;
}
CHECK_NUMBER_COERCE_MARKER (pos, 1);
charno = XINT (pos);
if (charno < BUF_BEGV (b))
charno = BUF_BEGV (b);
if (charno > BUF_ZV (b))
charno = BUF_ZV (b);
bytepos = buf_charpos_to_bytepos (b, charno);
if (charno > bytepos)
abort ();
m->bytepos = bytepos;
m->charpos = charno;
if (m->buffer != b)
{
unchain_marker (marker);
m->buffer = b;
m->chain = BUF_MARKERS (b);
BUF_MARKERS (b) = marker;
}
return marker;
}
Lisp_Object
set_marker_both (marker, buffer, charpos, bytepos)
Lisp_Object marker, buffer;
int charpos, bytepos;
{
register struct buffer *b;
register struct Lisp_Marker *m;
CHECK_MARKER (marker, 0);
if (NILP (buffer))
b = current_buffer;
else
{
CHECK_BUFFER (buffer, 1);
b = XBUFFER (buffer);
if (EQ (b->name, Qnil))
{
unchain_marker (marker);
return marker;
}
}
m = XMARKER (marker);
if (BUF_Z (b) == BUF_Z_BYTE (b)
&& charpos != bytepos)
abort ();
if (charpos > bytepos)
abort ();
m->bytepos = bytepos;
m->charpos = charpos;
if (m->buffer != b)
{
unchain_marker (marker);
m->buffer = b;
m->chain = BUF_MARKERS (b);
BUF_MARKERS (b) = marker;
}
return marker;
}
Lisp_Object
set_marker_restricted_both (marker, buffer, charpos, bytepos)
Lisp_Object marker, buffer;
int charpos, bytepos;
{
register struct buffer *b;
register struct Lisp_Marker *m;
CHECK_MARKER (marker, 0);
if (NILP (buffer))
b = current_buffer;
else
{
CHECK_BUFFER (buffer, 1);
b = XBUFFER (buffer);
if (EQ (b->name, Qnil))
{
unchain_marker (marker);
return marker;
}
}
m = XMARKER (marker);
if (charpos < BUF_BEGV (b))
charpos = BUF_BEGV (b);
if (charpos > BUF_ZV (b))
charpos = BUF_ZV (b);
if (bytepos < BUF_BEGV_BYTE (b))
bytepos = BUF_BEGV_BYTE (b);
if (bytepos > BUF_ZV_BYTE (b))
bytepos = BUF_ZV_BYTE (b);
if (BUF_Z (b) == BUF_Z_BYTE (b)
&& charpos != bytepos)
abort ();
if (charpos > bytepos)
abort ();
m->bytepos = bytepos;
m->charpos = charpos;
if (m->buffer != b)
{
unchain_marker (marker);
m->buffer = b;
m->chain = BUF_MARKERS (b);
BUF_MARKERS (b) = marker;
}
return marker;
}
void
unchain_marker (marker)
register Lisp_Object marker;
{
register Lisp_Object tail, prev, next;
register EMACS_INT omark;
register struct buffer *b;
b = XMARKER (marker)->buffer;
if (b == 0)
return;
if (EQ (b->name, Qnil))
abort ();
XMARKER (marker)->buffer = 0;
tail = BUF_MARKERS (b);
prev = Qnil;
while (XSYMBOL (tail) != XSYMBOL (Qnil))
{
next = XMARKER (tail)->chain;
XUNMARK (next);
if (XMARKER (marker) == XMARKER (tail))
{
if (NILP (prev))
{
BUF_MARKERS (b) = next;
if (!NILP (next) && b->text != XMARKER (next)->buffer->text)
abort ();
}
else
{
omark = XMARKBIT (XMARKER (prev)->chain);
XMARKER (prev)->chain = next;
XSETMARKBIT (XMARKER (prev)->chain, omark);
}
return;
}
else
prev = tail;
tail = next;
}
abort ();
}
int
marker_position (marker)
Lisp_Object marker;
{
register struct Lisp_Marker *m = XMARKER (marker);
register struct buffer *buf = m->buffer;
if (!buf)
error ("Marker does not point anywhere");
return m->charpos;
}
int
marker_byte_position (marker)
Lisp_Object marker;
{
register struct Lisp_Marker *m = XMARKER (marker);
register struct buffer *buf = m->buffer;
register int i = m->bytepos;
if (!buf)
error ("Marker does not point anywhere");
if (i < BUF_BEG_BYTE (buf) || i > BUF_Z_BYTE (buf))
abort ();
return i;
}
DEFUN ("copy-marker", Fcopy_marker, Scopy_marker, 1, 2, 0,
"Return a new marker pointing at the same place as MARKER.\n\
If argument is a number, makes a new marker pointing\n\
at that position in the current buffer.\n\
The optional argument TYPE specifies the insertion type of the new marker;\n\
see `marker-insertion-type'.")
(marker, type)
register Lisp_Object marker, type;
{
register Lisp_Object new;
if (! (INTEGERP (marker) || MARKERP (marker)))
marker = wrong_type_argument (Qinteger_or_marker_p, marker);
new = Fmake_marker ();
Fset_marker (new, marker,
(MARKERP (marker) ? Fmarker_buffer (marker) : Qnil));
XMARKER (new)->insertion_type = !NILP (type);
return new;
}
DEFUN ("marker-insertion-type", Fmarker_insertion_type,
Smarker_insertion_type, 1, 1, 0,
"Return insertion type of MARKER: t if it stays after inserted text.\n\
nil means the marker stays before text inserted there.")
(marker)
register Lisp_Object marker;
{
register Lisp_Object buf;
CHECK_MARKER (marker, 0);
return XMARKER (marker)->insertion_type ? Qt : Qnil;
}
DEFUN ("set-marker-insertion-type", Fset_marker_insertion_type,
Sset_marker_insertion_type, 2, 2, 0,
"Set the insertion-type of MARKER to TYPE.\n\
If TYPE is t, it means the marker advances when you insert text at it.\n\
If TYPE is nil, it means the marker stays behind when you insert text at it.")
(marker, type)
Lisp_Object marker, type;
{
CHECK_MARKER (marker, 0);
XMARKER (marker)->insertion_type = ! NILP (type);
return type;
}
DEFUN ("buffer-has-markers-at", Fbuffer_has_markers_at, Sbuffer_has_markers_at,
1, 1, 0,
"Return t if there are markers pointing at POSITION in the current buffer.")
(position)
Lisp_Object position;
{
register Lisp_Object tail;
register int charno;
charno = XINT (position);
if (charno < BEG)
charno = BEG;
if (charno > Z)
charno = Z;
for (tail = BUF_MARKERS (current_buffer);
!NILP (tail);
tail = XMARKER (tail)->chain)
if (XMARKER (tail)->charpos == charno)
return Qt;
return Qnil;
}
void
syms_of_marker ()
{
defsubr (&Smarker_position);
defsubr (&Smarker_buffer);
defsubr (&Sset_marker);
defsubr (&Scopy_marker);
defsubr (&Smarker_insertion_type);
defsubr (&Sset_marker_insertion_type);
defsubr (&Sbuffer_has_markers_at);
DEFVAR_BOOL ("byte-debug-flag", &byte_debug_flag,
"Non-nil enables debugging checks in byte/char position conversions.");
byte_debug_flag = 0;
}