diff src/lread.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 8de8e3f6228a
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lread.c	Mon Aug 13 11:28:15 2007 +0200
@@ -0,0 +1,3312 @@
+/* Lisp parsing and input streams.
+   Copyright (C) 1985-1989, 1992-1995 Free Software Foundation, Inc.
+   Copyright (C) 1995 Tinker Systems.
+   Copyright (C) 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. */
+
+#include <config.h>
+#include "lisp.h"
+
+#include "buffer.h"
+#include "bytecode.h"
+#include "elhash.h"
+#include "lstream.h"
+#include "opaque.h"
+#ifdef FILE_CODING
+#include "file-coding.h"
+#endif
+
+#include "sysfile.h"
+
+#ifdef LISP_FLOAT_TYPE
+#define THIS_FILENAME lread
+#include "sysfloat.h"
+#endif /* LISP_FLOAT_TYPE */
+
+Lisp_Object Qread_char, Qstandard_input;
+Lisp_Object Qvariable_documentation;
+#define LISP_BACKQUOTES
+#ifdef LISP_BACKQUOTES
+/*
+   Nonzero means inside a new-style backquote
+   with no surrounding parentheses.
+   Fread initializes this to zero, so we need not specbind it
+   or worry about what happens to it when there is an error.
+
+XEmacs:
+   Nested backquotes are perfectly legal and fail utterly with
+   this silliness. */
+static int new_backquote_flag, old_backquote_flag;
+Lisp_Object Qbackquote, Qbacktick, Qcomma, Qcomma_at, Qcomma_dot;
+#endif
+Lisp_Object Qvariable_domain;	/* I18N3 */
+Lisp_Object Vvalues, Vstandard_input, Vafter_load_alist;
+Lisp_Object Qcurrent_load_list;
+Lisp_Object Qload, Qload_file_name;
+Lisp_Object Qfset;
+
+/* Hash-table that maps directory names to hashes of their contents.  */
+static Lisp_Object Vlocate_file_hash_table;
+
+Lisp_Object Qexists, Qreadable, Qwritable, Qexecutable;
+
+/* See read_escape() for an explanation of this.  */
+#if 0
+int fail_on_bucky_bit_character_escapes;
+#endif
+
+/* This symbol is also used in fns.c */
+#define FEATUREP_SYNTAX
+
+#ifdef FEATUREP_SYNTAX
+Lisp_Object Qfeaturep;
+#endif
+
+/* non-zero if inside `load' */
+int load_in_progress;
+
+/* Whether Fload_internal() should check whether the .el is newer
+   when loading .elc */
+int load_warn_when_source_newer;
+/* Whether Fload_internal() should check whether the .elc doesn't exist */
+int load_warn_when_source_only;
+/* Whether Fload_internal() should ignore .elc files when no suffix is given */
+int load_ignore_elc_files;
+
+/* Search path for files to be loaded. */
+Lisp_Object Vload_path;
+
+/* Search path for files when dumping. */
+/* Lisp_Object Vdump_load_path; */
+
+/* This is the user-visible association list that maps features to
+   lists of defs in their load files. */
+Lisp_Object Vload_history;
+
+/* This is used to build the load history.  */
+Lisp_Object Vcurrent_load_list;
+
+/* Name of file actually being read by `load'.  */
+Lisp_Object Vload_file_name;
+
+/* Same as Vload_file_name but not Lisp-accessible.  This ensures that
+   our #$ checks are reliable. */
+Lisp_Object Vload_file_name_internal;
+
+Lisp_Object Vload_file_name_internal_the_purecopy;
+
+/* Function to use for reading, in `load' and friends.  */
+Lisp_Object Vload_read_function;
+
+/* The association list of objects read with the #n=object form.
+   Each member of the list has the form (n . object), and is used to
+   look up the object for the corresponding #n# construct.
+   It must be set to nil before all top-level calls to read0.  */
+Lisp_Object Vread_objects;
+
+/* Nonzero means load should forcibly load all dynamic doc strings.  */
+/* Note that this always happens (with some special behavior) when
+   purify_flag is set. */
+static int load_force_doc_strings;
+
+/* List of descriptors now open for Fload_internal.  */
+static Lisp_Object Vload_descriptor_list;
+
+/* In order to implement "load_force_doc_strings", we keep
+   a list of all the compiled-function objects and such
+   that we have created in the process of loading this file.
+   See the rant below.
+
+   We specbind this just like Vload_file_name, so there's no
+   problems with recursive loading. */
+static Lisp_Object Vload_force_doc_string_list;
+
+/* A resizing-buffer stream used to temporarily hold data while reading */
+static Lisp_Object Vread_buffer_stream;
+
+#ifdef COMPILED_FUNCTION_ANNOTATION_HACK
+Lisp_Object Vcurrent_compiled_function_annotation;
+#endif
+
+static int load_byte_code_version;
+
+/* An array describing all known built-in structure types */
+static structure_type_dynarr *the_structure_type_dynarr;
+
+#if 0 /* FSF defun hack */
+/* When nonzero, read conses in pure space */
+static int read_pure;
+#endif
+
+#if 0 /* FSF stuff */
+/* For use within read-from-string (this reader is non-reentrant!!)  */
+static int read_from_string_index;
+static int read_from_string_limit;
+#endif
+
+#if 0 /* More FSF implementation kludges. */
+/* In order to implement load-force-doc-string, FSF saves the
+   #@-quoted string when it's seen, and goes back and retrieves
+   it later.
+
+   This approach is not only kludgy, but it in general won't work
+   correctly because there's no stack of remembered #@-quoted-strings
+   and those strings don't generally appear in the file in the same
+   order as their #$ references. (Yes, that is amazingly stupid too.
+
+   It would be trivially easy to always encode the #@ string
+   [which is a comment, anyway] in the middle of the (#$ . INT) cons
+   reference.  That way, it would be really easy to implement
+   load-force-doc-string in a non-kludgy way by just retrieving the
+   string immediately, because it's delivered on a silver platter.)
+
+   And finally, this stupid approach doesn't work under Mule, or
+   under MS-DOS or Windows NT, or under VMS, or any other place
+   where you either can't do an ftell() or don't get back a byte
+   count.
+
+   Oh, and one more lossage in this approach: If you attempt to
+   dump any ELC files that were compiled with `byte-compile-dynamic'
+   (as opposed to just `byte-compile-dynamic-docstring'), you
+   get hosed.  FMH! (as the illustrious JWZ was prone to utter)
+
+   The approach we use is clean, solves all of these problems, and is
+   probably easier to implement anyway.  We just save a list of all
+   the containing objects that have (#$ . INT) conses in them (this
+   will only be compiled-function objects and lists), and when the
+   file is finished loading, we go through and fill in all the
+   doc strings at once. */
+
+ /* This contains the last string skipped with #@.  */
+static char *saved_doc_string;
+/* Length of buffer allocated in saved_doc_string.  */
+static int saved_doc_string_size;
+/* Length of actual data in saved_doc_string.  */
+static int saved_doc_string_length;
+/* This is the file position that string came from.  */
+static int saved_doc_string_position;
+#endif
+
+EXFUN (Fread_from_string, 3);
+
+/* When errors are signaled, the actual readcharfun should not be used
+   as an argument if it is an lstream, so that lstreams don't escape
+   to the Lisp level.  */
+#define READCHARFUN_MAYBE(x) (LSTREAMP (x)					\
+			      ? (build_string ("internal input stream"))	\
+			      : (x))
+
+
+static DOESNT_RETURN
+syntax_error (CONST char *string)
+{
+  signal_error (Qinvalid_read_syntax,
+		list1 (build_translated_string (string)));
+}
+
+static Lisp_Object
+continuable_syntax_error (CONST char *string)
+{
+  return Fsignal (Qinvalid_read_syntax,
+		  list1 (build_translated_string (string)));
+}
+
+
+/* Handle unreading and rereading of characters. */
+static Emchar
+readchar (Lisp_Object readcharfun)
+{
+  /* This function can GC */
+
+  if (BUFFERP (readcharfun))
+    {
+      Emchar c;
+      struct buffer *b = XBUFFER (readcharfun);
+
+      if (!BUFFER_LIVE_P (b))
+        error ("Reading from killed buffer");
+
+      if (BUF_PT (b) >= BUF_ZV (b))
+        return -1;
+      c = BUF_FETCH_CHAR (b, BUF_PT (b));
+      BUF_SET_PT (b, BUF_PT (b) + 1);
+
+      return c;
+    }
+  else if (LSTREAMP (readcharfun))
+    {
+      Emchar c = Lstream_get_emchar (XLSTREAM (readcharfun));
+#ifdef DEBUG_XEMACS /* testing Mule */
+      static int testing_mule = 0; /* Change via debugger */
+      if (testing_mule) {
+        if (c >= 0x20 && c <= 0x7E) fprintf (stderr, "%c", c);
+        else if (c == '\n')         fprintf (stderr, "\\n\n");
+        else                        fprintf (stderr, "\\%o ", c);
+      }
+#endif
+      return c;
+    }
+  else if (MARKERP (readcharfun))
+    {
+      Emchar c;
+      Bufpos mpos = marker_position (readcharfun);
+      struct buffer *inbuffer = XMARKER (readcharfun)->buffer;
+
+      if (mpos >= BUF_ZV (inbuffer))
+	return -1;
+      c = BUF_FETCH_CHAR (inbuffer, mpos);
+      set_marker_position (readcharfun, mpos + 1);
+      return c;
+    }
+  else
+    {
+      Lisp_Object tem = call0 (readcharfun);
+
+      if (!CHAR_OR_CHAR_INTP (tem))
+	return -1;
+      return XCHAR_OR_CHAR_INT (tem);
+    }
+}
+
+/* Unread the character C in the way appropriate for the stream READCHARFUN.
+   If the stream is a user function, call it with the char as argument.  */
+
+static void
+unreadchar (Lisp_Object readcharfun, Emchar c)
+{
+  if (c == -1)
+    /* Don't back up the pointer if we're unreading the end-of-input mark,
+       since readchar didn't advance it when we read it.  */
+    ;
+  else if (BUFFERP (readcharfun))
+    BUF_SET_PT (XBUFFER (readcharfun), BUF_PT (XBUFFER (readcharfun)) - 1);
+  else if (LSTREAMP (readcharfun))
+    {
+      Lstream_unget_emchar (XLSTREAM (readcharfun), c);
+#ifdef DEBUG_XEMACS /* testing Mule */
+      {
+        static int testing_mule = 0; /* Set this using debugger */
+        if (testing_mule)
+          fprintf (stderr,
+                   (c >= 0x20 && c <= 0x7E) ? "UU%c" :
+                   ((c == '\n') ? "UU\\n\n" : "UU\\%o"), c);
+      }
+#endif
+    }
+  else if (MARKERP (readcharfun))
+    set_marker_position (readcharfun, marker_position (readcharfun) - 1);
+  else
+    call1 (readcharfun, make_char (c));
+}
+
+static Lisp_Object read0 (Lisp_Object readcharfun);
+static Lisp_Object read1 (Lisp_Object readcharfun);
+/* allow_dotted_lists means that something like (foo bar . baz)
+   is acceptable.  If -1, means check for starting with defun
+   and make structure pure. (not implemented, probably for very
+   good reasons)
+*/
+/*
+   If check_for_doc_references, look for (#$ . INT) doc references
+   in the list and record if load_force_doc_strings is non-zero.
+   (Such doc references will be destroyed during the loadup phase
+   by replacing with Qzero, because Snarf-documentation will fill
+   them in again.)
+
+   WARNING: If you set this, you sure as hell better not call
+   free_list() on the returned list here. */
+
+static Lisp_Object read_list (Lisp_Object readcharfun,
+                              Emchar terminator,
+                              int allow_dotted_lists,
+			      int check_for_doc_references);
+
+static void readevalloop (Lisp_Object readcharfun,
+                          Lisp_Object sourcefile,
+                          Lisp_Object (*evalfun) (Lisp_Object),
+                          int printflag);
+
+static Lisp_Object
+load_unwind (Lisp_Object stream)  /* used as unwind-protect function in load */
+{
+  Lstream_close (XLSTREAM (stream));
+  if (--load_in_progress < 0)
+    load_in_progress = 0;
+  return Qnil;
+}
+
+static Lisp_Object
+load_descriptor_unwind (Lisp_Object oldlist)
+{
+  Vload_descriptor_list = oldlist;
+  return Qnil;
+}
+
+static Lisp_Object
+load_file_name_internal_unwind (Lisp_Object oldval)
+{
+  Vload_file_name_internal = oldval;
+  return Qnil;
+}
+
+static Lisp_Object
+load_file_name_internal_the_purecopy_unwind (Lisp_Object oldval)
+{
+  Vload_file_name_internal_the_purecopy = oldval;
+  return Qnil;
+}
+
+static Lisp_Object
+load_byte_code_version_unwind (Lisp_Object oldval)
+{
+  load_byte_code_version = XINT (oldval);
+  return Qnil;
+}
+
+/* The plague is coming.
+
+   Ring around the rosy, pocket full of posy,
+   Ashes ashes, they all fall down.
+   */
+void
+ebolify_bytecode_constants (Lisp_Object vector)
+{
+  int len = XVECTOR_LENGTH (vector);
+  int i;
+
+  for (i = 0; i < len; i++)
+    {
+      Lisp_Object el = XVECTOR_DATA (vector)[i];
+
+      /* We don't check for `eq', `equal', and the others that have
+	 bytecode opcodes.  This might lose if someone passes #'eq or
+	 something to `funcall', but who would really do that?  As
+	 they say in law, we've made a "good-faith effort" to
+	 unfuckify ourselves.  And doing it this way avoids screwing
+	 up args to `make-hash-table' and such.  As it is, we have to
+	 add an extra Ebola check in decode_weak_list_type(). --ben */
+      if      (EQ (el, Qassoc))  el = Qold_assoc;
+      else if (EQ (el, Qdelq))   el = Qold_delq;
+#if 0
+      /* I think this is a bad idea because it will probably mess
+	 with keymap code. */
+      else if (EQ (el, Qdelete)) el = Qold_delete;
+#endif
+      else if (EQ (el, Qrassq))  el = Qold_rassq;
+      else if (EQ (el, Qrassoc)) el = Qold_rassoc;
+
+      XVECTOR_DATA (vector)[i] = el;
+    }
+}
+
+static Lisp_Object
+pas_de_lache_ici (int fd, Lisp_Object victim)
+{
+  Lisp_Object tem;
+  EMACS_INT pos;
+
+  if (!INTP (XCDR (victim)))
+    signal_simple_error ("Bogus doc string reference", victim);
+  pos = XINT (XCDR (victim));
+  if (pos < 0)
+    pos = -pos; /* kludge to mark a user variable */
+  tem = unparesseuxify_doc_string (fd, pos, 0, Vload_file_name_internal);
+  if (!STRINGP (tem))
+    signal_error (Qerror, tem);
+  return tem;
+}
+
+static Lisp_Object
+load_force_doc_string_unwind (Lisp_Object oldlist)
+{
+  struct gcpro gcpro1;
+  Lisp_Object list = Vload_force_doc_string_list;
+  Lisp_Object tail;
+  int fd = XINT (XCAR (Vload_descriptor_list));
+
+  GCPRO1 (list);
+  /* restore the old value first just in case an error occurs. */
+  Vload_force_doc_string_list = oldlist;
+
+  LIST_LOOP (tail, list)
+    {
+      Lisp_Object john = Fcar (tail);
+      if (CONSP (john))
+	{
+	  assert (CONSP (XCAR (john)));
+	  assert (!purify_flag); /* should have been handled in read_list() */
+	  XCAR (john) = pas_de_lache_ici (fd, XCAR (john));
+	}
+      else
+	{
+	  Lisp_Object doc;
+
+	  assert (COMPILED_FUNCTIONP (john));
+	  if (CONSP (XCOMPILED_FUNCTION (john)->instructions))
+	    {
+	      struct gcpro ngcpro1;
+	      Lisp_Object juan = (pas_de_lache_ici
+				  (fd, XCOMPILED_FUNCTION (john)->instructions));
+	      Lisp_Object ivan;
+
+	      NGCPRO1 (juan);
+	      ivan = Fread (juan);
+	      if (!CONSP (ivan))
+		signal_simple_error ("invalid lazy-loaded byte code", ivan);
+	      XCOMPILED_FUNCTION (john)->instructions = XCAR (ivan);
+	      /* v18 or v19 bytecode file.  Need to Ebolify. */
+	      if (XCOMPILED_FUNCTION (john)->flags.ebolified
+		  && VECTORP (XCDR (ivan)))
+		ebolify_bytecode_constants (XCDR (ivan));
+	      XCOMPILED_FUNCTION (john)->constants = XCDR (ivan);
+	      NUNGCPRO;
+	    }
+	  doc = compiled_function_documentation (XCOMPILED_FUNCTION (john));
+	  if (CONSP (doc))
+	    {
+	      assert (!purify_flag); /* should have been handled in
+					read_compiled_function() */
+	      doc = pas_de_lache_ici (fd, doc);
+	      set_compiled_function_documentation (XCOMPILED_FUNCTION (john),
+						   doc);
+	    }
+	}
+    }
+
+  if (!NILP (list))
+    free_list (list);
+
+  UNGCPRO;
+  return Qnil;
+}
+
+/* Close all descriptors in use for Fload_internal.
+   This is used when starting a subprocess.  */
+
+void
+close_load_descs (void)
+{
+  Lisp_Object tail;
+  LIST_LOOP (tail, Vload_descriptor_list)
+    close (XINT (XCAR (tail)));
+}
+
+#ifdef I18N3
+Lisp_Object Vfile_domain;
+
+Lisp_Object
+restore_file_domain (Lisp_Object val)
+{
+  Vfile_domain = val;
+  return Qnil;
+}
+#endif /* I18N3 */
+
+DEFUN ("load-internal", Fload_internal, 1, 6, 0, /*
+Execute a file of Lisp code named FILE; no coding-system frobbing.
+This function is identical to `load' except for the handling of the
+CODESYS and USED-CODESYS arguments under XEmacs/Mule. (When Mule
+support is not present, both functions are identical and ignore the
+CODESYS and USED-CODESYS arguments.)
+
+If support for Mule exists in this Emacs, the file is decoded
+according to CODESYS; if omitted, no conversion happens.  If
+USED-CODESYS is non-nil, it should be a symbol, and the actual coding
+system that was used for the decoding is stored into it.  It will in
+general be different from CODESYS if CODESYS specifies automatic
+encoding detection or end-of-line detection.
+*/
+       (file, no_error, nomessage, nosuffix, codesys, used_codesys))
+{
+  /* This function can GC */
+  int fd = -1;
+  int speccount = specpdl_depth ();
+  int source_only = 0;
+  Lisp_Object newer   = Qnil;
+  Lisp_Object handler = Qnil;
+  Lisp_Object found   = Qnil;
+  struct gcpro gcpro1, gcpro2, gcpro3;
+  int reading_elc = 0;
+  int message_p = NILP (nomessage);
+/*#ifdef DEBUG_XEMACS*/
+  static Lisp_Object last_file_loaded;
+/*#endif*/
+  struct stat s1, s2;
+  GCPRO3 (file, newer, found);
+
+  CHECK_STRING (file);
+
+/*#ifdef DEBUG_XEMACS*/
+  if (purify_flag && noninteractive)
+    {
+      message_p = 1;
+      last_file_loaded = file;
+    }
+/*#endif / * DEBUG_XEMACS */
+
+  /* If file name is magic, call the handler.  */
+  handler = Ffind_file_name_handler (file, Qload);
+  if (!NILP (handler))
+    RETURN_UNGCPRO (call5 (handler, Qload, file, no_error,
+			  nomessage, nosuffix));
+
+  /* Do this after the handler to avoid
+     the need to gcpro noerror, nomessage and nosuffix.
+     (Below here, we care only whether they are nil or not.)  */
+  file = Fsubstitute_in_file_name (file);
+#ifdef FILE_CODING
+  if (!NILP (used_codesys))
+    CHECK_SYMBOL (used_codesys);
+#endif
+
+  /* Avoid weird lossage with null string as arg,
+     since it would try to load a directory as a Lisp file.
+     Unix truly sucks. */
+  if (XSTRING_LENGTH (file) > 0)
+    {
+      char *foundstr;
+      int foundlen;
+
+      fd = locate_file (Vload_path, file,
+                        ((!NILP (nosuffix)) ? Qnil :
+			 build_string (load_ignore_elc_files ? ".el:" :
+				       ".elc:.el:")),
+                        &found,
+                        -1);
+
+      if (fd < 0)
+	{
+	  if (NILP (no_error))
+	    signal_file_error ("Cannot open load file", file);
+	  else
+	    {
+	      UNGCPRO;
+	      return Qnil;
+	    }
+	}
+
+      foundstr = (char *) alloca (XSTRING_LENGTH (found) + 1);
+      strcpy (foundstr, (char *) XSTRING_DATA (found));
+      foundlen = strlen (foundstr);
+
+      /* The omniscient JWZ thinks this is worthless, but I beg to
+	 differ. --ben */
+      if (load_ignore_elc_files)
+	{
+	  newer = Ffile_name_nondirectory (found);
+	}
+      else if (load_warn_when_source_newer &&
+	       !memcmp (".elc", foundstr + foundlen - 4, 4))
+	{
+	  if (! fstat (fd, &s1))	/* can't fail, right? */
+	    {
+	      int result;
+	      /* temporarily hack the 'c' off the end of the filename */
+	      foundstr[foundlen - 1] = '\0';
+	      result = stat (foundstr, &s2);
+	      if (result >= 0 &&
+		  (unsigned) s1.st_mtime < (unsigned) s2.st_mtime)
+              {
+		Lisp_Object newer_name = make_string ((Bufbyte *) foundstr,
+						      foundlen - 1);
+                struct gcpro nngcpro1;
+                NNGCPRO1 (newer_name);
+		newer = Ffile_name_nondirectory (newer_name);
+                NNUNGCPRO;
+              }
+	      /* put the 'c' back on (kludge-o-rama) */
+	      foundstr[foundlen - 1] = 'c';
+	    }
+	}
+      else if (load_warn_when_source_only &&
+	       /* `found' ends in ".el" */
+	       !memcmp (".el", foundstr + foundlen - 3, 3) &&
+	       /* `file' does not end in ".el" */
+	       memcmp (".el",
+		       XSTRING_DATA (file) + XSTRING_LENGTH (file) - 3,
+		       3))
+	{
+	  source_only = 1;
+	}
+
+      if (!memcmp (".elc", foundstr + foundlen - 4, 4))
+	reading_elc = 1;
+    }
+
+#define PRINT_LOADING_MESSAGE(done) do {				\
+  if (load_ignore_elc_files)						\
+    {									\
+      if (message_p)							\
+	message ("Loading %s..." done, XSTRING_DATA (newer));		\
+    }									\
+  else if (!NILP (newer))						\
+    message ("Loading %s..." done " (file %s is newer)",		\
+	     XSTRING_DATA (file),					\
+	     XSTRING_DATA (newer));					\
+  else if (source_only)							\
+    message ("Loading %s..." done " (file %s.elc does not exist)",	\
+	     XSTRING_DATA (file),					\
+	     XSTRING_DATA (Ffile_name_nondirectory (file)));		\
+  else if (message_p)							\
+    message ("Loading %s..." done, XSTRING_DATA (file));		\
+  } while (0)
+
+  PRINT_LOADING_MESSAGE ("");
+
+  {
+    /* Lisp_Object's must be malloc'ed, not stack-allocated */
+    Lisp_Object lispstream = Qnil;
+    CONST int block_size = 8192;
+    struct gcpro ngcpro1;
+
+    NGCPRO1 (lispstream);
+    lispstream = make_filedesc_input_stream (fd, 0, -1, LSTR_CLOSING);
+    /* 64K is used for normal files; 8K should be OK here because Lisp
+       files aren't really all that big. */
+    Lstream_set_buffering (XLSTREAM (lispstream), LSTREAM_BLOCKN_BUFFERED,
+			   block_size);
+#ifdef FILE_CODING
+    lispstream = make_decoding_input_stream
+      (XLSTREAM (lispstream), Fget_coding_system (codesys));
+    Lstream_set_buffering (XLSTREAM (lispstream), LSTREAM_BLOCKN_BUFFERED,
+			   block_size);
+#endif
+    /* NOTE: Order of these is very important.  Don't rearrange them. */
+    record_unwind_protect (load_unwind, lispstream);
+    record_unwind_protect (load_descriptor_unwind, Vload_descriptor_list);
+    record_unwind_protect (load_file_name_internal_unwind,
+			   Vload_file_name_internal);
+    record_unwind_protect (load_file_name_internal_the_purecopy_unwind,
+			   Vload_file_name_internal_the_purecopy);
+    record_unwind_protect (load_force_doc_string_unwind,
+			   Vload_force_doc_string_list);
+    Vload_file_name_internal = found;
+    Vload_file_name_internal_the_purecopy = Qnil;
+    specbind (Qload_file_name, found);
+    Vload_descriptor_list = Fcons (make_int (fd), Vload_descriptor_list);
+    Vload_force_doc_string_list = Qnil;
+#ifdef I18N3
+    record_unwind_protect (restore_file_domain, Vfile_domain);
+    Vfile_domain = Qnil; /* set it to nil; a call to #'domain will set it. */
+#endif
+    load_in_progress++;
+
+    /* Now determine what sort of ELC file we're reading in. */
+    record_unwind_protect (load_byte_code_version_unwind,
+			   make_int (load_byte_code_version));
+    if (reading_elc)
+      {
+	char elc_header[8];
+	int num_read;
+
+	num_read = Lstream_read (XLSTREAM (lispstream), elc_header, 8);
+	if (num_read < 8
+	    || strncmp (elc_header, ";ELC", 4))
+	  {
+	    /* Huh?  Probably not a valid ELC file. */
+	    load_byte_code_version = 100; /* no Ebolification needed */
+	    Lstream_unread (XLSTREAM (lispstream), elc_header, num_read);
+	  }
+	else
+	  load_byte_code_version = elc_header[4];
+      }
+    else
+      load_byte_code_version = 100; /* no Ebolification needed */
+
+    readevalloop (lispstream, file, Feval, 0);
+#ifdef FILE_CODING
+    if (!NILP (used_codesys))
+      Fset (used_codesys,
+	    XCODING_SYSTEM_NAME
+	    (decoding_stream_coding_system (XLSTREAM (lispstream))));
+#endif
+    unbind_to (speccount, Qnil);
+
+    NUNGCPRO;
+  }
+
+  {
+    Lisp_Object tem;
+    /* #### Disgusting kludge */
+    /* Run any load-hooks for this file.  */
+    /* #### An even more disgusting kludge.  There is horrible code */
+    /* that is relying on the fact that dumped lisp files are found */
+    /* via `load-path' search. */
+    Lisp_Object name = file;
+
+    if (!NILP(Ffile_name_absolute_p(file)))
+      {
+	name = Ffile_name_nondirectory(file);
+      }
+
+    {
+      struct gcpro ngcpro1;
+
+      NGCPRO1 (name);
+      tem = Fassoc (name, Vafter_load_alist);
+      NUNGCPRO;
+    }
+    if (!NILP (tem))
+      {
+	struct gcpro ngcpro1;
+
+	NGCPRO1 (tem);
+	/* Use eval so that errors give a semi-meaningful backtrace.  --Stig */
+	tem = Fcons (Qprogn, Fcdr (tem));
+	Feval (tem);
+	NUNGCPRO;
+      }
+  }
+
+/*#ifdef DEBUG_XEMACS*/
+  if (purify_flag && noninteractive)
+    {
+      if (!EQ (last_file_loaded, file))
+	message ("Loading %s ...done", XSTRING_DATA (file));
+    }
+/*#endif / * DEBUG_XEMACS */
+
+  if (!noninteractive)
+    PRINT_LOADING_MESSAGE ("done");
+
+  UNGCPRO;
+  return Qt;
+}
+
+
+/* ------------------------------- */
+/*          locate_file            */
+/* ------------------------------- */
+
+static int
+decode_mode_1 (Lisp_Object mode)
+{
+  if (EQ (mode, Qexists))
+    return F_OK;
+  else if (EQ (mode, Qexecutable))
+    return X_OK;
+  else if (EQ (mode, Qwritable))
+    return W_OK;
+  else if (EQ (mode, Qreadable))
+    return R_OK;
+  else if (INTP (mode))
+    {
+      check_int_range (XINT (mode), 0, 7);
+      return XINT (mode);
+    }
+  else
+    signal_simple_error ("Invalid value", mode);
+  return 0;			/* unreached */
+}
+
+static int
+decode_mode (Lisp_Object mode)
+{
+  if (NILP (mode))
+    return R_OK;
+  else if (CONSP (mode))
+    {
+      Lisp_Object tail;
+      int mask = 0;
+      EXTERNAL_LIST_LOOP (tail, mode)
+	mask |= decode_mode_1 (XCAR (tail));
+      return mask;
+    }
+  else
+    return decode_mode_1 (mode);
+}
+
+DEFUN ("locate-file", Flocate_file, 2, 4, 0, /*
+Search for FILENAME through PATH-LIST.
+
+If SUFFIXES is non-nil, it should be a list of suffixes to append to
+file name when searching.
+
+If MODE is non-nil, it should be a symbol or a list of symbol representing
+requirements.  Allowed symbols are `exists', `executable', `writable', and
+`readable'.  If MODE is nil, it defaults to `readable'.
+
+`locate-file' keeps hash tables of the directories it searches through,
+in order to speed things up.  It tries valiantly to not get confused in
+the face of a changing and unpredictable environment, but can occasionally
+get tripped up.  In this case, you will have to call
+`locate-file-clear-hashing' to get it back on track.  See that function
+for details.
+*/
+       (filename, path_list, suffixes, mode))
+{
+  /* This function can GC */
+  Lisp_Object tp;
+
+  CHECK_STRING (filename);
+
+  if (LISTP (suffixes))
+    {
+      Lisp_Object tail;
+      EXTERNAL_LIST_LOOP (tail, suffixes)
+	CHECK_STRING (XCAR (tail));
+    }
+  else
+    CHECK_STRING (suffixes);
+
+  locate_file (path_list, filename, suffixes, &tp, decode_mode (mode));
+  return tp;
+}
+
+/* Recalculate the hash table for the given string.  DIRECTORY should
+   better have been through Fexpand_file_name() by now.  */
+
+static Lisp_Object
+locate_file_refresh_hashing (Lisp_Object directory)
+{
+  Lisp_Object hash =
+    make_directory_hash_table ((char *) XSTRING_DATA (directory));
+
+  if (!NILP (hash))
+    Fputhash (directory, hash, Vlocate_file_hash_table);
+  return hash;
+}
+
+/* find the hash table for the given directory, recalculating if necessary */
+
+static Lisp_Object
+locate_file_find_directory_hash_table (Lisp_Object directory)
+{
+  Lisp_Object hash = Fgethash (directory, Vlocate_file_hash_table, Qnil);
+  if (NILP (hash))
+    return locate_file_refresh_hashing (directory);
+  else
+    return hash;
+}
+
+/* The SUFFIXES argument in any of the locate_file* functions can be
+   nil, a list, or a string (for backward compatibility), with the
+   following semantics:
+
+   a) nil    - no suffix, just search for file name intact
+               (semantically different from "empty suffix list", which
+               would be meaningless.)
+   b) list   - list of suffixes to append to file name.  Each of these
+               must be a string.
+   c) string - colon-separated suffixes to append to file name (backward
+               compatibility).
+
+   All of this got hairy, so I decided to use a mapper.  Calling a
+   function for each suffix shouldn't slow things down, since
+   locate_file is rarely called with enough suffixes for funcalls to
+   make any difference.  */
+
+/* Map FUN over SUFFIXES, as described above.  FUN will be called with a
+   char * containing the current file name, and ARG.  Mapping stops when
+   FUN returns non-zero. */
+static void
+locate_file_map_suffixes (Lisp_Object filename, Lisp_Object suffixes,
+			  int (*fun) (char *, void *),
+			  void *arg)
+{
+  /* This function can GC */
+  char *fn;
+  int fn_len, max;
+
+  /* Calculate maximum size of any filename made from
+     this path element/specified file name and any possible suffix.  */
+  if (CONSP (suffixes))
+    {
+      /* We must traverse the list, so why not do it right. */
+      Lisp_Object tail;
+      max = 0;
+      LIST_LOOP (tail, suffixes)
+	{
+	  if (XSTRING_LENGTH (XCAR (tail)) > max)
+	    max = XSTRING_LENGTH (XCAR (tail));
+	}
+    }
+  else if (NILP (suffixes))
+    max = 0;
+  else
+    /* Just take the easy way out */
+    max = XSTRING_LENGTH (suffixes);
+
+  fn_len = XSTRING_LENGTH (filename);
+  fn = (char *) alloca (max + fn_len + 1);
+  memcpy (fn, (char *) XSTRING_DATA (filename), fn_len);
+
+  /* Loop over suffixes.  */
+  if (!STRINGP (suffixes))
+    {
+      if (NILP (suffixes))
+	{
+	  /* Case a) discussed in the comment above. */
+	  fn[fn_len] = 0;
+	  if ((*fun) (fn, arg))
+	    return;
+	}
+      else
+	{
+	  /* Case b) */
+	  Lisp_Object tail;
+	  LIST_LOOP (tail, suffixes)
+	    {
+	      memcpy (fn + fn_len, XSTRING_DATA (XCAR (tail)),
+		      XSTRING_LENGTH (XCAR (tail)));
+	      fn[fn_len + XSTRING_LENGTH (XCAR (tail))] = 0;
+	      if ((*fun) (fn, arg))
+		return;
+	    }
+	}
+    }
+  else
+    {
+      /* Case c) */
+      CONST char *nsuffix = (CONST char *) XSTRING_DATA (suffixes);
+
+      while (1)
+	{
+	  char *esuffix = (char *) strchr (nsuffix, ':');
+	  int lsuffix = ((esuffix) ? (esuffix - nsuffix) : strlen (nsuffix));
+
+	  /* Concatenate path element/specified name with the suffix.  */
+	  strncpy (fn + fn_len, nsuffix, lsuffix);
+	  fn[fn_len + lsuffix] = 0;
+
+	  if ((*fun) (fn, arg))
+	    return;
+
+	  /* Advance to next suffix.  */
+	  if (esuffix == 0)
+	    break;
+	  nsuffix += lsuffix + 1;
+	}
+    }
+}
+
+struct locate_file_in_directory_mapper_closure {
+  int fd;
+  Lisp_Object *storeptr;
+  int mode;
+};
+
+static int
+locate_file_in_directory_mapper (char *fn, void *arg)
+{
+  struct locate_file_in_directory_mapper_closure *closure =
+    (struct locate_file_in_directory_mapper_closure *)arg;
+  struct stat st;
+
+  /* Ignore file if it's a directory.  */
+  if (stat (fn, &st) >= 0
+      && (st.st_mode & S_IFMT) != S_IFDIR)
+    {
+      /* Check that we can access or open it.  */
+      if (closure->mode >= 0)
+	closure->fd = access (fn, closure->mode);
+      else
+	closure->fd = open (fn, O_RDONLY | OPEN_BINARY, 0);
+
+      if (closure->fd >= 0)
+	{
+	  /* We succeeded; return this descriptor and filename.  */
+	  if (closure->storeptr)
+	    *closure->storeptr = build_string (fn);
+
+#ifndef WINDOWSNT
+	  /* If we actually opened the file, set close-on-exec flag
+	     on the new descriptor so that subprocesses can't whack
+	     at it.  */
+	  if (closure->mode < 0)
+	    (void) fcntl (closure->fd, F_SETFD, FD_CLOEXEC);
+#endif
+
+	  return 1;
+	}
+    }
+  /* Keep mapping. */
+  return 0;
+}
+
+
+/* look for STR in PATH, optionally adding SUFFIXES.  DIRECTORY need
+   not have been expanded.  */
+
+static int
+locate_file_in_directory (Lisp_Object directory, Lisp_Object str,
+			  Lisp_Object suffixes, Lisp_Object *storeptr,
+			  int mode)
+{
+  /* This function can GC */
+  struct locate_file_in_directory_mapper_closure closure;
+  Lisp_Object filename = Qnil;
+  struct gcpro gcpro1, gcpro2, gcpro3;
+
+  GCPRO3 (directory, str, filename);
+
+  filename = Fexpand_file_name (str, directory);
+  if (NILP (filename) || NILP (Ffile_name_absolute_p (filename)))
+    /* If there are non-absolute elts in PATH (eg ".") */
+    /* Of course, this could conceivably lose if luser sets
+       default-directory to be something non-absolute ... */
+    {
+      if (NILP (filename))
+	/* NIL means current directory */
+	filename = current_buffer->directory;
+      else
+	filename = Fexpand_file_name (filename,
+				      current_buffer->directory);
+      if (NILP (Ffile_name_absolute_p (filename)))
+	{
+	  /* Give up on this directory! */
+	  UNGCPRO;
+	  return -1;
+	}
+    }
+
+  closure.fd = -1;
+  closure.storeptr = storeptr;
+  closure.mode = mode;
+
+  locate_file_map_suffixes (filename, suffixes, locate_file_in_directory_mapper,
+			    &closure);
+
+  UNGCPRO;
+  return closure.fd;
+}
+
+/* do the same as locate_file() but don't use any hash tables. */
+
+static int
+locate_file_without_hash (Lisp_Object path, Lisp_Object str,
+			  Lisp_Object suffixes, Lisp_Object *storeptr,
+			  int mode)
+{
+  /* This function can GC */
+  int absolute = !NILP (Ffile_name_absolute_p (str));
+
+  EXTERNAL_LIST_LOOP (path, path)
+    {
+      int val = locate_file_in_directory (XCAR (path), str, suffixes, storeptr,
+					  mode);
+      if (val >= 0)
+	return val;
+      if (absolute)
+	break;
+    }
+  return -1;
+}
+
+static int
+locate_file_construct_suffixed_files_mapper (char *fn, void *arg)
+{
+  Lisp_Object *tail = (Lisp_Object *)arg;
+  *tail = Fcons (build_string (fn), *tail);
+  return 0;
+}
+
+/* Construct a list of all files to search for.
+   It makes sense to have this despite locate_file_map_suffixes()
+   because we need Lisp strings to access the hash-table, and it would
+   be inefficient to create them on the fly, again and again for each
+   path component.  See locate_file(). */
+
+static Lisp_Object
+locate_file_construct_suffixed_files (Lisp_Object filename,
+				      Lisp_Object suffixes)
+{
+  Lisp_Object tail = Qnil;
+  struct gcpro gcpro1;
+  GCPRO1 (tail);
+
+  locate_file_map_suffixes (filename, suffixes,
+			    locate_file_construct_suffixed_files_mapper,
+			    &tail);
+
+  UNGCPRO;
+  return Fnreverse (tail);
+}
+
+DEFUN ("locate-file-clear-hashing", Flocate_file_clear_hashing, 1, 1, 0, /*
+Clear the hash records for the specified list of directories.
+`locate-file' uses a hashing scheme to speed lookup, and will correctly
+track the following environmental changes:
+
+-- changes of any sort to the list of directories to be searched.
+-- addition and deletion of non-shadowing files (see below) from the
+   directories in the list.
+-- byte-compilation of a .el file into a .elc file.
+
+`locate-file' will primarily get confused if you add a file that shadows
+\(i.e. has the same name as) another file further down in the directory list.
+In this case, you must call `locate-file-clear-hashing'.
+
+If PATH is t, it means to fully clear all the accumulated hashes.  This
+can be used if the internal tables grow too large, or when dumping.
+*/
+       (path))
+{
+  if (EQ (path, Qt))
+    Fclrhash (Vlocate_file_hash_table);
+  else
+    {
+      Lisp_Object pathtail;
+      EXTERNAL_LIST_LOOP (pathtail, path)
+	{
+	  Lisp_Object pathel = Fexpand_file_name (XCAR (pathtail), Qnil);
+	  Fremhash (pathel, Vlocate_file_hash_table);
+	}
+    }
+  return Qnil;
+}
+
+/* Search for a file whose name is STR, looking in directories
+   in the Lisp list PATH, and trying suffixes from SUFFIXES.
+   SUFFIXES is a list of possible suffixes, or (for backward
+   compatibility) a string containing possible suffixes separated by
+   colons.
+   On success, returns a file descriptor.  On failure, returns -1.
+
+   MODE nonnegative means don't open the files,
+   just look for one for which access(file,MODE) succeeds.  In this case,
+   returns 1 on success.
+
+   If STOREPTR is nonzero, it points to a slot where the name of
+   the file actually found should be stored as a Lisp string.
+   Nil is stored there on failure.
+
+   Called openp() in FSFmacs. */
+
+int
+locate_file (Lisp_Object path, Lisp_Object str, Lisp_Object suffixes,
+	     Lisp_Object *storeptr, int mode)
+{
+  /* This function can GC */
+  Lisp_Object suffixtab = Qnil;
+  Lisp_Object pathtail, pathel_expanded;
+  int val;
+  struct gcpro gcpro1, gcpro2, gcpro3, gcpro4;
+
+  if (storeptr)
+    *storeptr = Qnil;
+
+  /* Is it really necessary to gcpro path and str?  It shouldn't be
+     unless some caller has fucked up.  There are known instances that
+     call us with build_string("foo:bar") as SUFFIXES, though. */
+  GCPRO4 (path, str, suffixes, suffixtab);
+
+  /* if this filename has directory components, it's too complicated
+     to try and use the hash tables. */
+  if (!NILP (Ffile_name_directory (str)))
+    {
+      val = locate_file_without_hash (path, str, suffixes, storeptr, mode);
+      UNGCPRO;
+      return val;
+    }
+
+  suffixtab = locate_file_construct_suffixed_files (str, suffixes);
+
+  EXTERNAL_LIST_LOOP (pathtail, path)
+    {
+      Lisp_Object pathel = XCAR (pathtail);
+      Lisp_Object hash_table;
+      Lisp_Object tail;
+      int found = 0;
+
+      /* If this path element is relative, we have to look by hand. */
+      if (NILP (Ffile_name_absolute_p (pathel)))
+	{
+	  val = locate_file_in_directory (pathel, str, suffixes, storeptr,
+					  mode);
+	  if (val >= 0)
+	    {
+	      UNGCPRO;
+	      return val;
+	    }
+	  continue;
+	}
+
+      pathel_expanded = Fexpand_file_name (pathel, Qnil);
+      hash_table = locate_file_find_directory_hash_table (pathel_expanded);
+
+      if (!NILP (hash_table))
+	{
+	  /* Loop over suffixes.  */
+	  LIST_LOOP (tail, suffixtab)
+	    if (!NILP (Fgethash (XCAR (tail), hash_table, Qnil)))
+	      {
+		found = 1;
+		break;
+	      }
+	}
+
+      if (found)
+	{
+	  /* This is a likely candidate.  Look by hand in this directory
+	     so we don't get thrown off if someone byte-compiles a file. */
+	  val = locate_file_in_directory (pathel, str, suffixes, storeptr,
+					  mode);
+	  if (val >= 0)
+	    {
+	      UNGCPRO;
+	      return val;
+	    }
+
+	  /* Hmm ...  the file isn't actually there. (Or possibly it's
+	     a directory ...)  So refresh our hashing. */
+	  locate_file_refresh_hashing (pathel_expanded);
+	}
+    }
+
+  /* File is probably not there, but check the hard way just in case. */
+  val = locate_file_without_hash (path, str, suffixes, storeptr, mode);
+  if (val >= 0)
+    {
+      /* Sneaky user added a file without telling us. */
+      Flocate_file_clear_hashing (path);
+    }
+
+  UNGCPRO;
+  return val;
+}
+
+
+#ifdef LOADHIST
+
+/* Merge the list we've accumulated of globals from the current input source
+   into the load_history variable.  The details depend on whether
+   the source has an associated file name or not. */
+
+static void
+build_load_history (int loading, Lisp_Object source)
+{
+  REGISTER Lisp_Object tail, prev, newelt;
+  REGISTER Lisp_Object tem, tem2;
+  int foundit;
+
+#if !defined(LOADHIST_DUMPED)
+  /* Don't bother recording anything for preloaded files.  */
+  if (purify_flag)
+    return;
+#endif
+
+  tail = Vload_history;
+  prev = Qnil;
+  foundit = 0;
+  while (!NILP (tail))
+    {
+      tem = Fcar (tail);
+
+      /* Find the feature's previous assoc list... */
+      if (internal_equal (source, Fcar (tem), 0))
+	{
+	  foundit = 1;
+
+	  /*  If we're loading, remove it. */
+	  if (loading)
+	    {
+	      if (NILP (prev))
+		Vload_history = Fcdr (tail);
+	      else
+		Fsetcdr (prev, Fcdr (tail));
+	    }
+
+	  /*  Otherwise, cons on new symbols that are not already members.  */
+	  else
+	    {
+	      tem2 = Vcurrent_load_list;
+
+	      while (CONSP (tem2))
+		{
+		  newelt = XCAR (tem2);
+
+		  if (NILP (Fmemq (newelt, tem)))
+		    Fsetcar (tail, Fcons (Fcar (tem),
+					  Fcons (newelt, Fcdr (tem))));
+
+		  tem2 = XCDR (tem2);
+		  QUIT;
+		}
+	    }
+	}
+      else
+	prev = tail;
+      tail = Fcdr (tail);
+      QUIT;
+    }
+
+  /* If we're loading, cons the new assoc onto the front of load-history,
+     the most-recently-loaded position.  Also do this if we didn't find
+     an existing member for the current source.  */
+  if (loading || !foundit)
+    Vload_history = Fcons (Fnreverse (Vcurrent_load_list),
+			   Vload_history);
+}
+
+#else /* !LOADHIST */
+#define build_load_history(x,y)
+#endif /* !LOADHIST */
+
+
+#if 0 /* FSFmacs defun hack */
+Lisp_Object
+unreadpure (void)	/* Used as unwind-protect function in readevalloop */
+{
+  read_pure = 0;
+  return Qnil;
+}
+#endif /* 0 */
+
+static void
+readevalloop (Lisp_Object readcharfun,
+              Lisp_Object sourcename,
+              Lisp_Object (*evalfun) (Lisp_Object),
+              int printflag)
+{
+  /* This function can GC */
+  REGISTER Emchar c;
+  REGISTER Lisp_Object val = Qnil;
+  int speccount = specpdl_depth ();
+  struct gcpro gcpro1, gcpro2;
+  struct buffer *b = 0;
+
+  if (BUFFERP (readcharfun))
+    b = XBUFFER (readcharfun);
+  else if (MARKERP (readcharfun))
+    b = XMARKER (readcharfun)->buffer;
+
+  /* Don't do this.  It is not necessary, and it needlessly exposes
+     READCHARFUN (which can be a stream) to Lisp.  --hniksic */
+  /*specbind (Qstandard_input, readcharfun);*/
+
+  specbind (Qcurrent_load_list, Qnil);
+
+#ifdef COMPILED_FUNCTION_ANNOTATION_HACK
+  Vcurrent_compiled_function_annotation = Qnil;
+#endif
+  GCPRO2 (val, sourcename);
+
+  LOADHIST_ATTACH (sourcename);
+
+  while (1)
+    {
+      QUIT;
+
+      if (b != 0 && !BUFFER_LIVE_P (b))
+	error ("Reading from killed buffer");
+
+      c = readchar (readcharfun);
+      if (c == ';')
+	{
+          /* Skip comment */
+	  while ((c = readchar (readcharfun)) != '\n' && c != -1)
+            QUIT;
+	  continue;
+	}
+      if (c < 0)
+        break;
+
+      /* Ignore whitespace here, so we can detect eof.  */
+      if (c == ' ' || c == '\t' || c == '\n' || c == '\f' || c == '\r')
+        continue;
+
+#if 0 /* FSFmacs defun hack */
+      if (purify_flag && c == '(')
+	{
+	  int count1 = specpdl_depth ();
+	  record_unwind_protect (unreadpure, Qnil);
+	  val = read_list (readcharfun, ')', -1, 1);
+	  unbind_to (count1, Qnil);
+	}
+      else
+#else /* No "defun hack" -- Emacs 19 uses read-time syntax for bytecodes */
+	{
+	  unreadchar (readcharfun, c);
+	  Vread_objects = Qnil;
+	  if (NILP (Vload_read_function))
+	    val = read0 (readcharfun);
+	  else
+	    val = call1 (Vload_read_function, readcharfun);
+	}
+#endif
+      val = (*evalfun) (val);
+      if (printflag)
+	{
+	  Vvalues = Fcons (val, Vvalues);
+	  if (EQ (Vstandard_output, Qt))
+	    Fprin1 (val, Qnil);
+	  else
+	    Fprint (val, Qnil);
+	}
+    }
+
+  build_load_history (LSTREAMP (readcharfun) ||
+		      /* This looks weird, but it's what's in FSFmacs */
+		      (b ? BUF_NARROWED (b) : BUF_NARROWED (current_buffer)),
+                      sourcename);
+  UNGCPRO;
+
+  unbind_to (speccount, Qnil);
+}
+
+DEFUN ("eval-buffer", Feval_buffer, 0, 2, "bBuffer: ", /*
+Execute BUFFER as Lisp code.
+Programs can pass two arguments, BUFFER and PRINTFLAG.
+BUFFER is the buffer to evaluate (nil means use current buffer).
+PRINTFLAG controls printing of output:
+nil means discard it; anything else is stream for print.
+
+If there is no error, point does not move.  If there is an error,
+point remains at the end of the last character read from the buffer.
+Execute BUFFER as Lisp code.
+*/
+       (bufname, printflag))
+{
+  /* This function can GC */
+  int speccount = specpdl_depth ();
+  Lisp_Object tem, buf;
+
+  if (NILP (bufname))
+    buf = Fcurrent_buffer ();
+  else
+    buf = Fget_buffer (bufname);
+  if (NILP (buf))
+    error ("No such buffer.");
+
+  if (NILP (printflag))
+    tem = Qsymbolp;             /* #### #@[]*&$#*[& SI:NULL-STREAM */
+  else
+    tem = printflag;
+  specbind (Qstandard_output, tem);
+  record_unwind_protect (save_excursion_restore, save_excursion_save ());
+  BUF_SET_PT (XBUFFER (buf), BUF_BEGV (XBUFFER (buf)));
+  readevalloop (buf, XBUFFER (buf)->filename, Feval,
+		!NILP (printflag));
+
+  return unbind_to (speccount, Qnil);
+}
+
+#if 0
+xxDEFUN ("eval-current-buffer", Feval_current_buffer, 0, 1, "", /*
+Execute the current buffer as Lisp code.
+Programs can pass argument PRINTFLAG which controls printing of output:
+nil means discard it; anything else is stream for print.
+
+If there is no error, point does not move.  If there is an error,
+point remains at the end of the last character read from the buffer.
+*/
+	 (printflag))
+{
+  code omitted;
+}
+#endif /* 0 */
+
+DEFUN ("eval-region", Feval_region, 2, 3, "r", /*
+Execute the region as Lisp code.
+When called from programs, expects two arguments,
+giving starting and ending indices in the current buffer
+of the text to be executed.
+Programs can pass third argument PRINTFLAG which controls output:
+nil means discard it; anything else is stream for printing it.
+
+If there is no error, point does not move.  If there is an error,
+point remains at the end of the last character read from the buffer.
+
+Note:  Before evaling the region, this function narrows the buffer to it.
+If the code being eval'd should happen to trigger a redisplay you may
+see some text temporarily disappear because of this.
+*/
+       (b, e, printflag))
+{
+  /* This function can GC */
+  int speccount = specpdl_depth ();
+  Lisp_Object tem;
+  Lisp_Object cbuf = Fcurrent_buffer ();
+
+  if (NILP (printflag))
+    tem = Qsymbolp;             /* #### #@[]*&$#*[& SI:NULL-STREAM */
+  else
+    tem = printflag;
+  specbind (Qstandard_output, tem);
+
+  if (NILP (printflag))
+    record_unwind_protect (save_excursion_restore, save_excursion_save ());
+  record_unwind_protect (save_restriction_restore, save_restriction_save ());
+
+  /* This both uses b and checks its type.  */
+  Fgoto_char (b, cbuf);
+  Fnarrow_to_region (make_int (BUF_BEGV (current_buffer)), e, cbuf);
+  readevalloop (cbuf, XBUFFER (cbuf)->filename, Feval,
+		!NILP (printflag));
+
+  return unbind_to (speccount, Qnil);
+}
+
+DEFUN ("read", Fread, 0, 1, 0, /*
+Read one Lisp expression as text from STREAM, return as Lisp object.
+If STREAM is nil, use the value of `standard-input' (which see).
+STREAM or the value of `standard-input' may be:
+ a buffer (read from point and advance it)
+ a marker (read from where it points and advance it)
+ a function (call it with no arguments for each character,
+     call it with a char as argument to push a char back)
+ a string (takes text from string, starting at the beginning)
+ t (read text line using minibuffer and use it).
+*/
+       (stream))
+{
+  if (NILP (stream))
+    stream = Vstandard_input;
+  if (EQ (stream, Qt))
+    stream = Qread_char;
+
+  Vread_objects = Qnil;
+
+#ifdef COMPILED_FUNCTION_ANNOTATION_HACK
+  Vcurrent_compiled_function_annotation = Qnil;
+#endif
+  if (EQ (stream, Qread_char))
+    {
+      Lisp_Object val = call1 (Qread_from_minibuffer,
+			       build_translated_string ("Lisp expression: "));
+      return Fcar (Fread_from_string (val, Qnil, Qnil));
+    }
+
+  if (STRINGP (stream))
+    return Fcar (Fread_from_string (stream, Qnil, Qnil));
+
+  return read0 (stream);
+}
+
+DEFUN ("read-from-string", Fread_from_string, 1, 3, 0, /*
+Read one Lisp expression which is represented as text by STRING.
+Returns a cons: (OBJECT-READ . FINAL-STRING-INDEX).
+START and END optionally delimit a substring of STRING from which to read;
+ they default to 0 and (length STRING) respectively.
+*/
+       (string, start, end))
+{
+  Bytecount startval, endval;
+  Lisp_Object tem;
+  Lisp_Object lispstream = Qnil;
+  struct gcpro gcpro1;
+
+#ifdef COMPILED_FUNCTION_ANNOTATION_HACK
+  Vcurrent_compiled_function_annotation = Qnil;
+#endif
+  GCPRO1 (lispstream);
+  CHECK_STRING (string);
+  get_string_range_byte (string, start, end, &startval, &endval,
+			 GB_HISTORICAL_STRING_BEHAVIOR);
+  lispstream = make_lisp_string_input_stream (string, startval,
+					      endval - startval);
+
+  Vread_objects = Qnil;
+
+  tem = read0 (lispstream);
+  /* Yeah, it's ugly.  Gonna make something of it?
+     At least our reader is reentrant ... */
+  tem =
+    (Fcons (tem, make_int
+	    (bytecount_to_charcount
+	     (XSTRING_DATA (string),
+	      startval + Lstream_byte_count (XLSTREAM (lispstream))))));
+  Lstream_delete (XLSTREAM (lispstream));
+  UNGCPRO;
+  return tem;
+}
+
+
+#ifdef LISP_BACKQUOTES
+
+static Lisp_Object
+backquote_unwind (Lisp_Object ptr)
+{  /* used as unwind-protect function in read0() */
+  int *counter = (int *) get_opaque_ptr (ptr);
+  if (--*counter < 0)
+    *counter = 0;
+  free_opaque_ptr (ptr);
+  return Qnil;
+}
+
+#endif
+
+/* Use this for recursive reads, in contexts where internal tokens
+   are not allowed.  See also read1(). */
+static Lisp_Object
+read0 (Lisp_Object readcharfun)
+{
+  Lisp_Object val = read1 (readcharfun);
+
+  if (CONSP (val) && UNBOUNDP (XCAR (val)))
+    {
+      Emchar c = XCHAR (XCDR (val));
+      free_cons (XCONS (val));
+      return Fsignal (Qinvalid_read_syntax,
+		      list1 (Fchar_to_string (make_char (c))));
+    }
+
+  return val;
+}
+
+static Emchar
+read_escape (Lisp_Object readcharfun)
+{
+  /* This function can GC */
+  Emchar c = readchar (readcharfun);
+
+  if (c < 0)
+    signal_error (Qend_of_file, list1 (READCHARFUN_MAYBE (readcharfun)));
+
+  switch (c)
+    {
+    case 'a': return '\007';
+    case 'b': return '\b';
+    case 'd': return 0177;
+    case 'e': return 033;
+    case 'f': return '\f';
+    case 'n': return '\n';
+    case 'r': return '\r';
+    case 't': return '\t';
+    case 'v': return '\v';
+    case '\n': return -1;
+
+    case 'M':
+      c = readchar (readcharfun);
+      if (c < 0)
+	signal_error (Qend_of_file, list1 (READCHARFUN_MAYBE (readcharfun)));
+      if (c != '-')
+	error ("Invalid escape character syntax");
+      c = readchar (readcharfun);
+      if (c < 0)
+	signal_error (Qend_of_file, list1 (READCHARFUN_MAYBE (readcharfun)));
+      if (c == '\\')
+	c = read_escape (readcharfun);
+      return c | 0200;
+
+      /* Originally, FSF_KEYS provided a degree of FSF Emacs
+	 compatibility by defining character "modifiers" alt, super,
+	 hyper and shift to infest the characters (i.e. integers).
+
+	 However, this doesn't cut it for XEmacs 20, which
+	 distinguishes characters from integers.  Without Mule, ?\H-a
+	 simply returns ?a because every character is clipped into
+	 0-255.  Under Mule it is much worse -- ?\H-a with FSF_KEYS
+	 produces an illegal character, and moves us to crash-land.
+
+         For these reasons, FSF_KEYS hack is useless and without hope
+         of ever working under XEmacs 20.  */
+#undef FSF_KEYS
+
+#ifdef FSF_KEYS
+#define alt_modifier   (0x040000)
+#define super_modifier (0x080000)
+#define hyper_modifier (0x100000)
+#define shift_modifier (0x200000)
+/* fsf uses a different modifiers for meta and control.  Possibly
+   byte_compiled code will still work fsfmacs, though... --Stig
+
+   #define ctl_modifier   (0x400000)
+   #define meta_modifier  (0x800000)
+*/
+#define FSF_LOSSAGE(mask)							\
+      if (fail_on_bucky_bit_character_escapes ||				\
+	  ((c = readchar (readcharfun)) != '-'))				\
+	error ("Invalid escape character syntax");				\
+      c = readchar (readcharfun);						\
+      if (c < 0)								\
+	signal_error (Qend_of_file, list1 (READCHARFUN_MAYBE (readcharfun)));	\
+      if (c == '\\')								\
+	c = read_escape (readcharfun);						\
+      return c | mask
+
+    case 'S': FSF_LOSSAGE (shift_modifier);
+    case 'H': FSF_LOSSAGE (hyper_modifier);
+    case 'A': FSF_LOSSAGE (alt_modifier);
+    case 's': FSF_LOSSAGE (super_modifier);
+#undef alt_modifier
+#undef super_modifier
+#undef hyper_modifier
+#undef shift_modifier
+#undef FSF_LOSSAGE
+
+#endif /* FSF_KEYS */
+
+    case 'C':
+      c = readchar (readcharfun);
+      if (c < 0)
+	signal_error (Qend_of_file, list1 (READCHARFUN_MAYBE (readcharfun)));
+      if (c != '-')
+	error ("Invalid escape character syntax");
+    case '^':
+      c = readchar (readcharfun);
+      if (c < 0)
+	signal_error (Qend_of_file, list1 (READCHARFUN_MAYBE (readcharfun)));
+      if (c == '\\')
+	c = read_escape (readcharfun);
+      /* FSFmacs junk for non-ASCII controls.
+	 Not used here. */
+      if (c == '?')
+	return 0177;
+      else
+        return c & (0200 | 037);
+
+    case '0':
+    case '1':
+    case '2':
+    case '3':
+    case '4':
+    case '5':
+    case '6':
+    case '7':
+      /* An octal escape, as in ANSI C.  */
+      {
+	REGISTER Emchar i = c - '0';
+	REGISTER int count = 0;
+	while (++count < 3)
+	  {
+	    if ((c = readchar (readcharfun)) >= '0' && c <= '7')
+              i = (i << 3) + (c - '0');
+	    else
+	      {
+		unreadchar (readcharfun, c);
+		break;
+	      }
+	  }
+	return i;
+      }
+
+    case 'x':
+      /* A hex escape, as in ANSI C, except that we only allow latin-1
+	 characters to be read this way.  What is "\x4e03" supposed to
+	 mean, anyways, if the internal representation is hidden?
+         This is also consistent with the treatment of octal escapes. */
+      {
+	REGISTER Emchar i = 0;
+	REGISTER int count = 0;
+	while (++count <= 2)
+	  {
+	    c = readchar (readcharfun);
+	    /* Remember, can't use isdigit(), isalpha() etc. on Emchars */
+	    if      (c >= '0' && c <= '9')  i = (i << 4) + (c - '0');
+	    else if (c >= 'a' && c <= 'f')  i = (i << 4) + (c - 'a') + 10;
+            else if (c >= 'A' && c <= 'F')  i = (i << 4) + (c - 'A') + 10;
+	    else
+	      {
+		unreadchar (readcharfun, c);
+		break;
+	      }
+	  }
+	return i;
+      }
+
+#ifdef MULE
+      /* #### need some way of reading an extended character with
+	 an escape sequence. */
+#endif
+
+    default:
+	return c;
+    }
+}
+
+
+
+/* read symbol-constituent stuff into `Vread_buffer_stream'. */
+static Bytecount
+read_atom_0 (Lisp_Object readcharfun, Emchar firstchar, int *saw_a_backslash)
+{
+  /* This function can GC */
+  Emchar c = ((firstchar) >= 0 ? firstchar : readchar (readcharfun));
+  Lstream_rewind (XLSTREAM (Vread_buffer_stream));
+
+  *saw_a_backslash = 0;
+
+  while (c > 040	/* #### - comma should be here as should backquote */
+         && !(c == '\"' || c == '\'' || c == ';'
+              || c == '(' || c == ')'
+#ifndef LISP_FLOAT_TYPE
+	      /* If we have floating-point support, then we need
+		 to allow <digits><dot><digits>.  */
+	      || c =='.'
+#endif /* not LISP_FLOAT_TYPE */
+              || c == '[' || c == ']' || c == '#'
+              ))
+    {
+      if (c == '\\')
+	{
+	  c = readchar (readcharfun);
+	  if (c < 0)
+	    signal_error (Qend_of_file, list1 (READCHARFUN_MAYBE (readcharfun)));
+	  *saw_a_backslash = 1;
+	}
+      Lstream_put_emchar (XLSTREAM (Vread_buffer_stream), c);
+      QUIT;
+      c = readchar (readcharfun);
+    }
+
+  if (c >= 0)
+    unreadchar (readcharfun, c);
+  /* blasted terminating 0 */
+  Lstream_put_emchar (XLSTREAM (Vread_buffer_stream), 0);
+  Lstream_flush (XLSTREAM (Vread_buffer_stream));
+
+  return Lstream_byte_count (XLSTREAM (Vread_buffer_stream)) - 1;
+}
+
+static Lisp_Object parse_integer (CONST Bufbyte *buf, Bytecount len, int base);
+
+static Lisp_Object
+read_atom (Lisp_Object readcharfun,
+           Emchar firstchar,
+           int uninterned_symbol)
+{
+  /* This function can GC */
+  int saw_a_backslash;
+  Bytecount len = read_atom_0 (readcharfun, firstchar, &saw_a_backslash);
+  char *read_ptr = (char *)
+    resizing_buffer_stream_ptr (XLSTREAM (Vread_buffer_stream));
+
+  /* Is it an integer? */
+  if (! (saw_a_backslash || uninterned_symbol))
+    {
+      /* If a token had any backslashes in it, it is disqualified from
+	 being an integer or a float.  This means that 123\456 is a
+	 symbol, as is \123 (which is the way (intern "123") prints).
+	 Also, if token was preceded by #:, it's always a symbol.
+       */
+      char *p = read_ptr + len;
+      char *p1 = read_ptr;
+
+      if (*p1 == '+' || *p1 == '-') p1++;
+      if (p1 != p)
+	{
+          int c;
+
+          while (p1 != p && (c = *p1) >= '0' && c <= '9')
+            p1++;
+#ifdef LISP_FLOAT_TYPE
+	  /* Integers can have trailing decimal points.  */
+	  if (p1 > read_ptr && p1 < p && *p1 == '.')
+	    p1++;
+#endif
+          if (p1 == p)
+            {
+              /* It is an integer. */
+#ifdef LISP_FLOAT_TYPE
+	      if (p1[-1] == '.')
+		p1[-1] = '\0';
+#endif
+#if 0
+	      {
+		int number = 0;
+		if (sizeof (int) == sizeof (EMACS_INT))
+		  number = atoi (read_buffer);
+		else if (sizeof (long) == sizeof (EMACS_INT))
+		  number = atol (read_buffer);
+		else
+		  abort ();
+		return make_int (number);
+	      }
+#else
+              return parse_integer ((Bufbyte *) read_ptr, len, 10);
+#endif
+	    }
+	}
+#ifdef LISP_FLOAT_TYPE
+      if (isfloat_string (read_ptr))
+	return make_float (atof (read_ptr));
+#endif
+    }
+
+  {
+    Lisp_Object sym;
+    if (uninterned_symbol)
+      sym = Fmake_symbol ( make_string ((Bufbyte *) read_ptr, len));
+    else
+      {
+	Lisp_Object name = make_string ((Bufbyte *) read_ptr, len);
+	sym = Fintern (name, Qnil);
+      }
+    return sym;
+  }
+}
+
+
+static Lisp_Object
+parse_integer (CONST Bufbyte *buf, Bytecount len, int base)
+{
+  CONST Bufbyte *lim = buf + len;
+  CONST Bufbyte *p = buf;
+  EMACS_UINT num = 0;
+  int negativland = 0;
+
+  if (*p == '-')
+    {
+      negativland = 1;
+      p++;
+    }
+  else if (*p == '+')
+    {
+      p++;
+    }
+
+  if (p == lim)
+    goto loser;
+
+  for (; (p < lim) && (*p != '\0'); p++)
+    {
+      int c = *p;
+      EMACS_UINT onum;
+
+      if (isdigit (c))
+	c = c - '0';
+      else if (isupper (c))
+	c = c - 'A' + 10;
+      else if (islower (c))
+	c = c - 'a' + 10;
+      else
+	goto loser;
+
+      if (c < 0 || c >= base)
+	goto loser;
+
+      onum = num;
+      num = num * base + c;
+      if (num < onum)
+	goto overflow;
+    }
+
+  {
+    EMACS_INT int_result = negativland ? - (EMACS_INT) num : (EMACS_INT) num;
+    Lisp_Object result = make_int (int_result);
+    if (num && ((XINT (result) < 0) != negativland))
+      goto overflow;
+    if (XINT (result) != int_result)
+      goto overflow;
+    return result;
+  }
+ overflow:
+  return Fsignal (Qinvalid_read_syntax,
+                  list3 (build_translated_string
+			 ("Integer constant overflow in reader"),
+                         make_string (buf, len),
+                         make_int (base)));
+ loser:
+  return Fsignal (Qinvalid_read_syntax,
+                  list3 (build_translated_string
+			 ("Invalid integer constant in reader"),
+                         make_string (buf, len),
+                         make_int (base)));
+}
+
+
+static Lisp_Object
+read_integer (Lisp_Object readcharfun, int base)
+{
+  /* This function can GC */
+  int saw_a_backslash;
+  Bytecount len = read_atom_0 (readcharfun, -1, &saw_a_backslash);
+  return (parse_integer
+	  (resizing_buffer_stream_ptr (XLSTREAM (Vread_buffer_stream)),
+	   ((saw_a_backslash)
+	    ? 0 /* make parse_integer signal error */
+	    : len),
+	   base));
+}
+
+static Lisp_Object
+read_bit_vector (Lisp_Object readcharfun)
+{
+  unsigned_char_dynarr *dyn = Dynarr_new (unsigned_char);
+  Emchar c;
+
+  while (1)
+    {
+      c = readchar (readcharfun);
+      if (c != '0' && c != '1')
+	break;
+      Dynarr_add (dyn, (unsigned char) (c - '0'));
+    }
+
+  if (c >= 0)
+    unreadchar (readcharfun, c);
+
+  return make_bit_vector_from_byte_vector (Dynarr_atp (dyn, 0),
+					   Dynarr_length (dyn));
+}
+
+
+
+/* structures */
+
+struct structure_type *
+define_structure_type (Lisp_Object type,
+		       int (*validate) (Lisp_Object data,
+					Error_behavior errb),
+		       Lisp_Object (*instantiate) (Lisp_Object data))
+{
+  struct structure_type st;
+
+  st.type = type;
+  st.keywords = Dynarr_new (structure_keyword_entry);
+  st.validate = validate;
+  st.instantiate = instantiate;
+  Dynarr_add (the_structure_type_dynarr, st);
+
+  return Dynarr_atp (the_structure_type_dynarr,
+		     Dynarr_length (the_structure_type_dynarr) - 1);
+}
+
+void
+define_structure_type_keyword (struct structure_type *st, Lisp_Object keyword,
+			       int (*validate) (Lisp_Object keyword,
+						Lisp_Object value,
+						Error_behavior errb))
+{
+  struct structure_keyword_entry en;
+
+  en.keyword = keyword;
+  en.validate = validate;
+  Dynarr_add (st->keywords, en);
+}
+
+static struct structure_type *
+recognized_structure_type (Lisp_Object type)
+{
+  int i;
+
+  for (i = 0; i < Dynarr_length (the_structure_type_dynarr); i++)
+    {
+      struct structure_type *st = Dynarr_atp (the_structure_type_dynarr, i);
+      if (EQ (st->type, type))
+	return st;
+    }
+
+  return 0;
+}
+
+static Lisp_Object
+read_structure (Lisp_Object readcharfun)
+{
+  Emchar c = readchar (readcharfun);
+  Lisp_Object list = Qnil;
+  Lisp_Object orig_list = Qnil;
+  Lisp_Object already_seen = Qnil;
+  int keyword_count;
+  struct structure_type *st;
+  struct gcpro gcpro1, gcpro2;
+
+  GCPRO2 (orig_list, already_seen);
+  if (c != '(')
+    RETURN_UNGCPRO (continuable_syntax_error ("#s not followed by paren"));
+  list = read_list (readcharfun, ')', 0, 0);
+  orig_list = list;
+  {
+    int len = XINT (Flength (list));
+    if (len == 0)
+      RETURN_UNGCPRO (continuable_syntax_error
+		      ("structure type not specified"));
+    if (!(len & 1))
+      RETURN_UNGCPRO
+	(continuable_syntax_error
+	 ("structures must have alternating keyword/value pairs"));
+  }
+
+  st = recognized_structure_type (XCAR (list));
+  if (!st)
+    RETURN_UNGCPRO (Fsignal (Qinvalid_read_syntax,
+			     list2 (build_translated_string
+				    ("unrecognized structure type"),
+				    XCAR (list))));
+
+  list = Fcdr (list);
+  keyword_count = Dynarr_length (st->keywords);
+  while (!NILP (list))
+    {
+      Lisp_Object keyword, value;
+      int i;
+      struct structure_keyword_entry *en = NULL;
+
+      keyword = Fcar (list);
+      list = Fcdr (list);
+      value = Fcar (list);
+      list = Fcdr (list);
+
+      if (!NILP (memq_no_quit (keyword, already_seen)))
+	RETURN_UNGCPRO (Fsignal (Qinvalid_read_syntax,
+				 list2 (build_translated_string
+					("structure keyword already seen"),
+					keyword)));
+
+      for (i = 0; i < keyword_count; i++)
+	{
+	  en = Dynarr_atp (st->keywords, i);
+	  if (EQ (keyword, en->keyword))
+	    break;
+	}
+
+      if (i == keyword_count)
+	RETURN_UNGCPRO (Fsignal (Qinvalid_read_syntax,
+				   list2 (build_translated_string
+					  ("unrecognized structure keyword"),
+					  keyword)));
+
+      if (en->validate && ! (en->validate) (keyword, value, ERROR_ME))
+	RETURN_UNGCPRO
+	  (Fsignal (Qinvalid_read_syntax,
+		    list3 (build_translated_string
+			   ("invalid value for structure keyword"),
+			   keyword, value)));
+
+      already_seen = Fcons (keyword, already_seen);
+    }
+
+  if (st->validate && ! (st->validate) (orig_list, ERROR_ME))
+    RETURN_UNGCPRO (Fsignal (Qinvalid_read_syntax,
+			     list2 (build_translated_string
+				    ("invalid structure initializer"),
+				    orig_list)));
+
+  RETURN_UNGCPRO ((st->instantiate) (XCDR (orig_list)));
+}
+
+
+static Lisp_Object read_compiled_function (Lisp_Object readcharfun,
+					   int terminator);
+static Lisp_Object read_vector (Lisp_Object readcharfun, int terminator);
+
+/* Get the next character; filter out whitespace and comments */
+
+static Emchar
+reader_nextchar (Lisp_Object readcharfun)
+{
+  /* This function can GC */
+  Emchar c;
+
+ retry:
+  QUIT;
+  c = readchar (readcharfun);
+  if (c < 0)
+    signal_error (Qend_of_file, list1 (READCHARFUN_MAYBE (readcharfun)));
+
+  switch (c)
+    {
+    default:
+      {
+	/* Ignore whitespace and control characters */
+	if (c <= 040)
+	  goto retry;
+	return c;
+      }
+
+    case ';':
+      {
+        /* Comment */
+        while ((c = readchar (readcharfun)) >= 0 && c != '\n')
+          QUIT;
+        goto retry;
+      }
+    }
+}
+
+#if 0
+static Lisp_Object
+list2_pure (int pure, Lisp_Object a, Lisp_Object b)
+{
+  return pure ? pure_cons (a, pure_cons (b, Qnil)) : list2 (a, b);
+}
+#endif
+
+/* Read the next Lisp object from the stream READCHARFUN and return it.
+   If the return value is a cons whose car is Qunbound, then read1()
+   encountered a misplaced token (e.g. a right bracket, right paren,
+   or dot followed by a non-number).  To filter this stuff out,
+   use read0(). */
+
+static Lisp_Object
+read1 (Lisp_Object readcharfun)
+{
+  Emchar c;
+
+retry:
+  c = reader_nextchar (readcharfun);
+
+  switch (c)
+    {
+    case '(':
+      {
+#ifdef LISP_BACKQUOTES	/* old backquote compatibility in lisp reader */
+	/* if this is disabled, then other code in eval.c must be enabled */
+	Emchar ch = reader_nextchar (readcharfun);
+	switch (ch)
+	  {
+	  case '`':
+	    {
+	      Lisp_Object tem;
+	      int speccount = specpdl_depth ();
+	      ++old_backquote_flag;
+	      record_unwind_protect (backquote_unwind,
+				     make_opaque_ptr (&old_backquote_flag));
+	      tem = read0 (readcharfun);
+	      unbind_to (speccount, Qnil);
+	      ch = reader_nextchar (readcharfun);
+	      if (ch != ')')
+		{
+		  unreadchar (readcharfun, ch);
+		  return Fsignal (Qinvalid_read_syntax,
+				  list1 (build_string
+					 ("Weird old-backquote syntax")));
+		}
+	      return list2 (Qbacktick, tem);
+	    }
+	  case ',':
+	    {
+	      if (old_backquote_flag)
+		{
+		  Lisp_Object tem, comma_type;
+		  ch = readchar (readcharfun);
+		  if (ch == '@')
+		    comma_type = Qcomma_at;
+		  else
+		    {
+		      if (ch >= 0)
+			unreadchar (readcharfun, ch);
+		      comma_type = Qcomma;
+		    }
+		  tem = read0 (readcharfun);
+		  ch = reader_nextchar (readcharfun);
+		  if (ch != ')')
+		    {
+		      unreadchar (readcharfun, ch);
+		      return Fsignal (Qinvalid_read_syntax,
+				      list1 (build_string
+					     ("Weird old-backquote syntax")));
+		    }
+		  return list2 (comma_type, tem);
+		}
+	      else
+		{
+		  unreadchar (readcharfun, ch);
+#if 0
+		  return Fsignal (Qinvalid_read_syntax,
+		       list1 (build_string ("Comma outside of backquote")));
+#else
+		  /* #### - yuck....but this is reverse compatible. */
+		  /* mostly this is required by edebug, which does its own
+		     annotated reading.  We need to have an annotated_read
+		     function that records (with markers) the buffer
+		     positions of the elements that make up lists, then that
+		     can be used in edebug and bytecomp and the check above
+		     can go back in. --Stig */
+		  break;
+#endif
+		}
+	    }
+	  default:
+	    unreadchar (readcharfun, ch);
+	  }			/* switch(ch) */
+#endif /* old backquote crap... */
+	return read_list (readcharfun, ')', 1, 1);
+      }
+    case '[':
+      return read_vector (readcharfun, ']');
+
+    case ')':
+    case ']':
+      /* #### - huh? these don't do what they seem... */
+      return noseeum_cons (Qunbound, make_char (c));
+    case '.':
+      {
+#ifdef LISP_FLOAT_TYPE
+	/* If a period is followed by a number, then we should read it
+	   as a floating point number.  Otherwise, it denotes a dotted
+	   pair.
+	 */
+	c = readchar (readcharfun);
+	unreadchar (readcharfun, c);
+
+	/* Can't use isdigit on Emchars */
+	if (c < '0' || c > '9')
+	  return noseeum_cons (Qunbound, make_char ('.'));
+
+	/* Note that read_atom will loop
+	   at least once, assuring that we will not try to UNREAD
+           two characters in a row.
+	   (I think this doesn't matter anymore because there should
+	   be no more danger in unreading multiple characters) */
+        return read_atom (readcharfun, '.', 0);
+
+#else /* ! LISP_FLOAT_TYPE */
+	return noseeum_cons (Qunbound, make_char ('.'));
+#endif /* ! LISP_FLOAT_TYPE */
+      }
+
+    case '#':
+      {
+	c = readchar (readcharfun);
+	switch (c)
+	  {
+#if 0 /* FSFmacs silly char-table syntax */
+	  case '^':
+#endif
+#if 0 /* FSFmacs silly bool-vector syntax */
+	  case '&':
+#endif
+            /* "#["-- byte-code constant syntax */
+            /* purecons #[...] syntax */
+	  case '[': return read_compiled_function (readcharfun, ']'
+						   /*, purify_flag */ );
+            /* "#:"-- gensym syntax */
+	  case ':': return read_atom (readcharfun, -1, 1);
+            /* #'x => (function x) */
+	  case '\'': return list2 (Qfunction, read0 (readcharfun));
+#if 0
+	    /* RMS uses this syntax for fat-strings.
+	       If we use it for vectors, then obscure bugs happen.
+	     */
+            /* "#(" -- Scheme/CL vector syntax */
+	  case '(': return read_vector (readcharfun, ')');
+#endif
+#if 0 /* FSFmacs */
+	  case '(':
+	    {
+	      Lisp_Object tmp;
+	      struct gcpro gcpro1;
+
+	      /* Read the string itself.  */
+	      tmp = read1 (readcharfun);
+	      if (!STRINGP (tmp))
+		{
+		  if (CONSP (tmp) && UNBOUNDP (XCAR (tmp)))
+		    free_cons (XCONS (tmp));
+		  return Fsignal (Qinvalid_read_syntax,
+				   list1 (build_string ("#")));
+		}
+	      GCPRO1 (tmp);
+	      /* Read the intervals and their properties.  */
+	      while (1)
+		{
+		  Lisp_Object beg, end, plist;
+		  Emchar ch;
+		  int invalid = 0;
+
+		  beg = read1 (readcharfun);
+		  if (CONSP (beg) && UNBOUNDP (XCAR (beg)))
+		    {
+		      ch = XCHAR (XCDR (beg));
+		      free_cons (XCONS (beg));
+		      if (ch == ')')
+			break;
+		      else
+			invalid = 1;
+		    }
+		  if (!invalid)
+		    {
+		      end = read1 (readcharfun);
+		      if (CONSP (end) && UNBOUNDP (XCAR (end)))
+			{
+			  free_cons (XCONS (end));
+			  invalid = 1;
+			}
+		    }
+		  if (!invalid)
+		    {
+		      plist = read1 (readcharfun);
+		      if (CONSP (plist) && UNBOUNDP (XCAR (plist)))
+			{
+			  free_cons (XCONS (plist));
+			  invalid = 1;
+			}
+		    }
+		  if (invalid)
+		    RETURN_UNGCPRO
+		      (Fsignal (Qinvalid_read_syntax,
+				list2
+				(build_string ("invalid string property list"),
+				 XCDR (plist))));
+		  Fset_text_properties (beg, end, plist, tmp);
+		}
+	      UNGCPRO;
+	      return tmp;
+	    }
+#endif /* 0 */
+	  case '@':
+	    {
+	      /* #@NUMBER is used to skip NUMBER following characters.
+		 That's used in .elc files to skip over doc strings
+		 and function definitions.  */
+	      int i, nskip = 0;
+
+	      /* Read a decimal integer.  */
+	      while ((c = readchar (readcharfun)) >= 0
+		     && c >= '0' && c <= '9')
+                nskip = (10 * nskip) + (c - '0');
+	      if (c >= 0)
+		unreadchar (readcharfun, c);
+
+	      /* FSF has code here that maybe caches the skipped
+		 string.  See above for why this is totally
+		 losing.  We handle this differently. */
+
+	      /* Skip that many characters.  */
+	      for (i = 0; i < nskip && c >= 0; i++)
+		c = readchar (readcharfun);
+
+	      goto retry;
+	    }
+	  case '$': return Vload_file_name_internal;
+            /* bit vectors */
+	  case '*': return read_bit_vector (readcharfun);
+            /* #o10 => 8 -- octal constant syntax */
+	  case 'o': return read_integer (readcharfun, 8);
+            /* #xdead => 57005 -- hex constant syntax */
+	  case 'x': return read_integer (readcharfun, 16);
+            /* #b010 => 2 -- binary constant syntax */
+	  case 'b': return read_integer (readcharfun, 2);
+            /* #s(foobar key1 val1 key2 val2) -- structure syntax */
+	  case 's': return read_structure (readcharfun);
+	  case '<':
+	    {
+	      unreadchar (readcharfun, c);
+	      return Fsignal (Qinvalid_read_syntax,
+		    list1 (build_string ("Cannot read unreadable object")));
+	    }
+#ifdef FEATUREP_SYNTAX
+	  case '+':
+	  case '-':
+	    {
+	      Lisp_Object fexp, obj, tem;
+	      struct gcpro gcpro1, gcpro2;
+
+	      fexp = read0(readcharfun);
+	      obj = read0(readcharfun);
+
+	      /* the call to `featurep' may GC. */
+	      GCPRO2 (fexp, obj);
+	      tem = call1 (Qfeaturep, fexp);
+	      UNGCPRO;
+
+	      if (c == '+' &&  NILP(tem)) goto retry;
+	      if (c == '-' && !NILP(tem)) goto retry;
+	      return obj;
+	    }
+#endif
+	  case '0': case '1': case '2': case '3': case '4':
+	  case '5': case '6': case '7': case '8': case '9':
+	    /* Reader forms that can reuse previously read objects.  */
+	    {
+	      int n = 0;
+	      Lisp_Object found;
+
+	      /* Using read_integer() here is impossible, because it
+                 chokes on `='.  Using parse_integer() is too hard.
+                 So we simply read it in, and ignore overflows, which
+                 is safe.  */
+	      while (c >= '0' && c <= '9')
+		{
+		  n *= 10;
+		  n += c - '0';
+		  c = readchar (readcharfun);
+		}
+	      found = assq_no_quit (make_int (n), Vread_objects);
+	      if (c == '=')
+		{
+		  /* #n=object returns object, but associates it with
+		     n for #n#.  */
+		  Lisp_Object obj;
+		  if (CONSP (found))
+		    return Fsignal (Qinvalid_read_syntax,
+				    list2 (build_translated_string
+					   ("Multiply defined symbol label"),
+					   make_int (n)));
+		  obj = read0 (readcharfun);
+		  Vread_objects = Fcons (Fcons (make_int (n), obj),
+					 Vread_objects);
+		  return obj;
+		}
+	      else if (c == '#')
+		{
+		  /* #n# returns a previously read object.  */
+		  if (CONSP (found))
+		    return XCDR (found);
+		  else
+		    return Fsignal (Qinvalid_read_syntax,
+				    list2 (build_translated_string
+					   ("Undefined symbol label"),
+					   make_int (n)));
+		}
+	      return Fsignal (Qinvalid_read_syntax,
+			      list1 (build_string ("#")));
+	    }
+	  default:
+	    {
+	      unreadchar (readcharfun, c);
+	      return Fsignal (Qinvalid_read_syntax,
+			      list1 (build_string ("#")));
+	    }
+	  }
+      }
+
+      /* Quote */
+    case '\'': return list2 (Qquote, read0 (readcharfun));
+
+#ifdef LISP_BACKQUOTES
+    case '`':
+      {
+	Lisp_Object tem;
+	int speccount = specpdl_depth ();
+	++new_backquote_flag;
+	record_unwind_protect (backquote_unwind,
+			       make_opaque_ptr (&new_backquote_flag));
+	tem = read0 (readcharfun);
+	unbind_to (speccount, Qnil);
+	return list2 (Qbackquote, tem);
+      }
+
+    case ',':
+      {
+	if (new_backquote_flag)
+	  {
+	    Lisp_Object comma_type = Qnil;
+	    int ch = readchar (readcharfun);
+
+	    if (ch == '@')
+	      comma_type = Qcomma_at;
+	    else if (ch == '.')
+	      comma_type = Qcomma_dot;
+	    else
+	      {
+		if (ch >= 0)
+		  unreadchar (readcharfun, ch);
+		comma_type = Qcomma;
+	      }
+	    return list2 (comma_type, read0 (readcharfun));
+	  }
+	else
+	  {
+	    /* YUCK.  99.999% backwards compatibility.  The Right
+	       Thing(tm) is to signal an error here, because it's
+	       really invalid read syntax.  Instead, this permits
+	       commas to begin symbols (unless they're inside
+	       backquotes).  If an error is signalled here in the
+	       future, then commas should be invalid read syntax
+	       outside of backquotes anywhere they're found (i.e.
+	       they must be quoted in symbols) -- Stig */
+	    return read_atom (readcharfun, c, 0);
+	  }
+      }
+#endif
+
+    case '?':
+      {
+	/* Evil GNU Emacs "character" (ie integer) syntax */
+	c = readchar (readcharfun);
+	if (c < 0)
+	  return Fsignal (Qend_of_file, list1 (READCHARFUN_MAYBE (readcharfun)));
+
+	if (c == '\\')
+	  c = read_escape (readcharfun);
+	return make_char (c);
+      }
+
+    case '\"':
+      {
+	/* String */
+#ifdef I18N3
+	/* #### If the input stream is translating, then the string
+	   should be marked as translatable by setting its
+	   `string-translatable' property to t.  .el and .elc files
+	   normally are translating input streams.  See Fgettext()
+	   and print_internal(). */
+#endif
+	int cancel = 0;
+
+	Lstream_rewind (XLSTREAM (Vread_buffer_stream));
+	while ((c = readchar (readcharfun)) >= 0
+	       && c != '\"')
+	  {
+	    if (c == '\\')
+	      c = read_escape (readcharfun);
+	    /* c is -1 if \ newline has just been seen */
+	    if (c == -1)
+	      {
+		if (Lstream_byte_count (XLSTREAM (Vread_buffer_stream)) == 0)
+		  cancel = 1;
+	      }
+	    else
+	      Lstream_put_emchar (XLSTREAM (Vread_buffer_stream), c);
+	    QUIT;
+	  }
+	if (c < 0)
+	  return Fsignal (Qend_of_file, list1 (READCHARFUN_MAYBE (readcharfun)));
+
+	/* If purifying, and string starts with \ newline,
+	   return zero instead.  This is for doc strings
+	   that we are really going to find in lib-src/DOC.nn.nn  */
+	if (purify_flag && NILP (Vinternal_doc_file_name) && cancel)
+	  return Qzero;
+
+	Lstream_flush (XLSTREAM (Vread_buffer_stream));
+	return
+	  make_string
+	  (resizing_buffer_stream_ptr (XLSTREAM (Vread_buffer_stream)),
+	   Lstream_byte_count (XLSTREAM (Vread_buffer_stream)));
+      }
+
+    default:
+      {
+	/* Ignore whitespace and control characters */
+	if (c <= 040)
+	  goto retry;
+	return read_atom (readcharfun, c, 0);
+      }
+    }
+}
+
+
+
+#ifdef LISP_FLOAT_TYPE
+
+#define LEAD_INT 1
+#define DOT_CHAR 2
+#define TRAIL_INT 4
+#define E_CHAR 8
+#define EXP_INT 16
+
+int
+isfloat_string (CONST char *cp)
+{
+  int state = 0;
+  CONST Bufbyte *ucp = (CONST Bufbyte *) cp;
+
+  if (*ucp == '+' || *ucp == '-')
+    ucp++;
+
+  if (*ucp >= '0' && *ucp <= '9')
+    {
+      state |= LEAD_INT;
+      while (*ucp >= '0' && *ucp <= '9')
+	ucp++;
+    }
+  if (*ucp == '.')
+    {
+      state |= DOT_CHAR;
+      ucp++;
+    }
+  if (*ucp >= '0' && *ucp <= '9')
+    {
+      state |= TRAIL_INT;
+      while (*ucp >= '0' && *ucp <= '9')
+	ucp++;
+    }
+  if (*ucp == 'e' || *ucp == 'E')
+    {
+      state |= E_CHAR;
+      ucp++;
+      if ((*ucp == '+') || (*ucp == '-'))
+	ucp++;
+    }
+
+  if (*ucp >= '0' && *ucp <= '9')
+    {
+      state |= EXP_INT;
+      while (*ucp >= '0' && *ucp <= '9')
+	ucp++;
+    }
+  return (((*ucp == 0) || (*ucp == ' ') || (*ucp == '\t') || (*ucp == '\n')
+	   || (*ucp == '\r') || (*ucp == '\f'))
+	  && (state == (LEAD_INT|DOT_CHAR|TRAIL_INT)
+	      || state == (DOT_CHAR|TRAIL_INT)
+	      || state == (LEAD_INT|E_CHAR|EXP_INT)
+	      || state == (LEAD_INT|DOT_CHAR|TRAIL_INT|E_CHAR|EXP_INT)
+	      || state == (DOT_CHAR|TRAIL_INT|E_CHAR|EXP_INT)));
+}
+#endif /* LISP_FLOAT_TYPE */
+
+static void *
+sequence_reader (Lisp_Object readcharfun,
+                 Emchar terminator,
+                 void *state,
+                 void * (*conser) (Lisp_Object readcharfun,
+                                   void *state, Charcount len))
+{
+  Charcount len;
+
+  for (len = 0; ; len++)
+    {
+      Emchar ch;
+
+      QUIT;
+      ch = reader_nextchar (readcharfun);
+
+      if (ch == terminator)
+	return state;
+      else
+	unreadchar (readcharfun, ch);
+#ifdef FEATUREP_SYNTAX
+      if (ch == ']')
+	syntax_error ("\"]\" in a list");
+      else if (ch == ')')
+	syntax_error ("\")\" in a vector");
+#endif
+      state = ((conser) (readcharfun, state, len));
+    }
+}
+
+
+struct read_list_state
+  {
+    Lisp_Object head;
+    Lisp_Object tail;
+    int length;
+    int allow_dotted_lists;
+    Emchar terminator;
+  };
+
+static void *
+read_list_conser (Lisp_Object readcharfun, void *state, Charcount len)
+{
+  struct read_list_state *s = (struct read_list_state *) state;
+  Lisp_Object elt;
+
+  elt = read1 (readcharfun);
+
+  if (CONSP (elt) && UNBOUNDP (XCAR (elt)))
+    {
+      Lisp_Object tem = elt;
+      Emchar ch;
+
+      elt = XCDR (elt);
+      free_cons (XCONS (tem));
+      tem = Qnil;
+      ch = XCHAR (elt);
+#ifdef FEATUREP_SYNTAX
+      if (ch == s->terminator) /* deal with #+, #- reader macros */
+	{
+	  unreadchar (readcharfun, s->terminator);
+	  goto done;
+	}
+      else if (ch == ']')
+	syntax_error ("']' in a list");
+      else if (ch == ')')
+	syntax_error ("')' in a vector");
+      else
+#endif
+      if (ch != '.')
+	signal_simple_error ("BUG! Internal reader error", elt);
+      else if (!s->allow_dotted_lists)
+	syntax_error ("\".\" in a vector");
+      else
+	{
+	  if (!NILP (s->tail))
+	    XCDR (s->tail) = read0 (readcharfun);
+          else
+	    s->head = read0 (readcharfun);
+	  elt = read1 (readcharfun);
+	  if (CONSP (elt) && UNBOUNDP (XCAR (elt)))
+	    {
+	      ch = XCHAR (XCDR (elt));
+	      free_cons (XCONS (elt));
+	      if (ch == s->terminator)
+		{
+		  unreadchar (readcharfun, s->terminator);
+		  goto done;
+		}
+	    }
+	  syntax_error (". in wrong context");
+	}
+    }
+
+#if 0 /* FSFmacs defun hack, or something ... */
+  if (NILP (tail) && defun_hack && EQ (elt, Qdefun) && !read_pure)
+    {
+      record_unwind_protect (unreadpure, Qzero);
+      read_pure = 1;
+    }
+#endif
+
+#ifdef COMPILED_FUNCTION_ANNOTATION_HACK
+  if (s->length == 1 && s->allow_dotted_lists && EQ (XCAR (s->head), Qfset))
+    {
+      if (CONSP (elt) && EQ (XCAR (elt), Qquote) && CONSP (XCDR (elt)))
+	Vcurrent_compiled_function_annotation = XCAR (XCDR (elt));
+      else
+	Vcurrent_compiled_function_annotation = elt;
+    }
+#endif
+
+  elt = Fcons (elt, Qnil);
+  if (!NILP (s->tail))
+    XCDR (s->tail) = elt;
+  else
+    s->head = elt;
+  s->tail = elt;
+ done:
+  s->length++;
+  return s;
+}
+
+
+#if 0 /* FSFmacs defun hack */
+/* -1 for allow_dotted_lists means allow_dotted_lists and check
+   for starting with defun and make structure pure. */
+#endif
+
+static Lisp_Object
+read_list (Lisp_Object readcharfun,
+           Emchar terminator,
+           int allow_dotted_lists,
+	   int check_for_doc_references)
+{
+  struct read_list_state s;
+  struct gcpro gcpro1, gcpro2;
+#ifdef COMPILED_FUNCTION_ANNOTATION_HACK
+  Lisp_Object old_compiled_function_annotation =
+    Vcurrent_compiled_function_annotation;
+#endif
+
+  s.head = Qnil;
+  s.tail = Qnil;
+  s.length = 0;
+  s.allow_dotted_lists = allow_dotted_lists;
+  s.terminator = terminator;
+  GCPRO2 (s.head, s.tail);
+
+  sequence_reader (readcharfun, terminator, &s, read_list_conser);
+#ifdef COMPILED_FUNCTION_ANNOTATION_HACK
+  Vcurrent_compiled_function_annotation = old_compiled_function_annotation;
+#endif
+
+  if ((purify_flag || load_force_doc_strings) && check_for_doc_references)
+    {
+      /* check now for any doc string references and record them
+	 for later. */
+      Lisp_Object tail;
+
+      /* We might be dealing with an imperfect list so don't
+	 use LIST_LOOP */
+      for (tail = s.head; CONSP (tail); tail = XCDR (tail))
+	{
+	  Lisp_Object holding_cons = Qnil;
+
+	  {
+	    Lisp_Object elem = XCAR (tail);
+	    /* elem might be (#$ . INT) ... */
+	    if (CONSP (elem) && EQ (XCAR (elem), Vload_file_name_internal))
+	      holding_cons = tail;
+	    /* or it might be (quote (#$ . INT)) i.e.
+	       (quote . ((#$ . INT) . nil)) in the case of
+	       `autoload' (autoload evaluates its arguments, while
+	       `defvar', `defun', etc. don't). */
+	    if (CONSP (elem) && EQ (XCAR (elem), Qquote)
+		&& CONSP (XCDR (elem)))
+	      {
+		elem = XCAR (XCDR (elem));
+		if (CONSP (elem) && EQ (XCAR (elem), Vload_file_name_internal))
+		  holding_cons = XCDR (XCAR (tail));
+	      }
+	  }
+
+	  if (CONSP (holding_cons))
+	    {
+	      if (purify_flag)
+		{
+		  if (NILP (Vinternal_doc_file_name))
+		    /* We have not yet called Snarf-documentation, so
+		       assume this file is described in the DOC file
+		       and Snarf-documentation will fill in the right
+		       value later.  For now, replace the whole list
+		       with 0.  */
+		    XCAR (holding_cons) = Qzero;
+		  else
+		    /* We have already called Snarf-documentation, so
+		       make a relative file name for this file, so it
+		       can be found properly in the installed Lisp
+		       directory.  We don't use Fexpand_file_name
+		       because that would make the directory absolute
+		       now.  */
+		    XCAR (XCAR (holding_cons)) =
+		      concat2 (build_string ("../lisp/"),
+			       Ffile_name_nondirectory
+			       (Vload_file_name_internal));
+		}
+	      else
+		/* Not pure.  Just add to Vload_force_doc_string_list,
+		   and the string will be filled in properly in
+		   load_force_doc_string_unwind(). */
+		Vload_force_doc_string_list =
+		  /* We pass the cons that holds the (#$ . INT) so we
+		     can modify it in-place. */
+		  Fcons (holding_cons, Vload_force_doc_string_list);
+	    }
+	}
+    }
+
+  UNGCPRO;
+  return s.head;
+}
+
+static Lisp_Object
+read_vector (Lisp_Object readcharfun,
+             Emchar terminator)
+{
+  Lisp_Object tem;
+  Lisp_Object *p;
+  int len;
+  int i;
+  struct read_list_state s;
+  struct gcpro gcpro1, gcpro2;
+
+  s.head = Qnil;
+  s.tail = Qnil;
+  s.length = 0;
+  s.allow_dotted_lists = 0;
+  GCPRO2 (s.head, s.tail);
+
+  sequence_reader (readcharfun, terminator, &s, read_list_conser);
+
+  UNGCPRO;
+  tem = s.head;
+  len = XINT (Flength (tem));
+
+#if 0 /* FSFmacs defun hack */
+  if (read_pure)
+    s.head = make_pure_vector (len, Qnil);
+  else
+#endif
+    s.head = make_vector (len, Qnil);
+
+  for (i = 0, p = &(XVECTOR_DATA (s.head)[0]);
+       i < len;
+       i++, p++)
+  {
+    struct Lisp_Cons *otem = XCONS (tem);
+    tem = Fcar (tem);
+    *p = tem;
+    tem = otem->cdr;
+    free_cons (otem);
+  }
+  return s.head;
+}
+
+static Lisp_Object
+read_compiled_function (Lisp_Object readcharfun, Emchar terminator)
+{
+  /* Accept compiled functions at read-time so that we don't
+     have to build them at load-time. */
+  Lisp_Object stuff;
+  Lisp_Object make_byte_code_args[COMPILED_DOMAIN + 1];
+  struct gcpro gcpro1;
+  int len;
+  int iii;
+  int saw_a_doc_ref = 0;
+
+  /* Note: we tell read_list not to search for doc references
+     because we need to handle the "doc reference" for the
+     instructions and constants differently. */
+  stuff = read_list (readcharfun, terminator, 0, 0);
+  len = XINT (Flength (stuff));
+  if (len < COMPILED_STACK_DEPTH + 1 || len > COMPILED_DOMAIN + 1)
+    return
+      continuable_syntax_error ("#[...] used with wrong number of elements");
+
+  for (iii = 0; CONSP (stuff); iii++)
+    {
+      struct Lisp_Cons *victim = XCONS (stuff);
+      make_byte_code_args[iii] = Fcar (stuff);
+      if ((purify_flag || load_force_doc_strings)
+	   && CONSP (make_byte_code_args[iii])
+	  && EQ (XCAR (make_byte_code_args[iii]), Vload_file_name_internal))
+	{
+	  if (purify_flag && iii == COMPILED_DOC_STRING)
+	    {
+	      /* same as in read_list(). */
+	      if (NILP (Vinternal_doc_file_name))
+		make_byte_code_args[iii] = Qzero;
+	      else
+		XCAR (make_byte_code_args[iii]) =
+		  concat2 (build_string ("../lisp/"),
+			   Ffile_name_nondirectory
+			   (Vload_file_name_internal));
+	    }
+	  else
+	    saw_a_doc_ref = 1;
+	}
+      stuff = Fcdr (stuff);
+      free_cons (victim);
+    }
+  GCPRO1 (make_byte_code_args[0]);
+  gcpro1.nvars = len;
+
+  /* v18 or v19 bytecode file.  Need to Ebolify. */
+  if (load_byte_code_version < 20 && VECTORP (make_byte_code_args[2]))
+    ebolify_bytecode_constants (make_byte_code_args[2]);
+
+  /* make-byte-code looks at purify_flag, which should have the same
+   *  value as our "read-pure" argument */
+  stuff = Fmake_byte_code (len, make_byte_code_args);
+  XCOMPILED_FUNCTION (stuff)->flags.ebolified = (load_byte_code_version < 20);
+  if (saw_a_doc_ref)
+    Vload_force_doc_string_list = Fcons (stuff, Vload_force_doc_string_list);
+  UNGCPRO;
+  return stuff;
+}
+
+
+
+void
+init_lread (void)
+{
+  Vvalues = Qnil;
+
+  load_in_progress = 0;
+
+  Vload_descriptor_list = Qnil;
+
+  /* kludge: locate-file does not work for a null load-path, even if
+     the file name is absolute. */
+
+  Vload_path = Fcons (build_string (""), Qnil);
+
+  /* This used to get initialized in init_lread because all streams
+     got closed when dumping occurs.  This is no longer true --
+     Vread_buffer_stream is a resizing output stream, and there is no
+     reason to close it at dump-time.
+
+     Vread_buffer_stream is set to Qnil in vars_of_lread, and this
+     will initialize it only once, at dump-time.  */
+  if (NILP (Vread_buffer_stream))
+    Vread_buffer_stream = make_resizing_buffer_output_stream ();
+
+  Vload_force_doc_string_list = Qnil;
+}
+
+void
+syms_of_lread (void)
+{
+  DEFSUBR (Fread);
+  DEFSUBR (Fread_from_string);
+  DEFSUBR (Fload_internal);
+  DEFSUBR (Flocate_file);
+  DEFSUBR (Flocate_file_clear_hashing);
+  DEFSUBR (Feval_buffer);
+  DEFSUBR (Feval_region);
+
+  defsymbol (&Qstandard_input, "standard-input");
+  defsymbol (&Qread_char, "read-char");
+  defsymbol (&Qcurrent_load_list, "current-load-list");
+  defsymbol (&Qload, "load");
+  defsymbol (&Qload_file_name, "load-file-name");
+  defsymbol (&Qfset, "fset");
+
+#ifdef LISP_BACKQUOTES
+  defsymbol (&Qbackquote, "backquote");
+  defsymbol (&Qbacktick, "`");
+  defsymbol (&Qcomma, ",");
+  defsymbol (&Qcomma_at, ",@");
+  defsymbol (&Qcomma_dot, ",.");
+#endif
+
+  defsymbol (&Qexists, "exists");
+  defsymbol (&Qreadable, "readable");
+  defsymbol (&Qwritable, "writable");
+  defsymbol (&Qexecutable, "executable");
+}
+
+void
+structure_type_create (void)
+{
+  the_structure_type_dynarr = Dynarr_new (structure_type);
+}
+
+void
+reinit_vars_of_lread (void)
+{
+  Vread_buffer_stream = Qnil;
+  staticpro_nodump (&Vread_buffer_stream);
+}
+
+void
+vars_of_lread (void)
+{
+  reinit_vars_of_lread ();
+
+  DEFVAR_LISP ("values", &Vvalues /*
+List of values of all expressions which were read, evaluated and printed.
+Order is reverse chronological.
+*/ );
+
+  DEFVAR_LISP ("standard-input", &Vstandard_input /*
+Stream for read to get input from.
+See documentation of `read' for possible values.
+*/ );
+  Vstandard_input = Qt;
+
+  DEFVAR_LISP ("load-path", &Vload_path /*
+*List of directories to search for files to load.
+Each element is a string (directory name) or nil (try default directory).
+
+Note that the elements of this list *may not* begin with "~", so you must
+call `expand-file-name' on them before adding them to this list.
+
+Initialized based on EMACSLOADPATH environment variable, if any,
+otherwise to default specified in by file `paths.h' when XEmacs was built.
+If there were no paths specified in `paths.h', then XEmacs chooses a default
+value for this variable by looking around in the file-system near the
+directory in which the XEmacs executable resides.
+*/ );
+  Vload_path = Qnil;
+
+/*  xxxDEFVAR_LISP ("dump-load-path", &Vdump_load_path,
+    "*Location of lisp files to be used when dumping ONLY."); */
+
+  DEFVAR_BOOL ("load-in-progress", &load_in_progress /*
+Non-nil iff inside of `load'.
+*/ );
+
+  DEFVAR_LISP ("after-load-alist", &Vafter_load_alist /*
+An alist of expressions to be evalled when particular files are loaded.
+Each element looks like (FILENAME FORMS...).
+When `load' is run and the file-name argument is FILENAME,
+the FORMS in the corresponding element are executed at the end of loading.
+
+FILENAME must match exactly!  Normally FILENAME is the name of a library,
+with no directory specified, since that is how `load' is normally called.
+An error in FORMS does not undo the load,
+but does prevent execution of the rest of the FORMS.
+*/ );
+  Vafter_load_alist = Qnil;
+
+  DEFVAR_BOOL ("load-warn-when-source-newer", &load_warn_when_source_newer /*
+*Whether `load' should check whether the source is newer than the binary.
+If this variable is true, then when a `.elc' file is being loaded and the
+corresponding `.el' is newer, a warning message will be printed.
+*/ );
+  load_warn_when_source_newer = 0;
+
+  DEFVAR_BOOL ("load-warn-when-source-only", &load_warn_when_source_only /*
+*Whether `load' should warn when loading a `.el' file instead of an `.elc'.
+If this variable is true, then when `load' is called with a filename without
+an extension, and the `.elc' version doesn't exist but the `.el' version does,
+then a message will be printed.  If an explicit extension is passed to `load',
+no warning will be printed.
+*/ );
+  load_warn_when_source_only = 0;
+
+  DEFVAR_BOOL ("load-ignore-elc-files", &load_ignore_elc_files /*
+*Whether `load' should ignore `.elc' files when a suffix is not given.
+This is normally used only to bootstrap the `.elc' files when building XEmacs.
+*/ );
+  load_ignore_elc_files = 0;
+
+#ifdef LOADHIST
+  DEFVAR_LISP ("load-history", &Vload_history /*
+Alist mapping source file names to symbols and features.
+Each alist element is a list that starts with a file name,
+except for one element (optional) that starts with nil and describes
+definitions evaluated from buffers not visiting files.
+The remaining elements of each list are symbols defined as functions
+or variables, and cons cells `(provide . FEATURE)' and `(require . FEATURE)'.
+*/ );
+  Vload_history = Qnil;
+
+  DEFVAR_LISP ("current-load-list", &Vcurrent_load_list /*
+Used for internal purposes by `load'.
+*/ );
+  Vcurrent_load_list = Qnil;
+#endif
+
+  DEFVAR_LISP ("load-file-name", &Vload_file_name /*
+Full name of file being loaded by `load'.
+*/ );
+  Vload_file_name = Qnil;
+
+  DEFVAR_LISP ("load-read-function", &Vload_read_function /*
+Function used by `load' and `eval-region' for reading expressions.
+The default is nil, which means use the function `read'.
+*/ );
+  Vload_read_function = Qnil;
+
+  DEFVAR_BOOL ("load-force-doc-strings", &load_force_doc_strings /*
+Non-nil means `load' should force-load all dynamic doc strings.
+This is useful when the file being loaded is a temporary copy.
+*/ );
+  load_force_doc_strings = 0;
+
+  /* See read_escape().  */
+#if 0
+  /* Used to be named `puke-on-fsf-keys' */
+  DEFVAR_BOOL ("fail-on-bucky-bit-character-escapes",
+	       &fail_on_bucky_bit_character_escapes /*
+Whether `read' should signal an error when it encounters unsupported
+character escape syntaxes or just read them incorrectly.
+*/ );
+  fail_on_bucky_bit_character_escapes = 0;
+#endif
+
+  /* This must be initialized in init_lread otherwise it may start out
+     with values saved when the image is dumped. */
+  staticpro (&Vload_descriptor_list);
+
+  /* Initialized in init_lread. */
+  staticpro (&Vload_force_doc_string_list);
+
+  Vload_file_name_internal = Qnil;
+  staticpro (&Vload_file_name_internal);
+
+  Vload_file_name_internal_the_purecopy = Qnil;
+  staticpro (&Vload_file_name_internal_the_purecopy);
+
+#ifdef COMPILED_FUNCTION_ANNOTATION_HACK
+  Vcurrent_compiled_function_annotation = Qnil;
+  staticpro (&Vcurrent_compiled_function_annotation);
+#endif
+
+  /* So that early-early stuff will work */
+  Ffset (Qload, intern ("load-internal"));
+
+#ifdef FEATUREP_SYNTAX
+  defsymbol (&Qfeaturep, "featurep");
+  Fprovide(intern("xemacs"));
+#ifdef INFODOCK
+  Fprovide(intern("infodock"));
+#endif /* INFODOCK */
+#endif /* FEATUREP_SYNTAX */
+
+#ifdef LISP_BACKQUOTES
+  old_backquote_flag = new_backquote_flag = 0;
+#endif
+
+#ifdef I18N3
+  Vfile_domain = Qnil;
+#endif
+
+  Vread_objects = Qnil;
+  staticpro (&Vread_objects);
+
+  Vlocate_file_hash_table = make_lisp_hash_table (200,
+						  HASH_TABLE_NON_WEAK,
+						  HASH_TABLE_EQUAL);
+  staticpro (&Vlocate_file_hash_table);
+#ifdef DEBUG_XEMACS
+  symbol_value (XSYMBOL (intern ("Vlocate-file-hash-table")))
+    = Vlocate_file_hash_table;
+#endif
+}