Mercurial > hg > xemacs-beta
diff src/data.c @ 428:3ecd8885ac67 r21-2-22
Import from CVS: tag r21-2-22
author | cvs |
---|---|
date | Mon, 13 Aug 2007 11:28:15 +0200 |
parents | |
children | 080151679be2 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/data.c Mon Aug 13 11:28:15 2007 +0200 @@ -0,0 +1,2227 @@ +/* Primitive operations on Lisp data types for XEmacs Lisp interpreter. + Copyright (C) 1985, 1986, 1988, 1992, 1993, 1994, 1995 + Free Software Foundation, Inc. + +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 2, 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; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* Synched up with: Mule 2.0, FSF 19.30. Some of FSF's data.c is in + XEmacs' symbols.c. */ + +/* This file has been Mule-ized. */ + +#include <config.h> +#include "lisp.h" + +#include "buffer.h" +#include "bytecode.h" +#include "syssignal.h" + +#ifdef LISP_FLOAT_TYPE +/* Need to define a differentiating symbol -- see sysfloat.h */ +# define THIS_FILENAME data_c +# include "sysfloat.h" +#endif /* LISP_FLOAT_TYPE */ + +Lisp_Object Qnil, Qt, Qquote, Qlambda, Qunbound; +Lisp_Object Qerror_conditions, Qerror_message; +Lisp_Object Qerror, Qquit, Qwrong_type_argument, Qargs_out_of_range; +Lisp_Object Qvoid_variable, Qcyclic_variable_indirection; +Lisp_Object Qvoid_function, Qcyclic_function_indirection; +Lisp_Object Qsetting_constant, Qinvalid_read_syntax; +Lisp_Object Qmalformed_list, Qmalformed_property_list; +Lisp_Object Qcircular_list, Qcircular_property_list; +Lisp_Object Qinvalid_function, Qwrong_number_of_arguments, Qno_catch; +Lisp_Object Qio_error, Qend_of_file; +Lisp_Object Qarith_error, Qrange_error, Qdomain_error; +Lisp_Object Qsingularity_error, Qoverflow_error, Qunderflow_error; +Lisp_Object Qbeginning_of_buffer, Qend_of_buffer, Qbuffer_read_only; +Lisp_Object Qintegerp, Qnatnump, Qsymbolp; +Lisp_Object Qlistp, Qtrue_list_p, Qweak_listp; +Lisp_Object Qconsp, Qsubrp; +Lisp_Object Qcharacterp, Qstringp, Qarrayp, Qsequencep, Qvectorp; +Lisp_Object Qchar_or_string_p, Qmarkerp, Qinteger_or_marker_p, Qbufferp; +Lisp_Object Qinteger_or_char_p, Qinteger_char_or_marker_p; +Lisp_Object Qnumberp, Qnumber_char_or_marker_p; +Lisp_Object Qbit_vectorp, Qbitp, Qcdr; + +Lisp_Object Qfloatp; + +#ifdef DEBUG_XEMACS + +int debug_issue_ebola_notices; + +int debug_ebola_backtrace_length; + +int +eq_with_ebola_notice (Lisp_Object obj1, Lisp_Object obj2) +{ + if (debug_issue_ebola_notices + && ((CHARP (obj1) && INTP (obj2)) || (CHARP (obj2) && INTP (obj1)))) + { + /* #### It would be really nice if this were a proper warning + instead of brain-dead print ro Qexternal_debugging_output. */ + write_c_string ("Comparison between integer and character is constant nil (", + Qexternal_debugging_output); + Fprinc (obj1, Qexternal_debugging_output); + write_c_string (" and ", Qexternal_debugging_output); + Fprinc (obj2, Qexternal_debugging_output); + write_c_string (")\n", Qexternal_debugging_output); + debug_short_backtrace (debug_ebola_backtrace_length); + } + return EQ (obj1, obj2); +} + +#endif /* DEBUG_XEMACS */ + + + +Lisp_Object +wrong_type_argument (Lisp_Object predicate, Lisp_Object value) +{ + /* This function can GC */ + REGISTER Lisp_Object tem; + do + { + value = Fsignal (Qwrong_type_argument, list2 (predicate, value)); + tem = call1 (predicate, value); + } + while (NILP (tem)); + return value; +} + +DOESNT_RETURN +dead_wrong_type_argument (Lisp_Object predicate, Lisp_Object value) +{ + signal_error (Qwrong_type_argument, list2 (predicate, value)); +} + +DEFUN ("wrong-type-argument", Fwrong_type_argument, 2, 2, 0, /* +Signal an error until the correct type value is given by the user. +This function loops, signalling a continuable `wrong-type-argument' error +with PREDICATE and VALUE as the data associated with the error and then +calling PREDICATE on the returned value, until the value gotten satisfies +PREDICATE. At that point, the gotten value is returned. +*/ + (predicate, value)) +{ + return wrong_type_argument (predicate, value); +} + +DOESNT_RETURN +c_write_error (Lisp_Object obj) +{ + signal_simple_error ("Attempt to modify read-only object (c)", obj); +} + +DOESNT_RETURN +lisp_write_error (Lisp_Object obj) +{ + signal_simple_error ("Attempt to modify read-only object (lisp)", obj); +} + +DOESNT_RETURN +args_out_of_range (Lisp_Object a1, Lisp_Object a2) +{ + signal_error (Qargs_out_of_range, list2 (a1, a2)); +} + +DOESNT_RETURN +args_out_of_range_3 (Lisp_Object a1, Lisp_Object a2, Lisp_Object a3) +{ + signal_error (Qargs_out_of_range, list3 (a1, a2, a3)); +} + +void +check_int_range (EMACS_INT val, EMACS_INT min, EMACS_INT max) +{ + if (val < min || val > max) + args_out_of_range_3 (make_int (val), make_int (min), make_int (max)); +} + +/* On some machines, XINT needs a temporary location. + Here it is, in case it is needed. */ + +EMACS_INT sign_extend_temp; + +/* On a few machines, XINT can only be done by calling this. */ +/* XEmacs: only used by m/convex.h */ +EMACS_INT sign_extend_lisp_int (EMACS_INT num); +EMACS_INT +sign_extend_lisp_int (EMACS_INT num) +{ + if (num & (1L << (VALBITS - 1))) + return num | ((-1L) << VALBITS); + else + return num & ((1L << VALBITS) - 1); +} + + +/* Data type predicates */ + +DEFUN ("eq", Feq, 2, 2, 0, /* +Return t if the two args are the same Lisp object. +*/ + (obj1, obj2)) +{ + return EQ_WITH_EBOLA_NOTICE (obj1, obj2) ? Qt : Qnil; +} + +DEFUN ("old-eq", Fold_eq, 2, 2, 0, /* +Return t if the two args are (in most cases) the same Lisp object. + +Special kludge: A character is considered `old-eq' to its equivalent integer +even though they are not the same object and are in fact of different +types. This is ABSOLUTELY AND UTTERLY HORRENDOUS but is necessary to +preserve byte-code compatibility with v19. This kludge is known as the +\"char-int confoundance disease\" and appears in a number of other +functions with `old-foo' equivalents. + +Do not use this function! +*/ + (obj1, obj2)) +{ + /* #### blasphemy */ + return HACKEQ_UNSAFE (obj1, obj2) ? Qt : Qnil; +} + +DEFUN ("null", Fnull, 1, 1, 0, /* +Return t if OBJECT is nil. +*/ + (object)) +{ + return NILP (object) ? Qt : Qnil; +} + +DEFUN ("consp", Fconsp, 1, 1, 0, /* +Return t if OBJECT is a cons cell. `nil' is not a cons cell. +*/ + (object)) +{ + return CONSP (object) ? Qt : Qnil; +} + +DEFUN ("atom", Fatom, 1, 1, 0, /* +Return t if OBJECT is not a cons cell. `nil' is not a cons cell. +*/ + (object)) +{ + return CONSP (object) ? Qnil : Qt; +} + +DEFUN ("listp", Flistp, 1, 1, 0, /* +Return t if OBJECT is a list. `nil' is a list. +*/ + (object)) +{ + return LISTP (object) ? Qt : Qnil; +} + +DEFUN ("nlistp", Fnlistp, 1, 1, 0, /* +Return t if OBJECT is not a list. `nil' is a list. +*/ + (object)) +{ + return LISTP (object) ? Qnil : Qt; +} + +DEFUN ("true-list-p", Ftrue_list_p, 1, 1, 0, /* +Return t if OBJECT is a non-dotted, i.e. nil-terminated, list. +*/ + (object)) +{ + return TRUE_LIST_P (object) ? Qt : Qnil; +} + +DEFUN ("symbolp", Fsymbolp, 1, 1, 0, /* +Return t if OBJECT is a symbol. +*/ + (object)) +{ + return SYMBOLP (object) ? Qt : Qnil; +} + +DEFUN ("keywordp", Fkeywordp, 1, 1, 0, /* +Return t if OBJECT is a keyword. +*/ + (object)) +{ + return KEYWORDP (object) ? Qt : Qnil; +} + +DEFUN ("vectorp", Fvectorp, 1, 1, 0, /* +Return t if OBJECT is a vector. +*/ + (object)) +{ + return VECTORP (object) ? Qt : Qnil; +} + +DEFUN ("bit-vector-p", Fbit_vector_p, 1, 1, 0, /* +Return t if OBJECT is a bit vector. +*/ + (object)) +{ + return BIT_VECTORP (object) ? Qt : Qnil; +} + +DEFUN ("stringp", Fstringp, 1, 1, 0, /* +Return t if OBJECT is a string. +*/ + (object)) +{ + return STRINGP (object) ? Qt : Qnil; +} + +DEFUN ("arrayp", Farrayp, 1, 1, 0, /* +Return t if OBJECT is an array (string, vector, or bit vector). +*/ + (object)) +{ + return (VECTORP (object) || + STRINGP (object) || + BIT_VECTORP (object)) + ? Qt : Qnil; +} + +DEFUN ("sequencep", Fsequencep, 1, 1, 0, /* +Return t if OBJECT is a sequence (list or array). +*/ + (object)) +{ + return (LISTP (object) || + VECTORP (object) || + STRINGP (object) || + BIT_VECTORP (object)) + ? Qt : Qnil; +} + +DEFUN ("markerp", Fmarkerp, 1, 1, 0, /* +Return t if OBJECT is a marker (editor pointer). +*/ + (object)) +{ + return MARKERP (object) ? Qt : Qnil; +} + +DEFUN ("subrp", Fsubrp, 1, 1, 0, /* +Return t if OBJECT is a built-in function. +*/ + (object)) +{ + return SUBRP (object) ? Qt : Qnil; +} + +DEFUN ("subr-min-args", Fsubr_min_args, 1, 1, 0, /* +Return minimum number of args built-in function SUBR may be called with. +*/ + (subr)) +{ + CHECK_SUBR (subr); + return make_int (XSUBR (subr)->min_args); +} + +DEFUN ("subr-max-args", Fsubr_max_args, 1, 1, 0, /* +Return maximum number of args built-in function SUBR may be called with, +or nil if it takes an arbitrary number of arguments or is a special form. +*/ + (subr)) +{ + int nargs; + CHECK_SUBR (subr); + nargs = XSUBR (subr)->max_args; + if (nargs == MANY || nargs == UNEVALLED) + return Qnil; + else + return make_int (nargs); +} + +DEFUN ("subr-interactive", Fsubr_interactive, 1, 1, 0, /* +Return the interactive spec of the subr object, or nil. +If non-nil, the return value will be a list whose first element is +`interactive' and whose second element is the interactive spec. +*/ + (subr)) +{ + CONST char *prompt; + CHECK_SUBR (subr); + prompt = XSUBR (subr)->prompt; + return prompt ? list2 (Qinteractive, build_string (prompt)) : Qnil; +} + + +DEFUN ("characterp", Fcharacterp, 1, 1, 0, /* +Return t if OBJECT is a character. +Unlike in XEmacs v19 and FSF Emacs, a character is its own primitive type. +Any character can be converted into an equivalent integer using +`char-int'. To convert the other way, use `int-char'; however, +only some integers can be converted into characters. Such an integer +is called a `char-int'; see `char-int-p'. + +Some functions that work on integers (e.g. the comparison functions +<, <=, =, /=, etc. and the arithmetic functions +, -, *, etc.) +accept characters and implicitly convert them into integers. In +general, functions that work on characters also accept char-ints and +implicitly convert them into characters. WARNING: Neither of these +behaviors is very desirable, and they are maintained for backward +compatibility with old E-Lisp programs that confounded characters and +integers willy-nilly. These behaviors may change in the future; therefore, +do not rely on them. Instead, use the character-specific functions such +as `char='. +*/ + (object)) +{ + return CHARP (object) ? Qt : Qnil; +} + +DEFUN ("char-to-int", Fchar_to_int, 1, 1, 0, /* +Convert a character into an equivalent integer. +The resulting integer will always be non-negative. The integers in +the range 0 - 255 map to characters as follows: + +0 - 31 Control set 0 +32 - 127 ASCII +128 - 159 Control set 1 +160 - 255 Right half of ISO-8859-1 + +If support for Mule does not exist, these are the only valid character +values. When Mule support exists, the values assigned to other characters +may vary depending on the particular version of XEmacs, the order in which +character sets were loaded, etc., and you should not depend on them. +*/ + (ch)) +{ + CHECK_CHAR (ch); + return make_int (XCHAR (ch)); +} + +DEFUN ("int-to-char", Fint_to_char, 1, 1, 0, /* +Convert an integer into the equivalent character. +Not all integers correspond to valid characters; use `char-int-p' to +determine whether this is the case. If the integer cannot be converted, +nil is returned. +*/ + (integer)) +{ + CHECK_INT (integer); + if (CHAR_INTP (integer)) + return make_char (XINT (integer)); + else + return Qnil; +} + +DEFUN ("char-int-p", Fchar_int_p, 1, 1, 0, /* +Return t if OBJECT is an integer that can be converted into a character. +See `char-int'. +*/ + (object)) +{ + return CHAR_INTP (object) ? Qt : Qnil; +} + +DEFUN ("char-or-char-int-p", Fchar_or_char_int_p, 1, 1, 0, /* +Return t if OBJECT is a character or an integer that can be converted into one. +*/ + (object)) +{ + return CHAR_OR_CHAR_INTP (object) ? Qt : Qnil; +} + +DEFUN ("char-or-string-p", Fchar_or_string_p, 1, 1, 0, /* +Return t if OBJECT is a character (or a char-int) or a string. +It is semi-hateful that we allow a char-int here, as it goes against +the name of this function, but it makes the most sense considering the +other steps we take to maintain compatibility with the old character/integer +confoundedness in older versions of E-Lisp. +*/ + (object)) +{ + return CHAR_OR_CHAR_INTP (object) || STRINGP (object) ? Qt : Qnil; +} + +DEFUN ("integerp", Fintegerp, 1, 1, 0, /* +Return t if OBJECT is an integer. +*/ + (object)) +{ + return INTP (object) ? Qt : Qnil; +} + +DEFUN ("integer-or-marker-p", Finteger_or_marker_p, 1, 1, 0, /* +Return t if OBJECT is an integer or a marker (editor pointer). +*/ + (object)) +{ + return INTP (object) || MARKERP (object) ? Qt : Qnil; +} + +DEFUN ("integer-or-char-p", Finteger_or_char_p, 1, 1, 0, /* +Return t if OBJECT is an integer or a character. +*/ + (object)) +{ + return INTP (object) || CHARP (object) ? Qt : Qnil; +} + +DEFUN ("integer-char-or-marker-p", Finteger_char_or_marker_p, 1, 1, 0, /* +Return t if OBJECT is an integer, character or a marker (editor pointer). +*/ + (object)) +{ + return INTP (object) || CHARP (object) || MARKERP (object) ? Qt : Qnil; +} + +DEFUN ("natnump", Fnatnump, 1, 1, 0, /* +Return t if OBJECT is a nonnegative integer. +*/ + (object)) +{ + return NATNUMP (object) ? Qt : Qnil; +} + +DEFUN ("bitp", Fbitp, 1, 1, 0, /* +Return t if OBJECT is a bit (0 or 1). +*/ + (object)) +{ + return BITP (object) ? Qt : Qnil; +} + +DEFUN ("numberp", Fnumberp, 1, 1, 0, /* +Return t if OBJECT is a number (floating point or integer). +*/ + (object)) +{ + return INT_OR_FLOATP (object) ? Qt : Qnil; +} + +DEFUN ("number-or-marker-p", Fnumber_or_marker_p, 1, 1, 0, /* +Return t if OBJECT is a number or a marker. +*/ + (object)) +{ + return INT_OR_FLOATP (object) || MARKERP (object) ? Qt : Qnil; +} + +DEFUN ("number-char-or-marker-p", Fnumber_char_or_marker_p, 1, 1, 0, /* +Return t if OBJECT is a number, character or a marker. +*/ + (object)) +{ + return (INT_OR_FLOATP (object) || + CHARP (object) || + MARKERP (object)) + ? Qt : Qnil; +} + +#ifdef LISP_FLOAT_TYPE +DEFUN ("floatp", Ffloatp, 1, 1, 0, /* +Return t if OBJECT is a floating point number. +*/ + (object)) +{ + return FLOATP (object) ? Qt : Qnil; +} +#endif /* LISP_FLOAT_TYPE */ + +DEFUN ("type-of", Ftype_of, 1, 1, 0, /* +Return a symbol representing the type of OBJECT. +*/ + (object)) +{ + switch (XTYPE (object)) + { + case Lisp_Type_Record: + return intern (XRECORD_LHEADER_IMPLEMENTATION (object)->name); + + case Lisp_Type_Char: return Qcharacter; + + default: return Qinteger; + } +} + + +/* Extract and set components of lists */ + +DEFUN ("car", Fcar, 1, 1, 0, /* +Return the car of LIST. If arg is nil, return nil. +Error if arg is not nil and not a cons cell. See also `car-safe'. +*/ + (list)) +{ + while (1) + { + if (CONSP (list)) + return XCAR (list); + else if (NILP (list)) + return Qnil; + else + list = wrong_type_argument (Qlistp, list); + } +} + +DEFUN ("car-safe", Fcar_safe, 1, 1, 0, /* +Return the car of OBJECT if it is a cons cell, or else nil. +*/ + (object)) +{ + return CONSP (object) ? XCAR (object) : Qnil; +} + +DEFUN ("cdr", Fcdr, 1, 1, 0, /* +Return the cdr of LIST. If arg is nil, return nil. +Error if arg is not nil and not a cons cell. See also `cdr-safe'. +*/ + (list)) +{ + while (1) + { + if (CONSP (list)) + return XCDR (list); + else if (NILP (list)) + return Qnil; + else + list = wrong_type_argument (Qlistp, list); + } +} + +DEFUN ("cdr-safe", Fcdr_safe, 1, 1, 0, /* +Return the cdr of OBJECT if it is a cons cell, else nil. +*/ + (object)) +{ + return CONSP (object) ? XCDR (object) : Qnil; +} + +DEFUN ("setcar", Fsetcar, 2, 2, 0, /* +Set the car of CONSCELL to be NEWCAR. Return NEWCAR. +*/ + (conscell, newcar)) +{ + if (!CONSP (conscell)) + conscell = wrong_type_argument (Qconsp, conscell); + + XCAR (conscell) = newcar; + return newcar; +} + +DEFUN ("setcdr", Fsetcdr, 2, 2, 0, /* +Set the cdr of CONSCELL to be NEWCDR. Return NEWCDR. +*/ + (conscell, newcdr)) +{ + if (!CONSP (conscell)) + conscell = wrong_type_argument (Qconsp, conscell); + + XCDR (conscell) = newcdr; + return newcdr; +} + +/* Find the function at the end of a chain of symbol function indirections. + + If OBJECT is a symbol, find the end of its function chain and + return the value found there. If OBJECT is not a symbol, just + return it. If there is a cycle in the function chain, signal a + cyclic-function-indirection error. + + This is like Findirect_function, except that it doesn't signal an + error if the chain ends up unbound. */ +Lisp_Object +indirect_function (Lisp_Object object, int errorp) +{ +#define FUNCTION_INDIRECTION_SUSPICION_LENGTH 16 + Lisp_Object tortoise, hare; + int count; + + for (hare = tortoise = object, count = 0; + SYMBOLP (hare); + hare = XSYMBOL (hare)->function, count++) + { + if (count < FUNCTION_INDIRECTION_SUSPICION_LENGTH) continue; + + if (count & 1) + tortoise = XSYMBOL (tortoise)->function; + if (EQ (hare, tortoise)) + return Fsignal (Qcyclic_function_indirection, list1 (object)); + } + + if (errorp && UNBOUNDP (hare)) + signal_void_function_error (object); + + return hare; +} + +DEFUN ("indirect-function", Findirect_function, 1, 1, 0, /* +Return the function at the end of OBJECT's function chain. +If OBJECT is a symbol, follow all function indirections and return +the final function binding. +If OBJECT is not a symbol, just return it. +Signal a void-function error if the final symbol is unbound. +Signal a cyclic-function-indirection error if there is a loop in the +function chain of symbols. +*/ + (object)) +{ + return indirect_function (object, 1); +} + +/* Extract and set vector and string elements */ + +DEFUN ("aref", Faref, 2, 2, 0, /* +Return the element of ARRAY at index INDEX. +ARRAY may be a vector, bit vector, or string. INDEX starts at 0. +*/ + (array, index_)) +{ + EMACS_INT idx; + + retry: + + if (INTP (index_)) idx = XINT (index_); + else if (CHARP (index_)) idx = XCHAR (index_); /* yuck! */ + else + { + index_ = wrong_type_argument (Qinteger_or_char_p, index_); + goto retry; + } + + if (idx < 0) goto range_error; + + if (VECTORP (array)) + { + if (idx >= XVECTOR_LENGTH (array)) goto range_error; + return XVECTOR_DATA (array)[idx]; + } + else if (BIT_VECTORP (array)) + { + if (idx >= bit_vector_length (XBIT_VECTOR (array))) goto range_error; + return make_int (bit_vector_bit (XBIT_VECTOR (array), idx)); + } + else if (STRINGP (array)) + { + if (idx >= XSTRING_CHAR_LENGTH (array)) goto range_error; + return make_char (string_char (XSTRING (array), idx)); + } +#ifdef LOSING_BYTECODE + else if (COMPILED_FUNCTIONP (array)) + { + /* Weird, gross compatibility kludge */ + return Felt (array, index_); + } +#endif + else + { + check_losing_bytecode ("aref", array); + array = wrong_type_argument (Qarrayp, array); + goto retry; + } + + range_error: + args_out_of_range (array, index_); + return Qnil; /* not reached */ +} + +DEFUN ("aset", Faset, 3, 3, 0, /* +Store into the element of ARRAY at index INDEX the value NEWVAL. +ARRAY may be a vector, bit vector, or string. INDEX starts at 0. +*/ + (array, index_, newval)) +{ + EMACS_INT idx; + + retry: + + if (INTP (index_)) idx = XINT (index_); + else if (CHARP (index_)) idx = XCHAR (index_); /* yuck! */ + else + { + index_ = wrong_type_argument (Qinteger_or_char_p, index_); + goto retry; + } + + if (idx < 0) goto range_error; + + if (VECTORP (array)) + { + if (idx >= XVECTOR_LENGTH (array)) goto range_error; + XVECTOR_DATA (array)[idx] = newval; + } + else if (BIT_VECTORP (array)) + { + if (idx >= bit_vector_length (XBIT_VECTOR (array))) goto range_error; + CHECK_BIT (newval); + set_bit_vector_bit (XBIT_VECTOR (array), idx, !ZEROP (newval)); + } + else if (STRINGP (array)) + { + CHECK_CHAR_COERCE_INT (newval); + if (idx >= XSTRING_CHAR_LENGTH (array)) goto range_error; + set_string_char (XSTRING (array), idx, XCHAR (newval)); + bump_string_modiff (array); + } + else + { + array = wrong_type_argument (Qarrayp, array); + goto retry; + } + + return newval; + + range_error: + args_out_of_range (array, index_); + return Qnil; /* not reached */ +} + + +/**********************************************************************/ +/* Arithmetic functions */ +/**********************************************************************/ +typedef struct +{ + int int_p; + union + { + EMACS_INT ival; + double dval; + } c; +} int_or_double; + +static void +number_char_or_marker_to_int_or_double (Lisp_Object obj, int_or_double *p) +{ + retry: + p->int_p = 1; + if (INTP (obj)) p->c.ival = XINT (obj); + else if (CHARP (obj)) p->c.ival = XCHAR (obj); + else if (MARKERP (obj)) p->c.ival = marker_position (obj); +#ifdef LISP_FLOAT_TYPE + else if (FLOATP (obj)) p->c.dval = XFLOAT_DATA (obj), p->int_p = 0; +#endif + else + { + obj = wrong_type_argument (Qnumber_char_or_marker_p, obj); + goto retry; + } +} + +static double +number_char_or_marker_to_double (Lisp_Object obj) +{ + retry: + if (INTP (obj)) return (double) XINT (obj); + else if (CHARP (obj)) return (double) XCHAR (obj); + else if (MARKERP (obj)) return (double) marker_position (obj); +#ifdef LISP_FLOAT_TYPE + else if (FLOATP (obj)) return XFLOAT_DATA (obj); +#endif + else + { + obj = wrong_type_argument (Qnumber_char_or_marker_p, obj); + goto retry; + } +} + +static EMACS_INT +integer_char_or_marker_to_int (Lisp_Object obj) +{ + retry: + if (INTP (obj)) return XINT (obj); + else if (CHARP (obj)) return XCHAR (obj); + else if (MARKERP (obj)) return marker_position (obj); + else + { + obj = wrong_type_argument (Qinteger_char_or_marker_p, obj); + goto retry; + } +} + +#define ARITHCOMPARE_MANY(op) \ +{ \ + int_or_double iod1, iod2, *p = &iod1, *q = &iod2; \ + Lisp_Object *args_end = args + nargs; \ + \ + number_char_or_marker_to_int_or_double (*args++, p); \ + \ + while (args < args_end) \ + { \ + number_char_or_marker_to_int_or_double (*args++, q); \ + \ + if (!((p->int_p && q->int_p) ? \ + (p->c.ival op q->c.ival) : \ + ((p->int_p ? (double) p->c.ival : p->c.dval) op \ + (q->int_p ? (double) q->c.ival : q->c.dval)))) \ + return Qnil; \ + \ + { /* swap */ int_or_double *r = p; p = q; q = r; } \ + } \ + return Qt; \ +} + +DEFUN ("=", Feqlsign, 1, MANY, 0, /* +Return t if all the arguments are numerically equal. +The arguments may be numbers, characters or markers. +*/ + (int nargs, Lisp_Object *args)) +{ + ARITHCOMPARE_MANY (==) +} + +DEFUN ("<", Flss, 1, MANY, 0, /* +Return t if the sequence of arguments is monotonically increasing. +The arguments may be numbers, characters or markers. +*/ + (int nargs, Lisp_Object *args)) +{ + ARITHCOMPARE_MANY (<) +} + +DEFUN (">", Fgtr, 1, MANY, 0, /* +Return t if the sequence of arguments is monotonically decreasing. +The arguments may be numbers, characters or markers. +*/ + (int nargs, Lisp_Object *args)) +{ + ARITHCOMPARE_MANY (>) +} + +DEFUN ("<=", Fleq, 1, MANY, 0, /* +Return t if the sequence of arguments is monotonically nondecreasing. +The arguments may be numbers, characters or markers. +*/ + (int nargs, Lisp_Object *args)) +{ + ARITHCOMPARE_MANY (<=) +} + +DEFUN (">=", Fgeq, 1, MANY, 0, /* +Return t if the sequence of arguments is monotonically nonincreasing. +The arguments may be numbers, characters or markers. +*/ + (int nargs, Lisp_Object *args)) +{ + ARITHCOMPARE_MANY (>=) +} + +DEFUN ("/=", Fneq, 1, MANY, 0, /* +Return t if no two arguments are numerically equal. +The arguments may be numbers, characters or markers. +*/ + (int nargs, Lisp_Object *args)) +{ + Lisp_Object *args_end = args + nargs; + Lisp_Object *p, *q; + + /* Unlike all the other comparisons, this is an N*N algorithm. + We could use a hash table for nargs > 50 to make this linear. */ + for (p = args; p < args_end; p++) + { + int_or_double iod1, iod2; + number_char_or_marker_to_int_or_double (*p, &iod1); + + for (q = p + 1; q < args_end; q++) + { + number_char_or_marker_to_int_or_double (*q, &iod2); + + if (!((iod1.int_p && iod2.int_p) ? + (iod1.c.ival != iod2.c.ival) : + ((iod1.int_p ? (double) iod1.c.ival : iod1.c.dval) != + (iod2.int_p ? (double) iod2.c.ival : iod2.c.dval)))) + return Qnil; + } + } + return Qt; +} + +DEFUN ("zerop", Fzerop, 1, 1, 0, /* +Return t if NUMBER is zero. +*/ + (number)) +{ + retry: + if (INTP (number)) + return EQ (number, Qzero) ? Qt : Qnil; +#ifdef LISP_FLOAT_TYPE + else if (FLOATP (number)) + return XFLOAT_DATA (number) == 0.0 ? Qt : Qnil; +#endif /* LISP_FLOAT_TYPE */ + else + { + number = wrong_type_argument (Qnumberp, number); + goto retry; + } +} + +/* Convert between a 32-bit value and a cons of two 16-bit values. + This is used to pass 32-bit integers to and from the user. + Use time_to_lisp() and lisp_to_time() for time values. + + If you're thinking of using this to store a pointer into a Lisp Object + for internal purposes (such as when calling record_unwind_protect()), + try using make_opaque_ptr()/get_opaque_ptr() instead. */ +Lisp_Object +word_to_lisp (unsigned int item) +{ + return Fcons (make_int (item >> 16), make_int (item & 0xffff)); +} + +unsigned int +lisp_to_word (Lisp_Object item) +{ + if (INTP (item)) + return XINT (item); + else + { + Lisp_Object top = Fcar (item); + Lisp_Object bot = Fcdr (item); + CHECK_INT (top); + CHECK_INT (bot); + return (XINT (top) << 16) | (XINT (bot) & 0xffff); + } +} + + +DEFUN ("number-to-string", Fnumber_to_string, 1, 1, 0, /* +Convert NUM to a string by printing it in decimal. +Uses a minus sign if negative. +NUM may be an integer or a floating point number. +*/ + (num)) +{ + char buffer[VALBITS]; + + CHECK_INT_OR_FLOAT (num); + +#ifdef LISP_FLOAT_TYPE + if (FLOATP (num)) + { + char pigbuf[350]; /* see comments in float_to_string */ + + float_to_string (pigbuf, XFLOAT_DATA (num)); + return build_string (pigbuf); + } +#endif /* LISP_FLOAT_TYPE */ + + long_to_string (buffer, XINT (num)); + return build_string (buffer); +} + +static int +digit_to_number (int character, int base) +{ + /* Assumes ASCII */ + int digit = ((character >= '0' && character <= '9') ? character - '0' : + (character >= 'a' && character <= 'z') ? character - 'a' + 10 : + (character >= 'A' && character <= 'Z') ? character - 'A' + 10 : + -1); + + return digit >= base ? -1 : digit; +} + +DEFUN ("string-to-number", Fstring_to_number, 1, 2, 0, /* +Convert STRING to a number by parsing it as a decimal number. +This parses both integers and floating point numbers. +It ignores leading spaces and tabs. + +If BASE, interpret STRING as a number in that base. If BASE isn't +present, base 10 is used. BASE must be between 2 and 16 (inclusive). +Floating point numbers always use base 10. +*/ + (string, base)) +{ + char *p; + int b; + + CHECK_STRING (string); + + if (NILP (base)) + b = 10; + else + { + CHECK_INT (base); + b = XINT (base); + check_int_range (b, 2, 16); + } + + p = (char *) XSTRING_DATA (string); + + /* Skip any whitespace at the front of the number. Some versions of + atoi do this anyway, so we might as well make Emacs lisp consistent. */ + while (*p == ' ' || *p == '\t') + p++; + +#ifdef LISP_FLOAT_TYPE + if (isfloat_string (p)) + return make_float (atof (p)); +#endif /* LISP_FLOAT_TYPE */ + + if (b == 10) + { + /* Use the system-provided functions for base 10. */ +#if SIZEOF_EMACS_INT == SIZEOF_INT + return make_int (atoi (p)); +#elif SIZEOF_EMACS_INT == SIZEOF_LONG + return make_int (atol (p)); +#elif SIZEOF_EMACS_INT == SIZEOF_LONG_LONG + return make_int (atoll (p)); +#endif + } + else + { + int digit, negative = 1; + EMACS_INT v = 0; + + if (*p == '-') + { + negative = -1; + p++; + } + else if (*p == '+') + p++; + while (1) + { + digit = digit_to_number (*p++, b); + if (digit < 0) + break; + v = v * b + digit; + } + return make_int (negative * v); + } +} + + +DEFUN ("+", Fplus, 0, MANY, 0, /* +Return sum of any number of arguments. +The arguments should all be numbers, characters or markers. +*/ + (int nargs, Lisp_Object *args)) +{ + EMACS_INT iaccum = 0; + Lisp_Object *args_end = args + nargs; + + while (args < args_end) + { + int_or_double iod; + number_char_or_marker_to_int_or_double (*args++, &iod); + if (iod.int_p) + iaccum += iod.c.ival; + else + { + double daccum = (double) iaccum + iod.c.dval; + while (args < args_end) + daccum += number_char_or_marker_to_double (*args++); + return make_float (daccum); + } + } + + return make_int (iaccum); +} + +DEFUN ("-", Fminus, 1, MANY, 0, /* +Negate number or subtract numbers, characters or markers. +With one arg, negates it. With more than one arg, +subtracts all but the first from the first. +*/ + (int nargs, Lisp_Object *args)) +{ + EMACS_INT iaccum; + double daccum; + Lisp_Object *args_end = args + nargs; + int_or_double iod; + + number_char_or_marker_to_int_or_double (*args++, &iod); + if (iod.int_p) + iaccum = nargs > 1 ? iod.c.ival : - iod.c.ival; + else + { + daccum = nargs > 1 ? iod.c.dval : - iod.c.dval; + goto do_float; + } + + while (args < args_end) + { + number_char_or_marker_to_int_or_double (*args++, &iod); + if (iod.int_p) + iaccum -= iod.c.ival; + else + { + daccum = (double) iaccum - iod.c.dval; + goto do_float; + } + } + + return make_int (iaccum); + + do_float: + for (; args < args_end; args++) + daccum -= number_char_or_marker_to_double (*args); + return make_float (daccum); +} + +DEFUN ("*", Ftimes, 0, MANY, 0, /* +Return product of any number of arguments. +The arguments should all be numbers, characters or markers. +*/ + (int nargs, Lisp_Object *args)) +{ + EMACS_INT iaccum = 1; + Lisp_Object *args_end = args + nargs; + + while (args < args_end) + { + int_or_double iod; + number_char_or_marker_to_int_or_double (*args++, &iod); + if (iod.int_p) + iaccum *= iod.c.ival; + else + { + double daccum = (double) iaccum * iod.c.dval; + while (args < args_end) + daccum *= number_char_or_marker_to_double (*args++); + return make_float (daccum); + } + } + + return make_int (iaccum); +} + +DEFUN ("/", Fquo, 1, MANY, 0, /* +Return first argument divided by all the remaining arguments. +The arguments must be numbers, characters or markers. +With one argument, reciprocates the argument. +*/ + (int nargs, Lisp_Object *args)) +{ + EMACS_INT iaccum; + double daccum; + Lisp_Object *args_end = args + nargs; + int_or_double iod; + + if (nargs == 1) + iaccum = 1; + else + { + number_char_or_marker_to_int_or_double (*args++, &iod); + if (iod.int_p) + iaccum = iod.c.ival; + else + { + daccum = iod.c.dval; + goto divide_floats; + } + } + + while (args < args_end) + { + number_char_or_marker_to_int_or_double (*args++, &iod); + if (iod.int_p) + { + if (iod.c.ival == 0) goto divide_by_zero; + iaccum /= iod.c.ival; + } + else + { + if (iod.c.dval == 0) goto divide_by_zero; + daccum = (double) iaccum / iod.c.dval; + goto divide_floats; + } + } + + return make_int (iaccum); + + divide_floats: + for (; args < args_end; args++) + { + double dval = number_char_or_marker_to_double (*args); + if (dval == 0) goto divide_by_zero; + daccum /= dval; + } + return make_float (daccum); + + divide_by_zero: + Fsignal (Qarith_error, Qnil); + return Qnil; /* not reached */ +} + +DEFUN ("max", Fmax, 1, MANY, 0, /* +Return largest of all the arguments. +All arguments must be numbers, characters or markers. +The value is always a number; markers and characters are converted +to numbers. +*/ + (int nargs, Lisp_Object *args)) +{ + EMACS_INT imax; + double dmax; + Lisp_Object *args_end = args + nargs; + int_or_double iod; + + number_char_or_marker_to_int_or_double (*args++, &iod); + if (iod.int_p) + imax = iod.c.ival; + else + { + dmax = iod.c.dval; + goto max_floats; + } + + while (args < args_end) + { + number_char_or_marker_to_int_or_double (*args++, &iod); + if (iod.int_p) + { + if (imax < iod.c.ival) imax = iod.c.ival; + } + else + { + dmax = (double) imax; + if (dmax < iod.c.dval) dmax = iod.c.dval; + goto max_floats; + } + } + + return make_int (imax); + + max_floats: + while (args < args_end) + { + double dval = number_char_or_marker_to_double (*args++); + if (dmax < dval) dmax = dval; + } + return make_float (dmax); +} + +DEFUN ("min", Fmin, 1, MANY, 0, /* +Return smallest of all the arguments. +All arguments must be numbers, characters or markers. +The value is always a number; markers and characters are converted +to numbers. +*/ + (int nargs, Lisp_Object *args)) +{ + EMACS_INT imin; + double dmin; + Lisp_Object *args_end = args + nargs; + int_or_double iod; + + number_char_or_marker_to_int_or_double (*args++, &iod); + if (iod.int_p) + imin = iod.c.ival; + else + { + dmin = iod.c.dval; + goto min_floats; + } + + while (args < args_end) + { + number_char_or_marker_to_int_or_double (*args++, &iod); + if (iod.int_p) + { + if (imin > iod.c.ival) imin = iod.c.ival; + } + else + { + dmin = (double) imin; + if (dmin > iod.c.dval) dmin = iod.c.dval; + goto min_floats; + } + } + + return make_int (imin); + + min_floats: + while (args < args_end) + { + double dval = number_char_or_marker_to_double (*args++); + if (dmin > dval) dmin = dval; + } + return make_float (dmin); +} + +DEFUN ("logand", Flogand, 0, MANY, 0, /* +Return bitwise-and of all the arguments. +Arguments may be integers, or markers or characters converted to integers. +*/ + (int nargs, Lisp_Object *args)) +{ + EMACS_INT bits = ~0; + Lisp_Object *args_end = args + nargs; + + while (args < args_end) + bits &= integer_char_or_marker_to_int (*args++); + + return make_int (bits); +} + +DEFUN ("logior", Flogior, 0, MANY, 0, /* +Return bitwise-or of all the arguments. +Arguments may be integers, or markers or characters converted to integers. +*/ + (int nargs, Lisp_Object *args)) +{ + EMACS_INT bits = 0; + Lisp_Object *args_end = args + nargs; + + while (args < args_end) + bits |= integer_char_or_marker_to_int (*args++); + + return make_int (bits); +} + +DEFUN ("logxor", Flogxor, 0, MANY, 0, /* +Return bitwise-exclusive-or of all the arguments. +Arguments may be integers, or markers or characters converted to integers. +*/ + (int nargs, Lisp_Object *args)) +{ + EMACS_INT bits = 0; + Lisp_Object *args_end = args + nargs; + + while (args < args_end) + bits ^= integer_char_or_marker_to_int (*args++); + + return make_int (bits); +} + +DEFUN ("lognot", Flognot, 1, 1, 0, /* +Return the bitwise complement of NUMBER. +NUMBER may be an integer, marker or character converted to integer. +*/ + (number)) +{ + return make_int (~ integer_char_or_marker_to_int (number)); +} + +DEFUN ("%", Frem, 2, 2, 0, /* +Return remainder of first arg divided by second. +Both must be integers, characters or markers. +*/ + (num1, num2)) +{ + EMACS_INT ival1 = integer_char_or_marker_to_int (num1); + EMACS_INT ival2 = integer_char_or_marker_to_int (num2); + + if (ival2 == 0) + Fsignal (Qarith_error, Qnil); + + return make_int (ival1 % ival2); +} + +/* Note, ANSI *requires* the presence of the fmod() library routine. + If your system doesn't have it, complain to your vendor, because + that is a bug. */ + +#ifndef HAVE_FMOD +double +fmod (double f1, double f2) +{ + if (f2 < 0.0) + f2 = -f2; + return f1 - f2 * floor (f1/f2); +} +#endif /* ! HAVE_FMOD */ + + +DEFUN ("mod", Fmod, 2, 2, 0, /* +Return X modulo Y. +The result falls between zero (inclusive) and Y (exclusive). +Both X and Y must be numbers, characters or markers. +If either argument is a float, a float will be returned. +*/ + (x, y)) +{ + int_or_double iod1, iod2; + number_char_or_marker_to_int_or_double (x, &iod1); + number_char_or_marker_to_int_or_double (y, &iod2); + +#ifdef LISP_FLOAT_TYPE + if (!iod1.int_p || !iod2.int_p) + { + double dval1 = iod1.int_p ? (double) iod1.c.ival : iod1.c.dval; + double dval2 = iod2.int_p ? (double) iod2.c.ival : iod2.c.dval; + if (dval2 == 0) goto divide_by_zero; + dval1 = fmod (dval1, dval2); + + /* If the "remainder" comes out with the wrong sign, fix it. */ + if (dval2 < 0 ? dval1 > 0 : dval1 < 0) + dval1 += dval2; + + return make_float (dval1); + } +#endif /* LISP_FLOAT_TYPE */ + { + EMACS_INT ival; + if (iod2.c.ival == 0) goto divide_by_zero; + + ival = iod1.c.ival % iod2.c.ival; + + /* If the "remainder" comes out with the wrong sign, fix it. */ + if (iod2.c.ival < 0 ? ival > 0 : ival < 0) + ival += iod2.c.ival; + + return make_int (ival); + } + + divide_by_zero: + Fsignal (Qarith_error, Qnil); + return Qnil; /* not reached */ +} + +DEFUN ("ash", Fash, 2, 2, 0, /* +Return VALUE with its bits shifted left by COUNT. +If COUNT is negative, shifting is actually to the right. +In this case, the sign bit is duplicated. +*/ + (value, count)) +{ + CHECK_INT_COERCE_CHAR (value); + CONCHECK_INT (count); + + return make_int (XINT (count) > 0 ? + XINT (value) << XINT (count) : + XINT (value) >> -XINT (count)); +} + +DEFUN ("lsh", Flsh, 2, 2, 0, /* +Return VALUE with its bits shifted left by COUNT. +If COUNT is negative, shifting is actually to the right. +In this case, zeros are shifted in on the left. +*/ + (value, count)) +{ + CHECK_INT_COERCE_CHAR (value); + CONCHECK_INT (count); + + return make_int (XINT (count) > 0 ? + XUINT (value) << XINT (count) : + XUINT (value) >> -XINT (count)); +} + +DEFUN ("1+", Fadd1, 1, 1, 0, /* +Return NUMBER plus one. NUMBER may be a number, character or marker. +Markers and characters are converted to integers. +*/ + (number)) +{ + retry: + + if (INTP (number)) return make_int (XINT (number) + 1); + if (CHARP (number)) return make_int (XCHAR (number) + 1); + if (MARKERP (number)) return make_int (marker_position (number) + 1); +#ifdef LISP_FLOAT_TYPE + if (FLOATP (number)) return make_float (XFLOAT_DATA (number) + 1.0); +#endif /* LISP_FLOAT_TYPE */ + + number = wrong_type_argument (Qnumber_char_or_marker_p, number); + goto retry; +} + +DEFUN ("1-", Fsub1, 1, 1, 0, /* +Return NUMBER minus one. NUMBER may be a number, character or marker. +Markers and characters are converted to integers. +*/ + (number)) +{ + retry: + + if (INTP (number)) return make_int (XINT (number) - 1); + if (CHARP (number)) return make_int (XCHAR (number) - 1); + if (MARKERP (number)) return make_int (marker_position (number) - 1); +#ifdef LISP_FLOAT_TYPE + if (FLOATP (number)) return make_float (XFLOAT_DATA (number) - 1.0); +#endif /* LISP_FLOAT_TYPE */ + + number = wrong_type_argument (Qnumber_char_or_marker_p, number); + goto retry; +} + + +/************************************************************************/ +/* weak lists */ +/************************************************************************/ + +/* A weak list is like a normal list except that elements automatically + disappear when no longer in use, i.e. when no longer GC-protected. + The basic idea is that we don't mark the elements during GC, but + wait for them to be marked elsewhere. If they're not marked, we + remove them. This is analogous to weak hash tables; see the explanation + there for more info. */ + +static Lisp_Object Vall_weak_lists; /* Gemarke es nicht!!! */ + +static Lisp_Object encode_weak_list_type (enum weak_list_type type); + +static Lisp_Object +mark_weak_list (Lisp_Object obj) +{ + return Qnil; /* nichts ist gemarkt */ +} + +static void +print_weak_list (Lisp_Object obj, Lisp_Object printcharfun, int escapeflag) +{ + if (print_readably) + error ("printing unreadable object #<weak-list>"); + + write_c_string ("#<weak-list ", printcharfun); + print_internal (encode_weak_list_type (XWEAK_LIST (obj)->type), + printcharfun, 0); + write_c_string (" ", printcharfun); + print_internal (XWEAK_LIST (obj)->list, printcharfun, escapeflag); + write_c_string (">", printcharfun); +} + +static int +weak_list_equal (Lisp_Object obj1, Lisp_Object obj2, int depth) +{ + struct weak_list *w1 = XWEAK_LIST (obj1); + struct weak_list *w2 = XWEAK_LIST (obj2); + + return ((w1->type == w2->type) && + internal_equal (w1->list, w2->list, depth + 1)); +} + +static unsigned long +weak_list_hash (Lisp_Object obj, int depth) +{ + struct weak_list *w = XWEAK_LIST (obj); + + return HASH2 ((unsigned long) w->type, + internal_hash (w->list, depth + 1)); +} + +Lisp_Object +make_weak_list (enum weak_list_type type) +{ + Lisp_Object result; + struct weak_list *wl = + alloc_lcrecord_type (struct weak_list, &lrecord_weak_list); + + wl->list = Qnil; + wl->type = type; + XSETWEAK_LIST (result, wl); + wl->next_weak = Vall_weak_lists; + Vall_weak_lists = result; + return result; +} + +static const struct lrecord_description weak_list_description[] = { + { XD_LISP_OBJECT, offsetof(struct weak_list, list), 1 }, + { XD_LO_LINK, offsetof(struct weak_list, next_weak) }, + { XD_END } +}; + +DEFINE_LRECORD_IMPLEMENTATION ("weak-list", weak_list, + mark_weak_list, print_weak_list, + 0, weak_list_equal, weak_list_hash, + weak_list_description, + struct weak_list); +/* + -- we do not mark the list elements (either the elements themselves + or the cons cells that hold them) in the normal marking phase. + -- at the end of marking, we go through all weak lists that are + marked, and mark the cons cells that hold all marked + objects, and possibly parts of the objects themselves. + (See alloc.c, "after-mark".) + -- after that, we prune away all the cons cells that are not marked. + + WARNING WARNING WARNING WARNING WARNING: + + The code in the following two functions is *unbelievably* tricky. + Don't mess with it. You'll be sorry. + + Linked lists just majorly suck, d'ya know? +*/ + +int +finish_marking_weak_lists (void) +{ + Lisp_Object rest; + int did_mark = 0; + + for (rest = Vall_weak_lists; + !NILP (rest); + rest = XWEAK_LIST (rest)->next_weak) + { + Lisp_Object rest2; + enum weak_list_type type = XWEAK_LIST (rest)->type; + + if (! marked_p (rest)) + /* The weak list is probably garbage. Ignore it. */ + continue; + + for (rest2 = XWEAK_LIST (rest)->list; + /* We need to be trickier since we're inside of GC; + use CONSP instead of !NILP in case of user-visible + imperfect lists */ + CONSP (rest2); + rest2 = XCDR (rest2)) + { + Lisp_Object elem; + /* If the element is "marked" (meaning depends on the type + of weak list), we need to mark the cons containing the + element, and maybe the element itself (if only some part + was already marked). */ + int need_to_mark_cons = 0; + int need_to_mark_elem = 0; + + /* If a cons is already marked, then its car is already marked + (either because of an external pointer or because of + a previous call to this function), and likewise for all + the rest of the elements in the list, so we can stop now. */ + if (marked_p (rest2)) + break; + + elem = XCAR (rest2); + + switch (type) + { + case WEAK_LIST_SIMPLE: + if (marked_p (elem)) + need_to_mark_cons = 1; + break; + + case WEAK_LIST_ASSOC: + if (!CONSP (elem)) + { + /* just leave bogus elements there */ + need_to_mark_cons = 1; + need_to_mark_elem = 1; + } + else if (marked_p (XCAR (elem)) && + marked_p (XCDR (elem))) + { + need_to_mark_cons = 1; + /* We still need to mark elem, because it's + probably not marked. */ + need_to_mark_elem = 1; + } + break; + + case WEAK_LIST_KEY_ASSOC: + if (!CONSP (elem)) + { + /* just leave bogus elements there */ + need_to_mark_cons = 1; + need_to_mark_elem = 1; + } + else if (marked_p (XCAR (elem))) + { + need_to_mark_cons = 1; + /* We still need to mark elem and XCDR (elem); + marking elem does both */ + need_to_mark_elem = 1; + } + break; + + case WEAK_LIST_VALUE_ASSOC: + if (!CONSP (elem)) + { + /* just leave bogus elements there */ + need_to_mark_cons = 1; + need_to_mark_elem = 1; + } + else if (marked_p (XCDR (elem))) + { + need_to_mark_cons = 1; + /* We still need to mark elem and XCAR (elem); + marking elem does both */ + need_to_mark_elem = 1; + } + break; + + default: + abort (); + } + + if (need_to_mark_elem && ! marked_p (elem)) + { + mark_object (elem); + did_mark = 1; + } + + /* We also need to mark the cons that holds the elem or + assoc-pair. We do *not* want to call (mark_object) here + because that will mark the entire list; we just want to + mark the cons itself. + */ + if (need_to_mark_cons) + { + Lisp_Cons *c = XCONS (rest2); + if (!CONS_MARKED_P (c)) + { + MARK_CONS (c); + did_mark = 1; + } + } + } + + /* In case of imperfect list, need to mark the final cons + because we're not removing it */ + if (!NILP (rest2) && ! marked_p (rest2)) + { + mark_object (rest2); + did_mark = 1; + } + } + + return did_mark; +} + +void +prune_weak_lists (void) +{ + Lisp_Object rest, prev = Qnil; + + for (rest = Vall_weak_lists; + !NILP (rest); + rest = XWEAK_LIST (rest)->next_weak) + { + if (! (marked_p (rest))) + { + /* This weak list itself is garbage. Remove it from the list. */ + if (NILP (prev)) + Vall_weak_lists = XWEAK_LIST (rest)->next_weak; + else + XWEAK_LIST (prev)->next_weak = + XWEAK_LIST (rest)->next_weak; + } + else + { + Lisp_Object rest2, prev2 = Qnil; + Lisp_Object tortoise; + int go_tortoise = 0; + + for (rest2 = XWEAK_LIST (rest)->list, tortoise = rest2; + /* We need to be trickier since we're inside of GC; + use CONSP instead of !NILP in case of user-visible + imperfect lists */ + CONSP (rest2);) + { + /* It suffices to check the cons for marking, + regardless of the type of weak list: + + -- if the cons is pointed to somewhere else, + then it should stay around and will be marked. + -- otherwise, if it should stay around, it will + have been marked in finish_marking_weak_lists(). + -- otherwise, it's not marked and should disappear. + */ + if (! marked_p (rest2)) + { + /* bye bye :-( */ + if (NILP (prev2)) + XWEAK_LIST (rest)->list = XCDR (rest2); + else + XCDR (prev2) = XCDR (rest2); + rest2 = XCDR (rest2); + /* Ouch. Circularity checking is even trickier + than I thought. When we cut out a link + like this, we can't advance the turtle or + it'll catch up to us. Imagine that we're + standing on floor tiles and moving forward -- + what we just did here is as if the floor + tile under us just disappeared and all the + ones ahead of us slid one tile towards us. + In other words, we didn't move at all; + if the tortoise was one step behind us + previously, it still is, and therefore + it must not move. */ + } + else + { + prev2 = rest2; + + /* Implementing circularity checking is trickier here + than in other places because we have to guarantee + that we've processed all elements before exiting + due to a circularity. (In most places, an error + is issued upon encountering a circularity, so it + doesn't really matter if all elements are processed.) + The idea is that we process along with the hare + rather than the tortoise. If at any point in + our forward process we encounter the tortoise, + we must have already visited the spot, so we exit. + (If we process with the tortoise, we can fail to + process cases where a cons points to itself, or + where cons A points to cons B, which points to + cons A.) */ + + rest2 = XCDR (rest2); + if (go_tortoise) + tortoise = XCDR (tortoise); + go_tortoise = !go_tortoise; + if (EQ (rest2, tortoise)) + break; + } + } + + prev = rest; + } + } +} + +static enum weak_list_type +decode_weak_list_type (Lisp_Object symbol) +{ + CHECK_SYMBOL (symbol); + if (EQ (symbol, Qsimple)) return WEAK_LIST_SIMPLE; + if (EQ (symbol, Qassoc)) return WEAK_LIST_ASSOC; + if (EQ (symbol, Qold_assoc)) return WEAK_LIST_ASSOC; /* EBOLA ALERT! */ + if (EQ (symbol, Qkey_assoc)) return WEAK_LIST_KEY_ASSOC; + if (EQ (symbol, Qvalue_assoc)) return WEAK_LIST_VALUE_ASSOC; + + signal_simple_error ("Invalid weak list type", symbol); + return WEAK_LIST_SIMPLE; /* not reached */ +} + +static Lisp_Object +encode_weak_list_type (enum weak_list_type type) +{ + switch (type) + { + case WEAK_LIST_SIMPLE: return Qsimple; + case WEAK_LIST_ASSOC: return Qassoc; + case WEAK_LIST_KEY_ASSOC: return Qkey_assoc; + case WEAK_LIST_VALUE_ASSOC: return Qvalue_assoc; + default: + abort (); + } + + return Qnil; /* not reached */ +} + +DEFUN ("weak-list-p", Fweak_list_p, 1, 1, 0, /* +Return non-nil if OBJECT is a weak list. +*/ + (object)) +{ + return WEAK_LISTP (object) ? Qt : Qnil; +} + +DEFUN ("make-weak-list", Fmake_weak_list, 0, 1, 0, /* +Return a new weak list object of type TYPE. +A weak list object is an object that contains a list. This list behaves +like any other list except that its elements do not count towards +garbage collection -- if the only pointer to an object in inside a weak +list (other than pointers in similar objects such as weak hash tables), +the object is garbage collected and automatically removed from the list. +This is used internally, for example, to manage the list holding the +children of an extent -- an extent that is unused but has a parent will +still be reclaimed, and will automatically be removed from its parent's +list of children. + +Optional argument TYPE specifies the type of the weak list, and defaults +to `simple'. Recognized types are + +`simple' Objects in the list disappear if not pointed to. +`assoc' Objects in the list disappear if they are conses + and either the car or the cdr of the cons is not + pointed to. +`key-assoc' Objects in the list disappear if they are conses + and the car is not pointed to. +`value-assoc' Objects in the list disappear if they are conses + and the cdr is not pointed to. +*/ + (type)) +{ + if (NILP (type)) + type = Qsimple; + + return make_weak_list (decode_weak_list_type (type)); +} + +DEFUN ("weak-list-type", Fweak_list_type, 1, 1, 0, /* +Return the type of the given weak-list object. +*/ + (weak)) +{ + CHECK_WEAK_LIST (weak); + return encode_weak_list_type (XWEAK_LIST (weak)->type); +} + +DEFUN ("weak-list-list", Fweak_list_list, 1, 1, 0, /* +Return the list contained in a weak-list object. +*/ + (weak)) +{ + CHECK_WEAK_LIST (weak); + return XWEAK_LIST_LIST (weak); +} + +DEFUN ("set-weak-list-list", Fset_weak_list_list, 2, 2, 0, /* +Change the list contained in a weak-list object. +*/ + (weak, new_list)) +{ + CHECK_WEAK_LIST (weak); + XWEAK_LIST_LIST (weak) = new_list; + return new_list; +} + + +/************************************************************************/ +/* initialization */ +/************************************************************************/ + +static SIGTYPE +arith_error (int signo) +{ + EMACS_REESTABLISH_SIGNAL (signo, arith_error); + EMACS_UNBLOCK_SIGNAL (signo); + signal_error (Qarith_error, Qnil); +} + +void +init_data_very_early (void) +{ + /* Don't do this if just dumping out. + We don't want to call `signal' in this case + so that we don't have trouble with dumping + signal-delivering routines in an inconsistent state. */ +#ifndef CANNOT_DUMP + if (!initialized) + return; +#endif /* CANNOT_DUMP */ + signal (SIGFPE, arith_error); +#ifdef uts + signal (SIGEMT, arith_error); +#endif /* uts */ +} + +void +init_errors_once_early (void) +{ + defsymbol (&Qerror_conditions, "error-conditions"); + defsymbol (&Qerror_message, "error-message"); + + /* We declare the errors here because some other deferrors depend + on some of the errors below. */ + + /* ERROR is used as a signaler for random errors for which nothing + else is right */ + + deferror (&Qerror, "error", "error", Qnil); + deferror (&Qquit, "quit", "Quit", Qnil); + + deferror (&Qwrong_type_argument, "wrong-type-argument", + "Wrong type argument", Qerror); + deferror (&Qargs_out_of_range, "args-out-of-range", "Args out of range", + Qerror); + deferror (&Qvoid_function, "void-function", + "Symbol's function definition is void", Qerror); + deferror (&Qcyclic_function_indirection, "cyclic-function-indirection", + "Symbol's chain of function indirections contains a loop", Qerror); + deferror (&Qvoid_variable, "void-variable", + "Symbol's value as variable is void", Qerror); + deferror (&Qcyclic_variable_indirection, "cyclic-variable-indirection", + "Symbol's chain of variable indirections contains a loop", Qerror); + deferror (&Qsetting_constant, "setting-constant", + "Attempt to set a constant symbol", Qerror); + deferror (&Qinvalid_read_syntax, "invalid-read-syntax", + "Invalid read syntax", Qerror); + + /* Generated by list traversal macros */ + deferror (&Qmalformed_list, "malformed-list", + "Malformed list", Qerror); + deferror (&Qmalformed_property_list, "malformed-property-list", + "Malformed property list", Qmalformed_list); + deferror (&Qcircular_list, "circular-list", + "Circular list", Qerror); + deferror (&Qcircular_property_list, "circular-property-list", + "Circular property list", Qcircular_list); + + deferror (&Qinvalid_function, "invalid-function", "Invalid function", + Qerror); + deferror (&Qwrong_number_of_arguments, "wrong-number-of-arguments", + "Wrong number of arguments", Qerror); + deferror (&Qno_catch, "no-catch", "No catch for tag", + Qerror); + deferror (&Qbeginning_of_buffer, "beginning-of-buffer", + "Beginning of buffer", Qerror); + deferror (&Qend_of_buffer, "end-of-buffer", "End of buffer", Qerror); + deferror (&Qbuffer_read_only, "buffer-read-only", "Buffer is read-only", + Qerror); + + deferror (&Qio_error, "io-error", "IO Error", Qerror); + deferror (&Qend_of_file, "end-of-file", "End of stream", Qio_error); + + deferror (&Qarith_error, "arith-error", "Arithmetic error", Qerror); + deferror (&Qrange_error, "range-error", "Arithmetic range error", + Qarith_error); + deferror (&Qdomain_error, "domain-error", "Arithmetic domain error", + Qarith_error); + deferror (&Qsingularity_error, "singularity-error", + "Arithmetic singularity error", Qdomain_error); + deferror (&Qoverflow_error, "overflow-error", + "Arithmetic overflow error", Qdomain_error); + deferror (&Qunderflow_error, "underflow-error", + "Arithmetic underflow error", Qdomain_error); +} + +void +syms_of_data (void) +{ + defsymbol (&Qquote, "quote"); + defsymbol (&Qlambda, "lambda"); + defsymbol (&Qlistp, "listp"); + defsymbol (&Qtrue_list_p, "true-list-p"); + defsymbol (&Qconsp, "consp"); + defsymbol (&Qsubrp, "subrp"); + defsymbol (&Qsymbolp, "symbolp"); + defsymbol (&Qintegerp, "integerp"); + defsymbol (&Qcharacterp, "characterp"); + defsymbol (&Qnatnump, "natnump"); + defsymbol (&Qstringp, "stringp"); + defsymbol (&Qarrayp, "arrayp"); + defsymbol (&Qsequencep, "sequencep"); + defsymbol (&Qbufferp, "bufferp"); + defsymbol (&Qbitp, "bitp"); + defsymbol (&Qbit_vectorp, "bit-vector-p"); + defsymbol (&Qvectorp, "vectorp"); + defsymbol (&Qchar_or_string_p, "char-or-string-p"); + defsymbol (&Qmarkerp, "markerp"); + defsymbol (&Qinteger_or_marker_p, "integer-or-marker-p"); + defsymbol (&Qinteger_or_char_p, "integer-or-char-p"); + defsymbol (&Qinteger_char_or_marker_p, "integer-char-or-marker-p"); + defsymbol (&Qnumberp, "numberp"); + defsymbol (&Qnumber_char_or_marker_p, "number-char-or-marker-p"); + defsymbol (&Qcdr, "cdr"); + defsymbol (&Qweak_listp, "weak-list-p"); + +#ifdef LISP_FLOAT_TYPE + defsymbol (&Qfloatp, "floatp"); +#endif /* LISP_FLOAT_TYPE */ + + DEFSUBR (Fwrong_type_argument); + + DEFSUBR (Feq); + DEFSUBR (Fold_eq); + DEFSUBR (Fnull); + Ffset (intern ("not"), intern ("null")); + DEFSUBR (Flistp); + DEFSUBR (Fnlistp); + DEFSUBR (Ftrue_list_p); + DEFSUBR (Fconsp); + DEFSUBR (Fatom); + DEFSUBR (Fchar_or_string_p); + DEFSUBR (Fcharacterp); + DEFSUBR (Fchar_int_p); + DEFSUBR (Fchar_to_int); + DEFSUBR (Fint_to_char); + DEFSUBR (Fchar_or_char_int_p); + DEFSUBR (Fintegerp); + DEFSUBR (Finteger_or_marker_p); + DEFSUBR (Finteger_or_char_p); + DEFSUBR (Finteger_char_or_marker_p); + DEFSUBR (Fnumberp); + DEFSUBR (Fnumber_or_marker_p); + DEFSUBR (Fnumber_char_or_marker_p); +#ifdef LISP_FLOAT_TYPE + DEFSUBR (Ffloatp); +#endif /* LISP_FLOAT_TYPE */ + DEFSUBR (Fnatnump); + DEFSUBR (Fsymbolp); + DEFSUBR (Fkeywordp); + DEFSUBR (Fstringp); + DEFSUBR (Fvectorp); + DEFSUBR (Fbitp); + DEFSUBR (Fbit_vector_p); + DEFSUBR (Farrayp); + DEFSUBR (Fsequencep); + DEFSUBR (Fmarkerp); + DEFSUBR (Fsubrp); + DEFSUBR (Fsubr_min_args); + DEFSUBR (Fsubr_max_args); + DEFSUBR (Fsubr_interactive); + DEFSUBR (Ftype_of); + DEFSUBR (Fcar); + DEFSUBR (Fcdr); + DEFSUBR (Fcar_safe); + DEFSUBR (Fcdr_safe); + DEFSUBR (Fsetcar); + DEFSUBR (Fsetcdr); + DEFSUBR (Findirect_function); + DEFSUBR (Faref); + DEFSUBR (Faset); + + DEFSUBR (Fnumber_to_string); + DEFSUBR (Fstring_to_number); + DEFSUBR (Feqlsign); + DEFSUBR (Flss); + DEFSUBR (Fgtr); + DEFSUBR (Fleq); + DEFSUBR (Fgeq); + DEFSUBR (Fneq); + DEFSUBR (Fzerop); + DEFSUBR (Fplus); + DEFSUBR (Fminus); + DEFSUBR (Ftimes); + DEFSUBR (Fquo); + DEFSUBR (Frem); + DEFSUBR (Fmod); + DEFSUBR (Fmax); + DEFSUBR (Fmin); + DEFSUBR (Flogand); + DEFSUBR (Flogior); + DEFSUBR (Flogxor); + DEFSUBR (Flsh); + DEFSUBR (Fash); + DEFSUBR (Fadd1); + DEFSUBR (Fsub1); + DEFSUBR (Flognot); + + DEFSUBR (Fweak_list_p); + DEFSUBR (Fmake_weak_list); + DEFSUBR (Fweak_list_type); + DEFSUBR (Fweak_list_list); + DEFSUBR (Fset_weak_list_list); +} + +void +vars_of_data (void) +{ + /* This must not be staticpro'd */ + Vall_weak_lists = Qnil; + pdump_wire_list (&Vall_weak_lists); + +#ifdef DEBUG_XEMACS + DEFVAR_BOOL ("debug-issue-ebola-notices", &debug_issue_ebola_notices /* +If non-zero, note when your code may be suffering from char-int confoundance. +That is to say, if XEmacs encounters a usage of `eq', `memq', `equal', +etc. where an int and a char with the same value are being compared, +it will issue a notice on stderr to this effect, along with a backtrace. +In such situations, the result would be different in XEmacs 19 versus +XEmacs 20, and you probably don't want this. + +Note that in order to see these notices, you have to byte compile your +code under XEmacs 20 -- any code byte-compiled under XEmacs 19 will +have its chars and ints all confounded in the byte code, making it +impossible to accurately determine Ebola infection. +*/ ); + + debug_issue_ebola_notices = 0; + + DEFVAR_INT ("debug-ebola-backtrace-length", + &debug_ebola_backtrace_length /* +Length (in stack frames) of short backtrace printed out in Ebola notices. +See `debug-issue-ebola-notices'. +*/ ); + debug_ebola_backtrace_length = 32; + +#endif /* DEBUG_XEMACS */ +}