Mercurial > hg > xemacs-beta
diff src/fns.c @ 0:376386a54a3c r19-14
Import from CVS: tag r19-14
author | cvs |
---|---|
date | Mon, 13 Aug 2007 08:45:50 +0200 |
parents | |
children | ac2d302a0011 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/fns.c Mon Aug 13 08:45:50 2007 +0200 @@ -0,0 +1,3292 @@ +/* Random utility Lisp functions. + Copyright (C) 1985, 86, 87, 93, 94, 95 Free Software Foundation, Inc. + Copyright (C) 1995, 1996 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 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. */ + +/* This file has been Mule-ized. */ + +/* Note: FSF 19.30 has bool vectors. We have bit vectors. */ + +/* Hacked on for Mule by Ben Wing, December 1994, January 1995. */ + +#include <config.h> + +/* Note on some machines this defines `vector' as a typedef, + so make sure we don't use that name in this file. */ +#undef vector +#define vector ***** + +#include "lisp.h" + +#include "buffer.h" +#include "bytecode.h" +#include "commands.h" +#include "device.h" +#include "events.h" +#include "extents.h" +#include "frame.h" +#include "systime.h" + +Lisp_Object Qstring_lessp; +Lisp_Object Qidentity; + +static Lisp_Object mark_bit_vector (Lisp_Object, void (*) (Lisp_Object)); +static void print_bit_vector (Lisp_Object, Lisp_Object, int); +static int bit_vector_equal (Lisp_Object o1, Lisp_Object o2, int depth); +static unsigned long bit_vector_hash (Lisp_Object obj, int depth); +DEFINE_BASIC_LRECORD_IMPLEMENTATION ("bit-vector", bit_vector, + mark_bit_vector, print_bit_vector, 0, + bit_vector_equal, bit_vector_hash, + struct Lisp_Bit_Vector); + +static Lisp_Object +mark_bit_vector (Lisp_Object obj, void (*markobj) (Lisp_Object)) +{ + return (Qnil); +} + +static void +print_bit_vector (Lisp_Object obj, Lisp_Object printcharfun, int escapeflag) +{ + int i; + struct Lisp_Bit_Vector *v = XBIT_VECTOR (obj); + int len = bit_vector_length (v); + int last = len; + + if (INTP (Vprint_length)) + last = min (len, XINT (Vprint_length)); + write_c_string ("#*", printcharfun); + for (i = 0; i < last; i++) + { + if (bit_vector_bit (v, i)) + write_c_string ("1", printcharfun); + else + write_c_string ("0", printcharfun); + } + + if (last != len) + write_c_string ("...", printcharfun); +} + +static int +bit_vector_equal (Lisp_Object o1, Lisp_Object o2, int depth) +{ + struct Lisp_Bit_Vector *v1 = XBIT_VECTOR (o1); + struct Lisp_Bit_Vector *v2 = XBIT_VECTOR (o2); + + if (bit_vector_length (v1) != bit_vector_length (v2)) + return 0; + + return !memcmp (v1->bits, v2->bits, + BIT_VECTOR_LONG_STORAGE (bit_vector_length (v1)) * + sizeof (long)); +} + +static unsigned long +bit_vector_hash (Lisp_Object obj, int depth) +{ + struct Lisp_Bit_Vector *v = XBIT_VECTOR (obj); + return HASH2 (bit_vector_length (v), + memory_hash (v->bits, + BIT_VECTOR_LONG_STORAGE (bit_vector_length (v)) * + sizeof (long))); +} + +DEFUN ("identity", Fidentity, Sidentity, 1, 1, 0 /* +Return the argument unchanged. +*/ ) + (arg) + Lisp_Object arg; +{ + return arg; +} + +extern long get_random (void); +extern void seed_random (long arg); + +DEFUN ("random", Frandom, Srandom, 0, 1, 0 /* +Return a pseudo-random number. +All integers representable in Lisp are equally likely.\n\ + On most systems, this is 28 bits' worth.\n\ +With positive integer argument N, return random number in interval [0,N).\n\ +With argument t, set the random number seed from the current time and pid. +*/ ) + (limit) + Lisp_Object limit; +{ + EMACS_INT val; + Lisp_Object lispy_val; + unsigned long denominator; + + if (EQ (limit, Qt)) + seed_random (getpid () + time (NULL)); + if (NATNUMP (limit) && !ZEROP (limit)) + { + /* Try to take our random number from the higher bits of VAL, + not the lower, since (says Gentzel) the low bits of `random' + are less random than the higher ones. We do this by using the + quotient rather than the remainder. At the high end of the RNG + it's possible to get a quotient larger than limit; discarding + these values eliminates the bias that would otherwise appear + when using a large limit. */ + denominator = ((unsigned long)1 << VALBITS) / XINT (limit); + do + val = get_random () / denominator; + while (val >= XINT (limit)); + } + else + val = get_random (); + XSETINT (lispy_val, val); + return lispy_val; +} + +/* Random data-structure functions */ + +#ifdef LOSING_BYTECODE + +/* #### Delete this shit */ + +/* Charcount is a misnomer here as we might be dealing with the + length of a vector or list, but emphasizes that we're not dealing + with Bytecounts in strings */ +static Charcount +length_with_bytecode_hack (Lisp_Object seq) +{ + if (!COMPILED_FUNCTIONP (seq)) + return (XINT (Flength (seq))); + else + { + struct Lisp_Compiled_Function *b = XCOMPILED_FUNCTION (seq); + int intp = b->flags.interactivep; + int domainp = b->flags.domainp; + + if (intp) + return (COMPILED_INTERACTIVE + 1); + else if (domainp) + return (COMPILED_DOMAIN + 1); + else + return (COMPILED_DOC_STRING + 1); + } +} + +#endif /* LOSING_BYTECODE */ + +void +check_losing_bytecode (CONST char *function, Lisp_Object seq) +{ + if (COMPILED_FUNCTIONP (seq)) + error_with_frob + (seq, + "As of 19.14, `%s' no longer works with compiled-function objects", + function); +} + +DEFUN ("length", Flength, Slength, 1, 1, 0 /* +Return the length of vector, bit vector, list or string SEQUENCE. +*/ ) + (obj) + Lisp_Object obj; +{ + Lisp_Object tail; + int i; + + retry: + if (STRINGP (obj)) + return (make_int (string_char_length (XSTRING (obj)))); + else if (VECTORP (obj)) + return (make_int (vector_length (XVECTOR (obj)))); + else if (BIT_VECTORP (obj)) + return (make_int (bit_vector_length (XBIT_VECTOR (obj)))); + else if (CONSP (obj)) + { + for (i = 0, tail = obj; !NILP (tail); i++) + { + QUIT; + tail = Fcdr (tail); + } + + return (make_int (i)); + } + else if (NILP (obj)) + { + return (Qzero); + } + else + { + check_losing_bytecode ("length", obj); + obj = wrong_type_argument (Qsequencep, obj); + goto retry; + } +} + +/* This does not check for quits. That is safe + since it must terminate. */ + +DEFUN ("safe-length", Fsafe_length, Ssafe_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, +it returns 0. If LIST is circular, it returns a finite value +which is at least the number of distinct elements. +*/ ) + (list) + Lisp_Object list; +{ + Lisp_Object tail, halftail, length; + int len = 0; + + /* halftail is used to detect circular lists. */ + halftail = list; + for (tail = list; CONSP (tail); tail = XCDR (tail)) + { + if (EQ (tail, halftail) && len != 0) + break; + len++; + if ((len & 1) == 0) + halftail = XCDR (halftail); + } + + XSETINT (length, len); + return length; +} + +/*** string functions. ***/ + +DEFUN ("string-equal", Fstring_equal, Sstring_equal, 2, 2, 0 /* +T if two strings have identical contents. +Case is significant. Text properties are ignored. +(Under XEmacs, `equal' also ignores text properties and extents in +strings, but this is not the case under FSF Emacs.) +Symbols are also allowed; their print names are used instead. +*/ ) + (s1, s2) + Lisp_Object s1, s2; +{ + int len; + + if (SYMBOLP (s1)) + XSETSTRING (s1, XSYMBOL (s1)->name); + if (SYMBOLP (s2)) + XSETSTRING (s2, XSYMBOL (s2)->name); + CHECK_STRING (s1); + CHECK_STRING (s2); + + len = string_length (XSTRING (s1)); + if (len != string_length (XSTRING (s2)) || + memcmp (string_data (XSTRING (s1)), string_data (XSTRING (s2)), len)) + return Qnil; + return Qt; +} + + +DEFUN ("string-lessp", Fstring_lessp, Sstring_lessp, 2, 2, 0 /* +T if first arg string is less than second in lexicographic order. +If I18N2 support was compiled in, ordering is determined by the locale. +Case is significant for the default C locale. +Symbols are also allowed; their print names are used instead. +*/ ) + (s1, s2) + Lisp_Object s1, s2; +{ + struct Lisp_String *p1, *p2; + Charcount end, len2; + + if (SYMBOLP (s1)) + XSETSTRING (s1, XSYMBOL (s1)->name); + if (SYMBOLP (s2)) + XSETSTRING (s2, XSYMBOL (s2)->name); + CHECK_STRING (s1); + CHECK_STRING (s2); + + p1 = XSTRING (s1); + p2 = XSTRING (s2); + end = string_char_length (XSTRING (s1)); + len2 = string_char_length (XSTRING (s2)); + if (end > len2) + end = len2; + + { + int i; + +#ifdef I18N2 + Bytecount bcend = charcount_to_bytecount (string_data (p1), end); + /* Compare strings using collation order of locale. */ + /* Need to be tricky to handle embedded nulls. */ + + for (i = 0; i < bcend; i += strlen((char *) string_data (p1) + i) + 1) + { + int val = strcoll ((char *) string_data (p1) + i, + (char *) string_data (p2) + i); + if (val < 0) + return Qt; + if (val > 0) + return Qnil; + } +#else /* not I18N2 */ + for (i = 0; i < end; i++) + { + if (string_char (p1, i) != string_char (p2, i)) + return string_char (p1, i) < string_char (p2, i) ? Qt : Qnil; + } +#endif /* not I18N2 */ + /* Can't do i < len2 because then comparison between "foo" and "foo^@" + won't work right in I18N2 case */ + return ((end < len2) ? Qt : Qnil); + } +} + +DEFUN ("string-modified-tick", Fstring_modified_tick, Sstring_modified_tick, + 1, 1, 0 /* +Return STRING's tick counter, incremented for each change to the string. +Each string has a tick counter which is incremented each time the contents +of the string are changed (e.g. with `aset'). It wraps around occasionally. +*/ ) + (string) + Lisp_Object string; +{ + struct Lisp_String *s; + + CHECK_STRING (string); + s = XSTRING (string); + if (CONSP (s->plist) && INTP (XCAR (s->plist))) + return XCAR (s->plist); + else + return Qzero; +} + +void +bump_string_modiff (Lisp_Object str) +{ + struct Lisp_String *s = XSTRING (str); + Lisp_Object *ptr = &s->plist; + +#ifdef I18N3 + /* #### remove the `string-translatable' property from the string, + if there is one. */ +#endif + /* skip over extent info if it's there */ + if (CONSP (*ptr) && EXTENT_INFOP (XCAR (*ptr))) + ptr = &XCDR (*ptr); + if (CONSP (*ptr) && INTP (XCAR (*ptr))) + XSETINT (XCAR (*ptr), 1+XINT (XCAR (*ptr))); + else + *ptr = Fcons (make_int (1), *ptr); +} + + +enum concat_target_type { c_cons, c_string, c_vector, c_bit_vector }; +static Lisp_Object concat (int nargs, Lisp_Object *args, + enum concat_target_type target_type, + int last_special); + +Lisp_Object +concat2 (Lisp_Object s1, Lisp_Object s2) +{ + Lisp_Object args[2]; + args[0] = s1; + args[1] = s2; + return concat (2, args, c_string, 0); +} + +Lisp_Object +concat3 (Lisp_Object s1, Lisp_Object s2, Lisp_Object s3) +{ + Lisp_Object args[3]; + args[0] = s1; + args[1] = s2; + args[2] = s3; + return concat (3, args, c_string, 0); +} + +Lisp_Object +vconcat2 (Lisp_Object s1, Lisp_Object s2) +{ + Lisp_Object args[2]; + args[0] = s1; + args[1] = s2; + return concat (2, args, c_vector, 0); +} + +Lisp_Object +vconcat3 (Lisp_Object s1, Lisp_Object s2, Lisp_Object s3) +{ + Lisp_Object args[3]; + args[0] = s1; + args[1] = s2; + args[2] = s3; + return concat (3, args, c_vector, 0); +} + +DEFUN ("append", Fappend, Sappend, 0, MANY, 0 /* +Concatenate all the arguments and make the result a list. +The result is a list whose elements are the elements of all the arguments. +Each argument may be a list, vector, bit vector, or string. +The last argument is not copied, just used as the tail of the new list. +*/ ) + (nargs, args) + int nargs; + Lisp_Object *args; +{ + return concat (nargs, args, c_cons, 1); +} + +DEFUN ("concat", Fconcat, Sconcat, 0, MANY, 0 /* +Concatenate all the arguments and make the result a string. +The result is a string whose elements are the elements of all the arguments. +Each argument may be a string or a list or vector of characters (integers). + +Do not use individual integers as arguments! +The behavior of `concat' in that case will be changed later! +If your program passes an integer as an argument to `concat', +you should change it right away not to do so. +*/ ) + (nargs, args) + int nargs; + Lisp_Object *args; +{ + return concat (nargs, args, c_string, 0); +} + +DEFUN ("vconcat", Fvconcat, Svconcat, 0, MANY, 0 /* +Concatenate all the arguments and make the result a vector. +The result is a vector whose elements are the elements of all the arguments. +Each argument may be a list, vector, bit vector, or string. +*/ ) + (nargs, args) + int nargs; + Lisp_Object *args; +{ + return concat (nargs, args, c_vector, 0); +} + +DEFUN ("bvconcat", Fbvconcat, Sbvconcat, 0, MANY, 0 /* +Concatenate all the arguments and make the result a bit vector. +The result is a bit vector whose elements are the elements of all the +arguments. Each argument may be a list, vector, bit vector, or string. +*/ ) + (nargs, args) + int nargs; + Lisp_Object *args; +{ + return concat (nargs, args, c_bit_vector, 0); +} + +DEFUN ("copy-sequence", Fcopy_sequence, Scopy_sequence, 1, 1, 0 /* +Return a copy of a list, vector, bit vector or string. +The elements of a list or vector are not copied; they are shared +with the original. +*/ ) + (arg) + Lisp_Object arg; +{ + again: + if (NILP (arg)) return arg; + /* We handle conses separately because concat() is big and hairy and + doesn't handle (copy-sequence '(a b . c)) and it's easier to redo this + than to fix concat() without worrying about breaking other things. + */ + if (CONSP (arg)) + { + Lisp_Object rest = arg; + Lisp_Object head, tail; + tail = Qnil; + while (CONSP (rest)) + { + Lisp_Object new = Fcons (XCAR (rest), XCDR (rest)); + if (NILP (tail)) + head = tail = new; + else + XCDR (tail) = new, tail = new; + rest = XCDR (rest); + QUIT; + } + if (!NILP (tail)) + XCDR (tail) = rest; + return head; + } + else if (STRINGP (arg)) + return concat (1, &arg, c_string, 0); + else if (VECTORP (arg)) + return concat (1, &arg, c_vector, 0); + else if (BIT_VECTORP (arg)) + return concat (1, &arg, c_bit_vector, 0); + else + { + check_losing_bytecode ("copy-sequence", arg); + arg = wrong_type_argument (Qsequencep, arg); + goto again; + } +} + +struct merge_string_extents_struct +{ + Lisp_Object string; + Bytecount entry_offset; + Bytecount entry_length; +}; + +static Lisp_Object +concat (int nargs, Lisp_Object *args, + enum concat_target_type target_type, + int last_special) +{ + Lisp_Object val; + Lisp_Object tail = Qnil; + int toindex; + int argnum; + Lisp_Object last_tail; + Lisp_Object prev; + struct merge_string_extents_struct *args_mse = 0; + Bufbyte *string_result = 0; + Bufbyte *string_result_ptr = 0; + struct gcpro gcpro1; + + /* The modus operandi in Emacs is "caller gc-protects args". + However, concat is called many times in Emacs on freshly + created stuff. So we help those callers out by protecting + the args ourselves to save them a lot of temporary-variable + grief. */ + + GCPRO1 (args[0]); + gcpro1.nvars = nargs; + +#ifdef I18N3 + /* #### if the result is a string and any of the strings have a string + for the `string-translatable' property, then concat should also + concat the args but use the `string-translatable' strings, and store + the result in the returned string's `string-translatable' property. */ +#endif + if (target_type == c_string) + { + args_mse = ((struct merge_string_extents_struct *) + alloca (nargs * + sizeof (struct merge_string_extents_struct))); + } + + /* In append, the last arg isn't treated like the others */ + if (last_special && nargs > 0) + { + nargs--; + last_tail = args[nargs]; + } + else + last_tail = Qnil; + + /* Check and coerce the arguments. */ + for (argnum = 0; argnum < nargs; argnum++) + { + Lisp_Object seq = args[argnum]; + if (CONSP (seq) || NILP (seq)) + ; + else if (VECTORP (seq) || STRINGP (seq) || BIT_VECTORP (seq)) + ; +#ifdef LOSING_BYTECODE + else if (COMPILED_FUNCTIONP (seq)) + /* Urk! We allow this, for "compatibility"... */ + ; +#endif + else if (INTP (seq)) + /* This is too revolting to think about but maintains + compatibility with FSF (and lots and lots of old code). */ + args[argnum] = Fnumber_to_string (seq); + else + { + check_losing_bytecode ("concat", seq); + args[argnum] = wrong_type_argument (Qsequencep, seq); + } + + if (args_mse) + { + if (STRINGP (seq)) + args_mse[argnum].string = seq; + else + args_mse[argnum].string = Qnil; + } + } + + { + /* Charcount is a misnomer here as we might be dealing with the + length of a vector or list, but emphasizes that we're not dealing + with Bytecounts in strings */ + Charcount total_length; + + for (argnum = 0, total_length = 0; argnum < nargs; argnum++) + { +#ifdef LOSING_BYTECODE + Charcount thislen = length_with_bytecode_hack (args[argnum]); +#else + Charcount thislen = XINT (Flength (args[argnum])); +#endif + total_length += thislen; + } + + switch (target_type) + { + case c_cons: + if (total_length == 0) + /* In append, if all but last arg are nil, return last arg */ + RETURN_UNGCPRO (last_tail); + val = Fmake_list (make_int (total_length), Qnil); + break; + case c_vector: + val = make_vector (total_length, Qnil); + break; + case c_bit_vector: + val = make_bit_vector (total_length, Qzero); + break; + case c_string: + /* We don't make the string yet because we don't know the + actual number of bytes. This loop was formerly written + to call Fmake_string() here and then call set_string_char() + for each char. This seems logical enough but is waaaaaaaay + slow -- set_string_char() has to scan the whole string up + to the place where the substitution is called for in order + to find the place to change, and may have to do some + realloc()ing in order to make the char fit properly. + O(N^2) yuckage. */ + val = Qnil; + string_result = (Bufbyte *) alloca (total_length * MAX_EMCHAR_LEN); + string_result_ptr = string_result; + break; + default: + abort (); + } + } + + + if (CONSP (val)) + tail = val, toindex = -1; /* -1 in toindex is flag we are + making a list */ + else + toindex = 0; + + prev = Qnil; + + for (argnum = 0; argnum < nargs; argnum++) + { + Charcount thisleni = 0; + Charcount thisindex = 0; + Lisp_Object seq = args[argnum]; + Bufbyte *string_source_ptr = 0; + Bufbyte *string_prev_result_ptr = string_result_ptr; + + if (!CONSP (seq)) + { +#ifdef LOSING_BYTECODE + thisleni = length_with_bytecode_hack (seq); +#else + thisleni = XINT (Flength (seq)); +#endif + } + if (STRINGP (seq)) + string_source_ptr = string_data (XSTRING (seq)); + + while (1) + { + Lisp_Object elt; + + /* We've come to the end of this arg, so exit. */ + if (NILP (seq)) + break; + + /* Fetch next element of `seq' arg into `elt' */ + if (CONSP (seq)) + { + elt = Fcar (seq); + seq = Fcdr (seq); + } + else + { + if (thisindex >= thisleni) + break; + + if (STRINGP (seq)) + { + elt = make_char (charptr_emchar (string_source_ptr)); + INC_CHARPTR (string_source_ptr); + } + else if (VECTORP (seq)) + elt = vector_data (XVECTOR (seq))[thisindex]; + else if (BIT_VECTORP (seq)) + elt = make_int (bit_vector_bit (XBIT_VECTOR (seq), + thisindex)); + else + elt = Felt (seq, make_int (thisindex)); + thisindex++; + } + + /* Store into result */ + if (toindex < 0) + { + /* toindex negative means we are making a list */ + XCAR (tail) = elt; + prev = tail; + tail = XCDR (tail); + } + else if (VECTORP (val)) + vector_data (XVECTOR (val))[toindex++] = elt; + else if (BIT_VECTORP (val)) + { + CHECK_BIT (elt); + set_bit_vector_bit (XBIT_VECTOR (val), toindex++, XINT (elt)); + } + else + { + CHECK_CHAR_COERCE_INT (elt); + string_result_ptr += set_charptr_emchar (string_result_ptr, + XCHAR (elt)); + } + } + if (args_mse) + { + args_mse[argnum].entry_offset = + string_prev_result_ptr - string_result; + args_mse[argnum].entry_length = + string_result_ptr - string_prev_result_ptr; + } + } + + /* Now we finally make the string. */ + if (target_type == c_string) + { + val = make_string (string_result, string_result_ptr - string_result); + for (argnum = 0; argnum < nargs; argnum++) + { + if (STRINGP (args_mse[argnum].string)) + copy_string_extents (val, args_mse[argnum].string, + args_mse[argnum].entry_offset, 0, + args_mse[argnum].entry_length); + } + } + + if (!NILP (prev)) + XCDR (prev) = last_tail; + + RETURN_UNGCPRO (val); +} + +DEFUN ("copy-alist", Fcopy_alist, Scopy_alist, 1, 1, 0 /* +Return a copy of ALIST. +This is an alist which represents the same mapping from objects to objects, +but does not share the alist structure with ALIST. +The objects mapped (cars and cdrs of elements of the alist) +are shared, however. +Elements of ALIST that are not conses are also shared. +*/ ) + (alist) + Lisp_Object alist; +{ + Lisp_Object tem; + + CHECK_LIST (alist); + if (NILP (alist)) + return alist; + alist = concat (1, &alist, c_cons, 0); + for (tem = alist; CONSP (tem); tem = XCDR (tem)) + { + Lisp_Object car; + car = XCAR (tem); + + if (CONSP (car)) + XCAR (tem) = Fcons (XCAR (car), XCDR (car)); + } + return alist; +} + +DEFUN ("copy-tree", Fcopy_tree, Scopy_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) + Lisp_Object arg, vecp; +{ + 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) = Fcopy_tree (elt, vecp); + if (VECTORP (XCDR (rest))) /* hack for (a b . [c d]) */ + XCDR (rest) = Fcopy_tree (XCDR (rest), vecp); + rest = XCDR (rest); + } + } + else if (VECTORP (arg) && ! NILP (vecp)) + { + int i = vector_length (XVECTOR (arg)); + int j; + arg = Fcopy_sequence (arg); + for (j = 0; j < i; j++) + { + Lisp_Object elt = vector_data (XVECTOR (arg)) [j]; + QUIT; + if (CONSP (elt) || VECTORP (elt)) + vector_data (XVECTOR (arg)) [j] = Fcopy_tree (elt, vecp); + } + } + return arg; +} + +DEFUN ("substring", Fsubstring, Ssubstring, 2, 3, 0 /* +Return a substring of STRING, starting at index FROM and ending before TO. +TO may be nil or omitted; then the substring runs to the end of STRING. +If FROM or TO is negative, it counts from the end. +Relevant parts of the string-extent-data are copied in the new string. +*/ ) + (string, from, to) + Lisp_Object string; + Lisp_Object from, to; +{ + Charcount ccfr, ccto; + Bytecount bfr, bto; + Lisp_Object val; + + CHECK_STRING (string); + /* Historically, FROM could not be omitted. Whatever ... */ + CHECK_INT (from); + get_string_range_char (string, from, to, &ccfr, &ccto, + GB_HISTORICAL_STRING_BEHAVIOR); + bfr = charcount_to_bytecount (string_data (XSTRING (string)), ccfr); + bto = charcount_to_bytecount (string_data (XSTRING (string)), ccto); + val = make_string (string_data (XSTRING (string)) + bfr, bto - bfr); + /* Copy any applicable extent information into the new string: */ + copy_string_extents (val, string, 0, bfr, bto - bfr); + return (val); +} + +DEFUN ("subseq", Fsubseq, Ssubseq, 2, 3, 0 /* +Return a subsequence of SEQ, starting at index FROM and ending before TO. +TO may be nil or omitted; then the subsequence runs to the end of SEQ. +If FROM or TO is negative, it counts from the end. +The resulting subsequence is always the same type as the original + sequence. +If SEQ is a string, relevant parts of the string-extent-data are copied + in the new string. +*/ ) + (seq, from, to) + Lisp_Object seq; + Lisp_Object from, to; +{ + int len, f, t; + + if (STRINGP (seq)) + return Fsubstring (seq, from, to); + + if (CONSP (seq) || NILP (seq)) + ; + else if (VECTORP (seq) || BIT_VECTORP (seq)) + ; + else + { + check_losing_bytecode ("subseq", seq); + seq = wrong_type_argument (Qsequencep, seq); + } + + len = XINT (Flength (seq)); + CHECK_INT (from); + f = XINT (from); + if (f < 0) + f = len + f; + if (NILP (to)) + t = len; + else + { + CHECK_INT (to); + t = XINT (to); + if (t < 0) + t = len + t; + } + + if (!(0 <= f && f <= t && t <= len)) + args_out_of_range_3 (seq, make_int (f), make_int (t)); + + if (VECTORP (seq)) + { + Lisp_Object result = make_vector (t - f, Qnil); + int i; + Lisp_Object *in_elts = vector_data (XVECTOR (seq)); + Lisp_Object *out_elts = vector_data (XVECTOR (result)); + + for (i = f; i < t; i++) + out_elts[i - f] = in_elts[i]; + return result; + } + + if (CONSP (seq)) + { + Lisp_Object result = Qnil; + int i; + + seq = Fnthcdr (make_int (f), seq); + + for (i = f; i < t; i++) + { + result = Fcons (Fcar (seq), result); + seq = Fcdr (seq); + } + + return Fnreverse (result); + } + + /* bit vector */ + { + Lisp_Object result = make_bit_vector (t - f, Qzero); + int i; + + for (i = f; i < t; i++) + set_bit_vector_bit (XBIT_VECTOR (result), i - f, + bit_vector_bit (XBIT_VECTOR (seq), i)); + return result; + } +} + + +DEFUN ("nthcdr", Fnthcdr, Snthcdr, 2, 2, 0 /* +Take cdr N times on LIST, returns the result. +*/ ) + (n, list) + Lisp_Object n; + Lisp_Object list; +{ + REGISTER int i, num; + CHECK_INT (n); + num = XINT (n); + for (i = 0; i < num && !NILP (list); i++) + { + QUIT; + list = Fcdr (list); + } + return list; +} + +DEFUN ("nth", Fnth, Snth, 2, 2, 0 /* +Return the Nth element of LIST. +N counts from zero. If LIST is not that long, nil is returned. +*/ ) + (n, list) + Lisp_Object n, list; +{ + return Fcar (Fnthcdr (n, list)); +} + +DEFUN ("elt", Felt, Selt, 2, 2, 0 /* +Return element of SEQUENCE at index N. +*/ ) + (seq, n) + Lisp_Object seq, n; +{ + retry: + CHECK_INT_COERCE_CHAR (n); /* yuck! */ + if (CONSP (seq) || NILP (seq)) + { + Lisp_Object tem = Fnthcdr (n, seq); + /* #### 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 Says It Should Be. */ + args_out_of_range (seq, n); +#endif + } + else if (STRINGP (seq) + || VECTORP (seq) + || BIT_VECTORP (seq)) + return (Faref (seq, n)); +#ifdef LOSING_BYTECODE + else if (COMPILED_FUNCTIONP (seq)) + { + int idx = XINT (n); + if (idx < 0) + { + lose: + args_out_of_range (seq, n); + } + /* Utter perversity */ + { + struct Lisp_Compiled_Function *b = XCOMPILED_FUNCTION (seq); + switch (idx) + { + case COMPILED_ARGLIST: + return (b->arglist); + case COMPILED_BYTECODE: + return (b->bytecodes); + case COMPILED_CONSTANTS: + return (b->constants); + case COMPILED_STACK_DEPTH: + return (make_int (b->maxdepth)); + case COMPILED_DOC_STRING: + return (compiled_function_documentation (b)); + case COMPILED_DOMAIN: + return (compiled_function_domain (b)); + case COMPILED_INTERACTIVE: + if (b->flags.interactivep) + return (compiled_function_interactive (b)); + /* if we return nil, can't tell interactive with no args + from noninteractive. */ + goto lose; + default: + goto lose; + } + } + } +#endif /* LOSING_BYTECODE */ + else + { + check_losing_bytecode ("elt", seq); + seq = wrong_type_argument (Qsequencep, seq); + goto retry; + } +} + +DEFUN ("member", Fmember, Smember, 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) + Lisp_Object elt; + Lisp_Object list; +{ + REGISTER Lisp_Object tail, tem; + for (tail = list; !NILP (tail); tail = Fcdr (tail)) + { + tem = Fcar (tail); + if (! NILP (Fequal (elt, tem))) + return tail; + QUIT; + } + return Qnil; +} + +DEFUN ("memq", Fmemq, Smemq, 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) + Lisp_Object elt; + Lisp_Object list; +{ + REGISTER Lisp_Object tail, tem; + for (tail = list; !NILP (tail); tail = Fcdr (tail)) + { + tem = Fcar (tail); + if (HACKEQ_UNSAFE (elt, tem)) return tail; + QUIT; + } + return Qnil; +} + +Lisp_Object +memq_no_quit (Lisp_Object elt, Lisp_Object list) +{ + REGISTER Lisp_Object tail, tem; + for (tail = list; CONSP (tail); tail = XCDR (tail)) + { + tem = XCAR (tail); + if (HACKEQ_UNSAFE (elt, tem)) return tail; + } + return Qnil; +} + +DEFUN ("assoc", Fassoc, Sassoc, 2, 2, 0 /* +Return non-nil if KEY is `equal' to the car of an element of LIST. +The value is actually the element of LIST whose car equals KEY. +*/ ) + (key, list) + Lisp_Object key; + Lisp_Object list; +{ + /* This function can GC. */ + REGISTER Lisp_Object tail, elt, tem; + for (tail = list; !NILP (tail); tail = Fcdr (tail)) + { + elt = Fcar (tail); + if (!CONSP (elt)) continue; + tem = Fequal (Fcar (elt), key); + if (!NILP (tem)) return elt; + QUIT; + } + return Qnil; +} + +Lisp_Object +assoc_no_quit (Lisp_Object key, Lisp_Object list) +{ + int speccount = specpdl_depth (); + specbind (Qinhibit_quit, Qt); + return (unbind_to (speccount, Fassoc (key, list))); +} + +DEFUN ("assq", Fassq, Sassq, 2, 2, 0 /* +Return non-nil if KEY is `eq' to the car of an element of LIST. +The value is actually the element of LIST whose car is KEY. +Elements of LIST that are not conses are ignored. +*/ ) + (key, list) + Lisp_Object key; + Lisp_Object list; +{ + REGISTER Lisp_Object tail, elt, tem; + for (tail = list; !NILP (tail); tail = Fcdr (tail)) + { + elt = Fcar (tail); + if (!CONSP (elt)) continue; + tem = Fcar (elt); + if (HACKEQ_UNSAFE (key, tem)) return elt; + QUIT; + } + 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 list) +{ + /* This cannot GC. */ + REGISTER Lisp_Object tail, elt, tem; + for (tail = list; CONSP (tail); tail = XCDR (tail)) + { + elt = XCAR (tail); + if (!CONSP (elt)) continue; + tem = XCAR (elt); + if (HACKEQ_UNSAFE (key, tem)) return elt; + } + return Qnil; +} + +DEFUN ("rassoc", Frassoc, Srassoc, 2, 2, 0 /* +Return non-nil if KEY is `equal' to the cdr of an element of LIST. +The value is actually the element of LIST whose cdr equals KEY. +*/ ) + (key, list) + Lisp_Object key; + Lisp_Object list; +{ + REGISTER Lisp_Object tail; + for (tail = list; !NILP (tail); tail = Fcdr (tail)) + { + REGISTER Lisp_Object elt, tem; + elt = Fcar (tail); + if (!CONSP (elt)) continue; + tem = Fequal (Fcdr (elt), key); + if (!NILP (tem)) return elt; + QUIT; + } + return Qnil; +} + +DEFUN ("rassq", Frassq, Srassq, 2, 2, 0 /* +Return non-nil if KEY is `eq' to the cdr of an element of LIST. +The value is actually the element of LIST whose cdr is KEY. +*/ ) + (key, list) + Lisp_Object key; + Lisp_Object list; +{ + REGISTER Lisp_Object tail, elt, tem; + for (tail = list; !NILP (tail); tail = Fcdr (tail)) + { + elt = Fcar (tail); + if (!CONSP (elt)) continue; + tem = Fcdr (elt); + if (HACKEQ_UNSAFE (key, tem)) return elt; + QUIT; + } + return Qnil; +} + +Lisp_Object +rassq_no_quit (Lisp_Object key, Lisp_Object list) +{ + REGISTER Lisp_Object tail, elt, tem; + for (tail = list; CONSP (tail); tail = XCDR (tail)) + { + elt = XCAR (tail); + if (!CONSP (elt)) continue; + tem = XCDR (elt); + if (HACKEQ_UNSAFE (key, tem)) return elt; + } + return Qnil; +} + + +DEFUN ("delete", Fdelete, Sdelete, 2, 2, 0 /* +Delete by side effect any occurrences of ELT as a member of LIST. +The modified LIST is returned. Comparison is done with `equal'. +If the first member of LIST is ELT, there is no way to remove it by side +effect; therefore, write `(setq foo (delete element foo))' to be sure +of changing the value of `foo'. +*/ ) + (elt, list) + Lisp_Object elt; + Lisp_Object list; +{ + REGISTER Lisp_Object tail, prev; + + tail = list; + prev = Qnil; + while (!NILP (tail)) + { + if (!NILP (Fequal (elt, Fcar (tail)))) + { + if (NILP (prev)) + list = Fcdr (tail); + else + Fsetcdr (prev, Fcdr (tail)); + } + else + prev = tail; + tail = Fcdr (tail); + QUIT; + } + return list; +} + +DEFUN ("delq", Fdelq, Sdelq, 2, 2, 0 /* +Delete by side effect any occurrences of ELT as a member of LIST. +The modified LIST is returned. Comparison is done with `eq'. +If the first member of LIST is ELT, there is no way to remove it by side +effect; therefore, write `(setq foo (delq element foo))' to be sure of +changing the value of `foo'. +*/ ) + (elt, list) + Lisp_Object elt; + Lisp_Object list; +{ + REGISTER Lisp_Object tail, prev; + REGISTER Lisp_Object tem; + + tail = list; + prev = Qnil; + while (!NILP (tail)) + { + tem = Fcar (tail); + if (HACKEQ_UNSAFE (elt, tem)) + { + if (NILP (prev)) + list = Fcdr (tail); + else + Fsetcdr (prev, Fcdr (tail)); + } + else + prev = tail; + tail = Fcdr (tail); + QUIT; + } + return list; +} + +/* no quit, no errors; be careful */ + +Lisp_Object +delq_no_quit (Lisp_Object elt, Lisp_Object list) +{ + REGISTER Lisp_Object tail, prev; + REGISTER Lisp_Object tem; + + tail = list; + prev = Qnil; + while (CONSP (tail)) + { + tem = XCAR (tail); + if (HACKEQ_UNSAFE (elt, tem)) + { + if (NILP (prev)) + list = XCDR (tail); + else + XCDR (prev) = XCDR (tail); + } + else + prev = tail; + tail = XCDR (tail); + } + 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, prev; + REGISTER Lisp_Object tem; + + tail = list; + prev = Qnil; + while (CONSP (tail)) + { + Lisp_Object cons_to_free = Qnil; + tem = XCAR (tail); + if (HACKEQ_UNSAFE (elt, tem)) + { + if (NILP (prev)) + list = XCDR (tail); + else + XCDR (prev) = XCDR (tail); + cons_to_free = tail; + } + else + prev = tail; + tail = XCDR (tail); + if (!NILP (cons_to_free)) + free_cons (XCONS (cons_to_free)); + } + return list; +} + +DEFUN ("remassoc", Fremassoc, Sremassoc, 2, 2, 0 /* +Delete by side effect any elements of LIST whose car is `equal' to KEY. +The modified LIST is returned. If the first member of LIST has a car +that is `equal' to KEY, there is no way to remove it by side effect; +therefore, write `(setq foo (remassoc key foo))' to be sure of changing +the value of `foo'. +*/ ) + (key, list) + Lisp_Object key; + Lisp_Object list; +{ + REGISTER Lisp_Object tail, prev; + + tail = list; + prev = Qnil; + while (!NILP (tail)) + { + Lisp_Object elt = Fcar (tail); + if (CONSP (elt) && ! NILP (Fequal (key, Fcar (elt)))) + { + if (NILP (prev)) + list = Fcdr (tail); + else + Fsetcdr (prev, Fcdr (tail)); + } + else + prev = tail; + tail = Fcdr (tail); + QUIT; + } + return list; +} + +Lisp_Object +remassoc_no_quit (Lisp_Object key, Lisp_Object list) +{ + int speccount = specpdl_depth (); + specbind (Qinhibit_quit, Qt); + return (unbind_to (speccount, Fremassoc (key, list))); +} + +DEFUN ("remassq", Fremassq, Sremassq, 2, 2, 0 /* +Delete by side effect any elements of LIST whose car is `eq' to KEY. +The modified LIST is returned. If the first member of LIST has a car +that is `eq' to KEY, there is no way to remove it by side effect; +therefore, write `(setq foo (remassq key foo))' to be sure of changing +the value of `foo'. +*/ ) + (key, list) + Lisp_Object key; + Lisp_Object list; +{ + REGISTER Lisp_Object tail, prev; + + tail = list; + prev = Qnil; + while (!NILP (tail)) + { + Lisp_Object elt = Fcar (tail); + if (CONSP (elt) && HACKEQ_UNSAFE (key, Fcar (elt))) + { + if (NILP (prev)) + list = Fcdr (tail); + else + Fsetcdr (prev, Fcdr (tail)); + } + else + prev = tail; + tail = Fcdr (tail); + QUIT; + } + return list; +} + +/* no quit, no errors; be careful */ + +Lisp_Object +remassq_no_quit (Lisp_Object key, Lisp_Object list) +{ + REGISTER Lisp_Object tail, prev; + REGISTER Lisp_Object tem; + + tail = list; + prev = Qnil; + while (CONSP (tail)) + { + tem = XCAR (tail); + if (CONSP (tem) && HACKEQ_UNSAFE (key, XCAR (tem))) + { + if (NILP (prev)) + list = XCDR (tail); + else + XCDR (prev) = XCDR (tail); + } + else + prev = tail; + tail = XCDR (tail); + } + return list; +} + +DEFUN ("remrassoc", Fremrassoc, Sremrassoc, 2, 2, 0 /* +Delete by side effect any elements of LIST whose cdr is `equal' to VALUE. +The modified LIST is returned. If the first member of LIST has a car +that is `equal' to VALUE, there is no way to remove it by side effect; +therefore, write `(setq foo (remrassoc value foo))' to be sure of changing +the value of `foo'. +*/ ) + (value, list) + Lisp_Object value; + Lisp_Object list; +{ + REGISTER Lisp_Object tail, prev; + + tail = list; + prev = Qnil; + while (!NILP (tail)) + { + Lisp_Object elt = Fcar (tail); + if (CONSP (elt) && ! NILP (Fequal (value, Fcdr (elt)))) + { + if (NILP (prev)) + list = Fcdr (tail); + else + Fsetcdr (prev, Fcdr (tail)); + } + else + prev = tail; + tail = Fcdr (tail); + QUIT; + } + return list; +} + +DEFUN ("remrassq", Fremrassq, Sremrassq, 2, 2, 0 /* +Delete by side effect any elements of LIST whose cdr is `eq' to VALUE. +The modified LIST is returned. If the first member of LIST has a car +that is `eq' to VALUE, there is no way to remove it by side effect; +therefore, write `(setq foo (remrassq value foo))' to be sure of changing +the value of `foo'. +*/ ) + (value, list) + Lisp_Object value; + Lisp_Object list; +{ + REGISTER Lisp_Object tail, prev; + + tail = list; + prev = Qnil; + while (!NILP (tail)) + { + Lisp_Object elt = Fcar (tail); + if (CONSP (elt) && HACKEQ_UNSAFE (value, Fcdr (elt))) + { + if (NILP (prev)) + list = Fcdr (tail); + else + Fsetcdr (prev, Fcdr (tail)); + } + else + prev = tail; + tail = Fcdr (tail); + QUIT; + } + return list; +} + +/* no quit, no errors; be careful */ + +Lisp_Object +remrassq_no_quit (Lisp_Object value, Lisp_Object list) +{ + REGISTER Lisp_Object tail, prev; + REGISTER Lisp_Object tem; + + tail = list; + prev = Qnil; + while (CONSP (tail)) + { + tem = XCAR (tail); + if (CONSP (tem) && HACKEQ_UNSAFE (value, XCDR (tem))) + { + if (NILP (prev)) + list = XCDR (tail); + else + XCDR (prev) = XCDR (tail); + } + else + prev = tail; + tail = XCDR (tail); + } + return list; +} + +DEFUN ("nreverse", Fnreverse, Snreverse, 1, 1, 0 /* +Reverse LIST by modifying cdr pointers. +Returns the beginning of the reversed list. +*/ ) + (list) + Lisp_Object list; +{ + Lisp_Object prev, tail, next; + struct gcpro gcpro1, gcpro2; + + /* We gcpro our args; see `nconc' */ + prev = Qnil; + tail = list; + GCPRO2 (prev, tail); + while (!NILP (tail)) + { + QUIT; + next = Fcdr (tail); + Fsetcdr (tail, prev); + prev = tail; + tail = next; + } + UNGCPRO; + return prev; +} + +DEFUN ("reverse", Freverse, Sreverse, 1, 1, 0 /* +Reverse LIST, copying. Returns the beginning of the reversed list. +See also the function `nreverse', which is used more often. +*/ ) + (list) + Lisp_Object list; +{ + Lisp_Object length; + Lisp_Object *vec; + Lisp_Object tail; + REGISTER int i; + + length = Flength (list); + vec = (Lisp_Object *) alloca (XINT (length) * sizeof (Lisp_Object)); + for (i = XINT (length) - 1, tail = list; i >= 0; i--, tail = Fcdr (tail)) + vec[i] = Fcar (tail); + + return Flist (XINT (length), vec); +} + +static Lisp_Object list_merge (Lisp_Object org_l1, Lisp_Object org_l2, + Lisp_Object lisp_arg, + int (*pred_fn) (Lisp_Object, Lisp_Object, + Lisp_Object lisp_arg)); + +Lisp_Object +list_sort (Lisp_Object list, + Lisp_Object lisp_arg, + int (*pred_fn) (Lisp_Object, Lisp_Object, + Lisp_Object lisp_arg)) +{ + Lisp_Object front, back; + Lisp_Object len, tem; + struct gcpro gcpro1, gcpro2, gcpro3; + int length; + + front = list; + len = Flength (list); + length = XINT (len); + if (length < 2) + return list; + + XSETINT (len, (length / 2) - 1); + tem = Fnthcdr (len, list); + back = Fcdr (tem); + Fsetcdr (tem, Qnil); + + GCPRO3 (front, back, lisp_arg); + front = list_sort (front, lisp_arg, pred_fn); + back = list_sort (back, lisp_arg, pred_fn); + UNGCPRO; + return list_merge (front, back, lisp_arg, pred_fn); +} + + +static int +merge_pred_function (Lisp_Object obj1, Lisp_Object obj2, + Lisp_Object pred) +{ + Lisp_Object tmp; + + /* prevents the GC from happening in call2 */ + int speccount = specpdl_depth (); +/* Emacs' GC doesn't actually relocate pointers, so this probably + isn't strictly necessary */ + record_unwind_protect (restore_gc_inhibit, + make_int (gc_currently_forbidden)); + gc_currently_forbidden = 1; + tmp = call2 (pred, obj1, obj2); + unbind_to (speccount, Qnil); + + if (NILP (tmp)) + return -1; + else + return 1; +} + +DEFUN ("sort", Fsort, Ssort, 2, 2, 0 /* +Sort LIST, stably, comparing elements using PREDICATE. +Returns the sorted list. LIST is modified by side effects. +PREDICATE is called with two elements of LIST, and should return T +if the first element is \"less\" than the second. +*/ ) + (list, pred) + Lisp_Object list, pred; +{ + return list_sort (list, pred, merge_pred_function); +} + +Lisp_Object +merge (Lisp_Object org_l1, Lisp_Object org_l2, + Lisp_Object pred) +{ + return list_merge (org_l1, org_l2, pred, merge_pred_function); +} + + +static Lisp_Object +list_merge (Lisp_Object org_l1, Lisp_Object org_l2, + Lisp_Object lisp_arg, + int (*pred_fn) (Lisp_Object, Lisp_Object, Lisp_Object lisp_arg)) +{ + Lisp_Object value; + Lisp_Object tail; + Lisp_Object tem; + Lisp_Object l1, l2; + struct gcpro gcpro1, gcpro2, gcpro3, gcpro4; + + l1 = org_l1; + l2 = org_l2; + tail = Qnil; + value = Qnil; + + /* 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. */ + + GCPRO4 (org_l1, org_l2, lisp_arg, value); + + 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 (((*pred_fn) (Fcar (l2), Fcar (l1), lisp_arg)) < 0) + { + tem = l1; + l1 = Fcdr (l1); + org_l1 = l1; + } + else + { + tem = l2; + l2 = Fcdr (l2); + org_l2 = l2; + } + if (NILP (tail)) + value = tem; + else + Fsetcdr (tail, tem); + tail = tem; + } +} + + +/************************************************************************/ +/* property-list functions */ +/************************************************************************/ + +/* For properties of text, we need to do order-insensitive comparison of + plists. That is, we need to compare two plists such that they are the + same if they have the same set of keys, and equivalent values. + So (a 1 b 2) would be equal to (b 2 a 1). + + NIL_MEANS_NOT_PRESENT is as in `plists-eq' etc. + LAXP means use `equal' for comparisons. + */ +int +plists_differ (Lisp_Object a, Lisp_Object b, int nil_means_not_present, + int laxp, int depth) +{ + int eqp = (depth == -1); /* -1 as depth means us eq, not equal. */ + int la, lb, m, i, fill; + Lisp_Object *keys, *vals; + char *flags; + Lisp_Object rest; + + if (NILP (a) && NILP (b)) + return 0; + + Fcheck_valid_plist (a); + Fcheck_valid_plist (b); + + la = XINT (Flength (a)); + lb = XINT (Flength (b)); + m = (la > lb ? la : lb); + fill = 0; + keys = (Lisp_Object *) alloca (m * sizeof (Lisp_Object)); + vals = (Lisp_Object *) alloca (m * sizeof (Lisp_Object)); + flags = (char *) alloca (m * sizeof (char)); + + /* First extract the pairs from A. */ + for (rest = a; !NILP (rest); rest = XCDR (XCDR (rest))) + { + Lisp_Object k = XCAR (rest); + Lisp_Object v = XCAR (XCDR (rest)); + /* Maybe be Ebolified. */ + if (nil_means_not_present && NILP (v)) continue; + keys [fill] = k; + vals [fill] = v; + flags[fill] = 0; + fill++; + } + /* Now iterate over B, and stop if we find something that's not in A, + or that doesn't match. As we match, mark them. */ + for (rest = b; !NILP (rest); rest = XCDR (XCDR (rest))) + { + Lisp_Object k = XCAR (rest); + Lisp_Object v = XCAR (XCDR (rest)); + /* Maybe be Ebolified. */ + if (nil_means_not_present && NILP (v)) continue; + for (i = 0; i < fill; i++) + { + if (!laxp ? EQ (k, keys [i]) : internal_equal (k, keys [i], depth)) + { + if ((eqp + /* Ebolified here too, sigh ... */ + ? !HACKEQ_UNSAFE (v, vals [i]) + : !internal_equal (v, vals [i], depth))) + /* a property in B has a different value than in A */ + goto MISMATCH; + flags [i] = 1; + break; + } + } + if (i == fill) + /* there are some properties in B that are not in A */ + goto MISMATCH; + } + /* Now check to see that all the properties in A were also in B */ + for (i = 0; i < fill; i++) + if (flags [i] == 0) + goto MISMATCH; + + /* Ok. */ + return 0; + + MISMATCH: + return 1; +} + +DEFUN ("plists-eq", Fplists_eq, Splists_eq, 2, 3, 0 /* +Return non-nil if property lists A and B are `eq'. +A property list is an alternating list of keywords and values. + This function does order-insensitive comparisons of the property lists: + For example, the property lists '(a 1 b 2) and '(b 2 a 1) are equal. + Comparison between values is done using `eq'. See also `plists-equal'. +If optional arg NIL-MEANS-NOT-PRESENT is non-nil, then a property with + a nil value is ignored. This feature is a virus that has infected + old Lisp implementations (and thus E-Lisp, due to RMS's enamorment with + old Lisps), but should not be used except for backward compatibility. +*/ ) + (a, b, nil_means_not_present) + Lisp_Object a, b, nil_means_not_present; +{ + return (plists_differ (a, b, !NILP (nil_means_not_present), 0, -1) + ? Qnil : Qt); +} + +DEFUN ("plists-equal", Fplists_equal, Splists_equal, 2, 3, 0 /* +Return non-nil if property lists A and B are `equal'. +A property list is an alternating list of keywords and values. This + function does order-insensitive comparisons of the property lists: For + example, the property lists '(a 1 b 2) and '(b 2 a 1) are equal. + Comparison between values is done using `equal'. See also `plists-eq'. +If optional arg NIL-MEANS-NOT-PRESENT is non-nil, then a property with + a nil value is ignored. This feature is a virus that has infected + old Lisp implementations (and thus E-Lisp, due to RMS's enamorment with + old Lisps), but should not be used except for backward compatibility. +*/ ) + (a, b, nil_means_not_present) + Lisp_Object a, b, nil_means_not_present; +{ + return (plists_differ (a, b, !NILP (nil_means_not_present), 0, 1) + ? Qnil : Qt); +} + + +DEFUN ("lax-plists-eq", Flax_plists_eq, Slax_plists_eq, 2, 3, 0 /* +Return non-nil if lax property lists A and B are `eq'. +A property list is an alternating list of keywords and values. + This function does order-insensitive comparisons of the property lists: + For example, the property lists '(a 1 b 2) and '(b 2 a 1) are equal. + Comparison between values is done using `eq'. See also `plists-equal'. +A lax property list is like a regular one except that comparisons between + keywords is done using `equal' instead of `eq'. +If optional arg NIL-MEANS-NOT-PRESENT is non-nil, then a property with + a nil value is ignored. This feature is a virus that has infected + old Lisp implementations (and thus E-Lisp, due to RMS's enamorment with + old Lisps), but should not be used except for backward compatibility. +*/ ) + (a, b, nil_means_not_present) + Lisp_Object a, b, nil_means_not_present; +{ + return (plists_differ (a, b, !NILP (nil_means_not_present), 1, -1) + ? Qnil : Qt); +} + +DEFUN ("lax-plists-equal", Flax_plists_equal, Slax_plists_equal, 2, 3, 0 /* +Return non-nil if lax property lists A and B are `equal'. +A property list is an alternating list of keywords and values. This + function does order-insensitive comparisons of the property lists: For + example, the property lists '(a 1 b 2) and '(b 2 a 1) are equal. + Comparison between values is done using `equal'. See also `plists-eq'. +A lax property list is like a regular one except that comparisons between + keywords is done using `equal' instead of `eq'. +If optional arg NIL-MEANS-NOT-PRESENT is non-nil, then a property with + a nil value is ignored. This feature is a virus that has infected + old Lisp implementations (and thus E-Lisp, due to RMS's enamorment with + old Lisps), but should not be used except for backward compatibility. +*/ ) + (a, b, nil_means_not_present) + Lisp_Object a, b, nil_means_not_present; +{ + return (plists_differ (a, b, !NILP (nil_means_not_present), 1, 1) + ? Qnil : Qt); +} + +/* Return the value associated with key PROPERTY in property list PLIST. + Return nil if key not found. This function is used for internal + property lists that cannot be directly manipulated by the user. + */ + +Lisp_Object +internal_plist_get (Lisp_Object plist, Lisp_Object property) +{ + Lisp_Object tail = plist; + + for (; !NILP (tail); tail = XCDR (XCDR (tail))) + { + struct Lisp_Cons *c = XCONS (tail); + if (EQ (c->car, property)) + return XCAR (c->cdr); + } + + return Qunbound; +} + +/* Set PLIST's value for PROPERTY to VALUE. Analogous to + internal_plist_get(). */ + +void +internal_plist_put (Lisp_Object *plist, Lisp_Object property, + Lisp_Object value) +{ + Lisp_Object tail = *plist; + + for (; !NILP (tail); tail = XCDR (XCDR (tail))) + { + struct Lisp_Cons *c = XCONS (tail); + if (EQ (c->car, property)) + { + XCAR (c->cdr) = value; + return; + } + } + + *plist = Fcons (property, Fcons (value, *plist)); +} + +int +internal_remprop (Lisp_Object *plist, Lisp_Object property) +{ + Lisp_Object tail = *plist; + + if (NILP (tail)) + return 0; + + if (EQ (XCAR (tail), property)) + { + *plist = XCDR (XCDR (tail)); + return 1; + } + + for (tail = XCDR (tail); !NILP (XCDR (tail)); + tail = XCDR (XCDR (tail))) + { + struct Lisp_Cons *c = XCONS (tail); + if (EQ (XCAR (c->cdr), property)) + { + c->cdr = XCDR (XCDR (c->cdr)); + return 1; + } + } + + return 0; +} + +/* Called on a malformed property list. BADPLACE should be some + place where truncating will form a good list -- i.e. we shouldn't + result in a list with an odd length. */ + +static Lisp_Object +bad_bad_bunny (Lisp_Object *plist, Lisp_Object *badplace, Error_behavior errb) +{ + if (ERRB_EQ (errb, ERROR_ME)) + return Fsignal (Qmalformed_property_list, list2 (*plist, *badplace)); + else + { + if (ERRB_EQ (errb, ERROR_ME_WARN)) + { + warn_when_safe_lispobj + (Qlist, Qwarning, + list2 (build_string + ("Malformed property list -- list has been truncated"), + *plist)); + *badplace = Qnil; + } + return Qunbound; + } +} + +/* Called on a circular property list. BADPLACE should be some place + where truncating will result in an even-length list, as above. + If doesn't particularly matter where we truncate -- anywhere we + truncate along the entire list will break the circularity, because + it will create a terminus and the list currently doesn't have one. +*/ + +static Lisp_Object +bad_bad_turtle (Lisp_Object *plist, Lisp_Object *badplace, Error_behavior errb) +{ + if (ERRB_EQ (errb, ERROR_ME)) + /* #### Eek, this will probably result in another error + when PLIST is printed out */ + return Fsignal (Qcircular_property_list, list1 (*plist)); + else + { + if (ERRB_EQ (errb, ERROR_ME_WARN)) + { + warn_when_safe_lispobj + (Qlist, Qwarning, + list2 (build_string + ("Circular property list -- list has been truncated"), + *plist)); + *badplace = Qnil; + } + return Qunbound; + } +} + +/* Advance the tortoise pointer by two (one iteration of a property-list + loop) and the hare pointer by four and verify that no malformations + or circularities exist. If so, return zero and store a value into + RETVAL that should be returned by the calling function. Otherwise, + return 1. See external_plist_get(). + */ + +static int +advance_plist_pointers (Lisp_Object *plist, + Lisp_Object **tortoise, Lisp_Object **hare, + Error_behavior errb, Lisp_Object *retval) +{ + int i; + Lisp_Object *tortsave = *tortoise; + + /* Note that our "fixing" may be more brutal than necessary, + but it's the user's own problem, not ours. if they went in and + manually fucked up a plist. */ + + for (i = 0; i < 2; i++) + { + /* This is a standard iteration of a defensive-loop-checking + loop. We just do it twice because we want to advance past + both the property and its value. + + If the pointer indirection is confusing you, remember that + one level of indirection on the hare and tortoise pointers + is only due to pass-by-reference for this function. The other + level is so that the plist can be fixed in place. */ + + /* When we reach the end of a well-formed plist, **HARE is + nil. In that case, we don't do anything at all except + advance TORTOISE by one. Otherwise, we advance HARE + by two (making sure it's OK to do so), then advance + TORTOISE by one (it will always be OK to do so because + the HARE is always ahead of the TORTOISE and will have + already verified the path), then make sure TORTOISE and + HARE don't contain the same non-nil object -- if the + TORTOISE and the HARE ever meet, then obviously we're + in a circularity, and if we're in a circularity, then + the TORTOISE and the HARE can't cross paths without + meeting, since the HARE only gains one step over the + TORTOISE per iteration. */ + + if (!NILP (**hare)) + { + Lisp_Object *haresave = *hare; + if (!CONSP (**hare)) + { + *retval = bad_bad_bunny (plist, haresave, errb); + return 0; + } + *hare = &XCDR (**hare); + /* In a non-plist, we'd check here for a nil value for + **HARE, which is OK (it just means the list has an + odd number of elements). In a plist, it's not OK + for the list to have an odd number of elements. */ + if (!CONSP (**hare)) + { + *retval = bad_bad_bunny (plist, haresave, errb); + return 0; + } + *hare = &XCDR (**hare); + } + + *tortoise = &XCDR (**tortoise); + if (!NILP (**hare) && EQ (**tortoise, **hare)) + { + *retval = bad_bad_turtle (plist, tortsave, errb); + return 0; + } + } + + return 1; +} + +/* Return the value of PROPERTY from PLIST, or Qunbound if + property is not on the list. + + PLIST is a Lisp-accessible property list, meaning that it + has to be checked for malformations and circularities. + + If ERRB is ERROR_ME, an error will be signalled. Otherwise, the + function will never signal an error; and if ERRB is ERROR_ME_WARN, + on finding a malformation or a circularity, it issues a warning and + attempts to silently fix the problem. + + A pointer to PLIST is passed in so that PLIST can be successfully + "fixed" even if the error is at the beginning of the plist. */ + +Lisp_Object +external_plist_get (Lisp_Object *plist, Lisp_Object property, + int laxp, Error_behavior errb) +{ + Lisp_Object *tortoise = plist; + Lisp_Object *hare = plist; + + while (!NILP (*tortoise)) + { + Lisp_Object *tortsave = tortoise; + Lisp_Object retval; + + /* We do the standard tortoise/hare march. We isolate the + grungy stuff to do this in advance_plist_pointers(), though. + To us, all this function does is advance the tortoise + pointer by two and the hare pointer by four and make sure + everything's OK. We first advance the pointers and then + check if a property matched; this ensures that our + check for a matching property is safe. */ + + if (!advance_plist_pointers (plist, &tortoise, &hare, errb, &retval)) + return retval; + + if (!laxp ? EQ (XCAR (*tortsave), property) + : internal_equal (XCAR (*tortsave), property, 0)) + return XCAR (XCDR (*tortsave)); + } + + return Qunbound; +} + +/* Set PLIST's value for PROPERTY to VALUE, given a possibly + malformed or circular plist. Analogous to external_plist_get(). */ + +void +external_plist_put (Lisp_Object *plist, Lisp_Object property, + Lisp_Object value, int laxp, Error_behavior errb) +{ + Lisp_Object *tortoise = plist; + Lisp_Object *hare = plist; + + while (!NILP (*tortoise)) + { + Lisp_Object *tortsave = tortoise; + Lisp_Object retval; + + /* See above */ + if (!advance_plist_pointers (plist, &tortoise, &hare, errb, &retval)) + return; + + if (!laxp ? EQ (XCAR (*tortsave), property) + : internal_equal (XCAR (*tortsave), property, 0)) + { + XCAR (XCDR (*tortsave)) = value; + return; + } + } + + *plist = Fcons (property, Fcons (value, *plist)); +} + +int +external_remprop (Lisp_Object *plist, Lisp_Object property, + int laxp, Error_behavior errb) +{ + Lisp_Object *tortoise = plist; + Lisp_Object *hare = plist; + + while (!NILP (*tortoise)) + { + Lisp_Object *tortsave = tortoise; + Lisp_Object retval; + + /* See above */ + if (!advance_plist_pointers (plist, &tortoise, &hare, errb, &retval)) + return 0; + + if (!laxp ? EQ (XCAR (*tortsave), property) + : internal_equal (XCAR (*tortsave), property, 0)) + { + /* Now you see why it's so convenient to have that level + of indirection. */ + *tortsave = XCDR (XCDR (*tortsave)); + return 1; + } + } + + return 0; +} + +DEFUN ("plist-get", Fplist_get, Splist_get, 2, 3, 0 /* +Extract a value from a property list. +PLIST is a property list, which is a list of the form +\(PROP1 VALUE1 PROP2 VALUE2...). This function returns the value +corresponding to the given PROP, or DEFAULT if PROP is not +one of the properties on the list. +*/ ) + (plist, prop, defalt) /* Cant spel in C */ + Lisp_Object plist, prop, defalt; +{ + Lisp_Object val = external_plist_get (&plist, prop, 0, ERROR_ME); + if (UNBOUNDP (val)) + return defalt; + return val; +} + +DEFUN ("plist-put", Fplist_put, Splist_put, 3, 3, 0 /* +Change value in PLIST of PROP to VAL. +PLIST is a property list, which is a list of the form \(PROP1 VALUE1 +PROP2 VALUE2 ...). PROP is usually a symbol and VAL is any object. +If PROP is already a property on the list, its value is set to VAL, +otherwise the new PROP VAL pair is added. The new plist is returned; +use `(setq x (plist-put x prop val))' to be sure to use the new value. +The PLIST is modified by side effects. +*/ ) + (plist, prop, val) + Lisp_Object plist, prop, val; +{ + external_plist_put (&plist, prop, val, 0, ERROR_ME); + return plist; +} + +DEFUN ("plist-remprop", Fplist_remprop, Splist_remprop, 2, 2, 0 /* +Remove from PLIST the property PROP and its value. +PLIST is a property list, which is a list of the form \(PROP1 VALUE1 +PROP2 VALUE2 ...). PROP is usually a symbol. The new plist is +returned; use `(setq x (plist-remprop x prop val))' to be sure to use +the new value. The PLIST is modified by side effects. +*/ ) + (plist, prop) + Lisp_Object plist, prop; +{ + external_remprop (&plist, prop, 0, ERROR_ME); + return plist; +} + +DEFUN ("plist-member", Fplist_member, Splist_member, 2, 2, 0 /* +Return t if PROP has a value specified in PLIST. +*/ ) + (plist, prop) + Lisp_Object plist, prop; +{ + return UNBOUNDP (Fplist_get (plist, prop, Qunbound)) ? Qnil : Qt; +} + +DEFUN ("check-valid-plist", Fcheck_valid_plist, Scheck_valid_plist, + 1, 1, 0 /* +Given a plist, signal an error if there is anything wrong with it. +This means that it's a malformed or circular plist. +*/ ) + (plist) + Lisp_Object plist; +{ + Lisp_Object *tortoise; + Lisp_Object *hare; + + start_over: + tortoise = &plist; + hare = &plist; + while (!NILP (*tortoise)) + { + Lisp_Object retval; + + /* See above */ + if (!advance_plist_pointers (&plist, &tortoise, &hare, ERROR_ME, + &retval)) + goto start_over; + } + + return Qnil; +} + +DEFUN ("valid-plist-p", Fvalid_plist_p, Svalid_plist_p, + 1, 1, 0 /* +Given a plist, return non-nil if its format is correct. +If it returns nil, `check-valid-plist' will signal an error when given +the plist; that means it's a malformed or circular plist or has non-symbols +as keywords. +*/ ) + (plist) + Lisp_Object plist; +{ + Lisp_Object *tortoise; + Lisp_Object *hare; + + tortoise = &plist; + hare = &plist; + while (!NILP (*tortoise)) + { + Lisp_Object retval; + + /* See above */ + if (!advance_plist_pointers (&plist, &tortoise, &hare, ERROR_ME_NOT, + &retval)) + return Qnil; + } + + return Qt; +} + +DEFUN ("canonicalize-plist", Fcanonicalize_plist, Scanonicalize_plist, + 1, 2, 0 /* +Destructively remove any duplicate entries from a plist. +In such cases, the first entry applies. + +If optional arg NIL-MEANS-NOT-PRESENT is non-nil, then a property with + a nil value is removed. This feature is a virus that has infected + old Lisp implementations (and thus E-Lisp, due to RMS's enamorment with + old Lisps), but should not be used except for backward compatibility. + +The new plist is returned. If NIL-MEANS-NOT-PRESENT is given, the + return value may not be EQ to the passed-in value, so make sure to + `setq' the value back into where it came from. +*/ ) + (plist, nil_means_not_present) + Lisp_Object plist, nil_means_not_present; +{ + Lisp_Object head = plist; + + Fcheck_valid_plist (plist); + + while (!NILP (plist)) + { + Lisp_Object prop = Fcar (plist); + Lisp_Object next = Fcdr (plist); + + CHECK_CONS (next); /* just make doubly sure we catch any errors */ + if (!NILP (nil_means_not_present) && NILP (Fcar (next))) + { + if (EQ (head, plist)) + head = Fcdr (next); + plist = Fcdr (next); + continue; + } + /* external_remprop returns 1 if it removed any property. + We have to loop till it didn't remove anything, in case + the property occurs many times. */ + while (external_remprop (&XCDR (next), prop, 0, ERROR_ME)); + plist = Fcdr (next); + } + + return head; +} + +DEFUN ("lax-plist-get", Flax_plist_get, Slax_plist_get, 2, 3, 0 /* +Extract a value from a lax property list. + +LAX-PLIST is a lax property list, which is a list of the form \(PROP1 +VALUE1 PROP2 VALUE2...), where comparions between properties is done +using `equal' instead of `eq'. This function returns the value +corresponding to the given PROP, or DEFAULT if PROP is not one of the +properties on the list. +*/ ) + (lax_plist, prop, defalt) /* Cant spel in C */ + Lisp_Object lax_plist, prop, defalt; +{ + Lisp_Object val = external_plist_get (&lax_plist, prop, 1, ERROR_ME); + if (UNBOUNDP (val)) + return defalt; + return val; +} + +DEFUN ("lax-plist-put", Flax_plist_put, Slax_plist_put, 3, 3, 0 /* +Change value in LAX-PLIST of PROP to VAL. +LAX-PLIST is a lax property list, which is a list of the form \(PROP1 +VALUE1 PROP2 VALUE2...), where comparions between properties is done +using `equal' instead of `eq'. PROP is usually a symbol and VAL is +any object. If PROP is already a property on the list, its value is +set to VAL, otherwise the new PROP VAL pair is added. The new plist +is returned; use `(setq x (lax-plist-put x prop val))' to be sure to +use the new value. The LAX-PLIST is modified by side effects. +*/ ) + (lax_plist, prop, val) + Lisp_Object lax_plist, prop, val; +{ + external_plist_put (&lax_plist, prop, val, 1, ERROR_ME); + return lax_plist; +} + +DEFUN ("lax-plist-remprop", Flax_plist_remprop, Slax_plist_remprop, 2, 2, 0 /* +Remove from LAX-PLIST the property PROP and its value. +LAX-PLIST is a lax property list, which is a list of the form \(PROP1 +VALUE1 PROP2 VALUE2...), where comparions between properties is done +using `equal' instead of `eq'. PROP is usually a symbol. The new +plist is returned; use `(setq x (lax-plist-remprop x prop val))' to be +sure to use the new value. The LAX-PLIST is modified by side effects. +*/ ) + (lax_plist, prop) + Lisp_Object lax_plist, prop; +{ + external_remprop (&lax_plist, prop, 1, ERROR_ME); + return lax_plist; +} + +DEFUN ("lax-plist-member", Flax_plist_member, Slax_plist_member, 2, 2, 0 /* +Return t if PROP has a value specified in LAX-PLIST. +LAX-PLIST is a lax property list, which is a list of the form \(PROP1 +VALUE1 PROP2 VALUE2...), where comparions between properties is done +using `equal' instead of `eq'. +*/ ) + (lax_plist, prop) + Lisp_Object lax_plist, prop; +{ + return UNBOUNDP (Flax_plist_get (lax_plist, prop, Qunbound)) ? Qnil : Qt; +} + +DEFUN ("canonicalize-lax-plist", Fcanonicalize_lax_plist, + Scanonicalize_lax_plist, 1, 2, 0 /* +Destructively remove any duplicate entries from a lax plist. +In such cases, the first entry applies. + +If optional arg NIL-MEANS-NOT-PRESENT is non-nil, then a property with + a nil value is removed. This feature is a virus that has infected + old Lisp implementations (and thus E-Lisp, due to RMS's enamorment with + old Lisps), but should not be used except for backward compatibility. + +The new plist is returned. If NIL-MEANS-NOT-PRESENT is given, the + return value may not be EQ to the passed-in value, so make sure to + `setq' the value back into where it came from. +*/ ) + (lax_plist, nil_means_not_present) + Lisp_Object lax_plist, nil_means_not_present; +{ + Lisp_Object head = lax_plist; + + Fcheck_valid_plist (lax_plist); + + while (!NILP (lax_plist)) + { + Lisp_Object prop = Fcar (lax_plist); + Lisp_Object next = Fcdr (lax_plist); + + CHECK_CONS (next); /* just make doubly sure we catch any errors */ + if (!NILP (nil_means_not_present) && NILP (Fcar (next))) + { + if (EQ (head, lax_plist)) + head = Fcdr (next); + lax_plist = Fcdr (next); + continue; + } + /* external_remprop returns 1 if it removed any property. + We have to loop till it didn't remove anything, in case + the property occurs many times. */ + while (external_remprop (&XCDR (next), prop, 1, ERROR_ME)); + lax_plist = Fcdr (next); + } + + return head; +} + +/* In C because the frame props stuff uses it */ + +DEFUN ("destructive-alist-to-plist", Fdestructive_alist_to_plist, + Sdestructive_alist_to_plist, 1, 1, 0 /* +Convert association list ALIST into the equivalent property-list form. +The plist is returned. This converts from + +\((a . 1) (b . 2) (c . 3)) + +into + +\(a 1 b 2 c 3) + +The original alist is destroyed in the process of constructing the plist. +See also `alist-to-plist'. +*/ ) + (alist) + Lisp_Object alist; +{ + Lisp_Object head = alist; + while (!NILP (alist)) + { + /* remember the alist element. */ + Lisp_Object el = Fcar (alist); + + Fsetcar (alist, Fcar (el)); + Fsetcar (el, Fcdr (el)); + Fsetcdr (el, Fcdr (alist)); + Fsetcdr (alist, el); + alist = Fcdr (Fcdr (alist)); + } + + return head; +} + +/* Symbol plists are directly accessible, so we need to protect against + invalid property list structure */ + +static Lisp_Object +symbol_getprop (Lisp_Object sym, Lisp_Object propname, Lisp_Object defalt) +{ + Lisp_Object val = external_plist_get (&XSYMBOL (sym)->plist, propname, + 0, ERROR_ME); + if (UNBOUNDP (val)) + return defalt; + return val; +} + +static void +symbol_putprop (Lisp_Object sym, Lisp_Object propname, Lisp_Object value) +{ + external_plist_put (&XSYMBOL (sym)->plist, propname, value, 0, ERROR_ME); +} + +static int +symbol_remprop (Lisp_Object symbol, Lisp_Object propname) +{ + return external_remprop (&XSYMBOL (symbol)->plist, propname, 0, ERROR_ME); +} + +/* We store the string's extent info as the first element of the string's + property list; and the string's MODIFF as the first or second element + of the string's property list (depending on whether the extent info + is present), but only if the string has been modified. This is ugly + but it reduces the memory allocated for the string in the vast + majority of cases, where the string is never modified and has no + extent info. */ + + +static Lisp_Object * +string_plist_ptr (struct Lisp_String *s) +{ + Lisp_Object *ptr = &s->plist; + + if (CONSP (*ptr) && EXTENT_INFOP (XCAR (*ptr))) + ptr = &XCDR (*ptr); + if (CONSP (*ptr) && INTP (XCAR (*ptr))) + ptr = &XCDR (*ptr); + return ptr; +} + +Lisp_Object +string_getprop (struct Lisp_String *s, Lisp_Object property, + Lisp_Object defalt) +{ + Lisp_Object val = external_plist_get (string_plist_ptr (s), property, 0, + ERROR_ME); + if (UNBOUNDP (val)) + return defalt; + return val; +} + +void +string_putprop (struct Lisp_String *s, Lisp_Object property, + Lisp_Object value) +{ + external_plist_put (string_plist_ptr (s), property, value, 0, ERROR_ME); +} + +static int +string_remprop (struct Lisp_String *s, Lisp_Object property) +{ + return external_remprop (string_plist_ptr (s), property, 0, ERROR_ME); +} + +static Lisp_Object +string_plist (struct Lisp_String *s) +{ + return *string_plist_ptr (s); +} + +DEFUN ("get", Fget, Sget, 2, 3, 0 /* +Return the value of OBJECT's PROPNAME property. +This is the last VALUE stored with `(put OBJECT PROPNAME VALUE)'. +If there is no such property, return optional third arg DEFAULT +(which defaults to `nil'). OBJECT can be a symbol, face, extent, +or string. See also `put', `remprop', and `object-plist'. +*/ ) + (object, propname, defalt) /* Cant spel in C */ + Lisp_Object object, propname, defalt; +{ + Lisp_Object val; + + /* Various places in emacs call Fget() and expect it not to quit, + so don't quit. */ + + /* It's easiest to treat symbols specially because they may not + be an lrecord */ + if (SYMBOLP (object)) + val = symbol_getprop (object, propname, defalt); + else if (STRINGP (object)) + val = string_getprop (XSTRING (object), propname, defalt); + else if (LRECORDP (object)) + { + CONST struct lrecord_implementation + *imp = XRECORD_LHEADER (object)->implementation; + if (imp->getprop) + { + val = (imp->getprop) (object, propname); + if (UNBOUNDP (val)) + val = defalt; + } + else + goto noprops; + } + else + { + noprops: + signal_simple_error ("Object type has no properties", object); + } + + return val; +} + +DEFUN ("put", Fput, Sput, 3, 3, 0 /* +Store OBJECT's PROPNAME property with value VALUE. +It can be retrieved with `(get OBJECT PROPNAME)'. OBJECT can be a +symbol, face, extent, or string. + +For a string, no properties currently have predefined meanings. +For the predefined properties for extents, see `set-extent-property'. +For the predefined properties for faces, see `set-face-property'. + +See also `get', `remprop', and `object-plist'. +*/ ) + (object, propname, value) + Lisp_Object object; + Lisp_Object propname; + Lisp_Object value; +{ + CHECK_SYMBOL (propname); + CHECK_IMPURE (object); + + if (SYMBOLP (object)) + symbol_putprop (object, propname, value); + else if (STRINGP (object)) + string_putprop (XSTRING (object), propname, value); + else if (LRECORDP (object)) + { + CONST struct lrecord_implementation + *imp = XRECORD_LHEADER (object)->implementation; + if (imp->putprop) + { + if (! (imp->putprop) (object, propname, value)) + signal_simple_error ("Can't set property on object", propname); + } + else + goto noprops; + } + else + { + noprops: + signal_simple_error ("Object type has no settable properties", object); + } + + return value; +} + +void +pure_put (Lisp_Object sym, Lisp_Object prop, Lisp_Object val) +{ + Fput (sym, prop, Fpurecopy (val)); +} + +DEFUN ("remprop", Fremprop, Sremprop, 2, 2, 0 /* +Remove from OBJECT's property list the property PROPNAME and its +value. OBJECT can be a symbol, face, extent, or string. Returns +non-nil if the property list was actually changed (i.e. if PROPNAME +was present in the property list). See also `get', `put', and +`object-plist'. +*/ ) + (object, propname) + Lisp_Object object, propname; +{ + int retval = 0; + + CHECK_SYMBOL (propname); + CHECK_IMPURE (object); + + if (SYMBOLP (object)) + retval = symbol_remprop (object, propname); + else if (STRINGP (object)) + retval = string_remprop (XSTRING (object), propname); + else if (LRECORDP (object)) + { + CONST struct lrecord_implementation + *imp = XRECORD_LHEADER (object)->implementation; + if (imp->remprop) + { + retval = (imp->remprop) (object, propname); + if (retval == -1) + signal_simple_error ("Can't remove property from object", + propname); + } + else + goto noprops; + } + else + { + noprops: + signal_simple_error ("Object type has no removable properties", object); + } + + return retval ? Qt : Qnil; +} + +DEFUN ("object-plist", Fobject_plist, Sobject_plist, 1, 1, 0 /* +Return a property list of OBJECT's props. +For a symbol this is equivalent to `symbol-plist'. +Do not modify the property list directly; this may or may not have +the desired effects. (In particular, for a property with a special +interpretation, this will probably have no effect at all.) +*/ ) + (object) + Lisp_Object object; +{ + if (SYMBOLP (object)) + return Fsymbol_plist (object); + else if (STRINGP (object)) + return string_plist (XSTRING (object)); + else if (LRECORDP (object)) + { + CONST struct lrecord_implementation + *imp = XRECORD_LHEADER (object)->implementation; + if (imp->plist) + return (imp->plist) (object); + else + signal_simple_error ("Object type has no properties", object); + } + else + signal_simple_error ("Object type has no properties", object); + + return Qnil; +} + + +int +internal_equal (Lisp_Object o1, Lisp_Object o2, int depth) +{ + if (depth > 200) + error ("Stack overflow in equal"); + do_cdr: + QUIT; + if (HACKEQ_UNSAFE (o1, o2)) + return (1); + /* Note that (equal 20 20.0) should be nil */ + else if (XTYPE (o1) != XTYPE (o2)) + return (0); + else if (CONSP (o1)) + { + if (!internal_equal (Fcar (o1), Fcar (o2), depth + 1)) + return (0); + o1 = Fcdr (o1); + o2 = Fcdr (o2); + goto do_cdr; + } + +#ifndef LRECORD_VECTOR + else if (VECTORP (o1)) + { + int indecks; + int len = vector_length (XVECTOR (o1)); + if (len != vector_length (XVECTOR (o2))) + return (0); + for (indecks = 0; indecks < len; indecks++) + { + Lisp_Object v1, v2; + v1 = vector_data (XVECTOR (o1)) [indecks]; + v2 = vector_data (XVECTOR (o2)) [indecks]; + if (!internal_equal (v1, v2, depth + 1)) + return (0); + } + return (1); + } +#endif /* !LRECORD_VECTOR */ + else if (STRINGP (o1)) + { + Bytecount len = string_length (XSTRING (o1)); + if (len != string_length (XSTRING (o2))) + return (0); + if (memcmp (string_data (XSTRING (o1)), string_data (XSTRING (o2)), len)) + return (0); + return (1); + } + else if (LRECORDP (o1)) + { + CONST struct lrecord_implementation + *imp1 = XRECORD_LHEADER (o1)->implementation, + *imp2 = XRECORD_LHEADER (o2)->implementation; + if (imp1 != imp2) + return (0); + else if (imp1->equal == 0) + /* EQ-ness of the objects was noticed above */ + return (0); + else + return ((imp1->equal) (o1, o2, depth)); + } + + return (0); +} + +DEFUN ("equal", Fequal, Sequal, 2, 2, 0 /* +T if two Lisp objects have similar structure and contents. +They must have the same data type. +Conses are compared by comparing the cars and the cdrs. +Vectors and strings are compared element by element. +Numbers are compared by value. Symbols must match exactly. +*/ ) + (o1, o2) + Lisp_Object o1, o2; +{ + return ((internal_equal (o1, o2, 0)) ? Qt : Qnil); +} + + +DEFUN ("fillarray", Ffillarray, Sfillarray, 2, 2, 0 /* +Store each element of ARRAY with ITEM. +ARRAY is a vector, bit vector, or string. +*/ ) + (array, item) + Lisp_Object array, item; +{ + retry: + if (VECTORP (array)) + { + Lisp_Object *p; + int size; + int indecks; + CHECK_IMPURE (array); + size = vector_length (XVECTOR (array)); + p = vector_data (XVECTOR (array)); + for (indecks = 0; indecks < size; indecks++) + p[indecks] = item; + } + else if (VECTORP (array)) + { + struct Lisp_Bit_Vector *v; + int size; + int indecks; + + CHECK_BIT (item); + CHECK_IMPURE (array); + v = XBIT_VECTOR (array); + size = bit_vector_length (v); + for (indecks = 0; indecks < size; indecks++) + set_bit_vector_bit (v, indecks, XINT (item)); + } + else if (STRINGP (array)) + { + Charcount size; + Charcount indecks; + Emchar charval; + CHECK_CHAR_COERCE_INT (item); + CHECK_IMPURE (array); + charval = XCHAR (item); + size = string_char_length (XSTRING (array)); + for (indecks = 0; indecks < size; indecks++) + set_string_char (XSTRING (array), indecks, charval); + bump_string_modiff (array); + } + else + { + array = wrong_type_argument (Qarrayp, array); + goto retry; + } + return array; +} + +Lisp_Object +nconc2 (Lisp_Object s1, Lisp_Object s2) +{ + Lisp_Object args[2]; + args[0] = s1; + args[1] = s2; + return Fnconc (2, args); +} + +DEFUN ("nconc", Fnconc, Snconc, 0, MANY, 0 /* +Concatenate any number of lists by altering them. +Only the last argument is not altered, and need not be a list. +*/ ) + (nargs, args) + int nargs; + Lisp_Object *args; +{ + int argnum; + Lisp_Object tail, tem, val; + struct gcpro gcpro1; + + /* The modus operandi in Emacs is "caller gc-protects args". + However, nconc (particularly nconc2 ()) is called many times + in Emacs on freshly created stuff (e.g. you see the idiom + nconc2 (Fcopy_sequence (foo), bar) a lot). So we help those + callers out by protecting the args ourselves to save them + a lot of temporary-variable grief. */ + + GCPRO1 (args[0]); + gcpro1.nvars = nargs; + + val = Qnil; + + for (argnum = 0; argnum < nargs; argnum++) + { + tem = args[argnum]; + if (NILP (tem)) continue; + + if (NILP (val)) + val = tem; + + if (argnum + 1 == nargs) break; + + if (!CONSP (tem)) + tem = wrong_type_argument (Qlistp, tem); + + while (CONSP (tem)) + { + tail = tem; + tem = Fcdr (tail); + QUIT; + } + + tem = args[argnum + 1]; + Fsetcdr (tail, tem); + if (NILP (tem)) + args[argnum + 1] = tail; + } + + RETURN_UNGCPRO (val); +} + + +/* This is the guts of all mapping functions. + Apply fn to each element of seq, one by one, + storing the results into elements of vals, a C vector of Lisp_Objects. + leni is the length of vals, which should also be the length of seq. + + If VALS is a null pointer, do not accumulate the results. */ + +static void +mapcar1 (int leni, Lisp_Object *vals, Lisp_Object fn, Lisp_Object seq) +{ + Lisp_Object tail; + Lisp_Object dummy = Qnil; + int i; + struct gcpro gcpro1, gcpro2, gcpro3; + Lisp_Object result; + + GCPRO3 (dummy, fn, seq); + + if (vals) + { + /* Don't let vals contain any garbage when GC happens. */ + for (i = 0; i < leni; i++) + vals[i] = Qnil; + gcpro1.var = vals; + gcpro1.nvars = leni; + } + + /* We need not explicitly protect `tail' because it is used only on + lists, and 1) lists are not relocated and 2) the list is marked + via `seq' so will not be freed */ + + if (VECTORP (seq)) + { + for (i = 0; i < leni; i++) + { + dummy = vector_data (XVECTOR (seq))[i]; + result = call1 (fn, dummy); + if (vals) + vals[i] = result; + } + } + else if (BIT_VECTORP (seq)) + { + struct Lisp_Bit_Vector *v = XBIT_VECTOR (seq); + for (i = 0; i < leni; i++) + { + XSETINT (dummy, bit_vector_bit (v, i)); + result = call1 (fn, dummy); + if (vals) + vals[i] = result; + } + } + else if (STRINGP (seq)) + { + for (i = 0; i < leni; i++) + { + result = call1 (fn, make_char (string_char (XSTRING (seq), i))); + if (vals) + vals[i] = result; + } + } + else /* Must be a list, since Flength did not get an error */ + { + tail = seq; + for (i = 0; i < leni; i++) + { + result = call1 (fn, Fcar (tail)); + if (vals) + vals[i] = result; + tail = Fcdr (tail); + } + } + + UNGCPRO; +} + +DEFUN ("mapconcat", Fmapconcat, Smapconcat, 3, 3, 0 /* +Apply FN to each element of SEQ, and concat the results as strings. +In between each pair of results, stick in SEP. +Thus, \" \" as SEP results in spaces between the values returned by FN. +*/ ) + (fn, seq, sep) + Lisp_Object fn, seq, sep; +{ + Lisp_Object len; + int leni; + int nargs; + Lisp_Object *args; + int i; + struct gcpro gcpro1; + + len = Flength (seq); + leni = XINT (len); + nargs = leni + leni - 1; + if (nargs < 0) return build_string (""); + + args = (Lisp_Object *) alloca (nargs * sizeof (Lisp_Object)); + + GCPRO1 (sep); + mapcar1 (leni, args, fn, seq); + UNGCPRO; + + for (i = leni - 1; i >= 0; i--) + args[i + i] = args[i]; + + for (i = 1; i < nargs; i += 2) + args[i] = sep; + + return Fconcat (nargs, args); +} + +DEFUN ("mapcar", Fmapcar, Smapcar, 2, 2, 0 /* +Apply FUNCTION to each element of SEQUENCE, and make a list of the results. +The result is a list just as long as SEQUENCE. +SEQUENCE may be a list, a vector, a bit vector, or a string. +*/ ) + (fn, seq) + Lisp_Object fn, seq; +{ + Lisp_Object len; + int leni; + Lisp_Object *args; + + len = Flength (seq); + leni = XINT (len); + args = (Lisp_Object *) alloca (leni * sizeof (Lisp_Object)); + + mapcar1 (leni, args, fn, seq); + + return Flist (leni, args); +} + +DEFUN ("mapc-internal", Fmapc_internal, Smapc_internal, 2, 2, 0 /* +Apply FUNCTION to 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. +*/ ) + (fn, seq) + Lisp_Object fn, seq; +{ + Lisp_Object len; + int leni; + + len = Flength (seq); + leni = XINT (len); + + mapcar1 (leni, 0, fn, seq); + + return Qnil; +} + + +/* #### this function doesn't belong in this file! */ + +DEFUN ("load-average", Fload_average, Sload_average, 0, 0, 0 /* +Return list of 1 minute, 5 minute and 15 minute load averages. +Each of the three load averages is multiplied by 100, +then converted to integer. + +If the 5-minute or 15-minute load averages are not available, return a +shortened list, containing only those averages which are available. + +On most systems, this won't work unless the emacs executable is installed +as setgid kmem (assuming that /dev/kmem is in the group kmem). +*/ ) + () +{ + double load_ave[10]; /* hey, just in case */ + int loads = getloadavg (load_ave, 3); + Lisp_Object ret; + + if (loads == -2) + error ("load-average not implemented for this operating system."); + else if (loads < 0) + error ("could not get load-average; check permissions."); + + ret = Qnil; + while (loads > 0) + ret = Fcons (make_int ((int) (load_ave[--loads] * 100.0)), ret); + + return ret; +} + + +Lisp_Object Vfeatures; + +DEFUN ("featurep", Ffeaturep, Sfeaturep, 1, 1, 0 /* +Return t if FEATURE is present in this Emacs. +Use this to conditionalize execution of lisp code based on the presence or +absence of emacs or environment extensions. +Use `provide' to declare that a feature is available. +This function looks at the value of the variable `features'. +*/ ) + (feature) + Lisp_Object feature; +{ + Lisp_Object tem; + CHECK_SYMBOL (feature); + tem = Fmemq (feature, Vfeatures); + return (NILP (tem)) ? Qnil : Qt; +} + +DEFUN ("provide", Fprovide, Sprovide, 1, 1, 0 /* +Announce that FEATURE is a feature of the current Emacs. +*/ ) + (feature) + Lisp_Object feature; +{ + Lisp_Object tem; + CHECK_SYMBOL (feature); + if (!NILP (Vautoload_queue)) + Vautoload_queue = Fcons (Fcons (Vfeatures, Qnil), Vautoload_queue); + tem = Fmemq (feature, Vfeatures); + if (NILP (tem)) + Vfeatures = Fcons (feature, Vfeatures); + LOADHIST_ATTACH (Fcons (Qprovide, feature)); + return feature; +} + +DEFUN ("require", Frequire, Srequire, 1, 2, 0 /* +If feature FEATURE is not loaded, load it from FILENAME. +If FEATURE is not a member of the list `features', then the feature +is not loaded; so load the file FILENAME. +If FILENAME is omitted, the printname of FEATURE is used as the file name. +*/ ) + (feature, file_name) + Lisp_Object feature, file_name; +{ + Lisp_Object tem; + CHECK_SYMBOL (feature); + tem = Fmemq (feature, Vfeatures); + LOADHIST_ATTACH (Fcons (Qrequire, feature)); + if (!NILP (tem)) + return (feature); + else + { + int speccount = specpdl_depth (); + + /* Value saved here is to be restored into Vautoload_queue */ + record_unwind_protect (un_autoload, Vautoload_queue); + Vautoload_queue = Qt; + + call4 (Qload, NILP (file_name) ? Fsymbol_name (feature) : file_name, + Qnil, Qt, Qnil); + + tem = Fmemq (feature, Vfeatures); + if (NILP (tem)) + error ("Required feature %s was not provided", + string_data (XSYMBOL (feature)->name)); + + /* Once loading finishes, don't undo it. */ + Vautoload_queue = Qt; + return (unbind_to (speccount, feature)); + } +} + + +Lisp_Object Qyes_or_no_p; + +void +syms_of_fns (void) +{ + defsymbol (&Qstring_lessp, "string-lessp"); + defsymbol (&Qidentity, "identity"); + defsymbol (&Qyes_or_no_p, "yes-or-no-p"); + + defsubr (&Sidentity); + defsubr (&Srandom); + defsubr (&Slength); + defsubr (&Ssafe_length); + defsubr (&Sstring_equal); + defsubr (&Sstring_lessp); + defsubr (&Sstring_modified_tick); + defsubr (&Sappend); + defsubr (&Sconcat); + defsubr (&Svconcat); + defsubr (&Sbvconcat); + defsubr (&Scopy_sequence); + defsubr (&Scopy_alist); + defsubr (&Scopy_tree); + defsubr (&Ssubstring); + defsubr (&Ssubseq); + defsubr (&Snthcdr); + defsubr (&Snth); + defsubr (&Selt); + defsubr (&Smember); + defsubr (&Smemq); + defsubr (&Sassoc); + defsubr (&Sassq); + defsubr (&Srassoc); + defsubr (&Srassq); + defsubr (&Sdelete); + defsubr (&Sdelq); + defsubr (&Sremassoc); + defsubr (&Sremassq); + defsubr (&Sremrassoc); + defsubr (&Sremrassq); + defsubr (&Snreverse); + defsubr (&Sreverse); + defsubr (&Ssort); + defsubr (&Splists_eq); + defsubr (&Splists_equal); + defsubr (&Slax_plists_eq); + defsubr (&Slax_plists_equal); + defsubr (&Splist_get); + defsubr (&Splist_put); + defsubr (&Splist_remprop); + defsubr (&Splist_member); + defsubr (&Scheck_valid_plist); + defsubr (&Svalid_plist_p); + defsubr (&Scanonicalize_plist); + defsubr (&Slax_plist_get); + defsubr (&Slax_plist_put); + defsubr (&Slax_plist_remprop); + defsubr (&Slax_plist_member); + defsubr (&Scanonicalize_lax_plist); + defsubr (&Sdestructive_alist_to_plist); + defsubr (&Sget); + defsubr (&Sput); + defsubr (&Sremprop); + defsubr (&Sobject_plist); + defsubr (&Sequal); + defsubr (&Sfillarray); + defsubr (&Snconc); + defsubr (&Smapcar); + defsubr (&Smapc_internal); + defsubr (&Smapconcat); + defsubr (&Sload_average); + defsubr (&Sfeaturep); + defsubr (&Srequire); + defsubr (&Sprovide); +} + +void +init_provide_once (void) +{ + DEFVAR_LISP ("features", &Vfeatures /* +A list of symbols which are the features of the executing emacs. +Used by `featurep' and `require', and altered by `provide'. +*/ ); + Vfeatures = Qnil; +}