changeset 5772:cd4f5f1f1f4c

Add #'write-sequence, on the model of #'write-char, API from Common Lisp. src/ChangeLog addition: 2013-12-17 Aidan Kehoe <kehoea@parhasard.net> * lisp.h: * lisp.h (PARSE_KEYWORDS_8): Correct this in cases where we can have noticeably fewer arguments than KEYWORDS_OFFSET, check whether nargs > pk_offset. Declare check_sequence_range in this header. * print.c: * print.c (Fwrite_sequence) New: Write a sequence to a stream, in the same way #'write-char and #'terpri do. API from Common Lisp, not GNU, so while there is some char-int confoundance, it's more limited than usual with GNU APIs. * print.c (syms_of_print): Make it available. * sequence.c (check_sequence_range): Export this to other files. lisp/ChangeLog addition: 2013-12-17 Aidan Kehoe <kehoea@parhasard.net> * cl-extra.el: * cl-extra.el (write-string): New. * cl-extra.el (write-line): New. Add these here, implemented in terms of #'write-sequence in print.c. tests/ChangeLog addition: 2013-12-17 Aidan Kehoe <kehoea@parhasard.net> * automated/lisp-tests.el: Up max-lisp-eval-depth when compiling this file, some of what we're doing in testing #'write-sequence is demanding. * automated/lisp-tests.el (make-circular-list): New argument VALUE, the car of the conses to create. * automated/lisp-tests.el: Test #'write-sequence, #'write-string, #'write-line with function, buffer and marker STREAMs; test argument types, keyword argument ranges and values.
author Aidan Kehoe <kehoea@parhasard.net>
date Tue, 17 Dec 2013 19:29:10 +0200
parents 72a9467f93fc
children 94a6b8fbd56e
files lisp/ChangeLog lisp/cl-extra.el src/ChangeLog src/lisp.h src/print.c src/sequence.c tests/ChangeLog tests/automated/lisp-tests.el
diffstat 8 files changed, 588 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- a/lisp/ChangeLog	Sun Dec 15 17:01:58 2013 +0000
+++ b/lisp/ChangeLog	Tue Dec 17 19:29:10 2013 +0200
@@ -1,3 +1,10 @@
+2013-12-17  Aidan Kehoe  <kehoea@parhasard.net>
+
+	* cl-extra.el:
+	* cl-extra.el (write-string): New.
+	* cl-extra.el (write-line): New.
+	Add these here, implemented in terms of #'write-sequence in print.c.
+
 2013-09-15  Mats Lidell  <matsl@cxemacs.org>
 
 	* files.el (mode-require-final-newline): Variable synced from
--- a/lisp/cl-extra.el	Sun Dec 15 17:01:58 2013 +0000
+++ b/lisp/cl-extra.el	Tue Dec 17 19:29:10 2013 +0200
@@ -618,6 +618,38 @@
 ;; files to do the same, multiple times.
 (eval-when-compile (or (cl-compiling-file) (load "cl-macs")))
 
+;; XEmacs, functions from Common Lisp.
+(defun* write-string (string &optional output-stream &key (start 0) end)
+  "Output STRING to stream OUTPUT-STREAM.
+
+OUTPUT-STREAM defaults to the value of `standard-output', which see.
+
+Keywords :start and :end, if given, specify indices of a subsequence
+of STRING to output.  They default to 0 and nil, meaning write the
+entire string.
+
+Returns STRING (not the subsequence of STRING that has been written to
+OUTPUT-STREAM)."
+  (check-type string string)
+  (write-sequence string output-stream :start start :end end))
+
+(defun* write-line (string &optional output-stream &key (start 0) end)
+  "Output STRING, followed by a newline, to OUTPUT-STREAM.
+
+STRING must be a string.  OUTPUT-STREAM defaults to the value of
+`standard-output' (which see).
+
+Keywords :start and :end, if given, specify indices of a subsequence
+of STRING to output.  They default to 0 and nil, meaning write the
+entire string.
+
+Returns STRING (note, not the subsequence of STRING that has been written to
+OUTPUT-STREAM)."
+  (check-type string string)
+  (prog1
+      (write-sequence string output-stream :start start :end end)
+    (terpri output-stream)))
+
 ;; Implementation limits.
 
 ;; XEmacs; call cl-float-limits at dump time.
