unofficial mirror of guile-devel@gnu.org 
 help / color / mirror / Atom feed
* Some work on the R6RS I/O libraries
@ 2010-11-15 21:45 Andreas Rottmann
  2010-11-19 23:59 ` Ludovic Courtès
  0 siblings, 1 reply; 19+ messages in thread
From: Andreas Rottmann @ 2010-11-15 21:45 UTC (permalink / raw)
  To: Guile Development

[-- Attachment #1: Type: text/plain, Size: 2416 bytes --]


Fix missing port-table locking and bytevector output port segfault.

* libguile/r6rs-ports.c (make_bip, make_cbip, make_bop, make_cbop): Lock
  the port table.

* libguile/r6rs-ports.c (make_bop): Let the returned extraction
  procedure refer to the port's buffer instead of the port itself.  This
  fixes a segfault if the port is closed before the extraction procedure
  is called.
  (bop_proc_apply): Adapt accordingly.
* test-suite/tests/r6rs-ports.test (8.2.10 Output ports): Add testcase
  for extraction after close.

Add implementation of "transcoded ports".

* libguile/r6rs-ports.c (make_tp, tp_write, tp_fill_input, tp_flush,
  tp_close, initialize_transcoded_ports, scm_transcoded_port): New
  functions.
  (scm_init_r6rs_ports): Call `initialize_transcoded_ports'.
* module/rnrs/ports.scm (transcoded-port): Remove, this is now 
  implemented in C.
* test-suite/tests/r6rs-ports.test (8.2.6 Input and output ports): Added a 
  few tests for `transcoded-port'.

Move the I/O condition types from `(rnrs conditions)', where they were
not exported, to `(rnrs files)', where they are.

* module/rnrs/conditions.scm: Remove definition of I/O condition types.
* module/rnrs/files.scm: Replace references to I/O condition types
  inside `(rnrs conditions)' with the actual definitions.
* module/rnrs/io/simple.scm: Don't `@@'-reference the I/O condition types, just
  imported them from `(rnrs files)'.

Work towards a more complete implementation of `(rnrs io ports)'.

* module/rnrs/io/ports.scm: Change into an R6RS library from a "regular"
  Guile module, so the bookkeeping for #:re-export and #:replace is done
  automatically and we gain control over the imports from `(guile)'.

  (file-option, buffer-mode, eol-style, error-handling-mode,
  make-transcoder, native-transcoder, latin-1-codec, utf-8-codec,
  utf-16-codec, call-with-bytevector-output-port, open-file-input-port,
  open-file-output-port, make-custom-textual-output-port,
  flush-output-port, put-char, put-datum, put-string, get-char,
  get-datum, get-line, get-string-all, lookahead-char,
  standard-input-port, standard-output-port, standard-error-port):
  Define all of these.

  (call-with-port): Don't use `dynamic-wind', as it is against its
  specification in R6RS 8.2.6.

* module/rnrs/io/simple.scm (call-with-input-file,
  call-with-output-file): Define these in terms of R6RS procedures to
  get correct exception behavior.


[-- Attachment #2: rnrs-io-ports.diff --]
[-- Type: text/x-diff, Size: 37813 bytes --]

From: Andreas Rottmann <a.rottmann@gmx.at>
Subject: Some work on the R6RS I/O libraries

Fix missing port-table locking and bytevector output port segfault.

* libguile/r6rs-ports.c (make_bip, make_cbip, make_bop, make_cbop): Lock
  the port table.

* libguile/r6rs-ports.c (make_bop): Let the returned extraction
  procedure refer to the port's buffer instead of the port itself.  This
  fixes a segfault if the port is closed before the extraction procedure
  is called.
  (bop_proc_apply): Adapt accordingly.
* test-suite/tests/r6rs-ports.test (8.2.10 Output ports): Add testcase
  for extraction after close.

Add implementation of "transcoded ports".

* libguile/r6rs-ports.c (make_tp, tp_write, tp_fill_input, tp_flush,
  tp_close, initialize_transcoded_ports, scm_transcoded_port): New
  functions.
  (scm_init_r6rs_ports): Call `initialize_transcoded_ports'.
* module/rnrs/ports.scm (transcoded-port): Remove, this is now 
  implemented in C.
* test-suite/tests/r6rs-ports.test (8.2.6 Input and output ports): Added a 
  few tests for `transcoded-port'.

Move the I/O condition types from `(rnrs conditions)', where they were
not exported, to `(rnrs files)', where they are.

* module/rnrs/conditions.scm: Remove definition of I/O condition types.
* module/rnrs/files.scm: Replace references to I/O condition types
  inside `(rnrs conditions)' with the actual definitions.
* module/rnrs/io/simple.scm: Don't `@@'-reference the I/O condition types, just
  imported them from `(rnrs files)'.

Work towards a more complete implementation of `(rnrs io ports)'.

* module/rnrs/io/ports.scm: Change into an R6RS library from a "regular"
  Guile module, so the bookkeeping for #:re-export and #:replace is done
  automatically and we gain control over the imports from `(guile)'.

  (file-option, buffer-mode, eol-style, error-handling-mode,
  make-transcoder, native-transcoder, latin-1-codec, utf-8-codec,
  utf-16-codec, call-with-bytevector-output-port, open-file-input-port,
  open-file-output-port, make-custom-textual-output-port,
  flush-output-port, put-char, put-datum, put-string, get-char,
  get-datum, get-line, get-string-all, lookahead-char,
  standard-input-port, standard-output-port, standard-error-port):
  Define all of these.

  (call-with-port): Don't use `dynamic-wind', as it is against its
  specification in R6RS 8.2.6.

* module/rnrs/io/simple.scm (call-with-input-file,
  call-with-output-file): Define these in terms of R6RS procedures to
  get correct exception behavior.

---
 libguile/r6rs-ports.c            |  187 +++++++++++++++++++++++--
 module/rnrs.scm                  |   58 +++------
 module/rnrs/conditions.scm       |   26 ----
 module/rnrs/files.scm            |   81 ++++--------
 module/rnrs/io/ports.scm         |  286 +++++++++++++++++++++++++++++++++-----
 module/rnrs/io/simple.scm        |   83 +++---------
 test-suite/tests/r6rs-ports.test |   31 ++++
 7 files changed, 515 insertions(+), 237 deletions(-)

diff --git a/libguile/r6rs-ports.c b/libguile/r6rs-ports.c
index 968b329..d1a4fb1 100644
--- a/libguile/r6rs-ports.c
+++ b/libguile/r6rs-ports.c
@@ -84,6 +84,8 @@ make_bip (SCM bv)
   scm_t_port *c_port;
   const unsigned long mode_bits = SCM_OPN | SCM_RDNG;
 
+  scm_i_scm_pthread_mutex_lock (&scm_i_port_table_mutex);
+  
   port = scm_new_port_table_entry (bytevector_input_port_type);
 
   /* Prevent BV from being GC'd.  */
@@ -100,6 +102,8 @@ make_bip (SCM bv)
 
   /* Mark PORT as open, readable and unbuffered (hmm, how elegant...).  */
   SCM_SET_CELL_TYPE (port, bytevector_input_port_type | mode_bits);
+  
+  scm_i_pthread_mutex_unlock (&scm_i_port_table_mutex);
 
   return port;
 }
