changeset 5865:a45722e74335

Merge.
author Aidan Kehoe <kehoea@parhasard.net>
date Thu, 12 Mar 2015 16:27:13 +0000
parents 27876789edc5 (diff) 750fab17b299 (current diff)
children 5ea790936de9
files lisp/ChangeLog src/ChangeLog tests/ChangeLog tests/automated/lisp-tests.el
diffstat 15 files changed, 464 insertions(+), 178 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Wed Feb 25 11:47:12 2015 +0000
+++ b/ChangeLog	Thu Mar 12 16:27:13 2015 +0000
@@ -1,3 +1,7 @@
+2015-02-28  Mike Kupfer  <mike.kupfer@xemacs.org>
+
+	* README: fix note about which Bitbucket repository to push to.
+
 2015-01-10  Stephen J. Turnbull  <stephen@xemacs.org>
 
 	* configure.ac (Postgresql): Improve Installation text.
--- a/README	Wed Feb 25 11:47:12 2015 +0000
+++ b/README	Thu Mar 12 16:27:13 2015 +0000
@@ -1,5 +1,8 @@
 This directory tree holds version 21.5 of XEmacs.
 
+Note: new changesets should be pushed to the "xemacs" repository
+(https://bitbucket.org/xemacs/xemacs), not "xemacs-beta".
+
 The information in this file has been superseded by the XEmacs FAQ.
 
 The easiest way to read the FAQ is to go to
@@ -11,5 +14,3 @@
 
 If you don't have XEmacs running and can't access the web,
 look directly at `man/xemacs-faq.texi' or `info/xemacs-faq.info'.
-
-
--- a/lib-src/ChangeLog	Wed Feb 25 11:47:12 2015 +0000
+++ b/lib-src/ChangeLog	Thu Mar 12 16:27:13 2015 +0000
@@ -1,3 +1,16 @@
+2015-03-08  Aidan Kehoe  <kehoea@parhasard.net>
+
+	* gnuserv.c (echo_request):
+	No longer close the file handle unconditionally, leave this to the
+	individual socket types.
+	* gnuserv.c (handle_internet_request):
+	Close the file handle here.
+	* gnuserv.c (handle_unix_request):
+	Don't close the file handle here, document why (it broke gnuclient
+	under OS X). It should actually be OK, but my suspicion is that
+	the issues is that the Unix (local) domain sockets are still
+	underdocumented compared to the internet sockets.
+
 2014-12-05  Jerry James  <james@xemacs.org>
 
 	* gnuserv.c (echo_request): close the socket when done
--- a/lib-src/gnuserv.c	Wed Feb 25 11:47:12 2015 +0000
+++ b/lib-src/gnuserv.c	Thu Mar 12 16:27:13 2015 +0000
@@ -321,7 +321,6 @@
     exit(1);
   } /* if */
 
-  close(s);
 } /* echo_request */
 
 
@@ -754,6 +753,7 @@
 
   echo_request(s);
 
+  close(s); 
 } /* handle_internet_request */
 #endif /* INTERNET_DOMAIN_SOCKETS */
 
@@ -864,6 +864,14 @@
 
   echo_request(s);
 
+  /* Closing s here (or rather, within echo_request() with both
+     internet and local connections) meant gnuserv never returned
+     usefully under OS X, as of 20150308, reflecting changeset
+     https://bitbucket.org/xemacs/xemacs/commits/c03dd89 . Keeping it
+     open is not a significant security risk (it's a local connection,
+     with file system access restrictions) and given the practical
+     limitation on the number of handles gnuserv will keep around,
+     it's also not a significant resource issue. Leave it open.  */
 } /* handle_unix_request */
 #endif /* UNIX_DOMAIN_SOCKETS */
 