--- a/src/ChangeLog	Sun Dec 15 17:01:58 2013 +0000
+++ b/src/ChangeLog	Tue Dec 17 19:29:10 2013 +0200
@@ -1,3 +1,20 @@
+2013-12-17  Aidan Kehoe  <kehoea@parhasard.net>
+
+	* lisp.h:
+	* lisp.h (PARSE_KEYWORDS_8):
+	Correct this in cases where we can have noticeably fewer arguments
+	than KEYWORDS_OFFSET, check whether nargs > pk_offset.
+	Declare check_sequence_range in this header.
+	* print.c:
+	* print.c (Fwrite_sequence) New:
+	Write a sequence to a stream, in the same way #'write-char and
+	#'terpri do. API from Common Lisp, not GNU, so while there is some
+	char-int confoundance, it's more limited than usual with GNU APIs.
+	* print.c (syms_of_print):
+	Make it available.
+	* sequence.c (check_sequence_range):
+	Export this to other files.
+
 2013-12-15  Aidan Kehoe  <kehoea@parhasard.net>
 
 	* number.h:
--- a/src/lisp.h	Sun Dec 15 17:01:58 2013 +0000
+++ b/src/lisp.h	Tue Dec 17 19:29:10 2013 +0200
@@ -3633,7 +3633,7 @@
       Elemcount pk_i = nargs - 1, pk_offset = keywords_offset;		\
       Boolint pk_allow_other_keys = allow_other_keys;                   \
                                                                         \