@@ -305,6 +309,8 @@ make_cbip (SCM read_proc, SCM get_position_proc,
   SCM_SIMPLE_VECTOR_SET (method_vector, 2, set_position_proc);
   SCM_SIMPLE_VECTOR_SET (method_vector, 3, close_proc);
 
+  scm_i_pthread_mutex_lock (&scm_i_port_table_mutex);
+  
   port = scm_new_port_table_entry (custom_binary_input_port_type);
 
   /* Attach it the method vector.  */
@@ -319,6 +325,8 @@ make_cbip (SCM read_proc, SCM get_position_proc,
   /* Mark PORT as open, readable and unbuffered (hmm, how elegant...).  */
   SCM_SET_CELL_TYPE (port, custom_binary_input_port_type | mode_bits);
 
+  scm_i_pthread_mutex_unlock (&scm_i_port_table_mutex);
+  
   return port;
 }
 
@@ -751,10 +759,7 @@ SCM_DEFINE (scm_put_bytevector, "put-bytevector", 2, 2, 0,
    XXX: Access to a bop's internal buffer is not thread-safe.  */
 
 static scm_t_bits bytevector_output_port_type = 0;
-
-SCM_SMOB (bytevector_output_port_procedure,
-	  "r6rs-bytevector-output-port-procedure",
-	  0);
+static scm_t_bits bytevector_output_port_procedure = 0;
 
 #define SCM_GC_BOP "r6rs-bytevector-output-port"
 #define SCM_BOP_BUFFER_INITIAL_SIZE 4096
@@ -812,6 +817,8 @@ make_bop (void)
   scm_t_bop_buffer *buf;
   const unsigned long mode_bits = SCM_OPN | SCM_WRTNG;
 
+  scm_i_pthread_mutex_lock (&scm_i_port_table_mutex);
+  
   port = scm_new_port_table_entry (bytevector_output_port_type);
 
   buf = (scm_t_bop_buffer *) scm_gc_malloc (sizeof (* buf), SCM_GC_BOP);
@@ -825,11 +832,12 @@ make_bop (void)
 
   /* Mark PORT as open and writable.  */
   SCM_SET_CELL_TYPE (port, bytevector_output_port_type | mode_bits);
+  
+  scm_i_pthread_mutex_unlock (&scm_i_port_table_mutex);
 
   /* Make the bop procedure.  */
-  SCM_NEWSMOB (bop_proc, bytevector_output_port_procedure,
-	       SCM_PACK (port));
-
+  SCM_NEWSMOB (bop_proc, bytevector_output_port_procedure, buf);
+  
   return (scm_values (scm_list_2 (port, bop_proc)));
 }
 
@@ -886,14 +894,13 @@ bop_seek (SCM port, scm_t_off offset, int whence)
 #undef FUNC_NAME
 
 /* Fetch data from a bop.  */
-SCM_SMOB_APPLY (bytevector_output_port_procedure,
-		bop_proc_apply, 0, 0, 0, (SCM bop_proc))
+static SCM
+bop_proc_apply (SCM bop_proc)
 {
-  SCM port, bv;
+  SCM bv;
   scm_t_bop_buffer *buf, result_buf;
 
-  port = SCM_PACK (SCM_SMOB_DATA (bop_proc));
-  buf = SCM_BOP_BUFFER (port);
+  buf = (scm_t_bop_buffer *) SCM_SMOB_DATA (bop_proc);
 
   result_buf = *buf;
   bop_buffer_init (buf);
@@ -939,6 +946,12 @@ initialize_bytevector_output_ports (void)
 			NULL, bop_write);
 
   scm_set_port_seek (bytevector_output_port_type, bop_seek);
+  
+  bytevector_output_port_procedure =
+    scm_make_smob_type ("r6rs-bytevector-output-port-procedure", 0);
+  
+  scm_set_smob_apply (bytevector_output_port_procedure,
+                      bop_proc_apply, 0, 0, 0);
 }
 
 \f
