changeset 5177:b785049378e3

merge
author Ben Wing <ben@xemacs.org>
date Tue, 23 Feb 2010 07:28:35 -0600
parents 8b2f75cecb89 (current diff) cc74f60c150e (diff)
children 97eb4942aec8
files etc/ChangeLog etc/dbxrc.in lisp/ChangeLog lisp/dumped-lisp.el src/ChangeLog src/Makefile.in.in src/lrecord.h tests/ChangeLog tests/automated/test-harness.el
diffstat 24 files changed, 1762 insertions(+), 1245 deletions(-) [+]
line wrap: on
line diff
--- a/etc/ChangeLog	Mon Feb 22 06:49:30 2010 -0600
+++ b/etc/ChangeLog	Tue Feb 23 07:28:35 2010 -0600
@@ -3,6 +3,11 @@
 	* dbxrc.in:
 	Rename objects.c -> fontcolor.c.
 
+2010-02-22  Ben Wing  <ben@xemacs.org>
+
+	* dbxrc.in:
+	test-harness.el is in lisp directory now.
+
 2010-01-28  Jerry James  <james@xemacs.org>
 
 	* tests/external-widget/Makefile: Add copyright and license
--- a/etc/dbxrc.in	Mon Feb 22 06:49:30 2010 -0600
+++ b/etc/dbxrc.in	Tue Feb 23 07:28:35 2010 -0600
@@ -4,6 +4,7 @@
 ## The generated file depends on src/config.h (currently only in one place).
 
 ## Copyright (C) 1998 Free Software Foundation, Inc.
+## Copyright (C) 2010 Ben Wing.
 
 ## This file is part of XEmacs.
 
@@ -194,7 +195,7 @@
 end
 
 function check-xemacs {
-  run -batch -l ../tests/automated/test-harness.el -f batch-test-emacs ../tests/automated
+  run -batch -l test-harness -f batch-test-emacs ../tests/automated
 }
 
 document check-temacs << 'end'
@@ -205,7 +206,7 @@
 end
 
 function check-temacs {
-  run-temacs -q -batch -l ../tests/automated/test-harness.el -f batch-test-emacs ../tests/automated
+  run-temacs -q -batch -l test-harness -f batch-test-emacs ../tests/automated
 }
 
 document update-elc << 'end'
--- a/lisp/ChangeLog	Mon Feb 22 06:49:30 2010 -0600
+++ b/lisp/ChangeLog	Tue Feb 23 07:28:35 2010 -0600
@@ -46,6 +46,171 @@
 	put categories in alphabetical order, move remaning "misc"
 	stuff to bottom.
 
+2010-02-23  Ben Wing  <ben@xemacs.org>
+
+	* help.el: fux typo in comment. (oops)
+
+2010-02-23  Ben Wing  <ben@xemacs.org>
+
+	* autoload.el:
+	* autoload.el (make-autoload):
+	* cl-macs.el (cl-function-arglist):
+	* cl-macs.el (cl-transform-lambda):
+	Don't add argument list with the tag "Common Lisp lambda list:";
+	instead add in "standard" form using "arguments:" and omitting the
+	function name.  Add an arg to `cl-function-arglist' to omit the
+	name and use it in autoload.el instead of just hacking it off.
+	
+	* help.el:
+	* help.el (function-arglist):
+	* help.el (function-documentation-1): New.
+	Extract out common code to recognize and/or strip the arglist from
+	documentation into `function-documentation-1'.  Use in
+	`function-arglist' and `function-documentation'.  Modify
+	`function-arglist' so it looks for the `arguments: ' stuff in all
+	doc strings, not just subrs/autoloads, so that CL functions get
+	recognized properly.  Change the regexp used to match "arguments: "
+	specs to allow nested parens inside the arg list (happens when you
+	have a default value specified in a CL arglist).
+
+2010-02-22  Ben Wing  <ben@xemacs.org>
+
+	* test-harness.el:
+	* test-harness.el (test-harness-from-buffer):
+	* test-harness.el (batch-test-emacs):
+	Move file from tests/automated into lisp/ so it gets
+	byte-compiled.  This significantly reduces the amount of extra
+	crap in outputted backtraces.  Delete hack in batch-test-emacs to
+	look for test-harness.el in the test directory since it's not there
+	any more.
+
+	Also, in `Check-Message', incorporate call to `Skip-Test-Unless'
+	in the macro output rather than its body, to avoid problems byte-
+	compiling the file -- `Skip-Test-Unless' isn't available in the
+	environment during byte-compilation so we can't call it then.
+	
+2010-02-22  Ben Wing  <ben@xemacs.org>
+
+	* mule/make-coding-system.el:
+	* mule/make-coding-system.el (fixed-width-generate-helper):
+	* mule/make-coding-system.el (fixed-width-private-use-start): Removed.
+	* mule/make-coding-system.el (fixed-width-create-decode-encode-tables):
+	* coding.el:
+	* coding.el (decode-char): New.
+	* coding.el (featurep):
+	* coding.el (encode-char): New.
+	* dumped-lisp.el (preloaded-file-list):
+	Dump make-coding-system.  Aidan's hack to avoid dumping this file
+	never really worked right -- with some configurations (not clear
+	exactly which ones) `make-coding-system.el' gets dumped anyway due to
+	calls to `make-coding-system' in unicode.el, with the result that
+	the documentation of functions in make-coding-system.el gets lost.
+
+	Also needed to remove defvar fixed-width-private-use-start and
+	incorporate it inline, due to bootstrapping issues -- the call to
+	decode-char introduced a cross-dependency between unicode.el and
+	make-coding-system.el.
+	
+
+2010-02-22  Ben Wing  <ben@xemacs.org>
+
+	* cl-seq.el:
+	* cl-seq.el (stable-union): New.
+	* cl-seq.el (stable-intersection): New.
+	New functions to do stable set operations, i.e. preserve the order
+	of the elements in the argument lists, and prefer LIST1 over LIST2
+	when ordering the combined result.  The result looks as much like
+	LIST1 as possible, followed (in the case of `stable-union') by
+	any necessary elements from LIST2, in order.  This is contrary to
+	`union' and `intersection', which are not required to be order-
+	preserving and are not -- they prefer LIST2 and output results in
+	backwards order.
+
+2010-02-22  Ben Wing  <ben@xemacs.org>
+
+	* cl-seq.el:
+	* cl-seq.el (reduce):
+	* cl-seq.el (fill):
+	* cl-seq.el (replace):
+	* cl-seq.el (remove*):
+	* cl-seq.el (remove-if):
+	* cl-seq.el (remove-if-not):
+	* cl-seq.el (delete*):
+	* cl-seq.el (delete-if):
+	* cl-seq.el (delete-if-not):
+	* cl-seq.el (remove-duplicates):
+	* cl-seq.el (delete-duplicates):
+	* cl-seq.el (substitute):
+	* cl-seq.el (substitute-if):
+	* cl-seq.el (substitute-if-not):
+	* cl-seq.el (nsubstitute):
+	* cl-seq.el (nsubstitute-if):
+	* cl-seq.el (nsubstitute-if-not):
+	* cl-seq.el (find):
+	* cl-seq.el (find-if):
+	* cl-seq.el (find-if-not):
+	* cl-seq.el (position):
+	* cl-seq.el (position-if):
+	* cl-seq.el (position-if-not):
+	* cl-seq.el (count):
+	* cl-seq.el (count-if):
+	* cl-seq.el (count-if-not):
+	* cl-seq.el (mismatch):
+	* cl-seq.el (search):
+	* cl-seq.el (sort*):
+	* cl-seq.el (stable-sort):
+	* cl-seq.el (merge):
+	* cl-seq.el (member*):
+	* cl-seq.el (member-if):
+	* cl-seq.el (member-if-not):
+	* cl-seq.el (assoc*):
+	* cl-seq.el (assoc-if):
+	* cl-seq.el (assoc-if-not):
+	* cl-seq.el (rassoc*):
+	* cl-seq.el (rassoc-if):
+	* cl-seq.el (rassoc-if-not):
+	* cl-seq.el (union):
+	* cl-seq.el (nunion):
+	* cl-seq.el (intersection):
+	* cl-seq.el (nintersection):
+	* cl-seq.el (set-difference):
+	* cl-seq.el (nset-difference):
+	* cl-seq.el (set-exclusive-or):
+	* cl-seq.el (nset-exclusive-or):
+	* cl-seq.el (subsetp):
+	* cl-seq.el (subst-if):
+	* cl-seq.el (subst-if-not):
+	* cl-seq.el (nsubst):
+	* cl-seq.el (nsubst-if):
+	* cl-seq.el (nsubst-if-not):
+	* cl-seq.el (sublis):
+	* cl-seq.el (nsublis):
+	* cl-seq.el (tree-equal):
+	* cl-seq.el (cl-tree-equal-rec):
+	* cl.el:
+	* cl.el (pushnew):
+	* cl.el (adjoin):
+	* cl.el (subst):
+	Document the keywords to the various sequence/list functions.
+
+2010-02-21  Ben Wing  <ben@xemacs.org>
+
+	* diagnose.el:
+	* diagnose.el (show-object-memory-usage-stats):
+	Fix errors preventing this from working properly, account for
+	words like "entry" pluralized to "entries".
+
+2010-02-22  Aidan Kehoe  <kehoea@parhasard.net>
+
+	* cl-extra.el (constantly): 
+	Add this function, from ANSI Common Lisp, using the SBCL extension
+	that extra arguments to it are passed back as multiple values in
+	the constructed function.
+	* cl-macs.el (constantly):
+	In the compiler macro for #'constantly, construct a
+	compiled-function object almost every time, at compile time when
+	all arguments are constant, and at runtime when they vary.
+
 2010-02-08  Ben Wing  <ben@xemacs.org>
 
 	* help.el (describe-function-1):
--- a/lisp/autoload.el	Mon Feb 22 06:49:30 2010 -0600
+++ b/lisp/autoload.el	Tue Feb 23 07:28:35 2010 -0600
@@ -2,7 +2,7 @@
 
 ;; Copyright (C) 1991-1994, 1997, 2003 Free Software Foundation, Inc.
 ;; Copyright (C) 1995 Tinker Systems and INS Engineering Corp.
-;; Copyright (C) 1996, 2000, 2002, 2003, 2004 Ben Wing.
+;; Copyright (C) 1996, 2000, 2002, 2003, 2004, 2010 Ben Wing.
 
 ;; Original Author: Roland McGrath <roland@gnu.ai.mit.edu>
 ;; Heavily Modified: XEmacs Maintainers
@@ -290,10 +290,8 @@
 		  (placeholder (eval-when-compile (gensym))))
 	      (setq doc (concat (or doc "")
 				"\n\narguments: "
-				(replace-in-string
-				 (cl-function-arglist placeholder arglist)
-				 (format "^(%s ?" placeholder)
-				 "(") "\n"))))
+				(cl-function-arglist placeholder arglist t)
+				"\n"))))
 	;; `define-generic-mode' quotes the name, so take care of that
 	(list 'autoload (if (listp name) name (list 'quote name)) file doc
 	      (or (and (memq car '(define-skeleton define-derived-mode
--- a/lisp/cl-extra.el	Mon Feb 22 06:49:30 2010 -0600
+++ b/lisp/cl-extra.el	Tue Feb 23 07:28:35 2010 -0600
@@ -612,6 +612,17 @@
 	  ((memq (car plst) indicator-list)
 	   (return (values (car plst) (cadr plst) plst))))))
 
+;; See our compiler macro in cl-macs.el, we will only pass back the
+;; actual lambda list in interpreted code or if we've been funcalled
+;; (from #'apply or #'mapcar or whatever).
+(defun constantly (value &rest more-values)
+  "Construct a function always returning VALUE, and possibly MORE-VALUES.
+
+The constructed function accepts any number of arguments, and ignores them.
+
+Members of MORE-VALUES, if provided, will be passed as multiple values; see
+`multiple-value-bind' and `multiple-value-setq'."
+  `(lambda (&rest ignore) (values-list ',(cons value more-values))))
 
 ;;; Hash tables.
 
--- a/lisp/cl-macs.el	Mon Feb 22 06:49:30 2010 -0600
+++ b/lisp/cl-macs.el	Tue Feb 23 07:28:35 2010 -0600
@@ -299,29 +299,33 @@
 
 ;; npak@ispras.ru
 ;;;###autoload
-(defun cl-function-arglist (name arglist)
+(defun cl-function-arglist (name arglist &optional omit-name)
   "Returns string with printed representation of arguments list.
 Supports Common Lisp lambda lists."
+  ;; #### I would just change this so that OMIT-NAME is always true and
+  ;; eliminate the argument, but this function is autoloaded, which means
+  ;; someone might be using it somewhere.
   (if (not (or (listp arglist) (symbolp arglist)))
       "Not available"
     (check-argument-type #'true-list-p arglist)
     (let ((print-gensym nil))
       (condition-case nil
           (prin1-to-string
-           (cons (if (eq name 'cl-none) 'lambda name)
-                 (cond ((null arglist) nil)
-                       ((listp arglist) (cl-upcase-arg arglist))
-                       ((symbolp arglist)
-                        (cl-upcase-arg (list '&rest arglist)))
-                       (t (wrong-type-argument 'listp arglist)))))
-      (t "Not available")))))
+	   (let ((args (cond ((null arglist) nil)
+			     ((listp arglist) (cl-upcase-arg arglist))
+			     ((symbolp arglist)
+			      (cl-upcase-arg (list '&rest arglist)))
+			     (t (wrong-type-argument 'listp arglist)))))
+	     (if omit-name args
+	       (cons (if (eq name 'cl-none) 'lambda name) args))))
+	(t "Not available")))))
 
 (defun cl-transform-lambda (form bind-block)
   (let* ((args (car form)) (body (cdr form))
 	 (bind-defs nil) (bind-enquote nil)
 	 (bind-inits nil) (bind-lets nil) (bind-forms nil)
 	 (header nil) (simple-args nil)
-         (complex-arglist (cl-function-arglist bind-block args))
+         (complex-arglist (cl-function-arglist bind-block args t))
          (doc ""))
     (while (or (stringp (car body)) (eq (car-safe (car body)) 'interactive))
       (push (pop body) header))
@@ -348,12 +352,12 @@
     ;; Add CL lambda list to documentation, if the CL lambda list differs
     ;; from the non-CL lambda list. npak@ispras.ru
     (unless (equal complex-arglist
-                   (cl-function-arglist bind-block simple-args))
+                   (cl-function-arglist bind-block simple-args t))
       (and (stringp (car header)) (setq doc (pop header)))
-      (push (concat doc
-                    "\n\nCommon Lisp lambda list:\n" 
-                    "  " complex-arglist "\n\n")
-	  header))
+      ;; Stick the arguments onto the end of the doc string in a way that
+      ;; will be recognized specially by `function-arglist'.
+      (push (concat doc "\n\narguments: " complex-arglist "\n")
+	    header))
     (if (null args)
 	(list* nil simple-args (nconc header body))
       (if (memq '&optional simple-args) (push '&optional args))
@@ -3551,6 +3555,41 @@
 (define-compiler-macro notevery (&whole form &rest cl-rest)
   (cons 'not (cons 'every (cdr cl-rest))))
 
