Mercurial > hg > xemacs-beta
changeset 5607:1a507c4c6c42
Refactor out sequence-oriented builtins from fns.c to the new sequence.c.
src/ChangeLog addition:
2011-12-04 Aidan Kehoe <kehoea@parhasard.net>
* Makefile.in.in (objs):
* depend:
Add sequence.o to the list of objects and dependencies.
* alloc.c:
* alloc.c (mark_bit_vector):
* alloc.c (print_bit_vector):
* alloc.c (bit_vector_equal):
* alloc.c (internal_bit_vector_equalp_hash):
* alloc.c (bit_vector_hash):
* alloc.c (init_alloc_once_early):
Move the implementation of the bit vector type here from fns.c.
* emacs.c (main_1):
Call syms_of_sequence() here, now sequence.c is included.
* fns.c (Fold_rassq):
Move this together with the rest of the Fold_* functions.
* fns.c:
* fns.c (syms_of_fns):
Move most functions dealing with sequences generally, and
especially those taking key arguments, to a separate file,
sequence.c.
* general-slots.h:
Qyes_or_no_p belong here, not fns.c.
* lisp.h:
Make Flist_length available here, it's used by sequence.c
* sequence.c:
* sequence.c (check_sequence_range):
* sequence.c (Flength):
* sequence.c (check_other_nokey):
* sequence.c (check_other_key):
* sequence.c (check_if_key):
* sequence.c (check_match_eq_key):
* sequence.c (check_match_eql_key):
* sequence.c (check_match_equal_key):
* sequence.c (check_match_equalp_key):
* sequence.c (check_match_other_key):
* sequence.c (check_lss_key):
* sequence.c (check_lss_key_car):
* sequence.c (check_string_lessp_key):
* sequence.c (check_string_lessp_key_car):
* sequence.c (get_check_match_function_1):
* sequence.c (get_merge_predicate):
* sequence.c (count_with_tail):
* sequence.c (list_count_from_end):
* sequence.c (string_count_from_end):
* sequence.c (Fcount):
* sequence.c (Fsubseq):
* sequence.c (list_position_cons_before):
* sequence.c (FmemberX):
* sequence.c (Fadjoin):
* sequence.c (FassocX):
* sequence.c (FrassocX):
* sequence.c (position):
* sequence.c (Fposition):
* sequence.c (Ffind):
* sequence.c (delq_no_quit_and_free_cons):
* sequence.c (FdeleteX):
* sequence.c (FremoveX):
* sequence.c (list_delete_duplicates_from_end):
* sequence.c (Fdelete_duplicates):
* sequence.c (Fremove_duplicates):
* sequence.c (Fnreverse):
* sequence.c (Freverse):
* sequence.c (list_merge):
* sequence.c (array_merge):
* sequence.c (list_array_merge_into_list):
* sequence.c (list_list_merge_into_array):
* sequence.c (list_array_merge_into_array):
* sequence.c (Fmerge):
* sequence.c (list_sort):
* sequence.c (array_sort):
* sequence.c (FsortX):
* sequence.c (Ffill):
* sequence.c (mapcarX):
* sequence.c (shortest_length_among_sequences):
* sequence.c (Fmapconcat):
* sequence.c (FmapcarX):
* sequence.c (Fmapvector):
* sequence.c (Fmapcan):
* sequence.c (Fmap):
* sequence.c (Fmap_into):
* sequence.c (Fsome):
* sequence.c (Fevery):
* sequence.c (Freduce):
* sequence.c (replace_string_range_1):
* sequence.c (Freplace):
* sequence.c (Fnsubstitute):
* sequence.c (Fsubstitute):
* sequence.c (subst):
* sequence.c (sublis):
* sequence.c (Fsublis):
* sequence.c (nsublis):
* sequence.c (Fnsublis):
* sequence.c (Fsubst):
* sequence.c (Fnsubst):
* sequence.c (tree_equal):
* sequence.c (Ftree_equal):
* sequence.c (mismatch_from_end):
* sequence.c (mismatch_list_list):
* sequence.c (mismatch_list_string):
* sequence.c (mismatch_list_array):
* sequence.c (mismatch_string_array):
* sequence.c (mismatch_string_string):
* sequence.c (mismatch_array_array):
* sequence.c (get_mismatch_func):
* sequence.c (Fmismatch):
* sequence.c (Fsearch):
* sequence.c (venn):
* sequence.c (nvenn):
* sequence.c (Funion):
* sequence.c (Fset_exclusive_or):
* sequence.c (Fnset_exclusive_or):
* sequence.c (syms_of_sequence):
Add this file, containing those general functions that dealt with
sequences that were in fns.c.
* symsinit.h:
Make syms_of_sequence() available here.
man/ChangeLog addition:
2011-12-04 Aidan Kehoe <kehoea@parhasard.net>
* internals/internals.texi (Basic Lisp Modules):
Document sequence.c here too.
author | Aidan Kehoe <kehoea@parhasard.net> |
---|---|
date | Sun, 04 Dec 2011 18:42:50 +0000 |
parents | 7c383c5784ed |
children | 4cffcc80b299 |
files | man/ChangeLog man/internals/internals.texi src/ChangeLog src/Makefile.in.in src/alloc.c src/depend src/emacs.c src/fns.c src/general-slots.h src/lisp.h src/sequence.c src/symsinit.h |
diffstat | 12 files changed, 8568 insertions(+), 8404 deletions(-) [+] |
line wrap: on
line diff
--- a/man/ChangeLog Sat Dec 03 15:55:14 2011 +0000 +++ b/man/ChangeLog Sun Dec 04 18:42:50 2011 +0000 @@ -1,3 +1,8 @@ +2011-12-04 Aidan Kehoe <kehoea@parhasard.net> + + * internals/internals.texi (Basic Lisp Modules): + Document sequence.c here too. + 2011-10-09 Aidan Kehoe <kehoea@parhasard.net> * lispref/lists.texi (Association Lists):
--- a/man/internals/internals.texi Sat Dec 03 15:55:14 2011 +0000 +++ b/man/internals/internals.texi Sun Dec 04 18:42:50 2011 +0000 @@ -3211,6 +3211,7 @@ @item @file{select-x.c} @tab @ref{Modules for Interfacing with X Windows}. @item @file{select.c} @tab @ref{Modules for Interfacing with X Windows}. @item @file{select.h} @tab @ref{Modules for Interfacing with X Windows}. +@item @file{sequence.c} @tab @ref{Basic Lisp Modules}. @item @file{sgiplay.c} @tab @ref{Modules for Interfacing with the Operating System}. @item @file{sheap.c} @tab @item @file{signal.c} @tab @ref{Low-Level Modules}. @@ -3634,6 +3635,7 @@ @file{data.c} @file{floatfns.c} @file{fns.c} +@file{sequence.c} @end example These modules implement the methods and standard Lisp primitives for all @@ -3641,11 +3643,11 @@ above). @file{data.c} contains all the predicates (primitives that return whether an object is of a particular type); the integer arithmetic functions; and the basic accessor and mutator primitives for the various -object types. @file{fns.c} contains all the standard predicates for working +object types. @file{sequence.c} contains all the built-in functions for working with sequences (where, abstractly speaking, a sequence is an ordered set of objects, and can be represented by a list, string, vector, or -bit-vector); it also contains @code{equal}, perhaps on the grounds that -bulk of the operation of @code{equal} is comparing sequences. +bit-vector). @file{fns.c} contains @code{equal} and a grab-bag of other +functions that should probably be refactored elsewhere. @file{floatfns.c} contains methods and primitives for floats and floating-point arithmetic.
--- a/src/ChangeLog Sat Dec 03 15:55:14 2011 +0000 +++ b/src/ChangeLog Sun Dec 04 18:42:50 2011 +0000 @@ -1,3 +1,129 @@ +2011-12-04 Aidan Kehoe <kehoea@parhasard.net> + + * Makefile.in.in (objs): + * depend: + Add sequence.o to the list of objects and dependencies. + + * alloc.c: + * alloc.c (mark_bit_vector): + * alloc.c (print_bit_vector): + * alloc.c (bit_vector_equal): + * alloc.c (internal_bit_vector_equalp_hash): + * alloc.c (bit_vector_hash): + * alloc.c (init_alloc_once_early): + Move the implementation of the bit vector type here from fns.c. + + * emacs.c (main_1): + Call syms_of_sequence() here, now sequence.c is included. + + * fns.c (Fold_rassq): + Move this together with the rest of the Fold_* functions. + * fns.c: + * fns.c (syms_of_fns): + Move most functions dealing with sequences generally, and + especially those taking key arguments, to a separate file, + sequence.c. + + * general-slots.h: + Qyes_or_no_p belong here, not fns.c. + + * lisp.h: + Make Flist_length available here, it's used by sequence.c + + * sequence.c: + * sequence.c (check_sequence_range): + * sequence.c (Flength): + * sequence.c (check_other_nokey): + * sequence.c (check_other_key): + * sequence.c (check_if_key): + * sequence.c (check_match_eq_key): + * sequence.c (check_match_eql_key): + * sequence.c (check_match_equal_key): + * sequence.c (check_match_equalp_key): + * sequence.c (check_match_other_key): + * sequence.c (check_lss_key): + * sequence.c (check_lss_key_car): + * sequence.c (check_string_lessp_key): + * sequence.c (check_string_lessp_key_car): + * sequence.c (get_check_match_function_1): + * sequence.c (get_merge_predicate): + * sequence.c (count_with_tail): + * sequence.c (list_count_from_end): + * sequence.c (string_count_from_end): + * sequence.c (Fcount): + * sequence.c (Fsubseq): + * sequence.c (list_position_cons_before): + * sequence.c (FmemberX): + * sequence.c (Fadjoin): + * sequence.c (FassocX): + * sequence.c (FrassocX): + * sequence.c (position): + * sequence.c (Fposition): + * sequence.c (Ffind): + * sequence.c (delq_no_quit_and_free_cons): + * sequence.c (FdeleteX): + * sequence.c (FremoveX): + * sequence.c (list_delete_duplicates_from_end): + * sequence.c (Fdelete_duplicates): + * sequence.c (Fremove_duplicates): + * sequence.c (Fnreverse): + * sequence.c (Freverse): + * sequence.c (list_merge): + * sequence.c (array_merge): + * sequence.c (list_array_merge_into_list): + * sequence.c (list_list_merge_into_array): + * sequence.c (list_array_merge_into_array): + * sequence.c (Fmerge): + * sequence.c (list_sort): + * sequence.c (array_sort): + * sequence.c (FsortX): + * sequence.c (Ffill): + * sequence.c (mapcarX): + * sequence.c (shortest_length_among_sequences): + * sequence.c (Fmapconcat): + * sequence.c (FmapcarX): + * sequence.c (Fmapvector): + * sequence.c (Fmapcan): + * sequence.c (Fmap): + * sequence.c (Fmap_into): + * sequence.c (Fsome): + * sequence.c (Fevery): + * sequence.c (Freduce): + * sequence.c (replace_string_range_1): + * sequence.c (Freplace): + * sequence.c (Fnsubstitute): + * sequence.c (Fsubstitute): + * sequence.c (subst): + * sequence.c (sublis): + * sequence.c (Fsublis): + * sequence.c (nsublis): + * sequence.c (Fnsublis): + * sequence.c (Fsubst): + * sequence.c (Fnsubst): + * sequence.c (tree_equal): + * sequence.c (Ftree_equal): + * sequence.c (mismatch_from_end): + * sequence.c (mismatch_list_list): + * sequence.c (mismatch_list_string): + * sequence.c (mismatch_list_array): + * sequence.c (mismatch_string_array): + * sequence.c (mismatch_string_string): + * sequence.c (mismatch_array_array): + * sequence.c (get_mismatch_func): + * sequence.c (Fmismatch): + * sequence.c (Fsearch): + * sequence.c (venn): + * sequence.c (nvenn): + * sequence.c (Funion): + * sequence.c (Fset_exclusive_or): + * sequence.c (Fnset_exclusive_or): + * sequence.c (syms_of_sequence): + Add this file, containing those general functions that dealt with + sequences that were in fns.c. + + * symsinit.h: + Make syms_of_sequence() available here. + 2011-12-03 Aidan Kehoe <kehoea@parhasard.net> * lread.c (read1):
--- a/src/Makefile.in.in Sat Dec 03 15:55:14 2011 +0000 +++ b/src/Makefile.in.in Sun Dec 04 18:42:50 2011 +0000 @@ -294,7 +294,7 @@ $(mule_objs) $(mule_canna_objs) $(mule_wnn_objs) $(number_objs) fontcolor.o\ opaque.o $(postgresql_objs) print.o process.o $(process_objs) $(profile_objs)\ rangetab.o realpath.o redisplay.o redisplay-output.o regex.o\ - search.o select.o $(sheap_objs) $(shlib_objs) signal.o sound.o\ + search.o select.o sequence.o $(sheap_objs) $(shlib_objs) signal.o sound.o\ specifier.o strftime.o $(sunpro_objs) symbols.o syntax.o sysdep.o\ text.o $(tooltalk_objs) $(tty_objs) undo.o unicode.o $(x_objs) $(x_gui_objs)\ widget.o window.o $(win32_objs)
--- a/src/alloc.c Sat Dec 03 15:55:14 2011 +0000 +++ b/src/alloc.c Sun Dec 04 18:42:50 2011 +0000 @@ -1987,6 +1987,120 @@ /* Bit Vector allocation */ /************************************************************************/ +static Lisp_Object +mark_bit_vector (Lisp_Object UNUSED (obj)) +{ + return Qnil; +} + +static void +print_bit_vector (Lisp_Object obj, Lisp_Object printcharfun, + int UNUSED (escapeflag)) +{ + Elemcount i; + Lisp_Bit_Vector *v = XBIT_VECTOR (obj); + Elemcount len = bit_vector_length (v); + Elemcount last = len; + + if (FIXNUMP (Vprint_length)) + last = min (len, XFIXNUM (Vprint_length)); + write_ascstring (printcharfun, "#*"); + for (i = 0; i < last; i++) + { + if (bit_vector_bit (v, i)) + write_ascstring (printcharfun, "1"); + else + write_ascstring (printcharfun, "0"); + } + + if (last != len) + write_ascstring (printcharfun, "..."); +} + +static int +bit_vector_equal (Lisp_Object obj1, Lisp_Object obj2, int UNUSED (depth), + int UNUSED (foldcase)) +{ + Lisp_Bit_Vector *v1 = XBIT_VECTOR (obj1); + Lisp_Bit_Vector *v2 = XBIT_VECTOR (obj2); + + return ((bit_vector_length (v1) == bit_vector_length (v2)) && + !memcmp (v1->bits, v2->bits, + BIT_VECTOR_LONG_STORAGE (bit_vector_length (v1)) * + sizeof (long))); +} + +/* This needs to be algorithmically identical to internal_array_hash in + elhash.c when equalp is one, so arrays and bit vectors with the same + contents hash the same. It would be possible to enforce this by giving + internal_ARRAYLIKE_hash its own file and including it twice, but right + now that doesn't seem worth it. */ +static Hashcode +internal_bit_vector_equalp_hash (Lisp_Bit_Vector *v) +{ + int ii, size = bit_vector_length (v); + Hashcode hash = 0; + + if (size <= 5) + { + for (ii = 0; ii < size; ii++) + { + hash = HASH2 + (hash, + FLOAT_HASHCODE_FROM_DOUBLE ((double) (bit_vector_bit (v, ii)))); + } + return hash; + } + + /* just pick five elements scattered throughout the array. + A slightly better approach would be to offset by some + noise factor from the points chosen below. */ + for (ii = 0; ii < 5; ii++) + hash = HASH2 (hash, + FLOAT_HASHCODE_FROM_DOUBLE + ((double) (bit_vector_bit (v, ii * size / 5)))); + + return hash; +} + +static Hashcode +bit_vector_hash (Lisp_Object obj, int UNUSED (depth), Boolint equalp) +{ + Lisp_Bit_Vector *v = XBIT_VECTOR (obj); + if (equalp) + { + return HASH2 (bit_vector_length (v), + internal_bit_vector_equalp_hash (v)); + } + + return HASH2 (bit_vector_length (v), + memory_hash (v->bits, + BIT_VECTOR_LONG_STORAGE (bit_vector_length (v)) * + sizeof (long))); +} + +static Bytecount +size_bit_vector (Lisp_Object obj) +{ + Lisp_Bit_Vector *v = XBIT_VECTOR (obj); + return FLEXIBLE_ARRAY_STRUCT_SIZEOF (Lisp_Bit_Vector, unsigned long, bits, + BIT_VECTOR_LONG_STORAGE (bit_vector_length (v))); +} + +static const struct memory_description bit_vector_description[] = { + { XD_END } +}; + + +DEFINE_DUMPABLE_SIZABLE_LISP_OBJECT ("bit-vector", bit_vector, + mark_bit_vector, + print_bit_vector, 0, + bit_vector_equal, + bit_vector_hash, + bit_vector_description, + size_bit_vector, + Lisp_Bit_Vector); + /* #### should allocate `small' bit vectors from a frob-block */ static Lisp_Bit_Vector * make_bit_vector_internal (Elemcount sizei) @@ -5796,6 +5910,7 @@ INIT_LISP_OBJECT (cons); INIT_LISP_OBJECT (vector); + INIT_LISP_OBJECT (bit_vector); INIT_LISP_OBJECT (string); #ifdef NEW_GC
--- a/src/depend Sat Dec 03 15:55:14 2011 +0000 +++ b/src/depend Sun Dec 04 18:42:50 2011 +0000 @@ -204,7 +204,7 @@ number-mp.o: $(CONFIG_H) $(LISP_H) number.o: $(CONFIG_H) $(LISP_H) opaque.o: $(CONFIG_H) $(LISP_H) opaque.h -print.o: $(CONFIG_H) $(LISP_H) backtrace.h buffer.h bufslots.h bytecode.h casetab.h charset.h chartab.h conslots.h console-impl.h console-msw.h console-stream-impl.h console-stream.h console-tty-impl.h console-tty.h console.h device-impl.h device.h devslots.h extents.h frame.h insdel.h intl-auto-encap-win32.h lstream.h opaque.h redisplay.h specifier.h sysfile.h systty.h syswindows.h +print.o: $(CONFIG_H) $(LISP_H) backtrace.h buffer.h bufslots.h bytecode.h casetab.h charset.h chartab.h conslots.h console-impl.h console-msw.h console-stream-impl.h console-stream.h console-tty-impl.h console-tty.h console.h device-impl.h device.h devslots.h elhash.h extents.h frame.h insdel.h intl-auto-encap-win32.h lstream.h opaque.h redisplay.h specifier.h sysfile.h systty.h syswindows.h process-nt.o: $(CONFIG_H) $(LISP_H) console-msw.h console.h events.h hash.h intl-auto-encap-win32.h keymap-buttons.h lstream.h process-slots.h process.h procimpl.h sysfile.h sysproc.h syssignal.h systime.h syswindows.h process-unix.o: $(CONFIG_H) $(LISP_H) buffer.h bufslots.h casetab.h charset.h chartab.h coding-system-slots.h console.h events.h file-coding.h frame.h hash.h intl-auto-encap-win32.h keymap-buttons.h lstream.h ndir.h opaque.h process-slots.h process.h procimpl.h redisplay.h scrollbar.h sysdep.h sysdir.h sysfile.h sysproc.h syssignal.h systime.h systty.h syswait.h syswindows.h window.h process.o: $(CONFIG_H) $(LISP_H) buffer.h bufslots.h casetab.h charset.h chartab.h coding-system-slots.h commands.h console.h device.h events.h file-coding.h frame.h hash.h insdel.h intl-auto-encap-win32.h keymap-buttons.h lstream.h opaque.h process-slots.h process.h procimpl.h redisplay.h scrollbar.h sysdep.h sysfile.h sysproc.h syssignal.h systime.h systty.h syswait.h syswindows.h window.h @@ -218,6 +218,7 @@ scrollbar.o: $(CONFIG_H) $(LISP_H) buffer.h bufslots.h casetab.h charset.h chartab.h commands.h conslots.h console-impl.h console.h device-impl.h device.h devslots.h frame-impl.h frame.h frameslots.h glyphs.h gutter.h redisplay.h scrollbar.h specifier.h window-impl.h window.h winslots.h search.o: $(CONFIG_H) $(LISP_H) buffer.h bufslots.h casetab.h charset.h chartab.h insdel.h opaque.h regex.h syntax.h select.o: $(CONFIG_H) $(LISP_H) buffer.h bufslots.h casetab.h charset.h chartab.h conslots.h console-impl.h console.h device-impl.h device.h devslots.h extents.h fontcolor.h frame.h opaque.h redisplay.h select.h specifier.h +sequence.o: $(CONFIG_H) $(LISP_H) extents.h sgiplay.o: $(CONFIG_H) $(LISP_H) buffer.h bufslots.h casetab.h charset.h chartab.h intl-auto-encap-win32.h libst.h sound.h sysfile.h sysproc.h syssignal.h systime.h syswindows.h sheap.o: $(CONFIG_H) $(LISP_H) intl-auto-encap-win32.h sheap-adjust.h sysfile.h syswindows.h signal.o: $(CONFIG_H) $(LISP_H) conslots.h console-impl.h console.h device-impl.h device.h devslots.h events.h frame-impl.h frame.h frameslots.h intl-auto-encap-win32.h keymap-buttons.h process.h redisplay.h specifier.h sysdep.h sysfile.h syssignal.h systime.h syswindows.h
--- a/src/emacs.c Sat Dec 03 15:55:14 2011 +0000 +++ b/src/emacs.c Sun Dec 04 18:42:50 2011 +0000 @@ -1547,6 +1547,7 @@ #endif /* CLASH_DETECTION */ syms_of_floatfns (); syms_of_fns (); + syms_of_sequence (); #ifdef USE_C_FONT_LOCK syms_of_font_lock (); #endif /* USE_C_FONT_LOCK */
--- a/src/fns.c Sat Dec 03 15:55:14 2011 +0000 +++ b/src/fns.c Sun Dec 04 18:42:50 2011 +0000 @@ -52,738 +52,12 @@ /* NOTE: This symbol is also used in lread.c */ #define FEATUREP_SYNTAX -Lisp_Object Qstring_lessp, Qmerge, Qfill, Qreplace, QassocX, QrassocX; -Lisp_Object Qposition, Qfind, QdeleteX, QremoveX, Qidentity, Qadjoin; -Lisp_Object Qvector, Qarray, Qbit_vector, QsortX, Q_from_end, Q_initial_value; -Lisp_Object Qmapconcat, QmapcarX, Qmapvector, Qmapcan, Qmapc, Qmap, Qmap_into; -Lisp_Object Qsome, Qevery, Qmaplist, Qmapl, Qmapcon, Qreduce, Qsubstitute; -Lisp_Object Q_start1, Q_start2, Q_end1, Q_end2, Q_if_, Q_if_not, Q_stable; -Lisp_Object Q_test_not, Q_count, Qnsubstitute, Qdelete_duplicates, Qmismatch; -Lisp_Object Q_descend_structures; - -Lisp_Object Qintersection, Qset_difference, Qnset_difference; -Lisp_Object Qnunion, Qnintersection, Qsubsetp, Qcar_less_than_car; - -Lisp_Object Qbase64_conversion_error; - -Lisp_Object Vpath_separator; - extern Fixnum max_lisp_eval_depth; extern int lisp_eval_depth; -Lisp_Object safe_copy_tree (Lisp_Object arg, Lisp_Object vecp, int depth); - -static DOESNT_RETURN -mapping_interaction_error (Lisp_Object func, Lisp_Object object) -{ - invalid_state_2 ("object modified while traversing it", func, object); -} - -static void -check_sequence_range (Lisp_Object sequence, Lisp_Object start, - Lisp_Object end, Lisp_Object length) -{ - Lisp_Object args[] = { Qzero, start, NILP (end) ? length : end, length }; - - if (NILP (Fleq (countof (args), args))) - { - args_out_of_range_3 (sequence, start, end); - } -} - -static Lisp_Object -mark_bit_vector (Lisp_Object UNUSED (obj)) -{ - return Qnil; -} - -static void -print_bit_vector (Lisp_Object obj, Lisp_Object printcharfun, - int UNUSED (escapeflag)) -{ - Elemcount i; - Lisp_Bit_Vector *v = XBIT_VECTOR (obj); - Elemcount len = bit_vector_length (v); - Elemcount last = len; - - if (FIXNUMP (Vprint_length)) - last = min (len, XFIXNUM (Vprint_length)); - write_ascstring (printcharfun, "#*"); - for (i = 0; i < last; i++) - { - if (bit_vector_bit (v, i)) - write_ascstring (printcharfun, "1"); - else - write_ascstring (printcharfun, "0"); - } - - if (last != len) - write_ascstring (printcharfun, "..."); -} - -static int -bit_vector_equal (Lisp_Object obj1, Lisp_Object obj2, int UNUSED (depth), - int UNUSED (foldcase)) -{ - Lisp_Bit_Vector *v1 = XBIT_VECTOR (obj1); - Lisp_Bit_Vector *v2 = XBIT_VECTOR (obj2); - - return ((bit_vector_length (v1) == bit_vector_length (v2)) && - !memcmp (v1->bits, v2->bits, - BIT_VECTOR_LONG_STORAGE (bit_vector_length (v1)) * - sizeof (long))); -} - -/* This needs to be algorithmically identical to internal_array_hash in - elhash.c when equalp is one, so arrays and bit vectors with the same - contents hash the same. It would be possible to enforce this by giving - internal_ARRAYLIKE_hash its own file and including it twice, but right - now that doesn't seem worth it. */ -static Hashcode -internal_bit_vector_equalp_hash (Lisp_Bit_Vector *v) -{ - int ii, size = bit_vector_length (v); - Hashcode hash = 0; - - if (size <= 5) - { - for (ii = 0; ii < size; ii++) - { - hash = HASH2 - (hash, - FLOAT_HASHCODE_FROM_DOUBLE ((double) (bit_vector_bit (v, ii)))); - } - return hash; - } - - /* just pick five elements scattered throughout the array. - A slightly better approach would be to offset by some - noise factor from the points chosen below. */ - for (ii = 0; ii < 5; ii++) - hash = HASH2 (hash, - FLOAT_HASHCODE_FROM_DOUBLE - ((double) (bit_vector_bit (v, ii * size / 5)))); - - return hash; -} - -static Hashcode -bit_vector_hash (Lisp_Object obj, int UNUSED (depth), Boolint equalp) -{ - Lisp_Bit_Vector *v = XBIT_VECTOR (obj); - if (equalp) - { - return HASH2 (bit_vector_length (v), - internal_bit_vector_equalp_hash (v)); - } - - return HASH2 (bit_vector_length (v), - memory_hash (v->bits, - BIT_VECTOR_LONG_STORAGE (bit_vector_length (v)) * - sizeof (long))); -} - -static Bytecount -size_bit_vector (Lisp_Object obj) -{ - Lisp_Bit_Vector *v = XBIT_VECTOR (obj); - return FLEXIBLE_ARRAY_STRUCT_SIZEOF (Lisp_Bit_Vector, unsigned long, bits, - BIT_VECTOR_LONG_STORAGE (bit_vector_length (v))); -} - -static const struct memory_description bit_vector_description[] = { - { XD_END } -}; - - -DEFINE_DUMPABLE_SIZABLE_LISP_OBJECT ("bit-vector", bit_vector, - mark_bit_vector, - print_bit_vector, 0, - bit_vector_equal, - bit_vector_hash, - bit_vector_description, - size_bit_vector, - Lisp_Bit_Vector); - -/* Various test functions for #'member*, #'assoc* and the other functions - that take both TEST and KEY arguments. */ - -Boolint -check_eq_nokey (Lisp_Object UNUSED (test), Lisp_Object UNUSED (key), - Lisp_Object item, Lisp_Object elt) -{ - return EQ (item, elt); -} - -static Boolint -check_eq_key (Lisp_Object UNUSED (test), Lisp_Object key, Lisp_Object item, - Lisp_Object elt) -{ - elt = IGNORE_MULTIPLE_VALUES (call1 (key, elt)); - return EQ (item, elt); -} - -/* The next two are not used by #'member* and #'assoc*, since we can decide - on #'eq vs. #'equal when we have the type of ITEM. */ -static Boolint -check_eql_nokey (Lisp_Object UNUSED (test), Lisp_Object UNUSED (key), - Lisp_Object elt1, Lisp_Object elt2) -{ - return EQ (elt1, elt2) - || (NON_FIXNUM_NUMBER_P (elt1) && internal_equal (elt1, elt2, 0)); -} - -static Boolint -check_eql_key (Lisp_Object UNUSED (test), Lisp_Object key, Lisp_Object item, - Lisp_Object elt) -{ - elt = IGNORE_MULTIPLE_VALUES (call1 (key, elt)); - return EQ (item, elt) - || (NON_FIXNUM_NUMBER_P (item) && internal_equal (item, elt, 0)); -} - -static Boolint -check_equal_nokey (Lisp_Object UNUSED (test), Lisp_Object UNUSED (key), - Lisp_Object item, Lisp_Object elt) -{ - return internal_equal (item, elt, 0); -} - -static Boolint -check_equal_key (Lisp_Object UNUSED (test), Lisp_Object key, Lisp_Object item, - Lisp_Object elt) -{ - elt = IGNORE_MULTIPLE_VALUES (call1 (key, elt)); - return internal_equal (item, elt, 0); -} - -static Boolint -check_equalp_nokey (Lisp_Object UNUSED (test), Lisp_Object UNUSED (key), - Lisp_Object item, Lisp_Object elt) -{ - return internal_equalp (item, elt, 0); -} - -static Boolint -check_equalp_key (Lisp_Object UNUSED (test), Lisp_Object key, - Lisp_Object item, Lisp_Object elt) -{ - elt = IGNORE_MULTIPLE_VALUES (call1 (key, elt)); - return internal_equalp (item, elt, 0); -} - -static Boolint -check_string_match_nokey (Lisp_Object UNUSED (test), Lisp_Object UNUSED (key), - Lisp_Object item, Lisp_Object elt) -{ - return !NILP (Fstring_match (item, elt, Qnil, Qnil)); -} - -static Boolint -check_string_match_key (Lisp_Object UNUSED (test), Lisp_Object key, - Lisp_Object item, Lisp_Object elt) -{ - elt = IGNORE_MULTIPLE_VALUES (call1 (key, elt)); - return !NILP (Fstring_match (item, elt, Qnil, Qnil)); -} - -static Boolint -check_other_nokey (Lisp_Object test, Lisp_Object UNUSED (key), - Lisp_Object item, Lisp_Object elt) -{ - Lisp_Object args[] = { test, item, elt }; - struct gcpro gcpro1; - - GCPRO1 (args[0]); - gcpro1.nvars = countof (args); - item = IGNORE_MULTIPLE_VALUES (Ffuncall (countof (args), args)); - UNGCPRO; - - return !NILP (item); -} - -static Boolint -check_other_key (Lisp_Object test, Lisp_Object key, - Lisp_Object item, Lisp_Object elt) -{ - Lisp_Object args[] = { item, key, elt }; - struct gcpro gcpro1; - - GCPRO1 (args[0]); - gcpro1.nvars = countof (args); - args[2] = IGNORE_MULTIPLE_VALUES (Ffuncall (countof (args) - 1, args + 1)); - args[1] = item; - args[0] = test; - item = IGNORE_MULTIPLE_VALUES (Ffuncall (countof (args), args)); - UNGCPRO; - - return !NILP (item); -} - -static Boolint -check_if_nokey (Lisp_Object test, Lisp_Object UNUSED (key), - Lisp_Object UNUSED (item), Lisp_Object elt) -{ - elt = IGNORE_MULTIPLE_VALUES (call1 (test, elt)); - return !NILP (elt); -} - -static Boolint -check_if_key (Lisp_Object test, Lisp_Object key, - Lisp_Object UNUSED (item), Lisp_Object elt) -{ - Lisp_Object args[] = { key, elt }; - struct gcpro gcpro1; - - GCPRO1 (args[0]); - gcpro1.nvars = countof (args); - args[1] = IGNORE_MULTIPLE_VALUES (Ffuncall (countof (args), args)); - args[0] = test; - elt = IGNORE_MULTIPLE_VALUES (Ffuncall (countof (args), args)); - UNGCPRO; - - return !NILP (elt); -} - -static Boolint -check_match_eq_key (Lisp_Object UNUSED (test), Lisp_Object key, - Lisp_Object elt1, Lisp_Object elt2) -{ - Lisp_Object args[] = { key, elt1, elt2 }; - struct gcpro gcpro1; - - GCPRO1 (args[0]); - gcpro1.nvars = countof (args); - args[0] = IGNORE_MULTIPLE_VALUES (Ffuncall (2, args)); - args[1] = key; - args[1] = IGNORE_MULTIPLE_VALUES (Ffuncall (2, args + 1)); - UNGCPRO; - - return EQ (args[0], args[1]); -} - -static Boolint -check_match_eql_key (Lisp_Object UNUSED (test), Lisp_Object key, - Lisp_Object elt1, Lisp_Object elt2) -{ - Lisp_Object args[] = { key, elt1, elt2 }; - struct gcpro gcpro1; - - GCPRO1 (args[0]); - gcpro1.nvars = countof (args); - args[0] = IGNORE_MULTIPLE_VALUES (Ffuncall (2, args)); - args[1] = key; - args[1] = IGNORE_MULTIPLE_VALUES (Ffuncall (2, args + 1)); - UNGCPRO; - - return EQ (args[0], args[1]) || - (NON_FIXNUM_NUMBER_P (args[0]) && internal_equal (args[0], args[1], 0)); -} - -static Boolint -check_match_equal_key (Lisp_Object UNUSED (test), Lisp_Object key, - Lisp_Object elt1, Lisp_Object elt2) -{ - Lisp_Object args[] = { key, elt1, elt2 }; - struct gcpro gcpro1; - - GCPRO1 (args[0]); - gcpro1.nvars = countof (args); - args[0] = IGNORE_MULTIPLE_VALUES (Ffuncall (2, args)); - args[1] = key; - args[1] = IGNORE_MULTIPLE_VALUES (Ffuncall (2, args + 1)); - UNGCPRO; - - return internal_equal (args[0], args[1], 0); -} - -static Boolint -check_match_equalp_key (Lisp_Object UNUSED (test), Lisp_Object key, - Lisp_Object elt1, Lisp_Object elt2) -{ - Lisp_Object args[] = { key, elt1, elt2 }; - struct gcpro gcpro1; - - GCPRO1 (args[0]); - gcpro1.nvars = countof (args); - args[0] = IGNORE_MULTIPLE_VALUES (Ffuncall (2, args)); - args[1] = key; - args[1] = IGNORE_MULTIPLE_VALUES (Ffuncall (2, args + 1)); - UNGCPRO; - - return internal_equalp (args[0], args[1], 0); -} - -static Boolint -check_match_other_key (Lisp_Object test, Lisp_Object key, - Lisp_Object elt1, Lisp_Object elt2) -{ - Lisp_Object args[] = { key, elt1, elt2 }; - struct gcpro gcpro1; - - GCPRO1 (args[0]); - gcpro1.nvars = countof (args); - args[0] = IGNORE_MULTIPLE_VALUES (Ffuncall (2, args)); - args[1] = key; - args[2] = IGNORE_MULTIPLE_VALUES (Ffuncall (2, args + 1)); - args[1] = args[0]; - args[0] = test; - - elt1 = IGNORE_MULTIPLE_VALUES (Ffuncall (countof (args), args)); - UNGCPRO; - - return !NILP (elt1); -} - -static Boolint -check_lss_nokey (Lisp_Object UNUSED (test), Lisp_Object UNUSED (key), - Lisp_Object elt1, Lisp_Object elt2) -{ - return bytecode_arithcompare (elt1, elt2) < 0; -} - -static Boolint -check_lss_key (Lisp_Object UNUSED (test), Lisp_Object key, - Lisp_Object elt1, Lisp_Object elt2) -{ - Lisp_Object args[] = { key, elt1, elt2 }; - struct gcpro gcpro1; - - GCPRO1 (args[0]); - gcpro1.nvars = countof (args); - args[0] = IGNORE_MULTIPLE_VALUES (Ffuncall (2, args)); - args[1] = key; - args[1] = IGNORE_MULTIPLE_VALUES (Ffuncall (2, args + 1)); - UNGCPRO; - - return bytecode_arithcompare (args[0], args[1]) < 0; -} - -Boolint -check_lss_key_car (Lisp_Object UNUSED (test), Lisp_Object UNUSED (key), - Lisp_Object elt1, Lisp_Object elt2) -{ - struct gcpro gcpro1, gcpro2; - - GCPRO2 (elt1, elt2); - elt1 = CONSP (elt1) ? XCAR (elt1) : Fcar (elt1); - elt2 = CONSP (elt2) ? XCAR (elt2) : Fcar (elt2); - UNGCPRO; - - return bytecode_arithcompare (elt1, elt2) < 0; -} - -Boolint -check_string_lessp_nokey (Lisp_Object UNUSED (test), Lisp_Object UNUSED (key), - Lisp_Object elt1, Lisp_Object elt2) -{ - return !NILP (Fstring_lessp (elt1, elt2)); -} - -static Boolint -check_string_lessp_key (Lisp_Object UNUSED (test), Lisp_Object key, - Lisp_Object elt1, Lisp_Object elt2) -{ - Lisp_Object args[] = { key, elt1, elt2 }; - struct gcpro gcpro1; - - GCPRO1 (args[0]); - gcpro1.nvars = countof (args); - args[0] = IGNORE_MULTIPLE_VALUES (Ffuncall (2, args)); - args[1] = key; - args[1] = IGNORE_MULTIPLE_VALUES (Ffuncall (2, args + 1)); - UNGCPRO; - - return !NILP (Fstring_lessp (args[0], args[1])); -} - -static Boolint -check_string_lessp_key_car (Lisp_Object UNUSED (test), - Lisp_Object UNUSED (key), - Lisp_Object elt1, Lisp_Object elt2) -{ - struct gcpro gcpro1, gcpro2; - - GCPRO2 (elt1, elt2); - elt1 = CONSP (elt1) ? XCAR (elt1) : Fcar (elt1); - elt2 = CONSP (elt2) ? XCAR (elt2) : Fcar (elt2); - UNGCPRO; - - return !NILP (Fstring_lessp (elt1, elt2)); -} - -static check_test_func_t -get_check_match_function_1 (Lisp_Object item, - Lisp_Object *test_inout, Lisp_Object test_not, - Lisp_Object if_, Lisp_Object if_not, - Lisp_Object key, Boolint *test_not_unboundp_out, - check_test_func_t *test_func_out) -{ - Lisp_Object test = *test_inout; - check_test_func_t result = NULL, test_func = NULL; - Boolint force_if = 0; - - if (!NILP (if_)) - { - if (!(NILP (test) && NILP (test_not) && NILP (if_not))) - { - invalid_argument ("only one keyword among :test :test-not " - ":if :if-not allowed", if_); - } - - test = *test_inout = if_; - force_if = 1; - } - else if (!NILP (if_not)) - { - if (!(NILP (test) && NILP (test_not))) - { - invalid_argument ("only one keyword among :test :test-not " - ":if :if-not allowed", if_not); - } - - test_not = if_not; - force_if = 1; - } - - if (NILP (test)) - { - if (!NILP (test_not)) - { - test = *test_inout = test_not; - if (NULL != test_not_unboundp_out) - { - *test_not_unboundp_out = 0; - } - } - else - { - test = Qeql; - if (NULL != test_not_unboundp_out) - { - *test_not_unboundp_out = 1; - } - } - } - else if (!NILP (test_not)) - { - invalid_argument_2 ("conflicting :test and :test-not keyword arguments", - test, test_not); - } - - test = indirect_function (test, 1); - - if (NILP (key) || - EQ (indirect_function (key, 1), XSYMBOL_FUNCTION (Qidentity))) - { - key = Qidentity; - } - - if (force_if) - { - result = EQ (key, Qidentity) ? check_if_nokey : check_if_key; - - if (NULL != test_func_out) - { - *test_func_out = result; - } - - return result; - } - - if (!UNBOUNDP (item) && EQ (test, XSYMBOL_FUNCTION (Qeql))) - { - test = XSYMBOL_FUNCTION (NON_FIXNUM_NUMBER_P (item) ? Qequal : Qeq); - } - -#define FROB(known_test, eq_condition) \ - if (EQ (test, XSYMBOL_FUNCTION (Q##known_test))) do \ - { \ - if (eq_condition) \ - { \ - test = XSYMBOL_FUNCTION (Qeq); \ - goto force_eq_check; \ - } \ - \ - if (!EQ (Qidentity, key)) \ - { \ - test_func = check_##known_test##_key; \ - result = check_match_##known_test##_key; \ - } \ - else \ - { \ - result = test_func = check_##known_test##_nokey; \ - } \ - } while (0) - - FROB (eql, 0); - else if (SUBRP (test)) - { - force_eq_check: - FROB (eq, 0); - else FROB (equal, (SYMBOLP (item) || FIXNUMP (item) || CHARP (item))); - else FROB (equalp, (SYMBOLP (item))); - else if (EQ (test, XSYMBOL_FUNCTION (Qstring_match))) - { - if (EQ (Qidentity, key)) - { - test_func = result = check_string_match_nokey; - } - else - { - test_func = check_string_match_key; - result = check_other_key; - } - } - } - - if (NULL == result) - { - if (EQ (Qidentity, key)) - { - test_func = result = check_other_nokey; - } - else - { - test_func = check_other_key; - result = check_match_other_key; - } - } - - if (NULL != test_func_out) - { - *test_func_out = test_func; - } - - return result; -} -#undef FROB - -/* Given TEST, TEST_NOT, IF, IF_NOT, KEY, and ITEM, return a C function - pointer appropriate for use in deciding whether a given element of a - sequence satisfies TEST. - - Set *test_not_unboundp_out to 1 if TEST_NOT was not bound; set it to zero - if it was bound, and set *test_inout to the value it was bound to. If - TEST was not bound, leave *test_inout alone; the value is not used by - check_eq_*key() or check_equal_*key(), which are the defaults, depending - on the type of ITEM. - - The returned function takes arguments (TEST, KEY, ITEM, ELT), where ITEM - is the item being searched for and ELT is the element of the sequence - being examined. - - Error if both TEST and TEST_NOT were specified, which Common Lisp says is - undefined behaviour. */ - -static check_test_func_t -get_check_test_function (Lisp_Object item, - Lisp_Object *test_inout, Lisp_Object test_not, - Lisp_Object if_, Lisp_Object if_not, - Lisp_Object key, Boolint *test_not_unboundp_out) -{ - check_test_func_t result = NULL; - get_check_match_function_1 (item, test_inout, test_not, if_, if_not, - key, test_not_unboundp_out, &result); - return result; -} - -/* Given TEST, TEST_NOT, IF, IF_NOT and KEY, return a C function pointer - appropriate for use in deciding whether two given elements of a sequence - satisfy TEST. - - Set *test_not_unboundp_out to 1 if TEST_NOT was not bound; set it to zero - if it was bound, and set *test_inout to the value it was bound to. If - TEST was not bound, leave *test_inout alone; the value is not used by - check_eql_*key(). - - The returned function takes arguments (TEST, KEY, ELT1, ELT2), where ELT1 - and ELT2 are elements of the sequence being examined. - - The value that would be given by get_check_test_function() is returned in - *TEST_FUNC_OUT, which allows calling functions to do their own key checks - if they're processing one element at a time. - - Error if both TEST and TEST_NOT were specified, which Common Lisp says is - undefined behaviour. */ - -static check_test_func_t -get_check_match_function (Lisp_Object *test_inout, Lisp_Object test_not, - Lisp_Object if_, Lisp_Object if_not, - Lisp_Object key, Boolint *test_not_unboundp_out, - check_test_func_t *test_func_out) -{ - return get_check_match_function_1 (Qunbound, test_inout, test_not, - if_, if_not, key, - test_not_unboundp_out, test_func_out); -} - -/* Given PREDICATE and KEY, return a C function pointer appropriate for use - in deciding whether one given element of a sequence is less than - another. */ - -static check_test_func_t -get_merge_predicate (Lisp_Object predicate, Lisp_Object key) -{ - predicate = indirect_function (predicate, 1); - - if (NILP (key)) - { - key = Qidentity; - } - else - { - key = indirect_function (key, 1); - if (EQ (key, XSYMBOL_FUNCTION (Qidentity))) - { - key = Qidentity; - } - } - - if (EQ (key, Qidentity) && EQ (predicate, - XSYMBOL_FUNCTION (Qcar_less_than_car))) - { - key = XSYMBOL_FUNCTION (Qcar); - predicate = XSYMBOL_FUNCTION (Qlss); - } - - if (EQ (predicate, XSYMBOL_FUNCTION (Qlss))) - { - if (EQ (key, Qidentity)) - { - return check_lss_nokey; - } - - if (EQ (key, XSYMBOL_FUNCTION (Qcar))) - { - return check_lss_key_car; - } - - return check_lss_key; - } - - if (EQ (predicate, XSYMBOL_FUNCTION (Qstring_lessp))) - { - if (EQ (key, Qidentity)) - { - return check_string_lessp_nokey; - } - - if (EQ (key, XSYMBOL_FUNCTION (Qcar))) - { - return check_string_lessp_key_car; - } - - return check_string_lessp_key; - } - - if (EQ (key, Qidentity)) - { - return check_other_nokey; - } - - return check_match_other_key; -} +Lisp_Object Qmapl, Qmapcon, Qmaplist, Qbase64_conversion_error; + +Lisp_Object Vpath_separator; DEFUN ("identity", Fidentity, 1, 1, 0, /* Return the argument unchanged. @@ -847,34 +121,6 @@ function); } -DEFUN ("length", Flength, 1, 1, 0, /* -Return the length of vector, bit vector, list or string SEQUENCE. -*/ - (sequence)) -{ - retry: - if (STRINGP (sequence)) - return make_fixnum (string_char_length (sequence)); - else if (CONSP (sequence)) - { - Elemcount len; - GET_EXTERNAL_LIST_LENGTH (sequence, len); - return make_fixnum (len); - } - else if (VECTORP (sequence)) - return make_fixnum (XVECTOR_LENGTH (sequence)); - else if (NILP (sequence)) - return Qzero; - else if (BIT_VECTORP (sequence)) - return make_fixnum (bit_vector_length (XBIT_VECTOR (sequence))); - else - { - check_losing_bytecode ("length", sequence); - sequence = wrong_type_argument (Qsequencep, sequence); - goto retry; - } -} - DEFUN ("safe-length", Fsafe_length, 1, 1, 0, /* Return the length of a list, but avoid error or infinite loop. This function never gets an error. If LIST is not really a list, @@ -926,309 +172,6 @@ return EQ (hare, tortoise) && len != 0 ? Qnil : make_fixnum (len); } -static Lisp_Object string_count_from_end (Lisp_Object, Lisp_Object , - check_test_func_t, Boolint, - Lisp_Object, Lisp_Object, - Lisp_Object, Lisp_Object); - -static Lisp_Object list_count_from_end (Lisp_Object, Lisp_Object, - check_test_func_t, Boolint, - Lisp_Object, Lisp_Object, - Lisp_Object, Lisp_Object); - -/* Count the number of occurrences of ITEM in SEQUENCE; if SEQUENCE is a - list, store the cons cell of which the car is the last ITEM in SEQUENCE, - at the address given by tail_out. */ - -static Lisp_Object -count_with_tail (Lisp_Object *tail_out, int nargs, Lisp_Object *args, - Lisp_Object caller) -{ - Lisp_Object item = args[0], sequence = args[1]; - Elemcount starting = 0, ending = MOST_POSITIVE_FIXNUM, encountered = 0; - Elemcount len, ii = 0, counting = MOST_POSITIVE_FIXNUM; - Boolint test_not_unboundp = 1; - check_test_func_t check_test = NULL; - - PARSE_KEYWORDS_8 (caller, nargs, args, 9, - (test, key, start, end, from_end, test_not, count, - if_, if_not), (start = Qzero), 2, 0); - - CHECK_SEQUENCE (sequence); - CHECK_NATNUM (start); - starting = BIGNUMP (start) ? 1 + MOST_POSITIVE_FIXNUM : XFIXNUM (start); - - if (!NILP (end)) - { - CHECK_NATNUM (end); - ending = BIGNUMP (end) ? 1 + MOST_POSITIVE_FIXNUM : XFIXNUM (end); - } - - if (!NILP (count)) - { - CHECK_INTEGER (count); - counting = BIGNUMP (count) ? MOST_POSITIVE_FIXNUM + 1 : XFIXNUM (count); - - /* Our callers should have filtered out non-positive COUNT. */ - assert (counting >= 0); - /* And we're not prepared to handle COUNT from any other caller at the - moment. */ - assert (EQ (caller, QremoveX)|| EQ (caller, QdeleteX)); - } - - check_test = get_check_test_function (item, &test, test_not, if_, if_not, - key, &test_not_unboundp); - - *tail_out = Qnil; - - if (CONSP (sequence)) - { - if (EQ (caller, Qcount) && !NILP (from_end) - && (!EQ (key, Qnil) || - check_test == check_other_nokey || check_test == check_if_nokey)) - { - /* #'count, #'count-if, and #'count-if-not are documented to have - a given traversal order if :from-end t is passed in, even - though forward traversal of the sequence has the same result - and is algorithmically less expensive for lists and strings. - This order isn't necessary for other callers, though. */ - return list_count_from_end (item, sequence, check_test, - test_not_unboundp, test, key, - start, end); - } - - /* If COUNT is non-nil and FROM-END is t, we can give the tail - containing the last match, since that's what #'remove* is - interested in (a zero or negative COUNT won't ever reach - count_with_tail(), our callers will return immediately on seeing - it). */ - if (!NILP (count) && !NILP (from_end)) - { - counting = MOST_POSITIVE_FIXNUM; - } - - { - GC_EXTERNAL_LIST_LOOP_3 (elt, sequence, tail) - { - if (!(ii < ending)) - { - break; - } - - if (starting <= ii && - check_test (test, key, item, elt) == test_not_unboundp) - { - encountered++; - *tail_out = tail; - - if (encountered == counting) - { - break; - } - } - - ii++; - } - END_GC_EXTERNAL_LIST_LOOP (elt); - } - - if ((ii < starting || (ii < ending && !NILP (end))) && - encountered != counting) - { - check_sequence_range (args[1], start, end, Flength (args[1])); - } - } - else if (STRINGP (sequence)) - { - Ibyte *startp = XSTRING_DATA (sequence), *cursor = startp; - Bytecount byte_len = XSTRING_LENGTH (sequence), cursor_offset = 0; - Lisp_Object character = Qnil; - - if (EQ (caller, Qcount) && !NILP (from_end) - && (!EQ (key, Qnil) || - check_test == check_other_nokey || check_test == check_if_nokey)) - { - /* See comment above in the list code. */ - return string_count_from_end (item, sequence, - check_test, test_not_unboundp, - test, key, start, end); - } - - while (cursor_offset < byte_len && ii < ending && encountered < counting) - { - if (ii >= starting) - { - character = make_char (itext_ichar (cursor)); - - if (check_test (test, key, item, character) - == test_not_unboundp) - { - encountered++; - } - - startp = XSTRING_DATA (sequence); - cursor = startp + cursor_offset; - if (byte_len != XSTRING_LENGTH (sequence) - || !valid_ibyteptr_p (cursor)) - { - mapping_interaction_error (caller, sequence); - } - } - - INC_IBYTEPTR (cursor); - cursor_offset = cursor - startp; - ii++; - } - - if (ii < starting || (ii < ending && !NILP (end))) - { - check_sequence_range (sequence, start, end, Flength (sequence)); - } - } - else - { - Lisp_Object object = Qnil; - - len = XFIXNUM (Flength (sequence)); - check_sequence_range (sequence, start, end, make_fixnum (len)); - - ending = min (ending, len); - if (0 == len) - { - /* Catches the case where we have nil. */ - return make_integer (encountered); - } - - if (NILP (from_end)) - { - for (ii = starting; ii < ending && encountered < counting; ii++) - { - object = Faref (sequence, make_fixnum (ii)); - if (check_test (test, key, item, object) == test_not_unboundp) - { - encountered++; - } - } - } - else - { - for (ii = ending - 1; ii >= starting && encountered < counting; ii--) - { - object = Faref (sequence, make_fixnum (ii)); - if (check_test (test, key, item, object) == test_not_unboundp) - { - encountered++; - } - } - } - } - - return make_integer (encountered); -} - -static Lisp_Object -list_count_from_end (Lisp_Object item, Lisp_Object sequence, - check_test_func_t check_test, Boolint test_not_unboundp, - Lisp_Object test, Lisp_Object key, - Lisp_Object start, Lisp_Object end) -{ - Elemcount length = XFIXNUM (Flength (sequence)), ii = 0, starting = XFIXNUM (start); - Elemcount ending = NILP (end) ? length : XFIXNUM (end), encountered = 0; - Lisp_Object *storage; - struct gcpro gcpro1; - - check_sequence_range (sequence, start, end, make_integer (length)); - - storage = alloca_array (Lisp_Object, ending - starting); - - { - EXTERNAL_LIST_LOOP_2 (elt, sequence) - { - if (starting <= ii && ii < ending) - { - storage[ii - starting] = elt; - } - ii++; - } - } - - GCPRO1 (storage[0]); - gcpro1.nvars = ending - starting; - - for (ii = ending - 1; ii >= starting; ii--) - { - if (check_test (test, key, item, storage[ii - starting]) - == test_not_unboundp) - { - encountered++; - } - } - - UNGCPRO; - - return make_integer (encountered); -} - -static Lisp_Object -string_count_from_end (Lisp_Object item, Lisp_Object sequence, - check_test_func_t check_test, Boolint test_not_unboundp, - Lisp_Object test, Lisp_Object key, - Lisp_Object start, Lisp_Object end) -{ - Elemcount length = string_char_length (sequence), ii = 0; - Elemcount starting = XFIXNUM (start), ending = NILP (end) ? length : XFIXNUM (end); - Elemcount encountered = 0; - Ibyte *cursor = XSTRING_DATA (sequence); - Ibyte *endp = cursor + XSTRING_LENGTH (sequence); - Ichar *storage; - - check_sequence_range (sequence, start, end, make_integer (length)); - - storage = alloca_array (Ichar, ending - starting); - - while (cursor < endp && ii < ending) - { - if (starting <= ii && ii < ending) - { - storage [ii - starting] = itext_ichar (cursor); - } - - ii++; - INC_IBYTEPTR (cursor); - } - - for (ii = ending - 1; ii >= starting; ii--) - { - if (check_test (test, key, item, make_char (storage [ii - starting])) - == test_not_unboundp) - { - encountered++; - } - } - - return make_integer (encountered); -} - -DEFUN ("count", Fcount, 2, MANY, 0, /* -Count the number of occurrences of ITEM in SEQUENCE. - -See `remove*' for the meaning of the keywords. - -arguments: (ITEM SEQUENCE &key (TEST #'eql) (KEY #'identity) (START 0) END FROM-END TEST-NOT) -*/ - (int nargs, Lisp_Object *args)) -{ - Lisp_Object tail = Qnil; - - /* count_with_tail() accepts more keywords than we do, check those we've - been given. */ - PARSE_KEYWORDS (Fcount, nargs, args, 8, - (test, test_not, if_, if_not, key, start, end, from_end), - NULL); - - return count_with_tail (&tail, nargs, args, Qcount); -} - /*** string functions. ***/ DEFUN ("string-equal", Fstring_equal, 2, 2, 0, /* @@ -1836,206 +779,6 @@ return alist; } -DEFUN ("copy-tree", Fcopy_tree, 1, 2, 0, /* -Return a copy of a list and substructures. -The argument is copied, and any lists contained within it are copied -recursively. Circularities and shared substructures are not preserved. -Second arg VECP causes vectors to be copied, too. Strings and bit vectors -are not copied. -*/ - (arg, vecp)) -{ - return safe_copy_tree (arg, vecp, 0); -} - -Lisp_Object -safe_copy_tree (Lisp_Object arg, Lisp_Object vecp, int depth) -{ - if (depth + lisp_eval_depth > max_lisp_eval_depth) - stack_overflow ("Stack overflow in copy-tree", arg); - - if (CONSP (arg)) - { - Lisp_Object rest; - rest = arg = Fcopy_sequence (arg); - while (CONSP (rest)) - { - Lisp_Object elt = XCAR (rest); - QUIT; - if (CONSP (elt) || VECTORP (elt)) - XCAR (rest) = safe_copy_tree (elt, vecp, depth + 1); - if (VECTORP (XCDR (rest))) /* hack for (a b . [c d]) */ - XCDR (rest) = safe_copy_tree (XCDR (rest), vecp, depth +1); - rest = XCDR (rest); - } - } - else if (VECTORP (arg) && ! NILP (vecp)) - { - int i = XVECTOR_LENGTH (arg); - int j; - arg = Fcopy_sequence (arg); - for (j = 0; j < i; j++) - { - Lisp_Object elt = XVECTOR_DATA (arg) [j]; - QUIT; - if (CONSP (elt) || VECTORP (elt)) - XVECTOR_DATA (arg) [j] = safe_copy_tree (elt, vecp, depth + 1); - } - } - return arg; -} - -DEFUN ("subseq", Fsubseq, 2, 3, 0, /* -Return the subsequence of SEQUENCE starting at START and ending before END. -END may be omitted; then the subsequence runs to the end of SEQUENCE. - -If START or END is negative, it counts from the end, in contravention of -Common Lisp. -The returned subsequence is always of the same type as SEQUENCE. -If SEQUENCE is a string, relevant parts of the string-extent-data -are copied to the new string. - -See also `substring-no-properties', which only operates on strings, and does -not copy extent data. -*/ - (sequence, start, end)) -{ - Elemcount len, ss, ee = MOST_POSITIVE_FIXNUM, ii; - Lisp_Object result = Qnil; - - CHECK_SEQUENCE (sequence); - CHECK_FIXNUM (start); - ss = XFIXNUM (start); - - if (!NILP (end)) - { - CHECK_FIXNUM (end); - ee = XFIXNUM (end); - } - - if (STRINGP (sequence)) - { - Bytecount bstart, blen; - - get_string_range_char (sequence, start, end, &ss, &ee, - GB_HISTORICAL_STRING_BEHAVIOR); - bstart = string_index_char_to_byte (sequence, ss); - blen = string_offset_char_to_byte_len (sequence, bstart, ee - ss); - - result = make_string (XSTRING_DATA (sequence) + bstart, blen); - /* Copy any applicable extent information into the new string. */ - copy_string_extents (result, sequence, 0, bstart, blen); - } - else if (CONSP (sequence)) - { - Lisp_Object result_tail, saved = sequence; - - if (ss < 0 || ee < 0) - { - len = XFIXNUM (Flength (sequence)); - if (ss < 0) - { - ss = len + ss; - start = make_integer (ss); - } - - if (ee < 0) - { - ee = len + ee; - end = make_integer (ee); - } - else - { - ee = min (ee, len); - } - } - - if (0 != ss) - { - sequence = Fnthcdr (make_fixnum (ss), sequence); - } - - ii = ss + 1; - - if (ss < ee && !NILP (sequence)) - { - result = result_tail = Fcons (Fcar (sequence), Qnil); - sequence = Fcdr (sequence); - - { - EXTERNAL_LIST_LOOP_2 (elt, sequence) - { - if (!(ii < ee)) - { - break; - } - - XSETCDR (result_tail, Fcons (elt, Qnil)); - result_tail = XCDR (result_tail); - ii++; - } - } - } - - if (NILP (result) || (ii < ee && !NILP (end))) - { - /* We were handed a cons, which definitely has elements. nil - result means either ss >= ee or SEQUENCE was nil after the - nthcdr; in both cases that means START and END were incorrectly - specified for this sequence. ii < ee with a non-nil end means - the user handed us a bogus end value. */ - check_sequence_range (saved, start, end, Flength (saved)); - } - } - else - { - len = XFIXNUM (Flength (sequence)); - if (ss < 0) - { - ss = len + ss; - start = make_integer (ss); - } - - if (ee < 0) - { - ee = len + ee; - end = make_integer (ee); - } - else - { - ee = min (len, ee); - } - - check_sequence_range (sequence, start, end, make_fixnum (len)); - - if (VECTORP (sequence)) - { - result = Fvector (ee - ss, XVECTOR_DATA (sequence) + ss); - } - else if (BIT_VECTORP (sequence)) - { - result = make_bit_vector (ee - ss, Qzero); - - for (ii = ss; ii < ee; ii++) - { - set_bit_vector_bit (XBIT_VECTOR (result), ii - ss, - bit_vector_bit (XBIT_VECTOR (sequence), ii)); - } - } - else if (NILP (sequence)) - { - DO_NOTHING; - } - else - { - /* Won't happen, since CHECK_SEQUENCE didn't error. */ - ABORT (); - } - } - - return result; -} - DEFUN ("substring-no-properties", Fsubstring_no_properties, 1, 3, 0, /* Return a substring of STRING, without copying the extents. END may be nil or omitted; then the substring runs to the end of STRING. @@ -2285,44 +1028,6 @@ return Fcar (Fnthcdr (n, list)); } -DEFUN ("elt", Felt, 2, 2, 0, /* -Return element of SEQUENCE at index N. -*/ - (sequence, n)) -{ - /* This function can GC */ - retry: - CHECK_FIXNUM_COERCE_CHAR (n); /* yuck! */ - if (LISTP (sequence)) - { - Lisp_Object tem = Fnthcdr (n, sequence); - /* #### Utterly, completely, fucking disgusting. - * #### The whole point of "elt" is that it operates on - * #### sequences, and does error- (bounds-) checking. - */ - if (CONSP (tem)) - return XCAR (tem); - else -#if 1 - /* This is The Way It Has Always Been. */ - return Qnil; -#else - /* This is The Way Mly and Cltl2 say It Should Be. */ - args_out_of_range (sequence, n); -#endif - } - else if (STRINGP (sequence) || - VECTORP (sequence) || - BIT_VECTORP (sequence)) - return Faref (sequence, n); - else - { - check_losing_bytecode ("elt", sequence); - sequence = wrong_type_argument (Qsequencep, sequence); - goto retry; - } -} - DEFUN ("last", Flast, 1, 2, 0, /* Return the tail of list LIST, of length N (default 1). LIST may be a dotted list, but not a circular list. @@ -2459,2846 +1164,6 @@ return retval; } -DEFUN ("member", Fmember, 2, 2, 0, /* -Return non-nil if ELT is an element of LIST. Comparison done with `equal'. -The value is actually the tail of LIST whose car is ELT. -*/ - (elt, list)) -{ - EXTERNAL_LIST_LOOP_3 (list_elt, list, tail) - { - if (internal_equal (elt, list_elt, 0)) - return tail; - } - return Qnil; -} - -DEFUN ("memq", Fmemq, 2, 2, 0, /* -Return non-nil if ELT is an element of LIST. Comparison done with `eq'. -The value is actually the tail of LIST whose car is ELT. -*/ - (elt, list)) -{ - EXTERNAL_LIST_LOOP_3 (list_elt, list, tail) - { - if (EQ_WITH_EBOLA_NOTICE (elt, list_elt)) - return tail; - } - return Qnil; -} - -Lisp_Object -memq_no_quit (Lisp_Object elt, Lisp_Object list) -{ - LIST_LOOP_3 (list_elt, list, tail) - { - if (EQ_WITH_EBOLA_NOTICE (elt, list_elt)) - return tail; - } - return Qnil; -} - -/* Return the first index of ITEM in LIST. In CONS_OUT, return the cons cell - before that containing the element. If the element is in the first cons - cell, return Qnil in CONS_OUT. TEST, KEY, START, END are as in - #'remove*; CHECK_TEST and TEST_NOT_UNBOUNDP should have been initialized - with get_check_match_function() or get_check_test_function(). A non-zero - REVERSE_TEST_ORDER means call TEST with the element from LIST as its - first argument and ITEM as its second. Error if LIST is ill-formed, or - circular. */ -static Lisp_Object -list_position_cons_before (Lisp_Object *cons_out, - Lisp_Object item, Lisp_Object list, - check_test_func_t check_test, - Boolint test_not_unboundp, - Lisp_Object test, Lisp_Object key, - Boolint reverse_test_order, - Lisp_Object start, Lisp_Object end) -{ - struct gcpro gcpro1; - Lisp_Object tail_before = Qnil; - Elemcount ii = 0, starting = XFIXNUM (start); - Elemcount ending = NILP (end) ? MOST_POSITIVE_FIXNUM : XFIXNUM (end); - - GCPRO1 (tail_before); - - if (check_test == check_eq_nokey) - { - /* TEST is #'eq, no need to call any C functions, and the test order - won't be visible. */ - EXTERNAL_LIST_LOOP_3 (elt, list, tail) - { - if (starting <= ii && ii < ending && - EQ (item, elt) == test_not_unboundp) - { - *cons_out = tail_before; - RETURN_UNGCPRO (make_integer (ii)); - } - else - { - if (ii >= ending) - { - break; - } - } - ii++; - tail_before = tail; - } - } - else - { - GC_EXTERNAL_LIST_LOOP_3 (elt, list, tail) - { - if (starting <= ii && ii < ending && - (reverse_test_order ? - check_test (test, key, elt, item) : - check_test (test, key, item, elt)) == test_not_unboundp) - { - *cons_out = tail_before; - XUNGCPRO (elt); - UNGCPRO; - return make_integer (ii); - } - else - { - if (ii >= ending) - { - break; - } - } - ii++; - tail_before = tail; - } - END_GC_EXTERNAL_LIST_LOOP (elt); - } - - RETURN_UNGCPRO (Qnil); -} - -DEFUN ("member*", FmemberX, 2, MANY, 0, /* -Return the first sublist of LIST with car ITEM, or nil if no such sublist. - -The keyword :test specifies a two-argument function that is used to compare -ITEM with elements in LIST; if omitted, it defaults to `eql'. - -The keyword :test-not is similar, but specifies a negated function. That -is, ITEM is considered equal to an element in LIST if the given function -returns nil. Common Lisp deprecates :test-not, and if both are specified, -XEmacs signals an error. - -:key specifies a one-argument function that transforms elements of LIST into -\"comparison keys\" before the test predicate is applied. For example, -if :key is #'car, then ITEM is compared with the car of elements from LIST. -The :key function, however, is not applied to ITEM, and does not affect the -elements in the returned list, which are taken directly from the elements in -LIST. - -arguments: (ITEM LIST &key (TEST #'eql) TEST-NOT (KEY #'identity)) -*/ - (int nargs, Lisp_Object *args)) -{ - Lisp_Object item = args[0], list = args[1], result = Qnil, position0; - Boolint test_not_unboundp = 1; - check_test_func_t check_test = NULL; - - PARSE_KEYWORDS (FmemberX, nargs, args, 5, (test, if_not, if_, test_not, key), - NULL); - check_test = get_check_test_function (item, &test, test_not, if_, if_not, - key, &test_not_unboundp); - position0 - = list_position_cons_before (&result, item, list, check_test, - test_not_unboundp, test, key, 0, Qzero, Qnil); - - return CONSP (result) ? XCDR (result) : ZEROP (position0) ? list : Qnil; -} - -/* This macro might eventually find a better home than here. */ - -#define CHECK_KEY_ARGUMENT(key) \ - do { \ - if (NILP (key)) \ - { \ - key = Qidentity; \ - } \ - \ - if (!EQ (key, Qidentity)) \ - { \ - key = indirect_function (key, 1); \ - if (EQ (key, XSYMBOL_FUNCTION (Qidentity))) \ - { \ - key = Qidentity; \ - } \ - } \ - } while (0) - -#define KEY(key, item) (EQ (Qidentity, key) ? item : \ - IGNORE_MULTIPLE_VALUES (call1 (key, item))) - -DEFUN ("adjoin", Fadjoin, 2, MANY, 0, /* -Return ITEM consed onto the front of LIST, if not already in LIST. - -Otherwise, return LIST unmodified. - -See `member*' for the meaning of the keywords. - -arguments: (ITEM LIST &key (TEST #'eql) (KEY #'identity) TEST-NOT) -*/ - (int nargs, Lisp_Object *args)) -{ - Lisp_Object item = args[0], list = args[1], keyed = Qnil, ignore = Qnil; - struct gcpro gcpro1; - Boolint test_not_unboundp = 1; - check_test_func_t check_test = NULL; - - PARSE_KEYWORDS (Fadjoin, nargs, args, 3, (test, key, test_not), - NULL); - - CHECK_KEY_ARGUMENT (key); - - keyed = KEY (key, item); - - GCPRO1 (keyed); - check_test = get_check_test_function (keyed, &test, test_not, Qnil, Qnil, - key, &test_not_unboundp); - if (NILP (list_position_cons_before (&ignore, keyed, list, check_test, - test_not_unboundp, test, key, 0, Qzero, - Qnil))) - { - RETURN_UNGCPRO (Fcons (item, list)); - } - - RETURN_UNGCPRO (list); -} - -DEFUN ("assoc", Fassoc, 2, 2, 0, /* -Return non-nil if KEY is `equal' to the car of an element of ALIST. -The value is actually the element of ALIST whose car equals KEY. -*/ - (key, alist)) -{ - /* This function can GC. */ - EXTERNAL_ALIST_LOOP_4 (elt, elt_car, elt_cdr, alist) - { - if (internal_equal (key, elt_car, 0)) - return elt; - } - return Qnil; -} - -Lisp_Object -assoc_no_quit (Lisp_Object key, Lisp_Object alist) -{ - int speccount = specpdl_depth (); - specbind (Qinhibit_quit, Qt); - return unbind_to_1 (speccount, Fassoc (key, alist)); -} - -DEFUN ("assq", Fassq, 2, 2, 0, /* -Return non-nil if KEY is `eq' to the car of an element of ALIST. -The value is actually the element of ALIST whose car is KEY. -Elements of ALIST that are not conses are ignored. -*/ - (key, alist)) -{ - EXTERNAL_ALIST_LOOP_4 (elt, elt_car, elt_cdr, alist) - { - if (EQ_WITH_EBOLA_NOTICE (key, elt_car)) - return elt; - } - return Qnil; -} - -/* Like Fassq but never report an error and do not allow quits. - Use only on lists known never to be circular. */ - -Lisp_Object -assq_no_quit (Lisp_Object key, Lisp_Object alist) -{ - /* This cannot GC. */ - LIST_LOOP_2 (elt, alist) - { - Lisp_Object elt_car = XCAR (elt); - if (EQ_WITH_EBOLA_NOTICE (key, elt_car)) - return elt; - } - return Qnil; -} - -DEFUN ("assoc*", FassocX, 2, MANY, 0, /* -Find the first item whose car matches ITEM in ALIST. - -See `member*' for the meaning of :test, :test-not and :key. - -arguments: (ITEM ALIST &key (TEST #'eql) (KEY #'identity) TEST-NOT) -*/ - (int nargs, Lisp_Object *args)) -{ - Lisp_Object item = args[0], alist = args[1]; - Boolint test_not_unboundp = 1; - check_test_func_t check_test = NULL; - - PARSE_KEYWORDS (FassocX, nargs, args, 5, (test, if_, if_not, test_not, key), - NULL); - - check_test = get_check_test_function (item, &test, test_not, if_, if_not, - key, &test_not_unboundp); - - if (check_test == check_eq_nokey) - { - /* TEST is #'eq, no need to call any C functions. */ - EXTERNAL_ALIST_LOOP_4 (elt, elt_car, elt_cdr, alist) - { - if (EQ (item, elt_car) == test_not_unboundp) - { - return elt; - } - } - } - else - { - GC_EXTERNAL_LIST_LOOP_2 (elt, alist) - { - if (CONSP (elt) && - check_test (test, key, item, XCAR (elt)) == test_not_unboundp) - { - XUNGCPRO (elt); - return elt; - } - } - END_GC_EXTERNAL_LIST_LOOP (elt); - } - - return Qnil; -} - -DEFUN ("rassoc", Frassoc, 2, 2, 0, /* -Return non-nil if VALUE is `equal' to the cdr of an element of ALIST. -The value is actually the element of ALIST whose cdr equals VALUE. -*/ - (value, alist)) -{ - EXTERNAL_ALIST_LOOP_4 (elt, elt_car, elt_cdr, alist) - { - if (internal_equal (value, elt_cdr, 0)) - return elt; - } - return Qnil; -} - -DEFUN ("rassq", Frassq, 2, 2, 0, /* -Return non-nil if VALUE is `eq' to the cdr of an element of ALIST. -The value is actually the element of ALIST whose cdr is VALUE. -*/ - (value, alist)) -{ - EXTERNAL_ALIST_LOOP_4 (elt, elt_car, elt_cdr, alist) - { - if (EQ_WITH_EBOLA_NOTICE (value, elt_cdr)) - return elt; - } - return Qnil; -} - -DEFUN ("old-rassq", Fold_rassq, 2, 2, 0, /* -Return non-nil if VALUE is `old-eq' to the cdr of an element of ALIST. -The value is actually the element of ALIST whose cdr is VALUE. -*/ - (value, alist)) -{ - EXTERNAL_ALIST_LOOP_4 (elt, elt_car, elt_cdr, alist) - { - if (HACKEQ_UNSAFE (value, elt_cdr)) - return elt; - } - return Qnil; -} - -/* Like Frassq, but caller must ensure that ALIST is properly - nil-terminated and ebola-free. */ -Lisp_Object -rassq_no_quit (Lisp_Object value, Lisp_Object alist) -{ - LIST_LOOP_2 (elt, alist) - { - Lisp_Object elt_cdr = XCDR (elt); - if (EQ_WITH_EBOLA_NOTICE (value, elt_cdr)) - return elt; - } - return Qnil; -} - -DEFUN ("rassoc*", FrassocX, 2, MANY, 0, /* -Find the first item whose cdr matches ITEM in ALIST. - -See `member*' for the meaning of :test, :test-not and :key. - -arguments: (ITEM ALIST &key (TEST #'eql) (KEY #'identity) TEST-NOT) -*/ - (int nargs, Lisp_Object *args)) -{ - Lisp_Object item = args[0], alist = args[1]; - Boolint test_not_unboundp = 1; - check_test_func_t check_test = NULL; - - PARSE_KEYWORDS (FrassocX, nargs, args, 5, (test, if_, if_not, test_not, key), - NULL); - - check_test = get_check_test_function (item, &test, test_not, if_, if_not, - key, &test_not_unboundp); - - if (check_test == check_eq_nokey) - { - /* TEST is #'eq, no need to call any C functions. */ - EXTERNAL_ALIST_LOOP_4 (elt, elt_car, elt_cdr, alist) - { - if (EQ (item, elt_cdr) == test_not_unboundp) - { - return elt; - } - } - } - else - { - GC_EXTERNAL_LIST_LOOP_2 (elt, alist) - { - if (CONSP (elt) && - check_test (test, key, item, XCDR (elt)) == test_not_unboundp) - { - XUNGCPRO (elt); - return elt; - } - } - END_GC_EXTERNAL_LIST_LOOP (elt); - } - - return Qnil; -} - -/* This is the implementation of both #'find and #'position. */ -static Lisp_Object -position (Lisp_Object *object_out, Lisp_Object item, Lisp_Object sequence, - check_test_func_t check_test, Boolint test_not_unboundp, - Lisp_Object test, Lisp_Object key, Lisp_Object start, Lisp_Object end, - Lisp_Object from_end, Lisp_Object default_, Lisp_Object caller) -{ - Lisp_Object result = Qnil; - Elemcount starting = 0, ending = MOST_POSITIVE_FIXNUM, len, ii = 0; - - CHECK_SEQUENCE (sequence); - CHECK_NATNUM (start); - starting = FIXNUMP (start) ? XFIXNUM (start) : 1 + MOST_POSITIVE_FIXNUM; - - if (!NILP (end)) - { - CHECK_NATNUM (end); - ending = FIXNUMP (end) ? XFIXNUM (end) : 1 + MOST_POSITIVE_FIXNUM; - } - - *object_out = default_; - - if (CONSP (sequence)) - { - if (!(starting < ending)) - { - check_sequence_range (sequence, start, end, Flength (sequence)); - /* starting could be equal to ending, in which case nil is what - we want to return. */ - return Qnil; - } - - { - GC_EXTERNAL_LIST_LOOP_2 (elt, sequence) - { - if (starting <= ii && ii < ending - && check_test (test, key, item, elt) == test_not_unboundp) - { - result = make_integer (ii); - *object_out = elt; - - if (NILP (from_end)) - { - XUNGCPRO (elt); - return result; - } - } - else if (ii == ending) - { - break; - } - - ii++; - } - END_GC_EXTERNAL_LIST_LOOP (elt); - } - - if (ii < starting || (ii < ending && !NILP (end))) - { - check_sequence_range (sequence, start, end, Flength (sequence)); - } - } - else if (STRINGP (sequence)) - { - Ibyte *startp = XSTRING_DATA (sequence), *cursor = startp; - Bytecount byte_len = XSTRING_LENGTH (sequence), cursor_offset = 0; - Lisp_Object character = Qnil; - - while (cursor_offset < byte_len && ii < ending) - { - if (ii >= starting) - { - character = make_char (itext_ichar (cursor)); - - if (check_test (test, key, item, character) == test_not_unboundp) - { - result = make_integer (ii); - *object_out = character; - - if (NILP (from_end)) - { - return result; - } - } - - startp = XSTRING_DATA (sequence); - cursor = startp + cursor_offset; - if (byte_len != XSTRING_LENGTH (sequence) - || !valid_ibyteptr_p (cursor)) - { - mapping_interaction_error (caller, sequence); - } - } - - INC_IBYTEPTR (cursor); - cursor_offset = cursor - startp; - ii++; - } - - if (ii < starting || (ii < ending && !NILP (end))) - { - check_sequence_range (sequence, start, end, Flength (sequence)); - } - } - else - { - Lisp_Object object = Qnil; - len = XFIXNUM (Flength (sequence)); - check_sequence_range (sequence, start, end, make_fixnum (len)); - - ending = min (ending, len); - if (0 == len) - { - /* Catches the case where we have nil. */ - return result; - } - - if (NILP (from_end)) - { - for (ii = starting; ii < ending; ii++) - { - object = Faref (sequence, make_fixnum (ii)); - if (check_test (test, key, item, object) == test_not_unboundp) - { - result = make_integer (ii); - *object_out = object; - return result; - } - } - } - else - { - for (ii = ending - 1; ii >= starting; ii--) - { - object = Faref (sequence, make_fixnum (ii)); - if (check_test (test, key, item, object) == test_not_unboundp) - { - result = make_integer (ii); - *object_out = object; - return result; - } - } - } - } - - return result; -} - -DEFUN ("position", Fposition, 2, MANY, 0, /* -Return the index of the first occurrence of ITEM in SEQUENCE. - -Return nil if not found. See `remove*' for the meaning of the keywords. - -arguments: (ITEM SEQUENCE &key (TEST #'eql) (KEY #'identity) (START 0) (END (length SEQUENCE)) FROM-END TEST-NOT) -*/ - (int nargs, Lisp_Object *args)) -{ - Lisp_Object object = Qnil, item = args[0], sequence = args[1]; - Boolint test_not_unboundp = 1; - check_test_func_t check_test = NULL; - - PARSE_KEYWORDS (Fposition, nargs, args, 8, - (test, if_, test_not, if_not, key, start, end, from_end), - (start = Qzero)); - - check_test = get_check_test_function (item, &test, test_not, if_, if_not, - key, &test_not_unboundp); - - return position (&object, item, sequence, check_test, test_not_unboundp, - test, key, start, end, from_end, Qnil, Qposition); -} - -DEFUN ("find", Ffind, 2, MANY, 0, /* -Find the first occurrence of ITEM in SEQUENCE. - -Return the matching ITEM, or nil if not found. See `remove*' for the -meaning of the keywords. - -The keyword :default, not specified by Common Lisp, designates an object to -return instead of nil if ITEM is not found. - -arguments: (ITEM SEQUENCE &key (TEST #'eql) (KEY #'identity) (START 0) (END (length SEQUENCE)) DEFAULT FROM-END TEST-NOT) -*/ - (int nargs, Lisp_Object *args)) -{ - Lisp_Object object = Qnil, item = args[0], sequence = args[1]; - Boolint test_not_unboundp = 1; - check_test_func_t check_test = NULL; - - PARSE_KEYWORDS (Ffind, nargs, args, 9, - (test, if_, test_not, if_not, key, start, end, from_end, - default_), - (start = Qzero)); - - check_test = get_check_test_function (item, &test, test_not, if_, if_not, - key, &test_not_unboundp); - - position (&object, item, sequence, check_test, test_not_unboundp, - test, key, start, end, from_end, default_, Qposition); - - return object; -} - -/* Like Fdelq, but caller must ensure that LIST is properly - nil-terminated and ebola-free. */ - -Lisp_Object -delq_no_quit (Lisp_Object elt, Lisp_Object list) -{ - LIST_LOOP_DELETE_IF (list_elt, list, - (EQ_WITH_EBOLA_NOTICE (elt, list_elt))); - return list; -} - -/* Be VERY careful with this. This is like delq_no_quit() but - also calls free_cons() on the removed conses. You must be SURE - that no pointers to the freed conses remain around (e.g. - someone else is pointing to part of the list). This function - is useful on internal lists that are used frequently and where - the actual list doesn't escape beyond known code bounds. */ - -Lisp_Object -delq_no_quit_and_free_cons (Lisp_Object elt, Lisp_Object list) -{ - REGISTER Lisp_Object tail = list; - REGISTER Lisp_Object prev = Qnil; - - while (!NILP (tail)) - { - REGISTER Lisp_Object tem = XCAR (tail); - if (EQ (elt, tem)) - { - Lisp_Object cons_to_free = tail; - if (NILP (prev)) - list = XCDR (tail); - else - XCDR (prev) = XCDR (tail); - tail = XCDR (tail); - free_cons (cons_to_free); - } - else - { - prev = tail; - tail = XCDR (tail); - } - } - return list; -} - -DEFUN ("delete*", FdeleteX, 2, MANY, 0, /* -Remove all occurrences of ITEM in SEQUENCE, destructively. - -If SEQUENCE is a non-nil list, this modifies the list directly. A non-list -SEQUENCE will not be destructively modified, rather, if ITEM occurs in it, a -new SEQUENCE of the same type without ITEM will be returned. - -See `remove*' for a non-destructive alternative, and for explanation of the -keyword arguments. - -arguments: (ITEM SEQUENCE &key (TEST #'eql) (KEY #'identity) (START 0) (END (length SEQUENCE)) FROM-END COUNT TEST-NOT) -*/ - (int nargs, Lisp_Object *args)) -{ - Lisp_Object item = args[0], sequence = args[1]; - Elemcount starting = 0, ending = MOST_POSITIVE_FIXNUM, counting = MOST_POSITIVE_FIXNUM; - Elemcount len, ii = 0, encountered = 0, presenting = 0; - Boolint test_not_unboundp = 1; - check_test_func_t check_test = NULL; - - PARSE_KEYWORDS (FdeleteX, nargs, args, 9, - (test, if_not, if_, test_not, key, start, end, from_end, - count), (start = Qzero, count = Qunbound)); - - CHECK_SEQUENCE (sequence); - CHECK_NATNUM (start); - starting = BIGNUMP (start) ? 1 + MOST_POSITIVE_FIXNUM : XFIXNUM (start); - - if (!NILP (end)) - { - CHECK_NATNUM (end); - ending = BIGNUMP (end) ? 1 + MOST_POSITIVE_FIXNUM : XFIXNUM (end); - } - - if (!UNBOUNDP (count)) - { - if (!NILP (count)) - { - CHECK_INTEGER (count); - if (FIXNUMP (count)) - { - counting = XFIXNUM (count); - } -#ifdef HAVE_BIGNUM - else - { - counting = bignum_sign (XBIGNUM_DATA (count)) > 0 ? - 1 + MOST_POSITIVE_FIXNUM : MOST_NEGATIVE_FIXNUM - 1; - } -#endif - - if (counting < 1) - { - return sequence; - } - - if (!NILP (from_end)) - { - /* Sigh, this is inelegant. Force count_with_tail () to ignore - the count keyword, so we get the actual number of matching - elements, and can start removing from the beginning for the - from-end case. */ - for (ii = XSUBR (GET_DEFUN_LISP_OBJECT (FdeleteX))->min_args; - ii < nargs; ii += 2) - { - if (EQ (args[ii], Q_count)) - { - args[ii + 1] = Qnil; - break; - } - } - ii = 0; - } - } - } - - check_test = get_check_test_function (item, &test, test_not, if_, if_not, - key, &test_not_unboundp); - - if (CONSP (sequence)) - { - Lisp_Object prev_tail_list_elt = Qnil, ignore = Qnil; - Elemcount list_len = 0, deleted = 0; - struct gcpro gcpro1; - - if (!NILP (count) && !NILP (from_end)) - { - /* Both COUNT and FROM-END were specified; we need to traverse the - list twice. */ - Lisp_Object present = count_with_tail (&ignore, nargs, args, - QdeleteX); - - if (ZEROP (present)) - { - return sequence; - } - - presenting = XFIXNUM (present); - - /* If there are fewer items in the list than we have permission to - delete, we don't need to differentiate between the :from-end - nil and :from-end t cases. Otherwise, presenting is the number - of matching items we need to ignore before we start to - delete. */ - presenting = presenting <= counting ? 0 : presenting - counting; - } - - GCPRO1 (prev_tail_list_elt); - ii = -1; - - { - GC_EXTERNAL_LIST_LOOP_4 (list_elt, sequence, tail, list_len) - { - ii++; - - if (starting <= ii && ii < ending && - (check_test (test, key, item, list_elt) == test_not_unboundp) - && (presenting ? encountered++ >= presenting - : encountered++ < counting)) - { - if (NILP (prev_tail_list_elt)) - { - sequence = XCDR (tail); - } - else - { - XSETCDR (prev_tail_list_elt, XCDR (tail)); - } - - /* Keep tortoise from ever passing hare. */ - list_len = 0; - deleted++; - } - else - { - prev_tail_list_elt = tail; - if (ii >= ending || (!presenting && encountered > counting)) - { - break; - } - } - } - END_GC_EXTERNAL_LIST_LOOP (list_elt); - } - - UNGCPRO; - - if ((ii < starting || (ii < ending && !NILP (end))) && - !(presenting ? encountered == presenting : encountered == counting)) - { - check_sequence_range (args[1], start, end, - make_fixnum (deleted + XFIXNUM (Flength (args[1])))); - } - - return sequence; - } - else if (STRINGP (sequence)) - { - Ibyte *staging = alloca_ibytes (XSTRING_LENGTH (sequence)); - Ibyte *staging_cursor = staging, *startp = XSTRING_DATA (sequence); - Ibyte *cursor = startp; - Bytecount cursor_offset = 0, byte_len = XSTRING_LENGTH (sequence); - Lisp_Object character, result = sequence; - - if (!NILP (count) && !NILP (from_end)) - { - Lisp_Object present = count_with_tail (&character, nargs, args, - QdeleteX); - - if (ZEROP (present)) - { - return sequence; - } - - presenting = XFIXNUM (present); - - /* If there are fewer items in the list than we have permission to - delete, we don't need to differentiate between the :from-end - nil and :from-end t cases. Otherwise, presenting is the number - of matching items we need to ignore before we start to - delete. */ - presenting = presenting <= counting ? 0 : presenting - counting; - } - - ii = 0; - while (cursor_offset < byte_len) - { - if (ii >= starting && ii < ending) - { - character = make_char (itext_ichar (cursor)); - - if ((check_test (test, key, item, character) - == test_not_unboundp) - && (presenting ? encountered++ >= presenting : - encountered++ < counting)) - { - DO_NOTHING; - } - else - { - staging_cursor - += set_itext_ichar (staging_cursor, XCHAR (character)); - } - - startp = XSTRING_DATA (sequence); - cursor = startp + cursor_offset; - if (byte_len != XSTRING_LENGTH (sequence) - || !valid_ibyteptr_p (cursor)) - { - mapping_interaction_error (QdeleteX, sequence); - } - } - else - { - staging_cursor += itext_copy_ichar (cursor, staging_cursor); - } - - INC_IBYTEPTR (cursor); - cursor_offset = cursor - startp; - ii++; - } - - if (ii < starting || (ii < ending && !NILP (end))) - { - check_sequence_range (sequence, start, end, Flength (sequence)); - } - - if (0 != encountered) - { - result = make_string (staging, staging_cursor - staging); - copy_string_extents (result, sequence, 0, 0, - staging_cursor - staging); - sequence = result; - } - - return sequence; - } - else - { - Lisp_Object position0 = Qnil, object = Qnil; - Lisp_Object *staging = NULL, *staging_cursor, *staging_limit; - Elemcount positioning; - - len = XFIXNUM (Flength (sequence)); - - check_sequence_range (sequence, start, end, make_fixnum (len)); - - position0 = position (&object, item, sequence, check_test, - test_not_unboundp, test, key, start, end, - from_end, Qnil, QdeleteX); - if (NILP (position0)) - { - return sequence; - } - - ending = min (ending, len); - positioning = XFIXNUM (position0); - encountered = 1; - - if (NILP (from_end)) - { - staging = alloca_array (Lisp_Object, len - 1); - staging_cursor = staging; - - ii = 0; - while (ii < positioning) - { - *staging_cursor++ = Faref (sequence, make_fixnum (ii)); - ii++; - } - - ii = positioning + 1; - while (ii < ending) - { - object = Faref (sequence, make_fixnum (ii)); - if (encountered < counting - && (check_test (test, key, item, object) - == test_not_unboundp)) - { - encountered++; - } - else - { - *staging_cursor++ = object; - } - ii++; - } - - while (ii < len) - { - *staging_cursor++ = Faref (sequence, make_fixnum (ii)); - ii++; - } - } - else - { - staging = alloca_array (Lisp_Object, len - 1); - staging_cursor = staging_limit = staging + len - 1; - - ii = len - 1; - while (ii > positioning) - { - *--staging_cursor = Faref (sequence, make_fixnum (ii)); - ii--; - } - - ii = positioning - 1; - while (ii >= starting) - { - object = Faref (sequence, make_fixnum (ii)); - if (encountered < counting - && (check_test (test, key, item, object) == - test_not_unboundp)) - { - encountered++; - } - else - { - *--staging_cursor = object; - } - - ii--; - } - - while (ii >= 0) - { - *--staging_cursor = Faref (sequence, make_fixnum (ii)); - ii--; - } - - staging = staging_cursor; - staging_cursor = staging_limit; - } - - if (VECTORP (sequence)) - { - return Fvector (staging_cursor - staging, staging); - } - else if (BIT_VECTORP (sequence)) - { - return Fbit_vector (staging_cursor - staging, staging); - } - - /* A nil sequence will have given us a nil #'position, - above. */ - ABORT (); - - return Qnil; - } -} - -DEFUN ("remove*", FremoveX, 2, MANY, 0, /* -Remove all occurrences of ITEM in SEQUENCE, non-destructively. - -If SEQUENCE is a list, `remove*' makes a copy if that is necessary to avoid -corrupting the original SEQUENCE. - -The keywords :test and :test-not specify two-argument test and negated-test -predicates, respectively; :test defaults to `eql'. :key specifies a -one-argument function that transforms elements of SEQUENCE into \"comparison -keys\" before the test predicate is applied. See `member*' for more -information on these keywords. - -:start and :end, if given, specify indices of a subsequence of SEQUENCE to -be processed. Indices are 0-based and processing involves the subsequence -starting at the index given by :start and ending just before the index given -by :end. - -:count, if given, limits the number of items removed to the number -specified. :from-end, if given, causes processing to proceed starting from -the end instead of the beginning; in this case, this matters only if :count -is given. - -arguments: (ITEM SEQUENCE &key (TEST #'eql) (KEY #'identity) (START 0) (END (length SEQUENCE)) FROM-END COUNT TEST-NOT) -*/ - (int nargs, Lisp_Object *args)) -{ - Lisp_Object item = args[0], sequence = args[1], matched_count = Qnil, - tail = Qnil; - Elemcount starting = 0, ending = MOST_POSITIVE_FIXNUM, counting = MOST_POSITIVE_FIXNUM; - Elemcount ii = 0, encountered = 0, presenting = 0; - Boolint test_not_unboundp = 1; - check_test_func_t check_test = NULL; - - PARSE_KEYWORDS (FremoveX, nargs, args, 9, - (test, if_not, if_, test_not, key, start, end, from_end, - count), (start = Qzero)); - - if (!CONSP (sequence)) - { - return FdeleteX (nargs, args); - } - - CHECK_NATNUM (start); - starting = BIGNUMP (start) ? 1 + MOST_POSITIVE_FIXNUM : XFIXNUM (start); - - if (!NILP (end)) - { - CHECK_NATNUM (end); - ending = BIGNUMP (end) ? 1 + MOST_POSITIVE_FIXNUM : XFIXNUM (end); - } - - if (!NILP (count)) - { - CHECK_INTEGER (count); - if (FIXNUMP (count)) - { - counting = XFIXNUM (count); - } -#ifdef HAVE_BIGNUM - else - { - counting = bignum_sign (XBIGNUM_DATA (count)) > 0 ? - 1 + MOST_POSITIVE_FIXNUM : -1 + MOST_NEGATIVE_FIXNUM; - } -#endif - - if (counting <= 0) - { - return sequence; - } - - if (!NILP (from_end)) - { - /* Sigh, this is inelegant. Force count_with_tail () to ignore the - count keyword, so we get the actual number of matching - elements, and can start removing from the beginning for the - from-end case. */ - for (ii = XSUBR (GET_DEFUN_LISP_OBJECT (FremoveX))->min_args; - ii < nargs; ii += 2) - { - if (EQ (args[ii], Q_count)) - { - args[ii + 1] = Qnil; - break; - } - } - ii = 0; - } - } - - check_test = get_check_test_function (item, &test, test_not, if_, if_not, - key, &test_not_unboundp); - - matched_count = count_with_tail (&tail, nargs, args, QremoveX); - - if (!ZEROP (matched_count)) - { - Lisp_Object result = Qnil, result_tail = Qnil; - struct gcpro gcpro1, gcpro2; - - if (!NILP (count) && !NILP (from_end)) - { - presenting = XFIXNUM (matched_count); - - /* If there are fewer matching elements in the list than we have - permission to delete, we don't need to differentiate between - the :from-end nil and :from-end t cases. Otherwise, presenting - is the number of matching items we need to ignore before we - start to delete. */ - presenting = presenting <= counting ? 0 : presenting - counting; - } - - GCPRO2 (result, tail); - { - GC_EXTERNAL_LIST_LOOP_3 (elt, sequence, tailing) - { - if (EQ (tail, tailing)) - { - XUNGCPRO (elt); - UNGCPRO; - - if (NILP (result)) - { - return XCDR (tail); - } - - XSETCDR (result_tail, XCDR (tail)); - return result; - } - else if (starting <= ii && ii < ending && - (check_test (test, key, item, elt) == test_not_unboundp) - && (presenting ? encountered++ >= presenting - : encountered++ < counting)) - { - DO_NOTHING; - } - else if (NILP (result)) - { - result = result_tail = Fcons (elt, Qnil); - } - else - { - XSETCDR (result_tail, Fcons (elt, Qnil)); - result_tail = XCDR (result_tail); - } - - if (ii == ending) - { - break; - } - - ii++; - } - END_GC_EXTERNAL_LIST_LOOP (elt); - } - UNGCPRO; - - if (ii < starting || (ii < ending && !NILP (end))) - { - check_sequence_range (args[0], start, end, Flength (args[0])); - } - - return result; - } - - return sequence; -} - -Lisp_Object -remassoc_no_quit (Lisp_Object key, Lisp_Object alist) -{ - LIST_LOOP_DELETE_IF (elt, alist, - (CONSP (elt) && - internal_equal (key, XCAR (elt), 0))); - return alist; -} - -/* no quit, no errors; be careful */ - -Lisp_Object -remassq_no_quit (Lisp_Object key, Lisp_Object alist) -{ - LIST_LOOP_DELETE_IF (elt, alist, - (CONSP (elt) && - EQ_WITH_EBOLA_NOTICE (key, XCAR (elt)))); - return alist; -} - -/* Like Fremrassq, fast and unsafe; be careful */ -Lisp_Object -remrassq_no_quit (Lisp_Object value, Lisp_Object alist) -{ - LIST_LOOP_DELETE_IF (elt, alist, - (CONSP (elt) && - EQ_WITH_EBOLA_NOTICE (value, XCDR (elt)))); - return alist; -} - -/* Remove duplicate elements between START and END from LIST, a non-nil - list; if COPY is zero, do so destructively. Items to delete are selected - according to the algorithm used when :from-end t is passed to - #'delete-duplicates. Error if LIST is ill-formed or circular. - - TEST and KEY are as in #'remove*; CHECK_TEST and TEST_NOT_UNBOUNDP should - reflect them, having been initialised with get_check_match_function() or - get_check_test_function(). */ -static Lisp_Object -list_delete_duplicates_from_end (Lisp_Object list, - check_test_func_t check_test, - Boolint test_not_unboundp, - Lisp_Object test, Lisp_Object key, - Lisp_Object start, - Lisp_Object end, Boolint copy) -{ - Lisp_Object checking = Qnil, result = list; - Lisp_Object keyed, positioned, position_cons = Qnil, result_tail; - Elemcount len = XFIXNUM (Flength (list)), pos, starting = XFIXNUM (start); - Elemcount ending = (NILP (end) ? len : XFIXNUM (end)), greatest_pos_seen = -1; - Elemcount ii = 0; - struct gcpro gcpro1; - - /* We can't delete (or remove) as we go, because that breaks START and - END. We could if END were nil, and that would change an ON(N + 2) - algorithm to an ON^2 algorithm. Here and now it doesn't matter, though, - #'delete-duplicates is relatively expensive no matter what. */ - struct Lisp_Bit_Vector *deleting - = (Lisp_Bit_Vector *) ALLOCA (sizeof (struct Lisp_Bit_Vector) - + (sizeof (long) - * (BIT_VECTOR_LONG_STORAGE (len) - - 1))); - - check_sequence_range (list, start, end, make_integer (len)); - - deleting->size = len; - memset (&(deleting->bits), 0, - sizeof (long) * BIT_VECTOR_LONG_STORAGE (len)); - - GCPRO1 (keyed); - - { - GC_EXTERNAL_LIST_LOOP_3 (elt, list, tail) - { - if (!(starting <= ii && ii <= ending) || bit_vector_bit (deleting, ii)) - { - ii++; - continue; - } - - keyed = KEY (key, elt); - checking = XCDR (tail); - pos = ii + 1; - - while (!NILP ((positioned = list_position_cons_before - (&position_cons, keyed, checking, check_test, - test_not_unboundp, test, key, 0, - make_fixnum (max (starting - pos, 0)), - make_fixnum (ending - pos))))) - { - pos = XFIXNUM (positioned) + pos; - set_bit_vector_bit (deleting, pos, 1); - greatest_pos_seen = max (greatest_pos_seen, pos); - checking = NILP (position_cons) ? - XCDR (checking) : XCDR (XCDR (position_cons)); - pos += 1; - } - ii++; - } - END_GC_EXTERNAL_LIST_LOOP (elt); - } - - UNGCPRO; - - ii = 0; - - if (greatest_pos_seen > -1) - { - if (copy) - { - result = result_tail = Fcons (XCAR (list), Qnil); - list = XCDR (list); - ii = 1; - - { - EXTERNAL_LIST_LOOP_3 (elt, list, tail) - { - if (ii == greatest_pos_seen) - { - XSETCDR (result_tail, XCDR (tail)); - break; - } - else if (!bit_vector_bit (deleting, ii)) - { - XSETCDR (result_tail, Fcons (elt, Qnil)); - result_tail = XCDR (result_tail); - } - ii++; - } - } - } - else - { - EXTERNAL_LIST_LOOP_DELETE_IF (elt, list, - bit_vector_bit (deleting, ii++)); - } - } - - return result; -} - -DEFUN ("delete-duplicates", Fdelete_duplicates, 1, MANY, 0, /* -Remove all duplicate elements from SEQUENCE, destructively. - -If SEQUENCE is a list and has duplicates, modify and return it. Note that -SEQUENCE may start with an element to be deleted; because of this, if -modifying a variable, be sure to write `(setq VARIABLE (delete-duplicates -VARIABLE))' to be certain to have a list without duplicate elements. - -If SEQUENCE is an array and has duplicates, return a newly-allocated array -of the same type comprising all unique elements of SEQUENCE. - -If there are no duplicate elements in SEQUENCE, return it unmodified. - -See `remove*' for the meaning of the keywords. See `remove-duplicates' for -a non-destructive version of this function. - -arguments: (SEQUENCE &key (TEST #'eql) (KEY #'identity) (START 0) END FROM-END TEST-NOT) -*/ - (int nargs, Lisp_Object *args)) -{ - Lisp_Object sequence = args[0], keyed = Qnil; - Lisp_Object positioned = Qnil, ignore = Qnil; - Elemcount starting = 0, ending = MOST_POSITIVE_FIXNUM, len, ii = 0, jj = 0; - Boolint test_not_unboundp = 1; - check_test_func_t check_test = NULL; - struct gcpro gcpro1, gcpro2; - - PARSE_KEYWORDS (Fdelete_duplicates, nargs, args, 6, - (test, key, test_not, start, end, from_end), - (start = Qzero)); - - CHECK_SEQUENCE (sequence); - CHECK_NATNUM (start); - starting = BIGNUMP (start) ? 1 + MOST_POSITIVE_FIXNUM : XFIXNUM (start); - - if (!NILP (end)) - { - CHECK_NATNUM (end); - ending = BIGNUMP (end) ? 1 + MOST_POSITIVE_FIXNUM : XFIXNUM (end); - } - - CHECK_KEY_ARGUMENT (key); - - get_check_match_function (&test, test_not, Qnil, Qnil, key, - &test_not_unboundp, &check_test); - - if (CONSP (sequence)) - { - if (NILP (from_end)) - { - Lisp_Object prev_tail = Qnil; - Elemcount deleted = 0; - - GCPRO2 (keyed, prev_tail); - - { - GC_EXTERNAL_LIST_LOOP_3 (elt, sequence, tail) - { - if (starting <= ii && ii < ending) - { - keyed = KEY (key, elt); - positioned - = list_position_cons_before (&ignore, keyed, - XCDR (tail), check_test, - test_not_unboundp, test, key, - 0, make_fixnum (max (starting - - (ii + 1), - 0)), - make_fixnum (ending - - (ii + 1))); - if (!NILP (positioned)) - { - sequence = XCDR (tail); - deleted++; - } - else - { - break; - } - } - else - { - break; - } - - ii++; - } - END_GC_EXTERNAL_LIST_LOOP (elt); - } - { - GC_EXTERNAL_LIST_LOOP_3 (elt, sequence, tail) - { - if (!(starting <= ii && ii <= ending)) - { - prev_tail = tail; - ii++; - continue; - } - - keyed = KEY (key, elt); - positioned - = list_position_cons_before (&ignore, keyed, XCDR (tail), - check_test, test_not_unboundp, - test, key, 0, - make_fixnum (max (starting - - (ii + 1), 0)), - make_fixnum (ending - (ii + 1))); - if (!NILP (positioned)) - { - /* We know this isn't the first iteration of the loop, - because we advanced above to the point where we have at - least one non-duplicate entry at the head of the - list. */ - XSETCDR (prev_tail, XCDR (tail)); - len = 0; - deleted++; - } - else - { - prev_tail = tail; - if (ii >= ending) - { - break; - } - } - - ii++; - } - END_GC_EXTERNAL_LIST_LOOP (elt); - } - - UNGCPRO; - - if ((ii < starting || (ii < ending && !NILP (end)))) - { - check_sequence_range (args[0], start, end, - make_fixnum (deleted - + XFIXNUM (Flength (args[0])))); - } - } - else - { - sequence = list_delete_duplicates_from_end (sequence, check_test, - test_not_unboundp, - test, key, start, end, - 0); - } - } - else if (STRINGP (sequence)) - { - Lisp_Object elt = Qnil; - - if (EQ (Qidentity, key)) - { - /* We know all the elements will be characters; set check_test to - reflect that. This isn't useful if KEY is not #'identity, since - it may return non-characters for the elements. */ - check_test = get_check_test_function (make_char ('a'), - &test, test_not, - Qnil, Qnil, key, - &test_not_unboundp); - } - - if (NILP (from_end)) - { - Bytecount byte_len = XSTRING_LENGTH (sequence), cursor_offset = 0; - Ibyte *staging = alloca_ibytes (byte_len), *staging_cursor = staging; - Ibyte *cursor = XSTRING_DATA (sequence), *startp = cursor; - Elemcount deleted = 0; - - GCPRO1 (elt); - - while (cursor_offset < byte_len) - { - if (starting <= ii && ii < ending) - { - Ibyte *cursor0 = cursor; - Bytecount cursor0_offset; - Boolint delete_this = 0; - - elt = KEY (key, make_char (itext_ichar (cursor))); - INC_IBYTEPTR (cursor0); - cursor0_offset = cursor0 - startp; - - for (jj = ii + 1; jj < ending && cursor0_offset < byte_len; - jj++) - { - if (check_test (test, key, elt, - make_char (itext_ichar (cursor0))) - == test_not_unboundp) - { - delete_this = 1; - deleted++; - break; - } - - startp = XSTRING_DATA (sequence); - cursor0 = startp + cursor0_offset; - if (byte_len != XSTRING_LENGTH (sequence) - || !valid_ibyteptr_p (cursor0)) - { - mapping_interaction_error (Qdelete_duplicates, - sequence); - } - - INC_IBYTEPTR (cursor0); - cursor0_offset = cursor0 - startp; - } - - startp = XSTRING_DATA (sequence); - cursor = startp + cursor_offset; - - if (byte_len != XSTRING_LENGTH (sequence) - || !valid_ibyteptr_p (cursor)) - { - mapping_interaction_error (Qdelete_duplicates, sequence); - } - - if (!delete_this) - { - staging_cursor - += itext_copy_ichar (cursor, staging_cursor); - - } - } - else - { - staging_cursor += itext_copy_ichar (cursor, staging_cursor); - } - - INC_IBYTEPTR (cursor); - cursor_offset = cursor - startp; - ii++; - } - - UNGCPRO; - - if (ii < starting || (ii < ending && !NILP (end))) - { - check_sequence_range (sequence, start, end, Flength (sequence)); - } - - if (0 != deleted) - { - sequence = make_string (staging, staging_cursor - staging); - } - } - else - { - Elemcount deleted = 0; - Ibyte *staging = alloca_ibytes ((len = string_char_length (sequence)) - * MAX_ICHAR_LEN); - Ibyte *staging_cursor = staging, *startp = XSTRING_DATA (sequence); - Ibyte *endp = startp + XSTRING_LENGTH (sequence); - struct Lisp_Bit_Vector *deleting - = (Lisp_Bit_Vector *) ALLOCA (sizeof (struct Lisp_Bit_Vector) - + (sizeof (long) - * (BIT_VECTOR_LONG_STORAGE (len) - - 1))); - - check_sequence_range (sequence, start, end, make_integer (len)); - - /* For the from_end t case; transform contents to an array with - elements addressable in constant time, use the same algorithm - as for vectors. */ - deleting->size = len; - memset (&(deleting->bits), 0, - sizeof (long) * BIT_VECTOR_LONG_STORAGE (len)); - - while (startp < endp) - { - itext_copy_ichar (startp, staging + (ii * MAX_ICHAR_LEN)); - INC_IBYTEPTR (startp); - ii++; - } - - GCPRO1 (elt); - - ending = min (ending, len); - - for (ii = ending - 1; ii >= starting; ii--) - { - elt = KEY (key, make_char (itext_ichar (staging + - (ii * MAX_ICHAR_LEN)))); - for (jj = ii - 1; jj >= starting; jj--) - { - if (check_test (test, key, elt, - make_char (itext_ichar - (staging + (jj * MAX_ICHAR_LEN)))) - == test_not_unboundp) - { - set_bit_vector_bit (deleting, ii, 1); - deleted++; - break; - } - } - } - - UNGCPRO; - - if (0 != deleted) - { - startp = XSTRING_DATA (sequence); - - for (ii = 0; ii < len; ii++) - { - if (!bit_vector_bit (deleting, ii)) - { - staging_cursor - += itext_copy_ichar (startp, staging_cursor); - } - - INC_IBYTEPTR (startp); - } - - sequence = make_string (staging, staging_cursor - staging); - } - } - } - else if (VECTORP (sequence)) - { - Elemcount deleted = 0; - Lisp_Object *content = XVECTOR_DATA (sequence); - struct Lisp_Bit_Vector *deleting; - Lisp_Object elt = Qnil; - - len = XVECTOR_LENGTH (sequence); - check_sequence_range (sequence, start, end, make_integer (len)); - - deleting = (Lisp_Bit_Vector *) ALLOCA (sizeof (struct Lisp_Bit_Vector) - + (sizeof (long) - * (BIT_VECTOR_LONG_STORAGE (len) - - 1))); - deleting->size = len; - memset (&(deleting->bits), 0, - sizeof (long) * BIT_VECTOR_LONG_STORAGE (len)); - - GCPRO1 (elt); - - ending = min (ending, len); - - if (NILP (from_end)) - { - for (ii = starting; ii < ending; ii++) - { - elt = KEY (key, content[ii]); - - for (jj = ii + 1; jj < ending; jj++) - { - if (check_test (test, key, elt, content[jj]) - == test_not_unboundp) - { - set_bit_vector_bit (deleting, ii, 1); - deleted++; - break; - } - } - } - } - else - { - for (ii = ending - 1; ii >= starting; ii--) - { - elt = KEY (key, content[ii]); - - for (jj = ii - 1; jj >= starting; jj--) - { - if (check_test (test, key, elt, content[jj]) - == test_not_unboundp) - { - set_bit_vector_bit (deleting, ii, 1); - deleted++; - break; - } - } - } - } - - UNGCPRO; - - if (deleted) - { - Lisp_Object res = make_vector (len - deleted, Qnil), - *res_content = XVECTOR_DATA (res); - - for (ii = jj = 0; ii < len; ii++) - { - if (!bit_vector_bit (deleting, ii)) - { - res_content[jj++] = content[ii]; - } - } - - sequence = res; - } - } - else if (BIT_VECTORP (sequence)) - { - Lisp_Bit_Vector *bv = XBIT_VECTOR (sequence); - Elemcount deleted = 0; - /* I'm a little irritated at this. Basically, the only reasonable - thing delete-duplicates should do if handed a bit vector is return - something of maximum length two and minimum length 0 (because - that's the possible number of distinct elements if EQ is regarded - as identity, which it should be). But to support arbitrary TEST - and KEY arguments, which may be non-deterministic from our - perspective, we need the same algorithm as for vectors. */ - struct Lisp_Bit_Vector *deleting; - Lisp_Object elt = Qnil; - - len = bit_vector_length (bv); - - if (EQ (Qidentity, key)) - { - /* We know all the elements will be bits; set check_test to - reflect that. This isn't useful if KEY is not #'identity, since - it may return non-bits for the elements. */ - check_test = get_check_test_function (Qzero, &test, test_not, - Qnil, Qnil, key, - &test_not_unboundp); - } - - check_sequence_range (sequence, start, end, make_integer (len)); - - deleting = (Lisp_Bit_Vector *) ALLOCA (sizeof (struct Lisp_Bit_Vector) - + (sizeof (long) - * (BIT_VECTOR_LONG_STORAGE (len) - - 1))); - deleting->size = len; - memset (&(deleting->bits), 0, - sizeof (long) * BIT_VECTOR_LONG_STORAGE (len)); - - ending = min (ending, len); - - GCPRO1 (elt); - - if (NILP (from_end)) - { - for (ii = starting; ii < ending; ii++) - { - elt = KEY (key, make_fixnum (bit_vector_bit (bv, ii))); - - for (jj = ii + 1; jj < ending; jj++) - { - if (check_test (test, key, elt, - make_fixnum (bit_vector_bit (bv, jj))) - == test_not_unboundp) - { - set_bit_vector_bit (deleting, ii, 1); - deleted++; - break; - } - } - } - } - else - { - for (ii = ending - 1; ii >= starting; ii--) - { - elt = KEY (key, make_fixnum (bit_vector_bit (bv, ii))); - - for (jj = ii - 1; jj >= starting; jj--) - { - if (check_test (test, key, elt, - make_fixnum (bit_vector_bit (bv, jj))) - == test_not_unboundp) - { - set_bit_vector_bit (deleting, ii, 1); - deleted++; - break; - } - } - } - } - - UNGCPRO; - - if (deleted) - { - Lisp_Object res = make_bit_vector (len - deleted, Qzero); - Lisp_Bit_Vector *resbv = XBIT_VECTOR (res); - - for (ii = jj = 0; ii < len; ii++) - { - if (!bit_vector_bit (deleting, ii)) - { - set_bit_vector_bit (resbv, jj++, bit_vector_bit (bv, ii)); - } - } - - sequence = res; - } - } - - return sequence; -} - -DEFUN ("remove-duplicates", Fremove_duplicates, 1, MANY, 0, /* -Remove duplicate elements from SEQUENCE, non-destructively. - -If there are no duplicate elements in SEQUENCE, return it unmodified; -otherwise, return a new object. If SEQUENCE is a list, the new object may -share list structure with SEQUENCE. - -See `remove*' for the meaning of the keywords. - -arguments: (SEQUENCE &key (TEST #'eql) (KEY #'identity) (START 0) END FROM-END TEST-NOT) -*/ - (int nargs, Lisp_Object *args)) -{ - Lisp_Object sequence = args[0], keyed, positioned = Qnil; - Lisp_Object result = sequence, result_tail = result, cursor = Qnil; - Lisp_Object cons_with_shared_tail = Qnil; - Elemcount starting = 0, ending = MOST_POSITIVE_FIXNUM, ii = 0; - Boolint test_not_unboundp = 1; - check_test_func_t check_test = NULL; - struct gcpro gcpro1, gcpro2; - - PARSE_KEYWORDS (Fremove_duplicates, nargs, args, 6, - (test, key, test_not, start, end, from_end), - (start = Qzero)); - - CHECK_SEQUENCE (sequence); - - if (!CONSP (sequence)) - { - return Fdelete_duplicates (nargs, args); - } - - CHECK_NATNUM (start); - starting = BIGNUMP (start) ? 1 + MOST_POSITIVE_FIXNUM : XFIXNUM (start); - - if (!NILP (end)) - { - CHECK_NATNUM (end); - ending = BIGNUMP (end) ? 1 + MOST_POSITIVE_FIXNUM : XFIXNUM (end); - } - - if (NILP (key)) - { - key = Qidentity; - } - - get_check_match_function (&test, test_not, Qnil, Qnil, key, - &test_not_unboundp, &check_test); - - if (NILP (from_end)) - { - Lisp_Object ignore = Qnil; - - GCPRO2 (keyed, result); - - { - GC_EXTERNAL_LIST_LOOP_3 (elt, sequence, tail) - { - if (starting <= ii && ii <= ending) - { - keyed = KEY (key, elt); - positioned - = list_position_cons_before (&ignore, keyed, XCDR (tail), - check_test, test_not_unboundp, - test, key, 0, - make_fixnum (max (starting - - (ii + 1), 0)), - make_fixnum (ending - (ii + 1))); - if (!NILP (positioned)) - { - sequence = result = result_tail = XCDR (tail); - } - else - { - break; - } - } - else - { - break; - } - - ii++; - } - END_GC_EXTERNAL_LIST_LOOP (elt); - } - - { - GC_EXTERNAL_LIST_LOOP_3 (elt, sequence, tail) - { - if (!(starting <= ii && ii <= ending)) - { - ii++; - continue; - } - - /* For this algorithm, each time we encounter an object to be - removed, copy the output list from the tail beyond the last - removed cons to this one. Otherwise, the tail of the output list - is shared with the input list, which is OK. */ - - keyed = KEY (key, elt); - positioned - = list_position_cons_before (&ignore, keyed, XCDR (tail), - check_test, test_not_unboundp, - test, key, 0, - make_fixnum (max (starting - (ii + 1), - 0)), - make_fixnum (ending - (ii + 1))); - if (!NILP (positioned)) - { - if (EQ (result, sequence)) - { - result = cons_with_shared_tail - = Fcons (XCAR (sequence), XCDR (sequence)); - } - - result_tail = cons_with_shared_tail; - cursor = XCDR (cons_with_shared_tail); - - while (!EQ (cursor, tail) && !NILP (cursor)) - { - XSETCDR (result_tail, Fcons (XCAR (cursor), Qnil)); - result_tail = XCDR (result_tail); - cursor = XCDR (cursor); - } - - XSETCDR (result_tail, XCDR (tail)); - cons_with_shared_tail = result_tail; - } - - ii++; - } - END_GC_EXTERNAL_LIST_LOOP (elt); - } - - UNGCPRO; - - if ((ii < starting || (ii < ending && !NILP (end)))) - { - check_sequence_range (args[0], start, end, Flength (args[0])); - } - } - else - { - result = list_delete_duplicates_from_end (sequence, check_test, - test_not_unboundp, test, key, - start, end, 1); - } - - return result; -} -#undef KEY - -DEFUN ("nreverse", Fnreverse, 1, 1, 0, /* -Reverse SEQUENCE, destructively. - -Return the beginning of the reversed sequence, which will be a distinct Lisp -object if SEQUENCE is a list with length greater than one. See also -`reverse', the non-destructive version of this function. -*/ - (sequence)) -{ - CHECK_SEQUENCE (sequence); - - if (CONSP (sequence)) - { - struct gcpro gcpro1, gcpro2; - Lisp_Object prev = Qnil; - Lisp_Object tail = sequence; - - /* We gcpro our args; see `nconc' */ - GCPRO2 (prev, tail); - while (!NILP (tail)) - { - REGISTER Lisp_Object next; - CONCHECK_CONS (tail); - next = XCDR (tail); - XCDR (tail) = prev; - prev = tail; - tail = next; - } - UNGCPRO; - return prev; - } - else if (VECTORP (sequence)) - { - Elemcount length = XVECTOR_LENGTH (sequence), ii = length; - Elemcount half = length / 2; - Lisp_Object swap = Qnil; - CHECK_LISP_WRITEABLE (sequence); - - while (ii > half) - { - swap = XVECTOR_DATA (sequence) [length - ii]; - XVECTOR_DATA (sequence) [length - ii] - = XVECTOR_DATA (sequence) [ii - 1]; - XVECTOR_DATA (sequence) [ii - 1] = swap; - --ii; - } - } - else if (STRINGP (sequence)) - { - Elemcount length = XSTRING_LENGTH (sequence); - Ibyte *staging = alloca_ibytes (length), *staging_end = staging + length; - Ibyte *cursor = XSTRING_DATA (sequence), *endp = cursor + length; - - CHECK_LISP_WRITEABLE (sequence); - while (cursor < endp) - { - staging_end -= itext_ichar_len (cursor); - itext_copy_ichar (cursor, staging_end); - INC_IBYTEPTR (cursor); - } - - assert (staging == staging_end); - - memcpy (XSTRING_DATA (sequence), staging, length); - init_string_ascii_begin (sequence); - bump_string_modiff (sequence); - sledgehammer_check_ascii_begin (sequence); - } - else if (BIT_VECTORP (sequence)) - { - Lisp_Bit_Vector *bv = XBIT_VECTOR (sequence); - Elemcount length = bit_vector_length (bv), ii = length; - Elemcount half = length / 2; - int swap = 0; - - CHECK_LISP_WRITEABLE (sequence); - while (ii > half) - { - swap = bit_vector_bit (bv, length - ii); - set_bit_vector_bit (bv, length - ii, bit_vector_bit (bv, ii - 1)); - set_bit_vector_bit (bv, ii - 1, swap); - --ii; - } - } - else - { - assert (NILP (sequence)); - } - - return sequence; -} - -DEFUN ("reverse", Freverse, 1, 1, 0, /* -Reverse SEQUENCE, copying. Return the reversed sequence. -See also the function `nreverse', which is used more often. -*/ - (sequence)) -{ - Lisp_Object result = Qnil; - - CHECK_SEQUENCE (sequence); - - if (CONSP (sequence)) - { - EXTERNAL_LIST_LOOP_2 (elt, sequence) - { - result = Fcons (elt, result); - } - } - else if (VECTORP (sequence)) - { - Elemcount length = XVECTOR_LENGTH (sequence), ii = length; - Lisp_Object *staging = alloca_array (Lisp_Object, length); - - while (ii > 0) - { - staging[length - ii] = XVECTOR_DATA (sequence) [ii - 1]; - --ii; - } - - result = Fvector (length, staging); - } - else if (STRINGP (sequence)) - { - Elemcount length = XSTRING_LENGTH (sequence); - Ibyte *staging = alloca_ibytes (length), *staging_end = staging + length; - Ibyte *cursor = XSTRING_DATA (sequence), *endp = cursor + length; - - while (cursor < endp) - { - staging_end -= itext_ichar_len (cursor); - itext_copy_ichar (cursor, staging_end); - INC_IBYTEPTR (cursor); - } - - assert (staging == staging_end); - - result = make_string (staging, length); - } - else if (BIT_VECTORP (sequence)) - { - Lisp_Bit_Vector *bv = XBIT_VECTOR (sequence), *res; - Elemcount length = bit_vector_length (bv), ii = length; - - result = make_bit_vector (length, Qzero); - res = XBIT_VECTOR (result); - - while (ii > 0) - { - set_bit_vector_bit (res, length - ii, bit_vector_bit (bv, ii - 1)); - --ii; - } - } - else - { - assert (NILP (sequence)); - } - - return result; -} - -Lisp_Object -list_merge (Lisp_Object org_l1, Lisp_Object org_l2, - check_test_func_t check_merge, - Lisp_Object predicate, Lisp_Object key) -{ - Lisp_Object value; - Lisp_Object tail; - Lisp_Object tem; - Lisp_Object l1, l2; - Lisp_Object tortoises[2]; - struct gcpro gcpro1, gcpro2, gcpro3, gcpro4, gcpro5; - int l1_count = 0, l2_count = 0; - - l1 = org_l1; - l2 = org_l2; - tail = Qnil; - value = Qnil; - tortoises[0] = org_l1; - tortoises[1] = org_l2; - - /* It is sufficient to protect org_l1 and org_l2. When l1 and l2 are - updated, we copy the new values back into the org_ vars. */ - - GCPRO5 (org_l1, org_l2, predicate, value, tortoises[0]); - gcpro5.nvars = 2; - - while (1) - { - if (NILP (l1)) - { - UNGCPRO; - if (NILP (tail)) - return l2; - Fsetcdr (tail, l2); - return value; - } - if (NILP (l2)) - { - UNGCPRO; - if (NILP (tail)) - return l1; - Fsetcdr (tail, l1); - return value; - } - - if (check_merge (predicate, key, Fcar (l2), Fcar (l1)) == 0) - { - tem = l1; - l1 = Fcdr (l1); - org_l1 = l1; - - if (l1_count++ > CIRCULAR_LIST_SUSPICION_LENGTH) - { - if (l1_count & 1) - { - if (!CONSP (tortoises[0])) - { - mapping_interaction_error (Qmerge, tortoises[0]); - } - - tortoises[0] = XCDR (tortoises[0]); - } - - if (EQ (org_l1, tortoises[0])) - { - signal_circular_list_error (org_l1); - } - } - } - else - { - tem = l2; - l2 = Fcdr (l2); - org_l2 = l2; - - if (l2_count++ > CIRCULAR_LIST_SUSPICION_LENGTH) - { - if (l2_count & 1) - { - if (!CONSP (tortoises[1])) - { - mapping_interaction_error (Qmerge, tortoises[1]); - } - - tortoises[1] = XCDR (tortoises[1]); - } - - if (EQ (org_l2, tortoises[1])) - { - signal_circular_list_error (org_l2); - } - } - } - - if (NILP (tail)) - value = tem; - else - Fsetcdr (tail, tem); - - tail = tem; - } -} - -static void -array_merge (Lisp_Object *dest, Elemcount dest_len, - Lisp_Object *front, Elemcount front_len, - Lisp_Object *back, Elemcount back_len, - check_test_func_t check_merge, - Lisp_Object predicate, Lisp_Object key) -{ - Elemcount ii, fronting, backing; - Lisp_Object *front_staging = front; - Lisp_Object *back_staging = back; - struct gcpro gcpro1, gcpro2; - - assert (dest_len == (back_len + front_len)); - - if (0 == dest_len) - { - return; - } - - if (front >= dest && front < (dest + dest_len)) - { - front_staging = alloca_array (Lisp_Object, front_len); - - for (ii = 0; ii < front_len; ++ii) - { - front_staging[ii] = front[ii]; - } - } - - if (back >= dest && back < (dest + dest_len)) - { - back_staging = alloca_array (Lisp_Object, back_len); - - for (ii = 0; ii < back_len; ++ii) - { - back_staging[ii] = back[ii]; - } - } - - GCPRO2 (front_staging[0], back_staging[0]); - gcpro1.nvars = front_len; - gcpro2.nvars = back_len; - - for (ii = fronting = backing = 0; ii < dest_len; ++ii) - { - if (fronting >= front_len) - { - while (ii < dest_len) - { - dest[ii] = back_staging[backing]; - ++ii, ++backing; - } - UNGCPRO; - return; - } - - if (backing >= back_len) - { - while (ii < dest_len) - { - dest[ii] = front_staging[fronting]; - ++ii, ++fronting; - } - UNGCPRO; - return; - } - - if (check_merge (predicate, key, back_staging[backing], - front_staging[fronting]) == 0) - { - dest[ii] = front_staging[fronting]; - ++fronting; - } - else - { - dest[ii] = back_staging[backing]; - ++backing; - } - } - - UNGCPRO; -} - -static Lisp_Object -list_array_merge_into_list (Lisp_Object list, - Lisp_Object *array, Elemcount array_len, - check_test_func_t check_merge, - Lisp_Object predicate, Lisp_Object key, - Boolint reverse_order) -{ - Lisp_Object tail = Qnil, value = Qnil, tortoise = list; - struct gcpro gcpro1, gcpro2, gcpro3, gcpro4; - Elemcount array_index = 0; - int looped = 0; - - GCPRO4 (list, tail, value, tortoise); - - while (1) - { - if (NILP (list)) - { - UNGCPRO; - - if (NILP (tail)) - { - return Flist (array_len, array); - } - - Fsetcdr (tail, Flist (array_len - array_index, array + array_index)); - return value; - } - - if (array_index >= array_len) - { - UNGCPRO; - if (NILP (tail)) - { - return list; - } - - Fsetcdr (tail, list); - return value; - } - - - if (reverse_order ? - check_merge (predicate, key, Fcar (list), array [array_index]) - : !check_merge (predicate, key, array [array_index], Fcar (list))) - { - if (NILP (tail)) - { - value = tail = list; - } - else - { - Fsetcdr (tail, list); - tail = XCDR (tail); - } - - list = Fcdr (list); - } - else - { - if (NILP (tail)) - { - value = tail = Fcons (array [array_index], Qnil); - } - else - { - Fsetcdr (tail, Fcons (array [array_index], tail)); - tail = XCDR (tail); - } - ++array_index; - } - - if (++looped > CIRCULAR_LIST_SUSPICION_LENGTH) - { - if (looped & 1) - { - tortoise = XCDR (tortoise); - } - - if (EQ (list, tortoise)) - { - signal_circular_list_error (list); - } - } - } -} - -static void -list_list_merge_into_array (Lisp_Object *output, Elemcount output_len, - Lisp_Object list_one, Lisp_Object list_two, - check_test_func_t check_merge, - Lisp_Object predicate, Lisp_Object key) -{ - Elemcount output_index = 0; - - while (output_index < output_len) - { - if (NILP (list_one)) - { - while (output_index < output_len) - { - output [output_index] = Fcar (list_two); - list_two = Fcdr (list_two), ++output_index; - } - return; - } - - if (NILP (list_two)) - { - while (output_index < output_len) - { - output [output_index] = Fcar (list_one); - list_one = Fcdr (list_one), ++output_index; - } - return; - } - - if (check_merge (predicate, key, Fcar (list_two), Fcar (list_one)) - == 0) - { - output [output_index] = XCAR (list_one); - list_one = XCDR (list_one); - } - else - { - output [output_index] = XCAR (list_two); - list_two = XCDR (list_two); - } - - ++output_index; - - /* No need to check for circularity. */ - } -} - -static void -list_array_merge_into_array (Lisp_Object *output, Elemcount output_len, - Lisp_Object list, - Lisp_Object *array, Elemcount array_len, - check_test_func_t check_merge, - Lisp_Object predicate, Lisp_Object key, - Boolint reverse_order) -{ - Elemcount output_index = 0, array_index = 0; - - while (output_index < output_len) - { - if (NILP (list)) - { - if (array_len - array_index != output_len - output_index) - { - mapping_interaction_error (Qmerge, list); - } - - while (array_index < array_len) - { - output [output_index++] = array [array_index++]; - } - - return; - } - - if (array_index >= array_len) - { - while (output_index < output_len) - { - output [output_index++] = Fcar (list); - list = Fcdr (list); - } - - return; - } - - if (reverse_order ? - check_merge (predicate, key, Fcar (list), array [array_index]) : - !check_merge (predicate, key, array [array_index], Fcar (list))) - { - output [output_index] = XCAR (list); - list = XCDR (list); - } - else - { - output [output_index] = array [array_index]; - ++array_index; - } - - ++output_index; - } -} - -#define STRING_DATA_TO_OBJECT_ARRAY(strdata, c_array, counter, len) \ - do { \ - c_array = alloca_array (Lisp_Object, len); \ - for (counter = 0; counter < len; ++counter) \ - { \ - c_array[counter] = make_char (itext_ichar (strdata)); \ - INC_IBYTEPTR (strdata); \ - } \ - } while (0) - -#define BIT_VECTOR_TO_OBJECT_ARRAY(v, c_array, counter, len) do { \ - c_array = alloca_array (Lisp_Object, len); \ - for (counter = 0; counter < len; ++counter) \ - { \ - c_array[counter] = make_fixnum (bit_vector_bit (v, counter)); \ - } \ - } while (0) - -DEFUN ("merge", Fmerge, 4, MANY, 0, /* -Destructively merge SEQUENCE-ONE and SEQUENCE-TWO, producing a new sequence. - -TYPE is the type of sequence to return. PREDICATE is a `less-than' -predicate on the elements. - -Optional keyword argument KEY is a function used to extract an object to be -used for comparison from each element of SEQUENCE-ONE and SEQUENCE-TWO. - -arguments: (TYPE SEQUENCE-ONE SEQUENCE-TWO PREDICATE &key (KEY #'IDENTITY)) -*/ - (int nargs, Lisp_Object *args)) -{ - Lisp_Object type = args[0], sequence_one = args[1], sequence_two = args[2], - predicate = args[3], result = Qnil; - check_test_func_t check_merge = NULL; - - PARSE_KEYWORDS (Fmerge, nargs, args, 1, (key), NULL); - - CHECK_SEQUENCE (sequence_one); - CHECK_SEQUENCE (sequence_two); - - CHECK_KEY_ARGUMENT (key); - - check_merge = get_merge_predicate (predicate, key); - - if (EQ (type, Qlist) && (LISTP (sequence_one) || LISTP (sequence_two))) - { - if (NILP (sequence_two)) - { - result = Fappend (2, args + 1); - } - else if (NILP (sequence_one)) - { - args[3] = Qnil; /* Overwriting PREDICATE, and losing its GC - protection, but that doesn't matter. */ - result = Fappend (2, args + 2); - } - else if (CONSP (sequence_one) && CONSP (sequence_two)) - { - result = list_merge (sequence_one, sequence_two, check_merge, - predicate, key); - } - else - { - Lisp_Object *array_storage, swap; - Elemcount array_length, i; - Boolint reverse_order = 0; - - if (!CONSP (sequence_one)) - { - /* Make sequence_one the cons, sequence_two the array: */ - swap = sequence_one; - sequence_one = sequence_two; - sequence_two = swap; - reverse_order = 1; - } - - if (VECTORP (sequence_two)) - { - array_storage = XVECTOR_DATA (sequence_two); - array_length = XVECTOR_LENGTH (sequence_two); - } - else if (STRINGP (sequence_two)) - { - Ibyte *strdata = XSTRING_DATA (sequence_two); - array_length = string_char_length (sequence_two); - /* No need to GCPRO, characters are immediate. */ - STRING_DATA_TO_OBJECT_ARRAY (strdata, array_storage, i, - array_length); - - } - else - { - Lisp_Bit_Vector *v = XBIT_VECTOR (sequence_two); - array_length = bit_vector_length (v); - /* No need to GCPRO, fixnums are immediate. */ - BIT_VECTOR_TO_OBJECT_ARRAY (v, array_storage, i, array_length); - } - - result = list_array_merge_into_list (sequence_one, - array_storage, array_length, - check_merge, predicate, key, - reverse_order); - } - } - else - { - Elemcount sequence_one_len = XFIXNUM (Flength (sequence_one)), - sequence_two_len = XFIXNUM (Flength (sequence_two)), i; - Elemcount output_len = 1 + sequence_one_len + sequence_two_len; - Lisp_Object *output = alloca_array (Lisp_Object, output_len), - *sequence_one_storage = NULL, *sequence_two_storage = NULL; - Boolint do_coerce = !(EQ (type, Qvector) || EQ (type, Qstring) - || EQ (type, Qbit_vector) || EQ (type, Qlist)); - Ibyte *strdata = NULL; - Lisp_Bit_Vector *v = NULL; - struct gcpro gcpro1; - - output[0] = do_coerce ? Qlist : type; - for (i = 1; i < output_len; ++i) - { - output[i] = Qnil; - } - - GCPRO1 (output[0]); - gcpro1.nvars = output_len; - - if (VECTORP (sequence_one)) - { - sequence_one_storage = XVECTOR_DATA (sequence_one); - } - else if (STRINGP (sequence_one)) - { - strdata = XSTRING_DATA (sequence_one); - STRING_DATA_TO_OBJECT_ARRAY (strdata, sequence_one_storage, - i, sequence_one_len); - } - else if (BIT_VECTORP (sequence_one)) - { - v = XBIT_VECTOR (sequence_one); - BIT_VECTOR_TO_OBJECT_ARRAY (v, sequence_one_storage, - i, sequence_one_len); - } - - if (VECTORP (sequence_two)) - { - sequence_two_storage = XVECTOR_DATA (sequence_two); - } - else if (STRINGP (sequence_two)) - { - strdata = XSTRING_DATA (sequence_two); - STRING_DATA_TO_OBJECT_ARRAY (strdata, sequence_two_storage, - i, sequence_two_len); - } - else if (BIT_VECTORP (sequence_two)) - { - v = XBIT_VECTOR (sequence_two); - BIT_VECTOR_TO_OBJECT_ARRAY (v, sequence_two_storage, - i, sequence_two_len); - } - - if (LISTP (sequence_one) && LISTP (sequence_two)) - { - list_list_merge_into_array (output + 1, output_len - 1, - sequence_one, sequence_two, - check_merge, predicate, key); - } - else if (LISTP (sequence_one)) - { - list_array_merge_into_array (output + 1, output_len - 1, - sequence_one, - sequence_two_storage, - sequence_two_len, - check_merge, predicate, key, 0); - } - else if (LISTP (sequence_two)) - { - list_array_merge_into_array (output + 1, output_len - 1, - sequence_two, - sequence_one_storage, - sequence_one_len, - check_merge, predicate, key, 1); - } - else - { - array_merge (output + 1, output_len - 1, - sequence_one_storage, sequence_one_len, - sequence_two_storage, sequence_two_len, - check_merge, predicate, - key); - } - - result = Ffuncall (output_len, output); - - if (do_coerce) - { - result = call2 (Qcoerce, result, type); - } - - UNGCPRO; - } - - return result; -} - -Lisp_Object -list_sort (Lisp_Object list, check_test_func_t check_merge, - Lisp_Object predicate, Lisp_Object key) -{ - struct gcpro gcpro1, gcpro2, gcpro3, gcpro4; - Lisp_Object back, tem; - Lisp_Object front = list; - Lisp_Object len = Flength (list); - - if (XFIXNUM (len) < 2) - return list; - - len = make_fixnum (XFIXNUM (len) / 2 - 1); - tem = Fnthcdr (len, list); - back = Fcdr (tem); - Fsetcdr (tem, Qnil); - - GCPRO4 (front, back, predicate, key); - front = list_sort (front, check_merge, predicate, key); - back = list_sort (back, check_merge, predicate, key); - - RETURN_UNGCPRO (list_merge (front, back, check_merge, predicate, key)); -} - -static void -array_sort (Lisp_Object *array, Elemcount array_len, - check_test_func_t check_merge, - Lisp_Object predicate, Lisp_Object key) -{ - Elemcount split; - - if (array_len < 2) - return; - - split = array_len / 2; - - array_sort (array, split, check_merge, predicate, key); - array_sort (array + split, array_len - split, check_merge, predicate, - key); - array_merge (array, array_len, array, split, array + split, - array_len - split, check_merge, predicate, key); -} - -DEFUN ("sort*", FsortX, 2, MANY, 0, /* -Sort SEQUENCE, comparing elements using PREDICATE. -Returns the sorted sequence. SEQUENCE is modified by side effect. - -PREDICATE is called with two elements of SEQUENCE, and should return t if -the first element is `less' than the second. - -Optional keyword argument KEY is a function used to extract an object to be -used for comparison from each element of SEQUENCE. - -In this implementation, sorting is always stable; but call `stable-sort' if -this stability is important to you, other implementations may not make the -same guarantees. - -arguments: (SEQUENCE PREDICATE &key (KEY #'IDENTITY)) -*/ - (int nargs, Lisp_Object *args)) -{ - Lisp_Object sequence = args[0], predicate = args[1]; - Lisp_Object *sequence_carray; - check_test_func_t check_merge = NULL; - Elemcount sequence_len, i; - - PARSE_KEYWORDS (FsortX, nargs, args, 1, (key), NULL); - - CHECK_SEQUENCE (sequence); - - CHECK_KEY_ARGUMENT (key); - - check_merge = get_merge_predicate (predicate, key); - - if (LISTP (sequence)) - { - sequence = list_sort (sequence, check_merge, predicate, key); - } - else if (VECTORP (sequence)) - { - array_sort (XVECTOR_DATA (sequence), XVECTOR_LENGTH (sequence), - check_merge, predicate, key); - } - else if (STRINGP (sequence)) - { - Ibyte *strdata = XSTRING_DATA (sequence); - - sequence_len = string_char_length (sequence); - - STRING_DATA_TO_OBJECT_ARRAY (strdata, sequence_carray, i, sequence_len); - - /* No GCPRO necessary, characters are immediate. */ - array_sort (sequence_carray, sequence_len, check_merge, predicate, key); - - strdata = XSTRING_DATA (sequence); - - CHECK_LISP_WRITEABLE (sequence); - for (i = 0; i < sequence_len; ++i) - { - strdata += set_itext_ichar (strdata, XCHAR (sequence_carray[i])); - } - - init_string_ascii_begin (sequence); - bump_string_modiff (sequence); - sledgehammer_check_ascii_begin (sequence); - } - else if (BIT_VECTORP (sequence)) - { - Lisp_Bit_Vector *v = XBIT_VECTOR (sequence); - sequence_len = bit_vector_length (v); - - BIT_VECTOR_TO_OBJECT_ARRAY (v, sequence_carray, i, sequence_len); - - /* No GCPRO necessary, bits are immediate. */ - array_sort (sequence_carray, sequence_len, check_merge, predicate, key); - - for (i = 0; i < sequence_len; ++i) - { - set_bit_vector_bit (v, i, XFIXNUM (sequence_carray [i])); - } - } - - return sequence; -} /************************************************************************/ /* property-list functions */ @@ -6426,6 +2291,20 @@ return Qnil; } +DEFUN ("old-rassq", Fold_rassq, 2, 2, 0, /* +Return non-nil if VALUE is `old-eq' to the cdr of an element of ALIST. +The value is actually the element of ALIST whose cdr is VALUE. +*/ + (value, alist)) +{ + EXTERNAL_ALIST_LOOP_4 (elt, elt_car, elt_cdr, alist) + { + if (HACKEQ_UNSAFE (value, elt_cdr)) + return elt; + } + return Qnil; +} + DEFUN ("old-rassoc", Fold_rassoc, 2, 2, 0, /* Return non-nil if VALUE is `old-equal' to the cdr of an element of ALIST. The value is actually the element of ALIST whose cdr equals VALUE. @@ -6502,134 +2381,6 @@ #endif - -static Lisp_Object replace_string_range_1 (Lisp_Object dest, - Lisp_Object start, - Lisp_Object end, - const Ibyte *source, - const Ibyte *source_limit, - Lisp_Object item); - -/* Fill the substring of DEST beginning at START and ending before END with - the character ITEM. If DEST does not have sufficient space for END - - START characters at START, write as many as is possible without changing - the character length of DEST. Update the string modification flag and do - any sledgehammer checks we have turned on. - - START must be a Lisp integer. END can be nil, indicating the length of the - string, or a Lisp integer. The condition (<= 0 START END (length DEST)) - must hold, or fill_string_range() will signal an error. */ -static Lisp_Object -fill_string_range (Lisp_Object dest, Lisp_Object item, Lisp_Object start, - Lisp_Object end) -{ - return replace_string_range_1 (dest, start, end, NULL, NULL, item); -} - -DEFUN ("fill", Ffill, 2, MANY, 0, /* -Destructively modify SEQUENCE by replacing each element with ITEM. -SEQUENCE is a list, vector, bit vector, or string. - -Optional keyword START is the index of the first element of SEQUENCE -to be modified, and defaults to zero. Optional keyword END is the -exclusive upper bound on the elements of SEQUENCE to be modified, and -defaults to the length of SEQUENCE. - -arguments: (SEQUENCE ITEM &key (START 0) (END (length SEQUENCE))) -*/ - (int nargs, Lisp_Object *args)) -{ - Lisp_Object sequence = args[0]; - Lisp_Object item = args[1]; - Elemcount starting, ending = MOST_POSITIVE_FIXNUM + 1, ii, len; - - PARSE_KEYWORDS (Ffill, nargs, args, 2, (start, end), (start = Qzero)); - - CHECK_NATNUM (start); - starting = BIGNUMP (start) ? MOST_POSITIVE_FIXNUM + 1 : XFIXNUM (start); - - if (!NILP (end)) - { - CHECK_NATNUM (end); - ending = BIGNUMP (end) ? MOST_POSITIVE_FIXNUM + 1 : XFIXNUM (end); - } - - retry: - if (STRINGP (sequence)) - { - CHECK_CHAR_COERCE_INT (item); - CHECK_LISP_WRITEABLE (sequence); - - fill_string_range (sequence, item, start, end); - } - else if (VECTORP (sequence)) - { - Lisp_Object *p = XVECTOR_DATA (sequence); - - CHECK_LISP_WRITEABLE (sequence); - len = XVECTOR_LENGTH (sequence); - - check_sequence_range (sequence, start, end, make_fixnum (len)); - ending = min (ending, len); - - for (ii = starting; ii < ending; ++ii) - { - p[ii] = item; - } - } - else if (BIT_VECTORP (sequence)) - { - Lisp_Bit_Vector *v = XBIT_VECTOR (sequence); - int bit; - - CHECK_BIT (item); - bit = XFIXNUM (item); - CHECK_LISP_WRITEABLE (sequence); - len = bit_vector_length (v); - - check_sequence_range (sequence, start, end, make_fixnum (len)); - ending = min (ending, len); - - for (ii = starting; ii < ending; ++ii) - { - set_bit_vector_bit (v, ii, bit); - } - } - else if (LISTP (sequence)) - { - Elemcount counting = 0; - - { - EXTERNAL_LIST_LOOP_3 (elt, sequence, tail) - { - if (counting >= starting) - { - if (counting < ending) - { - XSETCAR (tail, item); - } - else if (counting == ending) - { - break; - } - } - ++counting; - } - } - - if (counting < starting || (counting != ending && !NILP (end))) - { - check_sequence_range (args[0], start, end, Flength (args[0])); - } - } - else - { - sequence = wrong_type_argument (Qsequencep, sequence); - goto retry; - } - return sequence; -} - Lisp_Object nconc2 (Lisp_Object arg1, Lisp_Object arg2) { @@ -6765,607 +2516,6 @@ } -/* Replace the substring of DEST beginning at START and ending before END - with the text at SOURCE, which is END - START characters long and - SOURCE_LIMIT - SOURCE octets long. If DEST does not have sufficient - space for END - START characters at START, write as many as is possible - without changing the length of DEST. Update the string modification flag - and do any sledgehammer checks we have turned on in this build. - - START must be a Lisp integer. END can be nil, indicating the length of the - string, or a Lisp integer. The condition (<= 0 START END (length DEST)) - must hold, or replace_string_range() will signal an error. */ -static Lisp_Object -replace_string_range (Lisp_Object dest, Lisp_Object start, Lisp_Object end, - const Ibyte *source, const Ibyte *source_limit) -{ - return replace_string_range_1 (dest, start, end, source, source_limit, - Qnil); -} - -/* This is the guts of several mapping functions. - - Call FUNCTION CALL_COUNT times, with NSEQUENCES arguments each time, - taking the elements from SEQUENCES. If VALS is non-NULL, store the - results into VALS, a C array of Lisp_Objects; else, if LISP_VALS is - non-nil, store the results into LISP_VALS, a sequence with sufficient - room for CALL_COUNT results (but see the documentation of SOME_OR_EVERY.) - Else, do not accumulate any result. - - If VALS is non-NULL, NSEQUENCES is one, and SEQUENCES[0] is a cons, - mapcarX will store the elements of SEQUENCES[0] in stack and GCPRO them, - so FUNCTION cannot insert a non-cons into SEQUENCES[0] and throw off - mapcarX. - - Otherwise, mapcarX signals an invalid state error (see - mapping_interaction_error(), above) if it encounters a non-cons, - non-array when traversing SEQUENCES. Common Lisp specifies in - MAPPING-DESTRUCTIVE-INTERACTION that it is an error when FUNCTION - destructively modifies SEQUENCES in a way that might affect the ongoing - traversal operation. - - CALLER is a symbol describing the Lisp-visible function that was called, - and any errors thrown because SEQUENCES was modified will reflect it. - - If CALLER is Qsome, return the (possibly multiple) values given by - FUNCTION the first time it is non-nil, and abandon the iterations. - LISP_VALS must be the result of calling STORE_VOID_IN_LISP on the address - of a Lisp object, and the return value will be stored at that address. - If CALLER is Qevery, LISP_VALS must also reflect a pointer to a Lisp - object, and Qnil will be stored at that address if FUNCTION gives nil; - otherwise it will be left alone. */ - -static void -mapcarX (Elemcount call_count, Lisp_Object *vals, Lisp_Object lisp_vals, - Lisp_Object function, int nsequences, Lisp_Object *sequences, - Lisp_Object caller) -{ - Lisp_Object called, *args; - struct gcpro gcpro1, gcpro2; - Ibyte *lisp_vals_staging = NULL, *cursor = NULL; - int i, j; - - assert ((EQ (caller, Qsome) || EQ (caller, Qevery)) ? vals == NULL : 1); - - args = alloca_array (Lisp_Object, nsequences + 1); - args[0] = function; - for (i = 1; i <= nsequences; ++i) - { - args[i] = Qnil; - } - - if (vals != NULL) - { - GCPRO2 (args[0], vals[0]); - gcpro1.nvars = nsequences + 1; - gcpro2.nvars = 0; - } - else - { - GCPRO1 (args[0]); - gcpro1.nvars = nsequences + 1; - } - - /* Be extra nice in the event that we've been handed one list and one - only; make it possible for FUNCTION to set cdrs not yet processed to - non-cons, non-nil objects without ill-effect, if we have been handed - the stack space to do that. */ - if (vals != NULL && 1 == nsequences && CONSP (sequences[0])) - { - Lisp_Object lst = sequences[0]; - Lisp_Object *val = vals; - for (i = 0; i < call_count; ++i) - { - *val++ = XCAR (lst); - lst = XCDR (lst); - } - gcpro2.nvars = call_count; - - for (i = 0; i < call_count; ++i) - { - args[1] = vals[i]; - vals[i] = IGNORE_MULTIPLE_VALUES (Ffuncall (nsequences + 1, args)); - } - } - else - { - enum lrecord_type lisp_vals_type = lrecord_type_symbol; - Binbyte *sequence_types = alloca_array (Binbyte, nsequences); - for (j = 0; j < nsequences; ++j) - { - sequence_types[j] = XRECORD_LHEADER (sequences[j])->type; - } - - if (!EQ (caller, Qsome) && !EQ (caller, Qevery)) - { - assert (LRECORDP (lisp_vals)); - - lisp_vals_type - = (enum lrecord_type) XRECORD_LHEADER (lisp_vals)->type; - - if (lrecord_type_string == lisp_vals_type) - { - lisp_vals_staging = cursor - = alloca_ibytes (call_count * MAX_ICHAR_LEN); - } - else if (ARRAYP (lisp_vals)) - { - CHECK_LISP_WRITEABLE (lisp_vals); - } - } - - for (i = 0; i < call_count; ++i) - { - for (j = 0; j < nsequences; ++j) - { - switch (sequence_types[j]) - { - case lrecord_type_cons: - { - if (!CONSP (sequences[j])) - { - /* This means FUNCTION has messed around with a cons - in one of the sequences, since we checked the - type (CHECK_SEQUENCE()) and the length and - structure (with Flength()) correctly in our - callers. */ - mapping_interaction_error (caller, sequences[j]); - } - args[j + 1] = XCAR (sequences[j]); - sequences[j] = XCDR (sequences[j]); - break; - } - case lrecord_type_vector: - { - args[j + 1] = XVECTOR_DATA (sequences[j])[i]; - break; - } - case lrecord_type_string: - { - args[j + 1] = make_char (string_ichar (sequences[j], i)); - break; - } - case lrecord_type_bit_vector: - { - args[j + 1] - = make_fixnum (bit_vector_bit (XBIT_VECTOR (sequences[j]), - i)); - break; - } - default: - ABORT(); - } - } - called = Ffuncall (nsequences + 1, args); - if (vals != NULL) - { - vals[i] = IGNORE_MULTIPLE_VALUES (called); - gcpro2.nvars += 1; - } - else if (EQ (Qsome, caller)) - { - if (!NILP (IGNORE_MULTIPLE_VALUES (called))) - { - Lisp_Object *result - = (Lisp_Object *) GET_VOID_FROM_LISP (lisp_vals); - *result = called; - UNGCPRO; - return; - } - } - else if (EQ (Qevery, caller)) - { - if (NILP (IGNORE_MULTIPLE_VALUES (called))) - { - Lisp_Object *result - = (Lisp_Object *) GET_VOID_FROM_LISP (lisp_vals); - *result = Qnil; - UNGCPRO; - return; - } - } - else - { - called = IGNORE_MULTIPLE_VALUES (called); - switch (lisp_vals_type) - { - case lrecord_type_symbol: - /* Discard the result of funcall. */ - break; - case lrecord_type_cons: - { - if (!CONSP (lisp_vals)) - { - /* If FUNCTION has inserted a non-cons non-nil - cdr into the list before we've processed the - relevant part, error. */ - mapping_interaction_error (caller, lisp_vals); - } - XSETCAR (lisp_vals, called); - lisp_vals = XCDR (lisp_vals); - break; - } - case lrecord_type_vector: - { - i < XVECTOR_LENGTH (lisp_vals) ? - (XVECTOR_DATA (lisp_vals)[i] = called) : - /* Let #'aset error. */ - Faset (lisp_vals, make_fixnum (i), called); - break; - } - case lrecord_type_string: - { - CHECK_CHAR_COERCE_INT (called); - cursor += set_itext_ichar (cursor, XCHAR (called)); - break; - } - case lrecord_type_bit_vector: - { - (BITP (called) && - i < bit_vector_length (XBIT_VECTOR (lisp_vals))) ? - set_bit_vector_bit (XBIT_VECTOR (lisp_vals), i, - XFIXNUM (called)) : - (void) Faset (lisp_vals, make_fixnum (i), called); - break; - } - default: - { - ABORT(); - break; - } - } - } - } - - if (lisp_vals_staging != NULL) - { - CHECK_LISP_WRITEABLE (lisp_vals); - replace_string_range (lisp_vals, Qzero, make_fixnum (call_count), - lisp_vals_staging, cursor); - } - } - - UNGCPRO; -} - -/* Given NSEQUENCES objects at the address pointed to by SEQUENCES, return - the length of the shortest sequence. Error if all are circular, or if any - one of them is not a sequence. */ -static Elemcount -shortest_length_among_sequences (int nsequences, Lisp_Object *sequences) -{ - Elemcount len = 1 + MOST_POSITIVE_FIXNUM; - Lisp_Object length = Qnil; - int i; - - for (i = 0; i < nsequences; ++i) - { - if (CONSP (sequences[i])) - { - length = Flist_length (sequences[i]); - if (!NILP (length)) - { - len = min (len, XFIXNUM (length)); - } - } - else - { - CHECK_SEQUENCE (sequences[i]); - length = Flength (sequences[i]); - len = min (len, XFIXNUM (length)); - } - } - - if (len == 1 + MOST_POSITIVE_FIXNUM) - { - signal_circular_list_error (sequences[0]); - } - - return len; -} - -DEFUN ("mapconcat", Fmapconcat, 3, MANY, 0, /* -Call FUNCTION on each element of SEQUENCE, and concat results to a string. -Between each pair of results, insert SEPARATOR. - -Each result, and SEPARATOR, should be strings. Thus, using " " as SEPARATOR -results in spaces between the values returned by FUNCTION. SEQUENCE itself -may be a list, a vector, a bit vector, or a string. - -With optional SEQUENCES, call FUNCTION each time with as many arguments as -there are SEQUENCES, plus one for the element from SEQUENCE. One element -from each sequence will be used each time FUNCTION is called, and -`mapconcat' will give up once the shortest sequence is exhausted. - -arguments: (FUNCTION SEQUENCE SEPARATOR &rest SEQUENCES) -*/ - (int nargs, Lisp_Object *args)) -{ - Lisp_Object function = args[0]; - Lisp_Object sequence = args[1]; - Lisp_Object separator = args[2]; - Elemcount len = MOST_POSITIVE_FIXNUM; - Lisp_Object *args0; - EMACS_INT i, nargs0; - - args[2] = sequence; - args[1] = separator; - - len = shortest_length_among_sequences (nargs - 2, args + 2); - - if (len == 0) return build_ascstring (""); - - nargs0 = len + len - 1; - args0 = alloca_array (Lisp_Object, nargs0); - - /* Special-case this, it's very common and doesn't require any - funcalls. Upside of doing it here, instead of cl-macs.el: no consing, - apart from the final string, we allocate everything on the stack. */ - if (EQ (function, Qidentity) && 3 == nargs && CONSP (sequence)) - { - for (i = 0; i < len; ++i) - { - args0[i] = XCAR (sequence); - sequence = XCDR (sequence); - } - } - else - { - mapcarX (len, args0, Qnil, function, nargs - 2, args + 2, Qmapconcat); - } - - for (i = len - 1; i >= 0; i--) - args0[i + i] = args0[i]; - - for (i = 1; i < nargs0; i += 2) - args0[i] = separator; - - return Fconcat (nargs0, args0); -} - -DEFUN ("mapcar*", FmapcarX, 2, MANY, 0, /* -Call FUNCTION on each element of SEQUENCE; return a list of the results. -The result is a list of the same length as SEQUENCE. -SEQUENCE may be a list, a vector, a bit vector, or a string. - -With optional SEQUENCES, call FUNCTION each time with as many arguments as -there are SEQUENCES, plus one for the element from SEQUENCE. One element -from each sequence will be used each time FUNCTION is called, and `mapcar' -stops calling FUNCTION once the shortest sequence is exhausted. - -arguments: (FUNCTION SEQUENCE &rest SEQUENCES) -*/ - (int nargs, Lisp_Object *args)) -{ - Lisp_Object function = args[0]; - Elemcount len = shortest_length_among_sequences (nargs - 1, args + 1); - Lisp_Object *args0; - - args0 = alloca_array (Lisp_Object, len); - mapcarX (len, args0, Qnil, function, nargs - 1, args + 1, QmapcarX); - - return Flist ((int) len, args0); -} - -DEFUN ("mapvector", Fmapvector, 2, MANY, 0, /* -Call FUNCTION on each element of SEQUENCE; return a vector of the results. -The result is a vector of the same length as SEQUENCE. -SEQUENCE may be a list, a vector, a bit vector, or a string. - -With optional SEQUENCES, call FUNCTION each time with as many arguments as -there are SEQUENCES, plus one for the element from SEQUENCE. One element -from each sequence will be used each time FUNCTION is called, and -`mapvector' stops calling FUNCTION once the shortest sequence is exhausted. - -arguments: (FUNCTION SEQUENCE &rest SEQUENCES) -*/ - (int nargs, Lisp_Object *args)) -{ - Lisp_Object function = args[0]; - Elemcount len = shortest_length_among_sequences (nargs - 1, args + 1); - Lisp_Object result = make_vector (len, Qnil); - - struct gcpro gcpro1; - GCPRO1 (result); - /* Don't pass result as the lisp_object argument, we want mapcarX to protect - a single list argument's elements from being garbage-collected. */ - mapcarX (len, XVECTOR_DATA (result), Qnil, function, nargs - 1, args +1, - Qmapvector); - RETURN_UNGCPRO (result); -} - -DEFUN ("mapcan", Fmapcan, 2, MANY, 0, /* -Call FUNCTION on each element of SEQUENCE; chain the results together. - -FUNCTION must normally return a list; the results will be concatenated -together using `nconc'. - -With optional SEQUENCES, call FUNCTION each time with as many arguments as -there are SEQUENCES, plus one for the element from SEQUENCE. One element -from each sequence will be used each time FUNCTION is called, and -`mapcan' stops calling FUNCTION once the shortest sequence is exhausted. - -arguments: (FUNCTION SEQUENCE &rest SEQUENCES) -*/ - (int nargs, Lisp_Object *args)) -{ - Elemcount len = shortest_length_among_sequences (nargs - 1, args + 1); - Lisp_Object function = args[0], *result = alloca_array (Lisp_Object, len); - - mapcarX (len, result, Qnil, function, nargs - 1, args + 1, Qmapcan); - - /* #'nconc GCPROs its args in case of signals and error. */ - return Fnconc (len, result); -} - -DEFUN ("mapc", Fmapc, 2, MANY, 0, /* -Call FUNCTION on each element of SEQUENCE. - -SEQUENCE may be a list, a vector, a bit vector, or a string. -This function is like `mapcar' but does not accumulate the results, -which is more efficient if you do not use the results. - -With optional SEQUENCES, call FUNCTION each time with as many arguments as -there are SEQUENCES, plus one for the elements from SEQUENCE. One element -from each sequence will be used each time FUNCTION is called, and -`mapc' stops calling FUNCTION once the shortest sequence is exhausted. - -Return SEQUENCE. - -arguments: (FUNCTION SEQUENCE &rest SEQUENCES) -*/ - (int nargs, Lisp_Object *args)) -{ - Elemcount len = shortest_length_among_sequences (nargs - 1, args + 1); - Lisp_Object sequence = args[1]; - struct gcpro gcpro1; - /* We need to GCPRO sequence, because mapcarX will modify the - elements of the args array handed to it, and this may involve - elements of sequence getting garbage collected. */ - GCPRO1 (sequence); - mapcarX (len, NULL, Qnil, args[0], nargs - 1, args + 1, Qmapc); - RETURN_UNGCPRO (sequence); -} - -DEFUN ("map", Fmap, 3, MANY, 0, /* -Map FUNCTION across one or more sequences, returning a sequence. - -TYPE is the sequence type to return, FUNCTION is the function, SEQUENCE is -the first argument sequence, SEQUENCES are the other argument sequences. - -FUNCTION will be called with (1+ (length SEQUENCES)) arguments, and must be -capable of accepting this number of arguments. - -Certain TYPEs are recognised internally by `map', but others are not, and -`coerce' may throw an error on an attempt to convert to a TYPE it does not -understand. A null TYPE means do not accumulate any values. - -arguments: (TYPE FUNCTION SEQUENCE &rest SEQUENCES) -*/ - (int nargs, Lisp_Object *args)) -{ - Lisp_Object type = args[0]; - Lisp_Object function = args[1]; - Lisp_Object result = Qnil; - Lisp_Object *args0 = NULL; - Elemcount len = shortest_length_among_sequences (nargs - 2, args + 2); - struct gcpro gcpro1; - - if (!NILP (type)) - { - args0 = alloca_array (Lisp_Object, len); - } - - mapcarX (len, args0, Qnil, function, nargs - 2, args + 2, Qmap); - - if (EQ (type, Qnil)) - { - return result; - } - - if (EQ (type, Qvector) || EQ (type, Qarray)) - { - result = Fvector (len, args0); - } - else if (EQ (type, Qstring)) - { - result = Fstring (len, args0); - } - else if (EQ (type, Qlist)) - { - result = Flist (len, args0); - } - else if (EQ (type, Qbit_vector)) - { - result = Fbit_vector (len, args0); - } - else - { - result = Flist (len, args0); - GCPRO1 (result); - result = call2 (Qcoerce, result, type); - UNGCPRO; - } - - return result; -} - -DEFUN ("map-into", Fmap_into, 2, MANY, 0, /* -Modify RESULT-SEQUENCE using the return values of FUNCTION on SEQUENCES. - -RESULT-SEQUENCE and SEQUENCES can be lists or arrays. - -FUNCTION must accept at least as many arguments as there are SEQUENCES -\(possibly zero). If RESULT-SEQUENCE and the elements of SEQUENCES are not -the same length, stop when the shortest is exhausted; any elements of -RESULT-SEQUENCE beyond that are unmodified. - -Return RESULT-SEQUENCE. - -arguments: (RESULT-SEQUENCE FUNCTION &rest SEQUENCES) -*/ - (int nargs, Lisp_Object *args)) -{ - Elemcount len; - Lisp_Object result_sequence = args[0]; - Lisp_Object function = args[1]; - - args[0] = function; - args[1] = result_sequence; - - len = shortest_length_among_sequences (nargs - 1, args + 1); - - mapcarX (len, NULL, result_sequence, function, nargs - 2, args + 2, - Qmap_into); - - return result_sequence; -} - -DEFUN ("some", Fsome, 2, MANY, 0, /* -Return true if PREDICATE gives non-nil for an element of SEQUENCE. - -If so, return the value (possibly multiple) given by PREDICATE. - -With optional SEQUENCES, call PREDICATE each time with as many arguments as -there are SEQUENCES (plus one for the element from SEQUENCE). - -See also `find-if', which returns the corresponding element of SEQUENCE, -rather than the value given by PREDICATE, and accepts bounding index -keywords. - -arguments: (PREDICATE SEQUENCE &rest SEQUENCES) -*/ - (int nargs, Lisp_Object *args)) -{ - Lisp_Object result = Qnil, - result_ptr = STORE_VOID_IN_LISP ((void *) &result); - Elemcount len = shortest_length_among_sequences (nargs - 1, args + 1); - - mapcarX (len, NULL, result_ptr, args[0], nargs - 1, args +1, Qsome); - - return result; -} - -DEFUN ("every", Fevery, 2, MANY, 0, /* -Return true if PREDICATE is true of every element of SEQUENCE. - -With optional SEQUENCES, call PREDICATE each time with as many arguments as -there are SEQUENCES (plus one for the element from SEQUENCE). - -In contrast to `some', `every' never returns multiple values. - -arguments: (PREDICATE SEQUENCE &rest SEQUENCES) -*/ - (int nargs, Lisp_Object *args)) -{ - Lisp_Object result = Qt, result_ptr = STORE_VOID_IN_LISP ((void *) &result); - Elemcount len = shortest_length_among_sequences (nargs - 1, args + 1); - - mapcarX (len, NULL, result_ptr, args[0], nargs - 1, args +1, Qevery); - - return result; -} - /* Call FUNCTION with NLISTS arguments repeatedly, each Nth argument corresponding to the result of calling (nthcdr ITERATION-COUNT LISTS[N]), until that #'nthcdr expression gives nil for some element of LISTS. @@ -7528,415 +2678,6 @@ { return maplist (args[0], nargs - 1, args + 1, Qmapcon); } - -/* Extra random functions */ - -DEFUN ("reduce", Freduce, 2, MANY, 0, /* -Combine the elements of sequence using FUNCTION, a binary operation. - -For example, `(reduce #'+ SEQUENCE)' returns the sum of all elements in -SEQUENCE, and `(reduce #'union SEQUENCE)' returns the union of all elements -in SEQUENCE. - -Keywords supported: :start :end :from-end :initial-value :key -See `remove*' for the meaning of :start, :end, :from-end and :key. - -:initial-value specifies an element (typically an identity element, such as -0) that is conceptually prepended to the sequence (or appended, when -:from-end is given). - -If the sequence has one element, that element is returned directly. -If the sequence has no elements, :initial-value is returned if given; -otherwise, FUNCTION is called with no arguments, and its result returned. - -arguments: (FUNCTION SEQUENCE &key (START 0) (END (length SEQUENCE)) FROM-END INITIAL-VALUE (KEY #'identity)) -*/ - (int nargs, Lisp_Object *args)) -{ - Lisp_Object function = args[0], sequence = args[1], accum = Qunbound; - Elemcount starting, ending = MOST_POSITIVE_FIXNUM + 1, ii = 0; - - PARSE_KEYWORDS (Freduce, nargs, args, 5, - (start, end, from_end, initial_value, key), - (start = Qzero, initial_value = Qunbound)); - - CHECK_SEQUENCE (sequence); - CHECK_NATNUM (start); - starting = BIGNUMP (start) ? MOST_POSITIVE_FIXNUM + 1 : XFIXNUM (start); - CHECK_KEY_ARGUMENT (key); - -#define KEY(key, item) (EQ (Qidentity, key) ? item : \ - IGNORE_MULTIPLE_VALUES (call1 (key, item))) -#define CALL2(function, accum, item) \ - IGNORE_MULTIPLE_VALUES (call2 (function, accum, item)) - - if (!NILP (end)) - { - CHECK_NATNUM (end); - ending = BIGNUMP (end) ? MOST_POSITIVE_FIXNUM + 1 : XFIXNUM (end); - } - - if (VECTORP (sequence)) - { - Lisp_Vector *vv = XVECTOR (sequence); - struct gcpro gcpro1; - - check_sequence_range (sequence, start, end, make_fixnum (vv->size)); - - ending = min (ending, vv->size); - - GCPRO1 (accum); - - if (!UNBOUNDP (initial_value)) - { - accum = initial_value; - } - else if (ending - starting) - { - if (NILP (from_end)) - { - accum = KEY (key, vv->contents[starting]); - starting++; - } - else - { - accum = KEY (key, vv->contents[ending - 1]); - ending--; - } - } - - if (NILP (from_end)) - { - for (ii = starting; ii < ending; ++ii) - { - accum = CALL2 (function, accum, KEY (key, vv->contents[ii])); - } - } - else - { - for (ii = ending - 1; ii >= starting; --ii) - { - accum = CALL2 (function, KEY (key, vv->contents[ii]), accum); - } - } - - UNGCPRO; - } - else if (BIT_VECTORP (sequence)) - { - Lisp_Bit_Vector *bv = XBIT_VECTOR (sequence); - struct gcpro gcpro1; - - check_sequence_range (sequence, start, end, make_fixnum (bv->size)); - ending = min (ending, bv->size); - - GCPRO1 (accum); - - if (!UNBOUNDP (initial_value)) - { - accum = initial_value; - } - else if (ending - starting) - { - if (NILP (from_end)) - { - accum = KEY (key, make_fixnum (bit_vector_bit (bv, starting))); - starting++; - } - else - { - accum = KEY (key, make_fixnum (bit_vector_bit (bv, ending - 1))); - ending--; - } - } - - if (NILP (from_end)) - { - for (ii = starting; ii < ending; ++ii) - { - accum = CALL2 (function, accum, - KEY (key, make_fixnum (bit_vector_bit (bv, ii)))); - } - } - else - { - for (ii = ending - 1; ii >= starting; --ii) - { - accum = CALL2 (function, KEY (key, - make_fixnum (bit_vector_bit (bv, - ii))), - accum); - } - } - - UNGCPRO; - - } - else if (STRINGP (sequence)) - { - struct gcpro gcpro1; - - GCPRO1 (accum); - - if (NILP (from_end)) - { - Bytecount byte_len = XSTRING_LENGTH (sequence); - Bytecount cursor_offset = 0; - const Ibyte *startp = XSTRING_DATA (sequence); - const Ibyte *cursor = startp; - - for (ii = 0; ii != starting && cursor_offset < byte_len; ++ii) - { - INC_IBYTEPTR (cursor); - cursor_offset = cursor - startp; - } - - if (!UNBOUNDP (initial_value)) - { - accum = initial_value; - } - else if (ending - starting && cursor_offset < byte_len) - { - accum = KEY (key, make_char (itext_ichar (cursor))); - starting++; - startp = XSTRING_DATA (sequence); - cursor = startp + cursor_offset; - - if (byte_len != XSTRING_LENGTH (sequence) - || !valid_ibyteptr_p (cursor)) - { - mapping_interaction_error (Qreduce, sequence); - } - - INC_IBYTEPTR (cursor); - cursor_offset = cursor - startp; - ii++; - } - - while (cursor_offset < byte_len && ii < ending) - { - accum = CALL2 (function, accum, - KEY (key, make_char (itext_ichar (cursor)))); - - startp = XSTRING_DATA (sequence); - cursor = startp + cursor_offset; - - if (byte_len != XSTRING_LENGTH (sequence) - || !valid_ibyteptr_p (cursor)) - { - mapping_interaction_error (Qreduce, sequence); - } - - INC_IBYTEPTR (cursor); - cursor_offset = cursor - startp; - ++ii; - } - - if (ii < starting || (ii < ending && !NILP (end))) - { - check_sequence_range (sequence, start, end, Flength (sequence)); - } - } - else - { - Elemcount len = string_char_length (sequence); - Bytecount cursor_offset, byte_len = XSTRING_LENGTH (sequence); - const Ibyte *cursor; - - check_sequence_range (sequence, start, end, make_fixnum (len)); - ending = min (ending, len); - starting = XFIXNUM (start); - - cursor = string_char_addr (sequence, ending - 1); - cursor_offset = cursor - XSTRING_DATA (sequence); - - if (!UNBOUNDP (initial_value)) - { - accum = initial_value; - } - else if (ending - starting) - { - accum = KEY (key, make_char (itext_ichar (cursor))); - ending--; - if (ending > 0) - { - cursor = XSTRING_DATA (sequence) + cursor_offset; - - if (!valid_ibyteptr_p (cursor)) - { - mapping_interaction_error (Qreduce, sequence); - } - - DEC_IBYTEPTR (cursor); - cursor_offset = cursor - XSTRING_DATA (sequence); - } - } - - for (ii = ending - 1; ii >= starting; --ii) - { - accum = CALL2 (function, KEY (key, - make_char (itext_ichar (cursor))), - accum); - if (ii > 0) - { - cursor = XSTRING_DATA (sequence) + cursor_offset; - - if (byte_len != XSTRING_LENGTH (sequence) - || !valid_ibyteptr_p (cursor)) - { - mapping_interaction_error (Qreduce, sequence); - } - - DEC_IBYTEPTR (cursor); - cursor_offset = cursor - XSTRING_DATA (sequence); - } - } - } - - UNGCPRO; - } - else if (LISTP (sequence)) - { - if (NILP (from_end)) - { - struct gcpro gcpro1; - - GCPRO1 (accum); - - if (!UNBOUNDP (initial_value)) - { - accum = initial_value; - } - else if (ending - starting) - { - GC_EXTERNAL_LIST_LOOP_2 (elt, sequence) - { - if (ii == starting) - { - accum = KEY (key, elt); - starting++; - break; - } - ++ii; - } - END_GC_EXTERNAL_LIST_LOOP (elt); - } - - ii = 0; - - if (ending - starting) - { - GC_EXTERNAL_LIST_LOOP_2 (elt, sequence) - { - if (ii >= starting) - { - if (ii < ending) - { - accum = CALL2 (function, accum, KEY (key, elt)); - } - else if (ii == ending) - { - break; - } - } - ++ii; - } - END_GC_EXTERNAL_LIST_LOOP (elt); - } - - UNGCPRO; - - if (ii < starting || (ii < ending && !NILP (end))) - { - check_sequence_range (sequence, start, end, Flength (sequence)); - } - } - else - { - Boolint need_accum = 0; - Lisp_Object *subsequence = NULL; - Elemcount counting = 0, len = 0; - struct gcpro gcpro1; - - len = XFIXNUM (Flength (sequence)); - check_sequence_range (sequence, start, end, make_fixnum (len)); - ending = min (ending, len); - - /* :from-end with a list; make an alloca copy of the relevant list - data, attempting to go backwards isn't worth the trouble. */ - if (!UNBOUNDP (initial_value)) - { - accum = initial_value; - if (ending - starting && starting < ending) - { - subsequence = alloca_array (Lisp_Object, ending - starting); - } - } - else if (ending - starting && starting < ending) - { - subsequence = alloca_array (Lisp_Object, ending - starting); - need_accum = 1; - } - - if (ending - starting && starting < ending) - { - EXTERNAL_LIST_LOOP_3 (elt, sequence, tail) - { - if (counting >= starting) - { - if (counting < ending) - { - subsequence[ii++] = elt; - } - else if (counting == ending) - { - break; - } - } - ++counting; - } - } - - if (subsequence != NULL) - { - len = ending - starting; - /* If we could be sure that neither FUNCTION nor KEY modify - SEQUENCE, this wouldn't be necessary, since all the - elements of SUBSEQUENCE would definitely always be - reachable via SEQUENCE. */ - GCPRO1 (subsequence[0]); - gcpro1.nvars = len; - } - - if (need_accum) - { - accum = KEY (key, subsequence[len - 1]); - --len; - } - - for (ii = len; ii != 0;) - { - --ii; - accum = CALL2 (function, KEY (key, subsequence[ii]), accum); - } - - if (subsequence != NULL) - { - UNGCPRO; - } - } - } - - /* At this point, if ACCUM is unbound, SEQUENCE has no elements; we - need to return the result of calling FUNCTION with zero - arguments. */ - if (UNBOUNDP (accum)) - { - accum = IGNORE_MULTIPLE_VALUES (call0 (function)); - } - - return accum; -} DEFUN ("replace-list", Freplace_list, 2, 2, 0, /* Destructively replace the list OLD with NEW. @@ -7978,3011 +2719,6 @@ return old; } -/* This function is the implementation of fill_string_range() and - replace_string_range(); see the comments for those functions. */ -static Lisp_Object -replace_string_range_1 (Lisp_Object dest, Lisp_Object start, Lisp_Object end, - const Ibyte *source, const Ibyte *source_limit, - Lisp_Object item) -{ - Ibyte *destp = XSTRING_DATA (dest), *p = destp, - *pend = p + XSTRING_LENGTH (dest), *pcursor, item_buf[MAX_ICHAR_LEN]; - Bytecount prefix_bytecount, source_len = source_limit - source; - Charcount ii = 0, ending, len; - Charcount starting = BIGNUMP (start) ? MOST_POSITIVE_FIXNUM + 1 : XFIXNUM (start); - Elemcount delta; - - while (ii < starting && p < pend) - { - INC_IBYTEPTR (p); - ii++; - } - - pcursor = p; - - if (NILP (end)) - { - while (pcursor < pend) - { - INC_IBYTEPTR (pcursor); - ii++; - } - - ending = len = ii; - } - else - { - ending = BIGNUMP (end) ? MOST_POSITIVE_FIXNUM + 1 : XFIXNUM (end); - while (ii < ending && pcursor < pend) - { - INC_IBYTEPTR (pcursor); - ii++; - } - } - - if (pcursor == pend) - { - /* We have the length, check it for our callers. */ - check_sequence_range (dest, start, end, make_fixnum (ii)); - } - - if (!(p == pend || p == pcursor)) - { - prefix_bytecount = p - destp; - - if (!NILP (item)) - { - assert (source == NULL && source_limit == NULL); - source_len = set_itext_ichar (item_buf, XCHAR (item)); - delta = (source_len * (ending - starting)) - (pcursor - p); - } - else - { - assert (source != NULL && source_limit != NULL); - delta = source_len - (pcursor - p); - } - - if (delta) - { - resize_string (dest, prefix_bytecount, delta); - destp = XSTRING_DATA (dest); - pcursor = destp + prefix_bytecount + (pcursor - p); - p = destp + prefix_bytecount; - } - - if (CHARP (item)) - { - while (starting < ending) - { - memcpy (p, item_buf, source_len); - p += source_len; - starting++; - } - } - else - { - while (starting < ending && source < source_limit) - { - source_len = itext_copy_ichar (source, p); - p += source_len, source += source_len; - } - } - - init_string_ascii_begin (dest); - bump_string_modiff (dest); - sledgehammer_check_ascii_begin (dest); - } - - return dest; -} - -DEFUN ("replace", Freplace, 2, MANY, 0, /* -Replace the elements of SEQUENCE-ONE with the elements of SEQUENCE-TWO. - -SEQUENCE-ONE is destructively modified, and returned. Its length is not -changed. - -Keywords :start1 and :end1 specify a subsequence of SEQUENCE-ONE, and -:start2 and :end2 a subsequence of SEQUENCE-TWO. See `search' for more -information. - -arguments: (SEQUENCE-ONE SEQUENCE-TWO &key (START1 0) (END1 (length SEQUENCE-ONE)) (START2 0) (END2 (length SEQUENCE-TWO))) -*/ - (int nargs, Lisp_Object *args)) -{ - Lisp_Object sequence1 = args[0], sequence2 = args[1], - result = sequence1; - Elemcount starting1, ending1 = MOST_POSITIVE_FIXNUM + 1, starting2; - Elemcount ending2 = MOST_POSITIVE_FIXNUM + 1, counting = 0, startcounting; - Boolint sequence1_listp, sequence2_listp, - overwriting = EQ (sequence1, sequence2); - - PARSE_KEYWORDS (Freplace, nargs, args, 4, (start1, end1, start2, end2), - (start1 = start2 = Qzero)); - - CHECK_SEQUENCE (sequence1); - CHECK_LISP_WRITEABLE (sequence1); - - CHECK_SEQUENCE (sequence2); - - CHECK_NATNUM (start1); - starting1 = BIGNUMP (start1) ? MOST_POSITIVE_FIXNUM + 1 : XFIXNUM (start1); - CHECK_NATNUM (start2); - starting2 = BIGNUMP (start2) ? MOST_POSITIVE_FIXNUM + 1 : XFIXNUM (start2); - - if (!NILP (end1)) - { - CHECK_NATNUM (end1); - ending1 = BIGNUMP (end1) ? MOST_POSITIVE_FIXNUM + 1 : XFIXNUM (end1); - } - - if (!NILP (end2)) - { - CHECK_NATNUM (end2); - ending2 = BIGNUMP (end2) ? MOST_POSITIVE_FIXNUM + 1 : XFIXNUM (end2); - } - - sequence1_listp = LISTP (sequence1); - sequence2_listp = LISTP (sequence2); - - overwriting = overwriting && starting2 <= starting1; - - if (sequence1_listp && !ZEROP (start1)) - { - sequence1 = Fnthcdr (start1, sequence1); - - if (NILP (sequence1)) - { - check_sequence_range (args[0], start1, end1, Flength (args[0])); - /* Give up early here. */ - return result; - } - - ending1 -= starting1; - starting1 = 0; - } - - if (sequence2_listp && !ZEROP (start2)) - { - sequence2 = Fnthcdr (start2, sequence2); - - if (NILP (sequence2)) - { - check_sequence_range (args[1], start1, end1, Flength (args[1])); - /* Nothing available to replace sequence1's contents. */ - return result; - } - - ending2 -= starting2; - starting2 = 0; - } - - if (overwriting) - { - if (EQ (start1, start2)) - { - return result; - } - - /* Our ranges may overlap. Save the data that might be overwritten. */ - - if (CONSP (sequence2)) - { - Elemcount len = XFIXNUM (Flength (sequence2)); - Lisp_Object *subsequence - = alloca_array (Lisp_Object, min (ending2, len)); - Elemcount ii = 0; - - LIST_LOOP_2 (elt, sequence2) - { - if (counting == ending2) - { - break; - } - - subsequence[ii++] = elt; - counting++; - } - - check_sequence_range (sequence1, start1, end1, - /* The XFIXNUM (start2) is intentional here; we - called #'length after doing (nthcdr - start2 sequence2). */ - make_fixnum (XFIXNUM (start2) + len)); - check_sequence_range (sequence2, start2, end2, - make_fixnum (XFIXNUM (start2) + len)); - - while (starting1 < ending1 - && starting2 < ending2 && !NILP (sequence1)) - { - XSETCAR (sequence1, subsequence[starting2]); - sequence1 = XCDR (sequence1); - starting1++; - starting2++; - } - } - else if (STRINGP (sequence2)) - { - Ibyte *p = XSTRING_DATA (sequence2), - *pend = p + XSTRING_LENGTH (sequence2), *pcursor, - *staging; - Bytecount ii = 0; - - while (ii < starting2 && p < pend) - { - INC_IBYTEPTR (p); - ii++; - } - - pcursor = p; - - while (ii < ending2 && starting1 < ending1 && pcursor < pend) - { - INC_IBYTEPTR (pcursor); - starting1++; - ii++; - } - - if (pcursor == pend) - { - check_sequence_range (sequence1, start1, end1, make_fixnum (ii)); - check_sequence_range (sequence2, start2, end2, make_fixnum (ii)); - } - else - { - assert ((pcursor - p) > 0); - staging = alloca_ibytes (pcursor - p); - memcpy (staging, p, pcursor - p); - replace_string_range (result, start1, - make_fixnum (starting1), - staging, staging + (pcursor - p)); - } - } - else - { - Elemcount seq_len = XFIXNUM (Flength (sequence2)), ii = 0, - subseq_len = min (min (ending1 - starting1, seq_len - starting1), - min (ending2 - starting2, seq_len - starting2)); - Lisp_Object *subsequence = alloca_array (Lisp_Object, subseq_len); - - check_sequence_range (sequence1, start1, end1, make_fixnum (seq_len)); - check_sequence_range (sequence2, start2, end2, make_fixnum (seq_len)); - - while (starting2 < ending2 && ii < seq_len) - { - subsequence[ii] = Faref (sequence2, make_fixnum (starting2)); - ii++, starting2++; - } - - ii = 0; - - while (starting1 < ending1 && ii < seq_len) - { - Faset (sequence1, make_fixnum (starting1), subsequence[ii]); - ii++, starting1++; - } - } - } - else if (sequence1_listp && sequence2_listp) - { - Lisp_Object sequence1_tortoise = sequence1, - sequence2_tortoise = sequence2; - Elemcount shortest_len = 0; - - counting = startcounting = min (ending1, ending2); - - while (counting-- > 0 && !NILP (sequence1) && !NILP (sequence2)) - { - XSETCAR (sequence1, - CONSP (sequence2) ? XCAR (sequence2) - : Fcar (sequence2)); - sequence1 = CONSP (sequence1) ? XCDR (sequence1) - : Fcdr (sequence1); - sequence2 = CONSP (sequence2) ? XCDR (sequence2) - : Fcdr (sequence2); - - shortest_len++; - - if (startcounting - counting > CIRCULAR_LIST_SUSPICION_LENGTH) - { - if (counting & 1) - { - sequence1_tortoise = XCDR (sequence1_tortoise); - sequence2_tortoise = XCDR (sequence2_tortoise); - } - - if (EQ (sequence1, sequence1_tortoise)) - { - signal_circular_list_error (sequence1); - } - - if (EQ (sequence2, sequence2_tortoise)) - { - signal_circular_list_error (sequence2); - } - } - } - - if (NILP (sequence1)) - { - check_sequence_range (args[0], start1, end1, - make_fixnum (XFIXNUM (start1) + shortest_len)); - } - else if (NILP (sequence2)) - { - check_sequence_range (args[1], start2, end2, - make_fixnum (XFIXNUM (start2) + shortest_len)); - } - } - else if (sequence1_listp) - { - if (STRINGP (sequence2)) - { - Ibyte *s2_data = XSTRING_DATA (sequence2), - *s2_end = s2_data + XSTRING_LENGTH (sequence2); - Elemcount char_count = 0; - Lisp_Object character; - - while (char_count < starting2 && s2_data < s2_end) - { - INC_IBYTEPTR (s2_data); - char_count++; - } - - while (starting1 < ending1 && starting2 < ending2 - && s2_data < s2_end && !NILP (sequence1)) - { - character = make_char (itext_ichar (s2_data)); - CONSP (sequence1) ? - XSETCAR (sequence1, character) - : Fsetcar (sequence1, character); - sequence1 = XCDR (sequence1); - starting1++; - starting2++; - char_count++; - INC_IBYTEPTR (s2_data); - } - - if (NILP (sequence1)) - { - check_sequence_range (sequence1, start1, end1, - make_fixnum (XFIXNUM (start1) + starting1)); - } - - if (s2_data == s2_end) - { - check_sequence_range (sequence2, start2, end2, - make_fixnum (char_count)); - } - } - else - { - Elemcount len2 = XFIXNUM (Flength (sequence2)); - check_sequence_range (sequence2, start2, end2, make_fixnum (len2)); - - ending2 = min (ending2, len2); - while (starting2 < ending2 - && starting1 < ending1 && !NILP (sequence1)) - { - CHECK_CONS (sequence1); - XSETCAR (sequence1, Faref (sequence2, make_fixnum (starting2))); - sequence1 = XCDR (sequence1); - starting1++; - starting2++; - } - - if (NILP (sequence1)) - { - check_sequence_range (args[0], start1, end1, - make_fixnum (XFIXNUM (start1) + starting1)); - } - } - } - else if (sequence2_listp) - { - if (STRINGP (sequence1)) - { - Elemcount ii = 0, count, len = string_char_length (sequence1); - Ibyte *staging, *cursor; - Lisp_Object obj; - - check_sequence_range (sequence1, start1, end1, make_fixnum (len)); - ending1 = min (ending1, len); - count = ending1 - starting1; - staging = cursor = alloca_ibytes (count * MAX_ICHAR_LEN); - - while (ii < count && !NILP (sequence2)) - { - obj = CONSP (sequence2) ? XCAR (sequence2) - : Fcar (sequence2); - - CHECK_CHAR_COERCE_INT (obj); - cursor += set_itext_ichar (cursor, XCHAR (obj)); - ii++; - sequence2 = XCDR (sequence2); - } - - if (NILP (sequence2)) - { - check_sequence_range (sequence2, start2, end2, - make_fixnum (XFIXNUM (start2) + ii)); - } - - replace_string_range (result, start1, make_fixnum (XFIXNUM (start1) + ii), - staging, cursor); - } - else - { - Elemcount len = XFIXNUM (Flength (sequence1)); - - check_sequence_range (sequence1, start2, end1, make_fixnum (len)); - ending1 = min (ending2, min (ending1, len)); - - while (starting1 < ending1 && !NILP (sequence2)) - { - Faset (sequence1, make_fixnum (starting1), - CONSP (sequence2) ? XCAR (sequence2) - : Fcar (sequence2)); - sequence2 = XCDR (sequence2); - starting1++; - starting2++; - } - - if (NILP (sequence2)) - { - check_sequence_range (args[1], start2, end2, - make_fixnum (XFIXNUM (start2) + starting2)); - } - } - } - else - { - if (STRINGP (sequence1) && STRINGP (sequence2)) - { - Ibyte *p2 = XSTRING_DATA (sequence2), - *p2end = p2 + XSTRING_LENGTH (sequence2), *p2cursor; - Charcount ii = 0, len1 = string_char_length (sequence1); - - check_sequence_range (sequence1, start1, end1, make_fixnum (len1)); - - while (ii < starting2 && p2 < p2end) - { - INC_IBYTEPTR (p2); - ii++; - } - - p2cursor = p2; - ending1 = min (ending1, len1); - - while (ii < ending2 && starting1 < ending1 && p2cursor < p2end) - { - INC_IBYTEPTR (p2cursor); - ii++; - starting1++; - } - - if (p2cursor == p2end) - { - check_sequence_range (sequence2, start2, end2, make_fixnum (ii)); - } - - /* This isn't great; any error message won't necessarily reflect - the END1 that was supplied to #'replace. */ - replace_string_range (result, start1, make_fixnum (starting1), - p2, p2cursor); - } - else if (STRINGP (sequence1)) - { - Ibyte *staging, *cursor; - Elemcount count, len1 = string_char_length (sequence1); - Elemcount len2 = XFIXNUM (Flength (sequence2)), ii = 0; - Lisp_Object obj; - - check_sequence_range (sequence1, start1, end1, make_fixnum (len1)); - check_sequence_range (sequence2, start2, end2, make_fixnum (len2)); - - ending1 = min (ending1, len1); - ending2 = min (ending2, len2); - count = min (ending1 - starting1, ending2 - starting2); - staging = cursor = alloca_ibytes (count * MAX_ICHAR_LEN); - - ii = 0; - while (ii < count) - { - obj = Faref (sequence2, make_fixnum (starting2)); - - CHECK_CHAR_COERCE_INT (obj); - cursor += set_itext_ichar (cursor, XCHAR (obj)); - starting2++, ii++; - } - - replace_string_range (result, start1, - make_fixnum (XFIXNUM (start1) + count), - staging, cursor); - } - else if (STRINGP (sequence2)) - { - Ibyte *p2 = XSTRING_DATA (sequence2), - *p2end = p2 + XSTRING_LENGTH (sequence2); - Elemcount len1 = XFIXNUM (Flength (sequence1)), ii = 0; - - check_sequence_range (sequence1, start1, end1, make_fixnum (len1)); - ending1 = min (ending1, len1); - - while (ii < starting2 && p2 < p2end) - { - INC_IBYTEPTR (p2); - ii++; - } - - while (p2 < p2end && starting1 < ending1 && starting2 < ending2) - { - Faset (sequence1, make_fixnum (starting1), - make_char (itext_ichar (p2))); - INC_IBYTEPTR (p2); - starting1++; - starting2++; - ii++; - } - - if (p2 == p2end) - { - check_sequence_range (sequence2, start2, end2, make_fixnum (ii)); - } - } - else - { - Elemcount len1 = XFIXNUM (Flength (sequence1)), - len2 = XFIXNUM (Flength (sequence2)); - - check_sequence_range (sequence1, start1, end1, make_fixnum (len1)); - check_sequence_range (sequence2, start2, end2, make_fixnum (len2)); - - ending1 = min (ending1, len1); - ending2 = min (ending2, len2); - - while (starting1 < ending1 && starting2 < ending2) - { - Faset (sequence1, make_fixnum (starting1), - Faref (sequence2, make_fixnum (starting2))); - starting1++; - starting2++; - } - } - } - - return result; -} - -DEFUN ("nsubstitute", Fnsubstitute, 3, MANY, 0, /* -Substitute NEW for OLD in SEQUENCE. - -This is a destructive function; it reuses the storage of SEQUENCE whenever -possible. See `remove*' for the meaning of the keywords. - -arguments: (NEW OLD SEQUENCE &key (TEST #'eql) (KEY #'identity) (START 0) (END (length SEQUENCE)) FROM-END COUNT) -*/ - (int nargs, Lisp_Object *args)) -{ - Lisp_Object new_ = args[0], item = args[1], sequence = args[2]; - Lisp_Object object_, position0; - Elemcount starting = 0, ending = MOST_POSITIVE_FIXNUM, encountered = 0; - Elemcount len, ii = 0, counting = MOST_POSITIVE_FIXNUM, presenting = 0; - Boolint test_not_unboundp = 1; - check_test_func_t check_test = NULL; - - PARSE_KEYWORDS (Fnsubstitute, nargs, args, 9, - (test, if_, if_not, test_not, key, start, end, count, - from_end), (start = Qzero)); - - CHECK_SEQUENCE (sequence); - CHECK_NATNUM (start); - starting = BIGNUMP (start) ? 1 + MOST_POSITIVE_FIXNUM : XFIXNUM (start); - - if (!NILP (end)) - { - CHECK_NATNUM (end); - ending = BIGNUMP (end) ? 1 + MOST_POSITIVE_FIXNUM : XFIXNUM (end); - } - - if (!NILP (count)) - { - CHECK_INTEGER (count); - if (FIXNUMP (count)) - { - counting = XFIXNUM (count); - } -#ifdef HAVE_BIGNUM - else - { - counting = bignum_sign (XBIGNUM_DATA (count)) > 0 ? - 1 + MOST_POSITIVE_FIXNUM : -1 + MOST_NEGATIVE_FIXNUM; - } -#endif - - if (counting <= 0) - { - return sequence; - } - } - - check_test = get_check_test_function (item, &test, test_not, if_, if_not, - key, &test_not_unboundp); - - if (CONSP (sequence)) - { - if (!NILP (count) && !NILP (from_end)) - { - Lisp_Object present = count_with_tail (&object_, nargs - 1, args + 1, - Qnsubstitute); - - if (ZEROP (present)) - { - return sequence; - } - - presenting = XFIXNUM (present); - presenting = presenting <= counting ? 0 : presenting - counting; - } - - { - GC_EXTERNAL_LIST_LOOP_3 (elt, sequence, tail) - { - if (!(ii < ending)) - { - break; - } - - if (starting <= ii && - check_test (test, key, item, elt) == test_not_unboundp - && (presenting ? encountered++ >= presenting - : encountered++ < counting)) - { - CHECK_LISP_WRITEABLE (tail); - XSETCAR (tail, new_); - } - else if (!presenting && encountered >= counting) - { - break; - } - - ii++; - } - END_GC_EXTERNAL_LIST_LOOP (elt); - } - - if ((ii < starting || (ii < ending && !NILP (end))) - && encountered < counting) - { - check_sequence_range (args[0], start, end, Flength (args[0])); - } - } - else if (STRINGP (sequence)) - { - Ibyte *staging, new_bytes[MAX_ICHAR_LEN], *staging_cursor; - Ibyte *startp = XSTRING_DATA (sequence), *cursor = startp; - Bytecount cursor_offset = 0, byte_len = XSTRING_LENGTH (sequence); - Bytecount new_len; - Lisp_Object character; - - CHECK_CHAR_COERCE_INT (new_); - - new_len = set_itext_ichar (new_bytes, XCHAR (new_)); - - /* Worst case scenario; new char is four octets long, all the old ones - were one octet long, all the old ones match. */ - staging = alloca_ibytes (XSTRING_LENGTH (sequence) * new_len); - staging_cursor = staging; - - if (!NILP (count) && !NILP (from_end)) - { - Lisp_Object present = count_with_tail (&character, nargs - 1, - args + 1, Qnsubstitute); - - if (ZEROP (present)) - { - return sequence; - } - - presenting = XFIXNUM (present); - - /* If there are fewer items in the string than we have - permission to change, we don't need to differentiate - between the :from-end nil and :from-end t - cases. Otherwise, presenting is the number of matching - items we need to ignore before we start to change. */ - presenting = presenting <= counting ? 0 : presenting - counting; - } - - ii = 0; - while (cursor_offset < byte_len && ii < ending) - { - if (ii >= starting) - { - character = make_char (itext_ichar (cursor)); - - if ((check_test (test, key, item, character) - == test_not_unboundp) - && (presenting ? encountered++ >= presenting : - encountered++ < counting)) - { - staging_cursor - += itext_copy_ichar (new_bytes, staging_cursor); - } - else - { - staging_cursor - += itext_copy_ichar (cursor, staging_cursor); - } - - startp = XSTRING_DATA (sequence); - cursor = startp + cursor_offset; - - if (byte_len != XSTRING_LENGTH (sequence) - || !valid_ibyteptr_p (cursor)) - { - mapping_interaction_error (Qnsubstitute, sequence); - } - } - else - { - staging_cursor += itext_copy_ichar (cursor, staging_cursor); - } - - INC_IBYTEPTR (cursor); - cursor_offset = cursor - startp; - ii++; - } - - if (ii < starting || (ii < ending && !NILP (end))) - { - check_sequence_range (sequence, start, end, Flength (sequence)); - } - - if (0 != encountered) - { - CHECK_LISP_WRITEABLE (sequence); - replace_string_range (sequence, Qzero, make_fixnum (ii), - staging, staging_cursor); - } - } - else - { - Elemcount positioning; - Lisp_Object object = Qnil; - - len = XFIXNUM (Flength (sequence)); - check_sequence_range (sequence, start, end, make_fixnum (len)); - - position0 = position (&object, item, sequence, check_test, - test_not_unboundp, test, key, start, end, from_end, - Qnil, Qnsubstitute); - - if (NILP (position0)) - { - return sequence; - } - - positioning = XFIXNUM (position0); - ending = min (len, ending); - - Faset (sequence, position0, new_); - encountered = 1; - - if (NILP (from_end)) - { - for (ii = positioning + 1; ii < ending; ii++) - { - object_ = Faref (sequence, make_fixnum (ii)); - - if (check_test (test, key, item, object_) == test_not_unboundp - && encountered++ < counting) - { - Faset (sequence, make_fixnum (ii), new_); - } - else if (encountered == counting) - { - break; - } - } - } - else - { - for (ii = positioning - 1; ii >= starting; ii--) - { - object_ = Faref (sequence, make_fixnum (ii)); - - if (check_test (test, key, item, object_) == test_not_unboundp - && encountered++ < counting) - { - Faset (sequence, make_fixnum (ii), new_); - } - else if (encountered == counting) - { - break; - } - } - } - } - - return sequence; -} - -DEFUN ("substitute", Fsubstitute, 3, MANY, 0, /* -Substitute NEW for OLD in SEQUENCE. - -This is a non-destructive function; it makes a copy of SEQUENCE if necessary -to avoid corrupting the original SEQUENCE. - -See `remove*' for the meaning of the keywords. - -arguments: (NEW OLD SEQUENCE &key (TEST #'eql) (KEY #'identity) (START 0) (END (length SEQUENCE)) COUNT) -*/ - (int nargs, Lisp_Object *args)) -{ - Lisp_Object new_ = args[0], item = args[1], sequence = args[2], tail = Qnil; - Lisp_Object result = Qnil, result_tail = Qnil; - Lisp_Object object, position0, matched_count; - Elemcount starting = 0, ending = MOST_POSITIVE_FIXNUM, encountered = 0; - Elemcount ii = 0, counting = MOST_POSITIVE_FIXNUM, presenting = 0; - Boolint test_not_unboundp = 1; - check_test_func_t check_test = NULL; - struct gcpro gcpro1; - - PARSE_KEYWORDS (Fsubstitute, nargs, args, 9, - (test, if_, if_not, test_not, key, start, end, count, - from_end), (start = Qzero, count = Qunbound)); - - CHECK_SEQUENCE (sequence); - - CHECK_NATNUM (start); - starting = BIGNUMP (start) ? 1 + MOST_POSITIVE_FIXNUM : XFIXNUM (start); - - if (!NILP (end)) - { - CHECK_NATNUM (end); - ending = BIGNUMP (end) ? 1 + MOST_POSITIVE_FIXNUM : XFIXNUM (end); - } - - check_test = get_check_test_function (item, &test, test_not, if_, if_not, - key, &test_not_unboundp); - - if (!UNBOUNDP (count)) - { - if (!NILP (count)) - { - CHECK_INTEGER (count); - if (FIXNUMP (count)) - { - counting = XFIXNUM (count); - } -#ifdef HAVE_BIGNUM - else - { - counting = bignum_sign (XBIGNUM_DATA (count)) > 0 ? - 1 + MOST_POSITIVE_FIXNUM : -1 + MOST_NEGATIVE_FIXNUM; - } -#endif - - if (counting <= 0) - { - return sequence; - } - } - } - - if (!CONSP (sequence)) - { - position0 = position (&object, item, sequence, check_test, - test_not_unboundp, test, key, start, end, from_end, - Qnil, Qsubstitute); - - if (NILP (position0)) - { - return sequence; - } - else - { - args[2] = Fcopy_sequence (sequence); - return Fnsubstitute (nargs, args); - } - } - - matched_count = count_with_tail (&tail, nargs - 1, args + 1, Qsubstitute); - - if (ZEROP (matched_count)) - { - return sequence; - } - - if (!NILP (count) && !NILP (from_end)) - { - presenting = XFIXNUM (matched_count); - presenting = presenting <= counting ? 0 : presenting - counting; - } - - GCPRO1 (result); - { - GC_EXTERNAL_LIST_LOOP_3 (elt, sequence, tailing) - { - if (EQ (tail, tailing)) - { - XUNGCPRO (elt); - UNGCPRO; - - if (NILP (result)) - { - return XCDR (tail); - } - - XSETCDR (result_tail, XCDR (tail)); - return result; - } - else if (starting <= ii && ii < ending && - (check_test (test, key, item, elt) == test_not_unboundp) - && (presenting ? encountered++ >= presenting - : encountered++ < counting)) - { - if (NILP (result)) - { - result = result_tail = Fcons (new_, Qnil); - } - else - { - XSETCDR (result_tail, Fcons (new_, Qnil)); - result_tail = XCDR (result_tail); - } - } - else if (NILP (result)) - { - result = result_tail = Fcons (elt, Qnil); - } - else - { - XSETCDR (result_tail, Fcons (elt, Qnil)); - result_tail = XCDR (result_tail); - } - - if (ii == ending) - { - break; - } - - ii++; - } - END_GC_EXTERNAL_LIST_LOOP (elt); - } - UNGCPRO; - - if (ii < starting || (ii < ending && !NILP (end))) - { - check_sequence_range (args[0], start, end, Flength (args[0])); - } - - return result; -} - -static Lisp_Object -subst (Lisp_Object new_, Lisp_Object old, Lisp_Object tree, int depth) -{ - if (depth + lisp_eval_depth > max_lisp_eval_depth) - { - stack_overflow ("Stack overflow in subst", tree); - } - - if (EQ (tree, old)) - { - return new_; - } - else if (CONSP (tree)) - { - Lisp_Object aa = subst (new_, old, XCAR (tree), depth + 1); - Lisp_Object dd = subst (new_, old, XCDR (tree), depth + 1); - - if (EQ (aa, XCAR (tree)) && EQ (dd, XCDR (tree))) - { - return tree; - } - else - { - return Fcons (aa, dd); - } - } - else - { - return tree; - } -} - -static Lisp_Object -sublis (Lisp_Object alist, Lisp_Object tree, - check_test_func_t check_test, Boolint test_not_unboundp, - Lisp_Object test, Lisp_Object key, int depth) -{ - Lisp_Object keyed = KEY (key, tree), aa, dd; - - if (depth + lisp_eval_depth > max_lisp_eval_depth) - { - stack_overflow ("Stack overflow in sublis", tree); - } - - { - GC_EXTERNAL_LIST_LOOP_2 (elt, alist) - { - if (CONSP (elt) && - check_test (test, key, XCAR (elt), keyed) == test_not_unboundp) - { - XUNGCPRO (elt); - return XCDR (elt); - } - } - END_GC_EXTERNAL_LIST_LOOP (elt); - } - - if (!CONSP (tree)) - { - return tree; - } - - aa = sublis (alist, XCAR (tree), check_test, test_not_unboundp, test, key, - depth + 1); - dd = sublis (alist, XCDR (tree), check_test, test_not_unboundp, test, key, - depth + 1); - - if (EQ (aa, XCAR (tree)) && EQ (dd, XCDR (tree))) - { - return tree; - } - - return Fcons (aa, dd); -} - -DEFUN ("sublis", Fsublis, 2, MANY, 0, /* -Perform substitutions indicated by ALIST in TREE (non-destructively). -Return a copy of TREE with all matching elements replaced. - -See `member*' for the meaning of :test, :test-not and :key. - -arguments: (ALIST TREE &key (TEST #'eql) (KEY #'identity) TEST-NOT) -*/ - (int nargs, Lisp_Object *args)) -{ - Lisp_Object alist = args[0], tree = args[1]; - Boolint test_not_unboundp = 1; - check_test_func_t check_test = NULL; - - PARSE_KEYWORDS (Fsublis, nargs, args, 5, (test, if_, test_not, if_not, key), - (key = Qidentity)); - - if (NILP (key)) - { - key = Qidentity; - } - - get_check_match_function (&test, test_not, if_, if_not, - /* sublis() is going to apply the key, don't ask - for a match function that will do it for - us. */ - Qidentity, &test_not_unboundp, &check_test); - - if (CONSP (alist) && NILP (XCDR (alist)) && CONSP (XCAR (alist)) - && EQ (key, Qidentity) && 1 == test_not_unboundp - && (check_eq_nokey == check_test || - (check_eql_nokey == check_test && - !NON_FIXNUM_NUMBER_P (XCAR (XCAR (alist)))))) - { - /* #'subst with #'eq is very cheap indeed; call it. */ - return subst (XCDR (XCAR (alist)), XCAR (XCAR (alist)), tree, 0); - } - - return sublis (alist, tree, check_test, test_not_unboundp, test, key, 0); -} - -static Lisp_Object -nsublis (Lisp_Object alist, Lisp_Object tree, - check_test_func_t check_test, - Boolint test_not_unboundp, - Lisp_Object test, Lisp_Object key, int depth) -{ - Lisp_Object tree_saved = tree, tortoise = tree, keyed = Qnil; - struct gcpro gcpro1, gcpro2; - int count = 0; - - if (depth + lisp_eval_depth > max_lisp_eval_depth) - { - stack_overflow ("Stack overflow in nsublis", tree); - } - - GCPRO2 (tree_saved, keyed); - - while (CONSP (tree)) - { - Boolint replaced = 0; - keyed = KEY (key, XCAR (tree)); - - { - GC_EXTERNAL_LIST_LOOP_2 (elt, alist) - { - if (CONSP (elt) && - check_test (test, key, XCAR (elt), keyed) == test_not_unboundp) - { - CHECK_LISP_WRITEABLE (tree); - /* See comment in sublis() on using elt_cdr. */ - XSETCAR (tree, XCDR (elt)); - replaced = 1; - break; - } - } - END_GC_EXTERNAL_LIST_LOOP (elt); - } - - if (!replaced) - { - if (CONSP (XCAR (tree))) - { - nsublis (alist, XCAR (tree), check_test, test_not_unboundp, - test, key, depth + 1); - } - } - - keyed = KEY (key, XCDR (tree)); - replaced = 0; - - { - GC_EXTERNAL_LIST_LOOP_2 (elt, alist) - { - if (CONSP (elt) && - check_test (test, key, XCAR (elt), keyed) == test_not_unboundp) - { - CHECK_LISP_WRITEABLE (tree); - XSETCDR (tree, XCDR (elt)); - tree = Qnil; - break; - } - } - END_GC_EXTERNAL_LIST_LOOP (elt); - } - - if (!NILP (tree)) - { - tree = XCDR (tree); - } - - if (++count > CIRCULAR_LIST_SUSPICION_LENGTH) - { - if (count & 1) - { - tortoise = XCDR (tortoise); - } - - if (EQ (tortoise, tree)) - { - signal_circular_list_error (tree); - } - } - } - - RETURN_UNGCPRO (tree_saved); -} - -DEFUN ("nsublis", Fnsublis, 2, MANY, 0, /* -Perform substitutions indicated by ALIST in TREE (destructively). -Any matching element of TREE is changed via a call to `setcar'. - -See `member*' for the meaning of :test, :test-not and :key. - -arguments: (ALIST TREE &key (TEST #'eql) (KEY #'identity) TEST-NOT) -*/ - (int nargs, Lisp_Object *args)) -{ - Lisp_Object alist = args[0], tree = args[1], tailed = Qnil, keyed = Qnil; - Boolint test_not_unboundp = 1; - check_test_func_t check_test = NULL; - struct gcpro gcpro1, gcpro2; - - PARSE_KEYWORDS (Fnsublis, nargs, args, 5, (test, if_, test_not, if_not, key), - (key = Qidentity)); - - if (NILP (key)) - { - key = Qidentity; - } - - get_check_match_function (&test, test_not, if_, if_not, - /* nsublis() is going to apply the key, don't ask - for a match function that will do it for - us. */ - Qidentity, &test_not_unboundp, &check_test); - - GCPRO2 (tailed, keyed); - - keyed = KEY (key, tree); - - { - /* nsublis() won't attempt to replace a cons handed to it, do that - ourselves. */ - GC_EXTERNAL_LIST_LOOP_2 (elt, alist) - { - if (CONSP (elt) && - check_test (test, key, XCAR (elt), keyed) == test_not_unboundp) - { - XUNGCPRO (elt); - return XCDR (elt); - } - } - END_GC_EXTERNAL_LIST_LOOP (elt); - } - - UNGCPRO; - - return nsublis (alist, tree, check_test, test_not_unboundp, test, key, 0); -} - -DEFUN ("subst", Fsubst, 3, MANY, 0, /* -Substitute NEW for OLD everywhere in TREE (non-destructively). - -Return a copy of TREE with all elements `eql' to OLD replaced by NEW. - -See `member*' for the meaning of :test, :test-not and :key. - -arguments: (NEW OLD TREE &key (TEST #'eql) (KEY #'identity) TEST-NOT) -*/ - (int nargs, Lisp_Object *args)) -{ - Lisp_Object result, alist = noseeum_cons (noseeum_cons (args[1], args[0]), - Qnil); - args[1] = alist; - result = Fsublis (nargs - 1, args + 1); - free_cons (XCAR (alist)); - free_cons (alist); - - return result; -} - -DEFUN ("nsubst", Fnsubst, 3, MANY, 0, /* -Substitute NEW for OLD everywhere in TREE (destructively). - -Any element of TREE which is `eql' to OLD is changed to NEW (via a call to -`setcar'). - -See `member*' for the meaning of the keywords. The keyword -:descend-structures, not specified by Common Lisp, allows callers to specify -that non-cons objects (vectors and range tables, among others) should also -undergo substitution. - -arguments: (NEW OLD TREE &key (TEST #'eql) (KEY #'identity) TEST-NOT DESCEND-STRUCTURES) -*/ - (int nargs, Lisp_Object *args)) -{ - Lisp_Object new_ = args[0], old = args[1], tree = args[2], result, alist; - Boolint test_not_unboundp = 1; - check_test_func_t check_test = NULL; - - PARSE_KEYWORDS (Fnsubst, nargs, args, 6, (test, if_, test_not, if_not, key, - descend_structures), NULL); - if (!NILP (descend_structures)) - { - check_test = get_check_test_function (old, &test, test_not, if_, if_not, - key, &test_not_unboundp); - - return nsubst_structures (new_, old, tree, check_test, test_not_unboundp, - test, key); - - } - - alist = noseeum_cons (noseeum_cons (old, new_), Qnil); - args[1] = alist; - result = Fnsublis (nargs - 1, args + 1); - free_cons (XCAR (alist)); - free_cons (alist); - - return result; -} - -static Boolint -tree_equal (Lisp_Object tree1, Lisp_Object tree2, - check_test_func_t check_test, Boolint test_not_unboundp, - Lisp_Object test, Lisp_Object key, int depth) -{ - Lisp_Object tortoise1 = tree1, tortoise2 = tree2; - struct gcpro gcpro1, gcpro2; - int count = 0; - Boolint result; - - if (depth + lisp_eval_depth > max_lisp_eval_depth) - { - stack_overflow ("Stack overflow in tree-equal", tree1); - } - - GCPRO2 (tree1, tree2); - - while (CONSP (tree1) && CONSP (tree2) - && tree_equal (XCAR (tree1), XCAR (tree2), check_test, - test_not_unboundp, test, key, depth + 1)) - { - tree1 = XCDR (tree1); - tree2 = XCDR (tree2); - - if (++count > CIRCULAR_LIST_SUSPICION_LENGTH) - { - if (count & 1) - { - tortoise1 = XCDR (tortoise1); - tortoise2 = XCDR (tortoise2); - } - - if (EQ (tortoise1, tree1)) - { - signal_circular_list_error (tree1); - } - - if (EQ (tortoise2, tree2)) - { - signal_circular_list_error (tree2); - } - } - } - - if (CONSP (tree1) || CONSP (tree2)) - { - UNGCPRO; - return 0; - } - - result = check_test (test, key, tree1, tree2) == test_not_unboundp; - UNGCPRO; - - return result; -} - -DEFUN ("tree-equal", Ftree_equal, 2, MANY, 0, /* -Return t if TREE1 and TREE2 have `eql' leaves. - -Atoms are compared by `eql', unless another test is specified using -:test; cons cells are compared recursively. - -See `union' for the meaning of :test, :test-not and :key. - -arguments: (TREE1 TREE2 &key (TEST #'eql) (KEY #'identity) TEST-NOT) -*/ - (int nargs, Lisp_Object *args)) -{ - Lisp_Object tree1 = args[0], tree2 = args[1]; - Boolint test_not_unboundp = 1; - check_test_func_t check_test = NULL; - - PARSE_KEYWORDS (Ftree_equal, nargs, args, 3, (test, key, test_not), - (key = Qidentity)); - - get_check_match_function (&test, test_not, Qnil, Qnil, key, - &test_not_unboundp, &check_test); - - return tree_equal (tree1, tree2, check_test, test_not_unboundp, test, key, - 0) ? Qt : Qnil; -} - -static Lisp_Object -mismatch_from_end (Lisp_Object sequence1, Lisp_Object start1, Lisp_Object end1, - Lisp_Object sequence2, Lisp_Object start2, Lisp_Object end2, - check_test_func_t check_match, Boolint test_not_unboundp, - Lisp_Object test, Lisp_Object key, - Boolint UNUSED (return_sequence1_index)) -{ - Elemcount sequence1_len = XFIXNUM (Flength (sequence1)); - Elemcount sequence2_len = XFIXNUM (Flength (sequence2)), ii = 0; - Elemcount starting1, ending1, starting2, ending2; - Lisp_Object *sequence1_storage = NULL, *sequence2_storage = NULL; - struct gcpro gcpro1, gcpro2; - - check_sequence_range (sequence1, start1, end1, make_fixnum (sequence1_len)); - starting1 = XFIXNUM (start1); - ending1 = FIXNUMP (end1) ? XFIXNUM (end1) : 1 + MOST_POSITIVE_FIXNUM; - ending1 = min (ending1, sequence1_len); - - check_sequence_range (sequence2, start2, end2, make_fixnum (sequence2_len)); - starting2 = XFIXNUM (start2); - ending2 = FIXNUMP (end2) ? XFIXNUM (end2) : 1 + MOST_POSITIVE_FIXNUM; - ending2 = min (ending2, sequence2_len); - - if (LISTP (sequence1)) - { - Lisp_Object *saving; - sequence1_storage = saving - = alloca_array (Lisp_Object, ending1 - starting1); - - { - EXTERNAL_LIST_LOOP_2 (elt, sequence1) - { - if (starting1 <= ii && ii < ending1) - { - *saving++ = elt; - } - else if (ii == ending1) - { - break; - } - - ++ii; - } - } - } - else if (STRINGP (sequence1)) - { - const Ibyte *cursor = string_char_addr (sequence1, starting1); - - STRING_DATA_TO_OBJECT_ARRAY (cursor, sequence1_storage, ii, - ending1 - starting1); - - } - else if (BIT_VECTORP (sequence1)) - { - Lisp_Bit_Vector *vv = XBIT_VECTOR (sequence1); - sequence1_storage = alloca_array (Lisp_Object, ending1 - starting1); - for (ii = starting1; ii < ending1; ++ii) - { - sequence1_storage[ii - starting1] - = make_fixnum (bit_vector_bit (vv, ii)); - } - } - else - { - sequence1_storage = XVECTOR_DATA (sequence1) + starting1; - } - - ii = 0; - - if (LISTP (sequence2)) - { - Lisp_Object *saving; - sequence2_storage = saving - = alloca_array (Lisp_Object, ending2 - starting2); - - { - EXTERNAL_LIST_LOOP_2 (elt, sequence2) - { - if (starting2 <= ii && ii < ending2) - { - *saving++ = elt; - } - else if (ii == ending2) - { - break; - } - - ++ii; - } - } - } - else if (STRINGP (sequence2)) - { - const Ibyte *cursor = string_char_addr (sequence2, starting2); - - STRING_DATA_TO_OBJECT_ARRAY (cursor, sequence2_storage, ii, - ending2 - starting2); - - } - else if (BIT_VECTORP (sequence2)) - { - Lisp_Bit_Vector *vv = XBIT_VECTOR (sequence2); - sequence2_storage = alloca_array (Lisp_Object, ending2 - starting2); - for (ii = starting2; ii < ending2; ++ii) - { - sequence2_storage[ii - starting2] - = make_fixnum (bit_vector_bit (vv, ii)); - } - } - else - { - sequence2_storage = XVECTOR_DATA (sequence2) + starting2; - } - - GCPRO2 (sequence1_storage[0], sequence2_storage[0]); - gcpro1.nvars = ending1 - starting1; - gcpro2.nvars = ending2 - starting2; - - while (ending1 > starting1 && ending2 > starting2) - { - --ending1; - --ending2; - - if (check_match (test, key, sequence1_storage[ending1 - starting1], - sequence2_storage[ending2 - starting2]) - != test_not_unboundp) - { - UNGCPRO; - return make_integer (ending1 + 1); - } - } - - UNGCPRO; - - if (ending1 > starting1 || ending2 > starting2) - { - return make_integer (ending1); - } - - return Qnil; -} - -static Lisp_Object -mismatch_list_list (Lisp_Object sequence1, Lisp_Object start1, Lisp_Object end1, - Lisp_Object sequence2, Lisp_Object start2, Lisp_Object end2, - check_test_func_t check_match, Boolint test_not_unboundp, - Lisp_Object test, Lisp_Object key, - Boolint UNUSED (return_list_index)) -{ - Lisp_Object sequence1_tortoise = sequence1, sequence2_tortoise = sequence2; - Lisp_Object orig_sequence1 = sequence1, orig_sequence2 = sequence2; - Elemcount ending1 = MOST_POSITIVE_FIXNUM, ending2 = MOST_POSITIVE_FIXNUM; - Elemcount starting1, starting2, counting, startcounting; - Elemcount shortest_len = 0; - struct gcpro gcpro1, gcpro2, gcpro3, gcpro4; - - starting1 = FIXNUMP (start1) ? XFIXNUM (start1) : 1 + MOST_POSITIVE_FIXNUM; - starting2 = FIXNUMP (start2) ? XFIXNUM (start2) : 1 + MOST_POSITIVE_FIXNUM; - - if (!NILP (end1)) - { - ending1 = FIXNUMP (end1) ? XFIXNUM (end1) : 1 + MOST_POSITIVE_FIXNUM; - } - - if (!NILP (end2)) - { - ending2 = FIXNUMP (end2) ? XFIXNUM (end2) : 1 + MOST_POSITIVE_FIXNUM; - } - - if (!ZEROP (start1)) - { - sequence1 = Fnthcdr (start1, sequence1); - - if (NILP (sequence1)) - { - check_sequence_range (sequence1_tortoise, start1, end1, - Flength (sequence1_tortoise)); - /* Give up early here. */ - return Qnil; - } - - ending1 -= starting1; - starting1 = 0; - sequence1_tortoise = sequence1; - } - - if (!ZEROP (start2)) - { - sequence2 = Fnthcdr (start2, sequence2); - - if (NILP (sequence2)) - { - check_sequence_range (sequence2_tortoise, start2, end2, - Flength (sequence2_tortoise)); - return Qnil; - } - - ending2 -= starting2; - starting2 = 0; - sequence2_tortoise = sequence2; - } - - GCPRO4 (sequence1, sequence2, sequence1_tortoise, sequence2_tortoise); - - counting = startcounting = min (ending1, ending2); - - while (counting-- > 0 && !NILP (sequence1) && !NILP (sequence2)) - { - if (check_match (test, key, - CONSP (sequence1) ? XCAR (sequence1) - : Fcar (sequence1), - CONSP (sequence2) ? XCAR (sequence2) - : Fcar (sequence2) ) != test_not_unboundp) - { - UNGCPRO; - return make_integer (XFIXNUM (start1) + shortest_len); - } - - sequence1 = CONSP (sequence1) ? XCDR (sequence1) : Fcdr (sequence1); - sequence2 = CONSP (sequence2) ? XCDR (sequence2) : Fcdr (sequence2); - - shortest_len++; - - if (startcounting - counting > CIRCULAR_LIST_SUSPICION_LENGTH) - { - if (counting & 1) - { - sequence1_tortoise = XCDR (sequence1_tortoise); - sequence2_tortoise = XCDR (sequence2_tortoise); - } - - if (EQ (sequence1, sequence1_tortoise)) - { - signal_circular_list_error (sequence1); - } - - if (EQ (sequence2, sequence2_tortoise)) - { - signal_circular_list_error (sequence2); - } - } - } - - UNGCPRO; - - if (NILP (sequence1)) - { - Lisp_Object args[] = { start1, make_fixnum (shortest_len) }; - check_sequence_range (orig_sequence1, start1, end1, - Fplus (countof (args), args)); - } - - if (NILP (sequence2)) - { - Lisp_Object args[] = { start2, make_fixnum (shortest_len) }; - check_sequence_range (orig_sequence2, start2, end2, - Fplus (countof (args), args)); - } - - if ((!NILP (end1) && shortest_len != ending1 - starting1) || - (!NILP (end2) && shortest_len != ending2 - starting2)) - { - return make_integer (XFIXNUM (start1) + shortest_len); - } - - if ((NILP (end1) && CONSP (sequence1)) || (NILP (end2) && CONSP (sequence2))) - { - return make_integer (XFIXNUM (start1) + shortest_len); - } - - return Qnil; -} - -static Lisp_Object -mismatch_list_string (Lisp_Object list, Lisp_Object list_start, - Lisp_Object list_end, - Lisp_Object string, Lisp_Object string_start, - Lisp_Object string_end, - check_test_func_t check_match, - Boolint test_not_unboundp, - Lisp_Object test, Lisp_Object key, - Boolint return_list_index) -{ - Ibyte *string_data = XSTRING_DATA (string), *startp = string_data; - Bytecount string_offset = 0, string_len = XSTRING_LENGTH (string); - Elemcount char_count = 0, list_starting, list_ending; - Elemcount string_starting, string_ending; - Lisp_Object character, orig_list = list; - struct gcpro gcpro1; - - list_ending = FIXNUMP (list_end) ? XFIXNUM (list_end) : 1 + MOST_POSITIVE_FIXNUM; - list_starting = FIXNUMP (list_start) ? XFIXNUM (list_start) : 1 + MOST_POSITIVE_FIXNUM; - - string_ending = FIXNUMP (string_end) ? XFIXNUM (string_end) : 1 + MOST_POSITIVE_FIXNUM; - string_starting - = FIXNUMP (string_start) ? XFIXNUM (string_start) : 1 + MOST_POSITIVE_FIXNUM; - - while (char_count < string_starting && string_offset < string_len) - { - INC_IBYTEPTR (string_data); - string_offset = string_data - startp; - char_count++; - } - - if (!ZEROP (list_start)) - { - list = Fnthcdr (list_start, list); - if (NILP (list)) - { - check_sequence_range (orig_list, list_start, list_end, - Flength (orig_list)); - return Qnil; - } - - list_ending -= list_starting; - list_starting = 0; - } - - GCPRO1 (list); - - while (list_starting < list_ending && string_starting < string_ending - && string_offset < string_len && !NILP (list)) - { - character = make_char (itext_ichar (string_data)); - - if (return_list_index) - { - if (check_match (test, key, CONSP (list) ? XCAR (list) : Fcar (list), - character) - != test_not_unboundp) - { - UNGCPRO; - return make_integer (XFIXNUM (list_start) + char_count); - } - } - else - { - if (check_match (test, key, character, - CONSP (list) ? XCAR (list) : Fcar (list)) - != test_not_unboundp) - { - UNGCPRO; - return make_integer (char_count); - } - } - - list = CONSP (list) ? XCDR (list) : Fcdr (list); - - startp = XSTRING_DATA (string); - string_data = startp + string_offset; - if (string_len != XSTRING_LENGTH (string) - || !valid_ibyteptr_p (string_data)) - { - mapping_interaction_error (Qmismatch, string); - } - - list_starting++; - string_starting++; - char_count++; - INC_IBYTEPTR (string_data); - string_offset = string_data - startp; - } - - UNGCPRO; - - if (NILP (list)) - { - Lisp_Object args[] = { list_start, make_fixnum (char_count) }; - check_sequence_range (orig_list, list_start, list_end, - Fplus (countof (args), args)); - } - - if (string_data == XSTRING_DATA (string) + XSTRING_LENGTH (string)) - { - check_sequence_range (string, string_start, string_end, - make_fixnum (char_count)); - } - - if ((NILP (string_end) ? - string_offset < string_len : string_starting < string_ending) || - (NILP (list_end) ? !NILP (list) : list_starting < list_ending)) - { - return make_integer (return_list_index ? XFIXNUM (list_start) + char_count : - char_count); - } - - return Qnil; -} - -static Lisp_Object -mismatch_list_array (Lisp_Object list, Lisp_Object list_start, - Lisp_Object list_end, - Lisp_Object array, Lisp_Object array_start, - Lisp_Object array_end, - check_test_func_t check_match, - Boolint test_not_unboundp, - Lisp_Object test, Lisp_Object key, - Boolint return_list_index) -{ - Elemcount ii = 0, list_starting, list_ending; - Elemcount array_starting, array_ending, array_len; - Lisp_Object orig_list = list; - struct gcpro gcpro1; - - list_ending = FIXNUMP (list_end) ? XFIXNUM (list_end) : 1 + MOST_POSITIVE_FIXNUM; - list_starting = FIXNUMP (list_start) ? XFIXNUM (list_start) : 1 + MOST_POSITIVE_FIXNUM; - - array_ending = FIXNUMP (array_end) ? XFIXNUM (array_end) : 1 + MOST_POSITIVE_FIXNUM; - array_starting = FIXNUMP (array_start) ? XFIXNUM (array_start) : 1 + MOST_POSITIVE_FIXNUM; - array_len = XFIXNUM (Flength (array)); - - array_ending = min (array_ending, array_len); - - check_sequence_range (array, array_start, array_end, make_fixnum (array_len)); - - if (!ZEROP (list_start)) - { - list = Fnthcdr (list_start, list); - if (NILP (list)) - { - check_sequence_range (orig_list, list_start, list_end, - Flength (orig_list)); - return Qnil; - } - - list_ending -= list_starting; - list_starting = 0; - } - - GCPRO1 (list); - - while (list_starting < list_ending && array_starting < array_ending - && !NILP (list)) - { - if (return_list_index) - { - if (check_match (test, key, CONSP (list) ? XCAR (list) : Fcar (list), - Faref (array, make_fixnum (array_starting))) - != test_not_unboundp) - { - UNGCPRO; - return make_integer (XFIXNUM (list_start) + ii); - } - } - else - { - if (check_match (test, key, Faref (array, make_fixnum (array_starting)), - CONSP (list) ? XCAR (list) : Fcar (list)) - != test_not_unboundp) - { - UNGCPRO; - return make_integer (array_starting); - } - } - - list = CONSP (list) ? XCDR (list) : Fcdr (list); - list_starting++; - array_starting++; - ii++; - } - - UNGCPRO; - - if (NILP (list)) - { - Lisp_Object args[] = { list_start, make_fixnum (ii) }; - check_sequence_range (orig_list, list_start, list_end, - Fplus (countof (args), args)); - } - - if (array_starting < array_ending || - (NILP (list_end) ? !NILP (list) : list_starting < list_ending)) - { - return make_integer (return_list_index ? XFIXNUM (list_start) + ii : - array_starting); - } - - return Qnil; -} - -static Lisp_Object -mismatch_string_array (Lisp_Object string, Lisp_Object string_start, - Lisp_Object string_end, - Lisp_Object array, Lisp_Object array_start, - Lisp_Object array_end, - check_test_func_t check_match, Boolint test_not_unboundp, - Lisp_Object test, Lisp_Object key, - Boolint return_string_index) -{ - Ibyte *string_data = XSTRING_DATA (string), *startp = string_data; - Bytecount string_offset = 0, string_len = XSTRING_LENGTH (string); - Elemcount char_count = 0, array_starting, array_ending, array_length; - Elemcount string_starting, string_ending; - Lisp_Object character; - - array_starting = FIXNUMP (array_start) ? XFIXNUM (array_start) : 1 + MOST_POSITIVE_FIXNUM; - array_ending = FIXNUMP (array_end) ? XFIXNUM (array_end) : 1 + MOST_POSITIVE_FIXNUM; - array_length = XFIXNUM (Flength (array)); - check_sequence_range (array, array_start, array_end, make_fixnum (array_length)); - array_ending = min (array_ending, array_length); - - string_ending = FIXNUMP (string_end) ? XFIXNUM (string_end) : 1 + MOST_POSITIVE_FIXNUM; - string_starting - = FIXNUMP (string_start) ? XFIXNUM (string_start) : 1 + MOST_POSITIVE_FIXNUM; - - while (char_count < string_starting && string_offset < string_len) - { - INC_IBYTEPTR (string_data); - string_offset = string_data - startp; - char_count++; - } - - while (array_starting < array_ending && string_starting < string_ending - && string_offset < string_len) - { - character = make_char (itext_ichar (string_data)); - - if (return_string_index) - { - if (check_match (test, key, character, - Faref (array, make_fixnum (array_starting))) - != test_not_unboundp) - { - return make_integer (char_count); - } - } - else - { - if (check_match (test, key, - Faref (array, make_fixnum (array_starting)), - character) - != test_not_unboundp) - { - return make_integer (XFIXNUM (array_start) + char_count); - } - } - - startp = XSTRING_DATA (string); - string_data = startp + string_offset; - if (string_len != XSTRING_LENGTH (string) - || !valid_ibyteptr_p (string_data)) - { - mapping_interaction_error (Qmismatch, string); - } - - array_starting++; - string_starting++; - char_count++; - INC_IBYTEPTR (string_data); - string_offset = string_data - startp; - } - - if (string_data == XSTRING_DATA (string) + XSTRING_LENGTH (string)) - { - check_sequence_range (string, string_start, string_end, - make_fixnum (char_count)); - } - - if ((NILP (string_end) ? - string_offset < string_len : string_starting < string_ending) || - (NILP (array_end) ? !NILP (array) : array_starting < array_ending)) - { - return make_integer (return_string_index ? char_count : - XFIXNUM (array_start) + char_count); - } - - return Qnil; -} - -static Lisp_Object -mismatch_string_string (Lisp_Object string1, - Lisp_Object string1_start, Lisp_Object string1_end, - Lisp_Object string2, Lisp_Object string2_start, - Lisp_Object string2_end, - check_test_func_t check_match, - Boolint test_not_unboundp, - Lisp_Object test, Lisp_Object key, - Boolint UNUSED (return_string1_index)) -{ - Ibyte *string1_data = XSTRING_DATA (string1), *startp1 = string1_data; - Bytecount string1_offset = 0, string1_len = XSTRING_LENGTH (string1); - Ibyte *string2_data = XSTRING_DATA (string2), *startp2 = string2_data; - Bytecount string2_offset = 0, string2_len = XSTRING_LENGTH (string2); - Elemcount char_count1 = 0, string1_starting, string1_ending; - Elemcount char_count2 = 0, string2_starting, string2_ending; - Lisp_Object character1, character2; - - string1_ending = FIXNUMP (string1_end) ? XFIXNUM (string1_end) : 1 + MOST_POSITIVE_FIXNUM; - string1_starting - = FIXNUMP (string1_start) ? XFIXNUM (string1_start) : 1 + MOST_POSITIVE_FIXNUM; - - string2_starting - = FIXNUMP (string2_start) ? XFIXNUM (string2_start) : 1 + MOST_POSITIVE_FIXNUM; - string2_ending = FIXNUMP (string2_end) ? XFIXNUM (string2_end) : 1 + MOST_POSITIVE_FIXNUM; - - while (char_count1 < string1_starting && string1_offset < string1_len) - { - INC_IBYTEPTR (string1_data); - string1_offset = string1_data - startp1; - char_count1++; - } - - while (char_count2 < string2_starting && string2_offset < string2_len) - { - INC_IBYTEPTR (string2_data); - string2_offset = string2_data - startp2; - char_count2++; - } - - while (string2_starting < string2_ending && string1_starting < string1_ending - && string1_offset < string1_len && string2_offset < string2_len) - { - character1 = make_char (itext_ichar (string1_data)); - character2 = make_char (itext_ichar (string2_data)); - - if (check_match (test, key, character1, character2) - != test_not_unboundp) - { - return make_integer (char_count1); - } - - startp1 = XSTRING_DATA (string1); - string1_data = startp1 + string1_offset; - if (string1_len != XSTRING_LENGTH (string1) - || !valid_ibyteptr_p (string1_data)) - { - mapping_interaction_error (Qmismatch, string1); - } - - startp2 = XSTRING_DATA (string2); - string2_data = startp2 + string2_offset; - if (string2_len != XSTRING_LENGTH (string2) - || !valid_ibyteptr_p (string2_data)) - { - mapping_interaction_error (Qmismatch, string2); - } - - string2_starting++; - string1_starting++; - char_count1++; - char_count2++; - INC_IBYTEPTR (string1_data); - string1_offset = string1_data - startp1; - INC_IBYTEPTR (string2_data); - string2_offset = string2_data - startp2; - } - - if (string1_data == XSTRING_DATA (string1) + XSTRING_LENGTH (string1)) - { - check_sequence_range (string1, string1_start, string1_end, - make_fixnum (char_count1)); - } - - if (string2_data == XSTRING_DATA (string2) + XSTRING_LENGTH (string2)) - { - check_sequence_range (string2, string2_start, string2_end, - make_fixnum (char_count2)); - } - - if ((!NILP (string1_end) && string1_starting < string1_ending) || - (!NILP (string2_end) && string2_starting < string2_ending)) - { - return make_integer (char_count1); - } - - if ((NILP (string1_end) && string1_data - < (XSTRING_DATA (string1) + XSTRING_LENGTH (string1))) || - (NILP (string2_end) && string2_data - < (XSTRING_DATA (string2) + XSTRING_LENGTH (string2)))) - { - return make_integer (char_count1); - } - - return Qnil; -} - -static Lisp_Object -mismatch_array_array (Lisp_Object array1, Lisp_Object start1, Lisp_Object end1, - Lisp_Object array2, Lisp_Object start2, Lisp_Object end2, - check_test_func_t check_match, Boolint test_not_unboundp, - Lisp_Object test, Lisp_Object key, - Boolint UNUSED (return_array1_index)) -{ - Elemcount len1 = XFIXNUM (Flength (array1)), len2 = XFIXNUM (Flength (array2)); - Elemcount ending1 = MOST_POSITIVE_FIXNUM, ending2 = MOST_POSITIVE_FIXNUM; - Elemcount starting1, starting2; - - check_sequence_range (array1, start1, end1, make_fixnum (len1)); - check_sequence_range (array2, start2, end2, make_fixnum (len2)); - - starting1 = FIXNUMP (start1) ? XFIXNUM (start1) : 1 + MOST_POSITIVE_FIXNUM; - starting2 = FIXNUMP (start2) ? XFIXNUM (start2) : 1 + MOST_POSITIVE_FIXNUM; - - if (!NILP (end1)) - { - ending1 = FIXNUMP (end1) ? XFIXNUM (end1) : 1 + MOST_POSITIVE_FIXNUM; - } - - if (!NILP (end2)) - { - ending2 = FIXNUMP (end2) ? XFIXNUM (end2) : 1 + MOST_POSITIVE_FIXNUM; - } - - ending1 = min (ending1, len1); - ending2 = min (ending2, len2); - - while (starting1 < ending1 && starting2 < ending2) - { - if (check_match (test, key, Faref (array1, make_fixnum (starting1)), - Faref (array2, make_fixnum (starting2))) - != test_not_unboundp) - { - return make_integer (starting1); - } - starting1++; - starting2++; - } - - if (starting1 < ending1 || starting2 < ending2) - { - return make_integer (starting1); - } - - return Qnil; -} - -typedef Lisp_Object -(*mismatch_func_t) (Lisp_Object sequence1, Lisp_Object start1, Lisp_Object end1, - Lisp_Object sequence2, Lisp_Object start2, Lisp_Object end2, - check_test_func_t check_match, Boolint test_not_unboundp, - Lisp_Object test, Lisp_Object key, - Boolint return_list_index); - -static mismatch_func_t -get_mismatch_func (Lisp_Object sequence1, Lisp_Object sequence2, - Lisp_Object from_end, Boolint *return_sequence1_index_out) -{ - CHECK_SEQUENCE (sequence1); - CHECK_SEQUENCE (sequence2); - - if (!NILP (from_end)) - { - *return_sequence1_index_out = 1; - return mismatch_from_end; - } - - if (LISTP (sequence1)) - { - if (LISTP (sequence2)) - { - *return_sequence1_index_out = 1; - return mismatch_list_list; - } - - if (STRINGP (sequence2)) - { - *return_sequence1_index_out = 1; - return mismatch_list_string; - } - - *return_sequence1_index_out = 1; - return mismatch_list_array; - } - - if (STRINGP (sequence1)) - { - if (STRINGP (sequence2)) - { - *return_sequence1_index_out = 1; - return mismatch_string_string; - } - - if (LISTP (sequence2)) - { - *return_sequence1_index_out = 0; - return mismatch_list_string; - } - - *return_sequence1_index_out = 1; - return mismatch_string_array; - } - - if (ARRAYP (sequence1)) - { - if (STRINGP (sequence2)) - { - *return_sequence1_index_out = 0; - return mismatch_string_array; - } - - if (LISTP (sequence2)) - { - *return_sequence1_index_out = 0; - return mismatch_list_array; - } - - *return_sequence1_index_out = 1; - return mismatch_array_array; - } - - RETURN_NOT_REACHED (NULL); - return NULL; -} - -DEFUN ("mismatch", Fmismatch, 2, MANY, 0, /* -Compare SEQUENCE1 with SEQUENCE2, return index of first mismatching element. - -Return nil if the sequences match. If one sequence is a prefix of the -other, the return value indicates the end of the shorter sequence. A -non-nil return value always reflects an index into SEQUENCE1. - -See `search' for the meaning of the keywords." - -arguments: (SEQUENCE1 SEQUENCE2 &key (TEST #'eql) (KEY #'identity) (START1 0) END1 (START2 0) END2 FROM-END TEST-NOT) -*/ - (int nargs, Lisp_Object *args)) -{ - Lisp_Object sequence1 = args[0], sequence2 = args[1]; - Boolint test_not_unboundp = 1, return_first_index = 0; - check_test_func_t check_match = NULL; - mismatch_func_t mismatch = NULL; - - PARSE_KEYWORDS (Fmismatch, nargs, args, 8, - (test, key, from_end, start1, end1, start2, end2, test_not), - (start1 = start2 = Qzero)); - - CHECK_SEQUENCE (sequence1); - CHECK_SEQUENCE (sequence2); - - CHECK_NATNUM (start1); - CHECK_NATNUM (start2); - - if (!NILP (end1)) - { - CHECK_NATNUM (end1); - } - - if (!NILP (end2)) - { - CHECK_NATNUM (end2); - } - - check_match = get_check_match_function (&test, test_not, Qnil, Qnil, key, - &test_not_unboundp, NULL); - mismatch = get_mismatch_func (sequence1, sequence2, from_end, - &return_first_index); - - if (return_first_index) - { - return mismatch (sequence1, start1, end1, sequence2, start2, end2, - check_match, test_not_unboundp, test, key, 1); - } - - return mismatch (sequence2, start2, end2, sequence1, start1, end1, - check_match, test_not_unboundp, test, key, 0); -} - -DEFUN ("search", Fsearch, 2, MANY, 0, /* -Search for SEQUENCE1 as a subsequence of SEQUENCE2. - -Return the index of the leftmost element of the first match found; return -nil if there are no matches. - -In this function, :start1 and :end1 specify a subsequence of SEQUENCE1, and -:start2 and :end2 specify a subsequence of SEQUENCE2. See `remove*' for -details of the other keywords. - -arguments: (SEQUENCE1 SEQUENCE2 &key (TEST #'eql) (KEY #'identity) (START1 0) END1 (START2 0) END2 FROM-END TEST-NOT) -*/ - (int nargs, Lisp_Object *args)) -{ - Lisp_Object sequence1 = args[0], sequence2 = args[1], position0 = Qnil; - Boolint test_not_unboundp = 1, return_first = 0; - check_test_func_t check_test = NULL, check_match = NULL; - mismatch_func_t mismatch = NULL; - Elemcount starting1 = 0, ending1 = 1 + MOST_POSITIVE_FIXNUM, starting2 = 0; - Elemcount ending2 = 1 + MOST_POSITIVE_FIXNUM, ii = 0; - Elemcount length1; - Lisp_Object object = Qnil; - struct gcpro gcpro1, gcpro2; - - PARSE_KEYWORDS (Fsearch, nargs, args, 8, - (test, key, from_end, start1, end1, start2, end2, test_not), - (start1 = start2 = Qzero)); - - CHECK_SEQUENCE (sequence1); - CHECK_SEQUENCE (sequence2); - CHECK_KEY_ARGUMENT (key); - - CHECK_NATNUM (start1); - starting1 = FIXNUMP (start1) ? XFIXNUM (start1) : 1 + MOST_POSITIVE_FIXNUM; - CHECK_NATNUM (start2); - starting2 = FIXNUMP (start2) ? XFIXNUM (start2) : 1 + MOST_POSITIVE_FIXNUM; - - if (!NILP (end1)) - { - Lisp_Object len1 = Flength (sequence1); - - CHECK_NATNUM (end1); - check_sequence_range (sequence1, start1, end1, len1); - ending1 = min (XFIXNUM (end1), XFIXNUM (len1)); - } - else - { - end1 = Flength (sequence1); - check_sequence_range (sequence1, start1, end1, end1); - ending1 = XFIXNUM (end1); - } - - length1 = ending1 - starting1; - - if (!NILP (end2)) - { - Lisp_Object len2 = Flength (sequence2); - - CHECK_NATNUM (end2); - check_sequence_range (sequence2, start2, end2, len2); - ending2 = min (XFIXNUM (end2), XFIXNUM (len2)); - } - else - { - end2 = Flength (sequence2); - check_sequence_range (sequence2, start2, end2, end2); - ending2 = XFIXNUM (end2); - } - - check_match = get_check_match_function (&test, test_not, Qnil, Qnil, key, - &test_not_unboundp, &check_test); - mismatch = get_mismatch_func (sequence1, sequence2, from_end, &return_first); - - if (bytecode_arithcompare (start1, make_integer (ending1)) >= 0) - { - if (NILP (from_end)) - { - return start2; - } - - if (NILP (end2)) - { - return Flength (sequence2); - } - - return end2; - } - - if (NILP (from_end)) - { - Lisp_Object mismatch_start1 = Fadd1 (start1); - Lisp_Object first = KEY (key, Felt (sequence1, start1)); - GCPRO2 (first, mismatch_start1); - - ii = starting2; - while (ii < ending2) - { - position0 = position (&object, first, sequence2, check_test, - test_not_unboundp, test, key, make_fixnum (ii), - end2, Qnil, Qnil, Qsearch); - if (NILP (position0)) - { - UNGCPRO; - return Qnil; - } - - if (length1 + XFIXNUM (position0) <= ending2 && - (return_first ? - NILP (mismatch (sequence1, mismatch_start1, end1, - sequence2, - make_fixnum (1 + XFIXNUM (position0)), - make_fixnum (length1 + XFIXNUM (position0)), - check_match, test_not_unboundp, test, key, 1)) : - NILP (mismatch (sequence2, - make_fixnum (1 + XFIXNUM (position0)), - make_fixnum (length1 + XFIXNUM (position0)), - sequence1, mismatch_start1, end1, - check_match, test_not_unboundp, test, key, 0)))) - - - { - UNGCPRO; - return position0; - } - - ii = XFIXNUM (position0) + 1; - } - - UNGCPRO; - } - else - { - Lisp_Object mismatch_end1 = make_integer (ending1 - 1); - Lisp_Object last = KEY (key, Felt (sequence1, mismatch_end1)); - GCPRO2 (last, mismatch_end1); - - ii = ending2; - while (ii > starting2) - { - position0 = position (&object, last, sequence2, check_test, - test_not_unboundp, test, key, start2, - make_fixnum (ii), Qt, Qnil, Qsearch); - - if (NILP (position0)) - { - UNGCPRO; - return Qnil; - } - - if (XFIXNUM (position0) - length1 + 1 >= starting2 && - (return_first ? - NILP (mismatch (sequence1, start1, mismatch_end1, - sequence2, - make_fixnum (XFIXNUM (position0) - length1 + 1), - make_fixnum (XFIXNUM (position0)), - check_match, test_not_unboundp, test, key, 1)) : - NILP (mismatch (sequence2, - make_fixnum (XFIXNUM (position0) - length1 + 1), - make_fixnum (XFIXNUM (position0)), - sequence1, start1, mismatch_end1, - check_match, test_not_unboundp, test, key, 0)))) - { - UNGCPRO; - return make_fixnum (XFIXNUM (position0) - length1 + 1); - } - - ii = XFIXNUM (position0); - } - - UNGCPRO; - } - - return Qnil; -} - -/* These two functions do set operations, those that can be visualised with - Venn diagrams. */ -static Lisp_Object -venn (Lisp_Object caller, int nargs, Lisp_Object *args, Boolint intersectionp) -{ - Lisp_Object liszt1 = args[0], liszt2 = args[1]; - Lisp_Object result = EQ (caller, Qsubsetp) ? Qt : Qnil, result_tail = Qnil; - Lisp_Object keyed = Qnil, ignore = Qnil; - Boolint test_not_unboundp = 1; - check_test_func_t check_test = NULL; - struct gcpro gcpro1, gcpro2; - - PARSE_KEYWORDS_8 (caller, nargs, args, 4, (test, key, test_not, stable), - NULL, 2, 0); - - CHECK_LIST (liszt1); - CHECK_LIST (liszt2); - - CHECK_KEY_ARGUMENT (key); - - if (NILP (liszt1) && intersectionp) - { - return Qnil; - } - - if (NILP (liszt2)) - { - return intersectionp ? Qnil : liszt1; - } - - get_check_match_function (&test, test_not, Qnil, Qnil, key, - &test_not_unboundp, &check_test); - - GCPRO2 (keyed, result); - - { - GC_EXTERNAL_LIST_LOOP_2 (elt, liszt1) - { - keyed = KEY (key, elt); - if (NILP (list_position_cons_before (&ignore, keyed, liszt2, - check_test, test_not_unboundp, - test, key, 0, Qzero, Qnil)) - != intersectionp) - { - if (EQ (Qsubsetp, caller)) - { - result = Qnil; - break; - } - else if (NILP (stable)) - { - result = Fcons (elt, result); - } - else if (NILP (result)) - { - result = result_tail = Fcons (elt, Qnil); - } - else - { - XSETCDR (result_tail, Fcons (elt, Qnil)); - result_tail = XCDR (result_tail); - } - } - } - END_GC_EXTERNAL_LIST_LOOP (elt); - } - - UNGCPRO; - - return result; -} - -static Lisp_Object -nvenn (Lisp_Object caller, int nargs, Lisp_Object *args, Boolint intersectionp) -{ - Lisp_Object liszt1 = args[0], liszt2 = args[1], tortoise_elt, ignore = Qnil; - Lisp_Object elt = Qnil, tail = Qnil, keyed = Qnil, prev_tail = Qnil; - Elemcount count; - Boolint test_not_unboundp = 1; - check_test_func_t check_test = NULL; - struct gcpro gcpro1, gcpro2, gcpro3, gcpro4; - - PARSE_KEYWORDS_8 (caller, nargs, args, 3, (test, key, test_not), - NULL, 2, 0); - - CHECK_LIST (liszt1); - CHECK_LIST (liszt2); - - CHECK_KEY_ARGUMENT (key); - - if (NILP (liszt1) && intersectionp) - { - return Qnil; - } - - if (NILP (liszt2)) - { - return intersectionp ? Qnil : liszt1; - } - - get_check_match_function (&test, test_not, Qnil, Qnil, key, - &test_not_unboundp, &check_test); - - tortoise_elt = tail = liszt1, count = 0; - - GCPRO4 (tail, keyed, liszt1, tortoise_elt); - - while (CONSP (tail) ? (elt = XCAR (tail), 1) : NILP (tail) ? 0 : - (signal_malformed_list_error (liszt1), 0)) - { - keyed = KEY (key, elt); - if (NILP (list_position_cons_before (&ignore, keyed, liszt2, - check_test, test_not_unboundp, - test, key, 0, Qzero, Qnil)) - == intersectionp) - { - if (NILP (prev_tail)) - { - liszt1 = XCDR (tail); - } - else - { - XSETCDR (prev_tail, XCDR (tail)); - } - - tail = XCDR (tail); - /* List is definitely not circular now! */ - count = 0; - } - else - { - prev_tail = tail; - tail = XCDR (tail); - } - - if (count++ < CIRCULAR_LIST_SUSPICION_LENGTH) continue; - - if (count & 1) - { - tortoise_elt = XCDR (tortoise_elt); - } - - if (EQ (elt, tortoise_elt)) - { - signal_circular_list_error (liszt1); - } - } - - UNGCPRO; - - return liszt1; -} - -DEFUN ("intersection", Fintersection, 2, MANY, 0, /* -Combine LIST1 and LIST2 using a set-intersection operation. - -The result list contains all items that appear in both LIST1 and LIST2. -This is a non-destructive function; it makes a copy of the data if necessary -to avoid corrupting the original LIST1 and LIST2. - -A non-nil value for the :stable keyword, not specified by Common Lisp, means -return the items in the order they appear in LIST1. - -See `union' for the meaning of :test, :test-not and :key." - -arguments: (LIST1 LIST2 &key (TEST #'eql) (KEY #'identity) TEST-NOT STABLE) -*/ - (int nargs, Lisp_Object *args)) -{ - return venn (Qintersection, nargs, args, 1); -} - -DEFUN ("nintersection", Fnintersection, 2, MANY, 0, /* -Combine LIST1 and LIST2 using a set-intersection operation. - -The result list contains all items that appear in both LIST1 and LIST2. -This is a destructive function; it reuses the storage of LIST1 whenever -possible. - -See `union' for the meaning of :test, :test-not and :key." - -arguments: (LIST1 LIST2 &key (TEST #'eql) (KEY #'identity) TEST-NOT) -*/ - (int nargs, Lisp_Object *args)) -{ - return nvenn (Qnintersection, nargs, args, 1); -} - -DEFUN ("subsetp", Fsubsetp, 2, MANY, 0, /* -Return non-nil if every element of LIST1 also appears in LIST2. - -See `union' for the meaning of the keyword arguments. - -arguments: (LIST1 LIST2 &key (TEST #'eql) (KEY #'identity) TEST-NOT) -*/ - (int nargs, Lisp_Object *args)) -{ - return venn (Qsubsetp, nargs, args, 0); -} - -DEFUN ("set-difference", Fset_difference, 2, MANY, 0, /* -Combine LIST1 and LIST2 using a set-difference operation. - -The result list contains all items that appear in LIST1 but not LIST2. This -is a non-destructive function; it makes a copy of the data if necessary to -avoid corrupting the original LIST1 and LIST2. - -See `union' for the meaning of :test, :test-not and :key. - -A non-nil value for the :stable keyword, not specified by Common Lisp, means -return the items in the order they appear in LIST1. - -arguments: (LIST1 LIST2 &key (TEST #'eql) (KEY #'identity) TEST-NOT STABLE) -*/ - (int nargs, Lisp_Object *args)) -{ - return venn (Qset_difference, nargs, args, 0); -} - -DEFUN ("nset-difference", Fnset_difference, 2, MANY, 0, /* -Combine LIST1 and LIST2 using a set-difference operation. - -The result list contains all items that appear in LIST1 but not LIST2. This -is a destructive function; it reuses the storage of LIST1 whenever possible. - -See `union' for the meaning of :test, :test-not and :key." - -arguments: (LIST1 LIST2 &key (TEST #'eql) (KEY #'identity) TEST-NOT) -*/ - (int nargs, Lisp_Object *args)) -{ - return nvenn (Qnset_difference, nargs, args, 0); -} - -DEFUN ("nunion", Fnunion, 2, MANY, 0, /* -Combine LIST1 and LIST2 using a set-union operation. -The result list contains all items that appear in either LIST1 or LIST2. - -This is a destructive function, it reuses the storage of LIST1 whenever -possible. - -See `union' for the meaning of :test, :test-not and :key. - -arguments: (LIST1 LIST2 &key (TEST #'eql) (KEY #'identity) TEST-NOT) -*/ - (int nargs, Lisp_Object *args)) -{ - args[0] = nvenn (Qnunion, nargs, args, 0); - return bytecode_nconc2 (args); -} - -DEFUN ("union", Funion, 2, MANY, 0, /* -Combine LIST1 and LIST2 using a set-union operation. -The result list contains all items that appear in either LIST1 or LIST2. -This is a non-destructive function; it makes a copy of the data if necessary -to avoid corrupting the original LIST1 and LIST2. - -The keywords :test and :test-not specify two-argument test and negated-test -predicates, respectively; :test defaults to `eql'. See `member*' for more -information. - -:key specifies a one-argument function that transforms elements of LIST1 -and LIST2 into \"comparison keys\" before the test predicate is applied. -For example, if :key is #'car, then the car of elements from LIST1 is -compared with the car of elements from LIST2. The :key function, however, -does not affect the elements in the returned list, which are taken directly -from the elements in LIST1 and LIST2. - -A non-nil value for the :stable keyword, not specified by Common Lisp, means -return the items of LIST1 in order, followed by the remaining items of LIST2 -in the order they occur in LIST2. - -arguments: (LIST1 LIST2 &key (TEST #'eql) (KEY #'identity) TEST-NOT STABLE) -*/ - (int nargs, Lisp_Object *args)) -{ - Lisp_Object liszt1 = args[0], liszt2 = args[1], ignore = Qnil; - Lisp_Object keyed = Qnil, result, result_tail; - Boolint test_not_unboundp = 1; - check_test_func_t check_test = NULL, check_match = NULL; - struct gcpro gcpro1, gcpro2; - - PARSE_KEYWORDS (Funion, nargs, args, 4, (test, key, test_not, stable), NULL); - - CHECK_LIST (liszt1); - CHECK_LIST (liszt2); - - CHECK_KEY_ARGUMENT (key); - - if (NILP (liszt1)) - { - return liszt2; - } - - if (NILP (liszt2)) - { - return liszt1; - } - - check_match = get_check_match_function (&test, test_not, Qnil, Qnil, key, - &test_not_unboundp, &check_test); - - GCPRO2 (keyed, result); - - if (NILP (stable)) - { - result = liszt2; - { - GC_EXTERNAL_LIST_LOOP_2 (elt, liszt1) - { - keyed = KEY (key, elt); - if (NILP (list_position_cons_before (&ignore, keyed, liszt2, - check_test, test_not_unboundp, - test, key, 0, Qzero, Qnil))) - { - /* The Lisp version of #'union used to check which list was - longer, and use that as the tail of the constructed - list. That fails when the order of arguments to TEST is - specified, as is the case for these functions. We could - pass the reverse_check argument to - list_position_cons_before, but that means any key argument - is called an awful lot more, so it's a space win but not - a time win. */ - result = Fcons (elt, result); - } - } - END_GC_EXTERNAL_LIST_LOOP (elt); - } - } - else - { - result = result_tail = Qnil; - - /* The standard `union' doesn't produce a "stable" union -- it - iterates over the second list instead of the first one, and returns - the values in backwards order. According to the CLTL2 - documentation, `union' is not required to preserve the ordering of - elements in any fashion; providing the functionality for a stable - union is an XEmacs extension. */ - { - GC_EXTERNAL_LIST_LOOP_2 (elt, liszt2) - { - if (NILP (list_position_cons_before (&ignore, elt, liszt1, - check_match, test_not_unboundp, - test, key, 1, Qzero, Qnil))) - { - if (NILP (result)) - { - result = result_tail = Fcons (elt, Qnil); - } - else - { - XSETCDR (result_tail, Fcons (elt, Qnil)); - result_tail = XCDR (result_tail); - } - } - } - END_GC_EXTERNAL_LIST_LOOP (elt); - } - - result = NILP (result) ? liszt1 : nconc2 (Fcopy_list (liszt1), result); - } - - UNGCPRO; - - return result; -} - -DEFUN ("set-exclusive-or", Fset_exclusive_or, 2, MANY, 0, /* -Combine LIST1 and LIST2 using a set-exclusive-or operation. - -The result list contains all items that appear in exactly one of LIST1, LIST2. -This is a non-destructive function; it makes a copy of the data if necessary -to avoid corrupting the original LIST1 and LIST2. - -See `union' for the meaning of :test, :test-not and :key. - -A non-nil value for the :stable keyword, not specified by Common Lisp, means -return the items in the order they appear in LIST1, followed by the -remaining items in the order they appear in LIST2. - -arguments: (LIST1 LIST2 &key (TEST #'eql) (KEY #'identity) TEST-NOT STABLE) -*/ - (int nargs, Lisp_Object *args)) -{ - Lisp_Object liszt1 = args[0], liszt2 = args[1]; - Lisp_Object result = Qnil, result_tail = Qnil, keyed = Qnil, ignore = Qnil; - Boolint test_not_unboundp = 1; - check_test_func_t check_match = NULL, check_test = NULL; - struct gcpro gcpro1, gcpro2; - - PARSE_KEYWORDS (Fset_exclusive_or, nargs, args, 4, - (test, key, test_not, stable), NULL); - - CHECK_LIST (liszt1); - CHECK_LIST (liszt2); - - CHECK_KEY_ARGUMENT (key); - - if (NILP (liszt2)) - { - return liszt1; - } - - check_match = get_check_match_function (&test, test_not, Qnil, Qnil, key, - &test_not_unboundp, &check_test); - - GCPRO2 (keyed, result); - { - GC_EXTERNAL_LIST_LOOP_2 (elt, liszt1) - { - keyed = KEY (key, elt); - if (NILP (list_position_cons_before (&ignore, keyed, liszt2, - check_test, test_not_unboundp, - test, key, 0, Qzero, Qnil))) - { - if (NILP (stable)) - { - result = Fcons (elt, result); - } - else if (NILP (result)) - { - result = result_tail = Fcons (elt, Qnil); - } - else - { - XSETCDR (result_tail, Fcons (elt, Qnil)); - result_tail = XCDR (result_tail); - } - } - } - END_GC_EXTERNAL_LIST_LOOP (elt); - } - - { - GC_EXTERNAL_LIST_LOOP_2 (elt, liszt2) - { - if (NILP (list_position_cons_before (&ignore, elt, liszt1, - check_match, test_not_unboundp, - test, key, 1, Qzero, Qnil))) - { - if (NILP (stable)) - { - result = Fcons (elt, result); - } - else if (NILP (result)) - { - result = result_tail = Fcons (elt, Qnil); - } - else - { - XSETCDR (result_tail, Fcons (elt, Qnil)); - result_tail = XCDR (result_tail); - } - } - } - END_GC_EXTERNAL_LIST_LOOP (elt); - } - - UNGCPRO; - - return result; -} - -DEFUN ("nset-exclusive-or", Fnset_exclusive_or, 2, MANY, 0, /* -Combine LIST1 and LIST2 using a set-exclusive-or operation. - -The result list contains all items that appear in exactly one of LIST1 and -LIST2. This is a destructive function; it reuses the storage of LIST1 and -LIST2 whenever possible. - -See `union' for the meaning of :test, :test-not and :key. - -arguments: (LIST1 LIST2 &key (TEST #'eql) (KEY #'identity) TEST-NOT) -*/ - (int nargs, Lisp_Object *args)) -{ - Lisp_Object liszt1 = args[0], liszt2 = args[1], elt = Qnil, tail = Qnil; - Lisp_Object result = Qnil, tortoise_elt = Qnil, keyed = Qnil, swap; - Lisp_Object prev_tail = Qnil, ignore = Qnil; - Elemcount count; - Boolint test_not_unboundp = 1; - check_test_func_t check_match = NULL, check_test = NULL; - struct gcpro gcpro1, gcpro2, gcpro3, gcpro4; - - PARSE_KEYWORDS (Fnset_exclusive_or, nargs, args, 4, - (test, key, test_not, stable), NULL); - - CHECK_LIST (liszt1); - CHECK_LIST (liszt2); - - CHECK_KEY_ARGUMENT (key); - - if (NILP (liszt2)) - { - return liszt1; - } - - check_match = get_check_match_function (&test, test_not, Qnil, Qnil, key, - &test_not_unboundp, &check_test); - - tortoise_elt = tail = liszt1, count = 0; - - GCPRO4 (tail, keyed, result, tortoise_elt); - - while (CONSP (tail) ? (elt = XCAR (tail), 1) : NILP (tail) ? 0 : - (signal_malformed_list_error (liszt1), 0)) - { - keyed = KEY (key, elt); - if (NILP (list_position_cons_before (&ignore, keyed, liszt2, - check_test, test_not_unboundp, - test, key, 0, Qzero, Qnil))) - { - swap = XCDR (tail); - - if (NILP (prev_tail)) - { - liszt1 = XCDR (tail); - } - else - { - XSETCDR (prev_tail, swap); - } - - XSETCDR (tail, result); - result = tail; - tail = swap; - - /* List is definitely not circular now! */ - count = 0; - } - else - { - prev_tail = tail; - tail = XCDR (tail); - } - - if (count++ < CIRCULAR_LIST_SUSPICION_LENGTH) continue; - - if (count & 1) - { - tortoise_elt = XCDR (tortoise_elt); - } - - if (EQ (elt, tortoise_elt)) - { - signal_circular_list_error (liszt1); - } - } - - tortoise_elt = tail = liszt2, count = 0; - - while (CONSP (tail) ? (elt = XCAR (tail), 1) : NILP (tail) ? 0 : - (signal_malformed_list_error (liszt2), 0)) - { - /* Need to leave the key calculation to list_position_cons_before(). */ - if (NILP (list_position_cons_before (&ignore, elt, liszt1, - check_match, test_not_unboundp, - test, key, 1, Qzero, Qnil))) - { - swap = XCDR (tail); - XSETCDR (tail, result); - result = tail; - tail = swap; - count = 0; - } - else - { - tail = XCDR (tail); - } - - if (count++ < CIRCULAR_LIST_SUSPICION_LENGTH) continue; - - if (count & 1) - { - tortoise_elt = XCDR (tortoise_elt); - } - - if (EQ (elt, tortoise_elt)) - { - signal_circular_list_error (liszt1); - } - } - - UNGCPRO; - - return result; -} - Lisp_Object add_suffix_to_symbol (Lisp_Object symbol, const Ascbyte *ascii_string) @@ -11608,77 +3344,19 @@ return result; } -Lisp_Object Qyes_or_no_p; - void syms_of_fns (void) { - INIT_LISP_OBJECT (bit_vector); - - DEFSYMBOL (Qstring_lessp); - DEFSYMBOL (Qmerge); - DEFSYMBOL (Qfill); - DEFSYMBOL (Qidentity); - DEFSYMBOL (Qvector); - DEFSYMBOL (Qarray); - DEFSYMBOL (Qstring); - DEFSYMBOL (Qlist); - DEFSYMBOL (Qbit_vector); - defsymbol (&QsortX, "sort*"); - DEFSYMBOL (Qreduce); - DEFSYMBOL (Qreplace); - DEFSYMBOL (Qposition); - DEFSYMBOL (Qfind); - defsymbol (&QdeleteX, "delete*"); - defsymbol (&QremoveX, "remove*"); - - DEFSYMBOL (Qmapconcat); - defsymbol (&QmapcarX, "mapcar*"); - DEFSYMBOL (Qmapvector); - DEFSYMBOL (Qmapcan); - DEFSYMBOL (Qmapc); - DEFSYMBOL (Qmap); - DEFSYMBOL (Qmap_into); - DEFSYMBOL (Qsome); - DEFSYMBOL (Qevery); - DEFSYMBOL (Qmaplist); DEFSYMBOL (Qmapl); DEFSYMBOL (Qmapcon); - DEFSYMBOL (Qnsubstitute); - DEFSYMBOL (Qdelete_duplicates); - DEFSYMBOL (Qsubstitute); - DEFSYMBOL (Qmismatch); - DEFSYMBOL (Qintersection); - DEFSYMBOL (Qnintersection); - DEFSYMBOL (Qsubsetp); - DEFSYMBOL (Qcar_less_than_car); - DEFSYMBOL (Qset_difference); - DEFSYMBOL (Qnset_difference); - DEFSYMBOL (Qnunion); - - DEFKEYWORD (Q_from_end); - DEFKEYWORD (Q_initial_value); - DEFKEYWORD (Q_start1); - DEFKEYWORD (Q_start2); - DEFKEYWORD (Q_end1); - DEFKEYWORD (Q_end2); - defkeyword (&Q_if_, ":if"); - DEFKEYWORD (Q_if_not); - DEFKEYWORD (Q_test_not); - DEFKEYWORD (Q_count); - DEFKEYWORD (Q_stable); - DEFKEYWORD (Q_descend_structures); - - DEFSYMBOL (Qyes_or_no_p); + DEFSYMBOL (Qmaplist); DEFERROR_STANDARD (Qbase64_conversion_error, Qconversion_error); DEFSUBR (Fidentity); DEFSUBR (Frandom); - DEFSUBR (Flength); DEFSUBR (Fsafe_length); DEFSUBR (Flist_length); - DEFSUBR (Fcount); DEFSUBR (Fstring_equal); DEFSUBR (Fcompare_strings); DEFSUBR (Fstring_lessp); @@ -11690,34 +3368,11 @@ DEFSUBR (Fcopy_list); DEFSUBR (Fcopy_sequence); DEFSUBR (Fcopy_alist); - DEFSUBR (Fcopy_tree); - DEFSUBR (Fsubseq); DEFSUBR (Fnthcdr); DEFSUBR (Fnth); - DEFSUBR (Felt); DEFSUBR (Flast); DEFSUBR (Fbutlast); DEFSUBR (Fnbutlast); - DEFSUBR (Fmember); - DEFSUBR (Fmemq); - DEFSUBR (FmemberX); - DEFSUBR (Fadjoin); - DEFSUBR (Fassoc); - DEFSUBR (Fassq); - DEFSUBR (Frassoc); - DEFSUBR (Frassq); - - DEFSUBR (Fposition); - DEFSUBR (Ffind); - - DEFSUBR (FdeleteX); - DEFSUBR (FremoveX); - DEFSUBR (Fdelete_duplicates); - DEFSUBR (Fremove_duplicates); - DEFSUBR (Fnreverse); - DEFSUBR (Freverse); - DEFSUBR (FsortX); - DEFSUBR (Fmerge); DEFSUBR (Fplists_eq); DEFSUBR (Fplists_equal); DEFSUBR (Flax_plists_eq); @@ -11742,61 +3397,26 @@ DEFSUBR (Fobject_setplist); DEFSUBR (Fequal); DEFSUBR (Fequalp); - DEFSUBR (Ffill); #ifdef SUPPORT_CONFOUNDING_FUNCTIONS DEFSUBR (Fold_member); DEFSUBR (Fold_memq); DEFSUBR (Fold_assoc); DEFSUBR (Fold_assq); + DEFSUBR (Fold_rassq); DEFSUBR (Fold_rassoc); - DEFSUBR (Fold_rassq); DEFSUBR (Fold_delete); DEFSUBR (Fold_delq); DEFSUBR (Fold_equal); DEFSUBR (Fold_eq); #endif - DEFSUBR (FassocX); - DEFSUBR (FrassocX); - DEFSUBR (Fnconc); - DEFSUBR (FmapcarX); - DEFSUBR (Fmapvector); - DEFSUBR (Fmapcan); - DEFSUBR (Fmapc); - DEFSUBR (Fmapconcat); - DEFSUBR (Fmap); - DEFSUBR (Fmap_into); - DEFSUBR (Fsome); - DEFSUBR (Fevery); - Ffset (intern ("mapc-internal"), Qmapc); - Ffset (intern ("mapcar"), QmapcarX); DEFSUBR (Fmaplist); DEFSUBR (Fmapl); DEFSUBR (Fmapcon); - DEFSUBR (Freduce); DEFSUBR (Freplace_list); - DEFSUBR (Freplace); - DEFSUBR (Fsubsetp); - DEFSUBR (Fnsubstitute); - DEFSUBR (Fsubstitute); - DEFSUBR (Fsublis); - DEFSUBR (Fnsublis); - DEFSUBR (Fsubst); - DEFSUBR (Fnsubst); - DEFSUBR (Ftree_equal); - DEFSUBR (Fmismatch); - DEFSUBR (Fsearch); - DEFSUBR (Funion); - DEFSUBR (Fnunion); - DEFSUBR (Fintersection); - DEFSUBR (Fnintersection); - DEFSUBR (Fset_difference); - DEFSUBR (Fnset_difference); - DEFSUBR (Fset_exclusive_or); - DEFSUBR (Fnset_exclusive_or); DEFSUBR (Fload_average); DEFSUBR (Ffeaturep);
--- a/src/general-slots.h Sat Dec 03 15:55:14 2011 +0000 +++ b/src/general-slots.h Sun Dec 04 18:42:50 2011 +0000 @@ -323,3 +323,5 @@ SYMBOL (Qx); SYMBOL (Qy); SYMBOL (Qyes); +SYMBOL (Qyes_or_no_p); +
--- a/src/lisp.h Sat Dec 03 15:55:14 2011 +0000 +++ b/src/lisp.h Sun Dec 04 18:42:50 2011 +0000 @@ -5269,6 +5269,7 @@ EXFUN (Flax_plist_get, 3); EXFUN (Flax_plist_remprop, 2); MODULE_API EXFUN (Flength, 1); +EXFUN (Flist_length, 1); EXFUN (FmapcarX, MANY); EXFUN (Fmember, 2); EXFUN (Fmemq, 2);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/sequence.c Sun Dec 04 18:42:50 2011 +0000 @@ -0,0 +1,8290 @@ +/* Various functions that operate on sequences, split out from fns.c + Copyright (C) 1985, 86, 87, 93, 94, 95 Free Software Foundation, Inc. + Copyright (C) 1995, 1996, 2000, 2001, 2002, 2003, 2010 Ben Wing. + +This file is part of XEmacs. + +XEmacs is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation, either version 3 of the License, or (at your +option) any later version. + +XEmacs is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with XEmacs. If not, see <http://www.gnu.org/licenses/>. */ + +#include <config.h> +#include "lisp.h" +#include "extents.h" + +Lisp_Object Qadjoin, Qarray, QassocX, Qbit_vector, Qcar_less_than_car; +Lisp_Object QdeleteX, Qdelete_duplicates, Qevery, Qfill, Qfind, Qidentity; +Lisp_Object Qintersection, Qmap, Qmap_into, Qmapc, Qmapcan, QmapcarX; +Lisp_Object Qmapconcat, Qmapvector, Qmerge, Qmismatch, Qnintersection; +Lisp_Object Qnset_difference, Qnsubstitute, Qnunion, Qposition, QrassocX; +Lisp_Object Qreduce, QremoveX, Qreplace, Qset_difference, Qsome, QsortX; +Lisp_Object Qstring_lessp, Qsubsetp, Qsubstitute, Qvector; + +Lisp_Object Q_count, Q_descend_structures, Q_end1, Q_end2, Q_from_end; +Lisp_Object Q_if_, Q_if_not, Q_initial_value, Q_stable, Q_start1, Q_start2; +Lisp_Object Q_test_not; + +extern Fixnum max_lisp_eval_depth; +extern int lisp_eval_depth; + +Lisp_Object safe_copy_tree (Lisp_Object arg, Lisp_Object vecp, int depth); + +static DOESNT_RETURN +mapping_interaction_error (Lisp_Object func, Lisp_Object object) +{ + invalid_state_2 ("object modified while traversing it", func, object); +} + +static void +check_sequence_range (Lisp_Object sequence, Lisp_Object start, + Lisp_Object end, Lisp_Object length) +{ + Lisp_Object args[] = { Qzero, start, NILP (end) ? length : end, length }; + + if (NILP (Fleq (countof (args), args))) + { + args_out_of_range_3 (sequence, start, end); + } +} + +DEFUN ("length", Flength, 1, 1, 0, /* +Return the length of vector, bit vector, list or string SEQUENCE. +*/ + (sequence)) +{ + retry: + if (STRINGP (sequence)) + return make_fixnum (string_char_length (sequence)); + else if (CONSP (sequence)) + { + Elemcount len; + GET_EXTERNAL_LIST_LENGTH (sequence, len); + return make_fixnum (len); + } + else if (VECTORP (sequence)) + return make_fixnum (XVECTOR_LENGTH (sequence)); + else if (NILP (sequence)) + return Qzero; + else if (BIT_VECTORP (sequence)) + return make_fixnum (bit_vector_length (XBIT_VECTOR (sequence))); + else + { + check_losing_bytecode ("length", sequence); + sequence = wrong_type_argument (Qsequencep, sequence); + goto retry; + } +} + +/* Various test functions for #'member*, #'assoc* and the other functions + that take both TEST and KEY arguments. */ + +Boolint +check_eq_nokey (Lisp_Object UNUSED (test), Lisp_Object UNUSED (key), + Lisp_Object item, Lisp_Object elt) +{ + return EQ (item, elt); +} + +static Boolint +check_eq_key (Lisp_Object UNUSED (test), Lisp_Object key, Lisp_Object item, + Lisp_Object elt) +{ + elt = IGNORE_MULTIPLE_VALUES (call1 (key, elt)); + return EQ (item, elt); +} + +/* The next two are not used by #'member* and #'assoc*, since we can decide + on #'eq vs. #'equal when we have the type of ITEM. */ +static Boolint +check_eql_nokey (Lisp_Object UNUSED (test), Lisp_Object UNUSED (key), + Lisp_Object elt1, Lisp_Object elt2) +{ + return EQ (elt1, elt2) + || (NON_FIXNUM_NUMBER_P (elt1) && internal_equal (elt1, elt2, 0)); +} + +static Boolint +check_eql_key (Lisp_Object UNUSED (test), Lisp_Object key, Lisp_Object item, + Lisp_Object elt) +{ + elt = IGNORE_MULTIPLE_VALUES (call1 (key, elt)); + return EQ (item, elt) + || (NON_FIXNUM_NUMBER_P (item) && internal_equal (item, elt, 0)); +} + +static Boolint +check_equal_nokey (Lisp_Object UNUSED (test), Lisp_Object UNUSED (key), + Lisp_Object item, Lisp_Object elt) +{ + return internal_equal (item, elt, 0); +} + +static Boolint +check_equal_key (Lisp_Object UNUSED (test), Lisp_Object key, Lisp_Object item, + Lisp_Object elt) +{ + elt = IGNORE_MULTIPLE_VALUES (call1 (key, elt)); + return internal_equal (item, elt, 0); +} + +static Boolint +check_equalp_nokey (Lisp_Object UNUSED (test), Lisp_Object UNUSED (key), + Lisp_Object item, Lisp_Object elt) +{ + return internal_equalp (item, elt, 0); +} + +static Boolint +check_equalp_key (Lisp_Object UNUSED (test), Lisp_Object key, + Lisp_Object item, Lisp_Object elt) +{ + elt = IGNORE_MULTIPLE_VALUES (call1 (key, elt)); + return internal_equalp (item, elt, 0); +} + +static Boolint +check_string_match_nokey (Lisp_Object UNUSED (test), Lisp_Object UNUSED (key), + Lisp_Object item, Lisp_Object elt) +{ + return !NILP (Fstring_match (item, elt, Qnil, Qnil)); +} + +static Boolint +check_string_match_key (Lisp_Object UNUSED (test), Lisp_Object key, + Lisp_Object item, Lisp_Object elt) +{ + elt = IGNORE_MULTIPLE_VALUES (call1 (key, elt)); + return !NILP (Fstring_match (item, elt, Qnil, Qnil)); +} + +static Boolint +check_other_nokey (Lisp_Object test, Lisp_Object UNUSED (key), + Lisp_Object item, Lisp_Object elt) +{ + Lisp_Object args[] = { test, item, elt }; + struct gcpro gcpro1; + + GCPRO1 (args[0]); + gcpro1.nvars = countof (args); + item = IGNORE_MULTIPLE_VALUES (Ffuncall (countof (args), args)); + UNGCPRO; + + return !NILP (item); +} + +static Boolint +check_other_key (Lisp_Object test, Lisp_Object key, + Lisp_Object item, Lisp_Object elt) +{ + Lisp_Object args[] = { item, key, elt }; + struct gcpro gcpro1; + + GCPRO1 (args[0]); + gcpro1.nvars = countof (args); + args[2] = IGNORE_MULTIPLE_VALUES (Ffuncall (countof (args) - 1, args + 1)); + args[1] = item; + args[0] = test; + item = IGNORE_MULTIPLE_VALUES (Ffuncall (countof (args), args)); + UNGCPRO; + + return !NILP (item); +} + +static Boolint +check_if_nokey (Lisp_Object test, Lisp_Object UNUSED (key), + Lisp_Object UNUSED (item), Lisp_Object elt) +{ + elt = IGNORE_MULTIPLE_VALUES (call1 (test, elt)); + return !NILP (elt); +} + +static Boolint +check_if_key (Lisp_Object test, Lisp_Object key, + Lisp_Object UNUSED (item), Lisp_Object elt) +{ + Lisp_Object args[] = { key, elt }; + struct gcpro gcpro1; + + GCPRO1 (args[0]); + gcpro1.nvars = countof (args); + args[1] = IGNORE_MULTIPLE_VALUES (Ffuncall (countof (args), args)); + args[0] = test; + elt = IGNORE_MULTIPLE_VALUES (Ffuncall (countof (args), args)); + UNGCPRO; + + return !NILP (elt); +} + +static Boolint +check_match_eq_key (Lisp_Object UNUSED (test), Lisp_Object key, + Lisp_Object elt1, Lisp_Object elt2) +{ + Lisp_Object args[] = { key, elt1, elt2 }; + struct gcpro gcpro1; + + GCPRO1 (args[0]); + gcpro1.nvars = countof (args); + args[0] = IGNORE_MULTIPLE_VALUES (Ffuncall (2, args)); + args[1] = key; + args[1] = IGNORE_MULTIPLE_VALUES (Ffuncall (2, args + 1)); + UNGCPRO; + + return EQ (args[0], args[1]); +} + +static Boolint +check_match_eql_key (Lisp_Object UNUSED (test), Lisp_Object key, + Lisp_Object elt1, Lisp_Object elt2) +{ + Lisp_Object args[] = { key, elt1, elt2 }; + struct gcpro gcpro1; + + GCPRO1 (args[0]); + gcpro1.nvars = countof (args); + args[0] = IGNORE_MULTIPLE_VALUES (Ffuncall (2, args)); + args[1] = key; + args[1] = IGNORE_MULTIPLE_VALUES (Ffuncall (2, args + 1)); + UNGCPRO; + + return EQ (args[0], args[1]) || + (NON_FIXNUM_NUMBER_P (args[0]) && internal_equal (args[0], args[1], 0)); +} + +static Boolint +check_match_equal_key (Lisp_Object UNUSED (test), Lisp_Object key, + Lisp_Object elt1, Lisp_Object elt2) +{ + Lisp_Object args[] = { key, elt1, elt2 }; + struct gcpro gcpro1; + + GCPRO1 (args[0]); + gcpro1.nvars = countof (args); + args[0] = IGNORE_MULTIPLE_VALUES (Ffuncall (2, args)); + args[1] = key; + args[1] = IGNORE_MULTIPLE_VALUES (Ffuncall (2, args + 1)); + UNGCPRO; + + return internal_equal (args[0], args[1], 0); +} + +static Boolint +check_match_equalp_key (Lisp_Object UNUSED (test), Lisp_Object key, + Lisp_Object elt1, Lisp_Object elt2) +{ + Lisp_Object args[] = { key, elt1, elt2 }; + struct gcpro gcpro1; + + GCPRO1 (args[0]); + gcpro1.nvars = countof (args); + args[0] = IGNORE_MULTIPLE_VALUES (Ffuncall (2, args)); + args[1] = key; + args[1] = IGNORE_MULTIPLE_VALUES (Ffuncall (2, args + 1)); + UNGCPRO; + + return internal_equalp (args[0], args[1], 0); +} + +static Boolint +check_match_other_key (Lisp_Object test, Lisp_Object key, + Lisp_Object elt1, Lisp_Object elt2) +{ + Lisp_Object args[] = { key, elt1, elt2 }; + struct gcpro gcpro1; + + GCPRO1 (args[0]); + gcpro1.nvars = countof (args); + args[0] = IGNORE_MULTIPLE_VALUES (Ffuncall (2, args)); + args[1] = key; + args[2] = IGNORE_MULTIPLE_VALUES (Ffuncall (2, args + 1)); + args[1] = args[0]; + args[0] = test; + + elt1 = IGNORE_MULTIPLE_VALUES (Ffuncall (countof (args), args)); + UNGCPRO; + + return !NILP (elt1); +} + +static Boolint +check_lss_nokey (Lisp_Object UNUSED (test), Lisp_Object UNUSED (key), + Lisp_Object elt1, Lisp_Object elt2) +{ + return bytecode_arithcompare (elt1, elt2) < 0; +} + +static Boolint +check_lss_key (Lisp_Object UNUSED (test), Lisp_Object key, + Lisp_Object elt1, Lisp_Object elt2) +{ + Lisp_Object args[] = { key, elt1, elt2 }; + struct gcpro gcpro1; + + GCPRO1 (args[0]); + gcpro1.nvars = countof (args); + args[0] = IGNORE_MULTIPLE_VALUES (Ffuncall (2, args)); + args[1] = key; + args[1] = IGNORE_MULTIPLE_VALUES (Ffuncall (2, args + 1)); + UNGCPRO; + + return bytecode_arithcompare (args[0], args[1]) < 0; +} + +Boolint +check_lss_key_car (Lisp_Object UNUSED (test), Lisp_Object UNUSED (key), + Lisp_Object elt1, Lisp_Object elt2) +{ + struct gcpro gcpro1, gcpro2; + + GCPRO2 (elt1, elt2); + elt1 = CONSP (elt1) ? XCAR (elt1) : Fcar (elt1); + elt2 = CONSP (elt2) ? XCAR (elt2) : Fcar (elt2); + UNGCPRO; + + return bytecode_arithcompare (elt1, elt2) < 0; +} + +Boolint +check_string_lessp_nokey (Lisp_Object UNUSED (test), Lisp_Object UNUSED (key), + Lisp_Object elt1, Lisp_Object elt2) +{ + return !NILP (Fstring_lessp (elt1, elt2)); +} + +static Boolint +check_string_lessp_key (Lisp_Object UNUSED (test), Lisp_Object key, + Lisp_Object elt1, Lisp_Object elt2) +{ + Lisp_Object args[] = { key, elt1, elt2 }; + struct gcpro gcpro1; + + GCPRO1 (args[0]); + gcpro1.nvars = countof (args); + args[0] = IGNORE_MULTIPLE_VALUES (Ffuncall (2, args)); + args[1] = key; + args[1] = IGNORE_MULTIPLE_VALUES (Ffuncall (2, args + 1)); + UNGCPRO; + + return !NILP (Fstring_lessp (args[0], args[1])); +} + +static Boolint +check_string_lessp_key_car (Lisp_Object UNUSED (test), + Lisp_Object UNUSED (key), + Lisp_Object elt1, Lisp_Object elt2) +{ + struct gcpro gcpro1, gcpro2; + + GCPRO2 (elt1, elt2); + elt1 = CONSP (elt1) ? XCAR (elt1) : Fcar (elt1); + elt2 = CONSP (elt2) ? XCAR (elt2) : Fcar (elt2); + UNGCPRO; + + return !NILP (Fstring_lessp (elt1, elt2)); +} + +static check_test_func_t +get_check_match_function_1 (Lisp_Object item, + Lisp_Object *test_inout, Lisp_Object test_not, + Lisp_Object if_, Lisp_Object if_not, + Lisp_Object key, Boolint *test_not_unboundp_out, + check_test_func_t *test_func_out) +{ + Lisp_Object test = *test_inout; + check_test_func_t result = NULL, test_func = NULL; + Boolint force_if = 0; + + if (!NILP (if_)) + { + if (!(NILP (test) && NILP (test_not) && NILP (if_not))) + { + invalid_argument ("only one keyword among :test :test-not " + ":if :if-not allowed", if_); + } + + test = *test_inout = if_; + force_if = 1; + } + else if (!NILP (if_not)) + { + if (!(NILP (test) && NILP (test_not))) + { + invalid_argument ("only one keyword among :test :test-not " + ":if :if-not allowed", if_not); + } + + test_not = if_not; + force_if = 1; + } + + if (NILP (test)) + { + if (!NILP (test_not)) + { + test = *test_inout = test_not; + if (NULL != test_not_unboundp_out) + { + *test_not_unboundp_out = 0; + } + } + else + { + test = Qeql; + if (NULL != test_not_unboundp_out) + { + *test_not_unboundp_out = 1; + } + } + } + else if (!NILP (test_not)) + { + invalid_argument_2 ("conflicting :test and :test-not keyword arguments", + test, test_not); + } + + test = indirect_function (test, 1); + + if (NILP (key) || + EQ (indirect_function (key, 1), XSYMBOL_FUNCTION (Qidentity))) + { + key = Qidentity; + } + + if (force_if) + { + result = EQ (key, Qidentity) ? check_if_nokey : check_if_key; + + if (NULL != test_func_out) + { + *test_func_out = result; + } + + return result; + } + + if (!UNBOUNDP (item) && EQ (test, XSYMBOL_FUNCTION (Qeql))) + { + test = XSYMBOL_FUNCTION (NON_FIXNUM_NUMBER_P (item) ? Qequal : Qeq); + } + +#define FROB(known_test, eq_condition) \ + if (EQ (test, XSYMBOL_FUNCTION (Q##known_test))) do \ + { \ + if (eq_condition) \ + { \ + test = XSYMBOL_FUNCTION (Qeq); \ + goto force_eq_check; \ + } \ + \ + if (!EQ (Qidentity, key)) \ + { \ + test_func = check_##known_test##_key; \ + result = check_match_##known_test##_key; \ + } \ + else \ + { \ + result = test_func = check_##known_test##_nokey; \ + } \ + } while (0) + + FROB (eql, 0); + else if (SUBRP (test)) + { + force_eq_check: + FROB (eq, 0); + else FROB (equal, (SYMBOLP (item) || FIXNUMP (item) || CHARP (item))); + else FROB (equalp, (SYMBOLP (item))); + else if (EQ (test, XSYMBOL_FUNCTION (Qstring_match))) + { + if (EQ (Qidentity, key)) + { + test_func = result = check_string_match_nokey; + } + else + { + test_func = check_string_match_key; + result = check_other_key; + } + } + } + + if (NULL == result) + { + if (EQ (Qidentity, key)) + { + test_func = result = check_other_nokey; + } + else + { + test_func = check_other_key; + result = check_match_other_key; + } + } + + if (NULL != test_func_out) + { + *test_func_out = test_func; + } + + return result; +} +#undef FROB + +/* Given TEST, TEST_NOT, IF, IF_NOT, KEY, and ITEM, return a C function + pointer appropriate for use in deciding whether a given element of a + sequence satisfies TEST. + + Set *test_not_unboundp_out to 1 if TEST_NOT was not bound; set it to zero + if it was bound, and set *test_inout to the value it was bound to. If + TEST was not bound, leave *test_inout alone; the value is not used by + check_eq_*key() or check_equal_*key(), which are the defaults, depending + on the type of ITEM. + + The returned function takes arguments (TEST, KEY, ITEM, ELT), where ITEM + is the item being searched for and ELT is the element of the sequence + being examined. + + Error if both TEST and TEST_NOT were specified, which Common Lisp says is + undefined behaviour. */ + +static check_test_func_t +get_check_test_function (Lisp_Object item, + Lisp_Object *test_inout, Lisp_Object test_not, + Lisp_Object if_, Lisp_Object if_not, + Lisp_Object key, Boolint *test_not_unboundp_out) +{ + check_test_func_t result = NULL; + get_check_match_function_1 (item, test_inout, test_not, if_, if_not, + key, test_not_unboundp_out, &result); + return result; +} + +/* Given TEST, TEST_NOT, IF, IF_NOT and KEY, return a C function pointer + appropriate for use in deciding whether two given elements of a sequence + satisfy TEST. + + Set *test_not_unboundp_out to 1 if TEST_NOT was not bound; set it to zero + if it was bound, and set *test_inout to the value it was bound to. If + TEST was not bound, leave *test_inout alone; the value is not used by + check_eql_*key(). + + The returned function takes arguments (TEST, KEY, ELT1, ELT2), where ELT1 + and ELT2 are elements of the sequence being examined. + + The value that would be given by get_check_test_function() is returned in + *TEST_FUNC_OUT, which allows calling functions to do their own key checks + if they're processing one element at a time. + + Error if both TEST and TEST_NOT were specified, which Common Lisp says is + undefined behaviour. */ + +static check_test_func_t +get_check_match_function (Lisp_Object *test_inout, Lisp_Object test_not, + Lisp_Object if_, Lisp_Object if_not, + Lisp_Object key, Boolint *test_not_unboundp_out, + check_test_func_t *test_func_out) +{ + return get_check_match_function_1 (Qunbound, test_inout, test_not, + if_, if_not, key, + test_not_unboundp_out, test_func_out); +} + +/* Given PREDICATE and KEY, return a C function pointer appropriate for use + in deciding whether one given element of a sequence is less than + another. */ + +static check_test_func_t +get_merge_predicate (Lisp_Object predicate, Lisp_Object key) +{ + predicate = indirect_function (predicate, 1); + + if (NILP (key)) + { + key = Qidentity; + } + else + { + key = indirect_function (key, 1); + if (EQ (key, XSYMBOL_FUNCTION (Qidentity))) + { + key = Qidentity; + } + } + + if (EQ (key, Qidentity) && EQ (predicate, + XSYMBOL_FUNCTION (Qcar_less_than_car))) + { + key = XSYMBOL_FUNCTION (Qcar); + predicate = XSYMBOL_FUNCTION (Qlss); + } + + if (EQ (predicate, XSYMBOL_FUNCTION (Qlss))) + { + if (EQ (key, Qidentity)) + { + return check_lss_nokey; + } + + if (EQ (key, XSYMBOL_FUNCTION (Qcar))) + { + return check_lss_key_car; + } + + return check_lss_key; + } + + if (EQ (predicate, XSYMBOL_FUNCTION (Qstring_lessp))) + { + if (EQ (key, Qidentity)) + { + return check_string_lessp_nokey; + } + + if (EQ (key, XSYMBOL_FUNCTION (Qcar))) + { + return check_string_lessp_key_car; + } + + return check_string_lessp_key; + } + + if (EQ (key, Qidentity)) + { + return check_other_nokey; + } + + return check_match_other_key; +} + + +static Lisp_Object string_count_from_end (Lisp_Object, Lisp_Object , + check_test_func_t, Boolint, + Lisp_Object, Lisp_Object, + Lisp_Object, Lisp_Object); + +static Lisp_Object list_count_from_end (Lisp_Object, Lisp_Object, + check_test_func_t, Boolint, + Lisp_Object, Lisp_Object, + Lisp_Object, Lisp_Object); + +/* Count the number of occurrences of ITEM in SEQUENCE; if SEQUENCE is a + list, store the cons cell of which the car is the last ITEM in SEQUENCE, + at the address given by tail_out. */ + +static Lisp_Object +count_with_tail (Lisp_Object *tail_out, int nargs, Lisp_Object *args, + Lisp_Object caller) +{ + Lisp_Object item = args[0], sequence = args[1]; + Elemcount starting = 0, ending = MOST_POSITIVE_FIXNUM, encountered = 0; + Elemcount len, ii = 0, counting = MOST_POSITIVE_FIXNUM; + Boolint test_not_unboundp = 1; + check_test_func_t check_test = NULL; + + PARSE_KEYWORDS_8 (caller, nargs, args, 9, + (test, key, start, end, from_end, test_not, count, + if_, if_not), (start = Qzero), 2, 0); + + CHECK_SEQUENCE (sequence); + CHECK_NATNUM (start); + starting = BIGNUMP (start) ? 1 + MOST_POSITIVE_FIXNUM : XFIXNUM (start); + + if (!NILP (end)) + { + CHECK_NATNUM (end); + ending = BIGNUMP (end) ? 1 + MOST_POSITIVE_FIXNUM : XFIXNUM (end); + } + + if (!NILP (count)) + { + CHECK_INTEGER (count); + counting = BIGNUMP (count) ? MOST_POSITIVE_FIXNUM + 1 : XFIXNUM (count); + + /* Our callers should have filtered out non-positive COUNT. */ + assert (counting >= 0); + /* And we're not prepared to handle COUNT from any other caller at the + moment. */ + assert (EQ (caller, QremoveX)|| EQ (caller, QdeleteX)); + } + + check_test = get_check_test_function (item, &test, test_not, if_, if_not, + key, &test_not_unboundp); + + *tail_out = Qnil; + + if (CONSP (sequence)) + { + if (EQ (caller, Qcount) && !NILP (from_end) + && (!EQ (key, Qnil) || + check_test == check_other_nokey || check_test == check_if_nokey)) + { + /* #'count, #'count-if, and #'count-if-not are documented to have + a given traversal order if :from-end t is passed in, even + though forward traversal of the sequence has the same result + and is algorithmically less expensive for lists and strings. + This order isn't necessary for other callers, though. */ + return list_count_from_end (item, sequence, check_test, + test_not_unboundp, test, key, + start, end); + } + + /* If COUNT is non-nil and FROM-END is t, we can give the tail + containing the last match, since that's what #'remove* is + interested in (a zero or negative COUNT won't ever reach + count_with_tail(), our callers will return immediately on seeing + it). */ + if (!NILP (count) && !NILP (from_end)) + { + counting = MOST_POSITIVE_FIXNUM; + } + + { + GC_EXTERNAL_LIST_LOOP_3 (elt, sequence, tail) + { + if (!(ii < ending)) + { + break; + } + + if (starting <= ii && + check_test (test, key, item, elt) == test_not_unboundp) + { + encountered++; + *tail_out = tail; + + if (encountered == counting) + { + break; + } + } + + ii++; + } + END_GC_EXTERNAL_LIST_LOOP (elt); + } + + if ((ii < starting || (ii < ending && !NILP (end))) && + encountered != counting) + { + check_sequence_range (args[1], start, end, Flength (args[1])); + } + } + else if (STRINGP (sequence)) + { + Ibyte *startp = XSTRING_DATA (sequence), *cursor = startp; + Bytecount byte_len = XSTRING_LENGTH (sequence), cursor_offset = 0; + Lisp_Object character = Qnil; + + if (EQ (caller, Qcount) && !NILP (from_end) + && (!EQ (key, Qnil) || + check_test == check_other_nokey || check_test == check_if_nokey)) + { + /* See comment above in the list code. */ + return string_count_from_end (item, sequence, + check_test, test_not_unboundp, + test, key, start, end); + } + + while (cursor_offset < byte_len && ii < ending && encountered < counting) + { + if (ii >= starting) + { + character = make_char (itext_ichar (cursor)); + + if (check_test (test, key, item, character) + == test_not_unboundp) + { + encountered++; + } + + startp = XSTRING_DATA (sequence); + cursor = startp + cursor_offset; + if (byte_len != XSTRING_LENGTH (sequence) + || !valid_ibyteptr_p (cursor)) + { + mapping_interaction_error (caller, sequence); + } + } + + INC_IBYTEPTR (cursor); + cursor_offset = cursor - startp; + ii++; + } + + if (ii < starting || (ii < ending && !NILP (end))) + { + check_sequence_range (sequence, start, end, Flength (sequence)); + } + } + else + { + Lisp_Object object = Qnil; + + len = XFIXNUM (Flength (sequence)); + check_sequence_range (sequence, start, end, make_fixnum (len)); + + ending = min (ending, len); + if (0 == len) + { + /* Catches the case where we have nil. */ + return make_integer (encountered); + } + + if (NILP (from_end)) + { + for (ii = starting; ii < ending && encountered < counting; ii++) + { + object = Faref (sequence, make_fixnum (ii)); + if (check_test (test, key, item, object) == test_not_unboundp) + { + encountered++; + } + } + } + else + { + for (ii = ending - 1; ii >= starting && encountered < counting; ii--) + { + object = Faref (sequence, make_fixnum (ii)); + if (check_test (test, key, item, object) == test_not_unboundp) + { + encountered++; + } + } + } + } + + return make_integer (encountered); +} + +static Lisp_Object +list_count_from_end (Lisp_Object item, Lisp_Object sequence, + check_test_func_t check_test, Boolint test_not_unboundp, + Lisp_Object test, Lisp_Object key, + Lisp_Object start, Lisp_Object end) +{ + Elemcount length = XFIXNUM (Flength (sequence)), ii = 0, starting = XFIXNUM (start); + Elemcount ending = NILP (end) ? length : XFIXNUM (end), encountered = 0; + Lisp_Object *storage; + struct gcpro gcpro1; + + check_sequence_range (sequence, start, end, make_integer (length)); + + storage = alloca_array (Lisp_Object, ending - starting); + + { + EXTERNAL_LIST_LOOP_2 (elt, sequence) + { + if (starting <= ii && ii < ending) + { + storage[ii - starting] = elt; + } + ii++; + } + } + + GCPRO1 (storage[0]); + gcpro1.nvars = ending - starting; + + for (ii = ending - 1; ii >= starting; ii--) + { + if (check_test (test, key, item, storage[ii - starting]) + == test_not_unboundp) + { + encountered++; + } + } + + UNGCPRO; + + return make_integer (encountered); +} + +static Lisp_Object +string_count_from_end (Lisp_Object item, Lisp_Object sequence, + check_test_func_t check_test, Boolint test_not_unboundp, + Lisp_Object test, Lisp_Object key, + Lisp_Object start, Lisp_Object end) +{ + Elemcount length = string_char_length (sequence), ii = 0; + Elemcount starting = XFIXNUM (start), ending = NILP (end) ? length : XFIXNUM (end); + Elemcount encountered = 0; + Ibyte *cursor = XSTRING_DATA (sequence); + Ibyte *endp = cursor + XSTRING_LENGTH (sequence); + Ichar *storage; + + check_sequence_range (sequence, start, end, make_integer (length)); + + storage = alloca_array (Ichar, ending - starting); + + while (cursor < endp && ii < ending) + { + if (starting <= ii && ii < ending) + { + storage [ii - starting] = itext_ichar (cursor); + } + + ii++; + INC_IBYTEPTR (cursor); + } + + for (ii = ending - 1; ii >= starting; ii--) + { + if (check_test (test, key, item, make_char (storage [ii - starting])) + == test_not_unboundp) + { + encountered++; + } + } + + return make_integer (encountered); +} + +DEFUN ("count", Fcount, 2, MANY, 0, /* +Count the number of occurrences of ITEM in SEQUENCE. + +See `remove*' for the meaning of the keywords. + +arguments: (ITEM SEQUENCE &key (TEST #'eql) (KEY #'identity) (START 0) END FROM-END TEST-NOT) +*/ + (int nargs, Lisp_Object *args)) +{ + Lisp_Object tail = Qnil; + + /* count_with_tail() accepts more keywords than we do, check those we've + been given. */ + PARSE_KEYWORDS (Fcount, nargs, args, 8, + (test, test_not, if_, if_not, key, start, end, from_end), + NULL); + + return count_with_tail (&tail, nargs, args, Qcount); +} + +DEFUN ("subseq", Fsubseq, 2, 3, 0, /* +Return the subsequence of SEQUENCE starting at START and ending before END. +END may be omitted; then the subsequence runs to the end of SEQUENCE. + +If START or END is negative, it counts from the end, in contravention of +Common Lisp. +The returned subsequence is always of the same type as SEQUENCE. +If SEQUENCE is a string, relevant parts of the string-extent-data +are copied to the new string. + +See also `substring-no-properties', which only operates on strings, and does +not copy extent data. +*/ + (sequence, start, end)) +{ + Elemcount len, ss, ee = MOST_POSITIVE_FIXNUM, ii; + Lisp_Object result = Qnil; + + CHECK_SEQUENCE (sequence); + CHECK_FIXNUM (start); + ss = XFIXNUM (start); + + if (!NILP (end)) + { + CHECK_FIXNUM (end); + ee = XFIXNUM (end); + } + + if (STRINGP (sequence)) + { + Bytecount bstart, blen; + + get_string_range_char (sequence, start, end, &ss, &ee, + GB_HISTORICAL_STRING_BEHAVIOR); + bstart = string_index_char_to_byte (sequence, ss); + blen = string_offset_char_to_byte_len (sequence, bstart, ee - ss); + + result = make_string (XSTRING_DATA (sequence) + bstart, blen); + /* Copy any applicable extent information into the new string. */ + copy_string_extents (result, sequence, 0, bstart, blen); + } + else if (CONSP (sequence)) + { + Lisp_Object result_tail, saved = sequence; + + if (ss < 0 || ee < 0) + { + len = XFIXNUM (Flength (sequence)); + if (ss < 0) + { + ss = len + ss; + start = make_integer (ss); + } + + if (ee < 0) + { + ee = len + ee; + end = make_integer (ee); + } + else + { + ee = min (ee, len); + } + } + + if (0 != ss) + { + sequence = Fnthcdr (make_fixnum (ss), sequence); + } + + ii = ss + 1; + + if (ss < ee && !NILP (sequence)) + { + result = result_tail = Fcons (Fcar (sequence), Qnil); + sequence = Fcdr (sequence); + + { + EXTERNAL_LIST_LOOP_2 (elt, sequence) + { + if (!(ii < ee)) + { + break; + } + + XSETCDR (result_tail, Fcons (elt, Qnil)); + result_tail = XCDR (result_tail); + ii++; + } + } + } + + if (NILP (result) || (ii < ee && !NILP (end))) + { + /* We were handed a cons, which definitely has elements. nil + result means either ss >= ee or SEQUENCE was nil after the + nthcdr; in both cases that means START and END were incorrectly + specified for this sequence. ii < ee with a non-nil end means + the user handed us a bogus end value. */ + check_sequence_range (saved, start, end, Flength (saved)); + } + } + else + { + len = XFIXNUM (Flength (sequence)); + if (ss < 0) + { + ss = len + ss; + start = make_integer (ss); + } + + if (ee < 0) + { + ee = len + ee; + end = make_integer (ee); + } + else + { + ee = min (len, ee); + } + + check_sequence_range (sequence, start, end, make_fixnum (len)); + + if (VECTORP (sequence)) + { + result = Fvector (ee - ss, XVECTOR_DATA (sequence) + ss); + } + else if (BIT_VECTORP (sequence)) + { + result = make_bit_vector (ee - ss, Qzero); + + for (ii = ss; ii < ee; ii++) + { + set_bit_vector_bit (XBIT_VECTOR (result), ii - ss, + bit_vector_bit (XBIT_VECTOR (sequence), ii)); + } + } + else if (NILP (sequence)) + { + DO_NOTHING; + } + else + { + /* Won't happen, since CHECK_SEQUENCE didn't error. */ + ABORT (); + } + } + + return result; +} + +DEFUN ("elt", Felt, 2, 2, 0, /* +Return element of SEQUENCE at index N. +*/ + (sequence, n)) +{ + /* This function can GC */ + retry: + CHECK_FIXNUM_COERCE_CHAR (n); /* yuck! */ + if (LISTP (sequence)) + { + Lisp_Object tem = Fnthcdr (n, sequence); + /* #### Utterly, completely, fucking disgusting. + * #### The whole point of "elt" is that it operates on + * #### sequences, and does error- (bounds-) checking. + */ + if (CONSP (tem)) + return XCAR (tem); + else +#if 1 + /* This is The Way It Has Always Been. */ + return Qnil; +#else + /* This is The Way Mly and Cltl2 say It Should Be. */ + args_out_of_range (sequence, n); +#endif + } + else if (STRINGP (sequence) || + VECTORP (sequence) || + BIT_VECTORP (sequence)) + return Faref (sequence, n); + else + { + check_losing_bytecode ("elt", sequence); + sequence = wrong_type_argument (Qsequencep, sequence); + goto retry; + } +} + +DEFUN ("copy-tree", Fcopy_tree, 1, 2, 0, /* +Return a copy of a list and substructures. +The argument is copied, and any lists contained within it are copied +recursively. Circularities and shared substructures are not preserved. +Second arg VECP causes vectors to be copied, too. Strings and bit vectors +are not copied. +*/ + (arg, vecp)) +{ + return safe_copy_tree (arg, vecp, 0); +} + +Lisp_Object +safe_copy_tree (Lisp_Object arg, Lisp_Object vecp, int depth) +{ + if (depth + lisp_eval_depth > max_lisp_eval_depth) + stack_overflow ("Stack overflow in copy-tree", arg); + + if (CONSP (arg)) + { + Lisp_Object rest; + rest = arg = Fcopy_sequence (arg); + while (CONSP (rest)) + { + Lisp_Object elt = XCAR (rest); + QUIT; + if (CONSP (elt) || VECTORP (elt)) + XCAR (rest) = safe_copy_tree (elt, vecp, depth + 1); + if (VECTORP (XCDR (rest))) /* hack for (a b . [c d]) */ + XCDR (rest) = safe_copy_tree (XCDR (rest), vecp, depth +1); + rest = XCDR (rest); + } + } + else if (VECTORP (arg) && ! NILP (vecp)) + { + int i = XVECTOR_LENGTH (arg); + int j; + arg = Fcopy_sequence (arg); + for (j = 0; j < i; j++) + { + Lisp_Object elt = XVECTOR_DATA (arg) [j]; + QUIT; + if (CONSP (elt) || VECTORP (elt)) + XVECTOR_DATA (arg) [j] = safe_copy_tree (elt, vecp, depth + 1); + } + } + return arg; +} + +DEFUN ("member", Fmember, 2, 2, 0, /* +Return non-nil if ELT is an element of LIST. Comparison done with `equal'. +The value is actually the tail of LIST whose car is ELT. +*/ + (elt, list)) +{ + EXTERNAL_LIST_LOOP_3 (list_elt, list, tail) + { + if (internal_equal (elt, list_elt, 0)) + return tail; + } + return Qnil; +} + +DEFUN ("memq", Fmemq, 2, 2, 0, /* +Return non-nil if ELT is an element of LIST. Comparison done with `eq'. +The value is actually the tail of LIST whose car is ELT. +*/ + (elt, list)) +{ + EXTERNAL_LIST_LOOP_3 (list_elt, list, tail) + { + if (EQ_WITH_EBOLA_NOTICE (elt, list_elt)) + return tail; + } + return Qnil; +} + +Lisp_Object +memq_no_quit (Lisp_Object elt, Lisp_Object list) +{ + LIST_LOOP_3 (list_elt, list, tail) + { + if (EQ_WITH_EBOLA_NOTICE (elt, list_elt)) + return tail; + } + return Qnil; +} + +/* Return the first index of ITEM in LIST. In CONS_OUT, return the cons cell + before that containing the element. If the element is in the first cons + cell, return Qnil in CONS_OUT. TEST, KEY, START, END are as in + #'remove*; CHECK_TEST and TEST_NOT_UNBOUNDP should have been initialized + with get_check_match_function() or get_check_test_function(). A non-zero + REVERSE_TEST_ORDER means call TEST with the element from LIST as its + first argument and ITEM as its second. Error if LIST is ill-formed, or + circular. */ +static Lisp_Object +list_position_cons_before (Lisp_Object *cons_out, + Lisp_Object item, Lisp_Object list, + check_test_func_t check_test, + Boolint test_not_unboundp, + Lisp_Object test, Lisp_Object key, + Boolint reverse_test_order, + Lisp_Object start, Lisp_Object end) +{ + struct gcpro gcpro1; + Lisp_Object tail_before = Qnil; + Elemcount ii = 0, starting = XFIXNUM (start); + Elemcount ending = NILP (end) ? MOST_POSITIVE_FIXNUM : XFIXNUM (end); + + GCPRO1 (tail_before); + + if (check_test == check_eq_nokey) + { + /* TEST is #'eq, no need to call any C functions, and the test order + won't be visible. */ + EXTERNAL_LIST_LOOP_3 (elt, list, tail) + { + if (starting <= ii && ii < ending && + EQ (item, elt) == test_not_unboundp) + { + *cons_out = tail_before; + RETURN_UNGCPRO (make_integer (ii)); + } + else + { + if (ii >= ending) + { + break; + } + } + ii++; + tail_before = tail; + } + } + else + { + GC_EXTERNAL_LIST_LOOP_3 (elt, list, tail) + { + if (starting <= ii && ii < ending && + (reverse_test_order ? + check_test (test, key, elt, item) : + check_test (test, key, item, elt)) == test_not_unboundp) + { + *cons_out = tail_before; + XUNGCPRO (elt); + UNGCPRO; + return make_integer (ii); + } + else + { + if (ii >= ending) + { + break; + } + } + ii++; + tail_before = tail; + } + END_GC_EXTERNAL_LIST_LOOP (elt); + } + + RETURN_UNGCPRO (Qnil); +} + +DEFUN ("member*", FmemberX, 2, MANY, 0, /* +Return the first sublist of LIST with car ITEM, or nil if no such sublist. + +The keyword :test specifies a two-argument function that is used to compare +ITEM with elements in LIST; if omitted, it defaults to `eql'. + +The keyword :test-not is similar, but specifies a negated function. That +is, ITEM is considered equal to an element in LIST if the given function +returns nil. Common Lisp deprecates :test-not, and if both are specified, +XEmacs signals an error. + +:key specifies a one-argument function that transforms elements of LIST into +\"comparison keys\" before the test predicate is applied. For example, +if :key is #'car, then ITEM is compared with the car of elements from LIST. +The :key function, however, is not applied to ITEM, and does not affect the +elements in the returned list, which are taken directly from the elements in +LIST. + +arguments: (ITEM LIST &key (TEST #'eql) TEST-NOT (KEY #'identity)) +*/ + (int nargs, Lisp_Object *args)) +{ + Lisp_Object item = args[0], list = args[1], result = Qnil, position0; + Boolint test_not_unboundp = 1; + check_test_func_t check_test = NULL; + + PARSE_KEYWORDS (FmemberX, nargs, args, 5, (test, if_not, if_, test_not, key), + NULL); + check_test = get_check_test_function (item, &test, test_not, if_, if_not, + key, &test_not_unboundp); + position0 + = list_position_cons_before (&result, item, list, check_test, + test_not_unboundp, test, key, 0, Qzero, Qnil); + + return CONSP (result) ? XCDR (result) : ZEROP (position0) ? list : Qnil; +} + +/* This macro might eventually find a better home than here. */ + +#define CHECK_KEY_ARGUMENT(key) \ + do { \ + if (NILP (key)) \ + { \ + key = Qidentity; \ + } \ + \ + if (!EQ (key, Qidentity)) \ + { \ + key = indirect_function (key, 1); \ + if (EQ (key, XSYMBOL_FUNCTION (Qidentity))) \ + { \ + key = Qidentity; \ + } \ + } \ + } while (0) + +#define KEY(key, item) (EQ (Qidentity, key) ? item : \ + IGNORE_MULTIPLE_VALUES (call1 (key, item))) + +DEFUN ("adjoin", Fadjoin, 2, MANY, 0, /* +Return ITEM consed onto the front of LIST, if not already in LIST. + +Otherwise, return LIST unmodified. + +See `member*' for the meaning of the keywords. + +arguments: (ITEM LIST &key (TEST #'eql) (KEY #'identity) TEST-NOT) +*/ + (int nargs, Lisp_Object *args)) +{ + Lisp_Object item = args[0], list = args[1], keyed = Qnil, ignore = Qnil; + struct gcpro gcpro1; + Boolint test_not_unboundp = 1; + check_test_func_t check_test = NULL; + + PARSE_KEYWORDS (Fadjoin, nargs, args, 3, (test, key, test_not), + NULL); + + CHECK_KEY_ARGUMENT (key); + + keyed = KEY (key, item); + + GCPRO1 (keyed); + check_test = get_check_test_function (keyed, &test, test_not, Qnil, Qnil, + key, &test_not_unboundp); + if (NILP (list_position_cons_before (&ignore, keyed, list, check_test, + test_not_unboundp, test, key, 0, Qzero, + Qnil))) + { + RETURN_UNGCPRO (Fcons (item, list)); + } + + RETURN_UNGCPRO (list); +} + +DEFUN ("assoc", Fassoc, 2, 2, 0, /* +Return non-nil if KEY is `equal' to the car of an element of ALIST. +The value is actually the element of ALIST whose car equals KEY. +*/ + (key, alist)) +{ + /* This function can GC. */ + EXTERNAL_ALIST_LOOP_4 (elt, elt_car, elt_cdr, alist) + { + if (internal_equal (key, elt_car, 0)) + return elt; + } + return Qnil; +} + +Lisp_Object +assoc_no_quit (Lisp_Object key, Lisp_Object alist) +{ + int speccount = specpdl_depth (); + specbind (Qinhibit_quit, Qt); + return unbind_to_1 (speccount, Fassoc (key, alist)); +} + +DEFUN ("assq", Fassq, 2, 2, 0, /* +Return non-nil if KEY is `eq' to the car of an element of ALIST. +The value is actually the element of ALIST whose car is KEY. +Elements of ALIST that are not conses are ignored. +*/ + (key, alist)) +{ + EXTERNAL_ALIST_LOOP_4 (elt, elt_car, elt_cdr, alist) + { + if (EQ_WITH_EBOLA_NOTICE (key, elt_car)) + return elt; + } + return Qnil; +} + +/* Like Fassq but never report an error and do not allow quits. + Use only on lists known never to be circular. */ + +Lisp_Object +assq_no_quit (Lisp_Object key, Lisp_Object alist) +{ + /* This cannot GC. */ + LIST_LOOP_2 (elt, alist) + { + Lisp_Object elt_car = XCAR (elt); + if (EQ_WITH_EBOLA_NOTICE (key, elt_car)) + return elt; + } + return Qnil; +} + +DEFUN ("assoc*", FassocX, 2, MANY, 0, /* +Find the first item whose car matches ITEM in ALIST. + +See `member*' for the meaning of :test, :test-not and :key. + +arguments: (ITEM ALIST &key (TEST #'eql) (KEY #'identity) TEST-NOT) +*/ + (int nargs, Lisp_Object *args)) +{ + Lisp_Object item = args[0], alist = args[1]; + Boolint test_not_unboundp = 1; + check_test_func_t check_test = NULL; + + PARSE_KEYWORDS (FassocX, nargs, args, 5, (test, if_, if_not, test_not, key), + NULL); + + check_test = get_check_test_function (item, &test, test_not, if_, if_not, + key, &test_not_unboundp); + + if (check_test == check_eq_nokey) + { + /* TEST is #'eq, no need to call any C functions. */ + EXTERNAL_ALIST_LOOP_4 (elt, elt_car, elt_cdr, alist) + { + if (EQ (item, elt_car) == test_not_unboundp) + { + return elt; + } + } + } + else + { + GC_EXTERNAL_LIST_LOOP_2 (elt, alist) + { + if (CONSP (elt) && + check_test (test, key, item, XCAR (elt)) == test_not_unboundp) + { + XUNGCPRO (elt); + return elt; + } + } + END_GC_EXTERNAL_LIST_LOOP (elt); + } + + return Qnil; +} + +DEFUN ("rassoc", Frassoc, 2, 2, 0, /* +Return non-nil if VALUE is `equal' to the cdr of an element of ALIST. +The value is actually the element of ALIST whose cdr equals VALUE. +*/ + (value, alist)) +{ + EXTERNAL_ALIST_LOOP_4 (elt, elt_car, elt_cdr, alist) + { + if (internal_equal (value, elt_cdr, 0)) + return elt; + } + return Qnil; +} + +DEFUN ("rassq", Frassq, 2, 2, 0, /* +Return non-nil if VALUE is `eq' to the cdr of an element of ALIST. +The value is actually the element of ALIST whose cdr is VALUE. +*/ + (value, alist)) +{ + EXTERNAL_ALIST_LOOP_4 (elt, elt_car, elt_cdr, alist) + { + if (EQ_WITH_EBOLA_NOTICE (value, elt_cdr)) + return elt; + } + return Qnil; +} + +/* Like Frassq, but caller must ensure that ALIST is properly + nil-terminated and ebola-free. */ +Lisp_Object +rassq_no_quit (Lisp_Object value, Lisp_Object alist) +{ + LIST_LOOP_2 (elt, alist) + { + Lisp_Object elt_cdr = XCDR (elt); + if (EQ_WITH_EBOLA_NOTICE (value, elt_cdr)) + return elt; + } + return Qnil; +} + +DEFUN ("rassoc*", FrassocX, 2, MANY, 0, /* +Find the first item whose cdr matches ITEM in ALIST. + +See `member*' for the meaning of :test, :test-not and :key. + +arguments: (ITEM ALIST &key (TEST #'eql) (KEY #'identity) TEST-NOT) +*/ + (int nargs, Lisp_Object *args)) +{ + Lisp_Object item = args[0], alist = args[1]; + Boolint test_not_unboundp = 1; + check_test_func_t check_test = NULL; + + PARSE_KEYWORDS (FrassocX, nargs, args, 5, (test, if_, if_not, test_not, key), + NULL); + + check_test = get_check_test_function (item, &test, test_not, if_, if_not, + key, &test_not_unboundp); + + if (check_test == check_eq_nokey) + { + /* TEST is #'eq, no need to call any C functions. */ + EXTERNAL_ALIST_LOOP_4 (elt, elt_car, elt_cdr, alist) + { + if (EQ (item, elt_cdr) == test_not_unboundp) + { + return elt; + } + } + } + else + { + GC_EXTERNAL_LIST_LOOP_2 (elt, alist) + { + if (CONSP (elt) && + check_test (test, key, item, XCDR (elt)) == test_not_unboundp) + { + XUNGCPRO (elt); + return elt; + } + } + END_GC_EXTERNAL_LIST_LOOP (elt); + } + + return Qnil; +} + +/* This is the implementation of both #'find and #'position. */ +static Lisp_Object +position (Lisp_Object *object_out, Lisp_Object item, Lisp_Object sequence, + check_test_func_t check_test, Boolint test_not_unboundp, + Lisp_Object test, Lisp_Object key, Lisp_Object start, Lisp_Object end, + Lisp_Object from_end, Lisp_Object default_, Lisp_Object caller) +{ + Lisp_Object result = Qnil; + Elemcount starting = 0, ending = MOST_POSITIVE_FIXNUM, len, ii = 0; + + CHECK_SEQUENCE (sequence); + CHECK_NATNUM (start); + starting = FIXNUMP (start) ? XFIXNUM (start) : 1 + MOST_POSITIVE_FIXNUM; + + if (!NILP (end)) + { + CHECK_NATNUM (end); + ending = FIXNUMP (end) ? XFIXNUM (end) : 1 + MOST_POSITIVE_FIXNUM; + } + + *object_out = default_; + + if (CONSP (sequence)) + { + if (!(starting < ending)) + { + check_sequence_range (sequence, start, end, Flength (sequence)); + /* starting could be equal to ending, in which case nil is what + we want to return. */ + return Qnil; + } + + { + GC_EXTERNAL_LIST_LOOP_2 (elt, sequence) + { + if (starting <= ii && ii < ending + && check_test (test, key, item, elt) == test_not_unboundp) + { + result = make_integer (ii); + *object_out = elt; + + if (NILP (from_end)) + { + XUNGCPRO (elt); + return result; + } + } + else if (ii == ending) + { + break; + } + + ii++; + } + END_GC_EXTERNAL_LIST_LOOP (elt); + } + + if (ii < starting || (ii < ending && !NILP (end))) + { + check_sequence_range (sequence, start, end, Flength (sequence)); + } + } + else if (STRINGP (sequence)) + { + Ibyte *startp = XSTRING_DATA (sequence), *cursor = startp; + Bytecount byte_len = XSTRING_LENGTH (sequence), cursor_offset = 0; + Lisp_Object character = Qnil; + + while (cursor_offset < byte_len && ii < ending) + { + if (ii >= starting) + { + character = make_char (itext_ichar (cursor)); + + if (check_test (test, key, item, character) == test_not_unboundp) + { + result = make_integer (ii); + *object_out = character; + + if (NILP (from_end)) + { + return result; + } + } + + startp = XSTRING_DATA (sequence); + cursor = startp + cursor_offset; + if (byte_len != XSTRING_LENGTH (sequence) + || !valid_ibyteptr_p (cursor)) + { + mapping_interaction_error (caller, sequence); + } + } + + INC_IBYTEPTR (cursor); + cursor_offset = cursor - startp; + ii++; + } + + if (ii < starting || (ii < ending && !NILP (end))) + { + check_sequence_range (sequence, start, end, Flength (sequence)); + } + } + else + { + Lisp_Object object = Qnil; + len = XFIXNUM (Flength (sequence)); + check_sequence_range (sequence, start, end, make_fixnum (len)); + + ending = min (ending, len); + if (0 == len) + { + /* Catches the case where we have nil. */ + return result; + } + + if (NILP (from_end)) + { + for (ii = starting; ii < ending; ii++) + { + object = Faref (sequence, make_fixnum (ii)); + if (check_test (test, key, item, object) == test_not_unboundp) + { + result = make_integer (ii); + *object_out = object; + return result; + } + } + } + else + { + for (ii = ending - 1; ii >= starting; ii--) + { + object = Faref (sequence, make_fixnum (ii)); + if (check_test (test, key, item, object) == test_not_unboundp) + { + result = make_integer (ii); + *object_out = object; + return result; + } + } + } + } + + return result; +} + +DEFUN ("position", Fposition, 2, MANY, 0, /* +Return the index of the first occurrence of ITEM in SEQUENCE. + +Return nil if not found. See `remove*' for the meaning of the keywords. + +arguments: (ITEM SEQUENCE &key (TEST #'eql) (KEY #'identity) (START 0) (END (length SEQUENCE)) FROM-END TEST-NOT) +*/ + (int nargs, Lisp_Object *args)) +{ + Lisp_Object object = Qnil, item = args[0], sequence = args[1]; + Boolint test_not_unboundp = 1; + check_test_func_t check_test = NULL; + + PARSE_KEYWORDS (Fposition, nargs, args, 8, + (test, if_, test_not, if_not, key, start, end, from_end), + (start = Qzero)); + + check_test = get_check_test_function (item, &test, test_not, if_, if_not, + key, &test_not_unboundp); + + return position (&object, item, sequence, check_test, test_not_unboundp, + test, key, start, end, from_end, Qnil, Qposition); +} + +DEFUN ("find", Ffind, 2, MANY, 0, /* +Find the first occurrence of ITEM in SEQUENCE. + +Return the matching ITEM, or nil if not found. See `remove*' for the +meaning of the keywords. + +The keyword :default, not specified by Common Lisp, designates an object to +return instead of nil if ITEM is not found. + +arguments: (ITEM SEQUENCE &key (TEST #'eql) (KEY #'identity) (START 0) (END (length SEQUENCE)) DEFAULT FROM-END TEST-NOT) +*/ + (int nargs, Lisp_Object *args)) +{ + Lisp_Object object = Qnil, item = args[0], sequence = args[1]; + Boolint test_not_unboundp = 1; + check_test_func_t check_test = NULL; + + PARSE_KEYWORDS (Ffind, nargs, args, 9, + (test, if_, test_not, if_not, key, start, end, from_end, + default_), + (start = Qzero)); + + check_test = get_check_test_function (item, &test, test_not, if_, if_not, + key, &test_not_unboundp); + + position (&object, item, sequence, check_test, test_not_unboundp, + test, key, start, end, from_end, default_, Qposition); + + return object; +} + +/* Like #'delq, but caller must ensure that LIST is properly + nil-terminated and ebola-free. */ + +Lisp_Object +delq_no_quit (Lisp_Object elt, Lisp_Object list) +{ + LIST_LOOP_DELETE_IF (list_elt, list, + (EQ_WITH_EBOLA_NOTICE (elt, list_elt))); + return list; +} + +/* Be VERY careful with this. This is like delq_no_quit() but + also calls free_cons() on the removed conses. You must be SURE + that no pointers to the freed conses remain around (e.g. + someone else is pointing to part of the list). This function + is useful on internal lists that are used frequently and where + the actual list doesn't escape beyond known code bounds. */ + +Lisp_Object +delq_no_quit_and_free_cons (Lisp_Object elt, Lisp_Object list) +{ + REGISTER Lisp_Object tail = list; + REGISTER Lisp_Object prev = Qnil; + + while (!NILP (tail)) + { + REGISTER Lisp_Object tem = XCAR (tail); + if (EQ (elt, tem)) + { + Lisp_Object cons_to_free = tail; + if (NILP (prev)) + list = XCDR (tail); + else + XCDR (prev) = XCDR (tail); + tail = XCDR (tail); + free_cons (cons_to_free); + } + else + { + prev = tail; + tail = XCDR (tail); + } + } + return list; +} + +DEFUN ("delete*", FdeleteX, 2, MANY, 0, /* +Remove all occurrences of ITEM in SEQUENCE, destructively. + +If SEQUENCE is a non-nil list, this modifies the list directly. A non-list +SEQUENCE will not be destructively modified, rather, if ITEM occurs in it, a +new SEQUENCE of the same type without ITEM will be returned. + +See `remove*' for a non-destructive alternative, and for explanation of the +keyword arguments. + +arguments: (ITEM SEQUENCE &key (TEST #'eql) (KEY #'identity) (START 0) (END (length SEQUENCE)) FROM-END COUNT TEST-NOT) +*/ + (int nargs, Lisp_Object *args)) +{ + Lisp_Object item = args[0], sequence = args[1]; + Elemcount starting = 0, ending = MOST_POSITIVE_FIXNUM, counting = MOST_POSITIVE_FIXNUM; + Elemcount len, ii = 0, encountered = 0, presenting = 0; + Boolint test_not_unboundp = 1; + check_test_func_t check_test = NULL; + + PARSE_KEYWORDS (FdeleteX, nargs, args, 9, + (test, if_not, if_, test_not, key, start, end, from_end, + count), (start = Qzero, count = Qunbound)); + + CHECK_SEQUENCE (sequence); + CHECK_NATNUM (start); + starting = BIGNUMP (start) ? 1 + MOST_POSITIVE_FIXNUM : XFIXNUM (start); + + if (!NILP (end)) + { + CHECK_NATNUM (end); + ending = BIGNUMP (end) ? 1 + MOST_POSITIVE_FIXNUM : XFIXNUM (end); + } + + if (!UNBOUNDP (count)) + { + if (!NILP (count)) + { + CHECK_INTEGER (count); + if (FIXNUMP (count)) + { + counting = XFIXNUM (count); + } +#ifdef HAVE_BIGNUM + else + { + counting = bignum_sign (XBIGNUM_DATA (count)) > 0 ? + 1 + MOST_POSITIVE_FIXNUM : MOST_NEGATIVE_FIXNUM - 1; + } +#endif + + if (counting < 1) + { + return sequence; + } + + if (!NILP (from_end)) + { + /* Sigh, this is inelegant. Force count_with_tail () to ignore + the count keyword, so we get the actual number of matching + elements, and can start removing from the beginning for the + from-end case. */ + for (ii = XSUBR (GET_DEFUN_LISP_OBJECT (FdeleteX))->min_args; + ii < nargs; ii += 2) + { + if (EQ (args[ii], Q_count)) + { + args[ii + 1] = Qnil; + break; + } + } + ii = 0; + } + } + } + + check_test = get_check_test_function (item, &test, test_not, if_, if_not, + key, &test_not_unboundp); + + if (CONSP (sequence)) + { + Lisp_Object prev_tail_list_elt = Qnil, ignore = Qnil; + Elemcount list_len = 0, deleted = 0; + struct gcpro gcpro1; + + if (!NILP (count) && !NILP (from_end)) + { + /* Both COUNT and FROM-END were specified; we need to traverse the + list twice. */ + Lisp_Object present = count_with_tail (&ignore, nargs, args, + QdeleteX); + + if (ZEROP (present)) + { + return sequence; + } + + presenting = XFIXNUM (present); + + /* If there are fewer items in the list than we have permission to + delete, we don't need to differentiate between the :from-end + nil and :from-end t cases. Otherwise, presenting is the number + of matching items we need to ignore before we start to + delete. */ + presenting = presenting <= counting ? 0 : presenting - counting; + } + + GCPRO1 (prev_tail_list_elt); + ii = -1; + + { + GC_EXTERNAL_LIST_LOOP_4 (list_elt, sequence, tail, list_len) + { + ii++; + + if (starting <= ii && ii < ending && + (check_test (test, key, item, list_elt) == test_not_unboundp) + && (presenting ? encountered++ >= presenting + : encountered++ < counting)) + { + if (NILP (prev_tail_list_elt)) + { + sequence = XCDR (tail); + } + else + { + XSETCDR (prev_tail_list_elt, XCDR (tail)); + } + + /* Keep tortoise from ever passing hare. */ + list_len = 0; + deleted++; + } + else + { + prev_tail_list_elt = tail; + if (ii >= ending || (!presenting && encountered > counting)) + { + break; + } + } + } + END_GC_EXTERNAL_LIST_LOOP (list_elt); + } + + UNGCPRO; + + if ((ii < starting || (ii < ending && !NILP (end))) && + !(presenting ? encountered == presenting : encountered == counting)) + { + check_sequence_range (args[1], start, end, + make_fixnum (deleted + XFIXNUM (Flength (args[1])))); + } + + return sequence; + } + else if (STRINGP (sequence)) + { + Ibyte *staging = alloca_ibytes (XSTRING_LENGTH (sequence)); + Ibyte *staging_cursor = staging, *startp = XSTRING_DATA (sequence); + Ibyte *cursor = startp; + Bytecount cursor_offset = 0, byte_len = XSTRING_LENGTH (sequence); + Lisp_Object character, result = sequence; + + if (!NILP (count) && !NILP (from_end)) + { + Lisp_Object present = count_with_tail (&character, nargs, args, + QdeleteX); + + if (ZEROP (present)) + { + return sequence; + } + + presenting = XFIXNUM (present); + + /* If there are fewer items in the list than we have permission to + delete, we don't need to differentiate between the :from-end + nil and :from-end t cases. Otherwise, presenting is the number + of matching items we need to ignore before we start to + delete. */ + presenting = presenting <= counting ? 0 : presenting - counting; + } + + ii = 0; + while (cursor_offset < byte_len) + { + if (ii >= starting && ii < ending) + { + character = make_char (itext_ichar (cursor)); + + if ((check_test (test, key, item, character) + == test_not_unboundp) + && (presenting ? encountered++ >= presenting : + encountered++ < counting)) + { + DO_NOTHING; + } + else + { + staging_cursor + += set_itext_ichar (staging_cursor, XCHAR (character)); + } + + startp = XSTRING_DATA (sequence); + cursor = startp + cursor_offset; + if (byte_len != XSTRING_LENGTH (sequence) + || !valid_ibyteptr_p (cursor)) + { + mapping_interaction_error (QdeleteX, sequence); + } + } + else + { + staging_cursor += itext_copy_ichar (cursor, staging_cursor); + } + + INC_IBYTEPTR (cursor); + cursor_offset = cursor - startp; + ii++; + } + + if (ii < starting || (ii < ending && !NILP (end))) + { + check_sequence_range (sequence, start, end, Flength (sequence)); + } + + if (0 != encountered) + { + result = make_string (staging, staging_cursor - staging); + copy_string_extents (result, sequence, 0, 0, + staging_cursor - staging); + sequence = result; + } + + return sequence; + } + else + { + Lisp_Object position0 = Qnil, object = Qnil; + Lisp_Object *staging = NULL, *staging_cursor, *staging_limit; + Elemcount positioning; + + len = XFIXNUM (Flength (sequence)); + + check_sequence_range (sequence, start, end, make_fixnum (len)); + + position0 = position (&object, item, sequence, check_test, + test_not_unboundp, test, key, start, end, + from_end, Qnil, QdeleteX); + if (NILP (position0)) + { + return sequence; + } + + ending = min (ending, len); + positioning = XFIXNUM (position0); + encountered = 1; + + if (NILP (from_end)) + { + staging = alloca_array (Lisp_Object, len - 1); + staging_cursor = staging; + + ii = 0; + while (ii < positioning) + { + *staging_cursor++ = Faref (sequence, make_fixnum (ii)); + ii++; + } + + ii = positioning + 1; + while (ii < ending) + { + object = Faref (sequence, make_fixnum (ii)); + if (encountered < counting + && (check_test (test, key, item, object) + == test_not_unboundp)) + { + encountered++; + } + else + { + *staging_cursor++ = object; + } + ii++; + } + + while (ii < len) + { + *staging_cursor++ = Faref (sequence, make_fixnum (ii)); + ii++; + } + } + else + { + staging = alloca_array (Lisp_Object, len - 1); + staging_cursor = staging_limit = staging + len - 1; + + ii = len - 1; + while (ii > positioning) + { + *--staging_cursor = Faref (sequence, make_fixnum (ii)); + ii--; + } + + ii = positioning - 1; + while (ii >= starting) + { + object = Faref (sequence, make_fixnum (ii)); + if (encountered < counting + && (check_test (test, key, item, object) == + test_not_unboundp)) + { + encountered++; + } + else + { + *--staging_cursor = object; + } + + ii--; + } + + while (ii >= 0) + { + *--staging_cursor = Faref (sequence, make_fixnum (ii)); + ii--; + } + + staging = staging_cursor; + staging_cursor = staging_limit; + } + + if (VECTORP (sequence)) + { + return Fvector (staging_cursor - staging, staging); + } + else if (BIT_VECTORP (sequence)) + { + return Fbit_vector (staging_cursor - staging, staging); + } + + /* A nil sequence will have given us a nil #'position, + above. */ + ABORT (); + + return Qnil; + } +} + +DEFUN ("remove*", FremoveX, 2, MANY, 0, /* +Remove all occurrences of ITEM in SEQUENCE, non-destructively. + +If SEQUENCE is a list, `remove*' makes a copy if that is necessary to avoid +corrupting the original SEQUENCE. + +The keywords :test and :test-not specify two-argument test and negated-test +predicates, respectively; :test defaults to `eql'. :key specifies a +one-argument function that transforms elements of SEQUENCE into \"comparison +keys\" before the test predicate is applied. See `member*' for more +information on these keywords. + +:start and :end, if given, specify indices of a subsequence of SEQUENCE to +be processed. Indices are 0-based and processing involves the subsequence +starting at the index given by :start and ending just before the index given +by :end. + +:count, if given, limits the number of items removed to the number +specified. :from-end, if given, causes processing to proceed starting from +the end instead of the beginning; in this case, this matters only if :count +is given. + +arguments: (ITEM SEQUENCE &key (TEST #'eql) (KEY #'identity) (START 0) (END (length SEQUENCE)) FROM-END COUNT TEST-NOT) +*/ + (int nargs, Lisp_Object *args)) +{ + Lisp_Object item = args[0], sequence = args[1], matched_count = Qnil, + tail = Qnil; + Elemcount starting = 0, ending = MOST_POSITIVE_FIXNUM, counting = MOST_POSITIVE_FIXNUM; + Elemcount ii = 0, encountered = 0, presenting = 0; + Boolint test_not_unboundp = 1; + check_test_func_t check_test = NULL; + + PARSE_KEYWORDS (FremoveX, nargs, args, 9, + (test, if_not, if_, test_not, key, start, end, from_end, + count), (start = Qzero)); + + if (!CONSP (sequence)) + { + return FdeleteX (nargs, args); + } + + CHECK_NATNUM (start); + starting = BIGNUMP (start) ? 1 + MOST_POSITIVE_FIXNUM : XFIXNUM (start); + + if (!NILP (end)) + { + CHECK_NATNUM (end); + ending = BIGNUMP (end) ? 1 + MOST_POSITIVE_FIXNUM : XFIXNUM (end); + } + + if (!NILP (count)) + { + CHECK_INTEGER (count); + if (FIXNUMP (count)) + { + counting = XFIXNUM (count); + } +#ifdef HAVE_BIGNUM + else + { + counting = bignum_sign (XBIGNUM_DATA (count)) > 0 ? + 1 + MOST_POSITIVE_FIXNUM : -1 + MOST_NEGATIVE_FIXNUM; + } +#endif + + if (counting <= 0) + { + return sequence; + } + + if (!NILP (from_end)) + { + /* Sigh, this is inelegant. Force count_with_tail () to ignore the + count keyword, so we get the actual number of matching + elements, and can start removing from the beginning for the + from-end case. */ + for (ii = XSUBR (GET_DEFUN_LISP_OBJECT (FremoveX))->min_args; + ii < nargs; ii += 2) + { + if (EQ (args[ii], Q_count)) + { + args[ii + 1] = Qnil; + break; + } + } + ii = 0; + } + } + + check_test = get_check_test_function (item, &test, test_not, if_, if_not, + key, &test_not_unboundp); + + matched_count = count_with_tail (&tail, nargs, args, QremoveX); + + if (!ZEROP (matched_count)) + { + Lisp_Object result = Qnil, result_tail = Qnil; + struct gcpro gcpro1, gcpro2; + + if (!NILP (count) && !NILP (from_end)) + { + presenting = XFIXNUM (matched_count); + + /* If there are fewer matching elements in the list than we have + permission to delete, we don't need to differentiate between + the :from-end nil and :from-end t cases. Otherwise, presenting + is the number of matching items we need to ignore before we + start to delete. */ + presenting = presenting <= counting ? 0 : presenting - counting; + } + + GCPRO2 (result, tail); + { + GC_EXTERNAL_LIST_LOOP_3 (elt, sequence, tailing) + { + if (EQ (tail, tailing)) + { + XUNGCPRO (elt); + UNGCPRO; + + if (NILP (result)) + { + return XCDR (tail); + } + + XSETCDR (result_tail, XCDR (tail)); + return result; + } + else if (starting <= ii && ii < ending && + (check_test (test, key, item, elt) == test_not_unboundp) + && (presenting ? encountered++ >= presenting + : encountered++ < counting)) + { + DO_NOTHING; + } + else if (NILP (result)) + { + result = result_tail = Fcons (elt, Qnil); + } + else + { + XSETCDR (result_tail, Fcons (elt, Qnil)); + result_tail = XCDR (result_tail); + } + + if (ii == ending) + { + break; + } + + ii++; + } + END_GC_EXTERNAL_LIST_LOOP (elt); + } + UNGCPRO; + + if (ii < starting || (ii < ending && !NILP (end))) + { + check_sequence_range (args[0], start, end, Flength (args[0])); + } + + return result; + } + + return sequence; +} + +Lisp_Object +remassoc_no_quit (Lisp_Object key, Lisp_Object alist) +{ + LIST_LOOP_DELETE_IF (elt, alist, + (CONSP (elt) && + internal_equal (key, XCAR (elt), 0))); + return alist; +} + +/* no quit, no errors; be careful */ + +Lisp_Object +remassq_no_quit (Lisp_Object key, Lisp_Object alist) +{ + LIST_LOOP_DELETE_IF (elt, alist, + (CONSP (elt) && + EQ_WITH_EBOLA_NOTICE (key, XCAR (elt)))); + return alist; +} + +/* Like Fremrassq, fast and unsafe; be careful */ +Lisp_Object +remrassq_no_quit (Lisp_Object value, Lisp_Object alist) +{ + LIST_LOOP_DELETE_IF (elt, alist, + (CONSP (elt) && + EQ_WITH_EBOLA_NOTICE (value, XCDR (elt)))); + return alist; +} + +/* Remove duplicate elements between START and END from LIST, a non-nil + list; if COPY is zero, do so destructively. Items to delete are selected + according to the algorithm used when :from-end t is passed to + #'delete-duplicates. Error if LIST is ill-formed or circular. + + TEST and KEY are as in #'remove*; CHECK_TEST and TEST_NOT_UNBOUNDP should + reflect them, having been initialised with get_check_match_function() or + get_check_test_function(). */ +static Lisp_Object +list_delete_duplicates_from_end (Lisp_Object list, + check_test_func_t check_test, + Boolint test_not_unboundp, + Lisp_Object test, Lisp_Object key, + Lisp_Object start, + Lisp_Object end, Boolint copy) +{ + Lisp_Object checking = Qnil, result = list; + Lisp_Object keyed, positioned, position_cons = Qnil, result_tail; + Elemcount len = XFIXNUM (Flength (list)), pos, starting = XFIXNUM (start); + Elemcount ending = (NILP (end) ? len : XFIXNUM (end)), greatest_pos_seen = -1; + Elemcount ii = 0; + struct gcpro gcpro1; + + /* We can't delete (or remove) as we go, because that breaks START and + END. We could if END were nil, and that would change an ON(N + 2) + algorithm to an ON^2 algorithm. Here and now it doesn't matter, though, + #'delete-duplicates is relatively expensive no matter what. */ + struct Lisp_Bit_Vector *deleting + = (Lisp_Bit_Vector *) ALLOCA (sizeof (struct Lisp_Bit_Vector) + + (sizeof (long) + * (BIT_VECTOR_LONG_STORAGE (len) + - 1))); + + check_sequence_range (list, start, end, make_integer (len)); + + deleting->size = len; + memset (&(deleting->bits), 0, + sizeof (long) * BIT_VECTOR_LONG_STORAGE (len)); + + GCPRO1 (keyed); + + { + GC_EXTERNAL_LIST_LOOP_3 (elt, list, tail) + { + if (!(starting <= ii && ii <= ending) || bit_vector_bit (deleting, ii)) + { + ii++; + continue; + } + + keyed = KEY (key, elt); + checking = XCDR (tail); + pos = ii + 1; + + while (!NILP ((positioned = list_position_cons_before + (&position_cons, keyed, checking, check_test, + test_not_unboundp, test, key, 0, + make_fixnum (max (starting - pos, 0)), + make_fixnum (ending - pos))))) + { + pos = XFIXNUM (positioned) + pos; + set_bit_vector_bit (deleting, pos, 1); + greatest_pos_seen = max (greatest_pos_seen, pos); + checking = NILP (position_cons) ? + XCDR (checking) : XCDR (XCDR (position_cons)); + pos += 1; + } + ii++; + } + END_GC_EXTERNAL_LIST_LOOP (elt); + } + + UNGCPRO; + + ii = 0; + + if (greatest_pos_seen > -1) + { + if (copy) + { + result = result_tail = Fcons (XCAR (list), Qnil); + list = XCDR (list); + ii = 1; + + { + EXTERNAL_LIST_LOOP_3 (elt, list, tail) + { + if (ii == greatest_pos_seen) + { + XSETCDR (result_tail, XCDR (tail)); + break; + } + else if (!bit_vector_bit (deleting, ii)) + { + XSETCDR (result_tail, Fcons (elt, Qnil)); + result_tail = XCDR (result_tail); + } + ii++; + } + } + } + else + { + EXTERNAL_LIST_LOOP_DELETE_IF (elt, list, + bit_vector_bit (deleting, ii++)); + } + } + + return result; +} + +DEFUN ("delete-duplicates", Fdelete_duplicates, 1, MANY, 0, /* +Remove all duplicate elements from SEQUENCE, destructively. + +If SEQUENCE is a list and has duplicates, modify and return it. Note that +SEQUENCE may start with an element to be deleted; because of this, if +modifying a variable, be sure to write `(setq VARIABLE (delete-duplicates +VARIABLE))' to be certain to have a list without duplicate elements. + +If SEQUENCE is an array and has duplicates, return a newly-allocated array +of the same type comprising all unique elements of SEQUENCE. + +If there are no duplicate elements in SEQUENCE, return it unmodified. + +See `remove*' for the meaning of the keywords. See `remove-duplicates' for +a non-destructive version of this function. + +arguments: (SEQUENCE &key (TEST #'eql) (KEY #'identity) (START 0) END FROM-END TEST-NOT) +*/ + (int nargs, Lisp_Object *args)) +{ + Lisp_Object sequence = args[0], keyed = Qnil; + Lisp_Object positioned = Qnil, ignore = Qnil; + Elemcount starting = 0, ending = MOST_POSITIVE_FIXNUM, len, ii = 0, jj = 0; + Boolint test_not_unboundp = 1; + check_test_func_t check_test = NULL; + struct gcpro gcpro1, gcpro2; + + PARSE_KEYWORDS (Fdelete_duplicates, nargs, args, 6, + (test, key, test_not, start, end, from_end), + (start = Qzero)); + + CHECK_SEQUENCE (sequence); + CHECK_NATNUM (start); + starting = BIGNUMP (start) ? 1 + MOST_POSITIVE_FIXNUM : XFIXNUM (start); + + if (!NILP (end)) + { + CHECK_NATNUM (end); + ending = BIGNUMP (end) ? 1 + MOST_POSITIVE_FIXNUM : XFIXNUM (end); + } + + CHECK_KEY_ARGUMENT (key); + + get_check_match_function (&test, test_not, Qnil, Qnil, key, + &test_not_unboundp, &check_test); + + if (CONSP (sequence)) + { + if (NILP (from_end)) + { + Lisp_Object prev_tail = Qnil; + Elemcount deleted = 0; + + GCPRO2 (keyed, prev_tail); + + { + GC_EXTERNAL_LIST_LOOP_3 (elt, sequence, tail) + { + if (starting <= ii && ii < ending) + { + keyed = KEY (key, elt); + positioned + = list_position_cons_before (&ignore, keyed, + XCDR (tail), check_test, + test_not_unboundp, test, key, + 0, make_fixnum (max (starting + - (ii + 1), + 0)), + make_fixnum (ending + - (ii + 1))); + if (!NILP (positioned)) + { + sequence = XCDR (tail); + deleted++; + } + else + { + break; + } + } + else + { + break; + } + + ii++; + } + END_GC_EXTERNAL_LIST_LOOP (elt); + } + { + GC_EXTERNAL_LIST_LOOP_3 (elt, sequence, tail) + { + if (!(starting <= ii && ii <= ending)) + { + prev_tail = tail; + ii++; + continue; + } + + keyed = KEY (key, elt); + positioned + = list_position_cons_before (&ignore, keyed, XCDR (tail), + check_test, test_not_unboundp, + test, key, 0, + make_fixnum (max (starting + - (ii + 1), 0)), + make_fixnum (ending - (ii + 1))); + if (!NILP (positioned)) + { + /* We know this isn't the first iteration of the loop, + because we advanced above to the point where we have at + least one non-duplicate entry at the head of the + list. */ + XSETCDR (prev_tail, XCDR (tail)); + len = 0; + deleted++; + } + else + { + prev_tail = tail; + if (ii >= ending) + { + break; + } + } + + ii++; + } + END_GC_EXTERNAL_LIST_LOOP (elt); + } + + UNGCPRO; + + if ((ii < starting || (ii < ending && !NILP (end)))) + { + check_sequence_range (args[0], start, end, + make_fixnum (deleted + + XFIXNUM (Flength (args[0])))); + } + } + else + { + sequence = list_delete_duplicates_from_end (sequence, check_test, + test_not_unboundp, + test, key, start, end, + 0); + } + } + else if (STRINGP (sequence)) + { + Lisp_Object elt = Qnil; + + if (EQ (Qidentity, key)) + { + /* We know all the elements will be characters; set check_test to + reflect that. This isn't useful if KEY is not #'identity, since + it may return non-characters for the elements. */ + check_test = get_check_test_function (make_char ('a'), + &test, test_not, + Qnil, Qnil, key, + &test_not_unboundp); + } + + if (NILP (from_end)) + { + Bytecount byte_len = XSTRING_LENGTH (sequence), cursor_offset = 0; + Ibyte *staging = alloca_ibytes (byte_len), *staging_cursor = staging; + Ibyte *cursor = XSTRING_DATA (sequence), *startp = cursor; + Elemcount deleted = 0; + + GCPRO1 (elt); + + while (cursor_offset < byte_len) + { + if (starting <= ii && ii < ending) + { + Ibyte *cursor0 = cursor; + Bytecount cursor0_offset; + Boolint delete_this = 0; + + elt = KEY (key, make_char (itext_ichar (cursor))); + INC_IBYTEPTR (cursor0); + cursor0_offset = cursor0 - startp; + + for (jj = ii + 1; jj < ending && cursor0_offset < byte_len; + jj++) + { + if (check_test (test, key, elt, + make_char (itext_ichar (cursor0))) + == test_not_unboundp) + { + delete_this = 1; + deleted++; + break; + } + + startp = XSTRING_DATA (sequence); + cursor0 = startp + cursor0_offset; + if (byte_len != XSTRING_LENGTH (sequence) + || !valid_ibyteptr_p (cursor0)) + { + mapping_interaction_error (Qdelete_duplicates, + sequence); + } + + INC_IBYTEPTR (cursor0); + cursor0_offset = cursor0 - startp; + } + + startp = XSTRING_DATA (sequence); + cursor = startp + cursor_offset; + + if (byte_len != XSTRING_LENGTH (sequence) + || !valid_ibyteptr_p (cursor)) + { + mapping_interaction_error (Qdelete_duplicates, sequence); + } + + if (!delete_this) + { + staging_cursor + += itext_copy_ichar (cursor, staging_cursor); + + } + } + else + { + staging_cursor += itext_copy_ichar (cursor, staging_cursor); + } + + INC_IBYTEPTR (cursor); + cursor_offset = cursor - startp; + ii++; + } + + UNGCPRO; + + if (ii < starting || (ii < ending && !NILP (end))) + { + check_sequence_range (sequence, start, end, Flength (sequence)); + } + + if (0 != deleted) + { + sequence = make_string (staging, staging_cursor - staging); + } + } + else + { + Elemcount deleted = 0; + Ibyte *staging = alloca_ibytes ((len = string_char_length (sequence)) + * MAX_ICHAR_LEN); + Ibyte *staging_cursor = staging, *startp = XSTRING_DATA (sequence); + Ibyte *endp = startp + XSTRING_LENGTH (sequence); + struct Lisp_Bit_Vector *deleting + = (Lisp_Bit_Vector *) ALLOCA (sizeof (struct Lisp_Bit_Vector) + + (sizeof (long) + * (BIT_VECTOR_LONG_STORAGE (len) + - 1))); + + check_sequence_range (sequence, start, end, make_integer (len)); + + /* For the from_end t case; transform contents to an array with + elements addressable in constant time, use the same algorithm + as for vectors. */ + deleting->size = len; + memset (&(deleting->bits), 0, + sizeof (long) * BIT_VECTOR_LONG_STORAGE (len)); + + while (startp < endp) + { + itext_copy_ichar (startp, staging + (ii * MAX_ICHAR_LEN)); + INC_IBYTEPTR (startp); + ii++; + } + + GCPRO1 (elt); + + ending = min (ending, len); + + for (ii = ending - 1; ii >= starting; ii--) + { + elt = KEY (key, make_char (itext_ichar (staging + + (ii * MAX_ICHAR_LEN)))); + for (jj = ii - 1; jj >= starting; jj--) + { + if (check_test (test, key, elt, + make_char (itext_ichar + (staging + (jj * MAX_ICHAR_LEN)))) + == test_not_unboundp) + { + set_bit_vector_bit (deleting, ii, 1); + deleted++; + break; + } + } + } + + UNGCPRO; + + if (0 != deleted) + { + startp = XSTRING_DATA (sequence); + + for (ii = 0; ii < len; ii++) + { + if (!bit_vector_bit (deleting, ii)) + { + staging_cursor + += itext_copy_ichar (startp, staging_cursor); + } + + INC_IBYTEPTR (startp); + } + + sequence = make_string (staging, staging_cursor - staging); + } + } + } + else if (VECTORP (sequence)) + { + Elemcount deleted = 0; + Lisp_Object *content = XVECTOR_DATA (sequence); + struct Lisp_Bit_Vector *deleting; + Lisp_Object elt = Qnil; + + len = XVECTOR_LENGTH (sequence); + check_sequence_range (sequence, start, end, make_integer (len)); + + deleting = (Lisp_Bit_Vector *) ALLOCA (sizeof (struct Lisp_Bit_Vector) + + (sizeof (long) + * (BIT_VECTOR_LONG_STORAGE (len) + - 1))); + deleting->size = len; + memset (&(deleting->bits), 0, + sizeof (long) * BIT_VECTOR_LONG_STORAGE (len)); + + GCPRO1 (elt); + + ending = min (ending, len); + + if (NILP (from_end)) + { + for (ii = starting; ii < ending; ii++) + { + elt = KEY (key, content[ii]); + + for (jj = ii + 1; jj < ending; jj++) + { + if (check_test (test, key, elt, content[jj]) + == test_not_unboundp) + { + set_bit_vector_bit (deleting, ii, 1); + deleted++; + break; + } + } + } + } + else + { + for (ii = ending - 1; ii >= starting; ii--) + { + elt = KEY (key, content[ii]); + + for (jj = ii - 1; jj >= starting; jj--) + { + if (check_test (test, key, elt, content[jj]) + == test_not_unboundp) + { + set_bit_vector_bit (deleting, ii, 1); + deleted++; + break; + } + } + } + } + + UNGCPRO; + + if (deleted) + { + Lisp_Object res = make_vector (len - deleted, Qnil), + *res_content = XVECTOR_DATA (res); + + for (ii = jj = 0; ii < len; ii++) + { + if (!bit_vector_bit (deleting, ii)) + { + res_content[jj++] = content[ii]; + } + } + + sequence = res; + } + } + else if (BIT_VECTORP (sequence)) + { + Lisp_Bit_Vector *bv = XBIT_VECTOR (sequence); + Elemcount deleted = 0; + /* I'm a little irritated at this. Basically, the only reasonable + thing delete-duplicates should do if handed a bit vector is return + something of maximum length two and minimum length 0 (because + that's the possible number of distinct elements if EQ is regarded + as identity, which it should be). But to support arbitrary TEST + and KEY arguments, which may be non-deterministic from our + perspective, we need the same algorithm as for vectors. */ + struct Lisp_Bit_Vector *deleting; + Lisp_Object elt = Qnil; + + len = bit_vector_length (bv); + + if (EQ (Qidentity, key)) + { + /* We know all the elements will be bits; set check_test to + reflect that. This isn't useful if KEY is not #'identity, since + it may return non-bits for the elements. */ + check_test = get_check_test_function (Qzero, &test, test_not, + Qnil, Qnil, key, + &test_not_unboundp); + } + + check_sequence_range (sequence, start, end, make_integer (len)); + + deleting = (Lisp_Bit_Vector *) ALLOCA (sizeof (struct Lisp_Bit_Vector) + + (sizeof (long) + * (BIT_VECTOR_LONG_STORAGE (len) + - 1))); + deleting->size = len; + memset (&(deleting->bits), 0, + sizeof (long) * BIT_VECTOR_LONG_STORAGE (len)); + + ending = min (ending, len); + + GCPRO1 (elt); + + if (NILP (from_end)) + { + for (ii = starting; ii < ending; ii++) + { + elt = KEY (key, make_fixnum (bit_vector_bit (bv, ii))); + + for (jj = ii + 1; jj < ending; jj++) + { + if (check_test (test, key, elt, + make_fixnum (bit_vector_bit (bv, jj))) + == test_not_unboundp) + { + set_bit_vector_bit (deleting, ii, 1); + deleted++; + break; + } + } + } + } + else + { + for (ii = ending - 1; ii >= starting; ii--) + { + elt = KEY (key, make_fixnum (bit_vector_bit (bv, ii))); + + for (jj = ii - 1; jj >= starting; jj--) + { + if (check_test (test, key, elt, + make_fixnum (bit_vector_bit (bv, jj))) + == test_not_unboundp) + { + set_bit_vector_bit (deleting, ii, 1); + deleted++; + break; + } + } + } + } + + UNGCPRO; + + if (deleted) + { + Lisp_Object res = make_bit_vector (len - deleted, Qzero); + Lisp_Bit_Vector *resbv = XBIT_VECTOR (res); + + for (ii = jj = 0; ii < len; ii++) + { + if (!bit_vector_bit (deleting, ii)) + { + set_bit_vector_bit (resbv, jj++, bit_vector_bit (bv, ii)); + } + } + + sequence = res; + } + } + + return sequence; +} + +DEFUN ("remove-duplicates", Fremove_duplicates, 1, MANY, 0, /* +Remove duplicate elements from SEQUENCE, non-destructively. + +If there are no duplicate elements in SEQUENCE, return it unmodified; +otherwise, return a new object. If SEQUENCE is a list, the new object may +share list structure with SEQUENCE. + +See `remove*' for the meaning of the keywords. + +arguments: (SEQUENCE &key (TEST #'eql) (KEY #'identity) (START 0) END FROM-END TEST-NOT) +*/ + (int nargs, Lisp_Object *args)) +{ + Lisp_Object sequence = args[0], keyed, positioned = Qnil; + Lisp_Object result = sequence, result_tail = result, cursor = Qnil; + Lisp_Object cons_with_shared_tail = Qnil; + Elemcount starting = 0, ending = MOST_POSITIVE_FIXNUM, ii = 0; + Boolint test_not_unboundp = 1; + check_test_func_t check_test = NULL; + struct gcpro gcpro1, gcpro2; + + PARSE_KEYWORDS (Fremove_duplicates, nargs, args, 6, + (test, key, test_not, start, end, from_end), + (start = Qzero)); + + CHECK_SEQUENCE (sequence); + + if (!CONSP (sequence)) + { + return Fdelete_duplicates (nargs, args); + } + + CHECK_NATNUM (start); + starting = BIGNUMP (start) ? 1 + MOST_POSITIVE_FIXNUM : XFIXNUM (start); + + if (!NILP (end)) + { + CHECK_NATNUM (end); + ending = BIGNUMP (end) ? 1 + MOST_POSITIVE_FIXNUM : XFIXNUM (end); + } + + if (NILP (key)) + { + key = Qidentity; + } + + get_check_match_function (&test, test_not, Qnil, Qnil, key, + &test_not_unboundp, &check_test); + + if (NILP (from_end)) + { + Lisp_Object ignore = Qnil; + + GCPRO2 (keyed, result); + + { + GC_EXTERNAL_LIST_LOOP_3 (elt, sequence, tail) + { + if (starting <= ii && ii <= ending) + { + keyed = KEY (key, elt); + positioned + = list_position_cons_before (&ignore, keyed, XCDR (tail), + check_test, test_not_unboundp, + test, key, 0, + make_fixnum (max (starting + - (ii + 1), 0)), + make_fixnum (ending - (ii + 1))); + if (!NILP (positioned)) + { + sequence = result = result_tail = XCDR (tail); + } + else + { + break; + } + } + else + { + break; + } + + ii++; + } + END_GC_EXTERNAL_LIST_LOOP (elt); + } + + { + GC_EXTERNAL_LIST_LOOP_3 (elt, sequence, tail) + { + if (!(starting <= ii && ii <= ending)) + { + ii++; + continue; + } + + /* For this algorithm, each time we encounter an object to be + removed, copy the output list from the tail beyond the last + removed cons to this one. Otherwise, the tail of the output list + is shared with the input list, which is OK. */ + + keyed = KEY (key, elt); + positioned + = list_position_cons_before (&ignore, keyed, XCDR (tail), + check_test, test_not_unboundp, + test, key, 0, + make_fixnum (max (starting - (ii + 1), + 0)), + make_fixnum (ending - (ii + 1))); + if (!NILP (positioned)) + { + if (EQ (result, sequence)) + { + result = cons_with_shared_tail + = Fcons (XCAR (sequence), XCDR (sequence)); + } + + result_tail = cons_with_shared_tail; + cursor = XCDR (cons_with_shared_tail); + + while (!EQ (cursor, tail) && !NILP (cursor)) + { + XSETCDR (result_tail, Fcons (XCAR (cursor), Qnil)); + result_tail = XCDR (result_tail); + cursor = XCDR (cursor); + } + + XSETCDR (result_tail, XCDR (tail)); + cons_with_shared_tail = result_tail; + } + + ii++; + } + END_GC_EXTERNAL_LIST_LOOP (elt); + } + + UNGCPRO; + + if ((ii < starting || (ii < ending && !NILP (end)))) + { + check_sequence_range (args[0], start, end, Flength (args[0])); + } + } + else + { + result = list_delete_duplicates_from_end (sequence, check_test, + test_not_unboundp, test, key, + start, end, 1); + } + + return result; +} +#undef KEY + +DEFUN ("nreverse", Fnreverse, 1, 1, 0, /* +Reverse SEQUENCE, destructively. + +Return the beginning of the reversed sequence, which will be a distinct Lisp +object if SEQUENCE is a list with length greater than one. See also +`reverse', the non-destructive version of this function. +*/ + (sequence)) +{ + CHECK_SEQUENCE (sequence); + + if (CONSP (sequence)) + { + struct gcpro gcpro1, gcpro2; + Lisp_Object prev = Qnil; + Lisp_Object tail = sequence; + + /* We gcpro our args; see `nconc' */ + GCPRO2 (prev, tail); + while (!NILP (tail)) + { + REGISTER Lisp_Object next; + CONCHECK_CONS (tail); + next = XCDR (tail); + XCDR (tail) = prev; + prev = tail; + tail = next; + } + UNGCPRO; + return prev; + } + else if (VECTORP (sequence)) + { + Elemcount length = XVECTOR_LENGTH (sequence), ii = length; + Elemcount half = length / 2; + Lisp_Object swap = Qnil; + CHECK_LISP_WRITEABLE (sequence); + + while (ii > half) + { + swap = XVECTOR_DATA (sequence) [length - ii]; + XVECTOR_DATA (sequence) [length - ii] + = XVECTOR_DATA (sequence) [ii - 1]; + XVECTOR_DATA (sequence) [ii - 1] = swap; + --ii; + } + } + else if (STRINGP (sequence)) + { + Elemcount length = XSTRING_LENGTH (sequence); + Ibyte *staging = alloca_ibytes (length), *staging_end = staging + length; + Ibyte *cursor = XSTRING_DATA (sequence), *endp = cursor + length; + + CHECK_LISP_WRITEABLE (sequence); + while (cursor < endp) + { + staging_end -= itext_ichar_len (cursor); + itext_copy_ichar (cursor, staging_end); + INC_IBYTEPTR (cursor); + } + + assert (staging == staging_end); + + memcpy (XSTRING_DATA (sequence), staging, length); + init_string_ascii_begin (sequence); + bump_string_modiff (sequence); + sledgehammer_check_ascii_begin (sequence); + } + else if (BIT_VECTORP (sequence)) + { + Lisp_Bit_Vector *bv = XBIT_VECTOR (sequence); + Elemcount length = bit_vector_length (bv), ii = length; + Elemcount half = length / 2; + int swap = 0; + + CHECK_LISP_WRITEABLE (sequence); + while (ii > half) + { + swap = bit_vector_bit (bv, length - ii); + set_bit_vector_bit (bv, length - ii, bit_vector_bit (bv, ii - 1)); + set_bit_vector_bit (bv, ii - 1, swap); + --ii; + } + } + else + { + assert (NILP (sequence)); + } + + return sequence; +} + +DEFUN ("reverse", Freverse, 1, 1, 0, /* +Reverse SEQUENCE, copying. Return the reversed sequence. +See also the function `nreverse', which is used more often. +*/ + (sequence)) +{ + Lisp_Object result = Qnil; + + CHECK_SEQUENCE (sequence); + + if (CONSP (sequence)) + { + EXTERNAL_LIST_LOOP_2 (elt, sequence) + { + result = Fcons (elt, result); + } + } + else if (VECTORP (sequence)) + { + Elemcount length = XVECTOR_LENGTH (sequence), ii = length; + Lisp_Object *staging = alloca_array (Lisp_Object, length); + + while (ii > 0) + { + staging[length - ii] = XVECTOR_DATA (sequence) [ii - 1]; + --ii; + } + + result = Fvector (length, staging); + } + else if (STRINGP (sequence)) + { + Elemcount length = XSTRING_LENGTH (sequence); + Ibyte *staging = alloca_ibytes (length), *staging_end = staging + length; + Ibyte *cursor = XSTRING_DATA (sequence), *endp = cursor + length; + + while (cursor < endp) + { + staging_end -= itext_ichar_len (cursor); + itext_copy_ichar (cursor, staging_end); + INC_IBYTEPTR (cursor); + } + + assert (staging == staging_end); + + result = make_string (staging, length); + } + else if (BIT_VECTORP (sequence)) + { + Lisp_Bit_Vector *bv = XBIT_VECTOR (sequence), *res; + Elemcount length = bit_vector_length (bv), ii = length; + + result = make_bit_vector (length, Qzero); + res = XBIT_VECTOR (result); + + while (ii > 0) + { + set_bit_vector_bit (res, length - ii, bit_vector_bit (bv, ii - 1)); + --ii; + } + } + else + { + assert (NILP (sequence)); + } + + return result; +} + +Lisp_Object +list_merge (Lisp_Object org_l1, Lisp_Object org_l2, + check_test_func_t check_merge, + Lisp_Object predicate, Lisp_Object key) +{ + Lisp_Object value; + Lisp_Object tail; + Lisp_Object tem; + Lisp_Object l1, l2; + Lisp_Object tortoises[2]; + struct gcpro gcpro1, gcpro2, gcpro3, gcpro4, gcpro5; + int l1_count = 0, l2_count = 0; + + l1 = org_l1; + l2 = org_l2; + tail = Qnil; + value = Qnil; + tortoises[0] = org_l1; + tortoises[1] = org_l2; + + /* It is sufficient to protect org_l1 and org_l2. When l1 and l2 are + updated, we copy the new values back into the org_ vars. */ + + GCPRO5 (org_l1, org_l2, predicate, value, tortoises[0]); + gcpro5.nvars = 2; + + while (1) + { + if (NILP (l1)) + { + UNGCPRO; + if (NILP (tail)) + return l2; + Fsetcdr (tail, l2); + return value; + } + if (NILP (l2)) + { + UNGCPRO; + if (NILP (tail)) + return l1; + Fsetcdr (tail, l1); + return value; + } + + if (check_merge (predicate, key, Fcar (l2), Fcar (l1)) == 0) + { + tem = l1; + l1 = Fcdr (l1); + org_l1 = l1; + + if (l1_count++ > CIRCULAR_LIST_SUSPICION_LENGTH) + { + if (l1_count & 1) + { + if (!CONSP (tortoises[0])) + { + mapping_interaction_error (Qmerge, tortoises[0]); + } + + tortoises[0] = XCDR (tortoises[0]); + } + + if (EQ (org_l1, tortoises[0])) + { + signal_circular_list_error (org_l1); + } + } + } + else + { + tem = l2; + l2 = Fcdr (l2); + org_l2 = l2; + + if (l2_count++ > CIRCULAR_LIST_SUSPICION_LENGTH) + { + if (l2_count & 1) + { + if (!CONSP (tortoises[1])) + { + mapping_interaction_error (Qmerge, tortoises[1]); + } + + tortoises[1] = XCDR (tortoises[1]); + } + + if (EQ (org_l2, tortoises[1])) + { + signal_circular_list_error (org_l2); + } + } + } + + if (NILP (tail)) + value = tem; + else + Fsetcdr (tail, tem); + + tail = tem; + } +} + +static void +array_merge (Lisp_Object *dest, Elemcount dest_len, + Lisp_Object *front, Elemcount front_len, + Lisp_Object *back, Elemcount back_len, + check_test_func_t check_merge, + Lisp_Object predicate, Lisp_Object key) +{ + Elemcount ii, fronting, backing; + Lisp_Object *front_staging = front; + Lisp_Object *back_staging = back; + struct gcpro gcpro1, gcpro2; + + assert (dest_len == (back_len + front_len)); + + if (0 == dest_len) + { + return; + } + + if (front >= dest && front < (dest + dest_len)) + { + front_staging = alloca_array (Lisp_Object, front_len); + + for (ii = 0; ii < front_len; ++ii) + { + front_staging[ii] = front[ii]; + } + } + + if (back >= dest && back < (dest + dest_len)) + { + back_staging = alloca_array (Lisp_Object, back_len); + + for (ii = 0; ii < back_len; ++ii) + { + back_staging[ii] = back[ii]; + } + } + + GCPRO2 (front_staging[0], back_staging[0]); + gcpro1.nvars = front_len; + gcpro2.nvars = back_len; + + for (ii = fronting = backing = 0; ii < dest_len; ++ii) + { + if (fronting >= front_len) + { + while (ii < dest_len) + { + dest[ii] = back_staging[backing]; + ++ii, ++backing; + } + UNGCPRO; + return; + } + + if (backing >= back_len) + { + while (ii < dest_len) + { + dest[ii] = front_staging[fronting]; + ++ii, ++fronting; + } + UNGCPRO; + return; + } + + if (check_merge (predicate, key, back_staging[backing], + front_staging[fronting]) == 0) + { + dest[ii] = front_staging[fronting]; + ++fronting; + } + else + { + dest[ii] = back_staging[backing]; + ++backing; + } + } + + UNGCPRO; +} + +static Lisp_Object +list_array_merge_into_list (Lisp_Object list, + Lisp_Object *array, Elemcount array_len, + check_test_func_t check_merge, + Lisp_Object predicate, Lisp_Object key, + Boolint reverse_order) +{ + Lisp_Object tail = Qnil, value = Qnil, tortoise = list; + struct gcpro gcpro1, gcpro2, gcpro3, gcpro4; + Elemcount array_index = 0; + int looped = 0; + + GCPRO4 (list, tail, value, tortoise); + + while (1) + { + if (NILP (list)) + { + UNGCPRO; + + if (NILP (tail)) + { + return Flist (array_len, array); + } + + Fsetcdr (tail, Flist (array_len - array_index, array + array_index)); + return value; + } + + if (array_index >= array_len) + { + UNGCPRO; + if (NILP (tail)) + { + return list; + } + + Fsetcdr (tail, list); + return value; + } + + + if (reverse_order ? + check_merge (predicate, key, Fcar (list), array [array_index]) + : !check_merge (predicate, key, array [array_index], Fcar (list))) + { + if (NILP (tail)) + { + value = tail = list; + } + else + { + Fsetcdr (tail, list); + tail = XCDR (tail); + } + + list = Fcdr (list); + } + else + { + if (NILP (tail)) + { + value = tail = Fcons (array [array_index], Qnil); + } + else + { + Fsetcdr (tail, Fcons (array [array_index], tail)); + tail = XCDR (tail); + } + ++array_index; + } + + if (++looped > CIRCULAR_LIST_SUSPICION_LENGTH) + { + if (looped & 1) + { + tortoise = XCDR (tortoise); + } + + if (EQ (list, tortoise)) + { + signal_circular_list_error (list); + } + } + } +} + +static void +list_list_merge_into_array (Lisp_Object *output, Elemcount output_len, + Lisp_Object list_one, Lisp_Object list_two, + check_test_func_t check_merge, + Lisp_Object predicate, Lisp_Object key) +{ + Elemcount output_index = 0; + + while (output_index < output_len) + { + if (NILP (list_one)) + { + while (output_index < output_len) + { + output [output_index] = Fcar (list_two); + list_two = Fcdr (list_two), ++output_index; + } + return; + } + + if (NILP (list_two)) + { + while (output_index < output_len) + { + output [output_index] = Fcar (list_one); + list_one = Fcdr (list_one), ++output_index; + } + return; + } + + if (check_merge (predicate, key, Fcar (list_two), Fcar (list_one)) + == 0) + { + output [output_index] = XCAR (list_one); + list_one = XCDR (list_one); + } + else + { + output [output_index] = XCAR (list_two); + list_two = XCDR (list_two); + } + + ++output_index; + + /* No need to check for circularity. */ + } +} + +static void +list_array_merge_into_array (Lisp_Object *output, Elemcount output_len, + Lisp_Object list, + Lisp_Object *array, Elemcount array_len, + check_test_func_t check_merge, + Lisp_Object predicate, Lisp_Object key, + Boolint reverse_order) +{ + Elemcount output_index = 0, array_index = 0; + + while (output_index < output_len) + { + if (NILP (list)) + { + if (array_len - array_index != output_len - output_index) + { + mapping_interaction_error (Qmerge, list); + } + + while (array_index < array_len) + { + output [output_index++] = array [array_index++]; + } + + return; + } + + if (array_index >= array_len) + { + while (output_index < output_len) + { + output [output_index++] = Fcar (list); + list = Fcdr (list); + } + + return; + } + + if (reverse_order ? + check_merge (predicate, key, Fcar (list), array [array_index]) : + !check_merge (predicate, key, array [array_index], Fcar (list))) + { + output [output_index] = XCAR (list); + list = XCDR (list); + } + else + { + output [output_index] = array [array_index]; + ++array_index; + } + + ++output_index; + } +} + +#define STRING_DATA_TO_OBJECT_ARRAY(strdata, c_array, counter, len) \ + do { \ + c_array = alloca_array (Lisp_Object, len); \ + for (counter = 0; counter < len; ++counter) \ + { \ + c_array[counter] = make_char (itext_ichar (strdata)); \ + INC_IBYTEPTR (strdata); \ + } \ + } while (0) + +#define BIT_VECTOR_TO_OBJECT_ARRAY(v, c_array, counter, len) do { \ + c_array = alloca_array (Lisp_Object, len); \ + for (counter = 0; counter < len; ++counter) \ + { \ + c_array[counter] = make_fixnum (bit_vector_bit (v, counter)); \ + } \ + } while (0) + +DEFUN ("merge", Fmerge, 4, MANY, 0, /* +Destructively merge SEQUENCE-ONE and SEQUENCE-TWO, producing a new sequence. + +TYPE is the type of sequence to return. PREDICATE is a `less-than' +predicate on the elements. + +Optional keyword argument KEY is a function used to extract an object to be +used for comparison from each element of SEQUENCE-ONE and SEQUENCE-TWO. + +arguments: (TYPE SEQUENCE-ONE SEQUENCE-TWO PREDICATE &key (KEY #'IDENTITY)) +*/ + (int nargs, Lisp_Object *args)) +{ + Lisp_Object type = args[0], sequence_one = args[1], sequence_two = args[2], + predicate = args[3], result = Qnil; + check_test_func_t check_merge = NULL; + + PARSE_KEYWORDS (Fmerge, nargs, args, 1, (key), NULL); + + CHECK_SEQUENCE (sequence_one); + CHECK_SEQUENCE (sequence_two); + + CHECK_KEY_ARGUMENT (key); + + check_merge = get_merge_predicate (predicate, key); + + if (EQ (type, Qlist) && (LISTP (sequence_one) || LISTP (sequence_two))) + { + if (NILP (sequence_two)) + { + result = Fappend (2, args + 1); + } + else if (NILP (sequence_one)) + { + args[3] = Qnil; /* Overwriting PREDICATE, and losing its GC + protection, but that doesn't matter. */ + result = Fappend (2, args + 2); + } + else if (CONSP (sequence_one) && CONSP (sequence_two)) + { + result = list_merge (sequence_one, sequence_two, check_merge, + predicate, key); + } + else + { + Lisp_Object *array_storage, swap; + Elemcount array_length, i; + Boolint reverse_order = 0; + + if (!CONSP (sequence_one)) + { + /* Make sequence_one the cons, sequence_two the array: */ + swap = sequence_one; + sequence_one = sequence_two; + sequence_two = swap; + reverse_order = 1; + } + + if (VECTORP (sequence_two)) + { + array_storage = XVECTOR_DATA (sequence_two); + array_length = XVECTOR_LENGTH (sequence_two); + } + else if (STRINGP (sequence_two)) + { + Ibyte *strdata = XSTRING_DATA (sequence_two); + array_length = string_char_length (sequence_two); + /* No need to GCPRO, characters are immediate. */ + STRING_DATA_TO_OBJECT_ARRAY (strdata, array_storage, i, + array_length); + + } + else + { + Lisp_Bit_Vector *v = XBIT_VECTOR (sequence_two); + array_length = bit_vector_length (v); + /* No need to GCPRO, fixnums are immediate. */ + BIT_VECTOR_TO_OBJECT_ARRAY (v, array_storage, i, array_length); + } + + result = list_array_merge_into_list (sequence_one, + array_storage, array_length, + check_merge, predicate, key, + reverse_order); + } + } + else + { + Elemcount sequence_one_len = XFIXNUM (Flength (sequence_one)), + sequence_two_len = XFIXNUM (Flength (sequence_two)), i; + Elemcount output_len = 1 + sequence_one_len + sequence_two_len; + Lisp_Object *output = alloca_array (Lisp_Object, output_len), + *sequence_one_storage = NULL, *sequence_two_storage = NULL; + Boolint do_coerce = !(EQ (type, Qvector) || EQ (type, Qstring) + || EQ (type, Qbit_vector) || EQ (type, Qlist)); + Ibyte *strdata = NULL; + Lisp_Bit_Vector *v = NULL; + struct gcpro gcpro1; + + output[0] = do_coerce ? Qlist : type; + for (i = 1; i < output_len; ++i) + { + output[i] = Qnil; + } + + GCPRO1 (output[0]); + gcpro1.nvars = output_len; + + if (VECTORP (sequence_one)) + { + sequence_one_storage = XVECTOR_DATA (sequence_one); + } + else if (STRINGP (sequence_one)) + { + strdata = XSTRING_DATA (sequence_one); + STRING_DATA_TO_OBJECT_ARRAY (strdata, sequence_one_storage, + i, sequence_one_len); + } + else if (BIT_VECTORP (sequence_one)) + { + v = XBIT_VECTOR (sequence_one); + BIT_VECTOR_TO_OBJECT_ARRAY (v, sequence_one_storage, + i, sequence_one_len); + } + + if (VECTORP (sequence_two)) + { + sequence_two_storage = XVECTOR_DATA (sequence_two); + } + else if (STRINGP (sequence_two)) + { + strdata = XSTRING_DATA (sequence_two); + STRING_DATA_TO_OBJECT_ARRAY (strdata, sequence_two_storage, + i, sequence_two_len); + } + else if (BIT_VECTORP (sequence_two)) + { + v = XBIT_VECTOR (sequence_two); + BIT_VECTOR_TO_OBJECT_ARRAY (v, sequence_two_storage, + i, sequence_two_len); + } + + if (LISTP (sequence_one) && LISTP (sequence_two)) + { + list_list_merge_into_array (output + 1, output_len - 1, + sequence_one, sequence_two, + check_merge, predicate, key); + } + else if (LISTP (sequence_one)) + { + list_array_merge_into_array (output + 1, output_len - 1, + sequence_one, + sequence_two_storage, + sequence_two_len, + check_merge, predicate, key, 0); + } + else if (LISTP (sequence_two)) + { + list_array_merge_into_array (output + 1, output_len - 1, + sequence_two, + sequence_one_storage, + sequence_one_len, + check_merge, predicate, key, 1); + } + else + { + array_merge (output + 1, output_len - 1, + sequence_one_storage, sequence_one_len, + sequence_two_storage, sequence_two_len, + check_merge, predicate, + key); + } + + result = Ffuncall (output_len, output); + + if (do_coerce) + { + result = call2 (Qcoerce, result, type); + } + + UNGCPRO; + } + + return result; +} + +Lisp_Object +list_sort (Lisp_Object list, check_test_func_t check_merge, + Lisp_Object predicate, Lisp_Object key) +{ + struct gcpro gcpro1, gcpro2, gcpro3, gcpro4; + Lisp_Object back, tem; + Lisp_Object front = list; + Lisp_Object len = Flength (list); + + if (XFIXNUM (len) < 2) + return list; + + len = make_fixnum (XFIXNUM (len) / 2 - 1); + tem = Fnthcdr (len, list); + back = Fcdr (tem); + Fsetcdr (tem, Qnil); + + GCPRO4 (front, back, predicate, key); + front = list_sort (front, check_merge, predicate, key); + back = list_sort (back, check_merge, predicate, key); + + RETURN_UNGCPRO (list_merge (front, back, check_merge, predicate, key)); +} + +static void +array_sort (Lisp_Object *array, Elemcount array_len, + check_test_func_t check_merge, + Lisp_Object predicate, Lisp_Object key) +{ + Elemcount split; + + if (array_len < 2) + return; + + split = array_len / 2; + + array_sort (array, split, check_merge, predicate, key); + array_sort (array + split, array_len - split, check_merge, predicate, + key); + array_merge (array, array_len, array, split, array + split, + array_len - split, check_merge, predicate, key); +} + +DEFUN ("sort*", FsortX, 2, MANY, 0, /* +Sort SEQUENCE, comparing elements using PREDICATE. +Returns the sorted sequence. SEQUENCE is modified by side effect. + +PREDICATE is called with two elements of SEQUENCE, and should return t if +the first element is `less' than the second. + +Optional keyword argument KEY is a function used to extract an object to be +used for comparison from each element of SEQUENCE. + +In this implementation, sorting is always stable; but call `stable-sort' if +this stability is important to you, other implementations may not make the +same guarantees. + +arguments: (SEQUENCE PREDICATE &key (KEY #'IDENTITY)) +*/ + (int nargs, Lisp_Object *args)) +{ + Lisp_Object sequence = args[0], predicate = args[1]; + Lisp_Object *sequence_carray; + check_test_func_t check_merge = NULL; + Elemcount sequence_len, i; + + PARSE_KEYWORDS (FsortX, nargs, args, 1, (key), NULL); + + CHECK_SEQUENCE (sequence); + + CHECK_KEY_ARGUMENT (key); + + check_merge = get_merge_predicate (predicate, key); + + if (LISTP (sequence)) + { + sequence = list_sort (sequence, check_merge, predicate, key); + } + else if (VECTORP (sequence)) + { + array_sort (XVECTOR_DATA (sequence), XVECTOR_LENGTH (sequence), + check_merge, predicate, key); + } + else if (STRINGP (sequence)) + { + Ibyte *strdata = XSTRING_DATA (sequence); + + sequence_len = string_char_length (sequence); + + STRING_DATA_TO_OBJECT_ARRAY (strdata, sequence_carray, i, sequence_len); + + /* No GCPRO necessary, characters are immediate. */ + array_sort (sequence_carray, sequence_len, check_merge, predicate, key); + + strdata = XSTRING_DATA (sequence); + + CHECK_LISP_WRITEABLE (sequence); + for (i = 0; i < sequence_len; ++i) + { + strdata += set_itext_ichar (strdata, XCHAR (sequence_carray[i])); + } + + init_string_ascii_begin (sequence); + bump_string_modiff (sequence); + sledgehammer_check_ascii_begin (sequence); + } + else if (BIT_VECTORP (sequence)) + { + Lisp_Bit_Vector *v = XBIT_VECTOR (sequence); + sequence_len = bit_vector_length (v); + + BIT_VECTOR_TO_OBJECT_ARRAY (v, sequence_carray, i, sequence_len); + + /* No GCPRO necessary, bits are immediate. */ + array_sort (sequence_carray, sequence_len, check_merge, predicate, key); + + for (i = 0; i < sequence_len; ++i) + { + set_bit_vector_bit (v, i, XFIXNUM (sequence_carray [i])); + } + } + + return sequence; +} + + +static Lisp_Object replace_string_range_1 (Lisp_Object dest, + Lisp_Object start, + Lisp_Object end, + const Ibyte *source, + const Ibyte *source_limit, + Lisp_Object item); + +/* Fill the substring of DEST beginning at START and ending before END with + the character ITEM. If DEST does not have sufficient space for END - + START characters at START, write as many as is possible without changing + the character length of DEST. Update the string modification flag and do + any sledgehammer checks we have turned on. + + START must be a Lisp integer. END can be nil, indicating the length of the + string, or a Lisp integer. The condition (<= 0 START END (length DEST)) + must hold, or fill_string_range() will signal an error. */ +static Lisp_Object +fill_string_range (Lisp_Object dest, Lisp_Object item, Lisp_Object start, + Lisp_Object end) +{ + return replace_string_range_1 (dest, start, end, NULL, NULL, item); +} + +DEFUN ("fill", Ffill, 2, MANY, 0, /* +Destructively modify SEQUENCE by replacing each element with ITEM. +SEQUENCE is a list, vector, bit vector, or string. + +Optional keyword START is the index of the first element of SEQUENCE +to be modified, and defaults to zero. Optional keyword END is the +exclusive upper bound on the elements of SEQUENCE to be modified, and +defaults to the length of SEQUENCE. + +arguments: (SEQUENCE ITEM &key (START 0) (END (length SEQUENCE))) +*/ + (int nargs, Lisp_Object *args)) +{ + Lisp_Object sequence = args[0]; + Lisp_Object item = args[1]; + Elemcount starting, ending = MOST_POSITIVE_FIXNUM + 1, ii, len; + + PARSE_KEYWORDS (Ffill, nargs, args, 2, (start, end), (start = Qzero)); + + CHECK_NATNUM (start); + starting = BIGNUMP (start) ? MOST_POSITIVE_FIXNUM + 1 : XFIXNUM (start); + + if (!NILP (end)) + { + CHECK_NATNUM (end); + ending = BIGNUMP (end) ? MOST_POSITIVE_FIXNUM + 1 : XFIXNUM (end); + } + + retry: + if (STRINGP (sequence)) + { + CHECK_CHAR_COERCE_INT (item); + CHECK_LISP_WRITEABLE (sequence); + + fill_string_range (sequence, item, start, end); + } + else if (VECTORP (sequence)) + { + Lisp_Object *p = XVECTOR_DATA (sequence); + + CHECK_LISP_WRITEABLE (sequence); + len = XVECTOR_LENGTH (sequence); + + check_sequence_range (sequence, start, end, make_fixnum (len)); + ending = min (ending, len); + + for (ii = starting; ii < ending; ++ii) + { + p[ii] = item; + } + } + else if (BIT_VECTORP (sequence)) + { + Lisp_Bit_Vector *v = XBIT_VECTOR (sequence); + int bit; + + CHECK_BIT (item); + bit = XFIXNUM (item); + CHECK_LISP_WRITEABLE (sequence); + len = bit_vector_length (v); + + check_sequence_range (sequence, start, end, make_fixnum (len)); + ending = min (ending, len); + + for (ii = starting; ii < ending; ++ii) + { + set_bit_vector_bit (v, ii, bit); + } + } + else if (LISTP (sequence)) + { + Elemcount counting = 0; + + { + EXTERNAL_LIST_LOOP_3 (elt, sequence, tail) + { + if (counting >= starting) + { + if (counting < ending) + { + XSETCAR (tail, item); + } + else if (counting == ending) + { + break; + } + } + ++counting; + } + } + + if (counting < starting || (counting != ending && !NILP (end))) + { + check_sequence_range (args[0], start, end, Flength (args[0])); + } + } + else + { + sequence = wrong_type_argument (Qsequencep, sequence); + goto retry; + } + return sequence; +} + + +/* Replace the substring of DEST beginning at START and ending before END + with the text at SOURCE, which is END - START characters long and + SOURCE_LIMIT - SOURCE octets long. If DEST does not have sufficient + space for END - START characters at START, write as many as is possible + without changing the length of DEST. Update the string modification flag + and do any sledgehammer checks we have turned on in this build. + + START must be a Lisp integer. END can be nil, indicating the length of the + string, or a Lisp integer. The condition (<= 0 START END (length DEST)) + must hold, or replace_string_range() will signal an error. */ +static Lisp_Object +replace_string_range (Lisp_Object dest, Lisp_Object start, Lisp_Object end, + const Ibyte *source, const Ibyte *source_limit) +{ + return replace_string_range_1 (dest, start, end, source, source_limit, + Qnil); +} + +/* This is the guts of several mapping functions. + + Call FUNCTION CALL_COUNT times, with NSEQUENCES arguments each time, + taking the elements from SEQUENCES. If VALS is non-NULL, store the + results into VALS, a C array of Lisp_Objects; else, if LISP_VALS is + non-nil, store the results into LISP_VALS, a sequence with sufficient + room for CALL_COUNT results (but see the documentation of SOME_OR_EVERY.) + Else, do not accumulate any result. + + If VALS is non-NULL, NSEQUENCES is one, and SEQUENCES[0] is a cons, + mapcarX will store the elements of SEQUENCES[0] in stack and GCPRO them, + so FUNCTION cannot insert a non-cons into SEQUENCES[0] and throw off + mapcarX. + + Otherwise, mapcarX signals an invalid state error (see + mapping_interaction_error(), above) if it encounters a non-cons, + non-array when traversing SEQUENCES. Common Lisp specifies in + MAPPING-DESTRUCTIVE-INTERACTION that it is an error when FUNCTION + destructively modifies SEQUENCES in a way that might affect the ongoing + traversal operation. + + CALLER is a symbol describing the Lisp-visible function that was called, + and any errors thrown because SEQUENCES was modified will reflect it. + + If CALLER is Qsome, return the (possibly multiple) values given by + FUNCTION the first time it is non-nil, and abandon the iterations. + LISP_VALS must be the result of calling STORE_VOID_IN_LISP on the address + of a Lisp object, and the return value will be stored at that address. + If CALLER is Qevery, LISP_VALS must also reflect a pointer to a Lisp + object, and Qnil will be stored at that address if FUNCTION gives nil; + otherwise it will be left alone. */ + +static void +mapcarX (Elemcount call_count, Lisp_Object *vals, Lisp_Object lisp_vals, + Lisp_Object function, int nsequences, Lisp_Object *sequences, + Lisp_Object caller) +{ + Lisp_Object called, *args; + struct gcpro gcpro1, gcpro2; + Ibyte *lisp_vals_staging = NULL, *cursor = NULL; + int i, j; + + assert ((EQ (caller, Qsome) || EQ (caller, Qevery)) ? vals == NULL : 1); + + args = alloca_array (Lisp_Object, nsequences + 1); + args[0] = function; + for (i = 1; i <= nsequences; ++i) + { + args[i] = Qnil; + } + + if (vals != NULL) + { + GCPRO2 (args[0], vals[0]); + gcpro1.nvars = nsequences + 1; + gcpro2.nvars = 0; + } + else + { + GCPRO1 (args[0]); + gcpro1.nvars = nsequences + 1; + } + + /* Be extra nice in the event that we've been handed one list and one + only; make it possible for FUNCTION to set cdrs not yet processed to + non-cons, non-nil objects without ill-effect, if we have been handed + the stack space to do that. */ + if (vals != NULL && 1 == nsequences && CONSP (sequences[0])) + { + Lisp_Object lst = sequences[0]; + Lisp_Object *val = vals; + for (i = 0; i < call_count; ++i) + { + *val++ = XCAR (lst); + lst = XCDR (lst); + } + gcpro2.nvars = call_count; + + for (i = 0; i < call_count; ++i) + { + args[1] = vals[i]; + vals[i] = IGNORE_MULTIPLE_VALUES (Ffuncall (nsequences + 1, args)); + } + } + else + { + enum lrecord_type lisp_vals_type = lrecord_type_symbol; + Binbyte *sequence_types = alloca_array (Binbyte, nsequences); + for (j = 0; j < nsequences; ++j) + { + sequence_types[j] = XRECORD_LHEADER (sequences[j])->type; + } + + if (!EQ (caller, Qsome) && !EQ (caller, Qevery)) + { + assert (LRECORDP (lisp_vals)); + + lisp_vals_type + = (enum lrecord_type) XRECORD_LHEADER (lisp_vals)->type; + + if (lrecord_type_string == lisp_vals_type) + { + lisp_vals_staging = cursor + = alloca_ibytes (call_count * MAX_ICHAR_LEN); + } + else if (ARRAYP (lisp_vals)) + { + CHECK_LISP_WRITEABLE (lisp_vals); + } + } + + for (i = 0; i < call_count; ++i) + { + for (j = 0; j < nsequences; ++j) + { + switch (sequence_types[j]) + { + case lrecord_type_cons: + { + if (!CONSP (sequences[j])) + { + /* This means FUNCTION has messed around with a cons + in one of the sequences, since we checked the + type (CHECK_SEQUENCE()) and the length and + structure (with Flength()) correctly in our + callers. */ + mapping_interaction_error (caller, sequences[j]); + } + args[j + 1] = XCAR (sequences[j]); + sequences[j] = XCDR (sequences[j]); + break; + } + case lrecord_type_vector: + { + args[j + 1] = XVECTOR_DATA (sequences[j])[i]; + break; + } + case lrecord_type_string: + { + args[j + 1] = make_char (string_ichar (sequences[j], i)); + break; + } + case lrecord_type_bit_vector: + { + args[j + 1] + = make_fixnum (bit_vector_bit (XBIT_VECTOR (sequences[j]), + i)); + break; + } + default: + ABORT(); + } + } + called = Ffuncall (nsequences + 1, args); + if (vals != NULL) + { + vals[i] = IGNORE_MULTIPLE_VALUES (called); + gcpro2.nvars += 1; + } + else if (EQ (Qsome, caller)) + { + if (!NILP (IGNORE_MULTIPLE_VALUES (called))) + { + Lisp_Object *result + = (Lisp_Object *) GET_VOID_FROM_LISP (lisp_vals); + *result = called; + UNGCPRO; + return; + } + } + else if (EQ (Qevery, caller)) + { + if (NILP (IGNORE_MULTIPLE_VALUES (called))) + { + Lisp_Object *result + = (Lisp_Object *) GET_VOID_FROM_LISP (lisp_vals); + *result = Qnil; + UNGCPRO; + return; + } + } + else + { + called = IGNORE_MULTIPLE_VALUES (called); + switch (lisp_vals_type) + { + case lrecord_type_symbol: + /* Discard the result of funcall. */ + break; + case lrecord_type_cons: + { + if (!CONSP (lisp_vals)) + { + /* If FUNCTION has inserted a non-cons non-nil + cdr into the list before we've processed the + relevant part, error. */ + mapping_interaction_error (caller, lisp_vals); + } + XSETCAR (lisp_vals, called); + lisp_vals = XCDR (lisp_vals); + break; + } + case lrecord_type_vector: + { + i < XVECTOR_LENGTH (lisp_vals) ? + (XVECTOR_DATA (lisp_vals)[i] = called) : + /* Let #'aset error. */ + Faset (lisp_vals, make_fixnum (i), called); + break; + } + case lrecord_type_string: + { + CHECK_CHAR_COERCE_INT (called); + cursor += set_itext_ichar (cursor, XCHAR (called)); + break; + } + case lrecord_type_bit_vector: + { + (BITP (called) && + i < bit_vector_length (XBIT_VECTOR (lisp_vals))) ? + set_bit_vector_bit (XBIT_VECTOR (lisp_vals), i, + XFIXNUM (called)) : + (void) Faset (lisp_vals, make_fixnum (i), called); + break; + } + default: + { + ABORT(); + break; + } + } + } + } + + if (lisp_vals_staging != NULL) + { + CHECK_LISP_WRITEABLE (lisp_vals); + replace_string_range (lisp_vals, Qzero, make_fixnum (call_count), + lisp_vals_staging, cursor); + } + } + + UNGCPRO; +} + +/* Given NSEQUENCES objects at the address pointed to by SEQUENCES, return + the length of the shortest sequence. Error if all are circular, or if any + one of them is not a sequence. */ +static Elemcount +shortest_length_among_sequences (int nsequences, Lisp_Object *sequences) +{ + Elemcount len = 1 + MOST_POSITIVE_FIXNUM; + Lisp_Object length = Qnil; + int i; + + for (i = 0; i < nsequences; ++i) + { + if (CONSP (sequences[i])) + { + length = Flist_length (sequences[i]); + if (!NILP (length)) + { + len = min (len, XFIXNUM (length)); + } + } + else + { + CHECK_SEQUENCE (sequences[i]); + length = Flength (sequences[i]); + len = min (len, XFIXNUM (length)); + } + } + + if (len == 1 + MOST_POSITIVE_FIXNUM) + { + signal_circular_list_error (sequences[0]); + } + + return len; +} + +DEFUN ("mapconcat", Fmapconcat, 3, MANY, 0, /* +Call FUNCTION on each element of SEQUENCE, and concat results to a string. +Between each pair of results, insert SEPARATOR. + +Each result, and SEPARATOR, should be strings. Thus, using " " as SEPARATOR +results in spaces between the values returned by FUNCTION. SEQUENCE itself +may be a list, a vector, a bit vector, or a string. + +With optional SEQUENCES, call FUNCTION each time with as many arguments as +there are SEQUENCES, plus one for the element from SEQUENCE. One element +from each sequence will be used each time FUNCTION is called, and +`mapconcat' will give up once the shortest sequence is exhausted. + +arguments: (FUNCTION SEQUENCE SEPARATOR &rest SEQUENCES) +*/ + (int nargs, Lisp_Object *args)) +{ + Lisp_Object function = args[0]; + Lisp_Object sequence = args[1]; + Lisp_Object separator = args[2]; + Elemcount len = MOST_POSITIVE_FIXNUM; + Lisp_Object *args0; + EMACS_INT i, nargs0; + + args[2] = sequence; + args[1] = separator; + + len = shortest_length_among_sequences (nargs - 2, args + 2); + + if (len == 0) return build_ascstring (""); + + nargs0 = len + len - 1; + args0 = alloca_array (Lisp_Object, nargs0); + + /* Special-case this, it's very common and doesn't require any + funcalls. Upside of doing it here, instead of cl-macs.el: no consing, + apart from the final string, we allocate everything on the stack. */ + if (EQ (function, Qidentity) && 3 == nargs && CONSP (sequence)) + { + for (i = 0; i < len; ++i) + { + args0[i] = XCAR (sequence); + sequence = XCDR (sequence); + } + } + else + { + mapcarX (len, args0, Qnil, function, nargs - 2, args + 2, Qmapconcat); + } + + for (i = len - 1; i >= 0; i--) + args0[i + i] = args0[i]; + + for (i = 1; i < nargs0; i += 2) + args0[i] = separator; + + return Fconcat (nargs0, args0); +} + +DEFUN ("mapcar*", FmapcarX, 2, MANY, 0, /* +Call FUNCTION on each element of SEQUENCE; return a list of the results. +The result is a list of the same length as SEQUENCE. +SEQUENCE may be a list, a vector, a bit vector, or a string. + +With optional SEQUENCES, call FUNCTION each time with as many arguments as +there are SEQUENCES, plus one for the element from SEQUENCE. One element +from each sequence will be used each time FUNCTION is called, and `mapcar' +stops calling FUNCTION once the shortest sequence is exhausted. + +arguments: (FUNCTION SEQUENCE &rest SEQUENCES) +*/ + (int nargs, Lisp_Object *args)) +{ + Lisp_Object function = args[0]; + Elemcount len = shortest_length_among_sequences (nargs - 1, args + 1); + Lisp_Object *args0; + + args0 = alloca_array (Lisp_Object, len); + mapcarX (len, args0, Qnil, function, nargs - 1, args + 1, QmapcarX); + + return Flist ((int) len, args0); +} + +DEFUN ("mapvector", Fmapvector, 2, MANY, 0, /* +Call FUNCTION on each element of SEQUENCE; return a vector of the results. +The result is a vector of the same length as SEQUENCE. +SEQUENCE may be a list, a vector, a bit vector, or a string. + +With optional SEQUENCES, call FUNCTION each time with as many arguments as +there are SEQUENCES, plus one for the element from SEQUENCE. One element +from each sequence will be used each time FUNCTION is called, and +`mapvector' stops calling FUNCTION once the shortest sequence is exhausted. + +arguments: (FUNCTION SEQUENCE &rest SEQUENCES) +*/ + (int nargs, Lisp_Object *args)) +{ + Lisp_Object function = args[0]; + Elemcount len = shortest_length_among_sequences (nargs - 1, args + 1); + Lisp_Object result = make_vector (len, Qnil); + + struct gcpro gcpro1; + GCPRO1 (result); + /* Don't pass result as the lisp_object argument, we want mapcarX to protect + a single list argument's elements from being garbage-collected. */ + mapcarX (len, XVECTOR_DATA (result), Qnil, function, nargs - 1, args +1, + Qmapvector); + RETURN_UNGCPRO (result); +} + +DEFUN ("mapcan", Fmapcan, 2, MANY, 0, /* +Call FUNCTION on each element of SEQUENCE; chain the results together. + +FUNCTION must normally return a list; the results will be concatenated +together using `nconc'. + +With optional SEQUENCES, call FUNCTION each time with as many arguments as +there are SEQUENCES, plus one for the element from SEQUENCE. One element +from each sequence will be used each time FUNCTION is called, and +`mapcan' stops calling FUNCTION once the shortest sequence is exhausted. + +arguments: (FUNCTION SEQUENCE &rest SEQUENCES) +*/ + (int nargs, Lisp_Object *args)) +{ + Elemcount len = shortest_length_among_sequences (nargs - 1, args + 1); + Lisp_Object function = args[0], *result = alloca_array (Lisp_Object, len); + + mapcarX (len, result, Qnil, function, nargs - 1, args + 1, Qmapcan); + + /* #'nconc GCPROs its args in case of signals and error. */ + return Fnconc (len, result); +} + +DEFUN ("mapc", Fmapc, 2, MANY, 0, /* +Call FUNCTION on each element of SEQUENCE. + +SEQUENCE may be a list, a vector, a bit vector, or a string. +This function is like `mapcar' but does not accumulate the results, +which is more efficient if you do not use the results. + +With optional SEQUENCES, call FUNCTION each time with as many arguments as +there are SEQUENCES, plus one for the elements from SEQUENCE. One element +from each sequence will be used each time FUNCTION is called, and +`mapc' stops calling FUNCTION once the shortest sequence is exhausted. + +Return SEQUENCE. + +arguments: (FUNCTION SEQUENCE &rest SEQUENCES) +*/ + (int nargs, Lisp_Object *args)) +{ + Elemcount len = shortest_length_among_sequences (nargs - 1, args + 1); + Lisp_Object sequence = args[1]; + struct gcpro gcpro1; + /* We need to GCPRO sequence, because mapcarX will modify the + elements of the args array handed to it, and this may involve + elements of sequence getting garbage collected. */ + GCPRO1 (sequence); + mapcarX (len, NULL, Qnil, args[0], nargs - 1, args + 1, Qmapc); + RETURN_UNGCPRO (sequence); +} + +DEFUN ("map", Fmap, 3, MANY, 0, /* +Map FUNCTION across one or more sequences, returning a sequence. + +TYPE is the sequence type to return, FUNCTION is the function, SEQUENCE is +the first argument sequence, SEQUENCES are the other argument sequences. + +FUNCTION will be called with (1+ (length SEQUENCES)) arguments, and must be +capable of accepting this number of arguments. + +Certain TYPEs are recognised internally by `map', but others are not, and +`coerce' may throw an error on an attempt to convert to a TYPE it does not +understand. A null TYPE means do not accumulate any values. + +arguments: (TYPE FUNCTION SEQUENCE &rest SEQUENCES) +*/ + (int nargs, Lisp_Object *args)) +{ + Lisp_Object type = args[0]; + Lisp_Object function = args[1]; + Lisp_Object result = Qnil; + Lisp_Object *args0 = NULL; + Elemcount len = shortest_length_among_sequences (nargs - 2, args + 2); + struct gcpro gcpro1; + + if (!NILP (type)) + { + args0 = alloca_array (Lisp_Object, len); + } + + mapcarX (len, args0, Qnil, function, nargs - 2, args + 2, Qmap); + + if (EQ (type, Qnil)) + { + return result; + } + + if (EQ (type, Qvector) || EQ (type, Qarray)) + { + result = Fvector (len, args0); + } + else if (EQ (type, Qstring)) + { + result = Fstring (len, args0); + } + else if (EQ (type, Qlist)) + { + result = Flist (len, args0); + } + else if (EQ (type, Qbit_vector)) + { + result = Fbit_vector (len, args0); + } + else + { + result = Flist (len, args0); + GCPRO1 (result); + result = call2 (Qcoerce, result, type); + UNGCPRO; + } + + return result; +} + +DEFUN ("map-into", Fmap_into, 2, MANY, 0, /* +Modify RESULT-SEQUENCE using the return values of FUNCTION on SEQUENCES. + +RESULT-SEQUENCE and SEQUENCES can be lists or arrays. + +FUNCTION must accept at least as many arguments as there are SEQUENCES +\(possibly zero). If RESULT-SEQUENCE and the elements of SEQUENCES are not +the same length, stop when the shortest is exhausted; any elements of +RESULT-SEQUENCE beyond that are unmodified. + +Return RESULT-SEQUENCE. + +arguments: (RESULT-SEQUENCE FUNCTION &rest SEQUENCES) +*/ + (int nargs, Lisp_Object *args)) +{ + Elemcount len; + Lisp_Object result_sequence = args[0]; + Lisp_Object function = args[1]; + + args[0] = function; + args[1] = result_sequence; + + len = shortest_length_among_sequences (nargs - 1, args + 1); + + mapcarX (len, NULL, result_sequence, function, nargs - 2, args + 2, + Qmap_into); + + return result_sequence; +} + +DEFUN ("some", Fsome, 2, MANY, 0, /* +Return true if PREDICATE gives non-nil for an element of SEQUENCE. + +If so, return the value (possibly multiple) given by PREDICATE. + +With optional SEQUENCES, call PREDICATE each time with as many arguments as +there are SEQUENCES (plus one for the element from SEQUENCE). + +See also `find-if', which returns the corresponding element of SEQUENCE, +rather than the value given by PREDICATE, and accepts bounding index +keywords. + +arguments: (PREDICATE SEQUENCE &rest SEQUENCES) +*/ + (int nargs, Lisp_Object *args)) +{ + Lisp_Object result = Qnil, + result_ptr = STORE_VOID_IN_LISP ((void *) &result); + Elemcount len = shortest_length_among_sequences (nargs - 1, args + 1); + + mapcarX (len, NULL, result_ptr, args[0], nargs - 1, args +1, Qsome); + + return result; +} + +DEFUN ("every", Fevery, 2, MANY, 0, /* +Return true if PREDICATE is true of every element of SEQUENCE. + +With optional SEQUENCES, call PREDICATE each time with as many arguments as +there are SEQUENCES (plus one for the element from SEQUENCE). + +In contrast to `some', `every' never returns multiple values. + +arguments: (PREDICATE SEQUENCE &rest SEQUENCES) +*/ + (int nargs, Lisp_Object *args)) +{ + Lisp_Object result = Qt, result_ptr = STORE_VOID_IN_LISP ((void *) &result); + Elemcount len = shortest_length_among_sequences (nargs - 1, args + 1); + + mapcarX (len, NULL, result_ptr, args[0], nargs - 1, args +1, Qevery); + + return result; +} + + +DEFUN ("reduce", Freduce, 2, MANY, 0, /* +Combine the elements of SEQUENCE using FUNCTION, a binary operation. + +For example, `(reduce #'+ SEQUENCE)' returns the sum of all elements in +SEQUENCE, and `(reduce #'union SEQUENCE)' returns the union of all elements +in SEQUENCE. + +Keywords supported: :start :end :from-end :initial-value :key +See `remove*' for the meaning of :start, :end, :from-end and :key. + +:initial-value specifies an element (typically an identity element, such as +0) that is conceptually prepended to the sequence (or appended, when +:from-end is given). + +If the sequence has one element, that element is returned directly. +If the sequence has no elements, :initial-value is returned if given; +otherwise, FUNCTION is called with no arguments, and its result returned. + +arguments: (FUNCTION SEQUENCE &key (START 0) (END (length SEQUENCE)) FROM-END INITIAL-VALUE (KEY #'identity)) +*/ + (int nargs, Lisp_Object *args)) +{ + Lisp_Object function = args[0], sequence = args[1], accum = Qunbound; + Elemcount starting, ending = MOST_POSITIVE_FIXNUM + 1, ii = 0; + + PARSE_KEYWORDS (Freduce, nargs, args, 5, + (start, end, from_end, initial_value, key), + (start = Qzero, initial_value = Qunbound)); + + CHECK_SEQUENCE (sequence); + CHECK_NATNUM (start); + starting = BIGNUMP (start) ? MOST_POSITIVE_FIXNUM + 1 : XFIXNUM (start); + CHECK_KEY_ARGUMENT (key); + +#define KEY(key, item) (EQ (Qidentity, key) ? item : \ + IGNORE_MULTIPLE_VALUES (call1 (key, item))) +#define CALL2(function, accum, item) \ + IGNORE_MULTIPLE_VALUES (call2 (function, accum, item)) + + if (!NILP (end)) + { + CHECK_NATNUM (end); + ending = BIGNUMP (end) ? MOST_POSITIVE_FIXNUM + 1 : XFIXNUM (end); + } + + if (VECTORP (sequence)) + { + Lisp_Vector *vv = XVECTOR (sequence); + struct gcpro gcpro1; + + check_sequence_range (sequence, start, end, make_fixnum (vv->size)); + + ending = min (ending, vv->size); + + GCPRO1 (accum); + + if (!UNBOUNDP (initial_value)) + { + accum = initial_value; + } + else if (ending - starting) + { + if (NILP (from_end)) + { + accum = KEY (key, vv->contents[starting]); + starting++; + } + else + { + accum = KEY (key, vv->contents[ending - 1]); + ending--; + } + } + + if (NILP (from_end)) + { + for (ii = starting; ii < ending; ++ii) + { + accum = CALL2 (function, accum, KEY (key, vv->contents[ii])); + } + } + else + { + for (ii = ending - 1; ii >= starting; --ii) + { + accum = CALL2 (function, KEY (key, vv->contents[ii]), accum); + } + } + + UNGCPRO; + } + else if (BIT_VECTORP (sequence)) + { + Lisp_Bit_Vector *bv = XBIT_VECTOR (sequence); + struct gcpro gcpro1; + + check_sequence_range (sequence, start, end, make_fixnum (bv->size)); + ending = min (ending, bv->size); + + GCPRO1 (accum); + + if (!UNBOUNDP (initial_value)) + { + accum = initial_value; + } + else if (ending - starting) + { + if (NILP (from_end)) + { + accum = KEY (key, make_fixnum (bit_vector_bit (bv, starting))); + starting++; + } + else + { + accum = KEY (key, make_fixnum (bit_vector_bit (bv, ending - 1))); + ending--; + } + } + + if (NILP (from_end)) + { + for (ii = starting; ii < ending; ++ii) + { + accum = CALL2 (function, accum, + KEY (key, make_fixnum (bit_vector_bit (bv, ii)))); + } + } + else + { + for (ii = ending - 1; ii >= starting; --ii) + { + accum = CALL2 (function, KEY (key, + make_fixnum (bit_vector_bit (bv, + ii))), + accum); + } + } + + UNGCPRO; + + } + else if (STRINGP (sequence)) + { + struct gcpro gcpro1; + + GCPRO1 (accum); + + if (NILP (from_end)) + { + Bytecount byte_len = XSTRING_LENGTH (sequence); + Bytecount cursor_offset = 0; + const Ibyte *startp = XSTRING_DATA (sequence); + const Ibyte *cursor = startp; + + for (ii = 0; ii != starting && cursor_offset < byte_len; ++ii) + { + INC_IBYTEPTR (cursor); + cursor_offset = cursor - startp; + } + + if (!UNBOUNDP (initial_value)) + { + accum = initial_value; + } + else if (ending - starting && cursor_offset < byte_len) + { + accum = KEY (key, make_char (itext_ichar (cursor))); + starting++; + startp = XSTRING_DATA (sequence); + cursor = startp + cursor_offset; + + if (byte_len != XSTRING_LENGTH (sequence) + || !valid_ibyteptr_p (cursor)) + { + mapping_interaction_error (Qreduce, sequence); + } + + INC_IBYTEPTR (cursor); + cursor_offset = cursor - startp; + ii++; + } + + while (cursor_offset < byte_len && ii < ending) + { + accum = CALL2 (function, accum, + KEY (key, make_char (itext_ichar (cursor)))); + + startp = XSTRING_DATA (sequence); + cursor = startp + cursor_offset; + + if (byte_len != XSTRING_LENGTH (sequence) + || !valid_ibyteptr_p (cursor)) + { + mapping_interaction_error (Qreduce, sequence); + } + + INC_IBYTEPTR (cursor); + cursor_offset = cursor - startp; + ++ii; + } + + if (ii < starting || (ii < ending && !NILP (end))) + { + check_sequence_range (sequence, start, end, Flength (sequence)); + } + } + else + { + Elemcount len = string_char_length (sequence); + Bytecount cursor_offset, byte_len = XSTRING_LENGTH (sequence); + const Ibyte *cursor; + + check_sequence_range (sequence, start, end, make_fixnum (len)); + ending = min (ending, len); + starting = XFIXNUM (start); + + cursor = string_char_addr (sequence, ending - 1); + cursor_offset = cursor - XSTRING_DATA (sequence); + + if (!UNBOUNDP (initial_value)) + { + accum = initial_value; + } + else if (ending - starting) + { + accum = KEY (key, make_char (itext_ichar (cursor))); + ending--; + if (ending > 0) + { + cursor = XSTRING_DATA (sequence) + cursor_offset; + + if (!valid_ibyteptr_p (cursor)) + { + mapping_interaction_error (Qreduce, sequence); + } + + DEC_IBYTEPTR (cursor); + cursor_offset = cursor - XSTRING_DATA (sequence); + } + } + + for (ii = ending - 1; ii >= starting; --ii) + { + accum = CALL2 (function, KEY (key, + make_char (itext_ichar (cursor))), + accum); + if (ii > 0) + { + cursor = XSTRING_DATA (sequence) + cursor_offset; + + if (byte_len != XSTRING_LENGTH (sequence) + || !valid_ibyteptr_p (cursor)) + { + mapping_interaction_error (Qreduce, sequence); + } + + DEC_IBYTEPTR (cursor); + cursor_offset = cursor - XSTRING_DATA (sequence); + } + } + } + + UNGCPRO; + } + else if (LISTP (sequence)) + { + if (NILP (from_end)) + { + struct gcpro gcpro1; + + GCPRO1 (accum); + + if (!UNBOUNDP (initial_value)) + { + accum = initial_value; + } + else if (ending - starting) + { + GC_EXTERNAL_LIST_LOOP_2 (elt, sequence) + { + if (ii == starting) + { + accum = KEY (key, elt); + starting++; + break; + } + ++ii; + } + END_GC_EXTERNAL_LIST_LOOP (elt); + } + + ii = 0; + + if (ending - starting) + { + GC_EXTERNAL_LIST_LOOP_2 (elt, sequence) + { + if (ii >= starting) + { + if (ii < ending) + { + accum = CALL2 (function, accum, KEY (key, elt)); + } + else if (ii == ending) + { + break; + } + } + ++ii; + } + END_GC_EXTERNAL_LIST_LOOP (elt); + } + + UNGCPRO; + + if (ii < starting || (ii < ending && !NILP (end))) + { + check_sequence_range (sequence, start, end, Flength (sequence)); + } + } + else + { + Boolint need_accum = 0; + Lisp_Object *subsequence = NULL; + Elemcount counting = 0, len = 0; + struct gcpro gcpro1; + + len = XFIXNUM (Flength (sequence)); + check_sequence_range (sequence, start, end, make_fixnum (len)); + ending = min (ending, len); + + /* :from-end with a list; make an alloca copy of the relevant list + data, attempting to go backwards isn't worth the trouble. */ + if (!UNBOUNDP (initial_value)) + { + accum = initial_value; + if (ending - starting && starting < ending) + { + subsequence = alloca_array (Lisp_Object, ending - starting); + } + } + else if (ending - starting && starting < ending) + { + subsequence = alloca_array (Lisp_Object, ending - starting); + need_accum = 1; + } + + if (ending - starting && starting < ending) + { + EXTERNAL_LIST_LOOP_3 (elt, sequence, tail) + { + if (counting >= starting) + { + if (counting < ending) + { + subsequence[ii++] = elt; + } + else if (counting == ending) + { + break; + } + } + ++counting; + } + } + + if (subsequence != NULL) + { + len = ending - starting; + /* If we could be sure that neither FUNCTION nor KEY modify + SEQUENCE, this wouldn't be necessary, since all the + elements of SUBSEQUENCE would definitely always be + reachable via SEQUENCE. */ + GCPRO1 (subsequence[0]); + gcpro1.nvars = len; + } + + if (need_accum) + { + accum = KEY (key, subsequence[len - 1]); + --len; + } + + for (ii = len; ii != 0;) + { + --ii; + accum = CALL2 (function, KEY (key, subsequence[ii]), accum); + } + + if (subsequence != NULL) + { + UNGCPRO; + } + } + } + + /* At this point, if ACCUM is unbound, SEQUENCE has no elements; we + need to return the result of calling FUNCTION with zero + arguments. */ + if (UNBOUNDP (accum)) + { + accum = IGNORE_MULTIPLE_VALUES (call0 (function)); + } + + return accum; +} + +/* This function is the implementation of fill_string_range() and + replace_string_range(); see the comments for those functions. */ +static Lisp_Object +replace_string_range_1 (Lisp_Object dest, Lisp_Object start, Lisp_Object end, + const Ibyte *source, const Ibyte *source_limit, + Lisp_Object item) +{ + Ibyte *destp = XSTRING_DATA (dest), *p = destp, + *pend = p + XSTRING_LENGTH (dest), *pcursor, item_buf[MAX_ICHAR_LEN]; + Bytecount prefix_bytecount, source_len = source_limit - source; + Charcount ii = 0, ending, len; + Charcount starting = BIGNUMP (start) ? MOST_POSITIVE_FIXNUM + 1 : XFIXNUM (start); + Elemcount delta; + + while (ii < starting && p < pend) + { + INC_IBYTEPTR (p); + ii++; + } + + pcursor = p; + + if (NILP (end)) + { + while (pcursor < pend) + { + INC_IBYTEPTR (pcursor); + ii++; + } + + ending = len = ii; + } + else + { + ending = BIGNUMP (end) ? MOST_POSITIVE_FIXNUM + 1 : XFIXNUM (end); + while (ii < ending && pcursor < pend) + { + INC_IBYTEPTR (pcursor); + ii++; + } + } + + if (pcursor == pend) + { + /* We have the length, check it for our callers. */ + check_sequence_range (dest, start, end, make_fixnum (ii)); + } + + if (!(p == pend || p == pcursor)) + { + prefix_bytecount = p - destp; + + if (!NILP (item)) + { + assert (source == NULL && source_limit == NULL); + source_len = set_itext_ichar (item_buf, XCHAR (item)); + delta = (source_len * (ending - starting)) - (pcursor - p); + } + else + { + assert (source != NULL && source_limit != NULL); + delta = source_len - (pcursor - p); + } + + if (delta) + { + resize_string (dest, prefix_bytecount, delta); + destp = XSTRING_DATA (dest); + pcursor = destp + prefix_bytecount + (pcursor - p); + p = destp + prefix_bytecount; + } + + if (CHARP (item)) + { + while (starting < ending) + { + memcpy (p, item_buf, source_len); + p += source_len; + starting++; + } + } + else + { + while (starting < ending && source < source_limit) + { + source_len = itext_copy_ichar (source, p); + p += source_len, source += source_len; + } + } + + init_string_ascii_begin (dest); + bump_string_modiff (dest); + sledgehammer_check_ascii_begin (dest); + } + + return dest; +} + +DEFUN ("replace", Freplace, 2, MANY, 0, /* +Replace the elements of SEQUENCE-ONE with the elements of SEQUENCE-TWO. + +SEQUENCE-ONE is destructively modified, and returned. Its length is not +changed. + +Keywords :start1 and :end1 specify a subsequence of SEQUENCE-ONE, and +:start2 and :end2 a subsequence of SEQUENCE-TWO. See `search' for more +information. + +arguments: (SEQUENCE-ONE SEQUENCE-TWO &key (START1 0) (END1 (length SEQUENCE-ONE)) (START2 0) (END2 (length SEQUENCE-TWO))) +*/ + (int nargs, Lisp_Object *args)) +{ + Lisp_Object sequence1 = args[0], sequence2 = args[1], + result = sequence1; + Elemcount starting1, ending1 = MOST_POSITIVE_FIXNUM + 1, starting2; + Elemcount ending2 = MOST_POSITIVE_FIXNUM + 1, counting = 0, startcounting; + Boolint sequence1_listp, sequence2_listp, + overwriting = EQ (sequence1, sequence2); + + PARSE_KEYWORDS (Freplace, nargs, args, 4, (start1, end1, start2, end2), + (start1 = start2 = Qzero)); + + CHECK_SEQUENCE (sequence1); + CHECK_LISP_WRITEABLE (sequence1); + + CHECK_SEQUENCE (sequence2); + + CHECK_NATNUM (start1); + starting1 = BIGNUMP (start1) ? MOST_POSITIVE_FIXNUM + 1 : XFIXNUM (start1); + CHECK_NATNUM (start2); + starting2 = BIGNUMP (start2) ? MOST_POSITIVE_FIXNUM + 1 : XFIXNUM (start2); + + if (!NILP (end1)) + { + CHECK_NATNUM (end1); + ending1 = BIGNUMP (end1) ? MOST_POSITIVE_FIXNUM + 1 : XFIXNUM (end1); + } + + if (!NILP (end2)) + { + CHECK_NATNUM (end2); + ending2 = BIGNUMP (end2) ? MOST_POSITIVE_FIXNUM + 1 : XFIXNUM (end2); + } + + sequence1_listp = LISTP (sequence1); + sequence2_listp = LISTP (sequence2); + + overwriting = overwriting && starting2 <= starting1; + + if (sequence1_listp && !ZEROP (start1)) + { + sequence1 = Fnthcdr (start1, sequence1); + + if (NILP (sequence1)) + { + check_sequence_range (args[0], start1, end1, Flength (args[0])); + /* Give up early here. */ + return result; + } + + ending1 -= starting1; + starting1 = 0; + } + + if (sequence2_listp && !ZEROP (start2)) + { + sequence2 = Fnthcdr (start2, sequence2); + + if (NILP (sequence2)) + { + check_sequence_range (args[1], start1, end1, Flength (args[1])); + /* Nothing available to replace sequence1's contents. */ + return result; + } + + ending2 -= starting2; + starting2 = 0; + } + + if (overwriting) + { + if (EQ (start1, start2)) + { + return result; + } + + /* Our ranges may overlap. Save the data that might be overwritten. */ + + if (CONSP (sequence2)) + { + Elemcount len = XFIXNUM (Flength (sequence2)); + Lisp_Object *subsequence + = alloca_array (Lisp_Object, min (ending2, len)); + Elemcount ii = 0; + + LIST_LOOP_2 (elt, sequence2) + { + if (counting == ending2) + { + break; + } + + subsequence[ii++] = elt; + counting++; + } + + check_sequence_range (sequence1, start1, end1, + /* The XFIXNUM (start2) is intentional here; we + called #'length after doing (nthcdr + start2 sequence2). */ + make_fixnum (XFIXNUM (start2) + len)); + check_sequence_range (sequence2, start2, end2, + make_fixnum (XFIXNUM (start2) + len)); + + while (starting1 < ending1 + && starting2 < ending2 && !NILP (sequence1)) + { + XSETCAR (sequence1, subsequence[starting2]); + sequence1 = XCDR (sequence1); + starting1++; + starting2++; + } + } + else if (STRINGP (sequence2)) + { + Ibyte *p = XSTRING_DATA (sequence2), + *pend = p + XSTRING_LENGTH (sequence2), *pcursor, + *staging; + Bytecount ii = 0; + + while (ii < starting2 && p < pend) + { + INC_IBYTEPTR (p); + ii++; + } + + pcursor = p; + + while (ii < ending2 && starting1 < ending1 && pcursor < pend) + { + INC_IBYTEPTR (pcursor); + starting1++; + ii++; + } + + if (pcursor == pend) + { + check_sequence_range (sequence1, start1, end1, make_fixnum (ii)); + check_sequence_range (sequence2, start2, end2, make_fixnum (ii)); + } + else + { + assert ((pcursor - p) > 0); + staging = alloca_ibytes (pcursor - p); + memcpy (staging, p, pcursor - p); + replace_string_range (result, start1, + make_fixnum (starting1), + staging, staging + (pcursor - p)); + } + } + else + { + Elemcount seq_len = XFIXNUM (Flength (sequence2)), ii = 0, + subseq_len = min (min (ending1 - starting1, seq_len - starting1), + min (ending2 - starting2, seq_len - starting2)); + Lisp_Object *subsequence = alloca_array (Lisp_Object, subseq_len); + + check_sequence_range (sequence1, start1, end1, make_fixnum (seq_len)); + check_sequence_range (sequence2, start2, end2, make_fixnum (seq_len)); + + while (starting2 < ending2 && ii < seq_len) + { + subsequence[ii] = Faref (sequence2, make_fixnum (starting2)); + ii++, starting2++; + } + + ii = 0; + + while (starting1 < ending1 && ii < seq_len) + { + Faset (sequence1, make_fixnum (starting1), subsequence[ii]); + ii++, starting1++; + } + } + } + else if (sequence1_listp && sequence2_listp) + { + Lisp_Object sequence1_tortoise = sequence1, + sequence2_tortoise = sequence2; + Elemcount shortest_len = 0; + + counting = startcounting = min (ending1, ending2); + + while (counting-- > 0 && !NILP (sequence1) && !NILP (sequence2)) + { + XSETCAR (sequence1, + CONSP (sequence2) ? XCAR (sequence2) + : Fcar (sequence2)); + sequence1 = CONSP (sequence1) ? XCDR (sequence1) + : Fcdr (sequence1); + sequence2 = CONSP (sequence2) ? XCDR (sequence2) + : Fcdr (sequence2); + + shortest_len++; + + if (startcounting - counting > CIRCULAR_LIST_SUSPICION_LENGTH) + { + if (counting & 1) + { + sequence1_tortoise = XCDR (sequence1_tortoise); + sequence2_tortoise = XCDR (sequence2_tortoise); + } + + if (EQ (sequence1, sequence1_tortoise)) + { + signal_circular_list_error (sequence1); + } + + if (EQ (sequence2, sequence2_tortoise)) + { + signal_circular_list_error (sequence2); + } + } + } + + if (NILP (sequence1)) + { + check_sequence_range (args[0], start1, end1, + make_fixnum (XFIXNUM (start1) + shortest_len)); + } + else if (NILP (sequence2)) + { + check_sequence_range (args[1], start2, end2, + make_fixnum (XFIXNUM (start2) + shortest_len)); + } + } + else if (sequence1_listp) + { + if (STRINGP (sequence2)) + { + Ibyte *s2_data = XSTRING_DATA (sequence2), + *s2_end = s2_data + XSTRING_LENGTH (sequence2); + Elemcount char_count = 0; + Lisp_Object character; + + while (char_count < starting2 && s2_data < s2_end) + { + INC_IBYTEPTR (s2_data); + char_count++; + } + + while (starting1 < ending1 && starting2 < ending2 + && s2_data < s2_end && !NILP (sequence1)) + { + character = make_char (itext_ichar (s2_data)); + CONSP (sequence1) ? + XSETCAR (sequence1, character) + : Fsetcar (sequence1, character); + sequence1 = XCDR (sequence1); + starting1++; + starting2++; + char_count++; + INC_IBYTEPTR (s2_data); + } + + if (NILP (sequence1)) + { + check_sequence_range (sequence1, start1, end1, + make_fixnum (XFIXNUM (start1) + starting1)); + } + + if (s2_data == s2_end) + { + check_sequence_range (sequence2, start2, end2, + make_fixnum (char_count)); + } + } + else + { + Elemcount len2 = XFIXNUM (Flength (sequence2)); + check_sequence_range (sequence2, start2, end2, make_fixnum (len2)); + + ending2 = min (ending2, len2); + while (starting2 < ending2 + && starting1 < ending1 && !NILP (sequence1)) + { + CHECK_CONS (sequence1); + XSETCAR (sequence1, Faref (sequence2, make_fixnum (starting2))); + sequence1 = XCDR (sequence1); + starting1++; + starting2++; + } + + if (NILP (sequence1)) + { + check_sequence_range (args[0], start1, end1, + make_fixnum (XFIXNUM (start1) + starting1)); + } + } + } + else if (sequence2_listp) + { + if (STRINGP (sequence1)) + { + Elemcount ii = 0, count, len = string_char_length (sequence1); + Ibyte *staging, *cursor; + Lisp_Object obj; + + check_sequence_range (sequence1, start1, end1, make_fixnum (len)); + ending1 = min (ending1, len); + count = ending1 - starting1; + staging = cursor = alloca_ibytes (count * MAX_ICHAR_LEN); + + while (ii < count && !NILP (sequence2)) + { + obj = CONSP (sequence2) ? XCAR (sequence2) + : Fcar (sequence2); + + CHECK_CHAR_COERCE_INT (obj); + cursor += set_itext_ichar (cursor, XCHAR (obj)); + ii++; + sequence2 = XCDR (sequence2); + } + + if (NILP (sequence2)) + { + check_sequence_range (sequence2, start2, end2, + make_fixnum (XFIXNUM (start2) + ii)); + } + + replace_string_range (result, start1, make_fixnum (XFIXNUM (start1) + ii), + staging, cursor); + } + else + { + Elemcount len = XFIXNUM (Flength (sequence1)); + + check_sequence_range (sequence1, start2, end1, make_fixnum (len)); + ending1 = min (ending2, min (ending1, len)); + + while (starting1 < ending1 && !NILP (sequence2)) + { + Faset (sequence1, make_fixnum (starting1), + CONSP (sequence2) ? XCAR (sequence2) + : Fcar (sequence2)); + sequence2 = XCDR (sequence2); + starting1++; + starting2++; + } + + if (NILP (sequence2)) + { + check_sequence_range (args[1], start2, end2, + make_fixnum (XFIXNUM (start2) + starting2)); + } + } + } + else + { + if (STRINGP (sequence1) && STRINGP (sequence2)) + { + Ibyte *p2 = XSTRING_DATA (sequence2), + *p2end = p2 + XSTRING_LENGTH (sequence2), *p2cursor; + Charcount ii = 0, len1 = string_char_length (sequence1); + + check_sequence_range (sequence1, start1, end1, make_fixnum (len1)); + + while (ii < starting2 && p2 < p2end) + { + INC_IBYTEPTR (p2); + ii++; + } + + p2cursor = p2; + ending1 = min (ending1, len1); + + while (ii < ending2 && starting1 < ending1 && p2cursor < p2end) + { + INC_IBYTEPTR (p2cursor); + ii++; + starting1++; + } + + if (p2cursor == p2end) + { + check_sequence_range (sequence2, start2, end2, make_fixnum (ii)); + } + + /* This isn't great; any error message won't necessarily reflect + the END1 that was supplied to #'replace. */ + replace_string_range (result, start1, make_fixnum (starting1), + p2, p2cursor); + } + else if (STRINGP (sequence1)) + { + Ibyte *staging, *cursor; + Elemcount count, len1 = string_char_length (sequence1); + Elemcount len2 = XFIXNUM (Flength (sequence2)), ii = 0; + Lisp_Object obj; + + check_sequence_range (sequence1, start1, end1, make_fixnum (len1)); + check_sequence_range (sequence2, start2, end2, make_fixnum (len2)); + + ending1 = min (ending1, len1); + ending2 = min (ending2, len2); + count = min (ending1 - starting1, ending2 - starting2); + staging = cursor = alloca_ibytes (count * MAX_ICHAR_LEN); + + ii = 0; + while (ii < count) + { + obj = Faref (sequence2, make_fixnum (starting2)); + + CHECK_CHAR_COERCE_INT (obj); + cursor += set_itext_ichar (cursor, XCHAR (obj)); + starting2++, ii++; + } + + replace_string_range (result, start1, + make_fixnum (XFIXNUM (start1) + count), + staging, cursor); + } + else if (STRINGP (sequence2)) + { + Ibyte *p2 = XSTRING_DATA (sequence2), + *p2end = p2 + XSTRING_LENGTH (sequence2); + Elemcount len1 = XFIXNUM (Flength (sequence1)), ii = 0; + + check_sequence_range (sequence1, start1, end1, make_fixnum (len1)); + ending1 = min (ending1, len1); + + while (ii < starting2 && p2 < p2end) + { + INC_IBYTEPTR (p2); + ii++; + } + + while (p2 < p2end && starting1 < ending1 && starting2 < ending2) + { + Faset (sequence1, make_fixnum (starting1), + make_char (itext_ichar (p2))); + INC_IBYTEPTR (p2); + starting1++; + starting2++; + ii++; + } + + if (p2 == p2end) + { + check_sequence_range (sequence2, start2, end2, make_fixnum (ii)); + } + } + else + { + Elemcount len1 = XFIXNUM (Flength (sequence1)), + len2 = XFIXNUM (Flength (sequence2)); + + check_sequence_range (sequence1, start1, end1, make_fixnum (len1)); + check_sequence_range (sequence2, start2, end2, make_fixnum (len2)); + + ending1 = min (ending1, len1); + ending2 = min (ending2, len2); + + while (starting1 < ending1 && starting2 < ending2) + { + Faset (sequence1, make_fixnum (starting1), + Faref (sequence2, make_fixnum (starting2))); + starting1++; + starting2++; + } + } + } + + return result; +} + +DEFUN ("nsubstitute", Fnsubstitute, 3, MANY, 0, /* +Substitute NEW for OLD in SEQUENCE. + +This is a destructive function; it reuses the storage of SEQUENCE whenever +possible. See `remove*' for the meaning of the keywords. + +arguments: (NEW OLD SEQUENCE &key (TEST #'eql) (KEY #'identity) (START 0) (END (length SEQUENCE)) FROM-END COUNT) +*/ + (int nargs, Lisp_Object *args)) +{ + Lisp_Object new_ = args[0], item = args[1], sequence = args[2]; + Lisp_Object object_, position0; + Elemcount starting = 0, ending = MOST_POSITIVE_FIXNUM, encountered = 0; + Elemcount len, ii = 0, counting = MOST_POSITIVE_FIXNUM, presenting = 0; + Boolint test_not_unboundp = 1; + check_test_func_t check_test = NULL; + + PARSE_KEYWORDS (Fnsubstitute, nargs, args, 9, + (test, if_, if_not, test_not, key, start, end, count, + from_end), (start = Qzero)); + + CHECK_SEQUENCE (sequence); + CHECK_NATNUM (start); + starting = BIGNUMP (start) ? 1 + MOST_POSITIVE_FIXNUM : XFIXNUM (start); + + if (!NILP (end)) + { + CHECK_NATNUM (end); + ending = BIGNUMP (end) ? 1 + MOST_POSITIVE_FIXNUM : XFIXNUM (end); + } + + if (!NILP (count)) + { + CHECK_INTEGER (count); + if (FIXNUMP (count)) + { + counting = XFIXNUM (count); + } +#ifdef HAVE_BIGNUM + else + { + counting = bignum_sign (XBIGNUM_DATA (count)) > 0 ? + 1 + MOST_POSITIVE_FIXNUM : -1 + MOST_NEGATIVE_FIXNUM; + } +#endif + + if (counting <= 0) + { + return sequence; + } + } + + check_test = get_check_test_function (item, &test, test_not, if_, if_not, + key, &test_not_unboundp); + + if (CONSP (sequence)) + { + if (!NILP (count) && !NILP (from_end)) + { + Lisp_Object present = count_with_tail (&object_, nargs - 1, args + 1, + Qnsubstitute); + + if (ZEROP (present)) + { + return sequence; + } + + presenting = XFIXNUM (present); + presenting = presenting <= counting ? 0 : presenting - counting; + } + + { + GC_EXTERNAL_LIST_LOOP_3 (elt, sequence, tail) + { + if (!(ii < ending)) + { + break; + } + + if (starting <= ii && + check_test (test, key, item, elt) == test_not_unboundp + && (presenting ? encountered++ >= presenting + : encountered++ < counting)) + { + CHECK_LISP_WRITEABLE (tail); + XSETCAR (tail, new_); + } + else if (!presenting && encountered >= counting) + { + break; + } + + ii++; + } + END_GC_EXTERNAL_LIST_LOOP (elt); + } + + if ((ii < starting || (ii < ending && !NILP (end))) + && encountered < counting) + { + check_sequence_range (args[0], start, end, Flength (args[0])); + } + } + else if (STRINGP (sequence)) + { + Ibyte *staging, new_bytes[MAX_ICHAR_LEN], *staging_cursor; + Ibyte *startp = XSTRING_DATA (sequence), *cursor = startp; + Bytecount cursor_offset = 0, byte_len = XSTRING_LENGTH (sequence); + Bytecount new_len; + Lisp_Object character; + + CHECK_CHAR_COERCE_INT (new_); + + new_len = set_itext_ichar (new_bytes, XCHAR (new_)); + + /* Worst case scenario; new char is four octets long, all the old ones + were one octet long, all the old ones match. */ + staging = alloca_ibytes (XSTRING_LENGTH (sequence) * new_len); + staging_cursor = staging; + + if (!NILP (count) && !NILP (from_end)) + { + Lisp_Object present = count_with_tail (&character, nargs - 1, + args + 1, Qnsubstitute); + + if (ZEROP (present)) + { + return sequence; + } + + presenting = XFIXNUM (present); + + /* If there are fewer items in the string than we have + permission to change, we don't need to differentiate + between the :from-end nil and :from-end t + cases. Otherwise, presenting is the number of matching + items we need to ignore before we start to change. */ + presenting = presenting <= counting ? 0 : presenting - counting; + } + + ii = 0; + while (cursor_offset < byte_len && ii < ending) + { + if (ii >= starting) + { + character = make_char (itext_ichar (cursor)); + + if ((check_test (test, key, item, character) + == test_not_unboundp) + && (presenting ? encountered++ >= presenting : + encountered++ < counting)) + { + staging_cursor + += itext_copy_ichar (new_bytes, staging_cursor); + } + else + { + staging_cursor + += itext_copy_ichar (cursor, staging_cursor); + } + + startp = XSTRING_DATA (sequence); + cursor = startp + cursor_offset; + + if (byte_len != XSTRING_LENGTH (sequence) + || !valid_ibyteptr_p (cursor)) + { + mapping_interaction_error (Qnsubstitute, sequence); + } + } + else + { + staging_cursor += itext_copy_ichar (cursor, staging_cursor); + } + + INC_IBYTEPTR (cursor); + cursor_offset = cursor - startp; + ii++; + } + + if (ii < starting || (ii < ending && !NILP (end))) + { + check_sequence_range (sequence, start, end, Flength (sequence)); + } + + if (0 != encountered) + { + CHECK_LISP_WRITEABLE (sequence); + replace_string_range (sequence, Qzero, make_fixnum (ii), + staging, staging_cursor); + } + } + else + { + Elemcount positioning; + Lisp_Object object = Qnil; + + len = XFIXNUM (Flength (sequence)); + check_sequence_range (sequence, start, end, make_fixnum (len)); + + position0 = position (&object, item, sequence, check_test, + test_not_unboundp, test, key, start, end, from_end, + Qnil, Qnsubstitute); + + if (NILP (position0)) + { + return sequence; + } + + positioning = XFIXNUM (position0); + ending = min (len, ending); + + Faset (sequence, position0, new_); + encountered = 1; + + if (NILP (from_end)) + { + for (ii = positioning + 1; ii < ending; ii++) + { + object_ = Faref (sequence, make_fixnum (ii)); + + if (check_test (test, key, item, object_) == test_not_unboundp + && encountered++ < counting) + { + Faset (sequence, make_fixnum (ii), new_); + } + else if (encountered == counting) + { + break; + } + } + } + else + { + for (ii = positioning - 1; ii >= starting; ii--) + { + object_ = Faref (sequence, make_fixnum (ii)); + + if (check_test (test, key, item, object_) == test_not_unboundp + && encountered++ < counting) + { + Faset (sequence, make_fixnum (ii), new_); + } + else if (encountered == counting) + { + break; + } + } + } + } + + return sequence; +} + +DEFUN ("substitute", Fsubstitute, 3, MANY, 0, /* +Substitute NEW for OLD in SEQUENCE. + +This is a non-destructive function; it makes a copy of SEQUENCE if necessary +to avoid corrupting the original SEQUENCE. + +See `remove*' for the meaning of the keywords. + +arguments: (NEW OLD SEQUENCE &key (TEST #'eql) (KEY #'identity) (START 0) (END (length SEQUENCE)) COUNT) +*/ + (int nargs, Lisp_Object *args)) +{ + Lisp_Object new_ = args[0], item = args[1], sequence = args[2], tail = Qnil; + Lisp_Object result = Qnil, result_tail = Qnil; + Lisp_Object object, position0, matched_count; + Elemcount starting = 0, ending = MOST_POSITIVE_FIXNUM, encountered = 0; + Elemcount ii = 0, counting = MOST_POSITIVE_FIXNUM, presenting = 0; + Boolint test_not_unboundp = 1; + check_test_func_t check_test = NULL; + struct gcpro gcpro1; + + PARSE_KEYWORDS (Fsubstitute, nargs, args, 9, + (test, if_, if_not, test_not, key, start, end, count, + from_end), (start = Qzero, count = Qunbound)); + + CHECK_SEQUENCE (sequence); + + CHECK_NATNUM (start); + starting = BIGNUMP (start) ? 1 + MOST_POSITIVE_FIXNUM : XFIXNUM (start); + + if (!NILP (end)) + { + CHECK_NATNUM (end); + ending = BIGNUMP (end) ? 1 + MOST_POSITIVE_FIXNUM : XFIXNUM (end); + } + + check_test = get_check_test_function (item, &test, test_not, if_, if_not, + key, &test_not_unboundp); + + if (!UNBOUNDP (count)) + { + if (!NILP (count)) + { + CHECK_INTEGER (count); + if (FIXNUMP (count)) + { + counting = XFIXNUM (count); + } +#ifdef HAVE_BIGNUM + else + { + counting = bignum_sign (XBIGNUM_DATA (count)) > 0 ? + 1 + MOST_POSITIVE_FIXNUM : -1 + MOST_NEGATIVE_FIXNUM; + } +#endif + + if (counting <= 0) + { + return sequence; + } + } + } + + if (!CONSP (sequence)) + { + position0 = position (&object, item, sequence, check_test, + test_not_unboundp, test, key, start, end, from_end, + Qnil, Qsubstitute); + + if (NILP (position0)) + { + return sequence; + } + else + { + args[2] = Fcopy_sequence (sequence); + return Fnsubstitute (nargs, args); + } + } + + matched_count = count_with_tail (&tail, nargs - 1, args + 1, Qsubstitute); + + if (ZEROP (matched_count)) + { + return sequence; + } + + if (!NILP (count) && !NILP (from_end)) + { + presenting = XFIXNUM (matched_count); + presenting = presenting <= counting ? 0 : presenting - counting; + } + + GCPRO1 (result); + { + GC_EXTERNAL_LIST_LOOP_3 (elt, sequence, tailing) + { + if (EQ (tail, tailing)) + { + XUNGCPRO (elt); + UNGCPRO; + + if (NILP (result)) + { + return XCDR (tail); + } + + XSETCDR (result_tail, XCDR (tail)); + return result; + } + else if (starting <= ii && ii < ending && + (check_test (test, key, item, elt) == test_not_unboundp) + && (presenting ? encountered++ >= presenting + : encountered++ < counting)) + { + if (NILP (result)) + { + result = result_tail = Fcons (new_, Qnil); + } + else + { + XSETCDR (result_tail, Fcons (new_, Qnil)); + result_tail = XCDR (result_tail); + } + } + else if (NILP (result)) + { + result = result_tail = Fcons (elt, Qnil); + } + else + { + XSETCDR (result_tail, Fcons (elt, Qnil)); + result_tail = XCDR (result_tail); + } + + if (ii == ending) + { + break; + } + + ii++; + } + END_GC_EXTERNAL_LIST_LOOP (elt); + } + UNGCPRO; + + if (ii < starting || (ii < ending && !NILP (end))) + { + check_sequence_range (args[0], start, end, Flength (args[0])); + } + + return result; +} + +static Lisp_Object +subst (Lisp_Object new_, Lisp_Object old, Lisp_Object tree, int depth) +{ + if (depth + lisp_eval_depth > max_lisp_eval_depth) + { + stack_overflow ("Stack overflow in subst", tree); + } + + if (EQ (tree, old)) + { + return new_; + } + else if (CONSP (tree)) + { + Lisp_Object aa = subst (new_, old, XCAR (tree), depth + 1); + Lisp_Object dd = subst (new_, old, XCDR (tree), depth + 1); + + if (EQ (aa, XCAR (tree)) && EQ (dd, XCDR (tree))) + { + return tree; + } + else + { + return Fcons (aa, dd); + } + } + else + { + return tree; + } +} + +static Lisp_Object +sublis (Lisp_Object alist, Lisp_Object tree, + check_test_func_t check_test, Boolint test_not_unboundp, + Lisp_Object test, Lisp_Object key, int depth) +{ + Lisp_Object keyed = KEY (key, tree), aa, dd; + + if (depth + lisp_eval_depth > max_lisp_eval_depth) + { + stack_overflow ("Stack overflow in sublis", tree); + } + + { + GC_EXTERNAL_LIST_LOOP_2 (elt, alist) + { + if (CONSP (elt) && + check_test (test, key, XCAR (elt), keyed) == test_not_unboundp) + { + XUNGCPRO (elt); + return XCDR (elt); + } + } + END_GC_EXTERNAL_LIST_LOOP (elt); + } + + if (!CONSP (tree)) + { + return tree; + } + + aa = sublis (alist, XCAR (tree), check_test, test_not_unboundp, test, key, + depth + 1); + dd = sublis (alist, XCDR (tree), check_test, test_not_unboundp, test, key, + depth + 1); + + if (EQ (aa, XCAR (tree)) && EQ (dd, XCDR (tree))) + { + return tree; + } + + return Fcons (aa, dd); +} + +DEFUN ("sublis", Fsublis, 2, MANY, 0, /* +Perform substitutions indicated by ALIST in TREE (non-destructively). +Return a copy of TREE with all matching elements replaced. + +See `member*' for the meaning of :test, :test-not and :key. + +arguments: (ALIST TREE &key (TEST #'eql) (KEY #'identity) TEST-NOT) +*/ + (int nargs, Lisp_Object *args)) +{ + Lisp_Object alist = args[0], tree = args[1]; + Boolint test_not_unboundp = 1; + check_test_func_t check_test = NULL; + + PARSE_KEYWORDS (Fsublis, nargs, args, 5, (test, if_, test_not, if_not, key), + (key = Qidentity)); + + if (NILP (key)) + { + key = Qidentity; + } + + get_check_match_function (&test, test_not, if_, if_not, + /* sublis() is going to apply the key, don't ask + for a match function that will do it for + us. */ + Qidentity, &test_not_unboundp, &check_test); + + if (CONSP (alist) && NILP (XCDR (alist)) && CONSP (XCAR (alist)) + && EQ (key, Qidentity) && 1 == test_not_unboundp + && (check_eq_nokey == check_test || + (check_eql_nokey == check_test && + !NON_FIXNUM_NUMBER_P (XCAR (XCAR (alist)))))) + { + /* #'subst with #'eq is very cheap indeed; call it. */ + return subst (XCDR (XCAR (alist)), XCAR (XCAR (alist)), tree, 0); + } + + return sublis (alist, tree, check_test, test_not_unboundp, test, key, 0); +} + +static Lisp_Object +nsublis (Lisp_Object alist, Lisp_Object tree, + check_test_func_t check_test, + Boolint test_not_unboundp, + Lisp_Object test, Lisp_Object key, int depth) +{ + Lisp_Object tree_saved = tree, tortoise = tree, keyed = Qnil; + struct gcpro gcpro1, gcpro2; + int count = 0; + + if (depth + lisp_eval_depth > max_lisp_eval_depth) + { + stack_overflow ("Stack overflow in nsublis", tree); + } + + GCPRO2 (tree_saved, keyed); + + while (CONSP (tree)) + { + Boolint replaced = 0; + keyed = KEY (key, XCAR (tree)); + + { + GC_EXTERNAL_LIST_LOOP_2 (elt, alist) + { + if (CONSP (elt) && + check_test (test, key, XCAR (elt), keyed) == test_not_unboundp) + { + CHECK_LISP_WRITEABLE (tree); + /* See comment in sublis() on using elt_cdr. */ + XSETCAR (tree, XCDR (elt)); + replaced = 1; + break; + } + } + END_GC_EXTERNAL_LIST_LOOP (elt); + } + + if (!replaced) + { + if (CONSP (XCAR (tree))) + { + nsublis (alist, XCAR (tree), check_test, test_not_unboundp, + test, key, depth + 1); + } + } + + keyed = KEY (key, XCDR (tree)); + replaced = 0; + + { + GC_EXTERNAL_LIST_LOOP_2 (elt, alist) + { + if (CONSP (elt) && + check_test (test, key, XCAR (elt), keyed) == test_not_unboundp) + { + CHECK_LISP_WRITEABLE (tree); + XSETCDR (tree, XCDR (elt)); + tree = Qnil; + break; + } + } + END_GC_EXTERNAL_LIST_LOOP (elt); + } + + if (!NILP (tree)) + { + tree = XCDR (tree); + } + + if (++count > CIRCULAR_LIST_SUSPICION_LENGTH) + { + if (count & 1) + { + tortoise = XCDR (tortoise); + } + + if (EQ (tortoise, tree)) + { + signal_circular_list_error (tree); + } + } + } + + RETURN_UNGCPRO (tree_saved); +} + +DEFUN ("nsublis", Fnsublis, 2, MANY, 0, /* +Perform substitutions indicated by ALIST in TREE (destructively). +Any matching element of TREE is changed via a call to `setcar'. + +See `member*' for the meaning of :test, :test-not and :key. + +arguments: (ALIST TREE &key (TEST #'eql) (KEY #'identity) TEST-NOT) +*/ + (int nargs, Lisp_Object *args)) +{ + Lisp_Object alist = args[0], tree = args[1], tailed = Qnil, keyed = Qnil; + Boolint test_not_unboundp = 1; + check_test_func_t check_test = NULL; + struct gcpro gcpro1, gcpro2; + + PARSE_KEYWORDS (Fnsublis, nargs, args, 5, (test, if_, test_not, if_not, key), + (key = Qidentity)); + + if (NILP (key)) + { + key = Qidentity; + } + + get_check_match_function (&test, test_not, if_, if_not, + /* nsublis() is going to apply the key, don't ask + for a match function that will do it for + us. */ + Qidentity, &test_not_unboundp, &check_test); + + GCPRO2 (tailed, keyed); + + keyed = KEY (key, tree); + + { + /* nsublis() won't attempt to replace a cons handed to it, do that + ourselves. */ + GC_EXTERNAL_LIST_LOOP_2 (elt, alist) + { + if (CONSP (elt) && + check_test (test, key, XCAR (elt), keyed) == test_not_unboundp) + { + XUNGCPRO (elt); + return XCDR (elt); + } + } + END_GC_EXTERNAL_LIST_LOOP (elt); + } + + UNGCPRO; + + return nsublis (alist, tree, check_test, test_not_unboundp, test, key, 0); +} + +DEFUN ("subst", Fsubst, 3, MANY, 0, /* +Substitute NEW for OLD everywhere in TREE (non-destructively). + +Return a copy of TREE with all elements `eql' to OLD replaced by NEW. + +See `member*' for the meaning of :test, :test-not and :key. + +arguments: (NEW OLD TREE &key (TEST #'eql) (KEY #'identity) TEST-NOT) +*/ + (int nargs, Lisp_Object *args)) +{ + Lisp_Object result, alist = noseeum_cons (noseeum_cons (args[1], args[0]), + Qnil); + args[1] = alist; + result = Fsublis (nargs - 1, args + 1); + free_cons (XCAR (alist)); + free_cons (alist); + + return result; +} + +DEFUN ("nsubst", Fnsubst, 3, MANY, 0, /* +Substitute NEW for OLD everywhere in TREE (destructively). + +Any element of TREE which is `eql' to OLD is changed to NEW (via a call to +`setcar'). + +See `member*' for the meaning of the keywords. The keyword +:descend-structures, not specified by Common Lisp, allows callers to specify +that non-cons objects (vectors and range tables, among others) should also +undergo substitution. + +arguments: (NEW OLD TREE &key (TEST #'eql) (KEY #'identity) TEST-NOT DESCEND-STRUCTURES) +*/ + (int nargs, Lisp_Object *args)) +{ + Lisp_Object new_ = args[0], old = args[1], tree = args[2], result, alist; + Boolint test_not_unboundp = 1; + check_test_func_t check_test = NULL; + + PARSE_KEYWORDS (Fnsubst, nargs, args, 6, (test, if_, test_not, if_not, key, + descend_structures), NULL); + if (!NILP (descend_structures)) + { + check_test = get_check_test_function (old, &test, test_not, if_, if_not, + key, &test_not_unboundp); + + return nsubst_structures (new_, old, tree, check_test, test_not_unboundp, + test, key); + + } + + alist = noseeum_cons (noseeum_cons (old, new_), Qnil); + args[1] = alist; + result = Fnsublis (nargs - 1, args + 1); + free_cons (XCAR (alist)); + free_cons (alist); + + return result; +} + +static Boolint +tree_equal (Lisp_Object tree1, Lisp_Object tree2, + check_test_func_t check_test, Boolint test_not_unboundp, + Lisp_Object test, Lisp_Object key, int depth) +{ + Lisp_Object tortoise1 = tree1, tortoise2 = tree2; + struct gcpro gcpro1, gcpro2; + int count = 0; + Boolint result; + + if (depth + lisp_eval_depth > max_lisp_eval_depth) + { + stack_overflow ("Stack overflow in tree-equal", tree1); + } + + GCPRO2 (tree1, tree2); + + while (CONSP (tree1) && CONSP (tree2) + && tree_equal (XCAR (tree1), XCAR (tree2), check_test, + test_not_unboundp, test, key, depth + 1)) + { + tree1 = XCDR (tree1); + tree2 = XCDR (tree2); + + if (++count > CIRCULAR_LIST_SUSPICION_LENGTH) + { + if (count & 1) + { + tortoise1 = XCDR (tortoise1); + tortoise2 = XCDR (tortoise2); + } + + if (EQ (tortoise1, tree1)) + { + signal_circular_list_error (tree1); + } + + if (EQ (tortoise2, tree2)) + { + signal_circular_list_error (tree2); + } + } + } + + if (CONSP (tree1) || CONSP (tree2)) + { + UNGCPRO; + return 0; + } + + result = check_test (test, key, tree1, tree2) == test_not_unboundp; + UNGCPRO; + + return result; +} + +DEFUN ("tree-equal", Ftree_equal, 2, MANY, 0, /* +Return t if TREE1 and TREE2 have `eql' leaves. + +Atoms are compared by `eql', unless another test is specified using +:test; cons cells are compared recursively. + +See `union' for the meaning of :test, :test-not and :key. + +arguments: (TREE1 TREE2 &key (TEST #'eql) (KEY #'identity) TEST-NOT) +*/ + (int nargs, Lisp_Object *args)) +{ + Lisp_Object tree1 = args[0], tree2 = args[1]; + Boolint test_not_unboundp = 1; + check_test_func_t check_test = NULL; + + PARSE_KEYWORDS (Ftree_equal, nargs, args, 3, (test, key, test_not), + (key = Qidentity)); + + get_check_match_function (&test, test_not, Qnil, Qnil, key, + &test_not_unboundp, &check_test); + + return tree_equal (tree1, tree2, check_test, test_not_unboundp, test, key, + 0) ? Qt : Qnil; +} + +static Lisp_Object +mismatch_from_end (Lisp_Object sequence1, Lisp_Object start1, Lisp_Object end1, + Lisp_Object sequence2, Lisp_Object start2, Lisp_Object end2, + check_test_func_t check_match, Boolint test_not_unboundp, + Lisp_Object test, Lisp_Object key, + Boolint UNUSED (return_sequence1_index)) +{ + Elemcount sequence1_len = XFIXNUM (Flength (sequence1)); + Elemcount sequence2_len = XFIXNUM (Flength (sequence2)), ii = 0; + Elemcount starting1, ending1, starting2, ending2; + Lisp_Object *sequence1_storage = NULL, *sequence2_storage = NULL; + struct gcpro gcpro1, gcpro2; + + check_sequence_range (sequence1, start1, end1, make_fixnum (sequence1_len)); + starting1 = XFIXNUM (start1); + ending1 = FIXNUMP (end1) ? XFIXNUM (end1) : 1 + MOST_POSITIVE_FIXNUM; + ending1 = min (ending1, sequence1_len); + + check_sequence_range (sequence2, start2, end2, make_fixnum (sequence2_len)); + starting2 = XFIXNUM (start2); + ending2 = FIXNUMP (end2) ? XFIXNUM (end2) : 1 + MOST_POSITIVE_FIXNUM; + ending2 = min (ending2, sequence2_len); + + if (LISTP (sequence1)) + { + Lisp_Object *saving; + sequence1_storage = saving + = alloca_array (Lisp_Object, ending1 - starting1); + + { + EXTERNAL_LIST_LOOP_2 (elt, sequence1) + { + if (starting1 <= ii && ii < ending1) + { + *saving++ = elt; + } + else if (ii == ending1) + { + break; + } + + ++ii; + } + } + } + else if (STRINGP (sequence1)) + { + const Ibyte *cursor = string_char_addr (sequence1, starting1); + + STRING_DATA_TO_OBJECT_ARRAY (cursor, sequence1_storage, ii, + ending1 - starting1); + + } + else if (BIT_VECTORP (sequence1)) + { + Lisp_Bit_Vector *vv = XBIT_VECTOR (sequence1); + sequence1_storage = alloca_array (Lisp_Object, ending1 - starting1); + for (ii = starting1; ii < ending1; ++ii) + { + sequence1_storage[ii - starting1] + = make_fixnum (bit_vector_bit (vv, ii)); + } + } + else + { + sequence1_storage = XVECTOR_DATA (sequence1) + starting1; + } + + ii = 0; + + if (LISTP (sequence2)) + { + Lisp_Object *saving; + sequence2_storage = saving + = alloca_array (Lisp_Object, ending2 - starting2); + + { + EXTERNAL_LIST_LOOP_2 (elt, sequence2) + { + if (starting2 <= ii && ii < ending2) + { + *saving++ = elt; + } + else if (ii == ending2) + { + break; + } + + ++ii; + } + } + } + else if (STRINGP (sequence2)) + { + const Ibyte *cursor = string_char_addr (sequence2, starting2); + + STRING_DATA_TO_OBJECT_ARRAY (cursor, sequence2_storage, ii, + ending2 - starting2); + + } + else if (BIT_VECTORP (sequence2)) + { + Lisp_Bit_Vector *vv = XBIT_VECTOR (sequence2); + sequence2_storage = alloca_array (Lisp_Object, ending2 - starting2); + for (ii = starting2; ii < ending2; ++ii) + { + sequence2_storage[ii - starting2] + = make_fixnum (bit_vector_bit (vv, ii)); + } + } + else + { + sequence2_storage = XVECTOR_DATA (sequence2) + starting2; + } + + GCPRO2 (sequence1_storage[0], sequence2_storage[0]); + gcpro1.nvars = ending1 - starting1; + gcpro2.nvars = ending2 - starting2; + + while (ending1 > starting1 && ending2 > starting2) + { + --ending1; + --ending2; + + if (check_match (test, key, sequence1_storage[ending1 - starting1], + sequence2_storage[ending2 - starting2]) + != test_not_unboundp) + { + UNGCPRO; + return make_integer (ending1 + 1); + } + } + + UNGCPRO; + + if (ending1 > starting1 || ending2 > starting2) + { + return make_integer (ending1); + } + + return Qnil; +} + +static Lisp_Object +mismatch_list_list (Lisp_Object sequence1, Lisp_Object start1, Lisp_Object end1, + Lisp_Object sequence2, Lisp_Object start2, Lisp_Object end2, + check_test_func_t check_match, Boolint test_not_unboundp, + Lisp_Object test, Lisp_Object key, + Boolint UNUSED (return_list_index)) +{ + Lisp_Object sequence1_tortoise = sequence1, sequence2_tortoise = sequence2; + Lisp_Object orig_sequence1 = sequence1, orig_sequence2 = sequence2; + Elemcount ending1 = MOST_POSITIVE_FIXNUM, ending2 = MOST_POSITIVE_FIXNUM; + Elemcount starting1, starting2, counting, startcounting; + Elemcount shortest_len = 0; + struct gcpro gcpro1, gcpro2, gcpro3, gcpro4; + + starting1 = FIXNUMP (start1) ? XFIXNUM (start1) : 1 + MOST_POSITIVE_FIXNUM; + starting2 = FIXNUMP (start2) ? XFIXNUM (start2) : 1 + MOST_POSITIVE_FIXNUM; + + if (!NILP (end1)) + { + ending1 = FIXNUMP (end1) ? XFIXNUM (end1) : 1 + MOST_POSITIVE_FIXNUM; + } + + if (!NILP (end2)) + { + ending2 = FIXNUMP (end2) ? XFIXNUM (end2) : 1 + MOST_POSITIVE_FIXNUM; + } + + if (!ZEROP (start1)) + { + sequence1 = Fnthcdr (start1, sequence1); + + if (NILP (sequence1)) + { + check_sequence_range (sequence1_tortoise, start1, end1, + Flength (sequence1_tortoise)); + /* Give up early here. */ + return Qnil; + } + + ending1 -= starting1; + starting1 = 0; + sequence1_tortoise = sequence1; + } + + if (!ZEROP (start2)) + { + sequence2 = Fnthcdr (start2, sequence2); + + if (NILP (sequence2)) + { + check_sequence_range (sequence2_tortoise, start2, end2, + Flength (sequence2_tortoise)); + return Qnil; + } + + ending2 -= starting2; + starting2 = 0; + sequence2_tortoise = sequence2; + } + + GCPRO4 (sequence1, sequence2, sequence1_tortoise, sequence2_tortoise); + + counting = startcounting = min (ending1, ending2); + + while (counting-- > 0 && !NILP (sequence1) && !NILP (sequence2)) + { + if (check_match (test, key, + CONSP (sequence1) ? XCAR (sequence1) + : Fcar (sequence1), + CONSP (sequence2) ? XCAR (sequence2) + : Fcar (sequence2) ) != test_not_unboundp) + { + UNGCPRO; + return make_integer (XFIXNUM (start1) + shortest_len); + } + + sequence1 = CONSP (sequence1) ? XCDR (sequence1) : Fcdr (sequence1); + sequence2 = CONSP (sequence2) ? XCDR (sequence2) : Fcdr (sequence2); + + shortest_len++; + + if (startcounting - counting > CIRCULAR_LIST_SUSPICION_LENGTH) + { + if (counting & 1) + { + sequence1_tortoise = XCDR (sequence1_tortoise); + sequence2_tortoise = XCDR (sequence2_tortoise); + } + + if (EQ (sequence1, sequence1_tortoise)) + { + signal_circular_list_error (sequence1); + } + + if (EQ (sequence2, sequence2_tortoise)) + { + signal_circular_list_error (sequence2); + } + } + } + + UNGCPRO; + + if (NILP (sequence1)) + { + Lisp_Object args[] = { start1, make_fixnum (shortest_len) }; + check_sequence_range (orig_sequence1, start1, end1, + Fplus (countof (args), args)); + } + + if (NILP (sequence2)) + { + Lisp_Object args[] = { start2, make_fixnum (shortest_len) }; + check_sequence_range (orig_sequence2, start2, end2, + Fplus (countof (args), args)); + } + + if ((!NILP (end1) && shortest_len != ending1 - starting1) || + (!NILP (end2) && shortest_len != ending2 - starting2)) + { + return make_integer (XFIXNUM (start1) + shortest_len); + } + + if ((NILP (end1) && CONSP (sequence1)) || (NILP (end2) && CONSP (sequence2))) + { + return make_integer (XFIXNUM (start1) + shortest_len); + } + + return Qnil; +} + +static Lisp_Object +mismatch_list_string (Lisp_Object list, Lisp_Object list_start, + Lisp_Object list_end, + Lisp_Object string, Lisp_Object string_start, + Lisp_Object string_end, + check_test_func_t check_match, + Boolint test_not_unboundp, + Lisp_Object test, Lisp_Object key, + Boolint return_list_index) +{ + Ibyte *string_data = XSTRING_DATA (string), *startp = string_data; + Bytecount string_offset = 0, string_len = XSTRING_LENGTH (string); + Elemcount char_count = 0, list_starting, list_ending; + Elemcount string_starting, string_ending; + Lisp_Object character, orig_list = list; + struct gcpro gcpro1; + + list_ending = FIXNUMP (list_end) ? XFIXNUM (list_end) : 1 + MOST_POSITIVE_FIXNUM; + list_starting = FIXNUMP (list_start) ? XFIXNUM (list_start) : 1 + MOST_POSITIVE_FIXNUM; + + string_ending = FIXNUMP (string_end) ? XFIXNUM (string_end) : 1 + MOST_POSITIVE_FIXNUM; + string_starting + = FIXNUMP (string_start) ? XFIXNUM (string_start) : 1 + MOST_POSITIVE_FIXNUM; + + while (char_count < string_starting && string_offset < string_len) + { + INC_IBYTEPTR (string_data); + string_offset = string_data - startp; + char_count++; + } + + if (!ZEROP (list_start)) + { + list = Fnthcdr (list_start, list); + if (NILP (list)) + { + check_sequence_range (orig_list, list_start, list_end, + Flength (orig_list)); + return Qnil; + } + + list_ending -= list_starting; + list_starting = 0; + } + + GCPRO1 (list); + + while (list_starting < list_ending && string_starting < string_ending + && string_offset < string_len && !NILP (list)) + { + character = make_char (itext_ichar (string_data)); + + if (return_list_index) + { + if (check_match (test, key, CONSP (list) ? XCAR (list) : Fcar (list), + character) + != test_not_unboundp) + { + UNGCPRO; + return make_integer (XFIXNUM (list_start) + char_count); + } + } + else + { + if (check_match (test, key, character, + CONSP (list) ? XCAR (list) : Fcar (list)) + != test_not_unboundp) + { + UNGCPRO; + return make_integer (char_count); + } + } + + list = CONSP (list) ? XCDR (list) : Fcdr (list); + + startp = XSTRING_DATA (string); + string_data = startp + string_offset; + if (string_len != XSTRING_LENGTH (string) + || !valid_ibyteptr_p (string_data)) + { + mapping_interaction_error (Qmismatch, string); + } + + list_starting++; + string_starting++; + char_count++; + INC_IBYTEPTR (string_data); + string_offset = string_data - startp; + } + + UNGCPRO; + + if (NILP (list)) + { + Lisp_Object args[] = { list_start, make_fixnum (char_count) }; + check_sequence_range (orig_list, list_start, list_end, + Fplus (countof (args), args)); + } + + if (string_data == XSTRING_DATA (string) + XSTRING_LENGTH (string)) + { + check_sequence_range (string, string_start, string_end, + make_fixnum (char_count)); + } + + if ((NILP (string_end) ? + string_offset < string_len : string_starting < string_ending) || + (NILP (list_end) ? !NILP (list) : list_starting < list_ending)) + { + return make_integer (return_list_index ? XFIXNUM (list_start) + char_count : + char_count); + } + + return Qnil; +} + +static Lisp_Object +mismatch_list_array (Lisp_Object list, Lisp_Object list_start, + Lisp_Object list_end, + Lisp_Object array, Lisp_Object array_start, + Lisp_Object array_end, + check_test_func_t check_match, + Boolint test_not_unboundp, + Lisp_Object test, Lisp_Object key, + Boolint return_list_index) +{ + Elemcount ii = 0, list_starting, list_ending; + Elemcount array_starting, array_ending, array_len; + Lisp_Object orig_list = list; + struct gcpro gcpro1; + + list_ending = FIXNUMP (list_end) ? XFIXNUM (list_end) : 1 + MOST_POSITIVE_FIXNUM; + list_starting = FIXNUMP (list_start) ? XFIXNUM (list_start) : 1 + MOST_POSITIVE_FIXNUM; + + array_ending = FIXNUMP (array_end) ? XFIXNUM (array_end) : 1 + MOST_POSITIVE_FIXNUM; + array_starting = FIXNUMP (array_start) ? XFIXNUM (array_start) : 1 + MOST_POSITIVE_FIXNUM; + array_len = XFIXNUM (Flength (array)); + + array_ending = min (array_ending, array_len); + + check_sequence_range (array, array_start, array_end, make_fixnum (array_len)); + + if (!ZEROP (list_start)) + { + list = Fnthcdr (list_start, list); + if (NILP (list)) + { + check_sequence_range (orig_list, list_start, list_end, + Flength (orig_list)); + return Qnil; + } + + list_ending -= list_starting; + list_starting = 0; + } + + GCPRO1 (list); + + while (list_starting < list_ending && array_starting < array_ending + && !NILP (list)) + { + if (return_list_index) + { + if (check_match (test, key, CONSP (list) ? XCAR (list) : Fcar (list), + Faref (array, make_fixnum (array_starting))) + != test_not_unboundp) + { + UNGCPRO; + return make_integer (XFIXNUM (list_start) + ii); + } + } + else + { + if (check_match (test, key, Faref (array, make_fixnum (array_starting)), + CONSP (list) ? XCAR (list) : Fcar (list)) + != test_not_unboundp) + { + UNGCPRO; + return make_integer (array_starting); + } + } + + list = CONSP (list) ? XCDR (list) : Fcdr (list); + list_starting++; + array_starting++; + ii++; + } + + UNGCPRO; + + if (NILP (list)) + { + Lisp_Object args[] = { list_start, make_fixnum (ii) }; + check_sequence_range (orig_list, list_start, list_end, + Fplus (countof (args), args)); + } + + if (array_starting < array_ending || + (NILP (list_end) ? !NILP (list) : list_starting < list_ending)) + { + return make_integer (return_list_index ? XFIXNUM (list_start) + ii : + array_starting); + } + + return Qnil; +} + +static Lisp_Object +mismatch_string_array (Lisp_Object string, Lisp_Object string_start, + Lisp_Object string_end, + Lisp_Object array, Lisp_Object array_start, + Lisp_Object array_end, + check_test_func_t check_match, Boolint test_not_unboundp, + Lisp_Object test, Lisp_Object key, + Boolint return_string_index) +{ + Ibyte *string_data = XSTRING_DATA (string), *startp = string_data; + Bytecount string_offset = 0, string_len = XSTRING_LENGTH (string); + Elemcount char_count = 0, array_starting, array_ending, array_length; + Elemcount string_starting, string_ending; + Lisp_Object character; + + array_starting = FIXNUMP (array_start) ? XFIXNUM (array_start) : 1 + MOST_POSITIVE_FIXNUM; + array_ending = FIXNUMP (array_end) ? XFIXNUM (array_end) : 1 + MOST_POSITIVE_FIXNUM; + array_length = XFIXNUM (Flength (array)); + check_sequence_range (array, array_start, array_end, make_fixnum (array_length)); + array_ending = min (array_ending, array_length); + + string_ending = FIXNUMP (string_end) ? XFIXNUM (string_end) : 1 + MOST_POSITIVE_FIXNUM; + string_starting + = FIXNUMP (string_start) ? XFIXNUM (string_start) : 1 + MOST_POSITIVE_FIXNUM; + + while (char_count < string_starting && string_offset < string_len) + { + INC_IBYTEPTR (string_data); + string_offset = string_data - startp; + char_count++; + } + + while (array_starting < array_ending && string_starting < string_ending + && string_offset < string_len) + { + character = make_char (itext_ichar (string_data)); + + if (return_string_index) + { + if (check_match (test, key, character, + Faref (array, make_fixnum (array_starting))) + != test_not_unboundp) + { + return make_integer (char_count); + } + } + else + { + if (check_match (test, key, + Faref (array, make_fixnum (array_starting)), + character) + != test_not_unboundp) + { + return make_integer (XFIXNUM (array_start) + char_count); + } + } + + startp = XSTRING_DATA (string); + string_data = startp + string_offset; + if (string_len != XSTRING_LENGTH (string) + || !valid_ibyteptr_p (string_data)) + { + mapping_interaction_error (Qmismatch, string); + } + + array_starting++; + string_starting++; + char_count++; + INC_IBYTEPTR (string_data); + string_offset = string_data - startp; + } + + if (string_data == XSTRING_DATA (string) + XSTRING_LENGTH (string)) + { + check_sequence_range (string, string_start, string_end, + make_fixnum (char_count)); + } + + if ((NILP (string_end) ? + string_offset < string_len : string_starting < string_ending) || + (NILP (array_end) ? !NILP (array) : array_starting < array_ending)) + { + return make_integer (return_string_index ? char_count : + XFIXNUM (array_start) + char_count); + } + + return Qnil; +} + +static Lisp_Object +mismatch_string_string (Lisp_Object string1, + Lisp_Object string1_start, Lisp_Object string1_end, + Lisp_Object string2, Lisp_Object string2_start, + Lisp_Object string2_end, + check_test_func_t check_match, + Boolint test_not_unboundp, + Lisp_Object test, Lisp_Object key, + Boolint UNUSED (return_string1_index)) +{ + Ibyte *string1_data = XSTRING_DATA (string1), *startp1 = string1_data; + Bytecount string1_offset = 0, string1_len = XSTRING_LENGTH (string1); + Ibyte *string2_data = XSTRING_DATA (string2), *startp2 = string2_data; + Bytecount string2_offset = 0, string2_len = XSTRING_LENGTH (string2); + Elemcount char_count1 = 0, string1_starting, string1_ending; + Elemcount char_count2 = 0, string2_starting, string2_ending; + Lisp_Object character1, character2; + + string1_ending = FIXNUMP (string1_end) ? XFIXNUM (string1_end) : 1 + MOST_POSITIVE_FIXNUM; + string1_starting + = FIXNUMP (string1_start) ? XFIXNUM (string1_start) : 1 + MOST_POSITIVE_FIXNUM; + + string2_starting + = FIXNUMP (string2_start) ? XFIXNUM (string2_start) : 1 + MOST_POSITIVE_FIXNUM; + string2_ending = FIXNUMP (string2_end) ? XFIXNUM (string2_end) : 1 + MOST_POSITIVE_FIXNUM; + + while (char_count1 < string1_starting && string1_offset < string1_len) + { + INC_IBYTEPTR (string1_data); + string1_offset = string1_data - startp1; + char_count1++; + } + + while (char_count2 < string2_starting && string2_offset < string2_len) + { + INC_IBYTEPTR (string2_data); + string2_offset = string2_data - startp2; + char_count2++; + } + + while (string2_starting < string2_ending && string1_starting < string1_ending + && string1_offset < string1_len && string2_offset < string2_len) + { + character1 = make_char (itext_ichar (string1_data)); + character2 = make_char (itext_ichar (string2_data)); + + if (check_match (test, key, character1, character2) + != test_not_unboundp) + { + return make_integer (char_count1); + } + + startp1 = XSTRING_DATA (string1); + string1_data = startp1 + string1_offset; + if (string1_len != XSTRING_LENGTH (string1) + || !valid_ibyteptr_p (string1_data)) + { + mapping_interaction_error (Qmismatch, string1); + } + + startp2 = XSTRING_DATA (string2); + string2_data = startp2 + string2_offset; + if (string2_len != XSTRING_LENGTH (string2) + || !valid_ibyteptr_p (string2_data)) + { + mapping_interaction_error (Qmismatch, string2); + } + + string2_starting++; + string1_starting++; + char_count1++; + char_count2++; + INC_IBYTEPTR (string1_data); + string1_offset = string1_data - startp1; + INC_IBYTEPTR (string2_data); + string2_offset = string2_data - startp2; + } + + if (string1_data == XSTRING_DATA (string1) + XSTRING_LENGTH (string1)) + { + check_sequence_range (string1, string1_start, string1_end, + make_fixnum (char_count1)); + } + + if (string2_data == XSTRING_DATA (string2) + XSTRING_LENGTH (string2)) + { + check_sequence_range (string2, string2_start, string2_end, + make_fixnum (char_count2)); + } + + if ((!NILP (string1_end) && string1_starting < string1_ending) || + (!NILP (string2_end) && string2_starting < string2_ending)) + { + return make_integer (char_count1); + } + + if ((NILP (string1_end) && string1_data + < (XSTRING_DATA (string1) + XSTRING_LENGTH (string1))) || + (NILP (string2_end) && string2_data + < (XSTRING_DATA (string2) + XSTRING_LENGTH (string2)))) + { + return make_integer (char_count1); + } + + return Qnil; +} + +static Lisp_Object +mismatch_array_array (Lisp_Object array1, Lisp_Object start1, Lisp_Object end1, + Lisp_Object array2, Lisp_Object start2, Lisp_Object end2, + check_test_func_t check_match, Boolint test_not_unboundp, + Lisp_Object test, Lisp_Object key, + Boolint UNUSED (return_array1_index)) +{ + Elemcount len1 = XFIXNUM (Flength (array1)), len2 = XFIXNUM (Flength (array2)); + Elemcount ending1 = MOST_POSITIVE_FIXNUM, ending2 = MOST_POSITIVE_FIXNUM; + Elemcount starting1, starting2; + + check_sequence_range (array1, start1, end1, make_fixnum (len1)); + check_sequence_range (array2, start2, end2, make_fixnum (len2)); + + starting1 = FIXNUMP (start1) ? XFIXNUM (start1) : 1 + MOST_POSITIVE_FIXNUM; + starting2 = FIXNUMP (start2) ? XFIXNUM (start2) : 1 + MOST_POSITIVE_FIXNUM; + + if (!NILP (end1)) + { + ending1 = FIXNUMP (end1) ? XFIXNUM (end1) : 1 + MOST_POSITIVE_FIXNUM; + } + + if (!NILP (end2)) + { + ending2 = FIXNUMP (end2) ? XFIXNUM (end2) : 1 + MOST_POSITIVE_FIXNUM; + } + + ending1 = min (ending1, len1); + ending2 = min (ending2, len2); + + while (starting1 < ending1 && starting2 < ending2) + { + if (check_match (test, key, Faref (array1, make_fixnum (starting1)), + Faref (array2, make_fixnum (starting2))) + != test_not_unboundp) + { + return make_integer (starting1); + } + starting1++; + starting2++; + } + + if (starting1 < ending1 || starting2 < ending2) + { + return make_integer (starting1); + } + + return Qnil; +} + +typedef Lisp_Object +(*mismatch_func_t) (Lisp_Object sequence1, Lisp_Object start1, Lisp_Object end1, + Lisp_Object sequence2, Lisp_Object start2, Lisp_Object end2, + check_test_func_t check_match, Boolint test_not_unboundp, + Lisp_Object test, Lisp_Object key, + Boolint return_list_index); + +static mismatch_func_t +get_mismatch_func (Lisp_Object sequence1, Lisp_Object sequence2, + Lisp_Object from_end, Boolint *return_sequence1_index_out) +{ + CHECK_SEQUENCE (sequence1); + CHECK_SEQUENCE (sequence2); + + if (!NILP (from_end)) + { + *return_sequence1_index_out = 1; + return mismatch_from_end; + } + + if (LISTP (sequence1)) + { + if (LISTP (sequence2)) + { + *return_sequence1_index_out = 1; + return mismatch_list_list; + } + + if (STRINGP (sequence2)) + { + *return_sequence1_index_out = 1; + return mismatch_list_string; + } + + *return_sequence1_index_out = 1; + return mismatch_list_array; + } + + if (STRINGP (sequence1)) + { + if (STRINGP (sequence2)) + { + *return_sequence1_index_out = 1; + return mismatch_string_string; + } + + if (LISTP (sequence2)) + { + *return_sequence1_index_out = 0; + return mismatch_list_string; + } + + *return_sequence1_index_out = 1; + return mismatch_string_array; + } + + if (ARRAYP (sequence1)) + { + if (STRINGP (sequence2)) + { + *return_sequence1_index_out = 0; + return mismatch_string_array; + } + + if (LISTP (sequence2)) + { + *return_sequence1_index_out = 0; + return mismatch_list_array; + } + + *return_sequence1_index_out = 1; + return mismatch_array_array; + } + + RETURN_NOT_REACHED (NULL); + return NULL; +} + +DEFUN ("mismatch", Fmismatch, 2, MANY, 0, /* +Compare SEQUENCE1 with SEQUENCE2, return index of first mismatching element. + +Return nil if the sequences match. If one sequence is a prefix of the +other, the return value indicates the end of the shorter sequence. A +non-nil return value always reflects an index into SEQUENCE1. + +See `search' for the meaning of the keywords." + +arguments: (SEQUENCE1 SEQUENCE2 &key (TEST #'eql) (KEY #'identity) (START1 0) END1 (START2 0) END2 FROM-END TEST-NOT) +*/ + (int nargs, Lisp_Object *args)) +{ + Lisp_Object sequence1 = args[0], sequence2 = args[1]; + Boolint test_not_unboundp = 1, return_first_index = 0; + check_test_func_t check_match = NULL; + mismatch_func_t mismatch = NULL; + + PARSE_KEYWORDS (Fmismatch, nargs, args, 8, + (test, key, from_end, start1, end1, start2, end2, test_not), + (start1 = start2 = Qzero)); + + CHECK_SEQUENCE (sequence1); + CHECK_SEQUENCE (sequence2); + + CHECK_NATNUM (start1); + CHECK_NATNUM (start2); + + if (!NILP (end1)) + { + CHECK_NATNUM (end1); + } + + if (!NILP (end2)) + { + CHECK_NATNUM (end2); + } + + check_match = get_check_match_function (&test, test_not, Qnil, Qnil, key, + &test_not_unboundp, NULL); + mismatch = get_mismatch_func (sequence1, sequence2, from_end, + &return_first_index); + + if (return_first_index) + { + return mismatch (sequence1, start1, end1, sequence2, start2, end2, + check_match, test_not_unboundp, test, key, 1); + } + + return mismatch (sequence2, start2, end2, sequence1, start1, end1, + check_match, test_not_unboundp, test, key, 0); +} + +DEFUN ("search", Fsearch, 2, MANY, 0, /* +Search for SEQUENCE1 as a subsequence of SEQUENCE2. + +Return the index of the leftmost element of the first match found; return +nil if there are no matches. + +In this function, :start1 and :end1 specify a subsequence of SEQUENCE1, and +:start2 and :end2 specify a subsequence of SEQUENCE2. See `remove*' for +details of the other keywords. + +arguments: (SEQUENCE1 SEQUENCE2 &key (TEST #'eql) (KEY #'identity) (START1 0) END1 (START2 0) END2 FROM-END TEST-NOT) +*/ + (int nargs, Lisp_Object *args)) +{ + Lisp_Object sequence1 = args[0], sequence2 = args[1], position0 = Qnil; + Boolint test_not_unboundp = 1, return_first = 0; + check_test_func_t check_test = NULL, check_match = NULL; + mismatch_func_t mismatch = NULL; + Elemcount starting1 = 0, ending1 = 1 + MOST_POSITIVE_FIXNUM, starting2 = 0; + Elemcount ending2 = 1 + MOST_POSITIVE_FIXNUM, ii = 0; + Elemcount length1; + Lisp_Object object = Qnil; + struct gcpro gcpro1, gcpro2; + + PARSE_KEYWORDS (Fsearch, nargs, args, 8, + (test, key, from_end, start1, end1, start2, end2, test_not), + (start1 = start2 = Qzero)); + + CHECK_SEQUENCE (sequence1); + CHECK_SEQUENCE (sequence2); + CHECK_KEY_ARGUMENT (key); + + CHECK_NATNUM (start1); + starting1 = FIXNUMP (start1) ? XFIXNUM (start1) : 1 + MOST_POSITIVE_FIXNUM; + CHECK_NATNUM (start2); + starting2 = FIXNUMP (start2) ? XFIXNUM (start2) : 1 + MOST_POSITIVE_FIXNUM; + + if (!NILP (end1)) + { + Lisp_Object len1 = Flength (sequence1); + + CHECK_NATNUM (end1); + check_sequence_range (sequence1, start1, end1, len1); + ending1 = min (XFIXNUM (end1), XFIXNUM (len1)); + } + else + { + end1 = Flength (sequence1); + check_sequence_range (sequence1, start1, end1, end1); + ending1 = XFIXNUM (end1); + } + + length1 = ending1 - starting1; + + if (!NILP (end2)) + { + Lisp_Object len2 = Flength (sequence2); + + CHECK_NATNUM (end2); + check_sequence_range (sequence2, start2, end2, len2); + ending2 = min (XFIXNUM (end2), XFIXNUM (len2)); + } + else + { + end2 = Flength (sequence2); + check_sequence_range (sequence2, start2, end2, end2); + ending2 = XFIXNUM (end2); + } + + check_match = get_check_match_function (&test, test_not, Qnil, Qnil, key, + &test_not_unboundp, &check_test); + mismatch = get_mismatch_func (sequence1, sequence2, from_end, &return_first); + + if (bytecode_arithcompare (start1, make_integer (ending1)) >= 0) + { + if (NILP (from_end)) + { + return start2; + } + + if (NILP (end2)) + { + return Flength (sequence2); + } + + return end2; + } + + if (NILP (from_end)) + { + Lisp_Object mismatch_start1 = Fadd1 (start1); + Lisp_Object first = KEY (key, Felt (sequence1, start1)); + GCPRO2 (first, mismatch_start1); + + ii = starting2; + while (ii < ending2) + { + position0 = position (&object, first, sequence2, check_test, + test_not_unboundp, test, key, make_fixnum (ii), + end2, Qnil, Qnil, Qsearch); + if (NILP (position0)) + { + UNGCPRO; + return Qnil; + } + + if (length1 + XFIXNUM (position0) <= ending2 && + (return_first ? + NILP (mismatch (sequence1, mismatch_start1, end1, + sequence2, + make_fixnum (1 + XFIXNUM (position0)), + make_fixnum (length1 + XFIXNUM (position0)), + check_match, test_not_unboundp, test, key, 1)) : + NILP (mismatch (sequence2, + make_fixnum (1 + XFIXNUM (position0)), + make_fixnum (length1 + XFIXNUM (position0)), + sequence1, mismatch_start1, end1, + check_match, test_not_unboundp, test, key, 0)))) + + + { + UNGCPRO; + return position0; + } + + ii = XFIXNUM (position0) + 1; + } + + UNGCPRO; + } + else + { + Lisp_Object mismatch_end1 = make_integer (ending1 - 1); + Lisp_Object last = KEY (key, Felt (sequence1, mismatch_end1)); + GCPRO2 (last, mismatch_end1); + + ii = ending2; + while (ii > starting2) + { + position0 = position (&object, last, sequence2, check_test, + test_not_unboundp, test, key, start2, + make_fixnum (ii), Qt, Qnil, Qsearch); + + if (NILP (position0)) + { + UNGCPRO; + return Qnil; + } + + if (XFIXNUM (position0) - length1 + 1 >= starting2 && + (return_first ? + NILP (mismatch (sequence1, start1, mismatch_end1, + sequence2, + make_fixnum (XFIXNUM (position0) - length1 + 1), + make_fixnum (XFIXNUM (position0)), + check_match, test_not_unboundp, test, key, 1)) : + NILP (mismatch (sequence2, + make_fixnum (XFIXNUM (position0) - length1 + 1), + make_fixnum (XFIXNUM (position0)), + sequence1, start1, mismatch_end1, + check_match, test_not_unboundp, test, key, 0)))) + { + UNGCPRO; + return make_fixnum (XFIXNUM (position0) - length1 + 1); + } + + ii = XFIXNUM (position0); + } + + UNGCPRO; + } + + return Qnil; +} + +/* These two functions do set operations, those that can be visualised with + Venn diagrams. */ +static Lisp_Object +venn (Lisp_Object caller, int nargs, Lisp_Object *args, Boolint intersectionp) +{ + Lisp_Object liszt1 = args[0], liszt2 = args[1]; + Lisp_Object result = EQ (caller, Qsubsetp) ? Qt : Qnil, result_tail = Qnil; + Lisp_Object keyed = Qnil, ignore = Qnil; + Boolint test_not_unboundp = 1; + check_test_func_t check_test = NULL; + struct gcpro gcpro1, gcpro2; + + PARSE_KEYWORDS_8 (caller, nargs, args, 4, (test, key, test_not, stable), + NULL, 2, 0); + + CHECK_LIST (liszt1); + CHECK_LIST (liszt2); + + CHECK_KEY_ARGUMENT (key); + + if (NILP (liszt1) && intersectionp) + { + return Qnil; + } + + if (NILP (liszt2)) + { + return intersectionp ? Qnil : liszt1; + } + + get_check_match_function (&test, test_not, Qnil, Qnil, key, + &test_not_unboundp, &check_test); + + GCPRO2 (keyed, result); + + { + GC_EXTERNAL_LIST_LOOP_2 (elt, liszt1) + { + keyed = KEY (key, elt); + if (NILP (list_position_cons_before (&ignore, keyed, liszt2, + check_test, test_not_unboundp, + test, key, 0, Qzero, Qnil)) + != intersectionp) + { + if (EQ (Qsubsetp, caller)) + { + result = Qnil; + break; + } + else if (NILP (stable)) + { + result = Fcons (elt, result); + } + else if (NILP (result)) + { + result = result_tail = Fcons (elt, Qnil); + } + else + { + XSETCDR (result_tail, Fcons (elt, Qnil)); + result_tail = XCDR (result_tail); + } + } + } + END_GC_EXTERNAL_LIST_LOOP (elt); + } + + UNGCPRO; + + return result; +} + +static Lisp_Object +nvenn (Lisp_Object caller, int nargs, Lisp_Object *args, Boolint intersectionp) +{ + Lisp_Object liszt1 = args[0], liszt2 = args[1], tortoise_elt, ignore = Qnil; + Lisp_Object elt = Qnil, tail = Qnil, keyed = Qnil, prev_tail = Qnil; + Elemcount count; + Boolint test_not_unboundp = 1; + check_test_func_t check_test = NULL; + struct gcpro gcpro1, gcpro2, gcpro3, gcpro4; + + PARSE_KEYWORDS_8 (caller, nargs, args, 3, (test, key, test_not), + NULL, 2, 0); + + CHECK_LIST (liszt1); + CHECK_LIST (liszt2); + + CHECK_KEY_ARGUMENT (key); + + if (NILP (liszt1) && intersectionp) + { + return Qnil; + } + + if (NILP (liszt2)) + { + return intersectionp ? Qnil : liszt1; + } + + get_check_match_function (&test, test_not, Qnil, Qnil, key, + &test_not_unboundp, &check_test); + + tortoise_elt = tail = liszt1, count = 0; + + GCPRO4 (tail, keyed, liszt1, tortoise_elt); + + while (CONSP (tail) ? (elt = XCAR (tail), 1) : NILP (tail) ? 0 : + (signal_malformed_list_error (liszt1), 0)) + { + keyed = KEY (key, elt); + if (NILP (list_position_cons_before (&ignore, keyed, liszt2, + check_test, test_not_unboundp, + test, key, 0, Qzero, Qnil)) + == intersectionp) + { + if (NILP (prev_tail)) + { + liszt1 = XCDR (tail); + } + else + { + XSETCDR (prev_tail, XCDR (tail)); + } + + tail = XCDR (tail); + /* List is definitely not circular now! */ + count = 0; + } + else + { + prev_tail = tail; + tail = XCDR (tail); + } + + if (count++ < CIRCULAR_LIST_SUSPICION_LENGTH) continue; + + if (count & 1) + { + tortoise_elt = XCDR (tortoise_elt); + } + + if (EQ (elt, tortoise_elt)) + { + signal_circular_list_error (liszt1); + } + } + + UNGCPRO; + + return liszt1; +} + +DEFUN ("intersection", Fintersection, 2, MANY, 0, /* +Combine LIST1 and LIST2 using a set-intersection operation. + +The result list contains all items that appear in both LIST1 and LIST2. +This is a non-destructive function; it makes a copy of the data if necessary +to avoid corrupting the original LIST1 and LIST2. + +A non-nil value for the :stable keyword, not specified by Common Lisp, means +return the items in the order they appear in LIST1. + +See `union' for the meaning of :test, :test-not and :key." + +arguments: (LIST1 LIST2 &key (TEST #'eql) (KEY #'identity) TEST-NOT STABLE) +*/ + (int nargs, Lisp_Object *args)) +{ + return venn (Qintersection, nargs, args, 1); +} + +DEFUN ("nintersection", Fnintersection, 2, MANY, 0, /* +Combine LIST1 and LIST2 using a set-intersection operation. + +The result list contains all items that appear in both LIST1 and LIST2. +This is a destructive function; it reuses the storage of LIST1 whenever +possible. + +See `union' for the meaning of :test, :test-not and :key." + +arguments: (LIST1 LIST2 &key (TEST #'eql) (KEY #'identity) TEST-NOT) +*/ + (int nargs, Lisp_Object *args)) +{ + return nvenn (Qnintersection, nargs, args, 1); +} + +DEFUN ("subsetp", Fsubsetp, 2, MANY, 0, /* +Return non-nil if every element of LIST1 also appears in LIST2. + +See `union' for the meaning of the keyword arguments. + +arguments: (LIST1 LIST2 &key (TEST #'eql) (KEY #'identity) TEST-NOT) +*/ + (int nargs, Lisp_Object *args)) +{ + return venn (Qsubsetp, nargs, args, 0); +} + +DEFUN ("set-difference", Fset_difference, 2, MANY, 0, /* +Combine LIST1 and LIST2 using a set-difference operation. + +The result list contains all items that appear in LIST1 but not LIST2. This +is a non-destructive function; it makes a copy of the data if necessary to +avoid corrupting the original LIST1 and LIST2. + +See `union' for the meaning of :test, :test-not and :key. + +A non-nil value for the :stable keyword, not specified by Common Lisp, means +return the items in the order they appear in LIST1. + +arguments: (LIST1 LIST2 &key (TEST #'eql) (KEY #'identity) TEST-NOT STABLE) +*/ + (int nargs, Lisp_Object *args)) +{ + return venn (Qset_difference, nargs, args, 0); +} + +DEFUN ("nset-difference", Fnset_difference, 2, MANY, 0, /* +Combine LIST1 and LIST2 using a set-difference operation. + +The result list contains all items that appear in LIST1 but not LIST2. This +is a destructive function; it reuses the storage of LIST1 whenever possible. + +See `union' for the meaning of :test, :test-not and :key." + +arguments: (LIST1 LIST2 &key (TEST #'eql) (KEY #'identity) TEST-NOT) +*/ + (int nargs, Lisp_Object *args)) +{ + return nvenn (Qnset_difference, nargs, args, 0); +} + +DEFUN ("nunion", Fnunion, 2, MANY, 0, /* +Combine LIST1 and LIST2 using a set-union operation. +The result list contains all items that appear in either LIST1 or LIST2. + +This is a destructive function, it reuses the storage of LIST1 whenever +possible. + +See `union' for the meaning of :test, :test-not and :key. + +arguments: (LIST1 LIST2 &key (TEST #'eql) (KEY #'identity) TEST-NOT) +*/ + (int nargs, Lisp_Object *args)) +{ + args[0] = nvenn (Qnunion, nargs, args, 0); + return bytecode_nconc2 (args); +} + +DEFUN ("union", Funion, 2, MANY, 0, /* +Combine LIST1 and LIST2 using a set-union operation. +The result list contains all items that appear in either LIST1 or LIST2. +This is a non-destructive function; it makes a copy of the data if necessary +to avoid corrupting the original LIST1 and LIST2. + +The keywords :test and :test-not specify two-argument test and negated-test +predicates, respectively; :test defaults to `eql'. See `member*' for more +information. + +:key specifies a one-argument function that transforms elements of LIST1 +and LIST2 into \"comparison keys\" before the test predicate is applied. +For example, if :key is #'car, then the car of elements from LIST1 is +compared with the car of elements from LIST2. The :key function, however, +does not affect the elements in the returned list, which are taken directly +from the elements in LIST1 and LIST2. + +A non-nil value for the :stable keyword, not specified by Common Lisp, means +return the items of LIST1 in order, followed by the remaining items of LIST2 +in the order they occur in LIST2. + +arguments: (LIST1 LIST2 &key (TEST #'eql) (KEY #'identity) TEST-NOT STABLE) +*/ + (int nargs, Lisp_Object *args)) +{ + Lisp_Object liszt1 = args[0], liszt2 = args[1], ignore = Qnil; + Lisp_Object keyed = Qnil, result, result_tail; + Boolint test_not_unboundp = 1; + check_test_func_t check_test = NULL, check_match = NULL; + struct gcpro gcpro1, gcpro2; + + PARSE_KEYWORDS (Funion, nargs, args, 4, (test, key, test_not, stable), NULL); + + CHECK_LIST (liszt1); + CHECK_LIST (liszt2); + + CHECK_KEY_ARGUMENT (key); + + if (NILP (liszt1)) + { + return liszt2; + } + + if (NILP (liszt2)) + { + return liszt1; + } + + check_match = get_check_match_function (&test, test_not, Qnil, Qnil, key, + &test_not_unboundp, &check_test); + + GCPRO2 (keyed, result); + + if (NILP (stable)) + { + result = liszt2; + { + GC_EXTERNAL_LIST_LOOP_2 (elt, liszt1) + { + keyed = KEY (key, elt); + if (NILP (list_position_cons_before (&ignore, keyed, liszt2, + check_test, test_not_unboundp, + test, key, 0, Qzero, Qnil))) + { + /* The Lisp version of #'union used to check which list was + longer, and use that as the tail of the constructed + list. That fails when the order of arguments to TEST is + specified, as is the case for these functions. We could + pass the reverse_check argument to + list_position_cons_before, but that means any key argument + is called an awful lot more, so it's a space win but not + a time win. */ + result = Fcons (elt, result); + } + } + END_GC_EXTERNAL_LIST_LOOP (elt); + } + } + else + { + result = result_tail = Qnil; + + /* The standard `union' doesn't produce a "stable" union -- it + iterates over the second list instead of the first one, and returns + the values in backwards order. According to the CLTL2 + documentation, `union' is not required to preserve the ordering of + elements in any fashion; providing the functionality for a stable + union is an XEmacs extension. */ + { + GC_EXTERNAL_LIST_LOOP_2 (elt, liszt2) + { + if (NILP (list_position_cons_before (&ignore, elt, liszt1, + check_match, test_not_unboundp, + test, key, 1, Qzero, Qnil))) + { + if (NILP (result)) + { + result = result_tail = Fcons (elt, Qnil); + } + else + { + XSETCDR (result_tail, Fcons (elt, Qnil)); + result_tail = XCDR (result_tail); + } + } + } + END_GC_EXTERNAL_LIST_LOOP (elt); + } + + result = NILP (result) ? liszt1 : nconc2 (Fcopy_list (liszt1), result); + } + + UNGCPRO; + + return result; +} + +DEFUN ("set-exclusive-or", Fset_exclusive_or, 2, MANY, 0, /* +Combine LIST1 and LIST2 using a set-exclusive-or operation. + +The result list contains all items that appear in exactly one of LIST1, LIST2. +This is a non-destructive function; it makes a copy of the data if necessary +to avoid corrupting the original LIST1 and LIST2. + +See `union' for the meaning of :test, :test-not and :key. + +A non-nil value for the :stable keyword, not specified by Common Lisp, means +return the items in the order they appear in LIST1, followed by the +remaining items in the order they appear in LIST2. + +arguments: (LIST1 LIST2 &key (TEST #'eql) (KEY #'identity) TEST-NOT STABLE) +*/ + (int nargs, Lisp_Object *args)) +{ + Lisp_Object liszt1 = args[0], liszt2 = args[1]; + Lisp_Object result = Qnil, result_tail = Qnil, keyed = Qnil, ignore = Qnil; + Boolint test_not_unboundp = 1; + check_test_func_t check_match = NULL, check_test = NULL; + struct gcpro gcpro1, gcpro2; + + PARSE_KEYWORDS (Fset_exclusive_or, nargs, args, 4, + (test, key, test_not, stable), NULL); + + CHECK_LIST (liszt1); + CHECK_LIST (liszt2); + + CHECK_KEY_ARGUMENT (key); + + if (NILP (liszt2)) + { + return liszt1; + } + + check_match = get_check_match_function (&test, test_not, Qnil, Qnil, key, + &test_not_unboundp, &check_test); + + GCPRO2 (keyed, result); + { + GC_EXTERNAL_LIST_LOOP_2 (elt, liszt1) + { + keyed = KEY (key, elt); + if (NILP (list_position_cons_before (&ignore, keyed, liszt2, + check_test, test_not_unboundp, + test, key, 0, Qzero, Qnil))) + { + if (NILP (stable)) + { + result = Fcons (elt, result); + } + else if (NILP (result)) + { + result = result_tail = Fcons (elt, Qnil); + } + else + { + XSETCDR (result_tail, Fcons (elt, Qnil)); + result_tail = XCDR (result_tail); + } + } + } + END_GC_EXTERNAL_LIST_LOOP (elt); + } + + { + GC_EXTERNAL_LIST_LOOP_2 (elt, liszt2) + { + if (NILP (list_position_cons_before (&ignore, elt, liszt1, + check_match, test_not_unboundp, + test, key, 1, Qzero, Qnil))) + { + if (NILP (stable)) + { + result = Fcons (elt, result); + } + else if (NILP (result)) + { + result = result_tail = Fcons (elt, Qnil); + } + else + { + XSETCDR (result_tail, Fcons (elt, Qnil)); + result_tail = XCDR (result_tail); + } + } + } + END_GC_EXTERNAL_LIST_LOOP (elt); + } + + UNGCPRO; + + return result; +} + +DEFUN ("nset-exclusive-or", Fnset_exclusive_or, 2, MANY, 0, /* +Combine LIST1 and LIST2 using a set-exclusive-or operation. + +The result list contains all items that appear in exactly one of LIST1 and +LIST2. This is a destructive function; it reuses the storage of LIST1 and +LIST2 whenever possible. + +See `union' for the meaning of :test, :test-not and :key. + +arguments: (LIST1 LIST2 &key (TEST #'eql) (KEY #'identity) TEST-NOT) +*/ + (int nargs, Lisp_Object *args)) +{ + Lisp_Object liszt1 = args[0], liszt2 = args[1], elt = Qnil, tail = Qnil; + Lisp_Object result = Qnil, tortoise_elt = Qnil, keyed = Qnil, swap; + Lisp_Object prev_tail = Qnil, ignore = Qnil; + Elemcount count; + Boolint test_not_unboundp = 1; + check_test_func_t check_match = NULL, check_test = NULL; + struct gcpro gcpro1, gcpro2, gcpro3, gcpro4; + + PARSE_KEYWORDS (Fnset_exclusive_or, nargs, args, 4, + (test, key, test_not, stable), NULL); + + CHECK_LIST (liszt1); + CHECK_LIST (liszt2); + + CHECK_KEY_ARGUMENT (key); + + if (NILP (liszt2)) + { + return liszt1; + } + + check_match = get_check_match_function (&test, test_not, Qnil, Qnil, key, + &test_not_unboundp, &check_test); + + tortoise_elt = tail = liszt1, count = 0; + + GCPRO4 (tail, keyed, result, tortoise_elt); + + while (CONSP (tail) ? (elt = XCAR (tail), 1) : NILP (tail) ? 0 : + (signal_malformed_list_error (liszt1), 0)) + { + keyed = KEY (key, elt); + if (NILP (list_position_cons_before (&ignore, keyed, liszt2, + check_test, test_not_unboundp, + test, key, 0, Qzero, Qnil))) + { + swap = XCDR (tail); + + if (NILP (prev_tail)) + { + liszt1 = XCDR (tail); + } + else + { + XSETCDR (prev_tail, swap); + } + + XSETCDR (tail, result); + result = tail; + tail = swap; + + /* List is definitely not circular now! */ + count = 0; + } + else + { + prev_tail = tail; + tail = XCDR (tail); + } + + if (count++ < CIRCULAR_LIST_SUSPICION_LENGTH) continue; + + if (count & 1) + { + tortoise_elt = XCDR (tortoise_elt); + } + + if (EQ (elt, tortoise_elt)) + { + signal_circular_list_error (liszt1); + } + } + + tortoise_elt = tail = liszt2, count = 0; + + while (CONSP (tail) ? (elt = XCAR (tail), 1) : NILP (tail) ? 0 : + (signal_malformed_list_error (liszt2), 0)) + { + /* Need to leave the key calculation to list_position_cons_before(). */ + if (NILP (list_position_cons_before (&ignore, elt, liszt1, + check_match, test_not_unboundp, + test, key, 1, Qzero, Qnil))) + { + swap = XCDR (tail); + XSETCDR (tail, result); + result = tail; + tail = swap; + count = 0; + } + else + { + tail = XCDR (tail); + } + + if (count++ < CIRCULAR_LIST_SUSPICION_LENGTH) continue; + + if (count & 1) + { + tortoise_elt = XCDR (tortoise_elt); + } + + if (EQ (elt, tortoise_elt)) + { + signal_circular_list_error (liszt1); + } + } + + UNGCPRO; + + return result; +} + +void +syms_of_sequence (void) +{ + DEFSYMBOL (Qstring_lessp); + DEFSYMBOL (Qmerge); + DEFSYMBOL (Qfill); + DEFSYMBOL (Qidentity); + DEFSYMBOL (Qvector); + DEFSYMBOL (Qarray); + DEFSYMBOL (Qstring); + DEFSYMBOL (Qlist); + DEFSYMBOL (Qbit_vector); + defsymbol (&QsortX, "sort*"); + DEFSYMBOL (Qreduce); + DEFSYMBOL (Qreplace); + DEFSYMBOL (Qposition); + DEFSYMBOL (Qfind); + defsymbol (&QdeleteX, "delete*"); + defsymbol (&QremoveX, "remove*"); + + DEFSYMBOL (Qmapconcat); + defsymbol (&QmapcarX, "mapcar*"); + DEFSYMBOL (Qmapvector); + DEFSYMBOL (Qmapcan); + DEFSYMBOL (Qmapc); + DEFSYMBOL (Qmap); + DEFSYMBOL (Qmap_into); + DEFSYMBOL (Qsome); + DEFSYMBOL (Qevery); + DEFSYMBOL (Qnsubstitute); + DEFSYMBOL (Qdelete_duplicates); + DEFSYMBOL (Qsubstitute); + DEFSYMBOL (Qmismatch); + DEFSYMBOL (Qintersection); + DEFSYMBOL (Qnintersection); + DEFSYMBOL (Qsubsetp); + DEFSYMBOL (Qcar_less_than_car); + DEFSYMBOL (Qset_difference); + DEFSYMBOL (Qnset_difference); + DEFSYMBOL (Qnunion); + + DEFKEYWORD (Q_from_end); + DEFKEYWORD (Q_initial_value); + DEFKEYWORD (Q_start1); + DEFKEYWORD (Q_start2); + DEFKEYWORD (Q_end1); + DEFKEYWORD (Q_end2); + defkeyword (&Q_if_, ":if"); + DEFKEYWORD (Q_if_not); + DEFKEYWORD (Q_test_not); + DEFKEYWORD (Q_count); + DEFKEYWORD (Q_stable); + DEFKEYWORD (Q_descend_structures); + + DEFSUBR (Flength); + DEFSUBR (Fcount); + DEFSUBR (Fsubseq); + DEFSUBR (Felt); + DEFSUBR (Fcopy_tree); + DEFSUBR (Fmember); + DEFSUBR (Fmemq); + DEFSUBR (FmemberX); + DEFSUBR (Fadjoin); + DEFSUBR (Fassoc); + DEFSUBR (Fassq); + DEFSUBR (FassocX); + DEFSUBR (Frassoc); + DEFSUBR (Frassq); + DEFSUBR (FrassocX); + DEFSUBR (Fposition); + DEFSUBR (Ffind); + DEFSUBR (FdeleteX); + DEFSUBR (FremoveX); + DEFSUBR (Fdelete_duplicates); + DEFSUBR (Fremove_duplicates); + DEFSUBR (Fnreverse); + DEFSUBR (Freverse); + DEFSUBR (Fmerge); + DEFSUBR (FsortX); + DEFSUBR (Ffill); + DEFSUBR (Fmapconcat); + DEFSUBR (FmapcarX); + DEFSUBR (Fmapvector); + DEFSUBR (Fmapcan); + DEFSUBR (Fmapc); + Ffset (intern ("mapc-internal"), Qmapc); + Ffset (intern ("mapcar"), QmapcarX); + DEFSUBR (Fmap); + DEFSUBR (Fmap_into); + DEFSUBR (Fsome); + DEFSUBR (Fevery); + DEFSUBR (Freduce); + DEFSUBR (Freplace); + DEFSUBR (Fnsubstitute); + DEFSUBR (Fsubstitute); + DEFSUBR (Fsublis); + DEFSUBR (Fnsublis); + DEFSUBR (Fsubst); + DEFSUBR (Fnsubst); + DEFSUBR (Ftree_equal); + DEFSUBR (Fmismatch); + DEFSUBR (Fsearch); + DEFSUBR (Fintersection); + DEFSUBR (Fnintersection); + DEFSUBR (Fsubsetp); + DEFSUBR (Fset_difference); + DEFSUBR (Fnset_difference); + DEFSUBR (Fnunion); + DEFSUBR (Funion); + DEFSUBR (Fset_exclusive_or); + DEFSUBR (Fnset_exclusive_or); +}
--- a/src/symsinit.h Sat Dec 03 15:55:14 2011 +0000 +++ b/src/symsinit.h Sun Dec 04 18:42:50 2011 +0000 @@ -184,6 +184,7 @@ void syms_of_select_gtk (void); void syms_of_select_mswindows (void); void syms_of_select_x (void); +void syms_of_sequence (void); void syms_of_signal (void); void syms_of_sound (void); void syms_of_specifier (void);