@@ -966,6 +979,8 @@ make_cbop (SCM write_proc, SCM get_position_proc,
   SCM_SIMPLE_VECTOR_SET (method_vector, 2, set_position_proc);
   SCM_SIMPLE_VECTOR_SET (method_vector, 3, close_proc);
 
+  scm_i_pthread_mutex_lock (&scm_i_port_table_mutex);
+
   port = scm_new_port_table_entry (custom_binary_output_port_type);
 
   /* Attach it the method vector.  */
@@ -978,6 +993,8 @@ make_cbop (SCM write_proc, SCM get_position_proc,
 
   /* Mark PORT as open, writable and unbuffered.  */
   SCM_SET_CELL_TYPE (port, custom_binary_output_port_type | mode_bits);
+  
+  scm_i_pthread_mutex_unlock (&scm_i_port_table_mutex);
 
   return port;
 }
@@ -1062,6 +1079,151 @@ initialize_custom_binary_output_ports (void)
 }
 
 \f
+/* Transcoded ports ("tp" for short).  */
+static scm_t_bits transcoded_port_type = 0;
+
+#define TP_INPUT_BUFFER_SIZE 4096
+
+#define SCM_TP_BINARY_PORT(_port) SCM_PACK (SCM_STREAM (_port))
+
+static inline SCM
+make_tp (SCM binary_port, unsigned long mode)
+{
+  SCM port;
+  scm_t_port *c_port;
+  const unsigned long mode_bits = SCM_OPN | mode;
+  
+  scm_i_pthread_mutex_lock (&scm_i_port_table_mutex);
+
+  port = scm_new_port_table_entry (transcoded_port_type);
+
+  SCM_SETSTREAM (port, SCM_UNPACK (binary_port));
+
+  SCM_SET_CELL_TYPE (port, transcoded_port_type | mode_bits);
+
+  if (SCM_INPUT_PORT_P (port))
+    {
+      c_port = SCM_PTAB_ENTRY (port);
+      c_port->read_buf = scm_gc_malloc_pointerless (TP_INPUT_BUFFER_SIZE,
+                                                    "port buffer");
+      c_port->read_pos = c_port->read_end = c_port->read_buf;
+      c_port->read_buf_size = TP_INPUT_BUFFER_SIZE;
+      
+      SCM_SET_CELL_WORD_0 (port, SCM_CELL_WORD_0 (port) & ~SCM_BUF0);
+    }
+  
+  scm_i_pthread_mutex_unlock (&scm_i_port_table_mutex);
+
+  return port;
+}
+
+static void
+tp_write (SCM port, const void *data, size_t size)
+{
+  scm_c_write (SCM_TP_BINARY_PORT (port), data, size);
+}
+
+static int
+tp_fill_input (SCM port)
+{
+  size_t count;
+  scm_t_port *c_port = SCM_PTAB_ENTRY (port);
+  SCM bport = SCM_TP_BINARY_PORT (port);
+  scm_t_port *c_bport = SCM_PTAB_ENTRY (bport);
+
+  /* We can't use scm_c_read() here, since it blocks until the whole
+     block has been read or EOF */
+  
+  if (c_bport->rw_active == SCM_PORT_WRITE)
+    scm_force_output (bport);
+
+  if (c_bport->read_pos >= c_bport->read_end)
+    scm_fill_input (bport);
+  
+  count = c_bport->read_end - c_bport->read_pos;
+  if (count > c_port->read_buf_size)
+    count = c_port->read_buf_size;
+
+  memcpy (c_port->read_buf, c_bport->read_pos, count);
+  c_bport->read_pos += count;
+
+  if (c_bport->rw_random)
+    c_bport->rw_active = SCM_PORT_READ;
+
+  if (count == 0)
+    return EOF;
+  else
+    {
+      c_port->read_pos = c_port->read_buf;
+      c_port->read_end = c_port->read_buf + count;
+      return *c_port->read_buf;
+    }
+}
+
+static void
+tp_flush (SCM port)
+{
+  SCM binary_port = SCM_TP_BINARY_PORT (port);
+  scm_t_port *c_port = SCM_PTAB_ENTRY (port);
+  size_t count = c_port->write_pos - c_port->write_buf;
+
+  scm_c_write (binary_port, c_port->write_buf, count);
+
+  c_port->write_pos = c_port->write_buf;
+  c_port->rw_active = SCM_PORT_NEITHER;
+
+  scm_force_output (binary_port);
+}
+
+static int
+tp_close (SCM port)
+{
+  if (SCM_OUTPUT_PORT_P (port))
+    tp_flush (port);
+  return scm_is_true (scm_close_port (SCM_TP_BINARY_PORT (port))) ? 0 : -1;
+}
+
+static inline void
+initialize_transcoded_ports (void)
+{
+  transcoded_port_type =
+    scm_make_port_type ("r6rs-transcoded-port", tp_fill_input, tp_write);
+  
+  scm_set_port_flush (transcoded_port_type, tp_flush);
+  scm_set_port_close (transcoded_port_type, tp_close);
+}
+
+SCM_DEFINE (scm_transcoded_port,
+	    "transcoded-port", 2, 0, 0,
+	    (SCM port, SCM transcoder),
+	    "")
+#define FUNC_NAME s_scm_transcoded_port
+{
+  SCM codec;
+  SCM result;
+  unsigned long mode = 0;
+  
+  SCM_VALIDATE_PORT (SCM_ARG1, port);
+  SCM_VALIDATE_STRUCT (SCM_ARG1, transcoder);
+
+  codec = scm_struct_ref (transcoder, scm_from_int8 (1));
+  
+  if (scm_is_true (scm_output_port_p (port)))
+    mode |= SCM_WRTNG;
+  else if (scm_is_true (scm_input_port_p (port)))
+    mode |=  SCM_RDNG;
+  
+  result = make_tp (port, mode);
+
+  scm_set_port_encoding_x (result, codec);
+    
+  /* SCM_CLR_PORT_OPEN_FLAG (port); */
+  
+  return result;
+}
+#undef FUNC_NAME
+
+\f
 /* Initialization.  */
 
 void
@@ -1082,4 +1244,5 @@ scm_init_r6rs_ports (void)
   initialize_custom_binary_input_ports ();
   initialize_bytevector_output_ports ();
   initialize_custom_binary_output_ports ();
+  initialize_transcoded_ports ();
 }