--- a/lisp/ChangeLog	Wed Feb 25 11:47:12 2015 +0000
+++ b/lisp/ChangeLog	Thu Mar 12 16:27:13 2015 +0000
@@ -1,3 +1,57 @@
+2015-03-12  Aidan Kehoe  <kehoea@parhasard.net>
+
+	* simple.el (append-message): Be more careful about saving a
+	non-nil value for START in message-stack.
+
+2015-03-11  Aidan Kehoe  <kehoea@parhasard.net>
+
+	* isearch-mode.el:
+	* isearch-mode.el (isearch-fix-case):
+	Use the new #'no-case-regexp-p function if treating ISEARCH-STRING
+	as a regular expression; otherwise, use the [[:upper:]] character
+	class.
+	* isearch-mode.el (isearch-no-upper-case-p): Removed. 
+	* isearch-mode.el (with-caps-disable-folding): Removed.
+	These two haven't been used since 1998.
+	* occur.el (occur-1):
+	Use #'no-case-regexp-p here.
+	* replace.el (perform-replace):
+	Don't use #'no-upper-case-p, use #'no-case-regexp-p or
+	(string-match "[[:upper:]]" ...) as appropriate.
+	* simple.el:
+	* simple.el (no-upper-case-p): Removed. This did two different
+	things, and its secondary function (examining regular expressions)
+	just became much more complicated; move the regular expression
+	functionality to its own function, use character classes when
+	examining non-regular-expressions instead.
+	The code to look for character classes, and the design decision
+	that this should be done, are from GNU, thank you Stefan Monnier.
+	* simple.el (no-case-regexp-p): New.
+	Given a REGEXP, return non-nil if it has nothing to suggest an
+	interactive user wants a case-sensitive search.
+	* simple.el (with-search-caps-disable-folding):
+	* simple.el (with-interactive-search-caps-disable-folding):
+	Update both these macros to use #'no-case-regexp-p.
+
+2015-03-11  Aidan Kehoe  <kehoea@parhasard.net>
+
+	Correct #'clear-message and friends so the START and END supplied
+	to #'append-message are reflected when restoring messages from the
+	message stack.
+	* simple.el (remove-message-hook):
+	Update this to reflect the START and END keyword arguments.
+	* simple.el (log-message):
+	Update this to take START and END keyword arguments.
+	* simple.el (clear-message):
+	Update this to reflect a changed `message-stack' alist structure.
+	* simple.el (remove-message):
+	Update this to reflect a changed `message-stack' alist structure;
+	don't do `with-trapping-errors' and resignal use
+	#'call-with-condition-handler directly instead, for better
+	backtraces and easier debugging.
+	* simple.el (append-message):
+	Update this to reflect a changed message-stack structure.
+
 2015-02-25  Aidan Kehoe  <kehoea@parhasard.net>
 
 	* cl-extra.el (digit-char-p): Moved to data.c.
--- a/lisp/isearch-mode.el	Wed Feb 25 11:47:12 2015 +0000
+++ b/lisp/isearch-mode.el	Thu Mar 12 16:27:13 2015 +0000
@@ -1068,7 +1068,11 @@
 	   (not isearch-fixed-case)
 	   search-caps-disable-folding)
       (setq isearch-case-fold-search
-	    (no-upper-case-p isearch-string isearch-regexp)))
+            (if isearch-regexp
+                (no-case-regexp-p isearch-string)
+              (save-match-data
+                (let (case-fold-search)
+                  (not (string-match "[[:upper:]]" isearch-string)))))))
   (setq isearch-mode (if case-fold-search
                          (if isearch-case-fold-search
                              " Isearch"  ;As God Intended Mode
@@ -1856,15 +1860,6 @@
 			 t))
 		     isearch-unhidden-extents)))))
 
-(defun isearch-no-upper-case-p (string)
-  "Return t if there are no upper case chars in string.
-But upper case chars preceded by \\ do not count since they
-have special meaning in a regexp."
-  ;; this incorrectly returns t for "\\\\A"
-  (let ((case-fold-search nil))
-    (not (string-match "\\(^\\|[^\\]\\)[A-Z]" string))))
-(make-obsolete 'isearch-no-upper-case-p 'no-upper-case-p)
-
 ;; Portability functions to support various Emacs versions.
 
 (defun isearch-char-to-string (c)
@@ -1876,20 +1871,6 @@
 ;  (isearch-char-to-string c))
 
 (define-function 'isearch-text-char-description 'text-char-description)
