ports.c, ports.h (scm_c_read, scm_c_write): New functions.
[bpt/guile.git] / libguile / ports.c
index 84df7e5..fd73620 100644 (file)
@@ -1,4 +1,4 @@
-/*     Copyright (C) 1995,1996,1997,1998,1999, 2000 Free Software Foundation, Inc.
+/*     Copyright (C) 1995,1996,1997,1998,1999, 2000, 2001 Free Software Foundation, Inc.
  * 
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -47,6 +47,7 @@
 
 #include <stdio.h>
 #include "libguile/_scm.h"
+#include "libguile/eval.h"
 #include "libguile/objects.h"
 #include "libguile/smob.h"
 #include "libguile/chars.h"
@@ -114,7 +115,7 @@ end_input_default (SCM port, int offset)
 {
 }
 
-long 
+scm_bits_t
 scm_make_port_type (char *name,
                    int (*fill_input) (SCM port),
                    void (*write) (SCM port, const void *data, size_t size))
@@ -271,17 +272,47 @@ SCM_DEFINE (scm_char_ready_p, "char-ready?", 0, 1, 0,
 }
 #undef FUNC_NAME
 
+/* move up to read_len chars from port's putback and/or read buffers
+   into memory starting at dest.  returns the number of chars moved.  */
+size_t scm_take_from_input_buffers (SCM port, char *dest, size_t read_len)
+{
+  scm_port *pt = SCM_PTAB_ENTRY (port);
+  size_t chars_read = 0;
+  size_t from_buf = min (pt->read_end - pt->read_pos, read_len);
+
+  if (from_buf > 0)
+    {
+      memcpy (dest, pt->read_pos, from_buf);
+      pt->read_pos += from_buf;
+      chars_read += from_buf;
+      read_len -= from_buf;
+      dest += from_buf;
+    }
+
+  /* if putback was active, try the real input buffer too.  */
+  if (pt->read_buf == pt->putback_buf)
+    {
+      from_buf = min (pt->saved_read_end - pt->saved_read_pos, read_len);
+      if (from_buf > 0)
+       {
+         memcpy (dest, pt->saved_read_pos, from_buf);
+         pt->saved_read_pos += from_buf;
+         chars_read += from_buf;
+       }
+    }
+  return chars_read;
+}
+
 /* Clear a port's read buffers, returning the contents.  */
 SCM_DEFINE (scm_drain_input, "drain-input", 1, 0, 0, 
             (SCM port),
-           "Drains @var{PORT}'s read buffers (including any pushed-back characters)\n"
-           "and returns the contents as a single string.")
+           "Drain @var{port}'s read buffers (including any pushed-back\n"
+           "characters) and returns the content as a single string.")
 #define FUNC_NAME s_scm_drain_input
 {
   SCM result;
   scm_port *pt = SCM_PTAB_ENTRY (port);
   int count;
-  char *dst;
 
   SCM_VALIDATE_OPINPORT (1,port);
 
@@ -290,16 +321,7 @@ SCM_DEFINE (scm_drain_input, "drain-input", 1, 0, 0,
     count += pt->saved_read_end - pt->saved_read_pos;
 
   result = scm_makstr (count, 0);
-  dst = SCM_STRING_CHARS (result);
-
-  while (pt->read_pos < pt->read_end)
-    *dst++ = *(pt->read_pos++);
-  
-  if (pt->read_buf == pt->putback_buf)
-    {
-      while (pt->saved_read_pos < pt->saved_read_end)
-       *dst++ = *(pt->saved_read_pos++);
-    }
+  scm_take_from_input_buffers (port, SCM_STRING_CHARS (result), count);
 
   return result;
 }
@@ -309,10 +331,10 @@ SCM_DEFINE (scm_drain_input, "drain-input", 1, 0, 0,
 /* Standard ports --- current input, output, error, and more(!).  */
 
 SCM_DEFINE (scm_current_input_port, "current-input-port", 0, 0, 0,
-           (),
-           "Returns the current input port.  This is the default port used by many\n"
-            "input procedures.  Initially, @code{current-input-port} returns the\n"
-            "value of @code{???}.")
+           (),
+           "Return the current input port.  This is the default port used\n"
+           "by many input procedures.  Initially, @code{current-input-port}\n"
+           "returns the @dfn{standard input} in Unix and C terminology.")
 #define FUNC_NAME s_scm_current_input_port
 {
   return scm_cur_inp;
@@ -320,10 +342,11 @@ SCM_DEFINE (scm_current_input_port, "current-input-port", 0, 0, 0,
 #undef FUNC_NAME
 
 SCM_DEFINE (scm_current_output_port, "current-output-port", 0, 0, 0,
-           (),
-            "Returns the current output port.  This is the default port used by many\n"
-            "output procedures.  Initially, @code{current-output-port} returns the\n"
-            "value of @code{???}.")
+           (),
+            "Return the current output port.  This is the default port used\n"
+           "by many output procedures.  Initially, \n"
+           "@code{current-output-port} returns the @dfn{standard output} in\n"
+           "Unix and C terminology.")
 #define FUNC_NAME s_scm_current_output_port
 {
   return scm_cur_outp;
@@ -341,9 +364,9 @@ SCM_DEFINE (scm_current_error_port, "current-error-port", 0, 0, 0,
 #undef FUNC_NAME
 
 SCM_DEFINE (scm_current_load_port, "current-load-port", 0, 0, 0,
-           (),
+           (),
            "Return the current-load-port.\n"
-            "The load port is used internally by `primitive-load'.")
+            "The load port is used internally by @code{primitive-load}.")
 #define FUNC_NAME s_scm_current_load_port
 {
   return scm_cur_loadp;
@@ -368,8 +391,8 @@ SCM_DEFINE (scm_set_current_input_port, "set-current-input-port", 1, 0, 0,
 
 
 SCM_DEFINE (scm_set_current_output_port, "set-current-output-port", 1, 0, 0,
-           (SCM port),
-           "Set the current default output port to PORT.")
+           (SCM port),
+           "Set the current default output port to @var{port}.")
 #define FUNC_NAME s_scm_set_current_output_port
 {
   SCM ooutp = scm_cur_outp;
@@ -382,8 +405,8 @@ SCM_DEFINE (scm_set_current_output_port, "set-current-output-port", 1, 0, 0,
 
 
 SCM_DEFINE (scm_set_current_error_port, "set-current-error-port", 1, 0, 0,
-           (SCM port),
-           "Set the current default error port to PORT.")
+           (SCM port),
+           "Set the current default error port to @var{port}.")
 #define FUNC_NAME s_scm_set_current_error_port
 {
   SCM oerrp = scm_cur_errp;
@@ -668,12 +691,57 @@ SCM_DEFINE (scm_close_output_port, "close-output-port", 1, 0, 0,
 }
 #undef FUNC_NAME
 
+SCM_DEFINE (scm_port_for_each, "port-for-each", 1, 0, 0,
+           (SCM proc),
+           "Apply @var{proc} to each port in the Guile port table\n"
+           "in turn.  The return value is unspecified.  More specifically,\n"
+           "@var{proc} is applied exactly once to every port that exists\n"
+           "in the system at the time @var{port-for-each} is invoked.\n"
+           "Changes to the port table while @var{port-for-each} is running\n"
+           "have no effect as far as @var{port-for-each} is concerned.\n") 
+#define FUNC_NAME s_scm_port_for_each
+{
+  int i;
+  SCM ports;
+
+  SCM_VALIDATE_PROC (1, proc);
+
+  /* when pre-emptive multithreading is supported, access to the port
+     table will need to be controlled by a mutex.  */
+
+  /* Even without pre-emptive multithreading, running arbitrary code
+     while scanning the port table is unsafe because the port table
+     can change arbitrarily (from a GC, for example).  So we build a
+     list in advance while blocking the GC. -mvo */
+
+  SCM_DEFER_INTS;
+  scm_block_gc++;
+  ports = SCM_EOL;
+  for (i = 0; i < scm_port_table_size; i++)
+    ports = scm_cons (scm_port_table[i]->port, ports);
+  scm_block_gc--;
+  SCM_ALLOW_INTS;
+
+  while (ports != SCM_EOL)
+    {
+      scm_apply (proc, scm_cons (SCM_CAR (ports), SCM_EOL), SCM_EOL);
+      ports = SCM_CDR (ports);
+    }
+
+  return SCM_UNSPECIFIED;
+}
+#undef FUNC_NAME
+
+#if (SCM_DEBUG_DEPRECATED == 0)
+
 SCM_DEFINE (scm_close_all_ports_except, "close-all-ports-except", 0, 0, 1,
            (SCM ports),
-           "Close all open file ports used by the interpreter\n"
+           "[DEPRECATED] Close all open file ports used by the interpreter\n"
            "except for those supplied as arguments.  This procedure\n"
-           "is intended to be used before an exec call to close file descriptors\n"
-           "which are not needed in the new process.")
+           "was intended to be used before an exec call to close file descriptors\n"
+           "which are not needed in the new process.  However it has the\n"
+           "undesirable side-effect of flushing buffes, so it's deprecated.\n"
+           "Use port-for-each instead.")
 #define FUNC_NAME s_scm_close_all_ports_except
 {
   int i = 0;
@@ -703,6 +771,7 @@ SCM_DEFINE (scm_close_all_ports_except, "close-all-ports-except", 0, 0, 1,
 }
 #undef FUNC_NAME
 
+#endif
 
 \f
 /* Utter miscellany.  Gosh, we should clean this up some time.  */
@@ -735,6 +804,16 @@ SCM_DEFINE (scm_output_port_p, "output-port?", 1, 0, 0,
 }
 #undef FUNC_NAME
 
+SCM_DEFINE (scm_port_p, "port?", 1, 0, 0,
+           (SCM x),
+            "Returns a boolean indicating whether @var{x} is a port.\n"
+           "Equivalent to @code{(or (input-port? X) (output-port? X))}.")
+#define FUNC_NAME s_scm_port_p
+{
+  return SCM_BOOL (SCM_PORTP (x));
+}
+#undef FUNC_NAME
+
 SCM_DEFINE (scm_port_closed_p, "port-closed?", 1, 0, 0,
            (SCM port),
            "Returns @code{#t} if @var{port} is closed or @code{#f} if it is open.")
@@ -884,6 +963,14 @@ scm_puts (const char *s, SCM port)
   scm_lfwrite (s, strlen (s), port);
 }
 
+/* scm_lfwrite
+ *
+ * Currently, this function has an identical implementation to
+ * scm_c_write.  We could have turned it into a macro expanding into a
+ * call to scm_c_write.  However, the implementation is small and
+ * might differ in the future.
+ */
+
 void 
 scm_lfwrite (const char *ptr, scm_sizet size, SCM port)
 {
@@ -899,6 +986,81 @@ scm_lfwrite (const char *ptr, scm_sizet size, SCM port)
     pt->rw_active = SCM_PORT_WRITE;
 }
 
+/* scm_c_read
+ *
+ * Used by an application to read arbitrary number of bytes from an
+ * SCM port.  Same semantics as libc read, except that scm_c_read only
+ * returns less than SIZE bytes if at end-of-file.
+ *
+ * Warning: Doesn't update port line and column counts!  */
+
+scm_sizet
+scm_c_read (SCM port, void *buffer, scm_sizet size)
+{
+  scm_port *pt = SCM_PTAB_ENTRY (port);
+  scm_sizet n_read = 0, n_available;
+
+  if (pt->rw_active == SCM_PORT_WRITE)
+    scm_ptobs[SCM_PTOBNUM (port)].flush (port);
+
+  if (pt->rw_random)
+    pt->rw_active = SCM_PORT_READ;
+
+  if (SCM_READ_BUFFER_EMPTY_P (pt))
+    {
+      if (scm_fill_input (port) == EOF)
+       return 0;
+    }
+  
+  n_available = pt->read_end - pt->read_pos;
+  
+  while (n_available < size)
+    {
+      memcpy (buffer, pt->read_pos, n_available);
+      buffer += n_available;
+      pt->read_pos += n_available;
+      n_read += n_available;
+      
+      if (SCM_READ_BUFFER_EMPTY_P (pt))
+       {
+         if (scm_fill_input (port) == EOF)
+           return n_read;
+       }
+
+      size -= n_available;
+      n_available = pt->read_end - pt->read_pos;
+    }
+
+  memcpy (buffer, pt->read_pos, size);
+  pt->read_pos += size;
+
+  return n_read + size;
+}
+
+/* scm_c_write
+ *
+ * Used by an application to write arbitrary number of bytes to an SCM
+ * port.  Similar semantics as libc write.  However, unlike libc
+ * write, scm_c_write writes the requested number of bytes and has no
+ * return value.
+ *
+ * Warning: Doesn't update port line and column counts!
+ */
+
+void 
+scm_c_write (SCM port, const void *ptr, scm_sizet size)
+{
+  scm_port *pt = SCM_PTAB_ENTRY (port);
+  scm_ptob_descriptor *ptob = &scm_ptobs[SCM_PTOBNUM (port)];
+
+  if (pt->rw_active == SCM_PORT_READ)
+    scm_end_input (port);
+
+  ptob->write (port, ptr, size);
+
+  if (pt->rw_random)
+    pt->rw_active = SCM_PORT_WRITE;
+}
 
 void 
 scm_flush (SCM port)
@@ -1047,7 +1209,7 @@ SCM_DEFINE (scm_peek_char, "peek-char", 0, 1, 0,
 }
 #undef FUNC_NAME
 
-SCM_DEFINE (scm_unread_char, "unread-char", 2, 0, 0,
+SCM_DEFINE (scm_unread_char, "unread-char", 1, 1, 0,
             (SCM cobj, SCM port),
            "Place @var{char} in @var{port} so that it will be read by the\n"
            "next read operation.  If called multiple times, the unread characters\n"
@@ -1084,7 +1246,7 @@ SCM_DEFINE (scm_unread_string, "unread-string", 2, 0, 0,
   else
     SCM_VALIDATE_OPINPORT (2,port);
 
-  scm_ungets (SCM_ROCHARS (str), SCM_STRING_LENGTH (str), port);
+  scm_ungets (SCM_STRING_CHARS (str), SCM_STRING_LENGTH (str), port);
   
   return str;
 }
@@ -1120,8 +1282,8 @@ SCM_DEFINE (scm_seek, "seek", 3, 0, 0,
 
   object = SCM_COERCE_OUTPORT (object);
 
-  off = SCM_NUM2LONG (2,offset);
-  SCM_VALIDATE_INUM_COPY (3,whence,how);
+  off = SCM_NUM2LONG (2, offset);
+  SCM_VALIDATE_INUM_COPY (3, whence, how);
   if (how != SEEK_SET && how != SEEK_CUR && how != SEEK_END)
     SCM_OUT_OF_RANGE (3, whence);
   if (SCM_OPPORTP (object))
@@ -1163,7 +1325,7 @@ SCM_DEFINE (scm_truncate_file, "truncate-file", 1, 1, 0,
   if (SCM_UNBNDP (length))
     {
       /* must supply length if object is a filename.  */
-      if (SCM_ROSTRINGP (object))
+      if (SCM_STRINGP (object))
         SCM_MISC_ERROR("must supply length if OBJECT is a filename",SCM_EOL);
       
       length = scm_seek (object, SCM_INUM0, SCM_MAKINUM (SEEK_CUR));
@@ -1194,9 +1356,9 @@ SCM_DEFINE (scm_truncate_file, "truncate-file", 1, 1, 0,
     }
   else
     {
-      SCM_VALIDATE_ROSTRING (1,object);
-      SCM_COERCE_SUBSTR (object);
-      SCM_SYSCALL (rv = truncate (SCM_ROCHARS (object), c_length));
+      SCM_VALIDATE_STRING (1, object);
+      SCM_STRING_COERCE_0TERMINATION_X (object);
+      SCM_SYSCALL (rv = truncate (SCM_STRING_CHARS (object), c_length));
     }
   if (rv == -1)
     SCM_SYSERROR;
@@ -1206,7 +1368,7 @@ SCM_DEFINE (scm_truncate_file, "truncate-file", 1, 1, 0,
 
 SCM_DEFINE (scm_port_line, "port-line", 1, 0, 0,
             (SCM port),
-           "Return the current line number for PORT.")
+           "Return the current line number for @var{port}.")
 #define FUNC_NAME s_scm_port_line
 {
   port = SCM_COERCE_OUTPORT (port);
@@ -1217,7 +1379,7 @@ SCM_DEFINE (scm_port_line, "port-line", 1, 0, 0,
 
 SCM_DEFINE (scm_set_port_line_x, "set-port-line!", 2, 0, 0,
             (SCM port, SCM line),
-           "Set the current line number for PORT to LINE.")
+           "Set the current line number for @var{port} to @var{line}.")
 #define FUNC_NAME s_scm_set_port_line_x
 {
   port = SCM_COERCE_OUTPORT (port);
@@ -1271,7 +1433,7 @@ SCM_DEFINE (scm_port_filename, "port-filename", 1, 0, 0,
 {
   port = SCM_COERCE_OUTPORT (port);
   SCM_VALIDATE_OPENPORT (1,port);
-  return SCM_PTAB_ENTRY (port)->file_name;
+  return SCM_FILENAME (port);
 }
 #undef FUNC_NAME
 
@@ -1286,7 +1448,8 @@ SCM_DEFINE (scm_set_port_filename_x, "set-port-filename!", 2, 0, 0,
   port = SCM_COERCE_OUTPORT (port);
   SCM_VALIDATE_OPENPORT (1,port);
   /* We allow the user to set the filename to whatever he likes.  */
-  return SCM_PTAB_ENTRY (port)->file_name = filename;
+  SCM_SET_FILENAME (port, filename);
+  return SCM_UNSPECIFIED;
 }
 #undef FUNC_NAME
 
@@ -1324,30 +1487,18 @@ scm_port_print (SCM exp, SCM port, scm_print_state *pstate)
   return 1;
 }
 
-extern void scm_make_fptob ();
-extern void scm_make_stptob ();
-extern void scm_make_sfptob ();
-
 void
 scm_ports_prehistory ()
 {
   scm_numptob = 0;
   scm_ptobs = (scm_ptob_descriptor *) malloc (sizeof (scm_ptob_descriptor));
-  
-  /* WARNING: These scm_newptob calls must be done in this order.
-   * They must agree with the port declarations in tags.h.
-   */
-  /* scm_tc16_fport = */ scm_make_fptob ();
-  /* scm_tc16_pipe was here */ scm_make_fptob (); /* dummy.  */
-  /* scm_tc16_strport = */ scm_make_stptob ();
-  /* scm_tc16_sfport = */ scm_make_sfptob ();
 }
 
 \f
 
 /* Void ports.   */
 
-long scm_tc16_void_port = 0;
+scm_bits_t scm_tc16_void_port = 0;
 
 static int fill_input_void_port (SCM port)
 {
@@ -1386,9 +1537,9 @@ SCM_DEFINE (scm_sys_make_void_port, "%make-void-port", 1, 0, 0,
            "documentation for @code{open-file} in @ref{File Ports}.")
 #define FUNC_NAME s_scm_sys_make_void_port
 {
-  SCM_VALIDATE_ROSTRING (1,mode);
-  SCM_COERCE_SUBSTR (mode);
-  return scm_void_port (SCM_ROCHARS (mode));
+  SCM_VALIDATE_STRING (1, mode);
+  SCM_STRING_COERCE_0TERMINATION_X (mode);
+  return scm_void_port (SCM_STRING_CHARS (mode));
 }
 #undef FUNC_NAME
 
@@ -1405,7 +1556,9 @@ scm_init_ports ()
 
   scm_tc16_void_port = scm_make_port_type ("void", fill_input_void_port, 
                                           write_void_port);
+#ifndef SCM_MAGIC_SNARFER
 #include "libguile/ports.x"
+#endif
 }
 
 /*