~ chicken-core (chicken-5) 3693970dc8b09eabc2c2ba6c04b268edef3ec8fa


commit 3693970dc8b09eabc2c2ba6c04b268edef3ec8fa
Author:     Evan Hanson <evhan@foldling.org>
AuthorDate: Thu Jun 30 20:09:46 2016 +1200
Commit:     Peter Bex <peter@more-magic.net>
CommitDate: Sat Jul 9 16:26:41 2016 +0200

    Add input-port-open? and output-port-open? procedures
    
    These test whether a port is open in a specific direction.
    
    Signed-off-by: Peter Bex <peter@more-magic.net>

diff --git a/NEWS b/NEWS
index fa8188c8..775ee576 100644
--- a/NEWS
+++ b/NEWS
@@ -33,6 +33,8 @@
   - `with-error-output-to-port' from the ports module has been renamed
     to the more common `with-error-to-port', and `with-error-to-string'
     has been added for completeness (thanks to Michael Silver).
+  - New `input-port-open?` and `output-port-open?` procedures have been
+    added for testing whether a port is open in a specific direction.
 
 - Module system
   - The compiler has been modularised, for improved namespacing.  This
diff --git a/chicken.h b/chicken.h
index 509af5c4..fe109ffe 100644
--- a/chicken.h
+++ b/chicken.h
@@ -1180,8 +1180,6 @@ typedef void (C_ccall *C_proc)(C_word, C_word *) C_noret;
 #define C_vectorp(x)              C_mk_bool(C_header_bits(x) == C_VECTOR_TYPE)
 #define C_bytevectorp(x)          C_mk_bool(C_header_bits(x) == C_BYTEVECTOR_TYPE)
 #define C_portp(x)                C_mk_bool(C_header_bits(x) == C_PORT_TYPE)
-#define C_input_portp(x)          C_mk_bool(C_header_bits(x) == C_PORT_TYPE && C_block_item(x, 1) & 0x2)
-#define C_output_portp(x)         C_mk_bool(C_header_bits(x) == C_PORT_TYPE && C_block_item(x, 1) & 0x4)
 #define C_structurep(x)           C_mk_bool(C_header_bits(x) == C_STRUCTURE_TYPE)
 #define C_locativep(x)            C_mk_bool(C_block_header(x) == C_LOCATIVE_TAG)
 #define C_charp(x)                C_mk_bool(((x) & C_IMMEDIATE_TYPE_BITS) == C_CHARACTER_BITS)
@@ -1202,6 +1200,14 @@ typedef void (C_ccall *C_proc)(C_word, C_word *) C_noret;
 #define C_vemptyp(x)              C_mk_bool(C_header_size(x) == 0)
 #define C_notvemptyp(x)           C_mk_bool(C_header_size(x) > 0)
 
+#define C_port_typep(x, n)        C_mk_bool((C_block_item(x, 1) & n) == n)
+#define C_input_portp(x)          C_and(C_portp(x), C_port_typep(x, 0x2))
+#define C_output_portp(x)         C_and(C_portp(x), C_port_typep(x, 0x4))
+
+#define C_port_openp(port, n)     C_mk_bool((C_block_item(port, 8) & n) == n)
+#define C_input_port_openp(port)  C_port_openp(port, 0x2)
+#define C_output_port_openp(port) C_port_openp(port, 0x4)
+
 #define C_slot(x, i)              C_block_item(x, C_unfix(i))
 #define C_subbyte(x, i)           C_fix(((C_byte *)C_data_pointer(x))[ C_unfix(i) ] & 0xff)
 #define C_subchar(x, i)           C_make_character(((C_uchar *)C_data_pointer(x))[ C_unfix(i) ])
diff --git a/chicken.import.scm b/chicken.import.scm
index 2b30f54f..de0ce6bc 100644
--- a/chicken.import.scm
+++ b/chicken.import.scm
@@ -114,6 +114,7 @@
    getter-with-setter
    implicit-exit-handler
    infinite?