-      if ((nargs - pk_offset) & 1)					\
+      if ((nargs - pk_offset) & 1 && (nargs > pk_offset))               \
         {                                                               \
           if (!allow_other_keys                                         \
               && !(pk_allow_other_keys                                  \
@@ -5307,6 +5307,9 @@
 EXFUN (Fsubseq, 3);
 EXFUN (Fvalid_plist_p, 1);
 
+extern void check_sequence_range (Lisp_Object, Lisp_Object, Lisp_Object,
+                                  Lisp_Object);
+
 extern Boolint check_eq_nokey (Lisp_Object, Lisp_Object, Lisp_Object,
                                Lisp_Object);
 extern Boolint check_lss_key_car (Lisp_Object, Lisp_Object, Lisp_Object,
--- a/src/print.c	Sun Dec 15 17:01:58 2013 +0000
+++ b/src/print.c	Tue Dec 17 19:29:10 2013 +0200
@@ -143,6 +143,8 @@
 Lisp_Object Qdisplay_error;
 Lisp_Object Qprint_message_label;
 
+Lisp_Object Qwrite_sequence;
+
 /* Force immediate output of all printed data.  Used for debugging. */
 int print_unbuffered;
 
@@ -838,6 +840,180 @@
   return character;
 }
 
+DEFUN ("write-sequence", Fwrite_sequence, 1, MANY, 0, /*
+Output string, list, vector or bit-vector SEQUENCE to STREAM.
+
+STREAM defaults to the value of `standard-output', which see.
+
+Keywords :start and :end, if given, specify indices of a subsequence
+of SEQUENCE to output.  They default to 0 and nil, meaning write the
+entire sequence.
+
+Elements of SEQUENCE can be characters (all are accepted by this function,
+though they may be corrupted depending on the coding system associated with
+STREAM) or integers below #x100, which are treated as equivalent to the
+characters with the corresponding code. This function is from Common Lisp,
+rather GNU Emacs API, so GNU Emacs' character-integer equivalence doesn't
+hold.
+
+Returns SEQUENCE (not the subsequence of SEQUENCE that has been written to
+STREAM).
+
+arguments: (SEQUENCE &optional STREAM &key (START 0) END)
+*/
+       (int nargs, Lisp_Object *args))
+{
+  Lisp_Object sequence = args[0], stream = (nargs > 1) ? args[1] : Qnil;
+  Lisp_Object reloc = Qnil;
+  Charcount starting = 0, ending = 1 + MOST_POSITIVE_FIXNUM;
+  Ibyte *nonreloc = NULL, *all = NULL, *allptr = all; 
+  Bytecount bstart = 0, blen = 0;
+  Elemcount ii = 0;
+
+  PARSE_KEYWORDS_8 (Qwrite_sequence, nargs, args, 2, (start, end), 
+                    (start = Qzero), 2, 0);
+
+  CHECK_SEQUENCE (sequence);
+  CHECK_NATNUM (start);
+
+  if (!NILP (end))
+    {
+      CHECK_NATNUM (end);
+    }
+
+  stream = canonicalize_printcharfun (stream);
+
+  if (BIGNUMP (start) || (BIGNUMP (end)))
+    {
+      /* None of the sequences will have bignum lengths. */
+      check_sequence_range (sequence, start, end, Flength (sequence));
+
+      RETURN_NOT_REACHED (sequence);
+    }
+
+  starting = XFIXNUM (start);
+  if (FIXNUMP (end))
+    {
+      ending = XFIXNUM (end);
+    }
+
+  if (STRINGP (sequence))
+    {
+      Ibyte *stringp = XSTRING_DATA (sequence);
+      Ibyte *strend = stringp + XSTRING_LENGTH (sequence);
+
+      reloc = sequence;
+
+      for (ii = 0; ii < starting && stringp < strend; ++ii)
+        {
+          INC_IBYTEPTR (stringp);
+        }
+
+      if (ii != starting)
+        {
+          /* Bad value for start. */
+          check_sequence_range (sequence, start, end,
+                                Flength (sequence));
+          RETURN_NOT_REACHED (sequence);
+        }
+
+      bstart = stringp - XSTRING_DATA (sequence);
+
+      for (; ii < ending && stringp < strend; ++ii)
+        {
+          INC_IBYTEPTR (stringp);
+        }
+
+      if (ii != ending && ending != (1 + MOST_POSITIVE_FIXNUM))
+        {
+          /* Bad value for end. */
+          check_sequence_range (sequence, start, end,
+                                Flength (sequence));
+          RETURN_NOT_REACHED (sequence);
+        }
+
+      blen = stringp - (XSTRING_DATA (sequence) + bstart);
+    }
+  else
+    {
+      Lisp_Object length = Flength (sequence);
+
+      check_sequence_range (sequence, start, end, length);
+      ending = NILP (end) ? XFIXNUM (length) : XFIXNUM (end);
+
+      if (VECTORP (sequence))
+        {
+          Lisp_Object *vdata = XVECTOR_DATA (sequence);
+          /* Worst case scenario; all characters, all the longest possible. More
+             likely: lots of small integers. */
+          nonreloc = allptr
+            = alloca_ibytes (((ending - starting)) * MAX_ICHAR_LEN);
+
+          for (ii = starting; ii < ending; ++ii)
+            {
+              if (!CHARP (vdata[ii]))
+                {
+                  check_integer_range (vdata[ii], Qzero, make_fixnum (0xff));
+                }
+
+              allptr += set_itext_ichar (allptr,
+                                         XCHAR_OR_CHAR_INT (vdata[ii]));
+            }
+        }
+      else if (CONSP (sequence))
+        {
+          /* Worst case scenario; all characters, all the longest
+             possible. More likely: lots of small integers. */
+          nonreloc = allptr
+            = alloca_ibytes (((ending - starting)) * MAX_ICHAR_LEN);
+          ii = 0;
+          {
+            EXTERNAL_LIST_LOOP_2 (elt, sequence)
+              {
+                if (ii >= starting)
+                  {
+                    if (ii >= ending)
+                      {
+                        break;
+                      }
+
+                    if (!CHARP (elt))
+                      {
+                        check_integer_range (elt, Qzero, make_fixnum (0xff));
+                      }
+                    allptr += set_itext_ichar (allptr,
+                                               XCHAR_OR_CHAR_INT (elt));
+                  }
+                ++ii;
+              }
+          }
+        }
+      else if (BIT_VECTORP (sequence))
+        {
+          Ibyte one [MAX_ICHAR_LEN];
+          Lisp_Bit_Vector *vv = XBIT_VECTOR (sequence);
+
+          nonreloc = allptr
+            = alloca_ibytes (((ending - starting) *
+                              (set_itext_ichar (one, (Ichar)1))));
+          for (ii = starting; ii < ending; ++ii)
+            {
+              allptr += set_itext_ichar (allptr, bit_vector_bit (vv, ii));
+            }
+        }
+      else if (NILP (sequence))
+        {
+          nonreloc = allptr = alloca_ibytes (1);
+        }
+
+      bstart = 0;
+      blen = allptr - nonreloc;
+    }
+
+  output_string (stream, nonreloc, reloc, bstart, blen);
+  return sequence;
+}
+
 void
 temp_output_buffer_setup (Lisp_Object bufname)
 {
@@ -2977,6 +3153,7 @@
 
   DEFSYMBOL (Qdisplay_error);
   DEFSYMBOL (Qprint_message_label);
+  DEFSYMBOL (Qwrite_sequence);
 
   DEFSUBR (Fprin1);
   DEFSUBR (Fprin1_to_string);
@@ -2986,6 +3163,7 @@
   DEFSUBR (Fdisplay_error);
   DEFSUBR (Fterpri);
   DEFSUBR (Fwrite_char);
+  DEFSUBR (Fwrite_sequence);
   DEFSUBR (Falternate_debugging_output);
   DEFSUBR (Fset_device_clear_left_side);
   DEFSUBR (Fdevice_left_side_clear_p);
--- a/src/sequence.c	Sun Dec 15 17:01:58 2013 +0000
+++ b/src/sequence.c	Tue Dec 17 19:29:10 2013 +0200
@@ -44,7 +44,7 @@
   invalid_state_2 ("object modified while traversing it", func, object);
 }
 
-static void
+void
 check_sequence_range (Lisp_Object sequence, Lisp_Object start,
 		      Lisp_Object end, Lisp_Object length)
 {
--- a/tests/ChangeLog	Sun Dec 15 17:01:58 2013 +0000
+++ b/tests/ChangeLog	Tue Dec 17 19:29:10 2013 +0200
@@ -1,3 +1,15 @@
+2013-12-17  Aidan Kehoe  <kehoea@parhasard.net>
+
+	* automated/lisp-tests.el:
+	Up max-lisp-eval-depth when compiling this file, some of what
+	we're doing in testing #'write-sequence is demanding.
+	* automated/lisp-tests.el (make-circular-list):
+	New argument VALUE, the car of the conses to create.
+	* automated/lisp-tests.el:
+	Test #'write-sequence, #'write-string, #'write-line with function,
+	buffer and marker STREAMs; test argument types, keyword argument
+	ranges and values.
+
 2013-12-15  Aidan Kehoe  <kehoea@parhasard.net>
 
 	* automated/lisp-tests.el (character):
--- a/tests/automated/lisp-tests.el	Sun Dec 15 17:01:58 2013 +0000
+++ b/tests/automated/lisp-tests.el	Tue Dec 17 19:29:10 2013 +0200
@@ -29,6 +29,9 @@
 ;;; See test-harness.el for instructions on how to run these tests.
 
 (eval-when-compile
+  ;; The labels below give trouble with a max-lisp-eval-depth of less than
+  ;; about 2000, work around that:
+  (setq max-lisp-eval-depth (max 2000 max-lisp-eval-depth))
   (condition-case nil
       (require 'test-harness)
     (file-error
@@ -102,12 +105,16 @@
   (Assert (eq (elt my-bit-vector 2) 0))
   )
 
-(defun make-circular-list (length)
-  "Create evil emacs-crashing circular list of length LENGTH"
+(defun make-circular-list (length &optional value)
+  "Create evil emacs-crashing circular list of length LENGTH.
+
+Optional VALUE is the value to go into the cars. If nil, some non-nil value
+will be used to make debugging easier."
   (let ((circular-list
 	 (make-list
 	  length
-	  'you-are-trapped-in-a-twisty-maze-of-cons-cells-all-alike)))
+          (or value
+              'you-are-trapped-in-a-twisty-maze-of-cons-cells-all-alike))))
     (setcdr (last circular-list) circular-list)
     circular-list))
 
@@ -2867,8 +2874,10 @@
 			#'(lambda (object) (if (fixnump object) 1 0)) list))
        (string (map 'string 
 		    #'(lambda (object) (or (and (fixnump object)
-						(int-char object))
-					   (decode-char 'ucs #x20ac))) list))
+                                                (int-char object))
+					   (decode-char 'ucs #x20ac)
+                                           ?\x20))
+                    list))
        (gensym (gensym)))
   (Assert (null (find 'not-in-it list)))
   (Assert (null (find 'not-in-it vector)))
@@ -3119,4 +3128,327 @@
       (map nil #'set-marker markers fixnums)
       (Assert-arith-equivalences markers "with Euro sign restored"))))
 
+;;-----------------------------------------------------
+;; Test #'write-sequence and friends.
+;;-----------------------------------------------------
+
+(macrolet
+    ((Assert-write-results (function context &key short-string long-string
+                                 sequences-too output-stream
+                                 clear-output get-last-output)
+       "Check correct output in CONTEXT for `write-sequence' and friends."
+       (let* ((short-bit-vector (map 'bit-vector #'logand short-string
+                                     (make-circular-list 1 1)))
+              (long-bit-vector (map 'bit-vector #'logand long-string
+                                    (make-circular-list 1 1)))
+              (short-bit-vector-string
+               (map #'string #'int-char short-bit-vector))
+              (long-bit-vector-string
+               (map #'string #'int-char long-bit-vector)))
+       `(progn
+          (,clear-output ,output-stream)
+          (,function ,short-string ,output-stream)
+          (Assert (equal ,short-string
+			 (,get-last-output ,output-stream
+					   ,(length short-string)))
+                  ,(format "checking %s with short string, %s"
+                           function context))
+          ,@(when sequences-too
+              `((,clear-output ,output-stream)
+                (,function ,(vconcat short-string) ,output-stream)
+                (Assert (equal ,short-string
+			       (,get-last-output ,output-stream
+						 ,(length short-string)))
+                        ,(format "checking %s with short vector, %s"
+                                 function context))
+                (,clear-output ,output-stream)
+                (,function ',(append short-string nil) ,output-stream)
+                (Assert (equal ,short-string
+			       (,get-last-output ,output-stream
+						 ,(length short-string)))
+                        ,(format "checking %s with short list, %s"
+                                 function context))
+                (,clear-output ,output-stream)
+                (,function ,short-bit-vector ,output-stream)
+                (Assert (equal ,short-bit-vector-string
+			       (,get-last-output
+				,output-stream
+				,(length short-bit-vector-string)))
+                        ,(format
+                          "checking %s with short bit-vector, %s"
+                          function context))
+                (,clear-output ,output-stream)
+                (,function ,long-bit-vector ,output-stream)
+                (Assert (equal ,long-bit-vector-string
+			       (,get-last-output
+				,output-stream
+				,(length long-bit-vector-string)))
+                        ,(format
+                          "checking %s with long bit-vector, %s"
+                          function context))))
+          ,(cons
+            'progn
+            (loop
+              for (subseq-start subseq-end description)
+              in `((0 ,(length short-string) "trivial range")
+                   (4 7 "harder range"))
+              nconc
+              `((,clear-output ,output-stream)                  
+                (,function ,short-string ,output-stream :start ,subseq-start
+                           :end ,subseq-end)
+                (Assert
+                  (equal ,(subseq short-string subseq-start subseq-end)
+			 (,get-last-output ,output-stream
+                                           ,(- subseq-end subseq-start)))
+                  ,(format
+                    "checking %s with short string, %s, %s"
+                    function context description))
+                 ,@(when sequences-too
+                     `((,clear-output ,output-stream)
+                       (,function ,(vconcat short-string) ,output-stream
+                                  :start ,subseq-start :end ,subseq-end)
+                       (Assert
+                        (equal ,(subseq short-string subseq-start subseq-end)
+			       (,get-last-output ,output-stream
+                                                 ,(- subseq-end subseq-start)))
+                        ,(format
+                          "checking %s with short vector, %s, %s"
+                          function context description))
+                       (,clear-output ,output-stream)
+                       (,function ',(append short-string nil) ,output-stream
+                                  :start ,subseq-start :end ,subseq-end)
+                       (Assert
+                        (equal ,(subseq short-string subseq-start subseq-end)
+			       (,get-last-output
+				,output-stream
+				,(- subseq-end subseq-start )))
+                        ,(format "checking %s with short list, %s, %s"
+                                 function context description))
+                       (,clear-output ,output-stream)
+                       (,function ,short-bit-vector ,output-stream
+                                  :start ,subseq-start :end ,subseq-end)
+                       (Assert
+                        (equal ,(subseq short-bit-vector-string subseq-start
+                                        subseq-end)
+			       (,get-last-output ,output-stream
+                                                 ,(- subseq-end subseq-start)))
+                        ,(format
+                          "checking %s with short bit-vector, %s, %s"
+                          function context description)))))))
+          ,(cons
+            'progn
+            (loop
+              for (subseq-start subseq-end description)
+              in `((0 ,(length long-string) "trivial range")
+                   (4 90 "harder range"))
+              nconc
+              `((,clear-output ,output-stream)                  
+                (,function ,long-string ,output-stream :start ,subseq-start
+                           :end ,subseq-end)
+                (Assert
+		 (equal ,(subseq long-string subseq-start subseq-end)
+			(,get-last-output ,output-stream
+                                          ,(- subseq-end subseq-start)))
+                  ,(format
+                    "checking %s with long string, %s, %s"
+                    function context description))
+                 ,@(when sequences-too
+                     `((,clear-output ,output-stream)
+                       (,function ,(vconcat long-string) ,output-stream
+                                  :start ,subseq-start :end ,subseq-end)
+                       (Assert
+                        (equal ,(subseq long-string subseq-start subseq-end)
+			       (,get-last-output
+				,output-stream
+                                ,(- subseq-end subseq-start)))
+                        ,(format
+                          "checking %s with long vector, %s, %s"
+                          function context description))
+                       (,clear-output ,output-stream)
+                       (,function ',(append long-string nil) ,output-stream
+                                  :start ,subseq-start :end ,subseq-end)
+                       (Assert
+                        (equal ,(subseq long-string subseq-start subseq-end)
+			       (,get-last-output ,output-stream
+                                                 ,(- subseq-end subseq-start)))
+                        ,(format "checking %s with long list, %s, %s"
+                                 function context description))
+                       (,clear-output ,output-stream)
+                       (,function ,long-bit-vector ,output-stream
+                                  :start ,subseq-start :end ,subseq-end)
+                       (Assert
+                        (equal ,(subseq long-bit-vector-string
+					subseq-start subseq-end)
+			       (,get-last-output
+				,output-stream
+                                ,(- subseq-end subseq-start)))
+                        ,(format
+                          "checking %s with long bit-vector, %s, %s"
+                          function context description)))))))
+          (,clear-output ,output-stream))))
+     (test-write-string (function &key sequences-too worry-about-newline)
+       (let* ((short-string "hello there")
+              (long-string
+               (decode-coding-string
+                (concat
+                 "\xd8\xb3\xd9\x84\xd8\xa7\xd9\x85 \xd8\xb9\xd9\x84"
+                 "\xdb\x8c\xda\xa9\xd9\x85\x2c \xd8\xa7\xd8\xb3\xd9"
+                 "\x85 \xd9\x85\xd9\x86 \xd8\xa7\xdb\x8c\xd8\xaf\xd9"
+                 "\x86 \xda\xa9\xdb\x8c\xd9\x88 \xd8\xa7\xd8\xb3\xd8"
+                 "\xaa\x2e \xd9\x85\xd9\x86 \xd8\xa7\xdb\x8c\xd8\xb1"
+                 "\xd9\x84\xd9\x86\xd8\xaf\xdb\x8c \xd8\xa7\xd9\x85"
+                 "\x2c \xd9\x88 \xd9\x85\xd9\x86 \xd8\xaf\xd8\xb1 "
+                 "\xd8\xa8\xdb\x8c\xd9\x85\xd8\xa7\xd8\xb1\xd8\xb3"
+                 "\xd8\xaa\xd8\xa7\xd9\x86 \xda\xa9\xd8\xa7\xd8\xb1"
+                 "\xd9\x85\xdb\x8c\xe2\x80\x8c\xda\xa9\xd9\x86\xd9"
+                 "\x85\x2e")
+                (if (featurep 'mule) 'utf-8 'raw-text-unix)))
+              (long-string (concat long-string long-string long-string
+                                   long-string long-string long-string
+                                   long-string long-string long-string
+                                   long-string long-string long-string)))
+         `(with-temp-buffer
+            (let* ((long-string ,long-string)
+                   (stashed-data
+                    (get-buffer-create
+                     (generate-new-buffer-name " *stash*")))
+                   (function-output-stream
+                    (apply-partially
+                     #'(lambda (buffer character)
+                         (insert-char character 1 nil buffer))
+                     stashed-data))
+                   (marker-buffer
+                    (get-buffer-create
+                     (generate-new-buffer-name " *for-marker*")))
+                   (marker-base-position 40)
+                   (marker
+                    (progn
+                      (insert-char ?\xff 90 nil marker-buffer)
+                      (set-marker (make-marker) 40 marker-buffer))))
+              (unwind-protect
+                   (labels
+		       ((clear-buffer (buffer)
+			  (delete-region (point-min buffer) (point-max buffer)
+					 buffer))
+			(clear-stashed-data (ignore)
+			  (delete-region (point-min stashed-data)
+					 (point-max stashed-data)
+					 stashed-data))
+			(clear-marker-data (marker)
+			  (delete-region marker-base-position marker
+					 (marker-buffer marker)))
+			(buffer-output (buffer length)
+			  (and (> (point buffer) length)
+			       (buffer-substring (- (point buffer) length)
+						 (point buffer) buffer)))
+			(stashed-data-output (ignore length)
+			  (and (> (point stashed-data) length)
+			       (buffer-substring (- (point stashed-data)
+						    length)
+						 (point stashed-data)
+						 stashed-data)))
+			(marker-data (marker length)
+			  (and (> marker length)
+			       (buffer-substring (- marker length) marker
+						 (marker-buffer marker))))
+			(buffer-output-sans-newline (buffer length)
+			  (and (> (point buffer) (+ length 1))
+			       (buffer-substring (- (point buffer) length 1)
+						 (1- (point buffer)))))
+			(stashed-data-output-sans-newline (ignore length)
+			  (and (> (point stashed-data) (+ length 1))
+			       (buffer-substring (- (point stashed-data)
+						    length 1)
+						 (1- (point stashed-data))
+						 stashed-data)))
+			(marker-data-sans-newline (marker length)
+			  (and (> marker (+ length 1))
+			       (buffer-substring (- marker length 1)
+						 (1- marker)
+						 (marker-buffer marker)))))
+                     (Check-Error wrong-number-of-arguments (,function))
+		     (,(if (subrp (symbol-function function))
+			   'progn
+			 'Implementation-Incomplete-Expect-Failure)
+		      (Check-Error wrong-number-of-arguments
+				   (,function ,short-string
+					      (current-buffer) :start))
+		      (Check-Error wrong-number-of-arguments
+				   (,function ,short-string
+					      (current-buffer) :start 0
+					      :end nil :start)))
+                     (Check-Error invalid-keyword-argument
+                                  (,function ,short-string
+                                             (current-buffer)
+                                             :test #'eq))
+                     (Check-Error wrong-type-argument (,function pi))
+                     ,@(if sequences-too
+                           `((Check-Error
+                              args-out-of-range
+                              (,function (vector most-positive-fixnum)))
+                             (Check-Error
+                              args-out-of-range
+                              (,function (list most-positive-fixnum)))
+                             ,@(if (featurep 'mule)
+                                   `((Check-Error
+                                      args-out-of-range
+                                      (,function
+                                       (vector
+                                        (char-int
+                                         (decode-char 'ucs #x20ac))))))))
+                           `((Check-Error wrong-type-argument
+                                          (,function
+                                           ',(append short-string nil)))
+                             (Check-Error wrong-type-argument
+                                          (,function
+                                           ,(vconcat long-string)))
+                             (Check-Error wrong-type-argument
+                                          (,function #*010010001010101))))
+                     (Check-Error wrong-type-argument
+                                  (,function ,short-string (current-buffer)
+                                             :start 0.0))
+                     (Check-Error wrong-type-argument
+                                  (,function ,short-string (current-buffer)
+                                             :end 4.0))
+                     (Check-Error invalid-function
+                                  (,function ,short-string pi))
+                     (Check-Error args-out-of-range
+                                  (,function ,short-string (current-buffer)
+                                             :end ,(1+ (length short-string))))
+                     (Check-Error args-out-of-range
+                                  (,function ,short-string nil
+                                             :start
+                                             ,(1+ (length short-string))))
+                     ;; Not checked here; output to a stdio stream, output
+                     ;; to an lstream, output to a frame.
+                     (Assert-write-results
+                      ,function "buffer point" :short-string ,short-string
+                      :long-string ,long-string :sequences-too ,sequences-too
+                      :output-stream (current-buffer)
+                      :clear-output clear-buffer
+                      :get-last-output
+                      ,(if worry-about-newline 'buffer-output-sans-newline
+                         'buffer-output))
+                     (Assert-write-results
+                      ,function "function output" :short-string ,short-string
+                      :long-string ,long-string :sequences-too ,sequences-too
+                      :output-stream function-output-stream
+                      :clear-output clear-stashed-data
+                      :get-last-output
+                      ,(if worry-about-newline
+                           'stashed-data-output-sans-newline
+                         'stashed-data-output))
+                     (Assert-write-results
+                      ,function "marker output" :short-string ,short-string
+                      :long-string ,long-string :sequences-too ,sequences-too
+                      :output-stream marker :clear-output clear-marker-data
+                      :get-last-output ,(if worry-about-newline
+                                            'marker-data-sans-newline
+                                          'marker-data)))
+                (kill-buffer stashed-data)
+                (kill-buffer marker-buffer)))))))
+  (test-write-string write-sequence :sequences-too t)
+  (test-write-string write-string :sequences-too nil)
+  (test-write-string write-line :worry-about-newline t :sequences-too nil))
+
 ;;; end of lisp-tests.el