; Copyright (c) 1993-1999 by Richard Kelsey and Jonathan Rees. See file COPYING.

; Ports and port handlers

; See doc/io.txt for a description of the i/o system, including ports,
; port handlers, and so forth.

;;; This is not the original file, but an adapted version for scsh
;;; Main difference is, that the ports have a steal-field 

(define-record-type port-handler :port-handler
  (really-make-port-handler discloser close buffer-proc ready? steal)
  port-handler?
  (discloser   port-handler-discloser)
  (close       port-handler-close)
  (buffer-proc port-handler-buffer-proc)
  (ready?      port-handler-ready?)
  (steal       port-handler-steal))

(define (make-port-handler discloser close buffer-proc ready? . maybe-steal)
  (if (pair? maybe-steal)
      (really-make-port-handler discloser close buffer-proc ready? 
				(car maybe-steal))
      (really-make-port-handler discloser close buffer-proc ready?
				(lambda (port-data owner) #f))))

(define (disclose-port port)
  ((port-handler-discloser (port-handler port)) (port-data port)))

(define-method &disclose ((port :input-port))
  (disclose-port port))

(define-method &disclose ((port :output-port))
  (disclose-port port))

; The PORT-LOCKED? bit is checked by READ-CHAR, PEEK-CHAR, and WRITE-CHAR
; which are too simple-minded to understand real locks.  The three procedures
; below ensure that (PORT-LOCKED? <port>) is #t whenever someone has aquired
; <port>'s lock.

(define (obtain-port-lock port)
  (set-port-locked?! port #t)
  (obtain-lock (port-lock port)))

(define (maybe-obtain-port-lock port)
  (let ((ints (set-enabled-interrupts! 0)))
    (set-port-locked?! port #t)
    (let ((result (if (maybe-obtain-lock (port-lock port))
		      #t
		      (begin
			(set-port-locked?! port #f)
			#f))))
      (set-enabled-interrupts! ints)
      result)))

(define (release-port-lock port)
  (let ((ints (set-enabled-interrupts! 0)))
    (if (release-lock (port-lock port))
	(set-port-locked?! port #f))
    (set-enabled-interrupts! ints)))

; Set up exception handlers for the three unnecessary I/O primitives,
; READ-CHAR, PEEK-CHAR, and WRITE-CHAR.  These do the right thing in
; the case of buffer overflow or underflow.
;
; This is abstracted to avoid a circular module dependency.

(define (initialize-i/o-handlers! define-exception-handler signal-exception)
  (define-exception-handler (enum op read-char)
    (one-arg-proc->handler (read-char-handler #t) signal-exception))
    
  (define-exception-handler (enum op peek-char)
    (one-arg-proc->handler (read-char-handler #f) signal-exception))
  
  (define-exception-handler (enum op write-char)
    (two-arg-proc->handler write-char-handler signal-exception)))

; Check the exception and then lock the port.

(define (one-arg-proc->handler proc signal-exception)
  (lambda (opcode reason port)
    (if (= reason (enum exception buffer-full/empty))
	(protect-port-op port (lambda () (proc port)))
	(signal-exception opcode reason port))))

; This could combined with on-arg-... if the port were the first argument.

(define (two-arg-proc->handler proc signal-exception)
  (lambda (opcode reason arg port)
    (if (= reason (enum exception buffer-full/empty))
	(protect-port-op port (lambda () (proc arg port)))
	(signal-exception opcode reason arg port))))

; If a character is available, use it; if there is an EOF waiting, use that;
; otherwise fill the buffer and go from there.

(define (read-char-handler read?)
  (lambda (port)
    (cond ((< (port-index port) (port-limit port))
	   (let ((index (port-index port)))
	     (if read?
		 (set-port-index! port (+ 1 index)))
	     (ascii->char (code-vector-ref (port-buffer port) index))))
	  ((port-pending-eof? port)
	   (if read?
	       (set-port-pending-eof?! port #f))
	   (eof-object))
	  (else
	   (let ((got (fill-port-buffer! port 'any)))
	     (cond ((or (eof-object? got)
			(= 0 got))
		    (if (not read?)
			(set-port-pending-eof?! port #t))
		    (eof-object))
		   (else
		    (set-port-index! port (if read? 1 0))
		    (set-port-limit! port got)
		    (ascii->char (code-vector-ref (port-buffer port) 0)))))))))

(define (fill-port-buffer! port needed)
  ((port-handler-buffer-proc (port-handler port))
   (port-data port)
   (port-buffer port)
   0
   needed))

; Write the character if there is room, or call the handler if there is
; no actual buffering.  Otherwise use the handler to empty the buffer.

(define (write-char-handler char port)
  (cond ((< (port-index port) (port-limit port))
	 (code-vector-set! (port-buffer port)
			   (port-index port)
			   (char->ascii char))
	 (set-port-index! port (+ 1 (port-index port))))
	((= 0 (port-limit port))
	 ((port-handler-buffer-proc (port-handler port)) (port-data port) char))
	(else
	 (empty-port-buffer! port)
	 (code-vector-set! (port-buffer port) 0 (char->ascii char))
	 (set-port-index! port 1)))
  (unspecific))

; This may change PORT's buffer.

(define (empty-port-buffer! port)
  ((port-handler-buffer-proc (port-handler port))
   (port-data port)
   (port-buffer port)
   0
   (port-index port))
  (set-port-index! port 0)
  (set-port-flushed?! port #t))

(define port-flushed? port-pending-eof?)
(define set-port-flushed?! set-port-pending-eof?!)

(define (protect-port-op port thunk)
  (obtain-port-lock port)
  (let ((result
	 (with-handler
	  (lambda (condition punt)
	    (release-port-lock port)
	    (punt))
	  thunk)))
    (release-port-lock port)
    result))

;----------------
; Closing is done with the appropriate handler.
; R4RS says that CLOSE-... is idempotent.

(define (close-input-port port)
  (if (input-port? port)
      (protect-port-op
       port
       (lambda ()
	 (if (open-input-port? port)
	     (begin
	       (make-input-port-closed! port)
	       ((port-handler-close (port-handler port)) (port-data port))))))
      (call-error "invalid argument" close-input-port port)))
	
;; Flushing the port may raise an error. If the port is marked closed
;; before, subsequent calls will never close the underlying channel.
;; This installs an error handler before calling REALLY-FORCE-OUTPUT,
;; the handler will try to close the port and raise the error
;; afterwards. The inner close has an additional error handler which
;; will invoke the original handler.

(define (close-output-port port)
  (if (output-port? port)
      (protect-port-op
	 port
	 (lambda ()
	   (if (open-output-port? port)
	       (begin
		 (make-output-port-closed! port)
		 (with-handler 
		  (lambda (condition more)
		    (with-handler 
		     (lambda (cond2 more2)
		       (more))
		     (lambda ()
		       ((port-handler-close (port-handler port)) (port-data port))
		       (more))))
		  (lambda ()
		    (really-force-output port)))
		 ((port-handler-close (port-handler port)) (port-data port))))))
      (call-error "invalid argument" close-output-port port)))

;----------------
; Port operations that do not have opcodes.  Each of these needs to check its
; arguments and then lock the port.  A macro is used to add the checking and
; locking code.  This has to check that the port is a port before locking it
; and then check that it is open after locking it.

; A macro to handle locking ports and checking that they're open.

(define-syntax define-port-op
  (syntax-rules ()
    ((define-port-op (?id ?args ...) ?port ?predicate ?body)
     (define (?id ?args ...)
       (if ?predicate                         ; if args are okay
	   (protect-port-op
	    ?port
	    (lambda ()
	     (if (open-port? ?port)           ; check that it's open
		 ?body
		 (call-error "invalid argument" ?id ?args ...))))
	   (call-error "invalid argument" ?id ?args ...))))

    ; ?port defaults to the first argument
    ((define-port-op (?id ?port ?args ...) ?predicate ?body)
     (define-port-op (?id ?port ?args ...) ?port ?predicate ?body))))

;----------------
; See if there is a character available.  CHAR-READY? itself is defined
; in current-ports.scm as it needs CURRENT-INPUT-PORT when called with
; no arguments.

(define (real-char-ready? port)
  (if (not (open-input-port? port))
      (call-error "invalid argument" char-ready? port)
      ((port-handler-ready? (port-handler port)) port)))
	 

;----------------
; Check the arguments and the state of the buffer.  Leave any actual work
; up to the next procedure.
;
; The arguments are the same as those for input port handlers: BUFFER is either
; a string or a code vector, START is the initial index, and COUNT is either
; the required number of characters, 'ANY (one or more characters wanted),
; or 'IMMEDIATE (get what's available, but don't block).

(define-port-op (read-block buffer start count port) port
  (and (open-input-port? port)
       (okay-limits? buffer start (if (or (eq? count 'any)
					  (eq? count 'immediate))
				      1
				      count)))
  (cond ((port-pending-eof? port)
	 (set-port-pending-eof?! port #f)
	 (eof-object))
	((eq? count 0)
	 0)
	(else
	 (really-read-block buffer start count port))))

; If nothing is available, call the port's handler procedure.  Otherwise,
; copy any available characters into the buffer.  If more are needed the
; buffer handler is called.

(define (really-read-block buffer start count port)
  (let ((have (- (port-limit port) (port-index port))))
    (if (= have 0)
	((port-handler-buffer-proc (port-handler port))
	   (port-data port)
	   buffer
	   start
	   count)
	(let ((move (min have
			 (if (symbol? count)
			     (- (buffer-length buffer) start)
			     count))))
	  (copy-bytes! (port-buffer port) (port-index port) buffer start move)
	  (set-port-index! port (+ (port-index port) move))
	  (if (or (symbol? count)
		  (= move count))
	      move
	      (read-more buffer start count port move))))))

(define (buffer-length buffer)
  (if (string? buffer)
      (string-length buffer)
      (code-vector-length buffer)))

(define (read-more buffer start count port have)
  (let ((more ((port-handler-buffer-proc (port-handler port))
	         (port-data port)
		 buffer
		 (+ start have)
		 (- count have))))
    (if (eof-object? more)
	(if (= 0 have)
	    (eof-object)
	    (begin
	      (set-port-pending-eof?! port #t)
	      have))
	(+ more have))))
    
; Check that BUFFER contains COUNT characters starting from START.

(define (okay-limits? buffer start count)
  (and (integer? start)
       (exact? start)
       (<= 0 start)
       (integer? count)
       (exact? count)
       (<= 0 count)
       (<= (+ start count)
	   (cond ((string? buffer)
		  (string-length buffer))
		 ((code-vector? buffer)
		  (code-vector-length buffer))
		 (else
		  -1)))))

;----------------
; Write the COUNT bytes beginning at START from BUFFER to PORT.

(define-port-op (write-block buffer start count port) port
  (and (open-output-port? port)
       (okay-limits? buffer start count))
  (if (= 0 (port-limit port))
      (write-unbuffered-block buffer start count port)
      (write-buffered-block buffer start count port)))

; WRITE-STRING is a front for WRITE-BLOCK.

(define (write-string string port)
  (write-block string 0 (string-length string) port))

; CHAR-READY? for output ports.

(define (output-port-ready? port)
  (cond ((not (open-output-port? port))
	 (call-error "invalid argument" output-port-ready? port))
	((not (maybe-obtain-port-lock port))
	 #f)
	((not (open-port? port))	; have to check again after the lock call
	 (release-port-lock port)
	 (call-error "invalid argument" output-port-ready? port))
	(else
	 (with-handler
	  (lambda (condition punt)
	    (release-port-lock port)
	    (punt))
	  (lambda ()
	    (let ((val ((port-handler-ready? (port-handler port)) 
			port)))
	      (release-port-lock port)
	      val))))))

; Copy the bytes into the buffer if there is room, otherwise write out anything
; in the buffer and then write BUFFER.

(define (write-buffered-block buffer start count port)
  (let ((space (- (port-limit port) (port-index port))))
    (cond ((>= space count)
	   (copy-bytes! buffer start (port-buffer port) (port-index port) count)
	   (set-port-index! port (+ (port-index port) count)))
	  (else
	   (really-force-output port)
	   ((port-handler-buffer-proc (port-handler port))
	    (port-data port) buffer start count)))))

; For unbuffered ports we have to call the handler on each character
; separately.

(define (write-unbuffered-block buffer start count port)
  (let ((proc (port-handler-buffer-proc (port-handler port)))
        (data (port-data port))
        (string? (string? buffer)))
    (do ((i 0 (+ i 1)))
        ((= i count))
      (proc data (if string?
                     (string-ref buffer (+ start i))
                     (ascii->char (code-vector-ref buffer (+ start i))))))))

;----------------
; Empty the buffer if it contains anything.

(define-port-op (force-output port)
  (open-output-port? port)
  (really-force-output port))

(define (really-force-output port)
  (if (< 0 (port-index port))
      (empty-port-buffer! port)))

; Used to avoid race conditions elsewhere.

(define (force-output-if-open port)
  (if (output-port? port)
      (protect-port-op
       port
       (lambda ()
	 (if (open-output-port? port)
	     (really-force-output port))))
      (call-error "invalid argument" force-output-if-open port)))

;----------------

(define default-buffer-size 4096)  ; should get this from the system

;----------------
; Is PORT open?

(define (open-port? port)
  (not (= 0 (bitwise-and open-port-mask (port-status port)))))

(define open-port-mask
  (bitwise-ior (arithmetic-shift 1 (enum port-status-options open-for-input))
	       (arithmetic-shift 1 (enum port-status-options open-for-output))))

;----------------
; Input ports

(define input-port-mask
  (arithmetic-shift 1 (enum port-status-options input)))

(define open-input-port-mask
  (arithmetic-shift 1 (enum port-status-options open-for-input)))

(define open-input-port-status
  (bitwise-ior input-port-mask open-input-port-mask))

(define (open-input-port? port)
  (not (= 0 (bitwise-and open-input-port-mask (port-status port)))))

(define (make-input-port-closed! port)
  (set-port-status! port (bitwise-and (port-status port)
				      (bitwise-not open-input-port-mask))))

(define (make-input-port handler data buffer index limit)
  (if (and (okay-buffer? buffer index limit)
	   (port-handler? handler))
      (make-port handler
		 (bitwise-ior input-port-mask open-input-port-mask)
		 (make-lock)
		 #f             ; locked?
		 data
		 buffer
		 index
		 limit
		 #f)            ; pending-eof?
      (call-error "invalid argument"
		  make-input-port handler data buffer index limit)))

(define (okay-buffer? buffer index limit)
  (and (code-vector? buffer)
       (let ((length (code-vector-length buffer)))
	 (integer? limit)
	 (<= 0 limit)
	 (<= limit length)
	 (<= index limit)
	 (integer? index)
	 (<= 0 index)
	 (<= index limit))))

;----------------
; Output ports

(define output-port-mask
  (arithmetic-shift 1 (enum port-status-options output)))

(define open-output-port-mask
  (arithmetic-shift 1 (enum port-status-options open-for-output)))

(define open-output-port-status
  (bitwise-ior output-port-mask open-output-port-mask))

(define (open-output-port? port)
  (not (= 0 (bitwise-and open-output-port-mask (port-status port)))))

(define (make-output-port-closed! port)
  (set-port-status! port (bitwise-and (port-status port)
				      (bitwise-not open-output-port-mask))))

(define (make-output-port handler data buffer index limit)
  (if (and (okay-buffer? buffer index limit)
	   (> limit 0)
	   (port-handler? handler))
      (make-port handler
		 open-output-port-status
		 (make-lock)
		 #f             ; locked?
		 data
		 buffer
		 index
		 limit
		 #f)            ; pending-eof?
      (call-error "invalid argument"
		  make-output-port handler data buffer index limit)))

(define (make-unbuffered-output-port handler data)
  (if (port-handler? handler)
      (make-port handler
		 open-output-port-status
		 (make-lock)
		 #f             ; locked?
		 data
		 (make-code-vector 0 0)
		 0
		 0
		 #f)            ; pending-eof?
      (call-error "invalid argument"
		  make-unbuffered-output-port handler data)))

(define null-output-port-handler
  (make-port-handler
   (lambda (ignore)
     (list 'null-output-port))
   (lambda (ignore)
     (unspecific))
   (lambda (channel buffer start need)
     (unspecific))
   (lambda (port)                        ; ready?
     #t)
   (lambda (ignore1 ignore2)
     #f)))

(define (make-null-output-port)
  (make-port null-output-port-handler
	     open-output-port-status
	     (make-lock)    ; wasted
	     #f             ; locked?
	     (unspecific)
	     (make-code-vector 1 0)
	     0
	     1              ; if 0 it would look unbuffered
	     #f))           ; pending-eof?

;----------------
; Code to periodically flush output ports.

(define *flush-these-ports* (cons #f '()))

(define (initialize-output-port-list!)
  (set! *flush-these-ports* (cons #f '())))

(define (periodically-force-output! port)
  (set-cdr! *flush-these-ports*
	    (cons (make-weak-pointer port) (cdr *flush-these-ports*))))

; Return a list of thunks that will flush the buffer of each open, unlocked
; port that contains characters that have been there since the last time
; this was called.  The actual i/o is done using separate threads to keep
; i/o errors from killing anything vital.
; 
; If USE-FLUSHED?-FLAGS? is true this won't flush buffers that have been
; flushed by someone else sinse the last call.  If it is false then flush
; all non-empty buffers, because the system has nothing to do and is going
; to pause while waiting for external events.

(define (output-port-forcers use-flushed?-flags? . maybe-ignore-port-locks?)
  (let ((ignore-port-locks? (if (null? maybe-ignore-port-locks?) #f #t)))
    (let loop ((next (cdr *flush-these-ports*))
	       (last *flush-these-ports*)
	       (thunks '()))
      (if (null? next)
	  thunks
	  (let ((port (weak-pointer-ref (car next))))
	    (cond ((or (not port)	; GCed or closed
		       (not (open-output-port? port))) ;  so drop it from the list
		   (set-cdr! last (cdr next))
		   (loop (cdr next) last thunks))
		  (ignore-port-locks?
		   (cond ((and use-flushed?-flags? ; flushed recently
			       (port-flushed? port))
			  (set-port-flushed?! port #f)
			  (loop (cdr next) next thunks))
			 ((< 0 (port-index port)) ; non-empty
			  (loop (cdr next) next
				(cons (make-forcing-thunk port ignore-port-locks?)
				      thunks)))
			 (else (loop (cdr next) next thunks))))
		  ((not (maybe-obtain-port-lock port)) ; locked
		   (loop (cdr next) next thunks))
		  ((and use-flushed?-flags? ; flushed recently
			(port-flushed? port))
		   (set-port-flushed?! port #f)
		   (release-port-lock port)
		   (loop (cdr next) next thunks))
		  ((< 0 (port-index port)) ; non-empty
		   (release-port-lock port)
		   (loop (cdr next) next
			 (cons (make-forcing-thunk port ignore-port-locks?)
			       thunks)))
		  (else			; empty
		   (release-port-lock port)
		   (loop (cdr next) next thunks))))))))

; Returns a list of the current ports that are flushed whenever.
; This is used to flush channel ports before forking.

(define (periodically-flushed-ports)
  (let ((ints (set-enabled-interrupts! 0)))
    (let loop ((next (cdr *flush-these-ports*))
	       (last *flush-these-ports*)
	       (ports '()))
      (if (null? next)
	  (begin
	    (set-enabled-interrupts! ints)
	    ports)
	  (let ((port (weak-pointer-ref (car next))))
	    (cond ((or (not port)                      ; GCed or closed
		       (not (open-output-port? port))) ; so drop it from the list
		   (set-cdr! last (cdr next))
		   (loop (cdr next) last ports))
		  (else
		   (loop (cdr next)
			 next
			 (cons port ports)))))))))

; Write out PORT's buffer.  If a problem occurs it is reported and PORT
; is closed.

(define (make-forcing-thunk port ignore-port-lock?)
  (lambda ()
    (if (and (report-errors-as-warnings
	      (lambda ()
		(cond ((maybe-obtain-port-lock port)
		       (with-handler
			(lambda (condition punt)
			  (release-port-lock port)
			  (punt))
			(lambda ()
			  (really-force-output port)
			  (release-port-lock port))))
		      (ignore-port-lock?
		       (really-force-output port))))
	      "error when flushing buffer; closing port"
	      port)
	     (open-output-port? port))
	(report-errors-as-warnings
	 (lambda ()
	   (make-output-port-closed! port)
	   ((port-handler-close (port-handler port)) (port-data port)))
	 "error when closing port"
	 port))))
	
;----------------
; The following is used to make the REPL's input, output, and error ports
; available after a keyboard interrupt.  If PORT is a locked channel port
; we save the its state and then reinitialize it.  The OS is told to
; abort any pending operation on the port's channel.  Finally, the owning
; thread's continuation is munged to restore the port when the thread
; resumes.  Any buffered input is thrown away at that point (it could
; be saved away with the channel).
;
; If the port is locked by us or one of our ancestors there is no point in
; trying to grab it.

(define (steal-port! port)
  (begin
    (disable-interrupts!)
    (let ((owner (if (lock-owner-uid (port-lock port))
		     (thread-uid->thread (lock-owner-uid (port-lock port)))
		     #f)))
      (if (and owner
	       (not (running? owner)))
	  (begin
;	    (message (list  (thread-uid owner) " "
;			    (thread-uid (current-thread)) " "))
	    (really-steal-port! port owner)))
      (enable-interrupts!))))

(define (really-steal-port! port owner)
  (let ((lock (port-lock port))
	(buffer (port-buffer port))
	(index (port-index port))
	(limit (port-limit port))
	(eof? (port-pending-eof? port))
	(status ((port-handler-steal (port-handler port))
		 (port-data port) owner)))
    (set-port-buffer! port (make-code-vector (code-vector-length buffer) 0))
    (set-port-index! port 0)
    (set-port-limit! port (if (input-port? port) 0 (code-vector-length buffer)))
    (set-port-pending-eof?! port #f)
    (set-port-locked?! port #f)
    (set-port-lock! port (make-lock))
    (interrupt-thread owner
		      (lambda results
			(obtain-port-lock port)
			(let ((cleanup
			       (lambda ()
				 (set-port-buffer! port buffer)
				 (set-port-index! port index)
				 (set-port-limit! port limit)
				 (set-port-pending-eof?! port eof?)
				 (set-port-lock! port lock))))
			  (with-handler
			   (lambda (condition punt)
			     (cleanup)
			     (punt))
			   (lambda ()
			     (cond ((output-port? port)
				    (really-force-output port))
				   ((< (port-index port)
				       (port-limit port))
				    (warn "dropping input from port" port)))
			     (cleanup)
			     (or status (apply values results)))))))
    ; if we took OWNER off a channel-wait queue we need to make it ready to run
    (if status (make-ready owner))))
			       
;;;;; We don't have unbuffered input ports for now. It's possible to
;;;;; define them if the handler takes care of the char for peek-char,
;;;;; but there is not much point in having them. A buffered port with
;;;;; buffer size 1 provides the same functionality. See 0.54 for
;;;;; unbuffered input ports


;;;;; buffered ports
;;;;; 
;;;;; This is only a skeleton. With the switch to 0.54 everything will
;;;;; change anyway, but for char-ready? we need some abstraction now
;;;;; This code is stolen from 0.54's port-buffer.scm and shortened

(define (make-buffered-input-port handler data buffer index limit)
  (if (and (okay-buffer? buffer index limit)
	   (port-handler? handler))
      (make-port handler
		 (bitwise-ior input-port-mask open-input-port-mask)
		 (make-lock)
		 #f             ; locked? 
		 data
		 buffer
		 index
		 limit
		 #f)            ; pending-eof?
      (call-error "invalid argument"
		  make-buffered-input-port handler data buffer index limit)))

(define (make-buffered-output-port handler data buffer index limit)
  (if (and (okay-buffer? buffer index limit)
	   (> limit 0)
	   (port-handler? handler))
      (make-port handler
		 open-output-port-status
		 (make-lock)
		 #f             ; locked? 
		 data
		 buffer
		 index
		 limit
		 #f)            ; pending-eof?
      (call-error "invalid argument"
		  make-buffered-output-port handler data buffer index limit)))

(define (okay-buffer? buffer index limit)
  (and (code-vector? buffer)
       (let ((length (code-vector-length buffer)))
	 (integer? limit)
	 (integer? index)
	 (exact? limit)
	 (exact? index)
	 (<= 0 limit length)
	 (<= 0 index limit))))


(define (make-buffered-input-port-handler discloser
					  closer!
					  read-block!
					  ready?
					  . maybe-steal!)
  (apply make-port-handler discloser
	                   closer!
			   read-block!
			   (make-char-ready? ready? #t)
			   maybe-steal!))			

;----------------
; See if there is a character available.

(define (make-char-ready? ready? read?)
  (lambda (port)
    (cond ((not ((if read?
		     open-input-port?
		     open-output-port?)
		 port))
	   (call-error "invalid argument" char-ready? port))
	  ((or (< (port-index port)
		  (port-limit port))
	       (and read?
		    (port-pending-eof? port)))
	   #t)
	  (else
	   (ready? port)))))


(define (make-buffered-output-port-handler discloser
					   closer!
					   buffer-emptier!
					   ready?
					   . maybe-steal!)
  (apply make-port-handler discloser
	                   closer!
			   buffer-emptier!
			   (make-char-ready? ready? #f)
			   maybe-steal!))