diff --git a/module/rnrs.scm b/module/rnrs.scm
index c329aeb..ddc602e 100644
--- a/module/rnrs.scm
+++ b/module/rnrs.scm
@@ -162,13 +162,27 @@
 
 	  eof-object? port? input-port? output-port? eof-object port-transcoder
 	  binary-port? transcoded-port port-position set-port-position!
-	  port-has-port-position? port-has-set-port-position!? call-with-port
+	  port-has-port-position? port-has-set-port-position!?
+          close-port call-with-port
 	  open-bytevector-input-port make-custom-binary-input-port get-u8 
 	  lookahead-u8 get-bytevector-n get-bytevector-n! get-bytevector-some 
 	  get-bytevector-all open-bytevector-output-port
 	  make-custom-binary-output-port put-u8 put-bytevector
           open-string-input-port open-string-output-port
-
+          call-with-bytevector-output-port
+          call-with-string-output-port
+          file-options buffer-mode buffer-mode?
+          eol-style native-eol-style error-handling-mode
+          make-transcoder transcoder-codec native-transcoder
+          latin-1-codec utf-8-codec utf-16-codec
+          open-file-input-port open-file-output-port
+          make-custom-textual-output-port
+          call-with-string-output-port
+	  flush-output-port put-string
+          get-char get-datum get-line get-string-all lookahead-char
+          put-char put-datum put-string
+          standard-input-port standard-output-port standard-error-port
+          
 	  ;; (rnrs io simple)
 	  
 	  call-with-input-file call-with-output-file current-input-port
@@ -244,45 +258,7 @@
 	  (rnrs enums (6))
 	  (rnrs exceptions (6))
 
-          ;; These i/o conditions are exported by (io simple), (files), and
-          ;; should be exported by (ports) but are not yet. Avoid duplicate
-          ;; bindings warnings, then, by excluding these bindings from all but
-          ;; (io simple).
-	  (except (rnrs files (6))
-                  &i/o make-i/o-error i/o-error?
-                  &i/o-read make-i/o-read-error i/o-read-error?
-                  &i/o-write make-i/o-write-error i/o-write-error?
-
-                  &i/o-invalid-position 
-                  make-i/o-invalid-position-error 
-                  i/o-invalid-position-error? 
-                  i/o-error-position
-	  
-                  &i/o-filename
-                  make-i/o-filename-error
-                  i/o-filename-error?
-                  i/o-error-filename
-	  
-                  &i/o-file-protection 
-                  make-i/o-file-protection-error
-                  i/o-file-protection-error?
-
-                  &i/o-file-is-read-only
-                  make-i/o-file-is-read-only-error
-                  i/o-file-is-read-only-error?
-
-                  &i/o-file-already-exists
-                  make-i/o-file-already-exists-error
-                  i/o-file-already-exists-error?
-
-                  &i/o-file-does-not-exist
-                  make-i/o-file-does-not-exist-error
-                  i/o-file-does-not-exist-error?
-
-                  &i/o-port
-                  make-i/o-port-error
-                  i/o-port-error?
-                  i/o-error-port)
+          (rnrs files (6))
 
 	  (rnrs hashtables (6))
 
diff --git a/module/rnrs/conditions.scm b/module/rnrs/conditions.scm
index b897221..6885ada 100644
--- a/module/rnrs/conditions.scm
+++ b/module/rnrs/conditions.scm
@@ -229,30 +229,4 @@
   (define-condition-type &undefined &violation
     make-undefined-violation undefined-violation?)
   
-  ;; Condition types that are used by (rnrs files), (rnrs io ports), and
-  ;; (rnrs io simple).  These are defined here so as to be easily shareable by
-  ;; these three libraries.
-  
-  (define-condition-type &i/o &error make-i/o-error i/o-error?)
-  (define-condition-type &i/o-read &i/o make-i/o-read-error i/o-read-error?)
-  (define-condition-type &i/o-write &i/o make-i/o-write-error i/o-write-error?)
-  (define-condition-type &i/o-invalid-position
-    &i/o make-i/o-invalid-position-error i/o-invalid-position-error?
-    (position i/o-error-position))
-  (define-condition-type &i/o-filename 
-    &i/o make-i/o-filename-error i/o-filename-error?
-    (filename i/o-error-filename))
-  (define-condition-type &i/o-file-protection
-    &i/o-filename make-i/o-file-protection-error i/o-file-protection-error?)
-  (define-condition-type &i/o-file-is-read-only
-    &i/o-file-protection make-i/o-file-is-read-only-error 
-    i/o-file-is-read-only-error?)
-  (define-condition-type &i/o-file-already-exists
-    &i/o-filename make-i/o-file-already-exists-error 
-    i/o-file-already-exists-error?)
-  (define-condition-type &i/o-file-does-not-exist
-    &i/o-filename make-i/o-file-does-not-exist-error
-    i/o-file-does-not-exist-error?)
-  (define-condition-type &i/o-port &i/o make-i/o-port-error i/o-port-error?
-    (port i/o-error-port))
 )
diff --git a/module/rnrs/files.scm b/module/rnrs/files.scm
index e6851d0..447b8b3 100644
--- a/module/rnrs/files.scm
+++ b/module/rnrs/files.scm
@@ -67,59 +67,30 @@
 	   (lambda () (delete-file-internal filename))
 	   (lambda (key . args) (raise (make-i/o-filename-error filename)))))
 
