diff src/fns.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 9d177e8d4150
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/fns.c	Mon Aug 13 11:28:15 2007 +0200
@@ -0,0 +1,3900 @@
+/* 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"
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <errno.h>
+
+#include "buffer.h"
+#include "bytecode.h"
+#include "device.h"
+#include "events.h"
+#include "extents.h"
+#include "frame.h"
+#include "systime.h"
+#include "insdel.h"
+#include "lstream.h"
+#include "opaque.h"
+
+/* NOTE: This symbol is also used in lread.c */
+#define FEATUREP_SYNTAX
+
+Lisp_Object Qstring_lessp;
+Lisp_Object Qidentity;
+
+static int internal_old_equal (Lisp_Object, Lisp_Object, int);
+
+static Lisp_Object
+mark_bit_vector (Lisp_Object obj)
+{
+  return Qnil;
+}
+
+static void
+print_bit_vector (Lisp_Object obj, Lisp_Object printcharfun, int escapeflag)
+{
+  size_t i;
+  struct Lisp_Bit_Vector *v = XBIT_VECTOR (obj);
+  size_t len = bit_vector_length (v);
+  size_t 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 obj1, Lisp_Object obj2, int depth)
+{
+  struct Lisp_Bit_Vector *v1 = XBIT_VECTOR (obj1);
+  struct Lisp_Bit_Vector *v2 = XBIT_VECTOR (obj2);
+
+  return ((bit_vector_length (v1) == bit_vector_length (v2)) &&
+	  !memcmp (v1->bits, v2->bits,
+		   BIT_VECTOR_LONG_STORAGE (bit_vector_length (v1)) *
+		   sizeof (long)));
+}
+
+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)));
+}
+
+static const struct lrecord_description bit_vector_description[] = {
+  { XD_LISP_OBJECT, offsetof(Lisp_Bit_Vector, next), 1 },
+  { XD_END }
+};
+
+
+DEFINE_BASIC_LRECORD_IMPLEMENTATION ("bit-vector", bit_vector,
+				     mark_bit_vector, print_bit_vector, 0,
+				     bit_vector_equal, bit_vector_hash,
+				     bit_vector_description,
+				     struct Lisp_Bit_Vector);
+
+DEFUN ("identity", Fidentity, 1, 1, 0, /*
+Return the argument unchanged.
+*/
+       (arg))
+{
+  return arg;
+}
+
+extern long get_random (void);
+extern void seed_random (long arg);
+
+DEFUN ("random", Frandom, 0, 1, 0, /*
+Return a pseudo-random number.
+All integers representable in Lisp are equally likely.
+  On most systems, this is 28 bits' worth.
+With positive integer argument N, return random number in interval [0,N).
+With argument t, set the random number seed from the current time and pid.
+*/
+       (limit))
+{
+  EMACS_INT 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 ();
+
+  return make_int (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 *f = XCOMPILED_FUNCTION (seq);
+
+      return (f->flags.interactivep ? COMPILED_INTERACTIVE :
+	      f->flags.domainp      ? COMPILED_DOMAIN :
+	      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 20.3, `%s' no longer works with compiled-function objects",
+       function);
+}
+
+DEFUN ("length", Flength, 1, 1, 0, /*
+Return the length of vector, bit vector, list or string SEQUENCE.
+*/
+       (sequence))
+{
+ retry:
+  if (STRINGP (sequence))
+    return make_int (XSTRING_CHAR_LENGTH (sequence));
+  else if (CONSP (sequence))
+    {
+      size_t len;
+      GET_EXTERNAL_LIST_LENGTH (sequence, len);
+      return make_int (len);
+    }
+  else if (VECTORP (sequence))
+    return make_int (XVECTOR_LENGTH (sequence));
+  else if (NILP (sequence))
+    return Qzero;
+  else if (BIT_VECTORP (sequence))
+    return make_int (bit_vector_length (XBIT_VECTOR (sequence)));
+  else
+    {
+      check_losing_bytecode ("length", sequence);
+      sequence = wrong_type_argument (Qsequencep, sequence);
+      goto retry;
+    }
+}
+
+DEFUN ("safe-length", Fsafe_length, 1, 1, 0, /*
+Return the length of a list, but avoid error or infinite loop.
+This function never gets an error.  If LIST is not really a list,
+it returns 0.  If LIST is circular, it returns a finite value
+which is at least the number of distinct elements.
+*/
+       (list))
+{
+  Lisp_Object hare, tortoise;
+  size_t len;
+
+  for (hare = tortoise = list, len = 0;
+       CONSP (hare) && (! EQ (hare, tortoise) || len == 0);
+       hare = XCDR (hare), len++)
+    {
+      if (len & 1)
+	tortoise = XCDR (tortoise);
+    }
+
+  return make_int (len);
+}
+
+/*** string functions. ***/
+
+DEFUN ("string-equal", Fstring_equal, 2, 2, 0, /*
+Return 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 19.  In FSF Emacs 20
+`equal' is the same as in XEmacs, in that respect.)
+Symbols are also allowed; their print names are used instead.
+*/
+       (s1, s2))
+{
+  Bytecount len;
+  struct Lisp_String *p1, *p2;
+
+  if (SYMBOLP (s1))
+    p1 = XSYMBOL (s1)->name;
+  else
+    {
+      CHECK_STRING (s1);
+      p1 = XSTRING (s1);
+    }
+
+  if (SYMBOLP (s2))
+    p2 = XSYMBOL (s2)->name;
+  else
+    {
+      CHECK_STRING (s2);
+      p2 = XSTRING (s2);
+    }
+
+  return (((len = string_length (p1)) == string_length (p2)) &&
+	  !memcmp (string_data (p1), string_data (p2), len)) ? Qt : Qnil;
+}
+
+
+DEFUN ("string-lessp", Fstring_lessp, 2, 2, 0, /*
+Return t if first arg string is less than second in lexicographic order.
+If I18N2 support (but not Mule support) was compiled in, ordering is
+determined by the locale. (Case is significant for the default C locale.)
+In all other cases, comparison is simply done on a character-by-
+character basis using the numeric value of a character. (Note that
+this may not produce particularly meaningful results under Mule if
+characters from different charsets are being compared.)
+
+Symbols are also allowed; their print names are used instead.
+
+The reason that the I18N2 locale-specific collation is not used under
+Mule is that the locale model of internationalization does not handle
+multiple charsets and thus has no hope of working properly under Mule.
+What we really should do is create a collation table over all built-in
+charsets.  This is extremely difficult to do from scratch, however.
+
+Unicode is a good first step towards solving this problem.  In fact,
+it is quite likely that a collation table exists (or will exist) for
+Unicode.  When Unicode support is added to XEmacs/Mule, this problem
+may be solved.
+*/
+       (s1, s2))
+{
+  struct Lisp_String *p1, *p2;
+  Charcount end, len2;
+  int i;
+
+  if (SYMBOLP (s1))
+    p1 = XSYMBOL (s1)->name;
+  else
+    {
+      CHECK_STRING (s1);
+      p1 = XSTRING (s1);
+    }
+
+  if (SYMBOLP (s2))
+    p2 = XSYMBOL (s2)->name;
+  else
+    {
+      CHECK_STRING (s2);
+      p2 = XSTRING (s2);
+    }
+
+  end  = string_char_length (p1);
+  len2 = string_char_length (p2);
+  if (end > len2)
+    end = len2;
+
+#if defined (I18N2) && !defined (MULE)
+  /* There is no hope of this working under Mule.  Even if we converted
+     the data into an external format so that strcoll() processed it
+     properly, it would still not work because strcoll() does not
+     handle multiple locales.  This is the fundamental flaw in the
+     locale model. */
+  {
+    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, or MULE */
+  {
+    Bufbyte *ptr1 = string_data (p1);
+    Bufbyte *ptr2 = string_data (p2);
+
+    /* #### It is not really necessary to do this: We could compare
+       byte-by-byte and still get a reasonable comparison, since this
+       would compare characters with a charset in the same way.  With
+       a little rearrangement of the leading bytes, we could make most
+       inter-charset comparisons work out the same, too; even if some
+       don't, this is not a big deal because inter-charset comparisons
+       aren't really well-defined anyway. */
+    for (i = 0; i < end; i++)
+      {
+	if (charptr_emchar (ptr1) != charptr_emchar (ptr2))
+	  return charptr_emchar (ptr1) < charptr_emchar (ptr2) ? Qt : Qnil;
+	INC_CHARPTR (ptr1);
+	INC_CHARPTR (ptr2);
+      }
+  }
+#endif /* not I18N2, or MULE */
+  /* 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, 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))
+{
+  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, 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.
+Also see: `nconc'.
+*/
+       (int nargs, Lisp_Object *args))
+{
+  return concat (nargs, args, c_cons, 1);
+}
+
+DEFUN ("concat", Fconcat, 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.
+
+As of XEmacs 21.0, this function does NOT accept individual integers
+as arguments.  Old code that relies on, for example, (concat "foo" 50)
+returning "foo50" will fail.  To fix such code, either apply
+`int-to-string' to the integer argument, or use `format'.
+*/
+       (int nargs, Lisp_Object *args))
+{
+  return concat (nargs, args, c_string, 0);
+}
+
+DEFUN ("vconcat", Fvconcat, 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.
+*/
+       (int nargs, Lisp_Object *args))
+{
+  return concat (nargs, args, c_vector, 0);
+}
+
+DEFUN ("bvconcat", Fbvconcat, 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.
+*/
+       (int nargs, Lisp_Object *args))
+{
+  return concat (nargs, args, c_bit_vector, 0);
+}
+
+/* Copy a (possibly dotted) list.  LIST must be a cons.
+   Can't use concat (1, &alist, c_cons, 0) - doesn't handle dotted lists. */
+static Lisp_Object
+copy_list (Lisp_Object list)
+{
+  Lisp_Object list_copy = Fcons (XCAR (list), XCDR (list));
+  Lisp_Object last = list_copy;
+  Lisp_Object hare, tortoise;
+  size_t len;
+
+  for (tortoise = hare = XCDR (list), len = 1;
+       CONSP (hare);
+       hare = XCDR (hare), len++)
+    {
+      XCDR (last) = Fcons (XCAR (hare), XCDR (hare));
+      last = XCDR (last);
+
+      if (len < CIRCULAR_LIST_SUSPICION_LENGTH)
+	continue;
+      if (len & 1)
+	tortoise = XCDR (tortoise);
+      if (EQ (tortoise, hare))
+	signal_circular_list_error (list);
+    }
+
+  return list_copy;
+}
+
+DEFUN ("copy-list", Fcopy_list, 1, 1, 0, /*
+Return a copy of list LIST, which may be a dotted list.
+The elements of LIST are not copied; they are shared
+with the original.
+*/
+       (list))
+{
+ again:
+  if (NILP  (list)) return list;
+  if (CONSP (list)) return copy_list (list);
+
+  list = wrong_type_argument (Qlistp, list);
+  goto again;
+}
+
+DEFUN ("copy-sequence", Fcopy_sequence, 1, 1, 0, /*
+Return a copy of list, vector, bit vector or string SEQUENCE.
+The elements of a list or vector are not copied; they are shared
+with the original. SEQUENCE may be a dotted list.
+*/
+       (sequence))
+{
+ again:
+  if (NILP        (sequence)) return sequence;
+  if (CONSP       (sequence)) return copy_list (sequence);
+  if (STRINGP     (sequence)) return concat (1, &sequence, c_string,     0);
+  if (VECTORP     (sequence)) return concat (1, &sequence, c_vector,     0);
+  if (BIT_VECTORP (sequence)) return concat (1, &sequence, c_bit_vector, 0);
+
+  check_losing_bytecode ("copy-sequence", sequence);
+  sequence = wrong_type_argument (Qsequencep, sequence);
+  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 = alloca_array (struct merge_string_extents_struct, nargs);
+
+  /* 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 (LISTP (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
+#if 0				/* removed for XEmacs 21 */
+      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);
+#endif
+      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 = XSTRING_DATA (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 = XCAR (seq);
+              seq = XCDR (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 = XVECTOR_DATA (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))
+	    XVECTOR_DATA (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, 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 tail;
+
+  if (NILP (alist))
+    return alist;
+  CHECK_CONS (alist);
+
+  alist = concat (1, &alist, c_cons, 0);
+  for (tail = alist; CONSP (tail); tail = XCDR (tail))
+    {
+      Lisp_Object car = XCAR (tail);
+
+      if (CONSP (car))
+	XCAR (tail) = Fcons (XCAR (car), XCDR (car));
+    }
+  return alist;
+}
+
+DEFUN ("copy-tree", Fcopy_tree, 1, 2, 0, /*
+Return a copy of a list and substructures.
+The argument is copied, and any lists contained within it are copied
+recursively.  Circularities and shared substructures are not preserved.
+Second arg VECP causes vectors to be copied, too.  Strings and bit vectors
+are not copied.
+*/
+       (arg, vecp))
+{
+  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 = XVECTOR_LENGTH (arg);
+      int j;
+      arg = Fcopy_sequence (arg);
+      for (j = 0; j < i; j++)
+	{
+	  Lisp_Object elt = XVECTOR_DATA (arg) [j];
+	  QUIT;
+	  if (CONSP (elt) || VECTORP (elt))
+	    XVECTOR_DATA (arg) [j] = Fcopy_tree (elt, vecp);
+	}
+    }
+  return arg;
+}
+
+DEFUN ("substring", Fsubstring, 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))
+{
+  Charcount ccfr, ccto;
+  Bytecount bfr, blen;
+  Lisp_Object val;
+
+  CHECK_STRING (string);
+  CHECK_INT (from);
+  get_string_range_char (string, from, to, &ccfr, &ccto,
+			 GB_HISTORICAL_STRING_BEHAVIOR);
+  bfr = charcount_to_bytecount (XSTRING_DATA (string), ccfr);
+  blen = charcount_to_bytecount (XSTRING_DATA (string) + bfr, ccto - ccfr);
+  val = make_string (XSTRING_DATA (string) + bfr, blen);
+  /* Copy any applicable extent information into the new string: */
+  copy_string_extents (val, string, 0, bfr, blen);
+  return val;
+}
+
+DEFUN ("subseq", Fsubseq, 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
+ to the new string.
+*/
+       (seq, from, to))
+{
+  EMACS_INT len, f, t;
+
+  if (STRINGP (seq))
+    return Fsubstring (seq, from, to);
+
+  if (!LISTP (seq) && !VECTORP (seq) && !BIT_VECTORP (seq))
+    {
+      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);
+      EMACS_INT i;
+      Lisp_Object *in_elts  = XVECTOR_DATA (seq);
+      Lisp_Object *out_elts = XVECTOR_DATA (result);
+
+      for (i = f; i < t; i++)
+	out_elts[i - f] = in_elts[i];
+      return result;
+    }
+
+  if (LISTP (seq))
+    {
+      Lisp_Object result = Qnil;
+      EMACS_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);
+    EMACS_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, 2, 2, 0, /*
+Take cdr N times on LIST, and return the result.
+*/
+       (n, list))
+{
+  REGISTER size_t i;
+  REGISTER Lisp_Object tail = list;
+  CHECK_NATNUM (n);
+  for (i = XINT (n); i; i--)
+    {
+      if (CONSP (tail))
+	tail = XCDR (tail);
+      else if (NILP (tail))
+	return Qnil;
+      else
+	{
+	  tail = wrong_type_argument (Qlistp, tail);
+	  i++;
+	}
+    }
+  return tail;
+}
+
+DEFUN ("nth", Fnth, 2, 2, 0, /*
+Return the Nth element of LIST.
+N counts from zero.  If LIST is not that long, nil is returned.
+*/
+       (n, list))
+{
+  return Fcar (Fnthcdr (n, list));
+}
+
+DEFUN ("elt", Felt, 2, 2, 0, /*
+Return element of SEQUENCE at index N.
+*/
+       (sequence, n))
+{
+ retry:
+  CHECK_INT_COERCE_CHAR (n); /* yuck! */
+  if (LISTP (sequence))
+    {
+      Lisp_Object tem = Fnthcdr (n, sequence);
+      /* #### Utterly, completely, fucking disgusting.
+       * #### The whole point of "elt" is that it operates on
+       * #### sequences, and does error- (bounds-) checking.
+       */
+      if (CONSP (tem))
+	return XCAR (tem);
+      else
+#if 1
+	/* This is The Way It Has Always Been. */
+	return Qnil;
+#else
+        /* This is The Way Mly and Cltl2 say It Should Be. */
+        args_out_of_range (sequence, n);
+#endif
+    }
+  else if (STRINGP     (sequence) ||
+           VECTORP     (sequence) ||
+           BIT_VECTORP (sequence))
+    return Faref (sequence, n);
+#ifdef LOSING_BYTECODE
+  else if (COMPILED_FUNCTIONP (sequence))
+    {
+      EMACS_INT idx = XINT (n);
+      if (idx < 0)
+        {
+        lose:
+          args_out_of_range (sequence, n);
+        }
+      /* Utter perversity */
+      {
+	Lisp_Compiled_Function *f = XCOMPILED_FUNCTION (sequence);
+        switch (idx)
+          {
+          case COMPILED_ARGLIST:
+            return compiled_function_arglist (f);
+          case COMPILED_INSTRUCTIONS:
+            return compiled_function_instructions (f);
+          case COMPILED_CONSTANTS:
+            return compiled_function_constants (f);
+          case COMPILED_STACK_DEPTH:
+            return compiled_function_stack_depth (f);
+          case COMPILED_DOC_STRING:
+	    return compiled_function_documentation (f);
+          case COMPILED_DOMAIN:
+	    return compiled_function_domain (f);
+          case COMPILED_INTERACTIVE:
+	    if (f->flags.interactivep)
+	      return compiled_function_interactive (f);
+	    /* 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", sequence);
+      sequence = wrong_type_argument (Qsequencep, sequence);
+      goto retry;
+    }
+}
+
+DEFUN ("last", Flast, 1, 2, 0, /*
+Return the tail of list LIST, of length N (default 1).
+LIST may be a dotted list, but not a circular list.
+Optional argument N must be a non-negative integer.
+If N is zero, then the atom that terminates the list is returned.
+If N is greater than the length of LIST, then LIST itself is returned.
+*/
+       (list, n))
+{
+  EMACS_INT int_n, count;
+  Lisp_Object retval, tortoise, hare;
+
+  CHECK_LIST (list);
+
+  if (NILP (n))
+    int_n = 1;
+  else
+    {
+      CHECK_NATNUM (n);
+      int_n = XINT (n);
+    }
+
+  for (retval = tortoise = hare = list, count = 0;
+       CONSP (hare);
+       hare = XCDR (hare),
+	 (int_n-- <= 0 ? ((void) (retval = XCDR (retval))) : (void)0),
+	 count++)
+    {
+      if (count < CIRCULAR_LIST_SUSPICION_LENGTH) continue;
+
+      if (count & 1)
+	tortoise = XCDR (tortoise);
+      if (EQ (hare, tortoise))
+	signal_circular_list_error (list);
+    }
+
+  return retval;
+}
+
+DEFUN ("nbutlast", Fnbutlast, 1, 2, 0, /*
+Modify LIST to remove the last N (default 1) elements.
+If LIST has N or fewer elements, nil is returned and LIST is unmodified.
+*/
+       (list, n))
+{
+  EMACS_INT int_n;
+
+  CHECK_LIST (list);
+
+  if (NILP (n))
+    int_n = 1;
+  else
+    {
+      CHECK_NATNUM (n);
+      int_n = XINT (n);
+    }
+
+  {
+    Lisp_Object last_cons = list;
+
+    EXTERNAL_LIST_LOOP_1 (list)
+      {
+	if (int_n-- < 0)
+	  last_cons = XCDR (last_cons);
+      }
+
+    if (int_n >= 0)
+      return Qnil;
+
+    XCDR (last_cons) = Qnil;
+    return list;
+  }
+}
+
+DEFUN ("butlast", Fbutlast, 1, 2, 0, /*
+Return a copy of LIST with the last N (default 1) elements removed.
+If LIST has N or fewer elements, nil is returned.
+*/
+       (list, n))
+{
+  int int_n;
+
+  CHECK_LIST (list);
+
+  if (NILP (n))
+    int_n = 1;
+  else
+    {
+      CHECK_NATNUM (n);
+      int_n = XINT (n);
+    }
+
+  {
+    Lisp_Object retval = Qnil;
+    Lisp_Object tail = list;
+
+    EXTERNAL_LIST_LOOP_1 (list)
+      {
+	if (--int_n < 0)
+	  {
+	    retval = Fcons (XCAR (tail), retval);
+	    tail = XCDR (tail);
+	  }
+      }
+
+    return Fnreverse (retval);
+  }
+}
+
+DEFUN ("member", Fmember, 2, 2, 0, /*
+Return non-nil if ELT is an element of LIST.  Comparison done with `equal'.
+The value is actually the tail of LIST whose car is ELT.
+*/
+       (elt, list))
+{
+  Lisp_Object list_elt, tail;
+  EXTERNAL_LIST_LOOP_3 (list_elt, list, tail)
+    {
+      if (internal_equal (elt, list_elt, 0))
+        return tail;
+    }
+  return Qnil;
+}
+
+DEFUN ("old-member", Fold_member, 2, 2, 0, /*
+Return non-nil if ELT is an element of LIST.  Comparison done with `old-equal'.
+The value is actually the tail of LIST whose car is ELT.
+This function is provided only for byte-code compatibility with v19.
+Do not use it.
+*/
+       (elt, list))
+{
+  Lisp_Object list_elt, tail;
+  EXTERNAL_LIST_LOOP_3 (list_elt, list, tail)
+    {
+      if (internal_old_equal (elt, list_elt, 0))
+        return tail;
+    }
+  return Qnil;
+}
+
+DEFUN ("memq", Fmemq, 2, 2, 0, /*
+Return non-nil if ELT is an element of LIST.  Comparison done with `eq'.
+The value is actually the tail of LIST whose car is ELT.
+*/
+       (elt, list))
+{
+  Lisp_Object list_elt, tail;
+  EXTERNAL_LIST_LOOP_3 (list_elt, list, tail)
+    {
+      if (EQ_WITH_EBOLA_NOTICE (elt, list_elt))
+        return tail;
+    }
+  return Qnil;
+}
+
+DEFUN ("old-memq", Fold_memq, 2, 2, 0, /*
+Return non-nil if ELT is an element of LIST.  Comparison done with `old-eq'.
+The value is actually the tail of LIST whose car is ELT.
+This function is provided only for byte-code compatibility with v19.
+Do not use it.
+*/
+       (elt, list))
+{
+  Lisp_Object list_elt, tail;
+  EXTERNAL_LIST_LOOP_3 (list_elt, list, tail)
+    {
+      if (HACKEQ_UNSAFE (elt, list_elt))
+        return tail;
+    }
+  return Qnil;
+}
+
+Lisp_Object
+memq_no_quit (Lisp_Object elt, Lisp_Object list)
+{
+  Lisp_Object list_elt, tail;
+  LIST_LOOP_3 (list_elt, list, tail)
+    {
+      if (EQ_WITH_EBOLA_NOTICE (elt, list_elt))
+        return tail;
+    }
+  return Qnil;
+}
+
+DEFUN ("assoc", Fassoc, 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))
+{
+  /* This function can GC. */
+  Lisp_Object elt, elt_car, elt_cdr;
+  EXTERNAL_ALIST_LOOP_4 (elt, elt_car, elt_cdr, list)
+    {
+      if (internal_equal (key, elt_car, 0))
+	return elt;
+    }
+  return Qnil;
+}
+
+DEFUN ("old-assoc", Fold_assoc, 2, 2, 0, /*
+Return non-nil if KEY is `old-equal' to the car of an element of LIST.
+The value is actually the element of LIST whose car equals KEY.
+*/
+       (key, list))
+{
+  /* This function can GC. */
+  Lisp_Object elt, elt_car, elt_cdr;
+  EXTERNAL_ALIST_LOOP_4 (elt, elt_car, elt_cdr, list)
+    {
+      if (internal_old_equal (key, elt_car, 0))
+	return elt;
+    }
+  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, 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 elt, elt_car, elt_cdr;
+  EXTERNAL_ALIST_LOOP_4 (elt, elt_car, elt_cdr, list)
+    {
+      if (EQ_WITH_EBOLA_NOTICE (key, elt_car))
+	return elt;
+    }
+  return Qnil;
+}
+
+DEFUN ("old-assq", Fold_assq, 2, 2, 0, /*
+Return non-nil if KEY is `old-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.
+This function is provided only for byte-code compatibility with v19.
+Do not use it.
+*/
+       (key, list))
+{
+  Lisp_Object elt, elt_car, elt_cdr;
+  EXTERNAL_ALIST_LOOP_4 (elt, elt_car, elt_cdr, list)
+    {
+      if (HACKEQ_UNSAFE (key, elt_car))
+	return elt;
+    }
+  return Qnil;
+}
+
+/* Like Fassq but never report an error and do not allow quits.
+   Use only on lists known never to be circular.  */
+
+Lisp_Object
+assq_no_quit (Lisp_Object key, Lisp_Object list)
+{
+  /* This cannot GC. */
+  Lisp_Object elt;
+  LIST_LOOP_2 (elt, list)
+    {
+      Lisp_Object elt_car = XCAR (elt);
+      if (EQ_WITH_EBOLA_NOTICE (key, elt_car))
+	return elt;
+    }
+  return Qnil;
+}
+
+DEFUN ("rassoc", Frassoc, 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 elt, elt_car, elt_cdr;
+  EXTERNAL_ALIST_LOOP_4 (elt, elt_car, elt_cdr, list)
+    {
+      if (internal_equal (key, elt_cdr, 0))
+	return elt;
+    }
+  return Qnil;
+}
+
+DEFUN ("old-rassoc", Fold_rassoc, 2, 2, 0, /*
+Return non-nil if KEY is `old-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 elt, elt_car, elt_cdr;
+  EXTERNAL_ALIST_LOOP_4 (elt, elt_car, elt_cdr, list)
+    {
+      if (internal_old_equal (key, elt_cdr, 0))
+	return elt;
+    }
+  return Qnil;
+}
+
+DEFUN ("rassq", Frassq, 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 elt, elt_car, elt_cdr;
+  EXTERNAL_ALIST_LOOP_4 (elt, elt_car, elt_cdr, list)
+    {
+      if (EQ_WITH_EBOLA_NOTICE (key, elt_cdr))
+	return elt;
+    }
+  return Qnil;
+}
+
+DEFUN ("old-rassq", Fold_rassq, 2, 2, 0, /*
+Return non-nil if KEY is `old-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 elt, elt_car, elt_cdr;
+  EXTERNAL_ALIST_LOOP_4 (elt, elt_car, elt_cdr, list)
+    {
+      if (HACKEQ_UNSAFE (key, elt_cdr))
+	return elt;
+    }
+  return Qnil;
+}
+
+/* Like Frassq, but caller must ensure that LIST is properly
+   nil-terminated and ebola-free. */
+Lisp_Object
+rassq_no_quit (Lisp_Object key, Lisp_Object list)
+{
+  Lisp_Object elt;
+  LIST_LOOP_2 (elt, list)
+    {
+      Lisp_Object elt_cdr = XCDR (elt);
+      if (EQ_WITH_EBOLA_NOTICE (key, elt_cdr))
+	return elt;
+    }
+  return Qnil;
+}
+
+
+DEFUN ("delete", Fdelete, 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'.
+Also see: `remove'.
+*/
+       (elt, list))
+{
+  Lisp_Object list_elt;
+  EXTERNAL_LIST_LOOP_DELETE_IF (list_elt, list,
+				(internal_equal (elt, list_elt, 0)));
+  return list;
+}
+
+DEFUN ("old-delete", Fold_delete, 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 `old-equal'.
+If the first member of LIST is ELT, there is no way to remove it by side
+effect; therefore, write `(setq foo (old-delete element foo))' to be sure
+of changing the value of `foo'.
+*/
+       (elt, list))
+{
+  Lisp_Object list_elt;
+  EXTERNAL_LIST_LOOP_DELETE_IF (list_elt, list,
+				(internal_old_equal (elt, list_elt, 0)));
+  return list;
+}
+
+DEFUN ("delq", Fdelq, 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 list_elt;
+  EXTERNAL_LIST_LOOP_DELETE_IF (list_elt, list,
+				(EQ_WITH_EBOLA_NOTICE (elt, list_elt)));
+  return list;
+}
+
+DEFUN ("old-delq", Fold_delq, 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 `old-eq'.
+If the first member of LIST is ELT, there is no way to remove it by side
+effect; therefore, write `(setq foo (old-delq element foo))' to be sure of
+changing the value of `foo'.
+*/
+       (elt, list))
+{
+  Lisp_Object list_elt;
+  EXTERNAL_LIST_LOOP_DELETE_IF (list_elt, list,
+				(HACKEQ_UNSAFE (elt, list_elt)));
+  return list;
+}
+
+/* Like Fdelq, but caller must ensure that LIST is properly
+   nil-terminated and ebola-free. */
+
+Lisp_Object
+delq_no_quit (Lisp_Object elt, Lisp_Object list)
+{
+  Lisp_Object list_elt;
+  LIST_LOOP_DELETE_IF (list_elt, list,
+		       (EQ_WITH_EBOLA_NOTICE (elt, list_elt)));
+  return list;
+}
+
+/* Be VERY careful with this.  This is like delq_no_quit() but
+   also calls free_cons() on the removed conses.  You must be SURE
+   that no pointers to the freed conses remain around (e.g.
+   someone else is pointing to part of the list).  This function
+   is useful on internal lists that are used frequently and where
+   the actual list doesn't escape beyond known code bounds. */
+
+Lisp_Object
+delq_no_quit_and_free_cons (Lisp_Object elt, Lisp_Object list)
+{
+  REGISTER Lisp_Object tail = list;
+  REGISTER Lisp_Object prev = Qnil;
+
+  while (!NILP (tail))
+    {
+      REGISTER Lisp_Object tem = XCAR (tail);
+      if (EQ (elt, tem))
+	{
+	  Lisp_Object cons_to_free = tail;
+	  if (NILP (prev))
+	    list = XCDR (tail);
+	  else
+	    XCDR (prev) = XCDR (tail);
+	  tail = XCDR (tail);
+	  free_cons (XCONS (cons_to_free));
+	}
+      else
+	{
+	  prev = tail;
+	  tail = XCDR (tail);
+	}
+    }
+  return list;
+}
+
+DEFUN ("remassoc", Fremassoc, 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 elt;
+  EXTERNAL_LIST_LOOP_DELETE_IF (elt, list,
+				(CONSP (elt) &&
+				 internal_equal (key, XCAR (elt), 0)));
+  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, 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 elt;
+  EXTERNAL_LIST_LOOP_DELETE_IF (elt, list,
+				(CONSP (elt) &&
+				 EQ_WITH_EBOLA_NOTICE (key, XCAR (elt))));
+  return list;
+}
+
+/* no quit, no errors; be careful */
+
+Lisp_Object
+remassq_no_quit (Lisp_Object key, Lisp_Object list)
+{
+  Lisp_Object elt;
+  LIST_LOOP_DELETE_IF (elt, list,
+		       (CONSP (elt) &&
+			EQ_WITH_EBOLA_NOTICE (key, XCAR (elt))));
+  return list;
+}
+
+DEFUN ("remrassoc", Fremrassoc, 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 elt;
+  EXTERNAL_LIST_LOOP_DELETE_IF (elt, list,
+				(CONSP (elt) &&
+				 internal_equal (value, XCDR (elt), 0)));
+  return list;
+}
+
+DEFUN ("remrassq", Fremrassq, 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 elt;
+  EXTERNAL_LIST_LOOP_DELETE_IF (elt, list,
+				(CONSP (elt) &&
+				 EQ_WITH_EBOLA_NOTICE (value, XCDR (elt))));
+  return list;
+}
+
+/* Like Fremrassq, fast and unsafe; be careful */
+Lisp_Object
+remrassq_no_quit (Lisp_Object value, Lisp_Object list)
+{
+  Lisp_Object elt;
+  LIST_LOOP_DELETE_IF (elt, list,
+		       (CONSP (elt) &&
+			EQ_WITH_EBOLA_NOTICE (value, XCDR (elt))));
+  return list;
+}
+
+DEFUN ("nreverse", Fnreverse, 1, 1, 0, /*
+Reverse LIST by destructively modifying cdr pointers.
+Return the beginning of the reversed list.
+Also see: `reverse'.
+*/
+       (list))
+{
+  struct gcpro gcpro1, gcpro2;
+  REGISTER Lisp_Object prev = Qnil;
+  REGISTER Lisp_Object tail = list;
+
+  /* We gcpro our args; see `nconc' */
+  GCPRO2 (prev, tail);
+  while (!NILP (tail))
+    {
+      REGISTER Lisp_Object next;
+      CONCHECK_CONS (tail);
+      next = XCDR (tail);
+      XCDR (tail) = prev;
+      prev = tail;
+      tail = next;
+    }
+  UNGCPRO;
+  return prev;
+}
+
+DEFUN ("reverse", Freverse, 1, 1, 0, /*
+Reverse LIST, copying.  Return the beginning of the reversed list.
+See also the function `nreverse', which is used more often.
+*/
+       (list))
+{
+  Lisp_Object reversed_list = Qnil;
+  Lisp_Object elt;
+  EXTERNAL_LIST_LOOP_2 (elt, list)
+    {
+      reversed_list = Fcons (elt, reversed_list);
+    }
+  return reversed_list;
+}
+
+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))
+{
+  struct gcpro gcpro1, gcpro2, gcpro3;
+  Lisp_Object back, tem;
+  Lisp_Object front = list;
+  Lisp_Object len = Flength (list);
+  int 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, 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))
+{
+  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  = alloca_array (Lisp_Object, m);
+  vals  = alloca_array (Lisp_Object, m);
+  flags = alloca_array (char, m);
+
+  /* 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
+		   /* We narrowly escaped being Ebolified here. */
+		   ? !EQ_WITH_EBOLA_NOTICE (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, 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, but should not be used except for backward
+ compatibility.
+*/
+       (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, 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, but should not be used except for backward
+ compatibility.
+*/
+       (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, 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, but should not be used except for backward
+ compatibility.
+*/
+       (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, 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, but should not be used except for backward
+ compatibility.
+*/
+       (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;
+
+  for (tail = plist; !NILP (tail); tail = XCDR (XCDR (tail)))
+    {
+      if (EQ (XCAR (tail), property))
+	return XCAR (XCDR (tail));
+    }
+
+  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;
+
+  for (tail = *plist; !NILP (tail); tail = XCDR (XCDR (tail)))
+    {
+      if (EQ (XCAR (tail), property))
+	{
+	  XCAR (XCDR (tail)) = value;
+	  return;
+	}
+    }
+
+  *plist = Fcons (property, Fcons (value, *plist));
+}
+
+int
+internal_remprop (Lisp_Object *plist, Lisp_Object property)
+{
+  Lisp_Object tail, prev;
+
+  for (tail = *plist, prev = Qnil;
+       !NILP (tail);
+       tail = XCDR (XCDR (tail)))
+    {
+      if (EQ (XCAR (tail), property))
+	{
+	  if (NILP (prev))
+	    *plist = XCDR (XCDR (tail));
+	  else
+	    XCDR (XCDR (prev)) = XCDR (XCDR (tail));
+	  return 1;
+	}
+      else
+	prev = tail;
+    }
+
+  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, 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, default_))
+{
+  Lisp_Object val = external_plist_get (&plist, prop, 0, ERROR_ME);
+  return UNBOUNDP (val) ? default_ : val;
+}
+
+DEFUN ("plist-put", Fplist_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))
+{
+  external_plist_put (&plist, prop, val, 0, ERROR_ME);
+  return plist;
+}
+
+DEFUN ("plist-remprop", Fplist_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))
+{
+  external_remprop (&plist, prop, 0, ERROR_ME);
+  return plist;
+}
+
+DEFUN ("plist-member", Fplist_member, 2, 2, 0, /*
+Return t if PROP has a value specified in PLIST.
+*/
+       (plist, prop))
+{
+  Lisp_Object val = Fplist_get (plist, prop, Qunbound);
+  return UNBOUNDP (val) ? Qnil : Qt;
+}
+
+DEFUN ("check-valid-plist", Fcheck_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 *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, 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 *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, 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, 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 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))
+	DO_NOTHING;
+      plist = Fcdr (next);
+    }
+
+  return head;
+}
+
+DEFUN ("lax-plist-get", Flax_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 comparisons 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, default_))
+{
+  Lisp_Object val = external_plist_get (&lax_plist, prop, 1, ERROR_ME);
+  if (UNBOUNDP (val))
+    return default_;
+  return val;
+}
+
+DEFUN ("lax-plist-put", Flax_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 comparisons 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))
+{
+  external_plist_put (&lax_plist, prop, val, 1, ERROR_ME);
+  return lax_plist;
+}
+
+DEFUN ("lax-plist-remprop", Flax_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 comparisons 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))
+{
+  external_remprop (&lax_plist, prop, 1, ERROR_ME);
+  return lax_plist;
+}
+
+DEFUN ("lax-plist-member", Flax_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 comparisons between properties is done
+using `equal' instead of `eq'.
+*/
+       (lax_plist, prop))
+{
+  return UNBOUNDP (Flax_plist_get (lax_plist, prop, Qunbound)) ? Qnil : Qt;
+}
+
+DEFUN ("canonicalize-lax-plist", Fcanonicalize_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, 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 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))
+	DO_NOTHING;
+      lax_plist = Fcdr (next);
+    }
+
+  return head;
+}
+
+/* In C because the frame props stuff uses it */
+
+DEFUN ("destructive-alist-to-plist", Fdestructive_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 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 default_)
+{
+  Lisp_Object val = external_plist_get (&XSYMBOL (sym)->plist, propname,
+					0, ERROR_ME);
+  return UNBOUNDP (val) ? default_ : 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;
+}
+
+static Lisp_Object
+string_getprop (struct Lisp_String *s, Lisp_Object property,
+		Lisp_Object default_)
+{
+  Lisp_Object val = external_plist_get (string_plist_ptr (s), property, 0,
+					ERROR_ME);
+  return UNBOUNDP (val) ? default_ : val;
+}
+
+static 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, 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, default_))
+{
+  /* 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))
+    return symbol_getprop (object, propname, default_);
+  else if (STRINGP (object))
+    return string_getprop (XSTRING (object), propname, default_);
+  else if (LRECORDP (object))
+    {
+      CONST struct lrecord_implementation *imp
+	= XRECORD_LHEADER_IMPLEMENTATION (object);
+      if (!imp->getprop)
+	goto noprops;
+
+      {
+	Lisp_Object val = (imp->getprop) (object, propname);
+	if (UNBOUNDP (val))
+	  val = default_;
+	return val;
+      }
+    }
+  else
+    {
+    noprops:
+      signal_simple_error ("Object type has no properties", object);
+      return Qnil;		/* Not reached */
+    }
+}
+
+DEFUN ("put", Fput, 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))
+{
+  CHECK_SYMBOL (propname);
+  CHECK_LISP_WRITEABLE (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_IMPLEMENTATION (object);
+      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;
+}
+
+DEFUN ("remprop", Fremprop, 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))
+{
+  int retval = 0;
+
+  CHECK_SYMBOL (propname);
+  CHECK_LISP_WRITEABLE (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_IMPLEMENTATION (object);
+      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, 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))
+{
+  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_IMPLEMENTATION (object);
+      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 obj1, Lisp_Object obj2, int depth)
+{
+  if (depth > 200)
+    error ("Stack overflow in equal");
+  QUIT;
+  if (EQ_WITH_EBOLA_NOTICE (obj1, obj2))
+    return 1;
+  /* Note that (equal 20 20.0) should be nil */
+  if (XTYPE (obj1) != XTYPE (obj2))
+    return 0;
+  if (LRECORDP (obj1))
+    {
+      CONST struct lrecord_implementation
+	*imp1 = XRECORD_LHEADER_IMPLEMENTATION (obj1),
+	*imp2 = XRECORD_LHEADER_IMPLEMENTATION (obj2);
+
+      return (imp1 == imp2) &&
+	/* EQ-ness of the objects was noticed above */
+	(imp1->equal && (imp1->equal) (obj1, obj2, depth));
+    }
+
+  return 0;
+}
+
+/* Note that we may be calling sub-objects that will use
+   internal_equal() (instead of internal_old_equal()).  Oh well.
+   We will get an Ebola note if there's any possibility of confusion,
+   but that seems unlikely. */
+
+static int
+internal_old_equal (Lisp_Object obj1, Lisp_Object obj2, int depth)
+{
+  if (depth > 200)
+    error ("Stack overflow in equal");
+  QUIT;
+  if (HACKEQ_UNSAFE (obj1, obj2))
+    return 1;
+  /* Note that (equal 20 20.0) should be nil */
+  if (XTYPE (obj1) != XTYPE (obj2))
+    return 0;
+
+  return internal_equal (obj1, obj2, depth);
+}
+
+DEFUN ("equal", Fequal, 2, 2, 0, /*
+Return 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.
+*/
+       (obj1, obj2))
+{
+  return internal_equal (obj1, obj2, 0) ? Qt : Qnil;
+}
+
+DEFUN ("old-equal", Fold_equal, 2, 2, 0, /*
+Return t if two Lisp objects have similar structure and contents.
+They must have the same data type.
+\(Note, however, that an exception is made for characters and integers;
+this is known as the "char-int confoundance disease." See `eq' and
+`old-eq'.)
+This function is provided only for byte-code compatibility with v19.
+Do not use it.
+*/
+       (obj1, obj2))
+{
+  return internal_old_equal (obj1, obj2, 0) ? Qt : Qnil;
+}
+
+
+DEFUN ("fillarray", Ffillarray, 2, 2, 0, /*
+Store each element of ARRAY with ITEM.
+ARRAY is a vector, bit vector, or string.
+*/
+       (array, item))
+{
+ retry:
+  if (STRINGP (array))
+    {
+      Emchar charval;
+      struct Lisp_String *s = XSTRING (array);
+      Charcount len = string_char_length (s);
+      Charcount i;
+      CHECK_CHAR_COERCE_INT (item);
+      CHECK_LISP_WRITEABLE (array);
+      charval = XCHAR (item);
+      for (i = 0; i < len; i++)
+	set_string_char (s, i, charval);
+      bump_string_modiff (array);
+    }
+  else if (VECTORP (array))
+    {
+      Lisp_Object *p = XVECTOR_DATA (array);
+      int len = XVECTOR_LENGTH (array);
+      CHECK_LISP_WRITEABLE (array);
+      while (len--)
+	*p++ = item;
+    }
+  else if (BIT_VECTORP (array))
+    {
+      struct Lisp_Bit_Vector *v = XBIT_VECTOR (array);
+      int len = bit_vector_length (v);
+      int bit;
+      CHECK_BIT (item);
+      CHECK_LISP_WRITEABLE (array);
+      bit = XINT (item);
+      while (len--)
+	set_bit_vector_bit (v, len, bit);
+    }
+  else
+    {
+      array = wrong_type_argument (Qarrayp, array);
+      goto retry;
+    }
+  return array;
+}
+
+Lisp_Object
+nconc2 (Lisp_Object arg1, Lisp_Object arg2)
+{
+  Lisp_Object args[2];
+  struct gcpro gcpro1;
+  args[0] = arg1;
+  args[1] = arg2;
+
+  GCPRO1 (args[0]);
+  gcpro1.nvars = 2;
+
+  RETURN_UNGCPRO (bytecode_nconc2 (args));
+}
+
+Lisp_Object
+bytecode_nconc2 (Lisp_Object *args)
+{
+ retry:
+
+  if (CONSP (args[0]))
+    {
+      /* (setcdr (last args[0]) args[1]) */
+      Lisp_Object tortoise, hare;
+      int count;
+
+      for (hare = tortoise = args[0], count = 0;
+	   CONSP (XCDR (hare));
+	   hare = XCDR (hare), count++)
+	{
+	  if (count < CIRCULAR_LIST_SUSPICION_LENGTH) continue;
+
+	  if (count & 1)
+	    tortoise = XCDR (tortoise);
+	  if (EQ (hare, tortoise))
+	    signal_circular_list_error (args[0]);
+	}
+      XCDR (hare) = args[1];
+      return args[0];
+    }
+  else if (NILP (args[0]))
+    {
+      return args[1];
+    }
+  else
+    {
+      args[0] = wrong_type_argument (args[0], Qlistp);
+      goto retry;
+    }
+}
+
+DEFUN ("nconc", Fnconc, 0, MANY, 0, /*
+Concatenate any number of lists by altering them.
+Only the last argument is not altered, and need not be a list.
+Also see: `append'.
+If the first argument is nil, there is no way to modify it by side
+effect; therefore, write `(setq foo (nconc foo list))' to be sure of
+changing the value of `foo'.
+*/
+       (int nargs, Lisp_Object *args))
+{
+  int argnum = 0;
+  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;
+
+  while (argnum < nargs)
+    {
+      Lisp_Object val;
+    retry:
+      val = args[argnum];
+      if (CONSP (val))
+	{
+	  /* `val' is the first cons, which will be our return value.  */
+	  /* `last_cons' will be the cons cell to mutate.  */
+	  Lisp_Object last_cons = val;
+	  Lisp_Object tortoise = val;
+
+	  for (argnum++; argnum < nargs; argnum++)
+	    {
+	      Lisp_Object next = args[argnum];
+	    retry_next:
+	      if (CONSP (next) || argnum == nargs -1)
+		{
+		  /* (setcdr (last val) next) */
+		  int count;
+
+		  for (count = 0;
+		       CONSP (XCDR (last_cons));
+		       last_cons = XCDR (last_cons), count++)
+		    {
+		      if (count < CIRCULAR_LIST_SUSPICION_LENGTH) continue;
+
+		      if (count & 1)
+			tortoise = XCDR (tortoise);
+		      if (EQ (last_cons, tortoise))
+			signal_circular_list_error (args[argnum-1]);
+		    }
+		  XCDR (last_cons) = next;
+		}
+	      else if (NILP (next))
+		{
+		  continue;
+		}
+	      else
+		{
+		  next = wrong_type_argument (Qlistp, next);
+		  goto retry_next;
+		}
+	    }
+	  RETURN_UNGCPRO (val);
+        }
+      else if (NILP (val))
+	argnum++;
+      else if (argnum == nargs - 1) /* last arg? */
+	RETURN_UNGCPRO (val);
+      else
+	{
+	  args[argnum] = wrong_type_argument (Qlistp, val);
+	  goto retry;
+	}
+    }
+  RETURN_UNGCPRO (Qnil);  /* No non-nil args provided. */
+}
+
+
+/* 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 (size_t leni, Lisp_Object *vals, Lisp_Object fn, Lisp_Object seq)
+{
+  Lisp_Object result;
+  Lisp_Object args[2];
+  int i;
+  struct gcpro gcpro1;
+
+  if (vals)
+    {
+      GCPRO1 (vals[0]);
+      gcpro1.nvars = 0;
+    }
+
+  args[0] = fn;
+
+  if (LISTP (seq))
+    {
+      for (i = 0; i < leni; i++)
+	{
+	  args[1] = XCAR (seq);
+	  seq = XCDR (seq);
+	  result = Ffuncall (2, args);
+	  if (vals) vals[gcpro1.nvars++] = result;
+	}
+    }
+  else if (VECTORP (seq))
+    {
+      Lisp_Object *objs = XVECTOR_DATA (seq);
+      for (i = 0; i < leni; i++)
+	{
+	  args[1] = *objs++;
+	  result = Ffuncall (2, args);
+	  if (vals) vals[gcpro1.nvars++] = result;
+	}
+    }
+  else if (STRINGP (seq))
+    {
+      Bufbyte *p = XSTRING_DATA (seq);
+      for (i = 0; i < leni; i++)
+	{
+	  args[1] = make_char (charptr_emchar (p));
+	  INC_CHARPTR (p);
+	  result = Ffuncall (2, args);
+	  if (vals) vals[gcpro1.nvars++] = result;
+	}
+    }
+  else if (BIT_VECTORP (seq))
+    {
+      struct Lisp_Bit_Vector *v = XBIT_VECTOR (seq);
+      for (i = 0; i < leni; i++)
+	{
+	  args[1] = make_int (bit_vector_bit (v, i));
+	  result = Ffuncall (2, args);
+	  if (vals) vals[gcpro1.nvars++] = result;
+	}
+    }
+  else
+    abort(); /* cannot get here since Flength(seq) did not get an error */
+
+  if (vals)
+    UNGCPRO;
+}
+
+DEFUN ("mapconcat", Fmapconcat, 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))
+{
+  size_t len = XINT (Flength (seq));
+  Lisp_Object *args;
+  int i;
+  struct gcpro gcpro1;
+  int nargs = len + len - 1;
+
+  if (nargs < 0) return build_string ("");
+
+  args = alloca_array (Lisp_Object, nargs);
+
+  GCPRO1 (sep);
+  mapcar1 (len, args, fn, seq);
+  UNGCPRO;
+
+  for (i = len - 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, 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))
+{
+  size_t len = XINT (Flength (seq));
+  Lisp_Object *args = alloca_array (Lisp_Object, len);
+
+  mapcar1 (len, args, fn, seq);
+
+  return Flist (len, args);
+}
+
+DEFUN ("mapvector", Fmapvector, 2, 2, 0, /*
+Apply FUNCTION to each element of SEQUENCE, making a vector of the results.
+The result is a vector of the same length as SEQUENCE.
+SEQUENCE may be a list, a vector or a string.
+*/
+       (fn, seq))
+{
+  size_t len = XINT (Flength (seq));
+  Lisp_Object result = make_vector (len, Qnil);
+  struct gcpro gcpro1;
+
+  GCPRO1 (result);
+  mapcar1 (len, XVECTOR_DATA (result), fn, seq);
+  UNGCPRO;
+
+  return result;
+}
+
+DEFUN ("mapc-internal", Fmapc_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.
+
+The difference between this and `mapc' is that `mapc' supports all
+the spiffy Common Lisp arguments.  You should normally use `mapc'.
+*/
+       (fn, seq))
+{
+  mapcar1 (XINT (Flength (seq)), 0, fn, seq);
+
+  return seq;
+}
+
+
+/* #### this function doesn't belong in this file! */
+
+DEFUN ("load-average", Fload_average, 0, 1, 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.
+
+When USE-FLOATS is non-nil, floats will be used instead of integers.
+These floats are not multiplied by 100.
+
+If the 5-minute or 15-minute load averages are not available, return a
+shortened list, containing only those averages which are available.
+
+On some systems, this won't work due to permissions on /dev/kmem,
+in which case you can't use this.
+*/
+       (use_floats))
+{
+  double load_ave[3];
+  int loads = getloadavg (load_ave, countof (load_ave));
+  Lisp_Object ret = Qnil;
+
+  if (loads == -2)
+    error ("load-average not implemented for this operating system");
+  else if (loads < 0)
+    signal_simple_error ("Could not get load-average",
+			 lisp_strerror (errno));
+
+  while (loads-- > 0)
+    {
+      Lisp_Object load = (NILP (use_floats) ?
+			  make_int ((int) (100.0 * load_ave[loads]))
+			  : make_float (load_ave[loads]));
+      ret = Fcons (load, ret);
+    }
+  return ret;
+}
+
+
+Lisp_Object Vfeatures;
+
+DEFUN ("featurep", Ffeaturep, 1, 1, 0, /*
+Return non-nil if feature FEXP is present in this Emacs.
+Use this to conditionalize execution of lisp code based on the
+ presence or absence of emacs or environment extensions.
+FEXP can be a symbol, a number, or a list.
+If it is a symbol, that symbol is looked up in the `features' variable,
+ and non-nil will be returned if found.
+If it is a number, the function will return non-nil if this Emacs
+ has an equal or greater version number than FEXP.
+If it is a list whose car is the symbol `and', it will return
+ non-nil if all the features in its cdr are non-nil.
+If it is a list whose car is the symbol `or', it will return non-nil
+ if any of the features in its cdr are non-nil.
+If it is a list whose car is the symbol `not', it will return
+ non-nil if the feature is not present.
+
+Examples:
+
+  (featurep 'xemacs)
+    => ; Non-nil on XEmacs.
+
+  (featurep '(and xemacs gnus))
+    => ; Non-nil on XEmacs with Gnus loaded.
+
+  (featurep '(or tty-frames (and emacs 19.30)))
+    => ; Non-nil if this Emacs supports TTY frames.
+
+  (featurep '(or (and xemacs 19.15) (and emacs 19.34)))
+    => ; Non-nil on XEmacs 19.15 and later, or FSF Emacs 19.34 and later.
+
+NOTE: The advanced arguments of this function (anything other than a
+symbol) are not yet supported by FSF Emacs.  If you feel they are useful
+for supporting multiple Emacs variants, lobby Richard Stallman at
+<bug-gnu-emacs@prep.ai.mit.edu>.
+*/
+       (fexp))
+{
+#ifndef FEATUREP_SYNTAX
+  CHECK_SYMBOL (fexp);
+  return NILP (Fmemq (fexp, Vfeatures)) ? Qnil : Qt;
+#else  /* FEATUREP_SYNTAX */
+  static double featurep_emacs_version;
+
+  /* Brute force translation from Erik Naggum's lisp function. */
+  if (SYMBOLP (fexp))
+    {
+      /* Original definition */
+      return NILP (Fmemq (fexp, Vfeatures)) ? Qnil : Qt;
+    }
+  else if (INTP (fexp) || FLOATP (fexp))
+    {
+      double d = extract_float (fexp);
+
+      if (featurep_emacs_version == 0.0)
+	{
+	  featurep_emacs_version = XINT (Vemacs_major_version) +
+	    (XINT (Vemacs_minor_version) / 100.0);
+	}
+      return featurep_emacs_version >= d ? Qt : Qnil;
+    }
+  else if (CONSP (fexp))
+    {
+      Lisp_Object tem = XCAR (fexp);
+      if (EQ (tem, Qnot))
+	{
+	  Lisp_Object negate;
+
+	  tem = XCDR (fexp);
+	  negate = Fcar (tem);
+	  if (!NILP (tem))
+	    return NILP (call1 (Qfeaturep, negate)) ? Qt : Qnil;
+	  else
+	    return Fsignal (Qinvalid_read_syntax, list1 (tem));
+	}
+      else if (EQ (tem, Qand))
+	{
+	  tem = XCDR (fexp);
+	  /* Use Fcar/Fcdr for error-checking. */
+	  while (!NILP (tem) && !NILP (call1 (Qfeaturep, Fcar (tem))))
+	    {
+	      tem = Fcdr (tem);
+	    }
+	  return NILP (tem) ? Qt : Qnil;
+	}
+      else if (EQ (tem, Qor))
+	{
+	  tem = XCDR (fexp);
+	  /* Use Fcar/Fcdr for error-checking. */
+	  while (!NILP (tem) && NILP (call1 (Qfeaturep, Fcar (tem))))
+	    {
+	      tem = Fcdr (tem);
+	    }
+	  return NILP (tem) ? Qnil : Qt;
+	}
+      else
+	{
+	  return Fsignal (Qinvalid_read_syntax, list1 (XCDR (fexp)));
+	}
+    }
+  else
+    {
+      return Fsignal (Qinvalid_read_syntax, list1 (fexp));
+    }
+}
+#endif /* FEATUREP_SYNTAX */
+
+DEFUN ("provide", Fprovide, 1, 1, 0, /*
+Announce that FEATURE is a feature of the current Emacs.
+This function updates the value of the variable `features'.
+*/
+       (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, 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 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);
+    }
+}
+
+/* base64 encode/decode functions.
+
+   Originally based on code from GNU recode.  Ported to FSF Emacs by
+   Lars Magne Ingebrigtsen and Karl Heuer.  Ported to XEmacs and
+   subsequently heavily hacked by Hrvoje Niksic.  */
+
+#define MIME_LINE_LENGTH 72
+
+#define IS_ASCII(Character) \
+  ((Character) < 128)
+#define IS_BASE64(Character) \
+  (IS_ASCII (Character) && base64_char_to_value[Character] >= 0)
+
+/* Table of characters coding the 64 values.  */
+static char base64_value_to_char[64] =
+{
+  'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',	/*  0- 9 */
+  'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',	/* 10-19 */
+  'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',	/* 20-29 */
+  'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',	/* 30-39 */
+  'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',	/* 40-49 */
+  'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',	/* 50-59 */
+  '8', '9', '+', '/'					/* 60-63 */
+};
+
+/* Table of base64 values for first 128 characters.  */
+static short base64_char_to_value[128] =
+{
+  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,	/*   0-  9 */
+  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,	/*  10- 19 */
+  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,	/*  20- 29 */
+  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,	/*  30- 39 */
+  -1,  -1,  -1,  62,  -1,  -1,  -1,  63,  52,  53,	/*  40- 49 */
+  54,  55,  56,  57,  58,  59,  60,  61,  -1,  -1,	/*  50- 59 */
+  -1,  -1,  -1,  -1,  -1,  0,   1,   2,   3,   4,	/*  60- 69 */
+  5,   6,   7,   8,   9,   10,  11,  12,  13,  14,	/*  70- 79 */
+  15,  16,  17,  18,  19,  20,  21,  22,  23,  24,	/*  80- 89 */
+  25,  -1,  -1,  -1,  -1,  -1,  -1,  26,  27,  28,	/*  90- 99 */
+  29,  30,  31,  32,  33,  34,  35,  36,  37,  38,	/* 100-109 */
+  39,  40,  41,  42,  43,  44,  45,  46,  47,  48,	/* 110-119 */
+  49,  50,  51,  -1,  -1,  -1,  -1,  -1			/* 120-127 */
+};
+
+/* The following diagram shows the logical steps by which three octets
+   get transformed into four base64 characters.
+
+		 .--------.  .--------.  .--------.
+		 |aaaaaabb|  |bbbbcccc|  |ccdddddd|
+		 `--------'  `--------'  `--------'
+                    6   2      4   4       2   6
+	       .--------+--------+--------+--------.
+	       |00aaaaaa|00bbbbbb|00cccccc|00dddddd|
+	       `--------+--------+--------+--------'
+
+	       .--------+--------+--------+--------.
+	       |AAAAAAAA|BBBBBBBB|CCCCCCCC|DDDDDDDD|
+	       `--------+--------+--------+--------'
+
+   The octets are divided into 6 bit chunks, which are then encoded into
+   base64 characters.  */
+
+#define ADVANCE_INPUT(c, stream)				\
+ ((ec = Lstream_get_emchar (stream)) == -1 ? 0 :		\
+  ((ec > 255) ?							\
+   (signal_simple_error ("Non-ascii character in base64 input",	\
+			 make_char (ec)), 0)			\
+   : (c = (Bufbyte)ec), 1))
+
+static Bytind
+base64_encode_1 (Lstream *istream, Bufbyte *to, int line_break)
+{
+  EMACS_INT counter = 0;
+  Bufbyte *e = to;
+  Emchar ec;
+  unsigned int value;
+
+  while (1)
+    {
+      Bufbyte c;
+      if (!ADVANCE_INPUT (c, istream))
+	break;
+
+      /* Wrap line every 76 characters.  */
+      if (line_break)
+	{
+	  if (counter < MIME_LINE_LENGTH / 4)
+	    counter++;
+	  else
+	    {
+	      *e++ = '\n';
+	      counter = 1;
+	    }
+	}
+
+      /* Process first byte of a triplet.  */
+      *e++ = base64_value_to_char[0x3f & c >> 2];
+      value = (0x03 & c) << 4;
+
+      /* Process second byte of a triplet.  */
+      if (!ADVANCE_INPUT (c, istream))
+	{
+	  *e++ = base64_value_to_char[value];
+	  *e++ = '=';
+	  *e++ = '=';
+	  break;
+	}
+
+      *e++ = base64_value_to_char[value | (0x0f & c >> 4)];
+      value = (0x0f & c) << 2;
+
+      /* Process third byte of a triplet.  */
+      if (!ADVANCE_INPUT (c, istream))
+	{
+	  *e++ = base64_value_to_char[value];
+	  *e++ = '=';
+	  break;
+	}
+
+      *e++ = base64_value_to_char[value | (0x03 & c >> 6)];
+      *e++ = base64_value_to_char[0x3f & c];
+    }
+
+  return e - to;
+}
+#undef ADVANCE_INPUT
+
+/* Get next character from the stream, except that non-base64
+   characters are ignored.  This is in accordance with rfc2045.  EC
+   should be an Emchar, so that it can hold -1 as the value for EOF.  */
+#define ADVANCE_INPUT_IGNORE_NONBASE64(ec, stream, streampos) do {	\
+  ec = Lstream_get_emchar (stream);					\
+  ++streampos;								\
+  /* IS_BASE64 may not be called with negative arguments so check for	\
+     EOF first. */							\
+  if (ec < 0 || IS_BASE64 (ec) || ec == '=')				\
+    break;								\
+} while (1)
+
+#define STORE_BYTE(pos, val, ccnt) do {					\
+  pos += set_charptr_emchar (pos, (Emchar)((unsigned char)(val)));	\
+  ++ccnt;								\
+} while (0)
+
+static Bytind
+base64_decode_1 (Lstream *istream, Bufbyte *to, Charcount *ccptr)
+{
+  Charcount ccnt = 0;
+  Bufbyte *e = to;
+  EMACS_INT streampos = 0;
+
+  while (1)
+    {
+      Emchar ec;
+      unsigned long value;
+
+      /* Process first byte of a quadruplet.  */
+      ADVANCE_INPUT_IGNORE_NONBASE64 (ec, istream, streampos);
+      if (ec < 0)
+	break;
+      if (ec == '=')
+	signal_simple_error ("Illegal `=' character while decoding base64",
+			     make_int (streampos));
+      value = base64_char_to_value[ec] << 18;
+
+      /* Process second byte of a quadruplet.  */
+      ADVANCE_INPUT_IGNORE_NONBASE64 (ec, istream, streampos);
+      if (ec < 0)
+	error ("Premature EOF while decoding base64");
+      if (ec == '=')
+	signal_simple_error ("Illegal `=' character while decoding base64",
+			     make_int (streampos));
+      value |= base64_char_to_value[ec] << 12;
+      STORE_BYTE (e, value >> 16, ccnt);
+
+      /* Process third byte of a quadruplet.  */
+      ADVANCE_INPUT_IGNORE_NONBASE64 (ec, istream, streampos);
+      if (ec < 0)
+	error ("Premature EOF while decoding base64");
+
+      if (ec == '=')
+	{
+	  ADVANCE_INPUT_IGNORE_NONBASE64 (ec, istream, streampos);
+	  if (ec < 0)
+	    error ("Premature EOF while decoding base64");
+	  if (ec != '=')
+	    signal_simple_error ("Padding `=' expected but not found while decoding base64",
+				 make_int (streampos));
+	  continue;
+	}
+
+      value |= base64_char_to_value[ec] << 6;
+      STORE_BYTE (e, 0xff & value >> 8, ccnt);
+
+      /* Process fourth byte of a quadruplet.  */
+      ADVANCE_INPUT_IGNORE_NONBASE64 (ec, istream, streampos);
+      if (ec < 0)
+	error ("Premature EOF while decoding base64");
+      if (ec == '=')
+	continue;
+
+      value |= base64_char_to_value[ec];
+      STORE_BYTE (e, 0xff & value, ccnt);
+    }
+
+  *ccptr = ccnt;
+  return e - to;
+}
+#undef ADVANCE_INPUT
+#undef ADVANCE_INPUT_IGNORE_NONBASE64
+#undef STORE_BYTE
+
+static Lisp_Object
+free_malloced_ptr (Lisp_Object unwind_obj)
+{
+  void *ptr = (void *)get_opaque_ptr (unwind_obj);
+  xfree (ptr);
+  free_opaque_ptr (unwind_obj);
+  return Qnil;
+}
+
+/* Don't use alloca for regions larger than this, lest we overflow
+   the stack.  */
+#define MAX_ALLOCA 65536
+
+/* We need to setup proper unwinding, because there is a number of
+   ways these functions can blow up, and we don't want to have memory
+   leaks in those cases.  */
+#define XMALLOC_OR_ALLOCA(ptr, len, type) do {			\
+  size_t XOA_len = (len);					\
+  if (XOA_len > MAX_ALLOCA)					\
+    {								\
+      ptr = xnew_array (type, XOA_len);				\
+      record_unwind_protect (free_malloced_ptr,			\
+			     make_opaque_ptr ((void *)ptr));	\
+    }								\
+  else								\
+    ptr = alloca_array (type, XOA_len);				\
+} while (0)
+
+#define XMALLOC_UNBIND(ptr, len, speccount) do {		\
+  if ((len) > MAX_ALLOCA)					\
+    unbind_to (speccount, Qnil);				\
+} while (0)
+
+DEFUN ("base64-encode-region", Fbase64_encode_region, 2, 3, "r", /*
+Base64-encode the region between BEG and END.
+Return the length of the encoded text.
+Optional third argument NO-LINE-BREAK means do not break long lines
+into shorter lines.
+*/
+       (beg, end, no_line_break))
+{
+  Bufbyte *encoded;
+  Bytind encoded_length;
+  Charcount allength, length;
+  struct buffer *buf = current_buffer;
+  Bufpos begv, zv, old_pt = BUF_PT (buf);
+  Lisp_Object input;
+  int speccount = specpdl_depth();
+
+  get_buffer_range_char (buf, beg, end, &begv, &zv, 0);
+  barf_if_buffer_read_only (buf, begv, zv);
+
+  /* We need to allocate enough room for encoding the text.
+     We need 33 1/3% more space, plus a newline every 76
+     characters, and then we round up. */
+  length = zv - begv;
+  allength = length + length/3 + 1;
+  allength += allength / MIME_LINE_LENGTH + 1 + 6;
+
+  input = make_lisp_buffer_input_stream (buf, begv, zv, 0);
+  /* We needn't multiply allength with MAX_EMCHAR_LEN because all the
+     base64 characters will be single-byte.  */
+  XMALLOC_OR_ALLOCA (encoded, allength, Bufbyte);
+  encoded_length = base64_encode_1 (XLSTREAM (input), encoded,
+				    NILP (no_line_break));
+  if (encoded_length > allength)
+    abort ();
+  Lstream_delete (XLSTREAM (input));
+
+  /* Now we have encoded the region, so we insert the new contents
+     and delete the old.  (Insert first in order to preserve markers.)  */
+  buffer_insert_raw_string_1 (buf, begv, encoded, encoded_length, 0);
+  XMALLOC_UNBIND (encoded, allength, speccount);
+  buffer_delete_range (buf, begv + encoded_length, zv + encoded_length, 0);
+
+  /* Simulate FSF Emacs implementation of this function: if point was
+     in the region, place it at the beginning.  */
+  if (old_pt >= begv && old_pt < zv)
+    BUF_SET_PT (buf, begv);
+
+  /* We return the length of the encoded text. */
+  return make_int (encoded_length);
+}
+
+DEFUN ("base64-encode-string", Fbase64_encode_string, 1, 2, 0, /*
+Base64 encode STRING and return the result.
+*/
+       (string, no_line_break))
+{
+  Charcount allength, length;
+  Bytind encoded_length;
+  Bufbyte *encoded;
+  Lisp_Object input, result;
+  int speccount = specpdl_depth();
+
+  CHECK_STRING (string);
+
+  length = XSTRING_CHAR_LENGTH (string);
+  allength = length + length/3 + 1;
+  allength += allength / MIME_LINE_LENGTH + 1 + 6;
+
+  input = make_lisp_string_input_stream (string, 0, -1);
+  XMALLOC_OR_ALLOCA (encoded, allength, Bufbyte);
+  encoded_length = base64_encode_1 (XLSTREAM (input), encoded,
+				    NILP (no_line_break));
+  if (encoded_length > allength)
+    abort ();
+  Lstream_delete (XLSTREAM (input));
+  result = make_string (encoded, encoded_length);
+  XMALLOC_UNBIND (encoded, allength, speccount);
+  return result;
+}
+
+DEFUN ("base64-decode-region", Fbase64_decode_region, 2, 2, "r", /*
+Base64-decode the region between BEG and END.
+Return the length of the decoded text.
+If the region can't be decoded, return nil and don't modify the buffer.
+Characters out of the base64 alphabet are ignored.
+*/
+       (beg, end))
+{
+  struct buffer *buf = current_buffer;
+  Bufpos begv, zv, old_pt = BUF_PT (buf);
+  Bufbyte *decoded;
+  Bytind decoded_length;
+  Charcount length, cc_decoded_length;
+  Lisp_Object input;
+  int speccount = specpdl_depth();
+
+  get_buffer_range_char (buf, beg, end, &begv, &zv, 0);
+  barf_if_buffer_read_only (buf, begv, zv);
+
+  length = zv - begv;
+
+  input = make_lisp_buffer_input_stream (buf, begv, zv, 0);
+  /* We need to allocate enough room for decoding the text. */
+  XMALLOC_OR_ALLOCA (decoded, length * MAX_EMCHAR_LEN, Bufbyte);
+  decoded_length = base64_decode_1 (XLSTREAM (input), decoded, &cc_decoded_length);
+  if (decoded_length > length * MAX_EMCHAR_LEN)
+    abort ();
+  Lstream_delete (XLSTREAM (input));
+
+  /* Now we have decoded the region, so we insert the new contents
+     and delete the old.  (Insert first in order to preserve markers.)  */
+  BUF_SET_PT (buf, begv);
+  buffer_insert_raw_string_1 (buf, begv, decoded, decoded_length, 0);
+  XMALLOC_UNBIND (decoded, length * MAX_EMCHAR_LEN, speccount);
+  buffer_delete_range (buf, begv + cc_decoded_length,
+		       zv + cc_decoded_length, 0);
+
+  /* Simulate FSF Emacs implementation of this function: if point was
+     in the region, place it at the beginning.  */
+  if (old_pt >= begv && old_pt < zv)
+    BUF_SET_PT (buf, begv);
+
+  return make_int (cc_decoded_length);
+}
+
+DEFUN ("base64-decode-string", Fbase64_decode_string, 1, 1, 0, /*
+Base64-decode STRING and return the result.
+Characters out of the base64 alphabet are ignored.
+*/
+       (string))
+{
+  Bufbyte *decoded;
+  Bytind decoded_length;
+  Charcount length, cc_decoded_length;
+  Lisp_Object input, result;
+  int speccount = specpdl_depth();
+
+  CHECK_STRING (string);
+
+  length = XSTRING_CHAR_LENGTH (string);
+  /* We need to allocate enough room for decoding the text. */
+  XMALLOC_OR_ALLOCA (decoded, length * MAX_EMCHAR_LEN, Bufbyte);
+
+  input = make_lisp_string_input_stream (string, 0, -1);
+  decoded_length = base64_decode_1 (XLSTREAM (input), decoded,
+				    &cc_decoded_length);
+  if (decoded_length > length * MAX_EMCHAR_LEN)
+    abort ();
+  Lstream_delete (XLSTREAM (input));
+
+  result = make_string (decoded, decoded_length);
+  XMALLOC_UNBIND (decoded, length * MAX_EMCHAR_LEN, speccount);
+  return result;
+}
+
+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 (Fidentity);
+  DEFSUBR (Frandom);
+  DEFSUBR (Flength);
+  DEFSUBR (Fsafe_length);
+  DEFSUBR (Fstring_equal);
+  DEFSUBR (Fstring_lessp);
+  DEFSUBR (Fstring_modified_tick);
+  DEFSUBR (Fappend);
+  DEFSUBR (Fconcat);
+  DEFSUBR (Fvconcat);
+  DEFSUBR (Fbvconcat);
+  DEFSUBR (Fcopy_list);
+  DEFSUBR (Fcopy_sequence);
+  DEFSUBR (Fcopy_alist);
+  DEFSUBR (Fcopy_tree);
+  DEFSUBR (Fsubstring);
+  DEFSUBR (Fsubseq);
+  DEFSUBR (Fnthcdr);
+  DEFSUBR (Fnth);
+  DEFSUBR (Felt);
+  DEFSUBR (Flast);
+  DEFSUBR (Fbutlast);
+  DEFSUBR (Fnbutlast);
+  DEFSUBR (Fmember);
+  DEFSUBR (Fold_member);
+  DEFSUBR (Fmemq);
+  DEFSUBR (Fold_memq);
+  DEFSUBR (Fassoc);
+  DEFSUBR (Fold_assoc);
+  DEFSUBR (Fassq);
+  DEFSUBR (Fold_assq);
+  DEFSUBR (Frassoc);
+  DEFSUBR (Fold_rassoc);
+  DEFSUBR (Frassq);
+  DEFSUBR (Fold_rassq);
+  DEFSUBR (Fdelete);
+  DEFSUBR (Fold_delete);
+  DEFSUBR (Fdelq);
+  DEFSUBR (Fold_delq);
+  DEFSUBR (Fremassoc);
+  DEFSUBR (Fremassq);
+  DEFSUBR (Fremrassoc);
+  DEFSUBR (Fremrassq);
+  DEFSUBR (Fnreverse);
+  DEFSUBR (Freverse);
+  DEFSUBR (Fsort);
+  DEFSUBR (Fplists_eq);
+  DEFSUBR (Fplists_equal);
+  DEFSUBR (Flax_plists_eq);
+  DEFSUBR (Flax_plists_equal);
+  DEFSUBR (Fplist_get);
+  DEFSUBR (Fplist_put);
+  DEFSUBR (Fplist_remprop);
+  DEFSUBR (Fplist_member);
+  DEFSUBR (Fcheck_valid_plist);
+  DEFSUBR (Fvalid_plist_p);
+  DEFSUBR (Fcanonicalize_plist);
+  DEFSUBR (Flax_plist_get);
+  DEFSUBR (Flax_plist_put);
+  DEFSUBR (Flax_plist_remprop);
+  DEFSUBR (Flax_plist_member);
+  DEFSUBR (Fcanonicalize_lax_plist);
+  DEFSUBR (Fdestructive_alist_to_plist);
+  DEFSUBR (Fget);
+  DEFSUBR (Fput);
+  DEFSUBR (Fremprop);
+  DEFSUBR (Fobject_plist);
+  DEFSUBR (Fequal);
+  DEFSUBR (Fold_equal);
+  DEFSUBR (Ffillarray);
+  DEFSUBR (Fnconc);
+  DEFSUBR (Fmapcar);
+  DEFSUBR (Fmapvector);
+  DEFSUBR (Fmapc_internal);
+  DEFSUBR (Fmapconcat);
+  DEFSUBR (Fload_average);
+  DEFSUBR (Ffeaturep);
+  DEFSUBR (Frequire);
+  DEFSUBR (Fprovide);
+  DEFSUBR (Fbase64_encode_region);
+  DEFSUBR (Fbase64_encode_string);
+  DEFSUBR (Fbase64_decode_region);
+  DEFSUBR (Fbase64_decode_string);
+}
+
+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;
+
+  Fprovide (intern ("base64"));
+}