+   input-port-open?
    (ir-macro-transformer . chicken.expand#ir-macro-transformer)
    keyword-style
    (load-library . chicken.eval#load-library)
@@ -135,6 +136,7 @@
    on-exit
    open-input-string
    open-output-string
+   output-port-open?
    parentheses-synonyms
    port-closed?
    port-name
diff --git a/library.scm b/library.scm
index 1c24ff19..be9c61ae 100644
--- a/library.scm
+++ b/library.scm
@@ -2506,6 +2506,14 @@ EOF
   (and (##core#inline "C_blockp" x)
        (##core#inline "C_output_portp" x)))
 
+(define (input-port-open? p)
+  (##sys#check-input-port p 'input-port-open?)
+  (##core#inline "C_input_port_openp" p))
+
+(define (output-port-open? p)
+  (##sys#check-output-port p 'output-port-open?)
+  (##core#inline "C_output_port_openp" p))
+
 (define (port-closed? p)
   (##sys#check-port p 'port-closed?)
   (fx= (##sys#slot p 8) 0))
@@ -2761,10 +2769,9 @@ EOF
   (define (close port inp loc)
     (##sys#check-port port loc)
     ; repeated closing is ignored
-    (let* ((old-closed (##sys#slot port 8))
-	   (new-closed (fxand old-closed (fxnot (if inp 1 2)))))
-      (unless (fx= new-closed old-closed) ; already closed?
-	(##sys#setislot port 8 new-closed)
+    (let ((direction (if inp 1 2)))
+      (when (##core#inline "C_port_openp" port direction)
+	(##sys#setislot port 8 (fxand (##sys#slot port 8) (fxnot direction)))
 	((##sys#slot (##sys#slot port 2) 4) port inp))))
 
   (set! open-input-file (lambda (name . mode) (open name #t mode 'open-input-file)))
@@ -2857,12 +2864,12 @@ EOF
   (##sys#setslot port 3 name) )
 
 (define (##sys#port-line port)
-  (and (fxodd? (##sys#slot port 1)) ; input port?
+  (and (##core#inline "C_input_portp" port)
        (##sys#slot port 4) ) )
 
 (define (port-position #!optional (port ##sys#standard-input))
   (##sys#check-port port 'port-position)
-  (if (fxodd? (##sys#slot port 1)) ; input port?
+  (if (##core#inline "C_input_portp" port)
       (##sys#values (##sys#slot port 4) (##sys#slot port 5))
       (##sys#error 'port-position "cannot compute position of port" port) ) )
 
diff --git a/manual/Unit library b/manual/Unit library
index 27f66960..ff93fe9a 100644
--- a/manual/Unit library	
+++ b/manual/Unit library	
@@ -341,11 +341,21 @@ different behavior.
 Write buffered output to the given output-port. {{PORT}} defaults
 to the value of {{(current-output-port)}}.
 
+==== input-port-open?
+
+<procedure>(input-port-open? PORT)</procedure>
+
+Is the given {{PORT}} open for input?
+
+<procedure>(output-port-open? PORT)</procedure>
+
+Is the given {{PORT}} open for output?
+
 ==== port-closed?
 
 <procedure>(port-closed? PORT)</procedure>
 
-Is the given {{PORT}} closed?
+Is the given {{PORT}} closed (in all directions)?
 
 ==== port-name
 
diff --git a/tests/port-tests.scm b/tests/port-tests.scm
index 49b8e138..2fc19a08 100644
--- a/tests/port-tests.scm
+++ b/tests/port-tests.scm
@@ -95,6 +95,13 @@ EOF
      (lambda (in) (read-char in)))
     (get-output-string out))))
 
+;; {input,output}-port-open?
+
+(assert (input-port-open? (open-input-string "abc")))
+(assert (output-port-open? (open-output-string)))
+(assert-error (input-port-open? (open-output-string)))
+(assert-error (output-port-open? (open-input-string "abc")))
+
 ;; direction-specific port closure
 
 (let* ((n 0)
@@ -102,26 +109,26 @@ EOF
 			   (constantly #t)
 			   (lambda () (set! n (add1 n))))))
   (close-output-port p)
-  (assert (not (port-closed? p)))
+  (assert (input-port-open? p))
   (assert (= n 0))
   (close-input-port p)
-  (assert (port-closed? p))
+  (assert (not (input-port-open? p)))
   (assert (= n 1))
   (close-input-port p)
-  (assert (port-closed? p))
+  (assert (not (input-port-open? p)))
   (assert (= n 1)))
 
 (let* ((n 0)
        (p (make-output-port (lambda () (display #\a))
 			    (lambda () (set! n (add1 n))))))
   (close-input-port p)
-  (assert (not (port-closed? p)))
+  (assert (output-port-open? p))
   (assert (= n 0))
   (close-output-port p)
-  (assert (port-closed? p))
+  (assert (not (output-port-open? p)))
   (assert (= n 1))
   (close-output-port p)
-  (assert (port-closed? p))
+  (assert (not (output-port-open? p)))
   (assert (= n 1)))
 
 ;; fill buffers
diff --git a/types.db b/types.db
index dc070f75..259f191e 100644
--- a/types.db
+++ b/types.db
@@ -756,6 +756,9 @@
 (open-output-file (#(procedure #:clean #:enforce) open-output-file (string #!rest symbol) output-port))
 (close-input-port (#(procedure #:enforce) close-input-port (input-port) undefined))
 (close-output-port (#(procedure #:enforce) close-output-port (output-port) undefined))
+(input-port-open? (#(procedure #:enforce) input-port-open? (input-port) boolean))
+(output-port-open? (#(procedure #:enforce) output-port-open? (output-port) boolean))
+
 (read (#(procedure #:enforce) read (#!optional input-port) *))
 
 (eof-object? (#(procedure #:pure #:predicate eof) eof-object? (*) boolean))
Trap