-  (define &i/o (@@ (rnrs conditions) &i/o))
-  (define make-i/o-error (@@ (rnrs conditions) make-i/o-error))
-  (define i/o-error? (@@ (rnrs conditions) i/o-error?))
-
-  (define &i/o-read (@@ (rnrs conditions) &i/o-read))
-  (define make-i/o-read-error (@@ (rnrs conditions) make-i/o-read-error))
-  (define i/o-read-error? (@@ (rnrs conditions) i/o-read-error?))
-
-  (define &i/o-write (@@ (rnrs conditions) &i/o-write))
-  (define make-i/o-write-error (@@ (rnrs conditions) make-i/o-write-error))
-  (define i/o-write-error? (@@ (rnrs conditions) i/o-write-error?))
-
-  (define &i/o-invalid-position (@@ (rnrs conditions) &i/o-invalid-position))
-  (define make-i/o-invalid-position-error 
-    (@@ (rnrs conditions) make-i/o-invalid-position-error))
-  (define i/o-invalid-position-error? 
-    (@@ (rnrs conditions) i/o-invalid-position-error?))
-  (define i/o-error-position (@@ (rnrs conditions) i/o-error-position))
-
-  (define &i/o-filename (@@ (rnrs conditions) &i/o-filename))
-  (define make-i/o-filename-error 
-    (@@ (rnrs conditions) make-i/o-filename-error))
-  (define i/o-filename-error? (@@ (rnrs conditions) i/o-filename-error?))
-  (define i/o-error-filename (@@ (rnrs conditions) i/o-error-filename))
-
-  (define &i/o-file-protection (@@ (rnrs conditions) &i/o-file-protection))
-  (define make-i/o-file-protection-error 
-    (@@ (rnrs conditions) make-i/o-file-protection-error))
-  (define i/o-file-protection-error? 
-    (@@ (rnrs conditions) i/o-file-protection-error?))
-
-  (define &i/o-file-is-read-only (@@ (rnrs conditions) &i/o-file-is-read-only))
-  (define make-i/o-file-is-read-only-error
-    (@@ (rnrs conditions) make-i/o-file-is-read-only-error))
-  (define i/o-file-is-read-only-error?
-    (@@ (rnrs conditions) i/o-file-is-read-only-error?))
-
-  (define &i/o-file-already-exists 
-    (@@ (rnrs conditions) &i/o-file-already-exists))
-  (define make-i/o-file-already-exists-error
-    (@@ (rnrs conditions) make-i/o-file-already-exists-error))
-  (define i/o-file-already-exists-error?
-    (@@ (rnrs conditions) i/o-file-already-exists-error?))
-
-  (define &i/o-file-does-not-exist
-    (@@ (rnrs conditions) &i/o-file-does-not-exist))
-  (define make-i/o-file-does-not-exist-error
-    (@@ (rnrs conditions) make-i/o-file-does-not-exist-error))
-  (define i/o-file-does-not-exist-error?
-    (@@ (rnrs conditions) i/o-file-does-not-exist-error?))
-
-  (define &i/o-port (@@ (rnrs conditions) &i/o-port))
-  (define make-i/o-port-error (@@ (rnrs conditions) make-i/o-port-error))
-  (define i/o-port-error? (@@ (rnrs conditions) i/o-port-error?))
-  (define i/o-error-port (@@ (rnrs conditions) i/o-error-port))
+  ;; Condition types that are used by (rnrs files), (rnrs io ports), and
+  ;; (rnrs io simple).  These are defined here so as to be easily shareable by
+  ;; these three libraries.
+  
+  (define-condition-type &i/o &error make-i/o-error i/o-error?)
+  (define-condition-type &i/o-read &i/o make-i/o-read-error i/o-read-error?)
+  (define-condition-type &i/o-write &i/o make-i/o-write-error i/o-write-error?)
+  (define-condition-type &i/o-invalid-position
+    &i/o make-i/o-invalid-position-error i/o-invalid-position-error?
+    (position i/o-error-position))
+  (define-condition-type &i/o-filename 
+    &i/o make-i/o-filename-error i/o-filename-error?
+    (filename i/o-error-filename))
+  (define-condition-type &i/o-file-protection
+    &i/o-filename make-i/o-file-protection-error i/o-file-protection-error?)
+  (define-condition-type &i/o-file-is-read-only
+    &i/o-file-protection make-i/o-file-is-read-only-error 
+    i/o-file-is-read-only-error?)
+  (define-condition-type &i/o-file-already-exists
+    &i/o-filename make-i/o-file-already-exists-error 
+    i/o-file-already-exists-error?)
+  (define-condition-type &i/o-file-does-not-exist
+    &i/o-filename make-i/o-file-does-not-exist-error
+    i/o-file-does-not-exist-error?)
+  (define-condition-type &i/o-port &i/o make-i/o-port-error i/o-port-error?
+    (port i/o-error-port))
 )
diff --git a/module/rnrs/io/ports.scm b/module/rnrs/io/ports.scm
index 04dabe6..dd6852a 100644
--- a/module/rnrs/io/ports.scm
+++ b/module/rnrs/io/ports.scm
@@ -26,34 +26,82 @@
 ;;;
 ;;; Code:
 