+(define-compiler-macro constantly (&whole form value &rest more-values)
+  (cond
+   ((< (length form) 2)
+    ;; Error at runtime:
+    form)
+   ((cl-const-exprs-p (cdr form))
+    `#'(lambda (&rest ignore) (values ,@(cdr form))))
+   (t
+    (let* ((num-values (length (cdr form)))
+	   (placeholders-counts (make-vector num-values -1))
+	   (placeholders (loop
+			   for i from 0 below num-values
+			   collect (make-symbol (format "%d" i))))
+	   (compiled
+	    (byte-compile-sexp
+	     `#'(lambda (&rest ignore)
+		  ;; Compiles to a references into the compiled function
+		  ;; constants vector:
+		  (values ,@(mapcar #'quote-maybe placeholders)))))
+	   position)
+      `(make-byte-code '(&rest ignore)
+	,(compiled-function-instructions compiled)
+	(vector ,@(loop
+		    for constant across (compiled-function-constants compiled)
+		    collect (if (setq position
+				      (position constant placeholders))
+				(prog2
+				    (incf (aref placeholders-counts position))
+				    (nth position (cdr form)))
+			      (quote-maybe constant))
+		    finally
+		    (assert (every #'zerop placeholders-counts)
+			    t "Placeholders should each have been used once")))
+	,(compiled-function-stack-depth compiled))))))
+
 (mapc
  #'(lambda (y)
      (put (car y) 'side-effect-free t)
--- a/lisp/cl-seq.el	Mon Feb 22 06:49:30 2010 -0600
+++ b/lisp/cl-seq.el	Tue Feb 23 07:28:35 2010 -0600
@@ -1,6 +1,7 @@
 ;;; cl-seq.el --- Common Lisp extensions for XEmacs Lisp (part three)
 
 ;; Copyright (C) 1993 Free Software Foundation, Inc.
+;; Copyright (C) 2010 Ben Wing.
 
 ;; Author: Dave Gillespie <daveg@synaptics.com>
 ;; Maintainer: XEmacs Development Team
@@ -147,8 +148,18 @@
 
 
 (defun reduce (cl-func cl-seq &rest cl-keys)
-  "Reduce two-argument FUNCTION across SEQUENCE.
-Keywords supported:  :start :end :from-end :initial-value :key"
+  "Combine the elements of sequence using FUNCTION, a binary operation.
+For example, `(reduce #'+ SEQUENCE)' returns the sum of all elements in
+SEQUENCE, and `(reduce #'union SEQUENCE)' returns the union of all elements
+in SEQUENCE.
+Keywords supported:  :start :end :from-end :initial-value :key
+See `remove*' for the meaning of :start, :end, :from-end and :key.
+:initial-value specifies an element (typically an identity element, such as 0)
+that is conceptually prepended to the sequence (or appended, when :from-end
+is given).
+If the sequence has one element, that element is returned directly.
+If the sequence has no elements, :initial-value is returned if given;
+otherwise, FUNCTION is called with no arguments, and its result returned."
   (cl-parsing-keywords (:from-end (:start 0) :end :initial-value :key) ()
     (or (listp cl-seq) (setq cl-seq (append cl-seq nil)))
     (setq cl-seq (subseq cl-seq cl-start cl-end))
@@ -167,7 +178,9 @@
 
 (defun fill (seq item &rest cl-keys)
   "Fill the elements of SEQ with ITEM.
-Keywords supported:  :start :end"
+Keywords supported:  :start :end
+:start and :end specify a subsequence of SEQ; see `remove*' for more
+information."
   (cl-parsing-keywords ((:start 0) :end) ()
     (if (listp seq)
 	(let ((p (nthcdr cl-start seq))
@@ -186,7 +199,9 @@
 (defun replace (cl-seq1 cl-seq2 &rest cl-keys)
   "Replace the elements of SEQ1 with the elements of SEQ2.
 SEQ1 is destructively modified, then returned.
-Keywords supported:  :start1 :end1 :start2 :end2"
+Keywords supported:  :start1 :end1 :start2 :end2
+:start1 and :end1 specify a subsequence of SEQ1, and :start2 and :end2 a
+subsequence of SEQ2; see `search' for more information."
   (cl-parsing-keywords ((:start1 0) :end1 (:start2 0) :end2) ()
     (if (and (eq cl-seq1 cl-seq2) (<= cl-start2 cl-start1))
 	(or (= cl-start1 cl-start2)
@@ -228,7 +243,19 @@
   "Remove all occurrences of ITEM in SEQ.
 This is a non-destructive function; it makes a copy of SEQ if necessary
 to avoid corrupting the original SEQ.
-Keywords supported:  :test :test-not :key :count :start :end :from-end"
+Keywords supported:  :test :test-not :key :count :start :end :from-end
+The keywords :test and :test-not specify two-argument test and negated-test
+predicates, respectively; :test defaults to `eql'.  :key specifies a
+one-argument function that transforms elements of SEQ into \"comparison keys\"
+before the test predicate is applied.  See `member*' for more information
+on these keywords.
+:start and :end, if given, specify indices of a subsequence of SEQ to
+be processed.  Indices are 0-based and processing involves the subsequence
+starting at the index given by :start and ending just before the index
+given by :end.
+:count, if given, limits the number of items removed to the number specified.
+:from-end, if given, causes processing to proceed starting from the end
+instead of the beginning; in this case, this matters only if :count is given."
   (cl-parsing-keywords (:test :test-not :key :if :if-not :count :from-end
 			(:start 0) :end) ()
     (if (<= (or cl-count (setq cl-count 8000000)) 0)
@@ -272,20 +299,23 @@
   "Remove all items satisfying PREDICATE in SEQ.
 This is a non-destructive function; it makes a copy of SEQ if necessary
 to avoid corrupting the original SEQ.
-Keywords supported:  :key :count :start :end :from-end"
+Keywords supported:  :key :count :start :end :from-end
+See `remove*' for the meaning of the keywords."
   (apply 'remove* nil cl-list :if cl-pred cl-keys))
 
 (defun remove-if-not (cl-pred cl-list &rest cl-keys)
   "Remove all items not satisfying PREDICATE in SEQ.
 This is a non-destructive function; it makes a copy of SEQ if necessary
 to avoid corrupting the original SEQ.
-Keywords supported:  :key :count :start :end :from-end"
+Keywords supported:  :key :count :start :end :from-end
+See `remove*' for the meaning of the keywords."
   (apply 'remove* nil cl-list :if-not cl-pred cl-keys))
 
 (defun delete* (cl-item cl-seq &rest cl-keys)
   "Remove all occurrences of ITEM in SEQ.
 This is a destructive function; it reuses the storage of SEQ whenever possible.
-Keywords supported:  :test :test-not :key :count :start :end :from-end"
+Keywords supported:  :test :test-not :key :count :start :end :from-end
+See `remove*' for the meaning of the keywords."
   (cl-parsing-keywords (:test :test-not :key :if :if-not :count :from-end
 			(:start 0) :end) ()
     (if (<= (or cl-count (setq cl-count 8000000)) 0)
@@ -327,16 +357,18 @@
 (defun delete-if (cl-pred cl-list &rest cl-keys)
   "Remove all items satisfying PREDICATE in SEQ.
 This is a destructive function; it reuses the storage of SEQ whenever possible.
-Keywords supported:  :key :count :start :end :from-end"
+Keywords supported:  :key :count :start :end :from-end
+See `remove*' for the meaning of the keywords."
   (apply 'delete* nil cl-list :if cl-pred cl-keys))
 
 (defun delete-if-not (cl-pred cl-list &rest cl-keys)
   "Remove all items not satisfying PREDICATE in SEQ.
 This is a destructive function; it reuses the storage of SEQ whenever possible.
-Keywords supported:  :key :count :start :end :from-end"
+Keywords supported:  :key :count :start :end :from-end
+See `remove*' for the meaning of the keywords."
   (apply 'delete* nil cl-list :if-not cl-pred cl-keys))
 
-;; XEmacs change: this is in subr.el in Emacs
+;; XEmacs change: this is in subr.el in GNU Emacs
 (defun remove (cl-item cl-seq)
   "Remove all occurrences of ITEM in SEQ, testing with `equal'
 This is a non-destructive function; it makes a copy of SEQ if necessary
@@ -344,7 +376,7 @@
 Also see: `remove*', `delete', `delete*'"
   (remove* cl-item cl-seq ':test 'equal))
 
-;; XEmacs change: this is in subr.el in Emacs
+;; XEmacs change: this is in subr.el in GNU Emacs
 (defun remq (cl-elt cl-list)
   "Remove all occurrences of ELT in LIST, comparing with `eq'.
 This is a non-destructive function; it makes a copy of LIST to avoid
@@ -356,12 +388,14 @@
 
 (defun remove-duplicates (cl-seq &rest cl-keys)
   "Return a copy of SEQ with all duplicate elements removed.
-Keywords supported:  :test :test-not :key :start :end :from-end"
+Keywords supported:  :test :test-not :key :start :end :from-end
+See `remove*' for the meaning of the keywords."
   (cl-delete-duplicates cl-seq cl-keys t))
 
 (defun delete-duplicates (cl-seq &rest cl-keys)
   "Remove all duplicate elements from SEQ (destructively).
-Keywords supported:  :test :test-not :key :start :end :from-end"
+Keywords supported:  :test :test-not :key :start :end :from-end
+See `remove*' for the meaning of the keywords."
   (cl-delete-duplicates cl-seq cl-keys nil))
 
 (defun cl-delete-duplicates (cl-seq cl-keys cl-copy)
@@ -408,7 +442,8 @@
   "Substitute NEW for OLD in SEQ.
 This is a non-destructive function; it makes a copy of SEQ if necessary
 to avoid corrupting the original SEQ.
-Keywords supported:  :test :test-not :key :count :start :end :from-end"
+Keywords supported:  :test :test-not :key :count :start :end :from-end
+See `remove*' for the meaning of the keywords."
   (cl-parsing-keywords (:test :test-not :key :if :if-not :count
 			(:start 0) :end :from-end) ()
     (if (or (eq cl-old cl-new)
@@ -428,20 +463,21 @@
   "Substitute NEW for all items satisfying PREDICATE in SEQ.
 This is a non-destructive function; it makes a copy of SEQ if necessary
 to avoid corrupting the original SEQ.
-Keywords supported:  :key :count :start :end :from-end"
+See `remove*' for the meaning of the keywords."
   (apply 'substitute cl-new nil cl-list :if cl-pred cl-keys))
 
 (defun substitute-if-not (cl-new cl-pred cl-list &rest cl-keys)
   "Substitute NEW for all items not satisfying PREDICATE in SEQ.
 This is a non-destructive function; it makes a copy of SEQ if necessary
 to avoid corrupting the original SEQ.
-Keywords supported:  :key :count :start :end :from-end"
+See `remove*' for the meaning of the keywords."
   (apply 'substitute cl-new nil cl-list :if-not cl-pred cl-keys))
 
 (defun nsubstitute (cl-new cl-old cl-seq &rest cl-keys)
   "Substitute NEW for OLD in SEQ.
 This is a destructive function; it reuses the storage of SEQ whenever possible.
-Keywords supported:  :test :test-not :key :count :start :end :from-end"
+Keywords supported:  :test :test-not :key :count :start :end :from-end
+See `remove*' for the meaning of the keywords."
   (cl-parsing-keywords (:test :test-not :key :if :if-not :count
 			(:start 0) :end :from-end) ()
     (or (eq cl-old cl-new) (<= (or cl-count (setq cl-count 8000000)) 0)
@@ -473,38 +509,44 @@
 (defun nsubstitute-if (cl-new cl-pred cl-list &rest cl-keys)
   "Substitute NEW for all items satisfying PREDICATE in SEQ.
 This is a destructive function; it reuses the storage of SEQ whenever possible.
-Keywords supported:  :key :count :start :end :from-end"
+Keywords supported:  :key :count :start :end :from-end
+See `remove*' for the meaning of the keywords."
   (apply 'nsubstitute cl-new nil cl-list :if cl-pred cl-keys))
 
 (defun nsubstitute-if-not (cl-new cl-pred cl-list &rest cl-keys)
   "Substitute NEW for all items not satisfying PREDICATE in SEQ.
 This is a destructive function; it reuses the storage of SEQ whenever possible.
-Keywords supported:  :key :count :start :end :from-end"
+Keywords supported:  :key :count :start :end :from-end
+See `remove*' for the meaning of the keywords."
   (apply 'nsubstitute cl-new nil cl-list :if-not cl-pred cl-keys))
 
 (defun find (cl-item cl-seq &rest cl-keys)
   "Find the first occurrence of ITEM in LIST.
 Return the matching ITEM, or nil if not found.
-Keywords supported:  :test :test-not :key :start :end :from-end"
+Keywords supported:  :test :test-not :key :start :end :from-end
+See `remove*' for the meaning of the keywords."
   (let ((cl-pos (apply 'position cl-item cl-seq cl-keys)))
     (and cl-pos (elt cl-seq cl-pos))))
 
 (defun find-if (cl-pred cl-list &rest cl-keys)
   "Find the first item satisfying PREDICATE in LIST.
 Return the matching ITEM, or nil if not found.
-Keywords supported:  :key :start :end :from-end"
+Keywords supported:  :key :start :end :from-end
+See `remove*' for the meaning of the keywords."
   (apply 'find nil cl-list :if cl-pred cl-keys))
 
 (defun find-if-not (cl-pred cl-list &rest cl-keys)
   "Find the first item not satisfying PREDICATE in LIST.
 Return the matching ITEM, or nil if not found.
-Keywords supported:  :key :start :end :from-end"
+Keywords supported:  :key :start :end :from-end
+See `remove*' for the meaning of the keywords."
   (apply 'find nil cl-list :if-not cl-pred cl-keys))
 
 (defun position (cl-item cl-seq &rest cl-keys)
   "Find the first occurrence of ITEM in LIST.
 Return the index of the matching item, or nil if not found.
-Keywords supported:  :test :test-not :key :start :end :from-end"
+Keywords supported:  :test :test-not :key :start :end :from-end
+See `remove*' for the meaning of the keywords."
   (cl-parsing-keywords (:test :test-not :key :if :if-not
 			(:start 0) :end :from-end) ()
     (cl-position cl-item cl-seq cl-start cl-end cl-from-end)))
@@ -533,18 +575,21 @@
 (defun position-if (cl-pred cl-list &rest cl-keys)
   "Find the first item satisfying PREDICATE in LIST.
 Return the index of the matching item, or nil if not found.
-Keywords supported:  :key :start :end :from-end"
+Keywords supported:  :key :start :end :from-end
+See `remove*' for the meaning of the keywords."
   (apply 'position nil cl-list :if cl-pred cl-keys))
 
 (defun position-if-not (cl-pred cl-list &rest cl-keys)
   "Find the first item not satisfying PREDICATE in LIST.
 Return the index of the matching item, or nil if not found.
-Keywords supported:  :key :start :end :from-end"
+Keywords supported:  :key :start :end :from-end
+See `remove*' for the meaning of the keywords."
   (apply 'position nil cl-list :if-not cl-pred cl-keys))
 
 (defun count (cl-item cl-seq &rest cl-keys)
   "Count the number of occurrences of ITEM in LIST.
-Keywords supported:  :test :test-not :key :start :end"
+Keywords supported:  :test :test-not :key :start :end
+See `remove*' for the meaning of the keywords."
   (cl-parsing-keywords (:test :test-not :key :if :if-not (:start 0) :end) ()
     (let ((cl-count 0) cl-x)
       (or cl-end (setq cl-end (length cl-seq)))
@@ -557,19 +602,22 @@
 
 (defun count-if (cl-pred cl-list &rest cl-keys)
   "Count the number of items satisfying PREDICATE in LIST.
-Keywords supported:  :key :start :end"
+Keywords supported:  :key :start :end
+See `remove*' for the meaning of the keywords."
   (apply 'count nil cl-list :if cl-pred cl-keys))
 
 (defun count-if-not (cl-pred cl-list &rest cl-keys)
   "Count the number of items not satisfying PREDICATE in LIST.
-Keywords supported:  :key :start :end"
+Keywords supported:  :key :start :end
+See `remove*' for the meaning of the keywords."
   (apply 'count nil cl-list :if-not cl-pred cl-keys))
 
 (defun mismatch (cl-seq1 cl-seq2 &rest cl-keys)
   "Compare SEQ1 with SEQ2, return index of first mismatching element.
 Return nil if the sequences match.  If one sequence is a prefix of the
 other, the return value indicates the end of the shorter sequence.
-Keywords supported:  :test :test-not :key :start1 :end1 :start2 :end2 :from-end"
+Keywords supported:  :test :test-not :key :start1 :end1 :start2 :end2 :from-end
+See `search' for the meaning of the keywords."
   (cl-parsing-keywords (:test :test-not :key :from-end
 			(:start1 0) :end1 (:start2 0) :end2) ()
     (or cl-end1 (setq cl-end1 (length cl-seq1)))
@@ -598,7 +646,10 @@
   "Search for SEQ1 as a subsequence of SEQ2.
 Return the index of the leftmost element of the first match found;
 return nil if there are no matches.
-Keywords supported:  :test :test-not :key :start1 :end1 :start2 :end2 :from-end"
+Keywords supported:  :test :test-not :key :start1 :end1 :start2 :end2 :from-end
+See `remove*' for the meaning of the keywords.  In this case, :start1 and :end1
+specify a subsequence of SEQ1, and :start2 and :end2 specify a subsequence
+of SEQ2."
   (cl-parsing-keywords (:test :test-not :key :from-end
 			(:start1 0) :end1 (:start2 0) :end2) ()
     (or cl-end1 (setq cl-end1 (length cl-seq1)))
@@ -622,7 +673,10 @@
 (defun sort* (cl-seq cl-pred &rest cl-keys)
   "Sort the argument SEQUENCE according to PREDICATE.
 This is a destructive function; it reuses the storage of SEQUENCE if possible.
-Keywords supported:  :key"
+Keywords supported:  :key
+:key specifies a one-argument function that transforms elements of SEQUENCE
+into \"comparison keys\" before the test predicate is applied.  See
+`member*' for more information."
   (if (nlistp cl-seq)
       (replace cl-seq (apply 'sort* (append cl-seq nil) cl-pred cl-keys))
     (cl-parsing-keywords (:key) ()
@@ -635,14 +689,20 @@
 (defun stable-sort (cl-seq cl-pred &rest cl-keys)
   "Sort the argument SEQUENCE stably according to PREDICATE.
 This is a destructive function; it reuses the storage of SEQUENCE if possible.
-Keywords supported:  :key"
+Keywords supported:  :key
+:key specifies a one-argument function that transforms elements of SEQUENCE
+into \"comparison keys\" before the test predicate is applied.  See
+`member*' for more information."
   (apply 'sort* cl-seq cl-pred cl-keys))
 
 (defun merge (cl-type cl-seq1 cl-seq2 cl-pred &rest cl-keys)
   "Destructively merge the two sequences to produce a new sequence.
 TYPE is the sequence type to return, SEQ1 and SEQ2 are the two
 argument sequences, and PRED is a `less-than' predicate on the elements.
-Keywords supported:  :key"
+Keywords supported:  :key
+:key specifies a one-argument function that transforms elements of SEQ1 and
+SEQ2 into \"comparison keys\" before the test predicate is applied.  See
+`member*' for more information."
   (or (listp cl-seq1) (setq cl-seq1 (append cl-seq1 nil)))
   (or (listp cl-seq2) (setq cl-seq2 (append cl-seq2 nil)))
   (cl-parsing-keywords (:key) ()
@@ -658,7 +718,18 @@
 (defun member* (cl-item cl-list &rest cl-keys)
   "Find the first occurrence of ITEM in LIST.
 Return the sublist of LIST whose car is ITEM.
-Keywords supported:  :test :test-not :key"
+Keywords supported:  :test :test-not :key
+The keyword :test specifies a two-argument function that is used to
+ compare ITEM with elements in LIST; if omitted, it defaults to `eql'.
+The keyword :test-not is similar, but specifies a negated predicate.  That
+ is, ITEM is considered equal to an element in LIST if the given predicate
+ returns nil.
+:key specifies a one-argument function that transforms elements of LIST into
+\"comparison keys\" before the test predicate is applied.  For example,
+if :key is #'car, then ITEM is compared with the car of elements from LIST1.
+The :key function, however, is not applied to ITEM, and does not affect the
+elements in the returned list, which are taken directly from the elements in
+LIST."
   (if cl-keys
       (cl-parsing-keywords (:test :test-not :key :if :if-not) ()
 	(while (and cl-list (not (cl-check-test cl-item (car cl-list))))
@@ -671,13 +742,15 @@
 (defun member-if (cl-pred cl-list &rest cl-keys)
   "Find the first item satisfying PREDICATE in LIST.
 Return the sublist of LIST whose car matches.
-Keywords supported:  :key"
+Keywords supported:  :key
+See `member*' for the meaning of :key."
   (apply 'member* nil cl-list :if cl-pred cl-keys))
 
 (defun member-if-not (cl-pred cl-list &rest cl-keys)
   "Find the first item not satisfying PREDICATE in LIST.
 Return the sublist of LIST whose car matches.
-Keywords supported:  :key"
+Keywords supported:  :key
+See `member*' for the meaning of :key."
   (apply 'member* nil cl-list :if-not cl-pred cl-keys))
 
 (defun cl-adjoin (cl-item cl-list &rest cl-keys)
@@ -689,7 +762,8 @@
 ;;; See compiler macro in cl-macs.el
 (defun assoc* (cl-item cl-alist &rest cl-keys)
   "Find the first item whose car matches ITEM in LIST.
-Keywords supported:  :test :test-not :key"
+Keywords supported:  :test :test-not :key
+See `member*' for the meaning of :test, :test-not and :key."
   (if cl-keys
       (cl-parsing-keywords (:test :test-not :key :if :if-not) ()
 	(while (and cl-alist
@@ -703,17 +777,20 @@
 
 (defun assoc-if (cl-pred cl-list &rest cl-keys)
   "Find the first item whose car satisfies PREDICATE in LIST.
-Keywords supported:  :key"
+Keywords supported:  :key
+See `member*' for the meaning of :key."
   (apply 'assoc* nil cl-list :if cl-pred cl-keys))
 
 (defun assoc-if-not (cl-pred cl-list &rest cl-keys)
   "Find the first item whose car does not satisfy PREDICATE in LIST.
-Keywords supported:  :key"
+Keywords supported:  :key
+See `member*' for the meaning of :key."
   (apply 'assoc* nil cl-list :if-not cl-pred cl-keys))
 
 (defun rassoc* (cl-item cl-alist &rest cl-keys)
   "Find the first item whose cdr matches ITEM in LIST.
-Keywords supported:  :test :test-not :key"
+Keywords supported:  :test :test-not :key
+See `member*' for the meaning of :test, :test-not and :key."
   (if (or cl-keys (and (numberp cl-item) (not (fixnump cl-item))))
       (cl-parsing-keywords (:test :test-not :key :if :if-not) ()
 	(while (and cl-alist
@@ -725,12 +802,14 @@
 
 (defun rassoc-if (cl-pred cl-list &rest cl-keys)
   "Find the first item whose cdr satisfies PREDICATE in LIST.
-Keywords supported:  :key"
+Keywords supported:  :key
+See `member*' for the meaning of :key."
   (apply 'rassoc* nil cl-list :if cl-pred cl-keys))
 
 (defun rassoc-if-not (cl-pred cl-list &rest cl-keys)
   "Find the first item whose cdr does not satisfy PREDICATE in LIST.
-Keywords supported:  :key"
+Keywords supported:  :key
+See `member*' for the meaning of :key."
   (apply 'rassoc* nil cl-list :if-not cl-pred cl-keys))
 
 (defun union (cl-list1 cl-list2 &rest cl-keys)
@@ -738,7 +817,16 @@
 The result list contains all items that appear in either LIST1 or LIST2.
 This is a non-destructive function; it makes a copy of the data if necessary
 to avoid corrupting the original LIST1 and LIST2.
-Keywords supported:  :test :test-not :key"
+Keywords supported:  :test :test-not :key
+The keywords :test and :test-not specify two-argument test and negated-test
+predicates, respectively; :test defaults to `eql'.  see `member*' for more
+information.
+:key specifies a one-argument function that transforms elements of LIST1
+and LIST2 into \"comparison keys\" before the test predicate is applied.
+For example, if :key is #'car, then the car of elements from LIST1 is
+compared with the car of elements from LIST2.  The :key function, however,
+does not affect the elements in the returned list, which are taken directly
+from the elements in LIST1 and LIST2."
   (cond ((null cl-list1) cl-list2) ((null cl-list2) cl-list1)
 	((equal cl-list1 cl-list2) cl-list1)
 	(t
@@ -757,16 +845,52 @@
 The result list contains all items that appear in either LIST1 or LIST2.
 This is a destructive function; it reuses the storage of LIST1 and LIST2
 whenever possible.
-Keywords supported:  :test :test-not :key"
+Keywords supported:  :test :test-not :key
+See `union' for the meaning of :test, :test-not and :key."
   (cond ((null cl-list1) cl-list2) ((null cl-list2) cl-list1)
 	(t (apply 'union cl-list1 cl-list2 cl-keys))))
 
+;; XEmacs addition: NOT IN COMMON LISP.
+(defun stable-union (cl-list1 cl-list2 &rest cl-keys)
+  "Stably combine LIST1 and LIST2 using a set-union operation.
+The result list contains all items that appear in either LIST1 or LIST2.
+The result is \"stable\" in that it preserves the ordering of elements in
+LIST1 and LIST2.  The result specifically consists of the elements in LIST1
+in order, followed by any elements in LIST2 that are not also in LIST1, in
+the order given in LIST2.
+This is a non-destructive function; it makes a copy of the data if necessary
+to avoid corrupting the original LIST1 and LIST2.
+Keywords supported:  :test :test-not :key
+See `union' for the meaning of :test, :test-not and :key.
+
+NOTE: This is *NOT* a function defined by Common Lisp, but an XEmacs
+extension."
+  ;; The standard `union' doesn't produce a "stable" union --
+  ;; it iterates over the second list instead of the first one, and returns
+  ;; the values in backwards order.  According to the CLTL2 documentation,
+  ;; `union' is not required to preserve the ordering of elements in
+  ;; any fashion, so we add a new function rather than changing the
+  ;; semantics of `union'.
+  (cond ((null cl-list1) cl-list2) ((null cl-list2) cl-list1)
+	((equal cl-list1 cl-list2) cl-list1)
+	(t
+	 (append
+	  cl-list1
+	  (cl-parsing-keywords (:key) (:test :test-not)
+	    (loop for cl-l in cl-list2
+	      if (not (if (or cl-keys (numberp cl-l))
+			  (apply 'member* (cl-check-key cl-l)
+				 cl-list1 cl-keys)
+			(memq cl-l cl-list1)))
+	      collect cl-l))))))
+
 (defun intersection (cl-list1 cl-list2 &rest cl-keys)
   "Combine LIST1 and LIST2 using a set-intersection operation.
 The result list contains all items that appear in both LIST1 and LIST2.
 This is a non-destructive function; it makes a copy of the data if necessary
 to avoid corrupting the original LIST1 and LIST2.
-Keywords supported:  :test :test-not :key"
+Keywords supported:  :test :test-not :key
+See `union' for the meaning of :test, :test-not and :key."
   (and cl-list1 cl-list2
        (if (equal cl-list1 cl-list2) cl-list1
 	 (cl-parsing-keywords (:key) (:test :test-not)
@@ -787,15 +911,46 @@
 The result list contains all items that appear in both LIST1 and LIST2.
 This is a destructive function; it reuses the storage of LIST1 and LIST2
 whenever possible.
-Keywords supported:  :test :test-not :key"
+Keywords supported:  :test :test-not :key
+See `union' for the meaning of :test, :test-not and :key."
   (and cl-list1 cl-list2 (apply 'intersection cl-list1 cl-list2 cl-keys)))
 
+;; XEmacs addition: NOT IN COMMON LISP.
+(defun stable-intersection (cl-list1 cl-list2 &rest cl-keys)
+  "Stably combine LIST1 and LIST2 using a set-intersection operation.
+The result list contains all items that appear in both LIST1 and LIST2.
+The result is \"stable\" in that it preserves the ordering of elements in
+LIST1 that are also in LIST2.
+This is a non-destructive function; it makes a copy of the data if necessary
+to avoid corrupting the original LIST1 and LIST2.
+Keywords supported:  :test :test-not :key
+See `union' for the meaning of :test, :test-not and :key.
+
+NOTE: This is *NOT* a function defined by Common Lisp, but an XEmacs
+extension."
+  ;; The standard `intersection' doesn't produce a "stable" intersection --
+  ;; it iterates over the second list instead of the first one, and returns
+  ;; the values in backwards order.  According to the CLTL2 documentation,
+  ;; `intersection' is not required to preserve the ordering of elements in
+  ;; any fashion, so we add a new function rather than changing the
+  ;; semantics of `intersection'.
+  (and cl-list1 cl-list2
+       (if (equal cl-list1 cl-list2) cl-list1
+	 (cl-parsing-keywords (:key) (:test :test-not)
+	   (loop for cl-l in cl-list1
+	     if (if (or cl-keys (numberp cl-l))
+		    (apply 'member* (cl-check-key cl-l)
+			   cl-list2 cl-keys)
+		  (memq cl-l cl-list2))
+	     collect cl-l)))))
+
 (defun set-difference (cl-list1 cl-list2 &rest cl-keys)
   "Combine LIST1 and LIST2 using a set-difference operation.
 The result list contains all items that appear in LIST1 but not LIST2.
 This is a non-destructive function; it makes a copy of the data if necessary
 to avoid corrupting the original LIST1 and LIST2.
-Keywords supported:  :test :test-not :key"
+Keywords supported:  :test :test-not :key
+See `union' for the meaning of :test, :test-not and :key."
   (if (or (null cl-list1) (null cl-list2)) cl-list1
     (cl-parsing-keywords (:key) (:test :test-not)
       (let ((cl-res nil))
@@ -813,7 +968,8 @@
 The result list contains all items that appear in LIST1 but not LIST2.
 This is a destructive function; it reuses the storage of LIST1 and LIST2
 whenever possible.
-Keywords supported:  :test :test-not :key"
+Keywords supported:  :test :test-not :key
+See `union' for the meaning of :test, :test-not and :key."
   (if (or (null cl-list1) (null cl-list2)) cl-list1
     (apply 'set-difference cl-list1 cl-list2 cl-keys)))
 
@@ -822,7 +978,8 @@
 The result list contains all items that appear in exactly one of LIST1, LIST2.
 This is a non-destructive function; it makes a copy of the data if necessary
 to avoid corrupting the original LIST1 and LIST2.
-Keywords supported:  :test :test-not :key"
+Keywords supported:  :test :test-not :key
+See `union' for the meaning of :test, :test-not and :key."
   (cond ((null cl-list1) cl-list2) ((null cl-list2) cl-list1)
 	((equal cl-list1 cl-list2) nil)
 	(t (append (apply 'set-difference cl-list1 cl-list2 cl-keys)
@@ -833,7 +990,8 @@
 The result list contains all items that appear in exactly one of LIST1, LIST2.
 This is a destructive function; it reuses the storage of LIST1 and LIST2
 whenever possible.
-Keywords supported:  :test :test-not :key"
+Keywords supported:  :test :test-not :key
+See `union' for the meaning of :test, :test-not and :key."
   (cond ((null cl-list1) cl-list2) ((null cl-list2) cl-list1)
 	((equal cl-list1 cl-list2) nil)
 	(t (nconc (apply 'nset-difference cl-list1 cl-list2 cl-keys)
@@ -842,7 +1000,8 @@
 (defun subsetp (cl-list1 cl-list2 &rest cl-keys)
   "True if LIST1 is a subset of LIST2.
 I.e., if every element of LIST1 also appears in LIST2.
-Keywords supported:  :test :test-not :key"
+Keywords supported:  :test :test-not :key
+See `union' for the meaning of :test, :test-not and :key."
   (cond ((null cl-list1) t) ((null cl-list2) nil)
 	((equal cl-list1 cl-list2) t)
 	(t (cl-parsing-keywords (:key) (:test :test-not)
@@ -855,38 +1014,44 @@
 (defun subst-if (cl-new cl-pred cl-tree &rest cl-keys)
   "Substitute NEW for elements matching PREDICATE in TREE (non-destructively).
 Return a copy of TREE with all matching elements replaced by NEW.
-Keywords supported:  :key"
+Keywords supported:  :key
+See `member*' for the meaning of :key."
   (apply 'sublis (list (cons nil cl-new)) cl-tree :if cl-pred cl-keys))
 
 (defun subst-if-not (cl-new cl-pred cl-tree &rest cl-keys)
   "Substitute NEW for elts not matching PREDICATE in TREE (non-destructively).
 Return a copy of TREE with all non-matching elements replaced by NEW.
-Keywords supported:  :key"
+Keywords supported:  :key
+See `member*' for the meaning of :key."
   (apply 'sublis (list (cons nil cl-new)) cl-tree :if-not cl-pred cl-keys))
 
 (defun nsubst (cl-new cl-old cl-tree &rest cl-keys)
   "Substitute NEW for OLD everywhere in TREE (destructively).
 Any element of TREE which is `eql' to OLD is changed to NEW (via a call
 to `setcar').
-Keywords supported:  :test :test-not :key"
+Keywords supported:  :test :test-not :key
+See `member*' for the meaning of :test, :test-not and :key."
   (apply 'nsublis (list (cons cl-old cl-new)) cl-tree cl-keys))
 
 (defun nsubst-if (cl-new cl-pred cl-tree &rest cl-keys)
   "Substitute NEW for elements matching PREDICATE in TREE (destructively).
 Any element of TREE which matches is changed to NEW (via a call to `setcar').
-Keywords supported:  :key"
+Keywords supported:  :key
+See `member*' for the meaning of :key."
   (apply 'nsublis (list (cons nil cl-new)) cl-tree :if cl-pred cl-keys))
 
 (defun nsubst-if-not (cl-new cl-pred cl-tree &rest cl-keys)
   "Substitute NEW for elements not matching PREDICATE in TREE (destructively).
 Any element of TREE which matches is changed to NEW (via a call to `setcar').
-Keywords supported:  :key"
+Keywords supported:  :key
+See `member*' for the meaning of :key."
   (apply 'nsublis (list (cons nil cl-new)) cl-tree :if-not cl-pred cl-keys))
 
 (defun sublis (cl-alist cl-tree &rest cl-keys)
   "Perform substitutions indicated by ALIST in TREE (non-destructively).
 Return a copy of TREE with all matching elements replaced.
-Keywords supported:  :test :test-not :key"
+Keywords supported:  :test :test-not :key
+See `member*' for the meaning of :test, :test-not and :key."
   (cl-parsing-keywords (:test :test-not :key :if :if-not) ()
     (cl-sublis-rec cl-tree)))
 
@@ -907,7 +1072,8 @@
 (defun nsublis (cl-alist cl-tree &rest cl-keys)
   "Perform substitutions indicated by ALIST in TREE (destructively).
 Any matching element of TREE is changed via a call to `setcar'.
-Keywords supported:  :test :test-not :key"
+Keywords supported:  :test :test-not :key
+See `member*' for the meaning of :test, :test-not and :key."
   (cl-parsing-keywords (:test :test-not :key :if :if-not) ()
     (let ((cl-hold (list cl-tree)))
       (cl-nsublis-rec cl-hold)
@@ -930,7 +1096,8 @@
 (defun tree-equal (cl-x cl-y &rest cl-keys)
   "Return t if trees X and Y have `eql' leaves.
 Atoms are compared by `eql'; cons cells are compared recursively.
-Keywords supported:  :test :test-not :key"
+Keywords supported:  :test :test-not :key
+See `union' for the meaning of :test, :test-not and :key."
   (cl-parsing-keywords (:test :test-not :key) ()
     (cl-tree-equal-rec cl-x cl-y)))
 
--- a/lisp/cl.el	Mon Feb 22 06:49:30 2010 -0600
+++ b/lisp/cl.el	Tue Feb 23 07:28:35 2010 -0600
@@ -1,6 +1,7 @@
 ;;; cl.el --- Common Lisp extensions for XEmacs Lisp
 
 ;; Copyright (C) 1993, 1997 Free Software Foundation, Inc.
+;; Copyright (C) 2010 Ben Wing.
 
 ;; Author: Dave Gillespie <daveg@synaptics.com>
 ;; Maintainer: XEmacs Development Team
@@ -179,7 +180,8 @@
   "Add NEWELT at the beginning of LISTNAME, unless it's already in LISTNAME.
 Like (push NEWELT LISTNAME), except that the list is unmodified if NEWELT is
 `eql' to an element already on the list.
-Keywords supported:  :test :test-not :key"
+Keywords supported:  :test :test-not :key
+See `member*' for the meaning of :test, :test-not and :key."
   (if (symbolp listname) (list 'setq listname 
 			       (list* 'adjoin newelt listname keys))
     (list* 'callf2 'adjoin newelt listname keys)))
@@ -583,7 +585,8 @@
 (defun adjoin (cl-item cl-list &rest cl-keys)  ; See compiler macro in cl-macs
   "Return ITEM consed onto the front of LIST only if it's not already there.
 Otherwise, return LIST unmodified.
-Keywords supported:  :test :test-not :key"
+Keywords supported:  :test :test-not :key
+See `member*' for the meaning of :test, :test-not and :key."
   (cond ((or (equal cl-keys '(:test eq))
 	     (and (null cl-keys) (not (numberp cl-item))))
 	 (if (memq cl-item cl-list) cl-list (cons cl-item cl-list)))
@@ -594,7 +597,8 @@
 (defun subst (cl-new cl-old cl-tree &rest cl-keys)
   "Substitute NEW for OLD everywhere in TREE (non-destructively).
 Return a copy of TREE with all elements `eql' to OLD replaced by NEW.
-Keywords supported:  :test :test-not :key"
+Keywords supported:  :test :test-not :key
+See `member*' for the meaning of :test, :test-not and :key."
   (if (or cl-keys (and (numberp cl-old) (not (fixnump cl-old))))
       (apply 'sublis (list (cons cl-old cl-new)) cl-tree cl-keys)
     (cl-do-subst cl-new cl-old cl-tree)))
--- a/lisp/coding.el	Mon Feb 22 06:49:30 2010 -0600
+++ b/lisp/coding.el	Tue Feb 23 07:28:35 2010 -0600
@@ -5,7 +5,7 @@
 ;; Copyright (C) 1995 Amdahl Corporation.
 ;; Copyright (C) 1995 Sun Microsystems.
 ;; Copyright (C) 1997 MORIOKA Tomohiko
-;; Copyright (C) 2000, 2001, 2002 Ben Wing.
+;; Copyright (C) 2000, 2001, 2002, 2010 Ben Wing.
 
 ;; This file is part of XEmacs.
 
@@ -464,27 +464,25 @@
   (and (query-coding-string char coding-system)
        (encode-coding-string char coding-system)))
 
-(if (featurep 'mule)
-    (progn
-      ;; Under Mule, we do much of the complicated coding system creation in
-      ;; Lisp and especially at compile time. We need some function
-      ;; definition for this function to be created in this file, but we can
-      ;; leave assigning the docstring to the autoload cookie
-      ;; handling later. Thankfully; that docstring is big.
-      (autoload 'make-coding-system "mule/make-coding-system")
+(defun decode-char (quote-ucs code &optional restriction) 
+  "FSF compatibility--return Mule character with Unicode codepoint CODE.
+The second argument must be 'ucs, the third argument is ignored.  "
+  ;; We're prepared to accept invalid Unicode in unicode-to-char, but not in
+  ;; this function, which is the API that should actually be used, since
+  ;; it's available in GNU and in Mule-UCS.
+  (check-argument-range code #x0 #x10FFFF)
+  (assert (eq quote-ucs 'ucs) t
+	  "Sorry, decode-char doesn't yet support anything but the UCS.  ")
+  (unicode-to-char code))
 
-      ;; (During byte-compile before dumping, make-coding-system may already
-      ;; have been loaded, make sure not to overwrite the correct compiler
-      ;; macro:)
-      (when (eq 'autoload (car (symbol-function 'make-coding-system)))
-        ;; Make sure to pick up the correct compiler macro when compiling
-        ;; files:
-        (define-compiler-macro make-coding-system (&whole form name type
-                                                   &optional description props)
-          (load (second (symbol-function 'make-coding-system)))
-          (funcall (get 'make-coding-system 'cl-compiler-macro)
-                   form name type description props))))
+(defun encode-char (char quote-ucs &optional restriction)
+  "FSF compatibility--return the Unicode code point of CHAR.
+The second argument must be 'ucs, the third argument is ignored.  "
+  (assert (eq quote-ucs 'ucs) t
+	  "Sorry, encode-char doesn't yet support anything but the UCS.  ")
+  (char-to-unicode char))
 
+(unless (featurep 'mule)
   ;; Mule's not available; 
   (fset 'make-coding-system (symbol-function 'make-coding-system-internal))
   (define-coding-system-alias 'escape-quoted 'binary)
--- a/lisp/diagnose.el	Mon Feb 22 06:49:30 2010 -0600
+++ b/lisp/diagnose.el	Tue Feb 23 07:28:35 2010 -0600
@@ -1,6 +1,6 @@
 ;;; diagnose.el --- routines for debugging problems in XEmacs
 
-;; Copyright (C) 2002 Ben Wing.
+;; Copyright (C) 2002, 2010 Ben Wing.
 
 ;; Maintainer: XEmacs Development Team
 ;; Keywords: dumped
@@ -197,29 +197,33 @@
 			 (intern (concat (match-string 1 (symbol-name stat))
 					 "-storage-including-overhead"))))
 		       (storage-count 
-			(or (plist-get 
-			     plist 
-			     (intern 
-			      (concat (match-string 1 (symbol-name stat)) 
-				      "s-used")))
+			(or (loop for str in '("s-used" "es-used" "-used")
+			      for val = (plist-get
+					 plist
+					 (intern
+					  (concat (match-string
+						   1 (symbol-name stat)) 
+						  str)))
+			      if val
+			      return val)
 			    (plist-get 
 			     plist 
 			     (intern 
-			      (concat (match-string 1 (symbol-name stat))
-				      "es-used")))
-			    (plist-get 
-			     plist 
-			     (intern 
-			      (concat (match-string 1 (symbol-name stat))
-				      "-used"))))))
+			      (concat (substring
+				       (match-string 1 (symbol-name stat))
+				       0 -1)
+				      "ies-used")))
+			    )))
 		   (incf total-use storage-use)
 		   (incf total-use-overhead (if storage-use-overhead 
 						storage-use-overhead 
 					      storage-use))
-		   (incf total-count storage-count)
-		   (princ (format fmt
-				  (match-string 1 (symbol-name stat)) 
-				  storage-count storage-use)))))
+		   (incf total-count (or storage-count 0))
+		   (and (> storage-use 0)
+			(princ (format fmt
+				       (match-string 1 (symbol-name stat)) 
+				       (or storage-count "unknown")
+				       storage-use))))))
 	   plist)
 	  (princ "\n")
 	  (princ (format fmt "total" 
@@ -229,7 +233,7 @@
             (sort-numeric-fields -1
                                  (save-excursion
                                    (goto-char begin)
-                                   (forward-line 2)
+                                   (forward-line 3)
                                    (point))
                                  (save-excursion
                                    (forward-line -2)
--- a/lisp/dumped-lisp.el	Mon Feb 22 06:49:30 2010 -0600
+++ b/lisp/dumped-lisp.el	Tue Feb 23 07:28:35 2010 -0600
@@ -160,6 +160,7 @@
        "code-process"
        ;; Provide basic commands to set coding systems to user
        "code-cmds"
+       (when (featurep 'mule) "mule/make-coding-system")
        "unicode"
 	;;;;;;;;;;;;;;;;;; MULE support
        (when (featurep 'mule)
--- a/lisp/help.el	Mon Feb 22 06:49:30 2010 -0600
+++ b/lisp/help.el	Tue Feb 23 07:28:35 2010 -0600
@@ -1,7 +1,7 @@
 ;; help.el --- help commands for XEmacs.
 
 ;; Copyright (C) 1985, 1986, 1992-4, 1997 Free Software Foundation, Inc.
-;; Copyright (C) 2001, 2002, 2003 Ben Wing.
+;; Copyright (C) 2001, 2002, 2003, 2010 Ben Wing.
 
 ;; Maintainer: FSF
 ;; Keywords: help, internal, dumped
@@ -1182,27 +1182,21 @@
 	 (fndef (if (eq (car-safe fnc) 'macro)
 		    (cdr fnc)
 		  fnc))
+	 (args (cdr (function-documentation-1 function t)))
 	 (arglist
-	  (cond ((compiled-function-p fndef)
-		 (compiled-function-arglist fndef))
-		((eq (car-safe fndef) 'lambda)
-		 (nth 1 fndef))
-		((or (subrp fndef) (eq 'autoload (car-safe fndef)))
-		 (let* ((doc (documentation function))
-			(args (and doc
-				   (string-match
-				    "[\n\t ]*\narguments: ?(\\([^)]*\\))\n?\\'"
-				    doc)
-				   (match-string 1 doc)))
-                        (args (and args (replace-in-string args
-                                                           "[ ]*\\\\\n[ \t]*"
-                                                           " " t))))
-		   ;; If there are no arguments documented for the
-		   ;; subr, rather don't print anything.
-		   (cond ((null args) t)
-			 ((equal args "") nil)
-			 (args))))
-		(t t)))
+	  (or args
+	      (cond ((compiled-function-p fndef)
+		     (compiled-function-arglist fndef))
+		    ((eq (car-safe fndef) 'lambda)
+		     (nth 1 fndef))
+		    ((or (subrp fndef) (eq 'autoload (car-safe fndef)))
+		 
+		     ;; If there are no arguments documented for the
+		     ;; subr, rather don't print anything.
+		     (cond ((null args) t)
+			   ((equal args "") nil)
+			   (args)))
+		    (t t))))
          (print-gensym nil))
     (cond ((listp arglist)
 	   (prin1-to-string
@@ -1217,20 +1211,31 @@
 	  ((stringp arglist)
 	   (format "(%s %s)" function arglist)))))
 
-(defun function-documentation (function &optional strip-arglist)
-  "Return a string giving the documentation for FUNCTION, if any.
-If the optional argument STRIP-ARGLIST is non-nil, remove the arglist
-part of the documentation of internal subroutines."
+;; If STRIP-ARGLIST is true, return a cons (DOC . ARGS) of the documentation
+;; with any embedded arglist stripped out, and the arglist that was stripped
+;; out.  If STRIP-ARGLIST is false, the cons will be (FULL-DOC . nil),
+;; where FULL-DOC is the full documentation without the embedded arglist
+;; stripped out.
+(defun function-documentation-1 (function &optional strip-arglist)
   (let ((doc (condition-case nil
 		 (or (documentation function)
 		     (gettext "not documented"))
 	       (void-function "(alias for undefined function)")
-	       (error "(unexpected error from `documention')"))))
+	       (error "(unexpected error from `documentation')")))
+	args)
     (when (and strip-arglist
-               (string-match "[\n\t ]*\narguments: ?(\\([^)]*\\))\n?\\'" doc))
+               (string-match "[\n\t ]*\narguments: ?(\\(.*\\))\n?\\'" doc))
+      (setq args (match-string 1 doc))
       (setq doc (substring doc 0 (match-beginning 0)))
+      (and args (setq args (replace-in-string args "[ ]*\\\\\n[ \t]*" " " t)))
       (and (zerop (length doc)) (setq doc (gettext "not documented"))))
-    doc))
+    (cons doc args)))
+
+(defun function-documentation (function &optional strip-arglist)
+  "Return a string giving the documentation for FUNCTION, if any.
+If the optional argument STRIP-ARGLIST is non-nil, remove the arglist
+part of the documentation of internal subroutines, CL lambda forms, etc."
+  (car (function-documentation-1 function strip-arglist)))
 
 ;; replacement for `princ' that puts the text in the specified face,
 ;; if possible
--- a/lisp/mule/make-coding-system.el	Mon Feb 22 06:49:30 2010 -0600
+++ b/lisp/mule/make-coding-system.el	Tue Feb 23 07:28:35 2010 -0600
@@ -2,6 +2,7 @@
 ;;; much of the implementation of the fixed-width coding system type.
 
 ;; Copyright (C) 2009 Free Software Foundation
+;; Copyright (C) 2010 Ben Wing.
 
 ;; Author: Aidan Kehoe
 
@@ -26,13 +27,6 @@
 
 ;;; Code:
 
-(defvar fixed-width-private-use-start (decode-char 'ucs #xE000)
-  "Start of a 256 code private use area for fixed-width coding systems.
-
-This is used to ensure that distinct octets on disk for a given coding
-system map to distinct XEmacs characters, preventing spurious changes when
-a file is read, not changed, and then written.  ")
-
 (defun fixed-width-generate-helper (decode-table encode-table
 				   encode-failure-octet)
   "Helper func, `fixed-width-generate-encode-program-and-skip-chars-strings',
@@ -323,7 +317,7 @@
   (check-argument-type #'listp unicode-map)
   (let ((decode-table (make-vector 256 nil))
         (encode-table (make-hash-table :size 256 :rehash-threshold 0.999))
-	(private-use-start (encode-char fixed-width-private-use-start 'ucs))
+	(private-use-start #xE000)
         (invalid-sequence-code-point-start
          (eval-when-compile
            (char-to-unicode
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/test-harness.el	Tue Feb 23 07:28:35 2010 -0600
@@ -0,0 +1,835 @@
+;; test-harness.el --- Run Emacs Lisp test suites.
+
+;;; Copyright (C) 1998, 2002, 2003 Free Software Foundation, Inc.
+;;; Copyright (C) 2002, 2010 Ben Wing.
+
+;; Author: Martin Buchholz
+;; Maintainer: Stephen J. Turnbull <stephen@xemacs.org>
+;; Keywords: testing
+
+;; 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: Not in FSF.
+
+;;; Commentary:
+
+;;; A test suite harness for testing XEmacs.
+;;; The actual tests are in other files in this directory.
+;;; Basically you just create files of emacs-lisp, and use the
+;;; Assert, Check-Error, Check-Message, and Check-Error-Message functions
+;;; to create tests.  See `test-harness-from-buffer' below.
+;;; Don't suppress tests just because they're due to known bugs not yet
+;;; fixed -- use the Known-Bug-Expect-Failure and
+;;; Implementation-Incomplete-Expect-Failure wrapper macros to mark them.
+;;; A lot of the tests we run push limits; suppress Ebola message with the
+;;; Ignore-Ebola wrapper macro.
+;;; Some noisy code will call `message'.  Output from `message' can be
+;;; suppressed with the Silence-Message macro.  Functions that are known to
+;;; issue messages include `write-region', `find-tag', `tag-loop-continue',
+;;; `insert', and `mark-whole-buffer'.  N.B. The Silence-Message macro
+;;; currently does not suppress the newlines printed by `message'.
+;;; Definitely do not use Silence-Message with Check-Message.
+;;; In general it should probably only be used on code that prepares for a
+;;; test, not on tests.
+;;; 
+;;; You run the tests using M-x test-emacs-test-file,
+;;; or $(EMACS) -batch -l test-harness -f batch-test-emacs file ...
+;;; which is run for you by the `make check' target in the top-level Makefile.
+
+(require 'bytecomp)
+
+(defvar unexpected-test-suite-failures 0
+  "Cumulative number of unexpected failures since test-harness was loaded.
+
+\"Unexpected failures\" are those caught by a generic handler established
+outside of the test context.  As such they involve an abort of the test
+suite for the file being tested.
+
+They often occur during preparation of a test or recording of the results.
+For example, an executable used to generate test data might not be present
+on the system, or a system error might occur while reading a data file.")
+
+(defvar unexpected-test-suite-failure-files nil
+  "List of test files causing unexpected failures.")
+
+;; Declared for dynamic scope; _do not_ initialize here.
+(defvar unexpected-test-file-failures)
+
+(defvar test-harness-bug-expected nil
+  "Non-nil means a bug is expected; backtracing/debugging should not happen.")
+
+(defvar test-harness-test-compiled nil
+  "Non-nil means the test code was compiled before execution.
+
+You probably should not make tests depend on compilation.
+However, it can be useful to conditionally change messages based on whether
+the code was compiled or not.  For example, the case that motivated the
+implementation of this variable:
+
+\(when test-harness-test-compiled
+  ;; this ha-a-ack depends on the failing compiled test coming last
+  \(setq test-harness-failure-tag
+	\"KNOWN BUG - fix reverted; after 2003-10-31 notify stephen\n\"))")
+
+(defvar test-harness-verbose
+  (and (not noninteractive) (> (device-baud-rate) search-slow-speed))
+  "*Non-nil means print messages describing progress of emacs-tester.")
+
+(defvar test-harness-unexpected-error-enter-debugger debug-on-error
+  "*Non-nil means enter debugger when an unexpected error occurs.
+Only applies interactively.  Normally true if `debug-on-error' has been set.
+See also `test-harness-assertion-failure-enter-debugger' and
+`test-harness-unexpected-error-show-backtrace'.")
+
+(defvar test-harness-assertion-failure-enter-debugger debug-on-error
+  "*Non-nil means enter debugger when an assertion failure occurs.
+Only applies interactively.  Normally true if `debug-on-error' has been set.
+See also `test-harness-unexpected-error-enter-debugger' and
+`test-harness-assertion-failure-show-backtrace'.")
+
+(defvar test-harness-unexpected-error-show-backtrace t
+  "*Non-nil means show backtrace upon unexpected error.
+Only applies when debugger is not entered.  Normally true by default.  See also
+`test-harness-unexpected-error-enter-debugger' and
+`test-harness-assertion-failure-show-backtrace'.")
+
+(defvar test-harness-assertion-failure-show-backtrace stack-trace-on-error
+  "*Non-nil means show backtrace upon assertion failure.
+Only applies when debugger is not entered.  Normally true if
+`stack-trace-on-error' has been set.  See also
+`test-harness-assertion-failure-enter-debugger' and
+`test-harness-unexpected-error-show-backtrace'.")
+
+(defvar test-harness-file-results-alist nil
+  "Each element is a list (FILE SUCCESSES TESTS).
+The order is the reverse of the order in which tests are run.
+
+FILE is a string naming the test file.
+SUCCESSES is a non-negative integer, the number of successes.
+TESTS is a non-negative integer, the number of tests run.")
+
+(defvar test-harness-risk-infloops nil
+  "*Non-nil to run tests that may loop infinitely in buggy implementations.")
+
+(defvar test-harness-current-file nil)
+
+(defvar emacs-lisp-file-regexp (purecopy "\\.el\\'")
+  "*Regexp which matches Emacs Lisp source files.")
+
+(defconst test-harness-file-summary-template
+  (format "%%-%ds %%%dd of %%%dd tests successful (%%3d%%%%)."
+	  (length "byte-compiler-tests.el:") ; use the longest file name
+	  5
+	  5)
+  "Format for summary lines printed after each file is run.")
+
+(defconst test-harness-null-summary-template
+  (format "%%-%ds             No tests run."
+	  (length "byte-compiler-tests.el:")) ; use the longest file name
+  "Format for \"No tests\" lines printed after a file is run.")
+
+(defconst test-harness-aborted-summary-template
+  (format "%%-%ds          %%%dd tests completed (aborted)."
+	  (length "byte-compiler-tests.el:") ; use the longest file name
+	  5)
+  "Format for summary lines printed after a test run on a file was aborted.")
+
+;;;###autoload
+(defun test-emacs-test-file (filename)
+  "Test a file of Lisp code named FILENAME.
+The output file's name is made by appending `c' to the end of FILENAME."
+  (interactive
+   (let ((file buffer-file-name)
+	 (file-name nil)
+	 (file-dir nil))
+     (and file
+	  (eq (cdr (assq 'major-mode (buffer-local-variables)))
+	      'emacs-lisp-mode)
+	  (setq file-name (file-name-nondirectory file)
+		file-dir (file-name-directory file)))
+     (list (read-file-name "Test file: " file-dir nil nil file-name))))
+  ;; Expand now so we get the current buffer's defaults
+  (setq filename (expand-file-name filename))
+
+  ;; If we're testing a file that's in a buffer and is modified, offer
+  ;; to save it first.
+  (or noninteractive
+      (let ((b (get-file-buffer (expand-file-name filename))))
+	(if (and b (buffer-modified-p b)
+		 (y-or-n-p (format "save buffer %s first? " (buffer-name b))))
+	    (save-excursion (set-buffer b) (save-buffer)))))
+
+  (if (or noninteractive test-harness-verbose)
+      (message "Testing %s..." filename))
+  (let ((test-harness-current-file filename)
+	input-buffer)
+    (save-excursion
+      (setq input-buffer (get-buffer-create " *Test Input*"))
+      (set-buffer input-buffer)
+      (erase-buffer)
+      (insert-file-contents filename)
+      ;; Run hooks including the uncompression hook.
+      ;; If they change the file name, then change it for the output also.
+      (let ((buffer-file-name filename)
+	    (default-major-mode 'emacs-lisp-mode)
+	    (enable-local-eval nil))
+        (normal-mode)
+        (setq filename buffer-file-name)))
+    (test-harness-from-buffer input-buffer filename)
+    (kill-buffer input-buffer)
+    ))
+
+(defsubst test-harness-assertion-failure-do-debug (error-info)
+  "Maybe enter debugger or display a backtrace on assertion failure.
+ERROR-INFO is a cons of the args (SIG . DATA) that were passed to `signal'.
+The debugger will be entered if noninteractive and
+`test-harness-unexpected-error-enter-debugger' is non-nil; else, a
+backtrace will be displayed if `test-harness-unexpected-error-show-backtrace'
+is non-nil."
+  (when (not test-harness-bug-expected)
+    (cond ((and (not noninteractive)
+		test-harness-assertion-failure-enter-debugger)
+	   (funcall debugger 'error error-info))
+	  (test-harness-assertion-failure-show-backtrace
+	   (backtrace nil t)))))
+
+(defsubst test-harness-unexpected-error-do-debug (error-info)
+  "Maybe enter debugger or display a backtrace on unexpected error.
+ERROR-INFO is a cons of the args (SIG . DATA) that were passed to `signal'.
+The debugger will be entered if noninteractive and
+`test-harness-unexpected-error-enter-debugger' is non-nil; else, a
+backtrace will be displayed if `test-harness-unexpected-error-show-backtrace'
+is non-nil."
+  (when (not test-harness-bug-expected)
+    (cond ((and (not noninteractive)
+		test-harness-unexpected-error-enter-debugger)
+	   (funcall debugger 'error error-info))
+	  (test-harness-unexpected-error-show-backtrace
+	   (backtrace nil t)))))
+
+(defsubst test-harness-unexpected-error-condition-handler (error-info context-msg)
+  "Condition handler for when unexpected errors occur.
+Useful in conjunction with `call-with-condition-handler'.  ERROR-INFO is the
+value passed to the condition handler.  CONTEXT-MSG is a string indicating
+the context in which the unexpected error occurred.  A message is outputted
+including CONTEXT-MSG in it, `unexpected-test-file-failures' is incremented,
+and `test-harness-unexpected-error-do-debug' is called, which may enter the
+debugger or output a backtrace, depending on the settings of
+`test-harness-unexpected-error-enter-debugger' and
+`test-harness-unexpected-error-show-backtrace'.
+
+The function returns normally, which causes error-handling processing to
+continue; if you want to catch the error, you also need to wrap everything
+in `condition-case'.  See also `test-harness-error-wrap', which does this
+wrapping."
+  (incf unexpected-test-file-failures)
+  (princ (format "Unexpected error %S while %s\n"
+		 error-info context-msg))
+  (message "Unexpected error %S while %s." error-info context-msg)
+  (test-harness-unexpected-error-do-debug error-info))
+
+(defmacro test-harness-error-wrap (context-msg abort-msg &rest body)
+  "Wrap BODY so that unexpected errors are caught.
+The debugger will be entered if noninteractive and
+`test-harness-unexpected-error-enter-debugger' is non-nil; else, a backtrace
+will be displayed if `test-harness-unexpected-error-show-backtrace' is
+non-nil.  CONTEXT-MSG is displayed as part of a message shown before entering
+the debugger or showing a backtrace, and ABORT-MSG, if non-nil, is displayed
+afterwards.  See "
+  `(condition-case nil
+    (call-with-condition-handler
+	#'(lambda (error-info)
+	    (test-harness-unexpected-error-condition-handler
+	     error-info ,context-msg))
+	#'(lambda ()
+	    ,@body))
+    (error ,(if abort-msg `(message ,abort-msg) nil))))
+
+(defun test-harness-read-from-buffer (buffer)
+  "Read forms from BUFFER, and turn it into a lambda test form."
+  (let ((body nil))
+    (goto-char (point-min) buffer)
+    (condition-case nil
+	(call-with-condition-handler
+	    #'(lambda (error-info)
+		;; end-of-file is expected, so don't output error or backtrace
+		;; or enter debugger in this case.
+		(unless (eq 'end-of-file (car error-info))
+		  (test-harness-unexpected-error-condition-handler
+		   error-info "reading forms from buffer")))
+	    #'(lambda ()
+		(while t
+		  (setq body (cons (read buffer) body)))))
+      (error nil))
+    `(lambda ()
+       (defvar passes)
+       (defvar assertion-failures)
+       (defvar no-error-failures)
+       (defvar wrong-error-failures)
+       (defvar missing-message-failures)
+       (defvar other-failures)
+
+       (defvar trick-optimizer)
+
+       ,@(nreverse body))))
+
+(defun test-harness-from-buffer (inbuffer filename)
+  "Run tests in buffer INBUFFER, visiting FILENAME."
+  (defvar trick-optimizer)
+  (let ((passes 0)
+	(assertion-failures 0)
+	(no-error-failures 0)
+	(wrong-error-failures 0)
+	(missing-message-failures 0)
+	(other-failures 0)
+	(unexpected-test-file-failures 0)
+
+	;; #### perhaps this should be a defvar, and output at the very end
+	;; OTOH, this way AC types can use a null EMACSPACKAGEPATH to find
+	;; what stuff is needed, and ways to avoid using them
+	(skipped-test-reasons (make-hash-table :test 'equal))
+
+	(trick-optimizer nil)
+	(debug-on-error t)
+	)
+    (with-output-to-temp-buffer "*Test-Log*"
+      (princ (format "Testing %s...\n\n" filename))
+
+      (defconst test-harness-failure-tag "FAIL")
+      (defconst test-harness-success-tag "PASS")
+
+;;;;; BEGIN DEFINITION OF MACROS USEFUL IN TEST CODE
+
+      (defmacro Known-Bug-Expect-Failure (&rest body)
+	"Wrap a BODY that consists of tests that are known to fail.
+This causes messages to be printed on failure indicating that this is expected,
+and on success indicating that this is unexpected."
+	`(let ((test-harness-bug-expected t)
+	       (test-harness-failure-tag "KNOWN BUG")
+	       (test-harness-success-tag "PASS (FAILURE EXPECTED)"))
+	  ,@body))
+
+      (defmacro Known-Bug-Expect-Error (expected-error &rest body)
+	"Wrap a BODY that consists of tests that are known to trigger an error.
+This causes messages to be printed on failure indicating that this is expected,
+and on success indicating that this is unexpected."
+	(let ((quoted-body (if (= 1 (length body))
+			       `(quote ,(car body)) `(quote (progn ,@body)))))
+          `(let ((test-harness-bug-expected t)
+		 (test-harness-failure-tag "KNOWN BUG")
+                 (test-harness-success-tag "PASS (FAILURE EXPECTED)"))
+            (condition-case error-info
+                (progn
+                  (setq trick-optimizer (progn ,@body))
+                  (Print-Pass 
+                   "%S executed successfully, but expected error %S"
+                   ,quoted-body
+                   ',expected-error)
+                  (incf passes))
+              (,expected-error
+               (Print-Failure "%S ==> error %S, as expected"
+                              ,quoted-body ',expected-error)
+               (incf no-error-failures))
+              (error
+               (Print-Failure "%S ==> expected error %S, got error %S instead"
+                              ,quoted-body ',expected-error error-info)
+               (incf wrong-error-failures))))))
+
+      (defmacro Implementation-Incomplete-Expect-Failure (&rest body)
+	"Wrap a BODY containing tests that are known to fail due to incomplete code.
+This causes messages to be printed on failure indicating that the
+implementation is incomplete (and hence the failure is expected); and on
+success indicating that this is unexpected."
+	`(let ((test-harness-bug-expected t)
+	       (test-harness-failure-tag "IMPLEMENTATION INCOMPLETE")
+	       (test-harness-success-tag "PASS (FAILURE EXPECTED)"))
+	  ,@body))
+    
+      (defun Print-Failure (fmt &rest args)
+	(setq fmt (format "%s: %s" test-harness-failure-tag fmt))
+	(if (noninteractive) (apply #'message fmt args))
+	(princ (concat (apply #'format fmt args) "\n")))
+
+      (defun Print-Pass (fmt &rest args)
+	(setq fmt (format "%s: %s" test-harness-success-tag fmt))
+	(and test-harness-verbose
+	     (princ (concat (apply #'format fmt args) "\n"))))
+
+      (defun Print-Skip (test reason &optional fmt &rest args)
+	(setq fmt (concat "SKIP: %S BECAUSE %S" fmt))
+	(princ (concat (apply #'format fmt test reason args) "\n")))
+
+      (defmacro Skip-Test-Unless (condition reason description &rest body)
+	"Unless CONDITION is satisfied, skip test BODY.
+REASON is a description of the condition failure, and must be unique (it
+is used as a hash key).  DESCRIPTION describes the tests that were skipped.
+BODY is a sequence of expressions and may contain several tests."
+	`(if (not ,condition)
+	     (let ((count (gethash ,reason skipped-test-reasons)))
+	       (puthash ,reason (if (null count) 1 (1+ count))
+			skipped-test-reasons)
+	       (Print-Skip ,description ,reason))
+	   ,@body))
+
+      (defmacro Assert (assertion &optional failing-case description)
+	"Test passes if ASSERTION is true.
+Optional FAILING-CASE describes the particular failure.  Optional
+DESCRIPTION describes the assertion; by default, the unevalated assertion
+expression is given.  FAILING-CASE and DESCRIPTION are useful when Assert
+is used in a loop."
+	(let ((description
+	       (or description `(quote ,assertion))))
+	  `(condition-case nil
+	    (call-with-condition-handler
+		#'(lambda (error-info)
+		    (if (eq 'cl-assertion-failed (car error-info))
+			(progn
+			  (Print-Failure
+			   (if ,failing-case
+			       "Assertion failed: %S; failing case = %S"
+			     "Assertion failed: %S")
+			   ,description ,failing-case)
+			  (incf assertion-failures)
+			  (test-harness-assertion-failure-do-debug error-info))
+		      (Print-Failure
+		       (if ,failing-case
+			   "%S ==> error: %S; failing case =  %S"
+			 "%S ==> error: %S")
+		       ,description error-info ,failing-case)
+		      (incf other-failures)
+		      (test-harness-unexpected-error-do-debug error-info)))
+		#'(lambda ()
+		    (assert ,assertion)
+		    (Print-Pass "%S" ,description)
+		    (incf passes)))
+	    (cl-assertion-failed nil))))
+
+;;;;; BEGIN DEFINITION OF SPECIFIC KINDS OF ASSERT MACROS
+
+      (defmacro Assert-test (test testval expected &optional failing-case
+			     description)
+	"Test passes if TESTVAL compares correctly to EXPECTED using TEST.
+TEST should be a two-argument predicate (i.e. a function of two arguments
+that returns t or nil), such as `eq', `eql', `equal', `equalp', `=', `<=',
+'>', 'file-newer-than-file-p' etc.  Optional FAILING-CASE describes the
+particular failure; any value given here will be concatenated with a phrase
+describing the expected and actual values of the comparison.  Optional
+DESCRIPTION describes the assertion; by default, the unevalated comparison
+expressions are given.  FAILING-CASE and DESCRIPTION are useful when Assert
+is used in a loop."
+	(let* ((assertion `(,test ,testval ,expected))
+	       (failmsg `(format "%S should be `%s' to %S but isn't"
+			  ,testval ',test ,expected))
+	       (failmsg2 (if failing-case `(concat 
+					   (format "%S, " ,failing-case)
+					   ,failmsg)
+			  failmsg)))
+	  `(Assert ,assertion ,failmsg2 ,description)))
+
+      (defmacro Assert-test-not (test testval expected &optional failing-case
+				 description)
+	"Test passes if TESTVAL does not compare correctly to EXPECTED using TEST.
+TEST should be a two-argument predicate (i.e. a function of two arguments
+that returns t or nil), such as `eq', `eql', `equal', `equalp', `=', `<=',
+'>', 'file-newer-than-file-p' etc.  Optional FAILING-CASE describes the
+particular failure; any value given here will be concatenated with a phrase
+describing the expected and actual values of the comparison.  Optional
+DESCRIPTION describes the assertion; by default, the unevalated comparison
+expressions are given.  FAILING-CASE and DESCRIPTION are useful when Assert
+is used in a loop."
+	(let* ((assertion `(not (,test ,testval ,expected)))
+	       (failmsg `(format "%S shouldn't be `%s' to %S but is"
+			  ,testval ',test ,expected))
+	       (failmsg2 (if failing-case `(concat 
+					   (format "%S, " ,failing-case)
+					   ,failmsg)
+			  failmsg)))
+	  `(Assert ,assertion ,failmsg2 ,description)))
+
+      ;; Specific versions of `Assert-test'.  These are just convenience
+      ;; functions, functioning identically to `Assert-test', and duplicating
+      ;; the doc string for each would be too annoying.
+      (defmacro Assert-eq (testval expected &optional failing-case
+			   description)
+	`(Assert-test eq ,testval ,expected ,failing-case ,description))
+      (defmacro Assert-eql (testval expected &optional failing-case
+			    description)
+	`(Assert-test eql ,testval ,expected ,failing-case ,description))
+      (defmacro Assert-equal (testval expected &optional failing-case
+			      description)
+	`(Assert-test equal ,testval ,expected ,failing-case ,description))
+      (defmacro Assert-equalp (testval expected &optional failing-case
+			      description)
+	`(Assert-test equalp ,testval ,expected ,failing-case ,description))
+      (defmacro Assert-string= (testval expected &optional failing-case
+			      description)
+	`(Assert-test string= ,testval ,expected ,failing-case ,description))
+      (defmacro Assert= (testval expected &optional failing-case
+			 description)
+	`(Assert-test = ,testval ,expected ,failing-case ,description))
+      (defmacro Assert<= (testval expected &optional failing-case
+			  description)
+	`(Assert-test <= ,testval ,expected ,failing-case ,description))
+
+      ;; Specific versions of `Assert-test-not'.  These are just convenience
+      ;; functions, functioning identically to `Assert-test-not', and
+      ;; duplicating the doc string for each would be too annoying.
+      (defmacro Assert-not-eq (testval expected &optional failing-case
+			       description)
+	`(Assert-test-not eq ,testval ,expected ,failing-case ,description))
+      (defmacro Assert-not-eql (testval expected &optional failing-case
+				description)
+	`(Assert-test-not eql ,testval ,expected ,failing-case ,description))
+      (defmacro Assert-not-equal (testval expected &optional failing-case
+				  description)
+	`(Assert-test-not equal ,testval ,expected ,failing-case ,description))
+      (defmacro Assert-not-equalp (testval expected &optional failing-case
+				   description)
+	`(Assert-test-not equalp ,testval ,expected ,failing-case ,description))
+      (defmacro Assert-not-string= (testval expected &optional failing-case
+				    description)
+	`(Assert-test-not string= ,testval ,expected ,failing-case ,description))
+      (defmacro Assert-not= (testval expected &optional failing-case
+			     description)
+	`(Assert-test-not = ,testval ,expected ,failing-case ,description))
+
+      (defmacro Check-Error (expected-error &rest body)
+	(let ((quoted-body (if (= 1 (length body))
+			       `(quote ,(car body)) `(quote (progn ,@body)))))
+	  `(condition-case error-info
+	       (progn
+		 (setq trick-optimizer (progn ,@body))
+		 (Print-Failure "%S executed successfully, but expected error %S"
+				,quoted-body
+				',expected-error)
+		 (incf no-error-failures))
+	     (,expected-error
+	      (Print-Pass "%S ==> error %S, as expected"
+			  ,quoted-body ',expected-error)
+	      (incf passes))
+	     (error
+	      (Print-Failure "%S ==> expected error %S, got error %S instead"
+			     ,quoted-body ',expected-error error-info)
+	      (incf wrong-error-failures)))))
+
+      (defmacro Check-Error-Message (expected-error expected-error-regexp
+						    &rest body)
+	(let ((quoted-body (if (= 1 (length body))
+			       `(quote ,(car body)) `(quote (progn ,@body)))))
+	  `(condition-case error-info
+	       (progn
+		 (setq trick-optimizer (progn ,@body))
+		 (Print-Failure "%S executed successfully, but expected error %S"
+				,quoted-body ',expected-error)
+		 (incf no-error-failures))
+	     (,expected-error
+	      ;; #### Damn, this binding doesn't capture frobs, eg, for
+	      ;; invalid_argument() ... you only get the REASON.  And for
+	      ;; wrong_type_argument(), there's no reason only FROBs.
+	      ;; If this gets fixed, fix tests in regexp-tests.el.
+	      (let ((error-message (second error-info)))
+		(if (string-match ,expected-error-regexp error-message)
+		    (progn
+		      (Print-Pass "%S ==> error %S %S, as expected"
+				  ,quoted-body error-message ',expected-error)
+		      (incf passes))
+		  (Print-Failure "%S ==> got error %S as expected, but error message %S did not match regexp %S"
+				 ,quoted-body ',expected-error error-message ,expected-error-regexp)
+		  (incf wrong-error-failures))))
+	     (error
+	      (Print-Failure "%S ==> expected error %S, got error %S instead"
+			     ,quoted-body ',expected-error error-info)
+	      (incf wrong-error-failures)))))
+
+      ;; Do not use this with Silence-Message.
+      (defmacro Check-Message (expected-message-regexp &rest body)
+	(let ((quoted-body (if (= 1 (length body))
+			       `(quote ,(car body))
+			     `(quote (progn ,@body)))))
+	  `(Skip-Test-Unless (fboundp 'defadvice) "can't defadvice"
+	    expected-message-regexp
+	    (let ((messages ""))
+	      (defadvice message (around collect activate)
+		(defvar messages)
+		(let ((msg-string (apply 'format (ad-get-args 0))))
+		  (setq messages (concat messages msg-string))
+		  msg-string))
+	      (ignore-errors
+		(call-with-condition-handler
+		    #'(lambda (error-info)
+			(Print-Failure "%S ==> unexpected error %S"
+				       ,quoted-body error-info)
+			(incf other-failures)
+			(test-harness-unexpected-error-do-debug error-info))
+		    #'(lambda ()
+			(setq trick-optimizer (progn ,@body))
+			(if (string-match ,expected-message-regexp messages)
+			    (progn
+			      (Print-Pass
+			       "%S ==> value %S, message %S, matching %S, as expected"
+			       ,quoted-body trick-optimizer messages
+			       ',expected-message-regexp)
+			      (incf passes))
+			  (Print-Failure
+			   "%S ==> value %S, message %S, NOT matching expected %S"
+			   ,quoted-body  trick-optimizer messages
+			   ',expected-message-regexp)
+			  (incf missing-message-failures)))))
+	      (ad-unadvise 'message)))))
+
+      ;; #### Perhaps this should override `message' itself, too?
+      (defmacro Silence-Message (&rest body)
+	`(flet ((append-message (&rest args) ())
+                (clear-message (&rest args) ()))
+          ,@body))
+
+      (defmacro Ignore-Ebola (&rest body)
+	`(let ((debug-issue-ebola-notices -42)) ,@body))
+
+      (defun Int-to-Marker (pos)
+	(save-excursion
+	  (set-buffer standard-output)
+	  (save-excursion
+	    (goto-char pos)
+	    (point-marker))))
+
+      (princ "Testing Interpreted Lisp\n\n")
+
+      (test-harness-error-wrap
+       "executing interpreted code"
+       "Test suite execution aborted."
+       (funcall (test-harness-read-from-buffer inbuffer)))
+
+      (princ "\nTesting Compiled Lisp\n\n")
+
+      (let (code
+	    (test-harness-test-compiled t))
+	(test-harness-error-wrap
+	 "byte-compiling code" nil
+	 (setq code
+	       ;; our lisp code is often intentionally dubious,
+	       ;; so throw away _all_ the byte compiler warnings.
+	       (letf (((symbol-function 'byte-compile-warn)
+		       'ignore))
+		 (byte-compile (test-harness-read-from-buffer
+				inbuffer))))
+	 )
+
+	(test-harness-error-wrap "executing byte-compiled code"
+				 "Test suite execution aborted."
+				 (if code (funcall code)))
+	)
+      (princ (format "\nSUMMARY for %s:\n" filename))
+      (princ (format "\t%5d passes\n" passes))
+      (princ (format "\t%5d assertion failures\n" assertion-failures))
+      (princ (format "\t%5d errors that should have been generated, but weren't\n" no-error-failures))
+      (princ (format "\t%5d wrong-error failures\n" wrong-error-failures))
+      (princ (format "\t%5d missing-message failures\n" missing-message-failures))
+      (princ (format "\t%5d other failures\n" other-failures))
+      (let* ((total (+ passes
+		       assertion-failures
+		       no-error-failures
+		       wrong-error-failures
+		       missing-message-failures
+		       other-failures))
+	     (basename (file-name-nondirectory filename))
+	     (summary-msg
+	      (cond ((> unexpected-test-file-failures 0)
+		     (format test-harness-aborted-summary-template
+			     (concat basename ":") total))
+		    ((> total 0)
+		     (format test-harness-file-summary-template
+			     (concat basename ":")
+			     passes total (/ (* 100 passes) total)))
+		    (t
+		     (format test-harness-null-summary-template
+			     (concat basename ":")))))
+	     (reasons ""))
+	(maphash (lambda (key value)
+		   (setq reasons
+			 (concat reasons
+				 (format "\n    %d tests skipped because %s."
+					 value key))))
+		 skipped-test-reasons)
+	(when (> (length reasons) 1)
+	  (setq summary-msg (concat summary-msg reasons "
+    It may be that XEmacs cannot find your installed packages.  Set
+    EMACSPACKAGEPATH to the package hierarchy root or configure with
+    --package-path to enable the skipped tests.")))
+	(setq test-harness-file-results-alist
+	      (cons (list filename passes total)
+		    test-harness-file-results-alist))
+	(message "%s" summary-msg))
+      (when (> unexpected-test-file-failures 0)
+	(setq unexpected-test-suite-failure-files
+	      (cons filename unexpected-test-suite-failure-files))
+	(setq unexpected-test-suite-failures
+	      (+ unexpected-test-suite-failures unexpected-test-file-failures))
+	(message "Test suite execution failed unexpectedly."))
+      (fmakunbound 'Assert)
+      (fmakunbound 'Check-Error)
+      (fmakunbound 'Check-Message)
+      (fmakunbound 'Check-Error-Message)
+      (fmakunbound 'Ignore-Ebola)
+      (fmakunbound 'Int-to-Marker)
+      (and noninteractive
+	   (message "%s" (buffer-substring-no-properties
+			  nil nil "*Test-Log*")))
+      )))
+
+(defvar test-harness-results-point-max nil)
+(defmacro displaying-emacs-test-results (&rest body)
+  `(let ((test-harness-results-point-max test-harness-results-point-max))
+     ;; Log the file name.
+     (test-harness-log-file)
+     ;; Record how much is logged now.
+     ;; We will display the log buffer if anything more is logged
+     ;; before the end of BODY.
+     (or test-harness-results-point-max
+	 (save-excursion
+	   (set-buffer (get-buffer-create "*Test-Log*"))
+	   (setq test-harness-results-point-max (point-max))))
+     (unwind-protect
+	 (condition-case error-info
+	     (progn ,@body)
+	   (error
+	    (test-harness-report-error error-info)))
+       (save-excursion
+	 ;; If there were compilation warnings, display them.
+	 (set-buffer "*Test-Log*")
+	 (if (= test-harness-results-point-max (point-max))
+	     nil
+	   (if temp-buffer-show-function
+	       (let ((show-buffer (get-buffer-create "*Test-Log-Show*")))
+		 (save-excursion
+		   (set-buffer show-buffer)
+		   (setq buffer-read-only nil)
+		   (erase-buffer))
+		 (copy-to-buffer show-buffer
+				 (save-excursion
+				   (goto-char test-harness-results-point-max)
+				   (forward-line -1)
+				   (point))
+				 (point-max))
+		 (funcall temp-buffer-show-function show-buffer))
+              (select-window
+               (prog1 (selected-window)
+                 (select-window (display-buffer (current-buffer)))
+                 (goto-char test-harness-results-point-max)
+                 (recenter 1)))))))))
+
+(defun batch-test-emacs-1 (file)
+  (condition-case error-info
+      (progn (test-emacs-test-file file) t)
+    (error
+     (princ ">>Error occurred processing ")
+     (princ file)
+     (princ ": ")
+     (display-error error-info nil)
+     (terpri)
+     nil)))
+
+(defun batch-test-emacs ()
+  "Run `test-harness' on the files remaining on the command line.
+Use this from the command line, with `-batch';
+it won't work in an interactive Emacs.
+Each file is processed even if an error occurred previously.
+A directory can be given as well, and all files will be processed.
+For example, invoke \"xemacs -batch -f batch-test-emacs tests\""
+  ;; command-line-args-left is what is left of the command line (from
+  ;; startup.el)
+  (defvar command-line-args-left)	;Avoid 'free variable' warning
+  (defvar debug-issue-ebola-notices)
+  (if (not noninteractive)
+      (error "`batch-test-emacs' is to be used only with -batch"))
+  (let ((error nil))
+    (dolist (file command-line-args-left)
+      (if (file-directory-p file)
+	  (dolist (file-in-dir (directory-files file t))
+	    (when (and (string-match emacs-lisp-file-regexp file-in-dir)
+		       (not (or (auto-save-file-name-p file-in-dir)
+				(backup-file-name-p file-in-dir))))
+	      (or (batch-test-emacs-1 file-in-dir)
+		  (setq error t))))
+	(or (batch-test-emacs-1 file)
+	    (setq error t))))
+    (let ((namelen 0)
+	  (succlen 0)
+	  (testlen 0)
+	  (results test-harness-file-results-alist))
+      ;; compute maximum lengths of variable components of report
+      ;; probably should just use (length "byte-compiler-tests.el")
+      ;; and 5-place sizes -- this will also work for the file-by-file
+      ;; printing when Adrian's kludge gets reverted
+      (flet ((print-width (i)
+	       (let ((x 10) (y 1))
+		 (while (>= i x)
+		   (setq x (* 10 x) y (1+ y)))
+		 y)))
+	(while results
+	  (let* ((head (car results))
+		 (nn (length (file-name-nondirectory (first head))))
+		 (ss (print-width (second head)))
+		 (tt (print-width (third head))))
+	    (when (> nn namelen) (setq namelen nn))
+	    (when (> ss succlen) (setq succlen ss))
+	    (when (> tt testlen) (setq testlen tt)))
+	  (setq results (cdr results))))
+      ;; create format and print
+      (let ((results (reverse test-harness-file-results-alist)))
+	(while results
+	  (let* ((head (car results))
+		 (basename (file-name-nondirectory (first head)))
+		 (nsucc (second head))
+		 (ntest (third head)))
+	    (cond ((member (first head) unexpected-test-suite-failure-files)
+		   (message test-harness-aborted-summary-template
+			    (concat basename ":")
+			    ntest))
+		  ((> ntest 0)
+		   (message test-harness-file-summary-template
+			    (concat basename ":")
+			    nsucc
+			    ntest
+			    (/ (* 100 nsucc) ntest)))
+		  (t
+		   (message test-harness-null-summary-template
+			    (concat basename ":"))))
+	    (setq results (cdr results)))))
+      (when (> unexpected-test-suite-failures 0)
+	(message "\n***** There %s %d unexpected test suite %s in %s:"
+		 (if (= unexpected-test-suite-failures 1) "was" "were")
+		 unexpected-test-suite-failures
+		 (if (= unexpected-test-suite-failures 1) "failure" "failures")
+		 (if (= (length unexpected-test-suite-failure-files) 1)
+		     "file"
+		   "files"))
+	(while unexpected-test-suite-failure-files
+	  (let ((line (pop unexpected-test-suite-failure-files)))
+	    (while (and (< (length line) 61)
+			unexpected-test-suite-failure-files)
+	      (setq line
+		    (concat line " "
+			    (pop unexpected-test-suite-failure-files))))
+	    (message line)))))
+    (message "\nDone")
+    (kill-emacs (if error 1 0))))
+
+(provide 'test-harness)
+
+;;; test-harness.el ends here
--- a/src/.gdbinit.in.in	Mon Feb 22 06:49:30 2010 -0600
+++ b/src/.gdbinit.in.in	Tue Feb 23 07:28:35 2010 -0600
@@ -159,7 +159,7 @@
 end
 
 define check-xemacs-arg
-  run -vanilla -batch -l @srcdir@/../tests/automated/test-harness.el -f batch-test-emacs @srcdir@/../tests/$arg0
+  run -vanilla -batch -l test-harness -f batch-test-emacs @srcdir@/../tests/$arg0
 end
 
 define check-xemacs
@@ -178,7 +178,7 @@
 
 define check-temacs-arg
   environment-to-run-temacs
-  run -nd -no-packages -batch -l @srcdir@/../lisp/loadup.el run-temacs -q -batch -l @srcdir@/../tests/automated/test-harness.el -f batch-test-emacs @srcdir@/../tests/$arg0
+  run -nd -no-packages -batch -l @srcdir@/../lisp/loadup.el run-temacs -q -batch -l test-harness -f batch-test-emacs @srcdir@/../tests/$arg0
 
 define check-temacs
   if $argc == 0
--- a/src/ChangeLog	Mon Feb 22 06:49:30 2010 -0600
+++ b/src/ChangeLog	Tue Feb 23 07:28:35 2010 -0600
@@ -93,6 +93,76 @@
 	the old files (e.g. in #include statements, Makefiles,
 	functions like syms_of_objects_x(), etc.).
 
+2010-02-22  Ben Wing  <ben@xemacs.org>
+
+	* .gdbinit.in.in:
+	* Makefile.in.in (batch_test_emacs):
+	test-harness.el is in lisp directory now so change how we call it.
+
+2010-02-22  Ben Wing  <ben@xemacs.org>
+
+	* alloc.c (object_memory_usage_stats):
+	Remove unused var.
+
+2010-02-21  Ben Wing  <ben@xemacs.org>
+
+	* alloc.c:
+	* alloc.c (FREE_FIXED_TYPE_WHEN_NOT_IN_GC):
+	* alloc.c (struct):
+	* alloc.c (tick_lrecord_stats):
+	* alloc.c (tick_lcrecord_stats):
+	* alloc.c (sweep_lcrecords_1):
+	* alloc.c (COUNT_FROB_BLOCK_USAGE):
+	* alloc.c (SWEEP_FIXED_TYPE_BLOCK_1):
+	* alloc.c (free_cons):
+	* alloc.c (free_key_data):
+	* alloc.c (free_button_data):
+	* alloc.c (free_motion_data):
+	* alloc.c (free_process_data):
+	* alloc.c (free_timeout_data):
+	* alloc.c (free_magic_data):
+	* alloc.c (free_magic_eval_data):
+	* alloc.c (free_eval_data):
+	* alloc.c (free_misc_user_data):
+	* alloc.c (free_marker):
+	* alloc.c (gc_sweep_1):
+	* alloc.c (HACK_O_MATIC):
+	* alloc.c (FROB):
+	* alloc.c (object_memory_usage_stats):
+	* alloc.c (Fgarbage_collect):
+	* dumper.c:
+	* dumper.c (pdump_objects_unmark):
+	* lrecord.h:
+	* lrecord.h (enum lrecord_alloc_status):
+	Fixes to memory-usage-tracking code, etc.
+
+	(1) Incorporate NEW_GC stuff into FREE_FIXED_TYPE_WHEN_NOT_IN_GC
+	to avoid duplication.
+
+	(2) Rewrite tick_lcrecord_stats() to include separate
+	tick_lrecord_stats(); use in dumper.c to note pdumped objects.
+
+	(3) Instead of handling frob-block objects specially in
+	object_memory_usage_stats(), have SWEEP_FIXED_TYPE_BLOCK_1
+	increment the stats in lrecord_stats[] so that they get handled
+	like other objects.
+
+	(4) Pluralize entry as entries, etc.
+
+2010-02-21  Ben Wing  <ben@xemacs.org>
+
+	* alloc.c:
+	* alloc.c (pluralize_word):
+	New function to pluralize a word.
+	* alloc.c (pluralize_and_append): New function.
+	* alloc.c (object_memory_usage_stats):
+	Clean up duplication.
+
+2010-02-21  Vin Shelton  <acs@xemacs.org>
+
+	* events.c (event_pixel_translation): Simplify assertion for
+	Visual C 6.
+
 2010-02-21  Ben Wing  <ben@xemacs.org>
 
 	* gc.c (kkcc_marking): Fix compile error.
--- a/src/Makefile.in.in	Mon Feb 22 06:49:30 2010 -0600
+++ b/src/Makefile.in.in	Tue Feb 23 07:28:35 2010 -0600
@@ -882,7 +882,7 @@
 
 ###################### Automated tests
 testdir = $(SRC)/../tests/automated
-batch_test_emacs = $(BATCH_PACKAGES) -l $(testdir)/test-harness.el -f batch-test-emacs $(testdir)
+batch_test_emacs = $(BATCH_PACKAGES) -l test-harness -f batch-test-emacs $(testdir)
 
 ## `config-changed' is useful if you are building both Unicode-internal
 ## and old-Mule workspaces using --srcdir and don't run configure before
--- a/src/alloc.c	Mon Feb 22 06:49:30 2010 -0600
+++ b/src/alloc.c	Tue Feb 23 07:28:35 2010 -0600
@@ -1150,7 +1150,12 @@
   PUT_FIXED_TYPE_ON_FREE_LIST (type, structtype, FFT_ptr);	\
   MARK_LRECORD_AS_FREE (FFT_ptr);				\
 } while (0)
-
+#endif /* NEW_GC */
+
+#ifdef NEW_GC
+#define FREE_FIXED_TYPE_WHEN_NOT_IN_GC(lo, type, structtype, ptr)	\
+  free_lrecord (lo)
+#else /* not NEW_GC */
 /* Like FREE_FIXED_TYPE() but used when we are explicitly
    freeing a structure through free_cons(), free_marker(), etc.
    rather than through the normal process of sweeping.
@@ -1165,15 +1170,15 @@
    set, which is used for Purify and the like. */
 
 #ifndef ALLOC_NO_POOLS
-#define FREE_FIXED_TYPE_WHEN_NOT_IN_GC(type, structtype, ptr)	\
-do { FREE_FIXED_TYPE (type, structtype, ptr);			\
-     DECREMENT_CONS_COUNTER (sizeof (structtype));		\
-     gc_count_num_##type##_freelist++;				\
+#define FREE_FIXED_TYPE_WHEN_NOT_IN_GC(lo, type, structtype, ptr)	\
+do { FREE_FIXED_TYPE (type, structtype, ptr);				\
+     DECREMENT_CONS_COUNTER (sizeof (structtype));			\
+     gc_count_num_##type##_freelist++;					\
    } while (0)
 #else
-#define FREE_FIXED_TYPE_WHEN_NOT_IN_GC(type, structtype, ptr)
+#define FREE_FIXED_TYPE_WHEN_NOT_IN_GC(lo, type, structtype, ptr)
 #endif
-#endif /* NEW_GC */
+#endif /* (not) NEW_GC */
 
 #ifdef NEW_GC
 #define ALLOCATE_FIXED_TYPE_AND_SET_IMPL(type, lisp_type, var, lrec_ptr) \
@@ -3481,33 +3486,45 @@
   int instances_freed;
   int bytes_freed;
   int instances_on_free_list;
-} lcrecord_stats [countof (lrecord_implementations_table)];
-
-static void
-tick_lcrecord_stats (const struct lrecord_header *h, int free_p)
+  int bytes_on_free_list;
+} lrecord_stats [countof (lrecord_implementations_table)];
+
+void
+tick_lrecord_stats (const struct lrecord_header *h,
+		    enum lrecord_alloc_status status)
 {
   int type_index = h->type;
-
+  Bytecount sz = detagged_lisp_object_size (h);
+
+  switch (status)
+    {
+    case ALLOC_IN_USE:
+      lrecord_stats[type_index].instances_in_use++;
+      lrecord_stats[type_index].bytes_in_use += sz;
+      break;
+    case ALLOC_FREE:
+      lrecord_stats[type_index].instances_freed++;
+      lrecord_stats[type_index].bytes_freed += sz;
+      break;
+    case ALLOC_ON_FREE_LIST:
+      lrecord_stats[type_index].instances_on_free_list++;
+      lrecord_stats[type_index].bytes_on_free_list += sz;
+      break;
+    default:
+      ABORT ();
+    }
+}
+
+inline static void
+tick_lcrecord_stats (const struct lrecord_header *h, int free_p)
+{
   if (((struct old_lcrecord_header *) h)->free)
     {
       gc_checking_assert (!free_p);
-      lcrecord_stats[type_index].instances_on_free_list++;
+      tick_lrecord_stats (h, ALLOC_ON_FREE_LIST);
     }
   else
-    {
-      Bytecount sz = detagged_lisp_object_size (h);
-
-      if (free_p)
-	{
-	  lcrecord_stats[type_index].instances_freed++;
-	  lcrecord_stats[type_index].bytes_freed += sz;
-	}
-      else
-	{
-	  lcrecord_stats[type_index].instances_in_use++;
-	  lcrecord_stats[type_index].bytes_in_use += sz;
-	}
-    }
+    tick_lrecord_stats (h, free_p ? ALLOC_FREE : ALLOC_IN_USE);
 }
 #endif /* not NEW_GC */
 
@@ -3521,8 +3538,6 @@
   int num_used = 0;
   /* int total_size = 0; */
 
-  xzero (lcrecord_stats); /* Reset all statistics to 0. */
-
   /* First go through and call all the finalize methods.
      Then go through and free the objects.  There used to
      be only one loop here, with the call to the finalizer
@@ -3577,6 +3592,22 @@
 /* And the Lord said: Thou shalt use the `c-backslash-region' command
    to make macros prettier. */
 
+#define COUNT_FROB_BLOCK_USAGE(type)					\
+  EMACS_INT s = 0;							\
+  struct type##_block *x = current_##type##_block;			\
+  while (x) { s += sizeof (*x) + MALLOC_OVERHEAD; x = x->prev; }	\
+  DO_NOTHING
+
+#define COPY_INTO_LRECORD_STATS(type)				\
+do {								\
+  COUNT_FROB_BLOCK_USAGE (type);				\
+  lrecord_stats[lrecord_type_##type].bytes_in_use += s;		\
+  lrecord_stats[lrecord_type_##type].instances_on_free_list +=	\
+    gc_count_num_##type##_freelist;				\
+  lrecord_stats[lrecord_type_##type].instances_in_use +=	\
+    gc_count_num_##type##_in_use;				\
+} while (0)
+
 #ifdef ERROR_CHECK_GC
 
 #define SWEEP_FIXED_TYPE_BLOCK_1(typename, obj_type, lheader)		\
@@ -3621,86 +3652,88 @@
 									\
   gc_count_num_##typename##_in_use = num_used;				\
   gc_count_num_##typename##_freelist = num_free;			\
+  COPY_INTO_LRECORD_STATS (typename);					\
 } while (0)
 
 #else /* !ERROR_CHECK_GC */
 
-#define SWEEP_FIXED_TYPE_BLOCK_1(typename, obj_type, lheader)		     \
-do {									     \
-  struct typename##_block *SFTB_current;				     \
-  struct typename##_block **SFTB_prev;					     \
-  int SFTB_limit;							     \
-  int num_free = 0, num_used = 0;					     \
-									     \
-  typename##_free_list = 0;						     \
-									     \
-  for (SFTB_prev = &current_##typename##_block,				     \
-       SFTB_current = current_##typename##_block,			     \
-       SFTB_limit = current_##typename##_block_index;			     \
-       SFTB_current;							     \
-       )								     \
-    {									     \
-      int SFTB_iii;							     \
-      int SFTB_empty = 1;						     \
-      Lisp_Free *SFTB_old_free_list = typename##_free_list;		     \
-									     \
-      for (SFTB_iii = 0; SFTB_iii < SFTB_limit; SFTB_iii++)		     \
-	{								     \
-	  obj_type *SFTB_victim = &(SFTB_current->block[SFTB_iii]);	     \
-									     \
-	  if (LRECORD_FREE_P (SFTB_victim))				     \
-	    {								     \
-	      num_free++;						     \
+#define SWEEP_FIXED_TYPE_BLOCK_1(typename, obj_type, lheader)		\
+do {									\
+  struct typename##_block *SFTB_current;				\
+  struct typename##_block **SFTB_prev;					\
+  int SFTB_limit;							\
+  int num_free = 0, num_used = 0;					\
+									\
+  typename##_free_list = 0;						\
+									\
+  for (SFTB_prev = &current_##typename##_block,				\
+       SFTB_current = current_##typename##_block,			\
+       SFTB_limit = current_##typename##_block_index;			\
+       SFTB_current;							\
+       )								\
+    {									\
+      int SFTB_iii;							\
+      int SFTB_empty = 1;						\
+      Lisp_Free *SFTB_old_free_list = typename##_free_list;		\
+									\
+      for (SFTB_iii = 0; SFTB_iii < SFTB_limit; SFTB_iii++)		\
+	{								\
+	  obj_type *SFTB_victim = &(SFTB_current->block[SFTB_iii]);	\
+									\
+	  if (LRECORD_FREE_P (SFTB_victim))				\
+	    {								\
+	      num_free++;						\
 	      PUT_FIXED_TYPE_ON_FREE_LIST (typename, obj_type, SFTB_victim); \
-	    }								     \
-	  else if (C_READONLY_RECORD_HEADER_P (&SFTB_victim->lheader))	     \
-	    {								     \
-	      SFTB_empty = 0;						     \
-	      num_used++;						     \
-	    }								     \
-	  else if (! MARKED_RECORD_HEADER_P (&SFTB_victim->lheader))	     \
-	    {								     \
-	      num_free++;						     \
-	      FREE_FIXED_TYPE (typename, obj_type, SFTB_victim);	     \
-	    }								     \
-	  else								     \
-	    {								     \
-	      SFTB_empty = 0;						     \
-	      num_used++;						     \
-	      UNMARK_##typename (SFTB_victim);				     \
-	    }								     \
-	}								     \
-      if (!SFTB_empty)							     \
-	{								     \
-	  SFTB_prev = &(SFTB_current->prev);				     \
-	  SFTB_current = SFTB_current->prev;				     \
-	}								     \
-      else if (SFTB_current == current_##typename##_block		     \
-	       && !SFTB_current->prev)					     \
-	{								     \
-	  /* No real point in freeing sole allocation block */		     \
-	  break;							     \
-	}								     \
-      else								     \
-	{								     \
-	  struct typename##_block *SFTB_victim_block = SFTB_current;	     \
-	  if (SFTB_victim_block == current_##typename##_block)		     \
-	    current_##typename##_block_index				     \
-	      = countof (current_##typename##_block->block);		     \
-	  SFTB_current = SFTB_current->prev;				     \
-	  {								     \
-	    *SFTB_prev = SFTB_current;					     \
-	    xfree (SFTB_victim_block);	     \
-	    /* Restore free list to what it was before victim was swept */   \
-	    typename##_free_list = SFTB_old_free_list;			     \
-	    num_free -= SFTB_limit;					     \
-	  }								     \
-	}								     \
-      SFTB_limit = countof (current_##typename##_block->block);		     \
-    }									     \
-									     \
-  gc_count_num_##typename##_in_use = num_used;				     \
-  gc_count_num_##typename##_freelist = num_free;			     \
+	    }								\
+	  else if (C_READONLY_RECORD_HEADER_P (&SFTB_victim->lheader))	\
+	    {								\
+	      SFTB_empty = 0;						\
+	      num_used++;						\
+	    }								\
+	  else if (! MARKED_RECORD_HEADER_P (&SFTB_victim->lheader))	\
+	    {								\
+	      num_free++;						\
+	      FREE_FIXED_TYPE (typename, obj_type, SFTB_victim);	\
+	    }								\
+	  else								\
+	    {								\
+	      SFTB_empty = 0;						\
+	      num_used++;						\
+	      UNMARK_##typename (SFTB_victim);				\
+	    }								\
+	}								\
+      if (!SFTB_empty)							\
+	{								\
+	  SFTB_prev = &(SFTB_current->prev);				\
+	  SFTB_current = SFTB_current->prev;				\
+	}								\
+      else if (SFTB_current == current_##typename##_block		\
+	       && !SFTB_current->prev)					\
+	{								\
+	  /* No real point in freeing sole allocation block */		\
+	  break;							\
+	}								\
+      else								\
+	{								\
+	  struct typename##_block *SFTB_victim_block = SFTB_current;	\
+	  if (SFTB_victim_block == current_##typename##_block)		\
+	    current_##typename##_block_index				\
+	      = countof (current_##typename##_block->block);		\
+	  SFTB_current = SFTB_current->prev;				\
+	  {								\
+	    *SFTB_prev = SFTB_current;					\
+	    xfree (SFTB_victim_block);					\
+	    /* Restore free list to what it was before victim was swept */ \
+	    typename##_free_list = SFTB_old_free_list;			\
+	    num_free -= SFTB_limit;					\
+	  }								\
+	}								\
+      SFTB_limit = countof (current_##typename##_block->block);		\
+    }									\
+									\
+  gc_count_num_##typename##_in_use = num_used;				\
+  gc_count_num_##typename##_freelist = num_free;			\
+  COPY_INTO_LRECORD_STATS (typename);					\
 } while (0)
 
 #endif /* !ERROR_CHECK_GC */
@@ -3748,11 +3781,7 @@
     ASSERT_VALID_POINTER (XPNTR (cons_car (ptr)));
 #endif /* ERROR_CHECK_GC */
 
-#ifdef NEW_GC
-  free_lrecord (cons);
-#else /* not NEW_GC */
-  FREE_FIXED_TYPE_WHEN_NOT_IN_GC (cons, Lisp_Cons, ptr);
-#endif /* not NEW_GC */
+  FREE_FIXED_TYPE_WHEN_NOT_IN_GC (cons, cons, Lisp_Cons, ptr);
 }
 
 /* explicitly free a list.  You **must make sure** that you have
@@ -3887,11 +3916,8 @@
 void
 free_key_data (Lisp_Object ptr)
 {
-#ifdef NEW_GC
-  free_lrecord (ptr);
-#else /* not NEW_GC */
-  FREE_FIXED_TYPE_WHEN_NOT_IN_GC (key_data, Lisp_Key_Data, XKEY_DATA (ptr));
-#endif /* not NEW_GC */
+  FREE_FIXED_TYPE_WHEN_NOT_IN_GC (ptr, key_data, Lisp_Key_Data,
+				  XKEY_DATA (ptr));
 }
 
 #ifndef NEW_GC
@@ -3908,11 +3934,8 @@
 void
 free_button_data (Lisp_Object ptr)
 {
-#ifdef NEW_GC
-  free_lrecord (ptr);
-#else /* not NEW_GC */
-  FREE_FIXED_TYPE_WHEN_NOT_IN_GC (button_data, Lisp_Button_Data, XBUTTON_DATA (ptr));
-#endif /* not NEW_GC */
+  FREE_FIXED_TYPE_WHEN_NOT_IN_GC (ptr, button_data, Lisp_Button_Data,
+				  XBUTTON_DATA (ptr));
 }
 
 #ifndef NEW_GC
@@ -3929,11 +3952,8 @@
 void
 free_motion_data (Lisp_Object ptr)
 {
-#ifdef NEW_GC
-  free_lrecord (ptr);
-#else /* not NEW_GC */
-  FREE_FIXED_TYPE_WHEN_NOT_IN_GC (motion_data, Lisp_Motion_Data, XMOTION_DATA (ptr));
-#endif /* not NEW_GC */
+  FREE_FIXED_TYPE_WHEN_NOT_IN_GC (ptr, motion_data, Lisp_Motion_Data,
+				  XMOTION_DATA (ptr));
 }
 
 #ifndef NEW_GC
@@ -3950,11 +3970,8 @@
 void
 free_process_data (Lisp_Object ptr)
 {
-#ifdef NEW_GC
-  free_lrecord (ptr);
-#else /* not NEW_GC */
-  FREE_FIXED_TYPE_WHEN_NOT_IN_GC (process_data, Lisp_Process_Data, XPROCESS_DATA (ptr));
-#endif /* not NEW_GC */
+  FREE_FIXED_TYPE_WHEN_NOT_IN_GC (ptr, process_data, Lisp_Process_Data,
+				  XPROCESS_DATA (ptr));
 }
 
 #ifndef NEW_GC
@@ -3971,11 +3988,8 @@
 void
 free_timeout_data (Lisp_Object ptr)
 {
-#ifdef NEW_GC
-  free_lrecord (ptr);
-#else /* not NEW_GC */
-  FREE_FIXED_TYPE_WHEN_NOT_IN_GC (timeout_data, Lisp_Timeout_Data, XTIMEOUT_DATA (ptr));
-#endif /* not NEW_GC */
+  FREE_FIXED_TYPE_WHEN_NOT_IN_GC (ptr, timeout_data, Lisp_Timeout_Data,
+				  XTIMEOUT_DATA (ptr));
 }
 
 #ifndef NEW_GC
@@ -3992,11 +4006,8 @@
 void
 free_magic_data (Lisp_Object ptr)
 {
-#ifdef NEW_GC
-  free_lrecord (ptr);
-#else /* not NEW_GC */
-  FREE_FIXED_TYPE_WHEN_NOT_IN_GC (magic_data, Lisp_Magic_Data, XMAGIC_DATA (ptr));
-#endif /* not NEW_GC */
+  FREE_FIXED_TYPE_WHEN_NOT_IN_GC (ptr, magic_data, Lisp_Magic_Data,
+				  XMAGIC_DATA (ptr));
 }
 
 #ifndef NEW_GC
@@ -4013,11 +4024,8 @@
 void
 free_magic_eval_data (Lisp_Object ptr)
 {
-#ifdef NEW_GC
-  free_lrecord (ptr);
-#else /* not NEW_GC */
-  FREE_FIXED_TYPE_WHEN_NOT_IN_GC (magic_eval_data, Lisp_Magic_Eval_Data, XMAGIC_EVAL_DATA (ptr));
-#endif /* not NEW_GC */
+  FREE_FIXED_TYPE_WHEN_NOT_IN_GC (ptr, magic_eval_data, Lisp_Magic_Eval_Data,
+				  XMAGIC_EVAL_DATA (ptr));
 }
 
 #ifndef NEW_GC
@@ -4034,11 +4042,8 @@
 void
 free_eval_data (Lisp_Object ptr)
 {
-#ifdef NEW_GC
-  free_lrecord (ptr);
-#else /* not NEW_GC */
-  FREE_FIXED_TYPE_WHEN_NOT_IN_GC (eval_data, Lisp_Eval_Data, XEVAL_DATA (ptr));
-#endif /* not NEW_GC */
+  FREE_FIXED_TYPE_WHEN_NOT_IN_GC (ptr, eval_data, Lisp_Eval_Data,
+				  XEVAL_DATA (ptr));
 }
 
 #ifndef NEW_GC
@@ -4055,11 +4060,8 @@
 void
 free_misc_user_data (Lisp_Object ptr)
 {
-#ifdef NEW_GC
-  free_lrecord (ptr);
-#else /* not NEW_GC */
-  FREE_FIXED_TYPE_WHEN_NOT_IN_GC (misc_user_data, Lisp_Misc_User_Data, XMISC_USER_DATA (ptr));
-#endif /* not NEW_GC */
+  FREE_FIXED_TYPE_WHEN_NOT_IN_GC (ptr, misc_user_data, Lisp_Misc_User_Data,
+				  XMISC_USER_DATA (ptr));
 }
 
 #endif /* EVENT_DATA_AS_OBJECTS */
@@ -4083,11 +4085,7 @@
 void
 free_marker (Lisp_Object ptr)
 {
-#ifdef NEW_GC
-  free_lrecord (ptr);
-#else /* not NEW_GC */
-  FREE_FIXED_TYPE_WHEN_NOT_IN_GC (marker, Lisp_Marker, XMARKER (ptr));
-#endif /* not NEW_GC */
+  FREE_FIXED_TYPE_WHEN_NOT_IN_GC (ptr, marker, Lisp_Marker, XMARKER (ptr));
 }
 
 
@@ -4304,6 +4302,10 @@
 void
 gc_sweep_1 (void)
 {
+  /* Reset all statistics to 0.  They will be incremented when
+     sweeping lcrecords, frob-block lrecords and dumped objects. */
+  xzero (lrecord_stats);
+
   /* Free all unmarked records.  Do this at the very beginning,
      before anything else, so that the finalize methods can safely
      examine items in the objects.  sweep_lcrecords_1() makes
@@ -4466,6 +4468,60 @@
   return cons3 (intern (name), make_int (value), tail);
 }
 
+/* Pluralize a lowercase English word stored in BUF, assuming BUF has
+   enough space to hold the extra letters (at most 2). */
+static void
+pluralize_word (Ascbyte *buf)
+{
+  Bytecount len = strlen (buf);
+  int upper = 0;
+  Ascbyte d, e;
+
+  if (len == 0 || len == 1)
+    goto pluralize_apostrophe_s;
+  e = buf[len - 1];
+  d = buf[len - 2];
+  upper = isupper (e);
+  e = tolower (e);
+  d = tolower (d);
+  if (e == 'y')
+    {
+      switch (d)
+	{
+	case 'a':
+	case 'e':
+	case 'i':
+	case 'o':
+	case 'u':
+	  goto pluralize_s;
+	default:
+	  buf[len - 1] = (upper ? 'I' : 'i');
+	  goto pluralize_es;
+	}
+    }
+  else if (e == 's' || e == 'x' || (e == 'h' && (d == 's' || d == 'c')))
+    {
+      pluralize_es:
+      buf[len++] = (upper ? 'E' : 'e');
+    }
+  pluralize_s:
+  buf[len++] = (upper ? 'S' : 's');
+  buf[len] = '\0';
+  return;
+
+  pluralize_apostrophe_s:
+  buf[len++] = '\'';
+  goto pluralize_s;
+}
+
+static void
+pluralize_and_append (Ascbyte *buf, const Ascbyte *name, const Ascbyte *suffix)
+{
+  strcpy (buf, name);
+  pluralize_word (buf);
+  strcat (buf, suffix);
+}
+
 static Lisp_Object
 object_memory_usage_stats (int set_total_gc_usage)
 {
@@ -4481,7 +4537,6 @@
         {
           Ascbyte buf[255];
           const Ascbyte *name = lrecord_implementations_table[i]->name;
-	  int len = strlen (name);
 
 	  if (lrecord_stats[i].bytes_in_use_including_overhead != 
 	      lrecord_stats[i].bytes_in_use)
@@ -4498,87 +4553,70 @@
 			      lrecord_stats[i].bytes_in_use,
 			      pl);
 	  tgu_val += lrecord_stats[i].bytes_in_use_including_overhead;
-	  
-	  if (name[len-1] == 's')
-	    sprintf (buf, "%ses-used", name);
-	  else
-	    sprintf (buf, "%ss-used", name);
+
+	  pluralize_and_append (buf, name, "-used");
 	  pl = gc_plist_hack (buf, lrecord_stats[i].instances_in_use, pl);
         }
     }
 
 #else /* not NEW_GC */
 
-#define HACK_O_MATIC(type, name, pl) do {				\
-  EMACS_INT s = 0;							\
-  struct type##_block *x = current_##type##_block;			\
-  while (x) { s += sizeof (*x) + MALLOC_OVERHEAD; x = x->prev; }	\
-  tgu_val += s;							\
-  (pl) = gc_plist_hack ((name), s, (pl));				\
+#define HACK_O_MATIC(type, name, pl)		\
+do {						\
+  COUNT_FROB_BLOCK_USAGE (type);		\
+  tgu_val += s;					\
+  (pl) = gc_plist_hack ((name), s, (pl));	\
+} while (0)
+
+#define FROB(type)				\
+do {						\
+  COUNT_FROB_BLOCK_USAGE (type);		\
+  tgu_val += s;					\
 } while (0)
 
+  FROB (extent);
+  FROB (event);
+  FROB (marker);
+  FROB (float);
+#ifdef HAVE_BIGNUM
+  FROB (bignum);
+#endif /* HAVE_BIGNUM */
+#ifdef HAVE_RATIO
+  FROB (ratio);
+#endif /* HAVE_RATIO */
+#ifdef HAVE_BIGFLOAT
+  FROB (bigfloat);
+#endif /* HAVE_BIGFLOAT */
+  FROB (compiled_function);
+  FROB (symbol);
+  FROB (cons);
+
+#undef FROB
+
   for (i = 0; i < lrecord_type_count; i++)
     {
-      if (lcrecord_stats[i].bytes_in_use != 0
-          || lcrecord_stats[i].bytes_freed != 0
-	  || lcrecord_stats[i].instances_on_free_list != 0)
+      if (lrecord_stats[i].bytes_in_use != 0
+          || lrecord_stats[i].bytes_freed != 0
+	  || lrecord_stats[i].instances_on_free_list != 0)
         {
           Ascbyte buf[255];
           const Ascbyte *name = lrecord_implementations_table[i]->name;
-	  int len = strlen (name);
 
           sprintf (buf, "%s-storage", name);
-          pl = gc_plist_hack (buf, lcrecord_stats[i].bytes_in_use, pl);
-	  tgu_val += lcrecord_stats[i].bytes_in_use;
-	  /* Okay, simple pluralization check for `symbol-value-varalias' */
-	  if (name[len-1] == 's')
-	    sprintf (buf, "%ses-freed", name);
-	  else
-	    sprintf (buf, "%ss-freed", name);
-          if (lcrecord_stats[i].instances_freed != 0)
-            pl = gc_plist_hack (buf, lcrecord_stats[i].instances_freed, pl);
-	  if (name[len-1] == 's')
-	    sprintf (buf, "%ses-on-free-list", name);
-	  else
-	    sprintf (buf, "%ss-on-free-list", name);
-          if (lcrecord_stats[i].instances_on_free_list != 0)
-            pl = gc_plist_hack (buf, lcrecord_stats[i].instances_on_free_list,
+          pl = gc_plist_hack (buf, lrecord_stats[i].bytes_in_use, pl);
+	  tgu_val += lrecord_stats[i].bytes_in_use;
+	  pluralize_and_append (buf, name, "-freed");
+          if (lrecord_stats[i].instances_freed != 0)
+            pl = gc_plist_hack (buf, lrecord_stats[i].instances_freed, pl);
+	  pluralize_and_append (buf, name, "-on-free-list");
+          if (lrecord_stats[i].instances_on_free_list != 0)
+            pl = gc_plist_hack (buf, lrecord_stats[i].instances_on_free_list,
 				pl);
-	  if (name[len-1] == 's')
-	    sprintf (buf, "%ses-used", name);
-	  else
-	    sprintf (buf, "%ss-used", name);
-          pl = gc_plist_hack (buf, lcrecord_stats[i].instances_in_use, pl);
+	  pluralize_and_append (buf, name, "-used");
+          pl = gc_plist_hack (buf, lrecord_stats[i].instances_in_use, pl);
         }
     }
 
-  HACK_O_MATIC (extent, "extent-storage", pl);
-  pl = gc_plist_hack ("extents-free", gc_count_num_extent_freelist, pl);
-  pl = gc_plist_hack ("extents-used", gc_count_num_extent_in_use, pl);
-  HACK_O_MATIC (event, "event-storage", pl);
-  pl = gc_plist_hack ("events-free", gc_count_num_event_freelist, pl);
-  pl = gc_plist_hack ("events-used", gc_count_num_event_in_use, pl);
-  HACK_O_MATIC (marker, "marker-storage", pl);
-  pl = gc_plist_hack ("markers-free", gc_count_num_marker_freelist, pl);
-  pl = gc_plist_hack ("markers-used", gc_count_num_marker_in_use, pl);
-  HACK_O_MATIC (float, "float-storage", pl);
-  pl = gc_plist_hack ("floats-free", gc_count_num_float_freelist, pl);
-  pl = gc_plist_hack ("floats-used", gc_count_num_float_in_use, pl);
-#ifdef HAVE_BIGNUM
-  HACK_O_MATIC (bignum, "bignum-storage", pl);
-  pl = gc_plist_hack ("bignums-free", gc_count_num_bignum_freelist, pl);
-  pl = gc_plist_hack ("bignums-used", gc_count_num_bignum_in_use, pl);
-#endif /* HAVE_BIGNUM */
-#ifdef HAVE_RATIO
-  HACK_O_MATIC (ratio, "ratio-storage", pl);
-  pl = gc_plist_hack ("ratios-free", gc_count_num_ratio_freelist, pl);
-  pl = gc_plist_hack ("ratios-used", gc_count_num_ratio_in_use, pl);
-#endif /* HAVE_RATIO */
-#ifdef HAVE_BIGFLOAT
-  HACK_O_MATIC (bigfloat, "bigfloat-storage", pl);
-  pl = gc_plist_hack ("bigfloats-free", gc_count_num_bigfloat_freelist, pl);
-  pl = gc_plist_hack ("bigfloats-used", gc_count_num_bigfloat_in_use, pl);
-#endif /* HAVE_BIGFLOAT */
   HACK_O_MATIC (string, "string-header-storage", pl);
   pl = gc_plist_hack ("long-strings-total-length",
                       gc_count_string_total_size
@@ -4593,20 +4631,6 @@
   pl = gc_plist_hack ("short-strings-used",
                       gc_count_num_short_string_in_use, pl);
 
-  HACK_O_MATIC (compiled_function, "compiled-function-storage", pl);
-  pl = gc_plist_hack ("compiled-functions-free",
-		      gc_count_num_compiled_function_freelist, pl);
-  pl = gc_plist_hack ("compiled-functions-used",
-		      gc_count_num_compiled_function_in_use, pl);
-
-  HACK_O_MATIC (symbol, "symbol-storage", pl);
-  pl = gc_plist_hack ("symbols-free", gc_count_num_symbol_freelist, pl);
-  pl = gc_plist_hack ("symbols-used", gc_count_num_symbol_in_use, pl);
-
-  HACK_O_MATIC (cons, "cons-storage", pl);
-  pl = gc_plist_hack ("conses-free", gc_count_num_cons_freelist, pl);
-  pl = gc_plist_hack ("conses-used", gc_count_num_cons_in_use, pl);
-
 #undef HACK_O_MATIC
 
 #endif /* NEW_GC */
@@ -4683,8 +4707,9 @@
 	   Fcons (make_int (gc_count_num_marker_in_use),
 		  make_int (gc_count_num_marker_freelist)),
 	   make_int (gc_count_string_total_size),
-	   make_int (lcrecord_stats[lrecord_type_vector].bytes_in_use +
-		     lcrecord_stats[lrecord_type_vector].bytes_freed),
+	   make_int (lrecord_stats[lrecord_type_vector].bytes_in_use +
+		     lrecord_stats[lrecord_type_vector].bytes_freed +
+		     lrecord_stats[lrecord_type_vector].bytes_on_free_list),
 	   object_memory_usage_stats (1));
 #endif /* not NEW_GC */
 #else /* not ALLOC_TYPE_STATS */
--- a/src/dumper.c	Mon Feb 22 06:49:30 2010 -0600
+++ b/src/dumper.c	Tue Feb 23 07:28:35 2010 -0600
@@ -1,7 +1,7 @@
 /* Portable data dumper for XEmacs.
    Copyright (C) 1999-2000,2004 Olivier Galibert
    Copyright (C) 2001 Martin Buchholz
-   Copyright (C) 2001, 2002, 2003, 2004, 2005 Ben Wing.
+   Copyright (C) 2001, 2002, 2003, 2004, 2005, 2010 Ben Wing.
 
 This file is part of XEmacs.
 
@@ -253,8 +253,20 @@
 	    for (i=0; i<rt->count; i++)
 	      {
 		struct lrecord_header *lh = * (struct lrecord_header **) p;
+#ifdef ALLOC_TYPE_STATS
+		if (C_READONLY_RECORD_HEADER_P (lh))
+		  tick_lrecord_stats (lh, ALLOC_IN_USE);
+
+		else
+		  {
+		    tick_lrecord_stats (lh, MARKED_RECORD_HEADER_P (lh) ?
+					ALLOC_IN_USE : ALLOC_ON_FREE_LIST);
+		    UNMARK_RECORD_HEADER (lh);
+		  }
+#else /* not ALLOC_TYPE_STATS */
 		if (! C_READONLY_RECORD_HEADER_P (lh))
 		  UNMARK_RECORD_HEADER (lh);
+#endif /* (not) ALLOC_TYPE_STATS */
 		p += sizeof (EMACS_INT);
 	      }
 	  } else
--- a/src/events.c	Mon Feb 22 06:49:30 2010 -0600
+++ b/src/events.c	Tue Feb 23 07:28:35 2010 -0600
@@ -2134,11 +2134,12 @@
      pointer points to random memory, often filled with 0, sometimes not.
    */
   /* #### Chuck, do we still need this crap? */
+#ifdef HAVE_TOOLBARS
   assert (NILP (ret_obj1) || GLYPHP (ret_obj1)
-#ifdef HAVE_TOOLBARS
-	  || TOOLBAR_BUTTONP (ret_obj1)
+	  || TOOLBAR_BUTTONP (ret_obj1));
+#else
+  assert (NILP (ret_obj1) || GLYPHP (ret_obj1));
 #endif
-	  );
   assert (NILP (ret_obj2) || EXTENTP (ret_obj2) || CONSP (ret_obj2));
 
   if (char_x)
--- a/src/lrecord.h	Mon Feb 22 06:49:30 2010 -0600
+++ b/src/lrecord.h	Tue Feb 23 07:28:35 2010 -0600
@@ -523,6 +523,16 @@
 
 #else /* not NEW_GC */
 
+enum lrecord_alloc_status
+{
+  ALLOC_IN_USE,
+  ALLOC_FREE,
+  ALLOC_ON_FREE_LIST
+};
+
+void tick_lrecord_stats (const struct lrecord_header *h,
+			 enum lrecord_alloc_status status);
+
 #define LRECORD_FREE_P(ptr)					\
 (((struct lrecord_header *) ptr)->type == lrecord_type_free)
 
--- a/tests/ChangeLog	Mon Feb 22 06:49:30 2010 -0600
+++ b/tests/ChangeLog	Tue Feb 23 07:28:35 2010 -0600
@@ -3,6 +3,17 @@
 	* reproduce-crashes.el (8):
 	objects*.[ch] -> fontcolor*.[ch].
 
+2010-02-22  Ben Wing  <ben@xemacs.org>
+
+	* automated/syntax-tests.el:
+	Use Known-Bug-Expect-Error, not Known-Bug-Expect-Failure, when
+	error expected; else test suite will abort this file.
+	
+2010-02-22  Ben Wing  <ben@xemacs.org>
+
+	* automated/test-harness.el (test-harness-from-buffer):
+	Remove unused binding.
+
 2010-02-15  Ben Wing  <ben@xemacs.org>
 
 	* automated/search-tests.el (let):
--- a/tests/automated/syntax-tests.el	Mon Feb 22 06:49:30 2010 -0600
+++ b/tests/automated/syntax-tests.el	Tue Feb 23 07:28:35 2010 -0600
@@ -192,8 +192,9 @@
     (Assert (backward-up-list-moves-point-from-to 20 3))
     (Known-Bug-Expect-Failure
      (Assert (backward-up-list-moves-point-from-to 22 3)))
-    (Known-Bug-Expect-Failure
-     (Assert (backward-up-list-moves-point-from-to 23 3)))
+    (Known-Bug-Expect-Error scan-error
+     (Assert (backward-up-list-moves-point-from-to 23 3))
+     )
     (Assert (backward-up-list-moves-point-from-to 24 3))
     ;; This is maybe a little tricky, since we don't expect the position
     ;; check to happen -- so use an illegal expected position
--- a/tests/automated/test-harness.el	Mon Feb 22 06:49:30 2010 -0600
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,840 +0,0 @@
-;; test-harness.el --- Run Emacs Lisp test suites.
-
-;;; Copyright (C) 1998, 2002, 2003 Free Software Foundation, Inc.
-;;; Copyright (C) 2002, 2010 Ben Wing.
-
-;; Author: Martin Buchholz
-;; Maintainer: Stephen J. Turnbull <stephen@xemacs.org>
-;; Keywords: testing
-
-;; 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: Not in FSF.
-
-;;; Commentary:
-
-;;; A test suite harness for testing XEmacs.
-;;; The actual tests are in other files in this directory.
-;;; Basically you just create files of emacs-lisp, and use the
-;;; Assert, Check-Error, Check-Message, and Check-Error-Message functions
-;;; to create tests.  See `test-harness-from-buffer' below.
-;;; Don't suppress tests just because they're due to known bugs not yet
-;;; fixed -- use the Known-Bug-Expect-Failure and
-;;; Implementation-Incomplete-Expect-Failure wrapper macros to mark them.
-;;; A lot of the tests we run push limits; suppress Ebola message with the
-;;; Ignore-Ebola wrapper macro.
-;;; Some noisy code will call `message'.  Output from `message' can be
-;;; suppressed with the Silence-Message macro.  Functions that are known to
-;;; issue messages include `write-region', `find-tag', `tag-loop-continue',
-;;; `insert', and `mark-whole-buffer'.  N.B. The Silence-Message macro
-;;; currently does not suppress the newlines printed by `message'.
-;;; Definitely do not use Silence-Message with Check-Message.
-;;; In general it should probably only be used on code that prepares for a
-;;; test, not on tests.
-;;; 
-;;; You run the tests using M-x test-emacs-test-file,
-;;; or $(EMACS) -batch -l .../test-harness.el -f batch-test-emacs file ...
-;;; which is run for you by the `make check' target in the top-level Makefile.
-
-(require 'bytecomp)
-
-(defvar unexpected-test-suite-failures 0
-  "Cumulative number of unexpected failures since test-harness was loaded.
-
-\"Unexpected failures\" are those caught by a generic handler established
-outside of the test context.  As such they involve an abort of the test
-suite for the file being tested.
-
-They often occur during preparation of a test or recording of the results.
-For example, an executable used to generate test data might not be present
-on the system, or a system error might occur while reading a data file.")
-
-(defvar unexpected-test-suite-failure-files nil
-  "List of test files causing unexpected failures.")
-
-;; Declared for dynamic scope; _do not_ initialize here.
-(defvar unexpected-test-file-failures)
-
-(defvar test-harness-bug-expected nil
-  "Non-nil means a bug is expected; backtracing/debugging should not happen.")
-
-(defvar test-harness-test-compiled nil
-  "Non-nil means the test code was compiled before execution.
-
-You probably should not make tests depend on compilation.
-However, it can be useful to conditionally change messages based on whether
-the code was compiled or not.  For example, the case that motivated the
-implementation of this variable:
-
-\(when test-harness-test-compiled
-  ;; this ha-a-ack depends on the failing compiled test coming last
-  \(setq test-harness-failure-tag
-	\"KNOWN BUG - fix reverted; after 2003-10-31 notify stephen\n\"))")
-
-(defvar test-harness-verbose
-  (and (not noninteractive) (> (device-baud-rate) search-slow-speed))
-  "*Non-nil means print messages describing progress of emacs-tester.")
-
-(defvar test-harness-unexpected-error-enter-debugger debug-on-error
-  "*Non-nil means enter debugger when an unexpected error occurs.
-Only applies interactively.  Normally true if `debug-on-error' has been set.
-See also `test-harness-assertion-failure-enter-debugger' and
-`test-harness-unexpected-error-show-backtrace'.")
-
-(defvar test-harness-assertion-failure-enter-debugger debug-on-error
-  "*Non-nil means enter debugger when an assertion failure occurs.
-Only applies interactively.  Normally true if `debug-on-error' has been set.
-See also `test-harness-unexpected-error-enter-debugger' and
-`test-harness-assertion-failure-show-backtrace'.")
-
-(defvar test-harness-unexpected-error-show-backtrace t
-  "*Non-nil means show backtrace upon unexpected error.
-Only applies when debugger is not entered.  Normally true by default.  See also
-`test-harness-unexpected-error-enter-debugger' and
-`test-harness-assertion-failure-show-backtrace'.")
-
-(defvar test-harness-assertion-failure-show-backtrace stack-trace-on-error
-  "*Non-nil means show backtrace upon assertion failure.
-Only applies when debugger is not entered.  Normally true if
-`stack-trace-on-error' has been set.  See also
-`test-harness-assertion-failure-enter-debugger' and
-`test-harness-unexpected-error-show-backtrace'.")
-
-(defvar test-harness-file-results-alist nil
-  "Each element is a list (FILE SUCCESSES TESTS).
-The order is the reverse of the order in which tests are run.
-
-FILE is a string naming the test file.
-SUCCESSES is a non-negative integer, the number of successes.
-TESTS is a non-negative integer, the number of tests run.")
-
-(defvar test-harness-risk-infloops nil
-  "*Non-nil to run tests that may loop infinitely in buggy implementations.")
-
-(defvar test-harness-current-file nil)
-
-(defvar emacs-lisp-file-regexp (purecopy "\\.el\\'")
-  "*Regexp which matches Emacs Lisp source files.")
-
-(defconst test-harness-file-summary-template
-  (format "%%-%ds %%%dd of %%%dd tests successful (%%3d%%%%)."
-	  (length "byte-compiler-tests.el:") ; use the longest file name
-	  5
-	  5)
-  "Format for summary lines printed after each file is run.")
-
-(defconst test-harness-null-summary-template
-  (format "%%-%ds             No tests run."
-	  (length "byte-compiler-tests.el:")) ; use the longest file name
-  "Format for \"No tests\" lines printed after a file is run.")
-
-(defconst test-harness-aborted-summary-template
-  (format "%%-%ds          %%%dd tests completed (aborted)."
-	  (length "byte-compiler-tests.el:") ; use the longest file name
-	  5)
-  "Format for summary lines printed after a test run on a file was aborted.")
-
-;;;###autoload
-(defun test-emacs-test-file (filename)
-  "Test a file of Lisp code named FILENAME.
-The output file's name is made by appending `c' to the end of FILENAME."
-  (interactive
-   (let ((file buffer-file-name)
-	 (file-name nil)
-	 (file-dir nil))
-     (and file
-	  (eq (cdr (assq 'major-mode (buffer-local-variables)))
-	      'emacs-lisp-mode)
-	  (setq file-name (file-name-nondirectory file)
-		file-dir (file-name-directory file)))
-     (list (read-file-name "Test file: " file-dir nil nil file-name))))
-  ;; Expand now so we get the current buffer's defaults
-  (setq filename (expand-file-name filename))
-
-  ;; If we're testing a file that's in a buffer and is modified, offer
-  ;; to save it first.
-  (or noninteractive
-      (let ((b (get-file-buffer (expand-file-name filename))))
-	(if (and b (buffer-modified-p b)
-		 (y-or-n-p (format "save buffer %s first? " (buffer-name b))))
-	    (save-excursion (set-buffer b) (save-buffer)))))
-
-  (if (or noninteractive test-harness-verbose)
-      (message "Testing %s..." filename))
-  (let ((test-harness-current-file filename)
-	input-buffer)
-    (save-excursion
-      (setq input-buffer (get-buffer-create " *Test Input*"))
-      (set-buffer input-buffer)
-      (erase-buffer)
-      (insert-file-contents filename)
-      ;; Run hooks including the uncompression hook.
-      ;; If they change the file name, then change it for the output also.
-      (let ((buffer-file-name filename)
-	    (default-major-mode 'emacs-lisp-mode)
-	    (enable-local-eval nil))
-        (normal-mode)
-        (setq filename buffer-file-name)))
-    (test-harness-from-buffer input-buffer filename)
-    (kill-buffer input-buffer)
-    ))
-
-(defsubst test-harness-assertion-failure-do-debug (error-info)
-  "Maybe enter debugger or display a backtrace on assertion failure.
-ERROR-INFO is a cons of the args (SIG . DATA) that were passed to `signal'.
-The debugger will be entered if noninteractive and
-`test-harness-unexpected-error-enter-debugger' is non-nil; else, a
-backtrace will be displayed if `test-harness-unexpected-error-show-backtrace'
-is non-nil."
-  (when (not test-harness-bug-expected)
-    (cond ((and (not noninteractive)
-		test-harness-assertion-failure-enter-debugger)
-	   (funcall debugger 'error error-info))
-	  (test-harness-assertion-failure-show-backtrace
-	   (backtrace nil t)))))
-
-(defsubst test-harness-unexpected-error-do-debug (error-info)
-  "Maybe enter debugger or display a backtrace on unexpected error.
-ERROR-INFO is a cons of the args (SIG . DATA) that were passed to `signal'.
-The debugger will be entered if noninteractive and
-`test-harness-unexpected-error-enter-debugger' is non-nil; else, a
-backtrace will be displayed if `test-harness-unexpected-error-show-backtrace'
-is non-nil."
-  (when (not test-harness-bug-expected)
-    (cond ((and (not noninteractive)
-		test-harness-unexpected-error-enter-debugger)
-	   (funcall debugger 'error error-info))
-	  (test-harness-unexpected-error-show-backtrace
-	   (backtrace nil t)))))
-
-(defsubst test-harness-unexpected-error-condition-handler (error-info context-msg)
-  "Condition handler for when unexpected errors occur.
-Useful in conjunction with `call-with-condition-handler'.  ERROR-INFO is the
-value passed to the condition handler.  CONTEXT-MSG is a string indicating
-the context in which the unexpected error occurred.  A message is outputted
-including CONTEXT-MSG in it, `unexpected-test-file-failures' is incremented,
-and `test-harness-unexpected-error-do-debug' is called, which may enter the
-debugger or output a backtrace, depending on the settings of
-`test-harness-unexpected-error-enter-debugger' and
-`test-harness-unexpected-error-show-backtrace'.
-
-The function returns normally, which causes error-handling processing to
-continue; if you want to catch the error, you also need to wrap everything
-in `condition-case'.  See also `test-harness-error-wrap', which does this
-wrapping."
-  (incf unexpected-test-file-failures)
-  (princ (format "Unexpected error %S while %s\n"
-		 error-info context-msg))
-  (message "Unexpected error %S while %s." error-info context-msg)
-  (test-harness-unexpected-error-do-debug error-info))
-
-(defmacro test-harness-error-wrap (context-msg abort-msg &rest body)
-  "Wrap BODY so that unexpected errors are caught.
-The debugger will be entered if noninteractive and
-`test-harness-unexpected-error-enter-debugger' is non-nil; else, a backtrace
-will be displayed if `test-harness-unexpected-error-show-backtrace' is
-non-nil.  CONTEXT-MSG is displayed as part of a message shown before entering
-the debugger or showing a backtrace, and ABORT-MSG, if non-nil, is displayed
-afterwards.  See "
-  `(condition-case nil
-    (call-with-condition-handler
-	#'(lambda (error-info)
-	    (test-harness-unexpected-error-condition-handler
-	     error-info ,context-msg))
-	#'(lambda ()
-	    ,@body))
-    (error ,(if abort-msg `(message ,abort-msg) nil))))
-
-(defun test-harness-read-from-buffer (buffer)
-  "Read forms from BUFFER, and turn it into a lambda test form."
-  (let ((body nil))
-    (goto-char (point-min) buffer)
-    (condition-case nil
-	(call-with-condition-handler
-	    #'(lambda (error-info)
-		;; end-of-file is expected, so don't output error or backtrace
-		;; or enter debugger in this case.
-		(unless (eq 'end-of-file (car error-info))
-		  (test-harness-unexpected-error-condition-handler
-		   error-info "reading forms from buffer")))
-	    #'(lambda ()
-		(while t
-		  (setq body (cons (read buffer) body)))))
-      (error nil))
-    `(lambda ()
-       (defvar passes)
-       (defvar assertion-failures)
-       (defvar no-error-failures)
-       (defvar wrong-error-failures)
-       (defvar missing-message-failures)
-       (defvar other-failures)
-
-       (defvar trick-optimizer)
-
-       ,@(nreverse body))))
-
-(defun test-harness-from-buffer (inbuffer filename)
-  "Run tests in buffer INBUFFER, visiting FILENAME."
-  (defvar trick-optimizer)
-  (let ((passes 0)
-	(assertion-failures 0)
-	(no-error-failures 0)
-	(wrong-error-failures 0)
-	(missing-message-failures 0)
-	(other-failures 0)
-	(unexpected-test-file-failures 0)
-
-	;; #### perhaps this should be a defvar, and output at the very end
-	;; OTOH, this way AC types can use a null EMACSPACKAGEPATH to find
-	;; what stuff is needed, and ways to avoid using them
-	(skipped-test-reasons (make-hash-table :test 'equal))
-
-	(trick-optimizer nil)
-	(debug-on-error t)
-	(pass-stream nil))
-    (with-output-to-temp-buffer "*Test-Log*"
-      (princ (format "Testing %s...\n\n" filename))
-
-      (defconst test-harness-failure-tag "FAIL")
-      (defconst test-harness-success-tag "PASS")
-
-;;;;; BEGIN DEFINITION OF MACROS USEFUL IN TEST CODE
-
-      (defmacro Known-Bug-Expect-Failure (&rest body)
-	"Wrap a BODY that consists of tests that are known to fail.
-This causes messages to be printed on failure indicating that this is expected,
-and on success indicating that this is unexpected."
-	`(let ((test-harness-bug-expected t)
-	       (test-harness-failure-tag "KNOWN BUG")
-	       (test-harness-success-tag "PASS (FAILURE EXPECTED)"))
-	  ,@body))
-
-      (defmacro Known-Bug-Expect-Error (expected-error &rest body)
-	"Wrap a BODY that consists of tests that are known to trigger an error.
-This causes messages to be printed on failure indicating that this is expected,
-and on success indicating that this is unexpected."
-	(let ((quoted-body (if (= 1 (length body))
-			       `(quote ,(car body)) `(quote (progn ,@body)))))
-          `(let ((test-harness-bug-expected t)
-		 (test-harness-failure-tag "KNOWN BUG")
-                 (test-harness-success-tag "PASS (FAILURE EXPECTED)"))
-            (condition-case error-info
-                (progn
-                  (setq trick-optimizer (progn ,@body))
-                  (Print-Pass 
-                   "%S executed successfully, but expected error %S"
-                   ,quoted-body
-                   ',expected-error)
-                  (incf passes))
-              (,expected-error
-               (Print-Failure "%S ==> error %S, as expected"
-                              ,quoted-body ',expected-error)
-               (incf no-error-failures))
-              (error
-               (Print-Failure "%S ==> expected error %S, got error %S instead"
-                              ,quoted-body ',expected-error error-info)
-               (incf wrong-error-failures))))))
-
-      (defmacro Implementation-Incomplete-Expect-Failure (&rest body)
-	"Wrap a BODY containing tests that are known to fail due to incomplete code.
-This causes messages to be printed on failure indicating that the
-implementation is incomplete (and hence the failure is expected); and on
-success indicating that this is unexpected."
-	`(let ((test-harness-bug-expected t)
-	       (test-harness-failure-tag "IMPLEMENTATION INCOMPLETE")
-	       (test-harness-success-tag "PASS (FAILURE EXPECTED)"))
-	  ,@body))
-    
-      (defun Print-Failure (fmt &rest args)
-	(setq fmt (format "%s: %s" test-harness-failure-tag fmt))
-	(if (noninteractive) (apply #'message fmt args))
-	(princ (concat (apply #'format fmt args) "\n")))
-
-      (defun Print-Pass (fmt &rest args)
-	(setq fmt (format "%s: %s" test-harness-success-tag fmt))
-	(and test-harness-verbose
-	     (princ (concat (apply #'format fmt args) "\n"))))
-
-      (defun Print-Skip (test reason &optional fmt &rest args)
-	(setq fmt (concat "SKIP: %S BECAUSE %S" fmt))
-	(princ (concat (apply #'format fmt test reason args) "\n")))
-
-      (defmacro Skip-Test-Unless (condition reason description &rest body)
-	"Unless CONDITION is satisfied, skip test BODY.
-REASON is a description of the condition failure, and must be unique (it
-is used as a hash key).  DESCRIPTION describes the tests that were skipped.
-BODY is a sequence of expressions and may contain several tests."
-	`(if (not ,condition)
-	     (let ((count (gethash ,reason skipped-test-reasons)))
-	       (puthash ,reason (if (null count) 1 (1+ count))
-			skipped-test-reasons)
-	       (Print-Skip ,description ,reason))
-	   ,@body))
-
-      (defmacro Assert (assertion &optional failing-case description)
-	"Test passes if ASSERTION is true.
-Optional FAILING-CASE describes the particular failure.  Optional
-DESCRIPTION describes the assertion; by default, the unevalated assertion
-expression is given.  FAILING-CASE and DESCRIPTION are useful when Assert
-is used in a loop."
-	(let ((description
-	       (or description `(quote ,assertion))))
-	  `(condition-case nil
-	    (call-with-condition-handler
-		#'(lambda (error-info)
-		    (if (eq 'cl-assertion-failed (car error-info))
-			(progn
-			  (Print-Failure
-			   (if ,failing-case
-			       "Assertion failed: %S; failing case = %S"
-			     "Assertion failed: %S")
-			   ,description ,failing-case)
-			  (incf assertion-failures)
-			  (test-harness-assertion-failure-do-debug error-info))
-		      (Print-Failure
-		       (if ,failing-case
-			   "%S ==> error: %S; failing case =  %S"
-			 "%S ==> error: %S")
-		       ,description error-info ,failing-case)
-		      (incf other-failures)
-		      (test-harness-unexpected-error-do-debug error-info)))
-		#'(lambda ()
-		    (assert ,assertion)
-		    (Print-Pass "%S" ,description)
-		    (incf passes)))
-	    (cl-assertion-failed nil))))
-
-;;;;; BEGIN DEFINITION OF SPECIFIC KINDS OF ASSERT MACROS
-
-      (defmacro Assert-test (test testval expected &optional failing-case
-			     description)
-	"Test passes if TESTVAL compares correctly to EXPECTED using TEST.
-TEST should be a two-argument predicate (i.e. a function of two arguments
-that returns t or nil), such as `eq', `eql', `equal', `equalp', `=', `<=',
-'>', 'file-newer-than-file-p' etc.  Optional FAILING-CASE describes the
-particular failure; any value given here will be concatenated with a phrase
-describing the expected and actual values of the comparison.  Optional
-DESCRIPTION describes the assertion; by default, the unevalated comparison
-expressions are given.  FAILING-CASE and DESCRIPTION are useful when Assert
-is used in a loop."
-	(let* ((assertion `(,test ,testval ,expected))
-	       (failmsg `(format "%S should be `%s' to %S but isn't"
-			  ,testval ',test ,expected))
-	       (failmsg2 (if failing-case `(concat 
-					   (format "%S, " ,failing-case)
-					   ,failmsg)
-			  failmsg)))
-	  `(Assert ,assertion ,failmsg2 ,description)))
-
-      (defmacro Assert-test-not (test testval expected &optional failing-case
-				 description)
-	"Test passes if TESTVAL does not compare correctly to EXPECTED using TEST.
-TEST should be a two-argument predicate (i.e. a function of two arguments
-that returns t or nil), such as `eq', `eql', `equal', `equalp', `=', `<=',
-'>', 'file-newer-than-file-p' etc.  Optional FAILING-CASE describes the
-particular failure; any value given here will be concatenated with a phrase
-describing the expected and actual values of the comparison.  Optional
-DESCRIPTION describes the assertion; by default, the unevalated comparison
-expressions are given.  FAILING-CASE and DESCRIPTION are useful when Assert
-is used in a loop."
-	(let* ((assertion `(not (,test ,testval ,expected)))
-	       (failmsg `(format "%S shouldn't be `%s' to %S but is"
-			  ,testval ',test ,expected))
-	       (failmsg2 (if failing-case `(concat 
-					   (format "%S, " ,failing-case)
-					   ,failmsg)
-			  failmsg)))
-	  `(Assert ,assertion ,failmsg2 ,description)))
-
-      ;; Specific versions of `Assert-test'.  These are just convenience
-      ;; functions, functioning identically to `Assert-test', and duplicating
-      ;; the doc string for each would be too annoying.
-      (defmacro Assert-eq (testval expected &optional failing-case
-			   description)
-	`(Assert-test eq ,testval ,expected ,failing-case ,description))
-      (defmacro Assert-eql (testval expected &optional failing-case
-			    description)
-	`(Assert-test eql ,testval ,expected ,failing-case ,description))
-      (defmacro Assert-equal (testval expected &optional failing-case
-			      description)
-	`(Assert-test equal ,testval ,expected ,failing-case ,description))
-      (defmacro Assert-equalp (testval expected &optional failing-case
-			      description)
-	`(Assert-test equalp ,testval ,expected ,failing-case ,description))
-      (defmacro Assert-string= (testval expected &optional failing-case
-			      description)
-	`(Assert-test string= ,testval ,expected ,failing-case ,description))
-      (defmacro Assert= (testval expected &optional failing-case
-			 description)
-	`(Assert-test = ,testval ,expected ,failing-case ,description))
-      (defmacro Assert<= (testval expected &optional failing-case
-			  description)
-	`(Assert-test <= ,testval ,expected ,failing-case ,description))
-
-      ;; Specific versions of `Assert-test-not'.  These are just convenience
-      ;; functions, functioning identically to `Assert-test-not', and
-      ;; duplicating the doc string for each would be too annoying.
-      (defmacro Assert-not-eq (testval expected &optional failing-case
-			       description)
-	`(Assert-test-not eq ,testval ,expected ,failing-case ,description))
-      (defmacro Assert-not-eql (testval expected &optional failing-case
-				description)
-	`(Assert-test-not eql ,testval ,expected ,failing-case ,description))
-      (defmacro Assert-not-equal (testval expected &optional failing-case
-				  description)
-	`(Assert-test-not equal ,testval ,expected ,failing-case ,description))
-      (defmacro Assert-not-equalp (testval expected &optional failing-case
-				   description)
-	`(Assert-test-not equalp ,testval ,expected ,failing-case ,description))
-      (defmacro Assert-not-string= (testval expected &optional failing-case
-				    description)
-	`(Assert-test-not string= ,testval ,expected ,failing-case ,description))
-      (defmacro Assert-not= (testval expected &optional failing-case
-			     description)
-	`(Assert-test-not = ,testval ,expected ,failing-case ,description))
-
-      (defmacro Check-Error (expected-error &rest body)
-	(let ((quoted-body (if (= 1 (length body))
-			       `(quote ,(car body)) `(quote (progn ,@body)))))
-	  `(condition-case error-info
-	       (progn
-		 (setq trick-optimizer (progn ,@body))
-		 (Print-Failure "%S executed successfully, but expected error %S"
-				,quoted-body
-				',expected-error)
-		 (incf no-error-failures))
-	     (,expected-error
-	      (Print-Pass "%S ==> error %S, as expected"
-			  ,quoted-body ',expected-error)
-	      (incf passes))
-	     (error
-	      (Print-Failure "%S ==> expected error %S, got error %S instead"
-			     ,quoted-body ',expected-error error-info)
-	      (incf wrong-error-failures)))))
-
-      (defmacro Check-Error-Message (expected-error expected-error-regexp
-						    &rest body)
-	(let ((quoted-body (if (= 1 (length body))
-			       `(quote ,(car body)) `(quote (progn ,@body)))))
-	  `(condition-case error-info
-	       (progn
-		 (setq trick-optimizer (progn ,@body))
-		 (Print-Failure "%S executed successfully, but expected error %S"
-				,quoted-body ',expected-error)
-		 (incf no-error-failures))
-	     (,expected-error
-	      ;; #### Damn, this binding doesn't capture frobs, eg, for
-	      ;; invalid_argument() ... you only get the REASON.  And for
-	      ;; wrong_type_argument(), there's no reason only FROBs.
-	      ;; If this gets fixed, fix tests in regexp-tests.el.
-	      (let ((error-message (second error-info)))
-		(if (string-match ,expected-error-regexp error-message)
-		    (progn
-		      (Print-Pass "%S ==> error %S %S, as expected"
-				  ,quoted-body error-message ',expected-error)
-		      (incf passes))
-		  (Print-Failure "%S ==> got error %S as expected, but error message %S did not match regexp %S"
-				 ,quoted-body ',expected-error error-message ,expected-error-regexp)
-		  (incf wrong-error-failures))))
-	     (error
-	      (Print-Failure "%S ==> expected error %S, got error %S instead"
-			     ,quoted-body ',expected-error error-info)
-	      (incf wrong-error-failures)))))
-
-      ;; Do not use this with Silence-Message.
-      (defmacro Check-Message (expected-message-regexp &rest body)
-	(Skip-Test-Unless (fboundp 'defadvice)
-			  "can't defadvice"
-			  expected-message-regexp
-	  (let ((quoted-body (if (= 1 (length body))
-				 `(quote ,(car body))
-			       `(quote (progn ,@body)))))
-	    `(let ((messages ""))
-	       (defadvice message (around collect activate)
-		 (defvar messages)
-		 (let ((msg-string (apply 'format (ad-get-args 0))))
-		   (setq messages (concat messages msg-string))
-		   msg-string))
-	       (ignore-errors
-		 (call-with-condition-handler
-		     #'(lambda (error-info)
-			 (Print-Failure "%S ==> unexpected error %S"
-					,quoted-body error-info)
-			 (incf other-failures)
-			 (test-harness-unexpected-error-do-debug error-info))
-		     #'(lambda ()
-			 (setq trick-optimizer (progn ,@body))
-			 (if (string-match ,expected-message-regexp messages)
-			     (progn
-			       (Print-Pass
-				"%S ==> value %S, message %S, matching %S, as expected"
-				,quoted-body trick-optimizer messages
-				',expected-message-regexp)
-			       (incf passes))
-			   (Print-Failure
-			    "%S ==> value %S, message %S, NOT matching expected %S"
-			    ,quoted-body  trick-optimizer messages
-			    ',expected-message-regexp)
-			   (incf missing-message-failures)))))
-	       (ad-unadvise 'message)))))
-
-      ;; #### Perhaps this should override `message' itself, too?
-      (defmacro Silence-Message (&rest body)
-	`(flet ((append-message (&rest args) ())
-                (clear-message (&rest args) ()))
-          ,@body))
-
-      (defmacro Ignore-Ebola (&rest body)
-	`(let ((debug-issue-ebola-notices -42)) ,@body))
-
-      (defun Int-to-Marker (pos)
-	(save-excursion
-	  (set-buffer standard-output)
-	  (save-excursion
-	    (goto-char pos)
-	    (point-marker))))
-
-      (princ "Testing Interpreted Lisp\n\n")
-
-      (test-harness-error-wrap
-       "executing interpreted code"
-       "Test suite execution aborted."
-       (funcall (test-harness-read-from-buffer inbuffer)))
-
-      (princ "\nTesting Compiled Lisp\n\n")
-
-      (let (code
-	    (test-harness-test-compiled t))
-	(test-harness-error-wrap
-	 "byte-compiling code" nil
-	 (setq code
-	       ;; our lisp code is often intentionally dubious,
-	       ;; so throw away _all_ the byte compiler warnings.
-	       (letf (((symbol-function 'byte-compile-warn)
-		       'ignore))
-		 (byte-compile (test-harness-read-from-buffer
-				inbuffer))))
-	 )
-
-	(test-harness-error-wrap "executing byte-compiled code"
-				 "Test suite execution aborted."
-				 (if code (funcall code)))
-	)
-      (princ (format "\nSUMMARY for %s:\n" filename))
-      (princ (format "\t%5d passes\n" passes))
-      (princ (format "\t%5d assertion failures\n" assertion-failures))
-      (princ (format "\t%5d errors that should have been generated, but weren't\n" no-error-failures))
-      (princ (format "\t%5d wrong-error failures\n" wrong-error-failures))
-      (princ (format "\t%5d missing-message failures\n" missing-message-failures))
-      (princ (format "\t%5d other failures\n" other-failures))
-      (let* ((total (+ passes
-		       assertion-failures
-		       no-error-failures
-		       wrong-error-failures
-		       missing-message-failures
-		       other-failures))
-	     (basename (file-name-nondirectory filename))
-	     (summary-msg
-	      (cond ((> unexpected-test-file-failures 0)
-		     (format test-harness-aborted-summary-template
-			     (concat basename ":") total))
-		    ((> total 0)
-		     (format test-harness-file-summary-template
-			     (concat basename ":")
-			     passes total (/ (* 100 passes) total)))
-		    (t
-		     (format test-harness-null-summary-template
-			     (concat basename ":")))))
-	     (reasons ""))
-	(maphash (lambda (key value)
-		   (setq reasons
-			 (concat reasons
-				 (format "\n    %d tests skipped because %s."
-					 value key))))
-		 skipped-test-reasons)
-	(when (> (length reasons) 1)
-	  (setq summary-msg (concat summary-msg reasons "
-    It may be that XEmacs cannot find your installed packages.  Set
-    EMACSPACKAGEPATH to the package hierarchy root or configure with
-    --package-path to enable the skipped tests.")))
-	(setq test-harness-file-results-alist
-	      (cons (list filename passes total)
-		    test-harness-file-results-alist))
-	(message "%s" summary-msg))
-      (when (> unexpected-test-file-failures 0)
-	(setq unexpected-test-suite-failure-files
-	      (cons filename unexpected-test-suite-failure-files))
-	(setq unexpected-test-suite-failures
-	      (+ unexpected-test-suite-failures unexpected-test-file-failures))
-	(message "Test suite execution failed unexpectedly."))
-      (fmakunbound 'Assert)
-      (fmakunbound 'Check-Error)
-      (fmakunbound 'Check-Message)
-      (fmakunbound 'Check-Error-Message)
-      (fmakunbound 'Ignore-Ebola)
-      (fmakunbound 'Int-to-Marker)
-      (and noninteractive
-	   (message "%s" (buffer-substring-no-properties
-			  nil nil "*Test-Log*")))
-      )))
-
-(defvar test-harness-results-point-max nil)
-(defmacro displaying-emacs-test-results (&rest body)
-  `(let ((test-harness-results-point-max test-harness-results-point-max))
-     ;; Log the file name.
-     (test-harness-log-file)
-     ;; Record how much is logged now.
-     ;; We will display the log buffer if anything more is logged
-     ;; before the end of BODY.
-     (or test-harness-results-point-max
-	 (save-excursion
-	   (set-buffer (get-buffer-create "*Test-Log*"))
-	   (setq test-harness-results-point-max (point-max))))
-     (unwind-protect
-	 (condition-case error-info
-	     (progn ,@body)
-	   (error
-	    (test-harness-report-error error-info)))
-       (save-excursion
-	 ;; If there were compilation warnings, display them.
-	 (set-buffer "*Test-Log*")
-	 (if (= test-harness-results-point-max (point-max))
-	     nil
-	   (if temp-buffer-show-function
-	       (let ((show-buffer (get-buffer-create "*Test-Log-Show*")))
-		 (save-excursion
-		   (set-buffer show-buffer)
-		   (setq buffer-read-only nil)
-		   (erase-buffer))
-		 (copy-to-buffer show-buffer
-				 (save-excursion
-				   (goto-char test-harness-results-point-max)
-				   (forward-line -1)
-				   (point))
-				 (point-max))
-		 (funcall temp-buffer-show-function show-buffer))
-              (select-window
-               (prog1 (selected-window)
-                 (select-window (display-buffer (current-buffer)))
-                 (goto-char test-harness-results-point-max)
-                 (recenter 1)))))))))
-
-(defun batch-test-emacs-1 (file)
-  (condition-case error-info
-      (progn (test-emacs-test-file file) t)
-    (error
-     (princ ">>Error occurred processing ")
-     (princ file)
-     (princ ": ")
-     (display-error error-info nil)
-     (terpri)
-     nil)))
-
-(defun batch-test-emacs ()
-  "Run `test-harness' on the files remaining on the command line.
-Use this from the command line, with `-batch';
-it won't work in an interactive Emacs.
-Each file is processed even if an error occurred previously.
-A directory can be given as well, and all files will be processed --
-however, the file test-harness.el, which implements the test harness,
-will be skipped.
-For example, invoke \"xemacs -batch -f batch-test-emacs tests\""
-  ;; command-line-args-left is what is left of the command line (from
-  ;; startup.el)
-  (defvar command-line-args-left)	;Avoid 'free variable' warning
-  (defvar debug-issue-ebola-notices)
-  (if (not noninteractive)
-      (error "`batch-test-emacs' is to be used only with -batch"))
-  (let ((error nil))
-    (dolist (file command-line-args-left)
-      (if (file-directory-p file)
-	  (dolist (file-in-dir (directory-files file t))
-	    (when (and (string-match emacs-lisp-file-regexp file-in-dir)
-		       (not (or (auto-save-file-name-p file-in-dir)
-				(backup-file-name-p file-in-dir)
-				(equal (file-name-nondirectory file-in-dir)
-				       "test-harness.el"))))
-	      (or (batch-test-emacs-1 file-in-dir)
-		  (setq error t))))
-	(or (batch-test-emacs-1 file)
-	    (setq error t))))
-    (let ((namelen 0)
-	  (succlen 0)
-	  (testlen 0)
-	  (results test-harness-file-results-alist))
-      ;; compute maximum lengths of variable components of report
-      ;; probably should just use (length "byte-compiler-tests.el")
-      ;; and 5-place sizes -- this will also work for the file-by-file
-      ;; printing when Adrian's kludge gets reverted
-      (flet ((print-width (i)
-	       (let ((x 10) (y 1))
-		 (while (>= i x)
-		   (setq x (* 10 x) y (1+ y)))
-		 y)))
-	(while results
-	  (let* ((head (car results))
-		 (nn (length (file-name-nondirectory (first head))))
-		 (ss (print-width (second head)))
-		 (tt (print-width (third head))))
-	    (when (> nn namelen) (setq namelen nn))
-	    (when (> ss succlen) (setq succlen ss))
-	    (when (> tt testlen) (setq testlen tt)))
-	  (setq results (cdr results))))
-      ;; create format and print
-      (let ((results (reverse test-harness-file-results-alist)))
-	(while results
-	  (let* ((head (car results))
-		 (basename (file-name-nondirectory (first head)))
-		 (nsucc (second head))
-		 (ntest (third head)))
-	    (cond ((member (first head) unexpected-test-suite-failure-files)
-		   (message test-harness-aborted-summary-template
-			    (concat basename ":")
-			    ntest))
-		  ((> ntest 0)
-		   (message test-harness-file-summary-template
-			    (concat basename ":")
-			    nsucc
-			    ntest
-			    (/ (* 100 nsucc) ntest)))
-		  (t
-		   (message test-harness-null-summary-template
-			    (concat basename ":"))))
-	    (setq results (cdr results)))))
-      (when (> unexpected-test-suite-failures 0)
-	(message "\n***** There %s %d unexpected test suite %s in %s:"
-		 (if (= unexpected-test-suite-failures 1) "was" "were")
-		 unexpected-test-suite-failures
-		 (if (= unexpected-test-suite-failures 1) "failure" "failures")
-		 (if (= (length unexpected-test-suite-failure-files) 1)
-		     "file"
-		   "files"))
-	(while unexpected-test-suite-failure-files
-	  (let ((line (pop unexpected-test-suite-failure-files)))
-	    (while (and (< (length line) 61)
-			unexpected-test-suite-failure-files)
-	      (setq line
-		    (concat line " "
-			    (pop unexpected-test-suite-failure-files))))
-	    (message line)))))
-    (message "\nDone")
-    (kill-emacs (if error 1 0))))
-
-(provide 'test-harness)
-
-;;; test-harness.el ends here