-
-;; Used by etags.el and info.el
-(defmacro with-caps-disable-folding (string &rest body) "\
-Eval BODY with `case-fold-search' let to nil if STRING contains
-uppercase letters and `search-caps-disable-folding' is t."
-  `(let ((case-fold-search
-          (if (and case-fold-search search-caps-disable-folding)
-              (isearch-no-upper-case-p ,string)
-            case-fold-search)))
-     ,@body))
-(make-obsolete 'with-caps-disable-folding 'with-search-caps-disable-folding)
-(put 'with-caps-disable-folding 'lisp-indent-function 1)
-(put 'with-caps-disable-folding 'edebug-form-spec '(form body))
-
 
 ;;;========================================================
 ;;; Advanced highlighting
--- a/lisp/occur.el	Wed Feb 25 11:47:12 2015 +0000
+++ b/lisp/occur.el	Thu Mar 12 16:27:13 2015 +0000
@@ -394,8 +394,7 @@
 	(let ((count (occur-engine
 		      regexp active-bufs occur-buf
 		      (or nlines list-matching-lines-default-context-lines)
-		      (and case-fold-search
-			   (no-upper-case-p regexp t))
+		      (and case-fold-search (no-case-regexp-p regexp))
 		      list-matching-lines-buffer-name-face
 		      nil list-matching-lines-face t)))
 	  (let* ((bufcount (length active-bufs))
--- a/lisp/replace.el	Wed Feb 25 11:47:12 2015 +0000
+++ b/lisp/replace.el	Thu Mar 12 16:27:13 2015 +0000
@@ -563,7 +563,11 @@
 	 ;; XEmacs addition
 	 (qr-case-fold-search
 	  (if (and case-fold-search search-caps-disable-folding)
-	      (no-upper-case-p search-string regexp-flag)
+              (if regexp-flag
+                  (no-case-regexp-p search-string)
+                (save-match-data
+                  (let (case-fold-search)
+                    (not (string-match "[[:upper:]]" search-string)))))
 	    case-fold-search))
 	 (message
 	  (if query-flag
--- a/lisp/simple.el	Wed Feb 25 11:47:12 2015 +0000
+++ b/lisp/simple.el	Thu Mar 12 16:27:13 2015 +0000
@@ -94,47 +94,70 @@
   "Warnings customizations."
   :group 'minibuffer)
 
-
 (defcustom search-caps-disable-folding t
   "*If non-nil, upper case chars disable case fold searching.
 This does not apply to \"yanked\" strings."
   :type 'boolean
   :group 'editing-basics)
 
-;; This is stolen (and slightly modified) from FSF emacs's
-;; `isearch-no-upper-case-p'.
-(defun no-upper-case-p (string &optional regexp-flag)
-  "Return t if there are no upper case chars in STRING.
-If REGEXP-FLAG is non-nil, disregard letters preceded by `\\' (but not `\\\\')
-since they have special meaning in a regexp."
+(defun no-case-regexp-p (regexp)
+  "Return t if there are no case-specific constructs in REGEXP.
+
+Lower case characters are regarded as not case-specific.  Upper case
+characters are usually regarded as case-specific, but upper case characters
+used in special regexp constructs, where they do not match upper case
+characters specifically, are regarded as not case-specific.  In contrast, the
+character classes [:lower:] and [:upper:] are viewed as case-specific.
+
+This is intended to be used by interactive searching code to decide, in a
+do-what-I-mean fashion, whether a given search should be case-sensitive."
   (let ((case-fold-search nil))
-    (not (string-match (if regexp-flag
-			   "\\(^\\|\\\\\\\\\\|[^\\]\\)[A-Z]"
-			 "[A-Z]")
-		       string))
-    ))
-
-(defmacro with-search-caps-disable-folding (string regexp-flag &rest body) "\
-Eval BODY with `case-fold-search' let to nil if `search-caps-disable-folding'
-is non-nil, and if STRING (either a string or a regular expression according
-to REGEXP-FLAG) contains uppercase letters."
+    (save-match-data
+      (not (or (string-match "\\(^\\|\\\\\\\\\\|[^\\]\\)[[:upper:]]" regexp)
+               (and (string-match "\\[:\\(upp\\|low\\)er:]" regexp)
+                    (condition-case err
+                        (progn
+                          (string-match (substring regexp 0
+                                                   (match-beginning 0)) "")
+                          nil)
+                      (invalid-regexp
+                       (equal "Unmatched [ or [^" (cadr err))))))))))
+
+(defmacro* with-search-caps-disable-folding (string regexp-p &body body)
+  "Execute the forms in BODY, respecting `search-caps-disable-folding'.
+
+Within BODY, bind `case-fold-search' to nil if `search-caps-disable-folding'
+is non-nil, REGEXP-P is nil, and if STRING contains any uppercase characters.
+
+If REGEXP-P is non-nil, treat STRING as a regular expression, and bind
+`case-fold-search' to nil if it contains uppercase characters that are
+not special regular expression constructs, or if it contains
+case-specific character classes such as `[[:upper:]]' or
+`[[:lower:]]'.  See `no-case-regexp-p'."
   `(let ((case-fold-search
           (if (and case-fold-search search-caps-disable-folding)
-              (no-upper-case-p ,string ,regexp-flag)
+              (if ,regexp-p
+                  (no-case-regexp-p ,string)
+                (save-match-data
+                  (let (case-fold-search)
+                    (not (string-match "[[:upper:]]" ,string)))))
             case-fold-search)))
      ,@body))
 (put 'with-search-caps-disable-folding 'lisp-indent-function 2)
 (put 'with-search-caps-disable-folding 'edebug-form-spec
      '(sexp sexp &rest form))
 
-(defmacro with-interactive-search-caps-disable-folding (string regexp-flag
-							       &rest body)
-  "Same as `with-search-caps-disable-folding', but only in the case of a
-function called interactively."
+(defmacro* with-interactive-search-caps-disable-folding (string regexp-p
+                                                                &body body)
+  "Like `with-search-caps-disable-folding', but only when interactive."
   `(let ((case-fold-search
-	  (if (and (interactive-p)
-		   case-fold-search search-caps-disable-folding)
-              (no-upper-case-p ,string ,regexp-flag)
+	  (if (and (interactive-p) case-fold-search
+                   search-caps-disable-folding)
+              (if ,regexp-p
+                  (no-case-regexp-p ,string)
+                (save-match-data
+                  (let (case-fold-search)
+                    (not (string-match "[[:upper:]]" ,string)))))
             case-fold-search)))
      ,@body))
 (put 'with-interactive-search-caps-disable-folding 'lisp-indent-function 2)
@@ -4166,8 +4189,9 @@
 (defvar remove-message-hook 'log-message
   "A function or list of functions to be called when a message is removed
 from the echo area at the bottom of the frame.  The label of the removed
-message is passed as the first argument, and the text of the message
-as the second argument.")
+message is passed as the first argument, the text of the message as the second
+argument, and the start and end of the substring of the message can be
+supplied as keyword arguments.")
 
 (defcustom log-message-max-size 50000
   "Maximum size of the \" *Message-Log*\" buffer.  See `log-message'."
@@ -4300,7 +4324,7 @@
   "For use as the `log-message-filter-function'.  Only logs error messages."
   (eq label 'error))
 
-(defun log-message (label message)
+(defun* log-message (label message &key (start 0) end)
   "Stuff a copy of the message into the \" *Message-Log*\" buffer,
 if it satisfies the `log-message-filter-function'.
 
@@ -4316,12 +4340,10 @@
       (let (extent)
 	;; Mark multiline message with an extent, which `view-lossage'
 	;; will recognize.
-	(save-match-data
-	  (when (string-match "\n" message)
-	    (setq extent (make-extent (point) (point)))
-	    (set-extent-properties extent '(end-open nil message-multiline t)))
-	  )
-	(insert message "\n")
+        (when (find ?\n message :start start :end end)
+          (setq extent (make-extent (point) (point)))
+          (set-extent-properties extent '(end-open nil message-multiline t)))
+	(write-line message (current-buffer) :start start :end end)
 	(when extent
 	  (set-extent-property extent 'end-open t)))
       (when (> (point-max) (max log-message-max-size (point-min)))
@@ -4377,42 +4399,48 @@
     (if no-restore
 	nil			; just preparing to put another msg up
       (if message-stack
-	  (let ((oldmsg (cdr (car message-stack))))
-	    (raw-append-message oldmsg frame stdout-p)
-	    oldmsg)
+          (let ((oldmsg (second (car message-stack))))
+            (prog1
+                ;; #### Doesn't pass back information about the substring of
+                ;; OLDMSG displayed. None of our callers use this, as of
+                ;; 20150311, though.
+                oldmsg
+              (raw-append-message oldmsg frame stdout-p
+                                  :start (third (car message-stack))
+                                  :end (fourth (car message-stack)))))
 	;; #### Should we (redisplay-echo-area) here?  Messes some
 	;; things up.
 	nil))))
 
 (defun remove-message (&optional label frame)
-  ;; If label is nil, we want to remove all matching messages.
-  ;; Must reverse the stack first to log them in the right order.
-  (let ((log nil))
-    (while (and message-stack
-		(or (null label)	; null label means clear whole stack
-		    (eq label (car (car message-stack)))))
-      (push (car message-stack) log)
-      (setq message-stack (cdr message-stack)))
-    (let ((s  message-stack))
-      (while (cdr s)
-	(let ((msg (car (cdr s))))
-	  (if (eq label (car msg))
-	      (progn
-		(push msg log)
-		(setcdr s (cdr (cdr s))))
-	    (setq s (cdr s))))))
+  "Remove any message with a specified LABEL from `message-stack'.
+
+With nil LABEL, remove all messages from `message-stack'. Calls those
+functions specified by `remove-message-hook' with the details of each removed
+message."
+  (let (log)
+    (if label
+        (setq log (reverse (remove* label message-stack :test-not #'eq
+                                    :key #'car))
+              message-stack (delete* label message-stack :key #'car))
+      ;; If label is nil, we want to remove all messages.  Must reverse the
+      ;; stack first to log them in the right order.
+      (setq log (nreverse message-stack)
+            message-stack nil))
     ;; (possibly) log each removed message
     (while log
-      (with-trapping-errors
-	:operation 'remove-message-hook
-	:class 'message-log
-	:error-form (progn
-		      (setq remove-message-hook nil)
-		      (let ((inhibit-read-only t))
-			(erase-buffer " *Echo Area*")))
-	:resignal t
-	(run-hook-with-args 'remove-message-hook
-			    (car (car log)) (cdr (car log))))
+      (call-with-condition-handler
+          ((macro . (lambda (function) (subst '#:xEbgpd2 'error function)))
+           #'(lambda (error)
+               (setq remove-message-hook nil)
+               (let ((inhibit-read-only t))
+                 (erase-buffer " *Echo Area*"))
+               (lwarn 'message-log 'warning
+                 "Error in `remove-message-hook': %s\n\nBacktrace follows:\n%s"
+                 (error-message-string error)
+                 (backtrace-in-condition-handler-eliminating-handler 'error))))
+          #'run-hook-with-args 'remove-message-hook (caar log)
+	  (cadar log) :start (third (car log)) :end (fourth (car log)))
       (setq log (cdr log)))))
 
 (defun* append-message (label message &optional frame stdout-p
@@ -4436,10 +4464,16 @@
   ;; able to append to an existing message.
   (if (eq 'stream (frame-type frame))
       (set-device-clear-left-side (frame-device frame) nil))
-  (let ((top (car message-stack)))
-    (if (eq label (car top))
-	(setcdr top (concat (cdr top) message))
-      (push (cons label message) message-stack)))
+  (if (eq label (caar message-stack))
+      (setf (cadar message-stack)
+            (concat (subseq (cadar message-stack) (third (car message-stack))
+                            (fourth (car message-stack)))
+                    (if (or end (not (eql start 0)))
+                        (subseq message start end)
+                      message))
+            (caddar message-stack) 0
+            (car (cdddar message-stack)) nil)
+    (push (list label message start end) message-stack))
   (raw-append-message message frame stdout-p :start start :end end)
   (if (eq 'stream (frame-type frame))
       (set-device-clear-left-side (frame-device frame) t)))
--- a/man/ChangeLog	Wed Feb 25 11:47:12 2015 +0000
+++ b/man/ChangeLog	Thu Mar 12 16:27:13 2015 +0000
@@ -1,3 +1,9 @@
+2015-02-23  Mike Kupfer  <mike.kupfer@xemacs.org>
+
+	* internals/internals.texi (The Redisplay Mechanism):
+	Add notes about pixel_to_glyph_translation and related code.
+	(pixel_to_glyph_translation): New section.	
+
 2014-03-28  Jerry James  <james@xemacs.org>
 
 	* Makefile.in: Do not build texinfo files.
--- a/man/internals/internals.texi	Wed Feb 25 11:47:12 2015 +0000
+++ b/man/internals/internals.texi	Thu Mar 12 16:27:13 2015 +0000
@@ -625,6 +625,7 @@
 * Critical Redisplay Sections::  
 * Line Start Cache::            
 * Redisplay Piece by Piece::    
+* pixel_to_glyph_translation::  
 * Modules for the Redisplay Mechanism::  
 * Modules for other Display-Related Lisp Objects::  
 
@@ -19049,12 +19050,18 @@
 @chapter The Redisplay Mechanism
 @cindex redisplay mechanism, the
 
-  The redisplay mechanism is one of the most complicated sections of
+  The redisplay mechanism is responsible for updating the display,
+such as after an edit or a highlighting change.  It is one of the most
+complicated sections of
 XEmacs, especially from a conceptual standpoint.  This is doubly so
 because, unlike for the basic aspects of the Lisp interpreter, the
 computer science theories of how to efficiently handle redisplay are not
 well-developed.
 
+  The redisplay code also provides a low-level operation to 
+map window system coordinates to XEmacs objects.  This is used
+elsewhere in XEmacs, most notably for dealing with mouse events.
+
   When working with the redisplay mechanism, remember the Golden Rules
 of Redisplay:
 
@@ -19071,6 +19078,7 @@
 * Critical Redisplay Sections::  
 * Line Start Cache::            
 * Redisplay Piece by Piece::    
+* pixel_to_glyph_translation::  
 * Modules for the Redisplay Mechanism::  
 * Modules for other Display-Related Lisp Objects::  
 @end menu
@@ -19485,7 +19493,7 @@
   In case you're wondering, the Second Golden Rule of Redisplay is not
 applicable.
 
-@node Redisplay Piece by Piece, Modules for the Redisplay Mechanism, Line Start Cache, The Redisplay Mechanism
+@node Redisplay Piece by Piece, pixel_to_glyph_translation, Line Start Cache, The Redisplay Mechanism
 @section Redisplay Piece by Piece
 @cindex redisplay piece by piece
 
@@ -19582,7 +19590,30 @@
 @code{ensure_face_cachel_complete}, with the actual work being done by
 @code{ensure_face_cachel_contains_charset}.
 
-@node Modules for the Redisplay Mechanism, Modules for other Display-Related Lisp Objects, Redisplay Piece by Piece, The Redisplay Mechanism
+@node pixel_to_glyph_translation, Modules for the Redisplay Mechanism, Redisplay Piece by Piece, The Redisplay Mechanism
+@section pixel_to_glyph_translation
+@cindex pixel_to_glyph_translation
+@cindex events, mouse motion
+@cindex mouse motion events
+
+The data structures described in @ref{Redisplay Piece by Piece} are
+also the basis for mapping native window system coordinates to
+higher-level objects, such as a toolbar button, a modeline character,
+a glyph, or a text character.  @code{pixel_to_glyph_translation} does
+the bulk of this translation, with some further tweaking done by the
+functions in @file{events.c}.
+
+@code{pixel_to_glyph_translation} is called very frequently when
+XEmacs is processing mouse-motion events.  To improve performance,
+@code{pixel_to_glyph_translation} caches the most recently returned
+values.  The cache includes the pixel coordinate boundaries for which
+the cached results are valid.  So if the next event is within those
+boundaries, @code{pixel_to_glyph_translation} returns the cached
+results (fast path).  Otherwise, @code{pixel_to_glyph_translation}
+walks through the redisplay data structures, then updates the cache
+with the new results (slow path).
+
+@node Modules for the Redisplay Mechanism, Modules for other Display-Related Lisp Objects, pixel_to_glyph_translation, The Redisplay Mechanism
 @section Modules for the Redisplay Mechanism
 @cindex modules for the redisplay mechanism
 @cindex redisplay mechanism, modules for the
--- a/src/ChangeLog	Wed Feb 25 11:47:12 2015 +0000
+++ b/src/ChangeLog	Thu Mar 12 16:27:13 2015 +0000
@@ -1,3 +1,18 @@
+2015-03-04  Aidan Kehoe  <kehoea@parhasard.net>
+
+	* sequence.c (count_with_tail):
+	Accept COUNT from #'substitute, #'nsubstitute too.
+	* sequence.c (FdeleteX):
+	Only remove COUNT from the arguments if FROM-END is non-nil.
+	* sequence.c (Fnsubstitute):
+	Remove COUNT from the arguments if specified and FROM-END is
+	non-nil.
+	* sequence.c (Fsubstitute):
+	Remove COUNT from the arguments if specified and FROM-END is
+	non-nil. Do this before calling count_with_tail(). When we
+	encounter the cons return by count_with_tail(), use the
+	replacement object.
+
 2015-02-25  Aidan Kehoe  <kehoea@parhasard.net>
 
 	* lread.c (read_atom): Use the new calling convention for
--- a/src/sequence.c	Wed Feb 25 11:47:12 2015 +0000
+++ b/src/sequence.c	Thu Mar 12 16:27:13 2015 +0000
@@ -710,9 +710,6 @@
 
       /* Our callers should have filtered out non-positive COUNT. */
       assert (counting >= 0);
-      /* And we're not prepared to handle COUNT from any other caller at the
-	 moment. */
-      assert (EQ (caller, QremoveX)|| EQ (caller, QdeleteX));
     }
 
   check_test = get_check_test_function (item, &test, test_not, if_, if_not,
@@ -1878,7 +1875,7 @@
 
   PARSE_KEYWORDS (FdeleteX, nargs, args, 9,
 		  (test, if_not, if_, test_not, key, start, end, from_end,
-		   count), (start = Qzero, count = Qunbound));
+		   count), (start = Qzero));
 
   CHECK_SEQUENCE (sequence);
   CHECK_NATNUM (start);
@@ -1890,45 +1887,41 @@
       ending = BIGNUMP (end) ? 1 + MOST_POSITIVE_FIXNUM : XFIXNUM (end);
     }
 
-  if (!UNBOUNDP (count))
-    {
-      if (!NILP (count))
-	{
-	  CHECK_INTEGER (count);
-          if (FIXNUMP (count))
-            {
-              counting = XFIXNUM (count);
-            }
+  if (!NILP (count))
+    {
+      CHECK_INTEGER (count);
+      if (FIXNUMP (count))
+        {
+          counting = XFIXNUM (count);
+        }
 #ifdef HAVE_BIGNUM
-          else
-            {
-              counting = bignum_sign (XBIGNUM_DATA (count)) > 0 ?
-                1 + MOST_POSITIVE_FIXNUM : MOST_NEGATIVE_FIXNUM - 1;
-            }
+      else
+        {
+          counting = bignum_sign (XBIGNUM_DATA (count)) > 0 ?
+            1 + MOST_POSITIVE_FIXNUM : MOST_NEGATIVE_FIXNUM - 1;
+        }
 #endif
-
-	  if (counting < 1)
-	    {
-	      return sequence;
-	    }
-
-          if (!NILP (from_end))
+      if (counting < 1)
+        {
+          return sequence;
+        }
+
+      if (!NILP (from_end))
+        {
+          /* Sigh, this is inelegant. Force count_with_tail () to ignore
+             the count keyword, so we get the actual number of matching
+             elements, and can start removing from the beginning for the
+             from-end case.  */
+          for (ii = XSUBR (GET_DEFUN_LISP_OBJECT (FdeleteX))->min_args;
+               ii < nargs; ii += 2)
             {
-              /* Sigh, this is inelegant. Force count_with_tail () to ignore
-                 the count keyword, so we get the actual number of matching
-                 elements, and can start removing from the beginning for the
-                 from-end case.  */
-              for (ii = XSUBR (GET_DEFUN_LISP_OBJECT (FdeleteX))->min_args;
-                   ii < nargs; ii += 2)
+              if (EQ (args[ii], Q_count))
                 {
-                  if (EQ (args[ii], Q_count))
-                    {
-                      args[ii + 1] = Qnil;
-                      break;
-                    }
+                  args[ii + 1] = Qnil;
+                  break;
                 }
-              ii = 0;
             }
+          ii = 0;
         }
     }
 
@@ -5797,6 +5790,20 @@
 	{
 	  return sequence;
 	}
+
+      if (!NILP (from_end))
+        {
+          for (ii = XSUBR (GET_DEFUN_LISP_OBJECT (Fnsubstitute))->min_args;
+               ii < nargs; ii += 2)
+            {
+              if (EQ (args[ii], Q_count))
+                {
+                  args[ii + 1] = Qnil;
+                  break;
+                }
+            }
+          ii = 0;
+        }
     }
 
   check_test = get_check_test_function (item, &test, test_not, if_, if_not,
@@ -6015,16 +6022,16 @@
 {
   Lisp_Object new_ = args[0], item = args[1], sequence = args[2], tail = Qnil;
   Lisp_Object result = Qnil, result_tail = Qnil;
-  Lisp_Object object, position0, matched_count;
+  Lisp_Object object, position0, matched;
   Elemcount starting = 0, ending = MOST_POSITIVE_FIXNUM, encountered = 0;
-  Elemcount ii = 0, counting = MOST_POSITIVE_FIXNUM, presenting = 0;
+  Elemcount ii = 0, counting = MOST_POSITIVE_FIXNUM, skipping = 0;
   Boolint test_not_unboundp = 1;
   check_test_func_t check_test = NULL;
   struct gcpro gcpro1;
 
   PARSE_KEYWORDS (Fsubstitute, nargs, args, 9,
 		  (test, if_, if_not, test_not, key, start, end, count,
-		   from_end), (start = Qzero, count = Qunbound));
+		   from_end), (start = Qzero));
 
   CHECK_SEQUENCE (sequence);
 
@@ -6040,30 +6047,6 @@
   check_test = get_check_test_function (item, &test, test_not, if_, if_not,
 					key, &test_not_unboundp);
 
-  if (!UNBOUNDP (count))
-    {
-      if (!NILP (count))
-	{
-          CHECK_INTEGER (count);
-          if (FIXNUMP (count))
-            {
-              counting = XFIXNUM (count);
-            }
-#ifdef HAVE_BIGNUM
-          else
-            {
-              counting = bignum_sign (XBIGNUM_DATA (count)) > 0 ?
-                1 + MOST_POSITIVE_FIXNUM : -1 + MOST_NEGATIVE_FIXNUM;
-            }
-#endif
-
-          if (counting <= 0)
-            {
-              return sequence;
-            }
-	}
-    }
-
   if (!CONSP (sequence))
     {
       position0 = position (&object, item, sequence, check_test,
@@ -6081,17 +6064,62 @@
 	}
     }
 
-  matched_count = count_with_tail (&tail, nargs - 1, args + 1, Qsubstitute);
-
-  if (ZEROP (matched_count))
+  if (!NILP (count))
+    {
+      CHECK_INTEGER (count);
+      if (FIXNUMP (count))
+        {
+          counting = XFIXNUM (count);
+        }
+#ifdef HAVE_BIGNUM
+      else
+        {
+          counting = bignum_sign (XBIGNUM_DATA (count)) > 0 ?
+            1 + MOST_POSITIVE_FIXNUM : -1 + MOST_NEGATIVE_FIXNUM;
+        }
+#endif
+
+      if (counting <= 0)
+        {
+          return sequence;
+        }
+
+      /* Sigh, this is inelegant. Force count_with_tail () to ignore the count
+         keyword, so we get the actual number of matching elements, and can
+         start removing from the beginning for the from-end case.  */
+      if (!NILP (from_end))
+        {
+          for (ii = XSUBR (GET_DEFUN_LISP_OBJECT (Fsubstitute))->min_args;
+               ii < nargs; ii += 2)
+            {
+              if (EQ (args[ii], Q_count))
+                {
+                  args[ii + 1] = Qnil;
+                  break;
+                }
+            }
+          ii = 0;
+        }
+    }
+
+  matched = count_with_tail (&tail, nargs - 1, args + 1, Qsubstitute);
+
+  if (ZEROP (matched))
     {
       return sequence;
     }
 
   if (!NILP (count) && !NILP (from_end))
     {
-      presenting = XFIXNUM (matched_count);
-      presenting = presenting <= counting ? 0 : presenting - counting;
+      Elemcount matching = XFIXNUM (matched);
+      if (matching > counting)
+        {
+          /* skipping is the number of elements to be skipped before we start
+             substituting. It is for those cases where both :count and
+             :from-end are specified, and the number of elements present is
+             greater than that limit specified with :count. */
+          skipping = matching - counting;
+        }
     }
 
   GCPRO1 (result);
@@ -6100,20 +6128,32 @@
       {
         if (EQ (tail, tailing))
           {
+            /* No need to do check_test, we're sure that this element matches
+               because its cons is what count_with_tail returned as the
+               tail. */
+            if (skipping ? encountered >= skipping : encountered < counting)
+              {
+                if (NILP (result))
+                  {
+                    result = Fcons (new_, XCDR (tail));
+                  }
+                else
+                  {
+                    XSETCDR (result_tail, Fcons (new_, XCDR (tail)));
+                  }
+              }
+            else
+              {
+                XSETCDR (result_tail, tail);
+              }
+
 	    XUNGCPRO (elt);
 	    UNGCPRO;
-
-            if (NILP (result))
-              {
-                return XCDR (tail);
-              }
-	  
-            XSETCDR (result_tail, XCDR (tail));
-	    return result;
+            return result;
           }
         else if (starting <= ii && ii < ending &&
                  (check_test (test, key, item, elt) == test_not_unboundp)
-                 && (presenting ? encountered++ >= presenting
+                 && (skipping ? encountered++ >= skipping
                      : encountered++ < counting))
           {
             if (NILP (result))
--- a/tests/ChangeLog	Wed Feb 25 11:47:12 2015 +0000
+++ b/tests/ChangeLog	Thu Mar 12 16:27:13 2015 +0000
@@ -1,3 +1,8 @@
+2015-03-04  Aidan Kehoe  <kehoea@parhasard.net>
+
+	* automated/lisp-tests.el:
+	Add some tests for #'substitute.
+
 2015-02-25  Aidan Kehoe  <kehoea@parhasard.net>
 
 	* automated/lisp-tests.el:
--- a/tests/automated/lisp-tests.el	Wed Feb 25 11:47:12 2015 +0000
+++ b/tests/automated/lisp-tests.el	Thu Mar 12 16:27:13 2015 +0000
@@ -2988,6 +2988,97 @@
   (Check-Error wrong-number-of-arguments
                (funcall list-and-four 7 8 9 10)))
 
+;; Test #'substitute. Paul Dietz has much more comprehensive tests.
+
+(Assert (equal (substitute 'a 'b '(a b c d e f g)) '(a a c d e f g)))
+(Assert (equal (substitute 'a 'b '(a b c d e b f g) :from-end t :count 1)
+               '(a b c d e a f g)))
+
+(Assert (equal (let* ((nomodif '(a b c a b d a c b a e))
+                      (x (copy-list nomodif))
+                      (y (substitute 'z 'a x)))
+                 (and (equal nomodif x) y))
+               '(z b c z b d z c b z e)))
+(Assert (equal (let* ((nomodif '(a b c a b d a c b a e))
+                      (x (copy-list nomodif))
+                      (y (substitute 'z 'a x :count nil)))
+                 (and (equal nomodif x) y))
+               '(z b c z b d z c b z e)))
+(Assert (equal (let* ((nomodif '(a b c a b d a c b a e))
+                      (x (copy-list nomodif))
+                      (y (substitute 'z 'a x :key nil)))
+                 (and (equal nomodif x) y))
+               '(z b c z b d z c b z e)))
+(Assert (equal (let* ((nomodif '(a b c a b d a c b a e))
+                      (x (copy-list nomodif))
+                      (y (substitute 'z 'a x :count 100)))
+                 (and (equal nomodif x) y))
+               '(z b c z b d z c b z e)))
+(Assert (equal (let* ((nomodif '(a b c a b d a c b a e))
+                      (x (copy-list nomodif))
+                      (y (substitute 'z 'a x :count 0)))
+                 (and (equal nomodif x) y))
+               '(a b c a b d a c b a e)))
+(Assert (equal (let* ((nomodif '(a b c a b d a c b a e))
+                      (x (copy-list nomodif))
+                      (y (substitute 'z 'a x :count 1)))
+                 (and (equal nomodif x) y))
+               '(z b c a b d a c b a e)))
+(Assert (equal (let* ((nomodif '(a b c a b d a c b a e))
+                      (x (copy-list nomodif))
+                      (y (substitute 'z 'c x :count 1)))
+                 (and (equal nomodif x) y))
+               '(a b z a b d a c b a e)))
+(Assert (equal (let* ((nomodif '(a b c a b d a c b a e))
+                      (x (copy-list nomodif))
+                      (y (substitute 'z 'a x :from-end t)))
+                 (and (equal nomodif x) y))
+               '(z b c z b d z c b z e)))
+(Assert (equal (let* ((nomodif '(a b c a b d a c b a e))
+                      (x (copy-list nomodif))
+                      (y (substitute 'z 'a x :from-end t :count 1)))
+                 (and (equal nomodif x) y))
+               '(a b c a b d a c b z e)))
+(Assert (equal (let* ((nomodif '(a b c a b d a c b a e))
+                      (x (copy-list nomodif))
+                      (y (substitute 'z 'a x :from-end t :count 4)))
+                 (and (equal nomodif x) y))
+               '(z b c z b d z c b z e)))
+(Assert (equal (multiple-value-list
+                   (let* ((nomodif '(a b c a b d a c b a e))
+                          (x (copy-list nomodif)))
+                     (values
+                      (loop for i from 0 to 10
+                            collect (substitute 'z 'a x :start i))
+                      (equal nomodif x))))
+               '(((z b c z b d z c b z e) (a b c z b d z c b z e)
+                  (a b c z b d z c b z e) (a b c z b d z c b z e)
+                  (a b c a b d z c b z e) (a b c a b d z c b z e)
+                  (a b c a b d z c b z e) (a b c a b d a c b z e)
+                  (a b c a b d a c b z e) (a b c a b d a c b z e)
+                  (a b c a b d a c b a e))
+                 t)))
+(Assert (equal (multiple-value-list
+                   (let* ((nomodif '(a b c a b d a c b a e))
+                          (x (copy-list nomodif)))
+                     (values
+                      (loop for i from 0 to 10
+                            collect (substitute 'z 'a x :start i :end nil))
+                      (equal nomodif x))))
+               '(((z b c z b d z c b z e) (a b c z b d z c b z e)
+                  (a b c z b d z c b z e) (a b c z b d z c b z e)
+                  (a b c a b d z c b z e) (a b c a b d z c b z e)
+                  (a b c a b d z c b z e) (a b c a b d a c b z e)
+                  (a b c a b d a c b z e) (a b c a b d a c b z e)
+                  (a b c a b d a c b a e))
+                 t)))
+(Assert (equal
+         (let* ((nomodif '(1 2 3 2 6 1 2 4 1 3 2 7))
+                (x (copy-list nomodif))
+                (y (substitute 300 1 x :key #'1-)))
+           (and (equal nomodif x) y))
+         '(1 300 3 300 6 1 300 4 1 3 300 7)))
+
 ;; Test labels and inlining.
 (labels
     ((+ (&rest arguments)