-(define-module (rnrs io ports)
-  #:version (6)
-  #:re-export (eof-object? port? input-port? output-port?)
-  #:export (eof-object
-
-           ;; input & output ports
-           port-transcoder binary-port? transcoded-port
-           port-position set-port-position!
-           port-has-port-position? port-has-set-port-position!?
-           call-with-port
-
-           ;; input ports
-           open-bytevector-input-port
-           open-string-input-port
-           make-custom-binary-input-port
-
-           ;; binary input
-           get-u8 lookahead-u8
-           get-bytevector-n get-bytevector-n!
-           get-bytevector-some get-bytevector-all
-
-           ;; output ports
-           open-bytevector-output-port
-           open-string-output-port
-           make-custom-binary-output-port
-
-           ;; binary output
-           put-u8 put-bytevector))
+(library (rnrs io ports (6))
+  (export eof-object eof-object?
+
+          ;; auxiliary types
+          file-options buffer-mode buffer-mode?
+          eol-style native-eol-style error-handling-mode
+          make-transcoder transcoder-codec native-transcoder
+          latin-1-codec utf-8-codec utf-16-codec
+           
+          ;; input & output ports
+          port? input-port? output-port?           
+          port-transcoder binary-port? transcoded-port
+          port-position set-port-position!
+          port-has-port-position? port-has-set-port-position!?
+          call-with-port close-port
+
+          ;; input ports
+          open-bytevector-input-port
+          open-string-input-port
+          open-file-input-port
+          make-custom-binary-input-port
+
+          ;; binary input
+          get-u8 lookahead-u8
+          get-bytevector-n get-bytevector-n!
+          get-bytevector-some get-bytevector-all
+
+          ;; output ports
+          open-bytevector-output-port
+          open-string-output-port
+          open-file-output-port
+          make-custom-binary-output-port
+          call-with-bytevector-output-port
+          call-with-string-output-port
+          make-custom-textual-output-port
+          flush-output-port
+           
+          ;; binary output
+          put-u8 put-bytevector
+
+          ;; textual input
+          get-char get-datum get-line get-string-all lookahead-char
+           
+          ;; textual output
+          put-char put-datum put-string
+
+          ;; standard ports
+          standard-input-port standard-output-port standard-error-port
+
+          ;; condition types
+          &i/o i/o-error? make-i/o-error
+          &i/o-read i/o-read-error? make-i/o-read-error
+          &i/o-write i/o-write-error? make-i/o-write-error
+          &i/o-invalid-position i/o-invalid-position-error?
+          make-i/o-invalid-position-error
+          &i/o-filename i/o-filename-error? make-i/o-filename-error
+          i/o-error-filename
+          &i/o-file-protection i/o-file-protection-error?
+          make-i/o-file-protection-error
+          &i/o-file-is-read-only i/o-file-is-read-only-error?
+          make-i/o-file-is-read-only-error
+          &i/o-file-already-exists i/o-file-already-exists-error?
+          make-i/o-file-already-exists-error
+          &i/o-file-does-not-exist i/o-file-does-not-exist-error?
+          make-i/o-file-does-not-exist-error
+          &i/o-port i/o-port-error? make-i/o-port-error
+          i/o-error-port)
+  (import (only (rnrs base) assertion-violation)
+          (rnrs enums)
+          (rnrs records syntactic)
+          (rnrs exceptions)
+          (rnrs conditions)
+          (rnrs files) ;for the condition types
+          (srfi srfi-8)
+          (ice-9 rdelim)
+          (except (guile) raise))
 
 (load-extension (string-append "libguile-" (effective-version))
                 "scm_init_r6rs_ports")
@@ -61,6 +109,73 @@
 
 \f
 ;;;
+;;; Auxiliary types
+;;;
+
+(define-enumeration file-option
+  (no-create no-fail no-truncate)
+  file-options)
+
+(define-enumeration buffer-mode
+  (none line block)
+  buffer-modes)
+
+(define (buffer-mode? symbol)
+  (and (memq symbol '(none line block))))
+
+(define-enumeration eol-style
+  (lf cr crlf nel crnel ls)
+  eol-styles)
+
+(define (native-eol-style)
+  (eol-style lf))
+
+(define-enumeration error-handling-mode
+  (ignore raise replace)
+  error-handling-modes)
+
+(define-record-type (transcoder %make-transcoder transcoder?)
+  (fields codec eol-style error-handling-mode))
+
+(define* (make-transcoder codec
+                          #:optional
+                          (eol-style (native-eol-style))
+                          (handling-mode (error-handling-mode replace)))
+  (%make-transcoder codec eol-style handling-mode))
+
+(define (native-transcoder)
+  (make-transcoder (or (fluid-ref %default-port-encoding)
+                       (latin-1-codec))))
+
+(define (latin-1-codec)
+  "ISO-8859-1")
+
+(define (utf-8-codec)
+  "UTF-8")
+
+(define (utf-16-codec)
+  "UTF-16")
+
+(define (with-i/o-filename-conditions filename thunk)
+  (catch 'system-error
+         thunk
+         (lambda args
+           (let ((errno (system-error-errno args)))
+             (let ((construct-condition
+                    (cond ((= errno EACCES)
+                           make-i/o-file-protection-error)
+                          ((= errno EEXIST)
+                           make-i/o-file-already-exists-error)
+                          ((= errno ENOENT)
+                           make-i/o-file-does-not-exist-error)
+                          ((= errno EROFS)
+                           make-i/o-file-is-read-only-error)
+                          (else
+                           make-i/o-filename-error))))
+               (raise (construct-condition filename)))))))
+
+\f
+;;;
 ;;; Input and output ports.
 ;;;
 
@@ -71,9 +186,6 @@
   ;; So far, we don't support transcoders other than the binary transcoder.
   #t)
 
-(define (transcoded-port port)
-  (error "port transcoders are not supported" port))
-
 (define (port-position port)
   "Return the offset (an integer) indicating where the next octet will be
 read from/written to in @var{port}."
@@ -100,19 +212,33 @@ read from/written to in @var{port}."
 (define (call-with-port port proc)
   "Call @var{proc}, passing it @var{port} and closing @var{port} upon exit of
 @var{proc}.  Return the return values of @var{proc}."
-  (dynamic-wind
-      (lambda ()
-        #t)
-      (lambda ()
-        (proc port))
-      (lambda ()
-        (close-port port))))
+  (call-with-values
+      (lambda () (proc port))
+    (lambda vals
+      (close-port port)
+      (apply values vals))))
+
+(define* (call-with-bytevector-output-port proc #:optional (transcoder #f))
+  (receive (port extract) (open-bytevector-output-port transcoder)
+    (call-with-port port proc)
+    (extract)))
 
 (define (open-string-input-port str)
   "Open an input port that will read from @var{str}."
   (with-fluids ((%default-port-encoding "UTF-8"))
     (open-input-string str)))
 
+(define* (open-file-input-port filename
+                               #:optional
+                               (file-options (file-options))
+                               (buffer-mode (buffer-mode block))
+                               maybe-transcoder)
+  (let ((port (with-i/o-filename-conditions filename
+                (lambda () (open filename O_RDONLY)))))
+    (cond (maybe-transcoder
+           (set-port-encoding! port (transcoder-codec maybe-transcoder))))
+    port))
+
 (define (open-string-output-port)
   "Return two values: an output port that will collect characters written to it
 as a string, and a thunk to retrieve the characters associated with that port."
@@ -121,4 +247,88 @@ as a string, and a thunk to retrieve the characters associated with that port."
     (values port
             (lambda () (get-output-string port)))))
 
+(define* (open-file-output-port filename
+                                #:optional
+                                (file-options (file-options))
+                                (buffer-mode (buffer-mode block))
+                                maybe-transcoder)
+  (let* ((flags (logior O_WRONLY
+                        (if (enum-set-member? 'no-create file-options)
+                            0
+                            O_CREAT)
+                        (if (enum-set-member? 'no-truncate file-options)
+                            0
+                            O_TRUNC)))
+         (port (with-i/o-filename-conditions filename
+                 (lambda () (open filename flags)))))
+    (cond (maybe-transcoder
+           (set-port-encoding! port (transcoder-codec maybe-transcoder))))
+    port))
+
+(define (call-with-string-output-port proc)
+  "Call @var{proc}, passing it a string output port. When @var{proc} returns,
+return the characters accumulated in that port."
+  (let ((port (open-output-string)))
+    (proc port)
+    (get-output-string port)))
+
+(define (make-custom-textual-output-port id
+                                         write!
+                                         get-position
+                                         set-position!
+                                         close)
+  (make-soft-port (vector (lambda (c) (write! (string c) 0 1))
+                          (lambda (s) (write! s 0 (string-length s)))
+                          #f ;flush
+                          #f ;read character
+                          close)
+                  "w"))
+
+(define (flush-output-port port)
+  (force-output port))
+
+(define (put-char port char)
+  (write-char char port))
+
+(define (put-datum port datum)
+  (write datum port))
+
+(define* (put-string port s #:optional start count)
+  (cond ((not (string? s))
+         (assertion-violation 'put-string "expected string" s))
+        ((and start count)
+         (display (substring/shared s start (+ start count)) port))
+        (start
+         (display (substring/shared s start (string-length s)) port))
+        (else
+         (display s port))))
+
+(define (get-char port)
+  (read-char port))
+
+(define (get-datum port)
+  (read port))
+
+(define (get-line port)
+  (read-line port 'trim))
+
+(define (get-string-all port)
+  (read-delimited "" port 'concat))
+
+(define (lookahead-char port)
+  (peek-char port))
+
+
+\f
+(define (standard-input-port)
+  (dup->inport 0))
+
+(define (standard-output-port)
+  (dup->outport 1))
+
+(define (standard-error-port)
+  (dup->outport 2))
+
+)
+
 ;;; ports.scm ends here
diff --git a/module/rnrs/io/simple.scm b/module/rnrs/io/simple.scm
index 6afae14..59e614d 100644
--- a/module/rnrs/io/simple.scm
+++ b/module/rnrs/io/simple.scm
@@ -83,15 +83,16 @@
 	  i/o-port-error?
 	  i/o-error-port)	  
 
-  (import (only (rnrs io ports) eof-object 
-		                eof-object? 
- 
-                                input-port? 
-				output-port?)
+  (import (only (rnrs io ports)
+                call-with-port
+                open-file-input-port
+                open-file-output-port
+                eof-object 
+                eof-object? 
+                
+                input-port? 
+                output-port?)
           (only (guile) @@
-                        call-with-input-file
-			call-with-output-file
-
 			current-input-port
 			current-output-port
 			current-error-port
@@ -113,61 +114,13 @@
 			display
 			write)
 	  (rnrs base (6))
-	  (rnrs conditions (6)))
-
-  (define &i/o (@@ (rnrs conditions) &i/o))
-  (define make-i/o-error (@@ (rnrs conditions) make-i/o-error))
-  (define i/o-error? (@@ (rnrs conditions) i/o-error?))
-
-  (define &i/o-read (@@ (rnrs conditions) &i/o-read))
-  (define make-i/o-read-error (@@ (rnrs conditions) make-i/o-read-error))
-  (define i/o-read-error? (@@ (rnrs conditions) i/o-read-error?))
-
-  (define &i/o-write (@@ (rnrs conditions) &i/o-write))
-  (define make-i/o-write-error (@@ (rnrs conditions) make-i/o-write-error))
-  (define i/o-write-error? (@@ (rnrs conditions) i/o-write-error?))
-
-  (define &i/o-invalid-position (@@ (rnrs conditions) &i/o-invalid-position))
-  (define make-i/o-invalid-position-error 
-    (@@ (rnrs conditions) make-i/o-invalid-position-error))
-  (define i/o-invalid-position-error? 
-    (@@ (rnrs conditions) i/o-invalid-position-error?))
-  (define i/o-error-position (@@ (rnrs conditions) i/o-error-position))
-
-  (define &i/o-filename (@@ (rnrs conditions) &i/o-filename))
-  (define make-i/o-filename-error 
-    (@@ (rnrs conditions) make-i/o-filename-error))
-  (define i/o-filename-error? (@@ (rnrs conditions) i/o-filename-error?))
-  (define i/o-error-filename (@@ (rnrs conditions) i/o-error-filename))
-
-  (define &i/o-file-protection (@@ (rnrs conditions) &i/o-file-protection))
-  (define make-i/o-file-protection-error 
-    (@@ (rnrs conditions) make-i/o-file-protection-error))
-  (define i/o-file-protection-error? 
-    (@@ (rnrs conditions) i/o-file-protection-error?))
-
-  (define &i/o-file-is-read-only (@@ (rnrs conditions) &i/o-file-is-read-only))
-  (define make-i/o-file-is-read-only-error
-    (@@ (rnrs conditions) make-i/o-file-is-read-only-error))
-  (define i/o-file-is-read-only-error?
-    (@@ (rnrs conditions) i/o-file-is-read-only-error?))
-
-  (define &i/o-file-already-exists 
-    (@@ (rnrs conditions) &i/o-file-already-exists))
-  (define make-i/o-file-already-exists-error
-    (@@ (rnrs conditions) make-i/o-file-already-exists-error))
-  (define i/o-file-already-exists-error?
-    (@@ (rnrs conditions) i/o-file-already-exists-error?))
-
-  (define &i/o-file-does-not-exist
-    (@@ (rnrs conditions) &i/o-file-does-not-exist))
-  (define make-i/o-file-does-not-exist-error
-    (@@ (rnrs conditions) make-i/o-file-does-not-exist-error))
-  (define i/o-file-does-not-exist-error?
-    (@@ (rnrs conditions) i/o-file-does-not-exist-error?))
-
-  (define &i/o-port (@@ (rnrs conditions) &i/o-port))
-  (define make-i/o-port-error (@@ (rnrs conditions) make-i/o-port-error))
-  (define i/o-port-error? (@@ (rnrs conditions) i/o-port-error?))
-  (define i/o-error-port (@@ (rnrs conditions) i/o-error-port))
+          (rnrs files (6)) ;for the condition types
+          )
+
+  (define (call-with-input-file filename proc)
+    (call-with-port (open-file-input-port filename) proc))
+
+  (define (call-with-output-file filename proc)
+    (call-with-port (open-file-output-port filename) proc))
+  
 )
diff --git a/test-suite/tests/r6rs-ports.test b/test-suite/tests/r6rs-ports.test
index 7d80ed7..7d746ca 100644
--- a/test-suite/tests/r6rs-ports.test
+++ b/test-suite/tests/r6rs-ports.test
@@ -395,6 +395,14 @@
         (put-bytevector port source)
         (and (bytevector=? (get-content) source)
              (bytevector=? (get-content) (make-bytevector 0))))))
+    
+  (pass-if "open-bytevector-output-port [extract after close]"
+    (let-values (((port get-content)
+                  (open-bytevector-output-port)))
+      (let ((source (make-bytevector 12345 #xFE)))
+        (put-bytevector port source)
+        (close-port port)
+        (bytevector=? (get-content) source))))
 
   (pass-if "open-bytevector-output-port [put-u8]"
     (let-values (((port get-content)
@@ -489,6 +497,29 @@
            (not eof?)
            (bytevector=? sink source)))))
 
+(with-test-prefix "8.2.6  Input and output ports"
+  (pass-if "transcoded-port [output]"
+    (let ((s "Hello\nÄÖÜ"))
+      (bytevector=?
+       (string->utf8 s)
+       (call-with-bytevector-output-port
+         (lambda (bv-port)
+           (call-with-port (transcoded-port bv-port (make-transcoder (utf-8-codec)))
+             (lambda (utf8-port)
+               (put-string utf8-port s))))))))
+  (pass-if "transcoded-port [input]"
+    (let ((s "Hello\nÄÖÜ"))
+      (string=?
+       s
+       (get-string-all
+        (transcoded-port (open-bytevector-input-port (string->utf8 s))
+                         (make-transcoder (utf-8-codec)))))))
+  (pass-if "transcoded-port [input line]"
+    (string=? "ÄÖÜ"
+              (get-line (transcoded-port
+                         (open-bytevector-input-port (string->utf8 "ÄÖÜ\nFooBar"))
+                         (make-transcoder (utf-8-codec)))))))
+
 ;;; Local Variables:
 ;;; mode: scheme
 ;;; End:
-- 
tg: (c05ce90..) t/rnrs-io-ports (depends on: master t/r6rs-exception-print)

[-- Attachment #3: Type: text/plain, Size: 62 bytes --]


Cheers, Rotty
-- 
Andreas Rottmann -- <http://rotty.yi.org/>

^ permalink raw reply related	[flat|nested] 19+ messages in thread

end of thread, other threads:[~2010-11-25 21:15 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-11-15 21:45 Some work on the R6RS I/O libraries Andreas Rottmann
2010-11-19 23:59 ` Ludovic Courtès
2010-11-20 17:40   ` [PATCH 1/4] Fix missing port-table locking and bytevector output port segfault Andreas Rottmann
2010-11-20 22:43     ` Ludovic Courtès
2010-11-20 17:40   ` [PATCH 2/4] Add implementation of "transcoded ports" Andreas Rottmann
2010-11-20 22:52     ` Ludovic Courtès
2010-11-21 22:07       ` Andreas Rottmann
2010-11-21 22:17         ` [PATCH 1/4] Turn `(rnrs io ports)' into an R6RS library Andreas Rottmann
2010-11-21 22:17         ` [PATCH 2/4] Reorganize the R6RS I/O condition types Andreas Rottmann
2010-11-21 22:17         ` [PATCH 3/4] Work towards a more complete implementation of `(rnrs io ports)' Andreas Rottmann
2010-11-23 21:13           ` Ludovic Courtès
2010-11-23 23:44             ` Andreas Rottmann
2010-11-24 20:24               ` Ludovic Courtès
2010-11-21 22:17         ` [PATCH 4/4] Add implementation of "transcoded ports" Andreas Rottmann
2010-11-24 22:29           ` Ludovic Courtès
2010-11-25  0:08             ` Andreas Rottmann
2010-11-25 21:15               ` Ludovic Courtès
2010-11-20 17:40   ` [PATCH 3/4] Reorganize the R6RS I/O condition types Andreas Rottmann
2010-11-20 17:40   ` [PATCH 4/4] Work towards a more complete implementation of `(rnrs io ports)' Andreas Rottmann

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).