[PR,guile/21104] Fix GDB build failure with Guile 2.2

Message ID CADPb22RNWRkNvHxpVuUG8yb_F87jKzeZY8zE6YQ02AEq0TyS0Q@mail.gmail.com
State New, archived
Headers

Commit Message

Terekhov, Mikhail via Gdb-patches Feb. 6, 2018, 6:58 p.m. UTC
  Guile folk. Any comments?

This patch gets gdb building again with guile 2.2.
Regression tested with guile 2.0.{13,14} and 2.2.3 on amd64-linux.

In time I think we could delete Guile 2.0 support and that would
simplify the code, but no rush.
[And if someone wants to redo the 2.0 support to further reduced diffs
that may be a possibility.]

2018-02-06  Doug Evans  <dje@google.com>

        PR guile/21104
        * NEWS: Mention guile 2.2 is supported again.
        * configure.ac: Add guile-2.2 back.
        * configure: Regenerate.
        * guile/scm-ports.c (PORTS_V22): New macro.
        (ioscm_memory_port) [!PORTS_V22]: Make read_buf_size,write_buf_size
        !PORTS_V22 only.
        (port_type_t): New type.
        (stdio_port_type): Renamed from stdio_port_desc.
        (stdio_port_type_name): Renamed from stdio_port_desc_name.
        (memory_port_type): Renamed from memory_port_desc.
        (memmory_port_type_name): Renamed from memory_port_desc_name.
        (natural_buf_size) [PORTS_V22]: New variable.
        (ioscm_open_port): New argument stream. All callers updated.
        (ioscm_read, ioscm_write) [PORTS_V22]: New functions.
        (ioscm_init_gdb_stdio_port) [PORTS_V22]: Adapt for use in Guile >= 2.2.
        (gdbscm_is_stdio_port): New function.
        (gdbscm_stdio_port_p): Call it.
        (gdbscm_get_natural_buffer_sizes, gdbscm_memory_port_seek)
        (gdbscm_memory_port_read, gdbscm_memory_port_write) [PORTS_V22]: New
        functions.
        (gdbscm_memory_port_print): Adapt for use in Guile >= 2.2.
        (ioscm_init_memory_port_type): Ditto.
        (ioscm_init_memory_stream): Replaces ioscm_init_memory_port.
        (ioscm_init_memory_port_buffers): New function.
        (gdbscm_open_memory): Update.
        (gdbscm_is_memory_port): Adapt for use in Guile >= 2.2.
        (port_functions) [!PORTS_V22]: Only define Guile functions
        memory-port-read-buffer-size, set-memory-port-read-buffer-size!,
        memory-port-write-buffer-size, set-memory-port-write-buffer-size!
        for Guile < 2.2.
        (gdbscm_initialize_ports): Only initialize out_of_range_buf_size if
        !PORTS_V22.

        doc/
        * guile.texi (Memory Ports in Guile): Mention that these functions
        are only supported in Guile 2.0: memory-port-read-buffer-size,
        set-memory-port-read-buffer-size!, memory-port-write-buffer-size,
        set-memory-port-read-buffer-size!.
  

Comments

Eli Zaretskii Feb. 6, 2018, 7:25 p.m. UTC | #1
> From: "Doug Evans via gdb-patches" <gdb-patches@sourceware.org>
> Date: Tue, 6 Feb 2018 10:58:07 -0800
> Cc: guile-devel@gnu.org
> 
> diff --git a/gdb/NEWS b/gdb/NEWS
> index f69173a245..324f98b217 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -3,6 +3,8 @@
>  
>  *** Changes since GDB 8.1
>  
> +* Guile 2.2 support has been restored.

Do we need this entry?  Maybe I'm missing something, but I see nothing
in NEWS that says we stopped supporting Guile 2.2 at some point, so
"restored" here is surprising.  Maybe we shouldn't say anything about
this in NEWS, as (AFAIU) your work was a fix for a bug, and we don't
announce bugfixes in NEWS.

Thanks.
  
Terekhov, Mikhail via Gdb-patches Feb. 6, 2018, 7:46 p.m. UTC | #2
On Tue, Feb 6, 2018 at 11:25 AM, Eli Zaretskii <eliz@gnu.org> wrote:
>> From: "Doug Evans via gdb-patches" <gdb-patches@sourceware.org>
>> Date: Tue, 6 Feb 2018 10:58:07 -0800
>> Cc: guile-devel@gnu.org
>>
>> diff --git a/gdb/NEWS b/gdb/NEWS
>> index f69173a245..324f98b217 100644
>> --- a/gdb/NEWS
>> +++ b/gdb/NEWS
>> @@ -3,6 +3,8 @@
>>
>>  *** Changes since GDB 8.1
>>
>> +* Guile 2.2 support has been restored.
>
> Do we need this entry?  Maybe I'm missing something, but I see nothing
> in NEWS that says we stopped supporting Guile 2.2 at some point, so
> "restored" here is surprising.  Maybe we shouldn't say anything about
> this in NEWS, as (AFAIU) your work was a fix for a bug, and we don't
> announce bugfixes in NEWS.

Not important enough to spend time on.
Whatever is fine with me.
  

Patch

2018-01-21  Doug Evans  <dje@google.com>

	PR guile/21104
	* NEWS: Mention guile 2.2 is supported again.
	* configure.ac: Add guile-2.2 back.
	* configure: Regenerate.
	* guile/scm-ports.c (PORTS_V22): New macro.
	(ioscm_memory_port) [!PORTS_V22]: Make read_buf_size,write_buf_size
	!PORTS_V22 only.
	(port_type_t): New type.
	(stdio_port_type): Renamed from stdio_port_desc.
	(stdio_port_type_name): Renamed from stdio_port_desc_name.
	(memory_port_type): Renamed from memory_port_desc.
	(memmory_port_type_name): Renamed from memory_port_desc_name.
	(natural_buf_size) [PORTS_V22]: New variable.
	(ioscm_open_port): New argument stream. All callers updated.
	(ioscm_read, ioscm_write) [PORTS_V22]: New functions.
	(ioscm_init_gdb_stdio_port) [PORTS_V22]: Adapt for use in Guile >= 2.2.
	(gdbscm_is_stdio_port): New function.
	(gdbscm_stdio_port_p): Call it.
	(gdbscm_get_natural_buffer_sizes, gdbscm_memory_port_seek)
	(gdbscm_memory_port_read, gdbscm_memory_port_write) [PORTS_V22]: New
	functions.
	(gdbscm_memory_port_print): Adapt for use in Guile >= 2.2.
	(ioscm_init_memory_port_type): Ditto.
	(ioscm_init_memory_stream): Replaces ioscm_init_memory_port.
	(ioscm_init_memory_port_buffers): New function.
	(gdbscm_open_memory): Update.
	(gdbscm_is_memory_port): Adapt for use in Guile >= 2.2.
	(port_functions) [!PORTS_V22]: Only define Guile functions
	memory-port-read-buffer-size, set-memory-port-read-buffer-size!,
	memory-port-write-buffer-size, set-memory-port-write-buffer-size!
	for Guile < 2.2.
	(gdbscm_initialize_ports): Only initialize out_of_range_buf_size if
	!PORTS_V22.

	doc/
	* guile.texi (Memory Ports in Guile): Mention that these functions
	are only supported in Guile 2.0: memory-port-read-buffer-size,
	set-memory-port-read-buffer-size!, memory-port-write-buffer-size,
	set-memory-port-read-buffer-size!.

diff --git a/gdb/NEWS b/gdb/NEWS
index f69173a245..324f98b217 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -3,6 +3,8 @@ 
 
 *** Changes since GDB 8.1
 
+* Guile 2.2 support has been restored.
+
 * 'info proc' now works on running processes on FreeBSD systems and core
   files created on FreeBSD systems.
 
diff --git a/gdb/configure b/gdb/configure
index a61a73f6e2..8657055ee5 100755
--- a/gdb/configure
+++ b/gdb/configure
@@ -10864,7 +10864,7 @@  fi
 
 
 
-try_guile_versions="guile-2.0"
+try_guile_versions="guile-2.0 guile-2.2"
 have_libguile=no
 case "${with_guile}" in
 no)
diff --git a/gdb/configure.ac b/gdb/configure.ac
index e53dda00d2..a454202146 100644
--- a/gdb/configure.ac
+++ b/gdb/configure.ac
@@ -1154,7 +1154,7 @@  AC_MSG_RESULT([$with_guile])
 dnl We check guile with pkg-config.
 AC_PATH_PROG(pkg_config_prog_path, pkg-config, missing)
 
-try_guile_versions="guile-2.0"
+try_guile_versions="guile-2.0 guile-2.2"
 have_libguile=no
 case "${with_guile}" in
 no)
diff --git a/gdb/doc/guile.texi b/gdb/doc/guile.texi
index a5cfe9c435..d71fb85f31 100644
--- a/gdb/doc/guile.texi
+++ b/gdb/doc/guile.texi
@@ -3556,21 +3556,29 @@  inclusive.
 @deffn {Scheme Procedure} memory-port-read-buffer-size memory-port
 Return the size of the read buffer of @code{<gdb:memory-port>}
 @var{memory-port}.
+This function is only available with Guile 2.0. Support for later versions
+of Guile use Guile's builtin buffering and do not need this.
 @end deffn
 
 @deffn {Scheme Procedure} set-memory-port-read-buffer-size! memory-port size
 Set the size of the read buffer of @code{<gdb:memory-port>}
 @var{memory-port} to @var{size}.  The result is unspecified.
+This function is only available with Guile 2.0. Support for later versions
+of Guile use Guile's builtin buffering and do not need this.
 @end deffn
 
 @deffn {Scheme Procedure} memory-port-write-buffer-size memory-port
 Return the size of the write buffer of @code{<gdb:memory-port>}
 @var{memory-port}.
+This function is only available with Guile 2.0. Support for later versions
+of Guile use Guile's builtin buffering and do not need this.
 @end deffn
 
 @deffn {Scheme Procedure} set-memory-port-write-buffer-size! memory-port size
 Set the size of the write buffer of @code{<gdb:memory-port>}
 @var{memory-port} to @var{size}.  The result is unspecified.
+This function is only available with Guile 2.0. Support for later versions
+of Guile use Guile's builtin buffering and do not need this.
 @end deffn
 
 A memory port is closed like any other port, with @code{close-port}.
diff --git a/gdb/guile/scm-ports.c b/gdb/guile/scm-ports.c
index 26361e4a0c..01fe617641 100644
--- a/gdb/guile/scm-ports.c
+++ b/gdb/guile/scm-ports.c
@@ -36,6 +36,13 @@ 
 #endif
 #endif
 
+/* Guile ports radically changed in Guile 2.2.
+   Note: We don't support Guile < 2.0.
+   TODO(dje): Suggest deprecating and then removing Guile 2.0 support
+   at some point in the future.  */
+#define PORTS_V22 (SCM_MAJOR_VERSION > 2 \
+		   || SCM_MAJOR_VERSION == 2 && SCM_MINOR_VERSION >= 2)
+
 /* A ui-file for sending output to Guile.  */
 
 class ioscm_file_port : public ui_file
@@ -66,12 +73,14 @@  typedef struct
      This value is always in the range [0, size].  */
   ULONGEST current;
 
+#if !PORTS_V22
   /* The size of the internal r/w buffers.
      Scheme ports aren't a straightforward mapping to memory r/w.
      Generally the user specifies how much to r/w and all access is
      unbuffered.  We don't try to provide equivalent access, but we allow
      the user to specify these values to help get something similar.  */
   unsigned read_buf_size, write_buf_size;
+#endif
 } ioscm_memory_port;
 
 /* Copies of the original system input/output/error ports.
@@ -80,11 +89,17 @@  static SCM orig_input_port_scm;
 static SCM orig_output_port_scm;
 static SCM orig_error_port_scm;
 
-/* This is the stdio port descriptor, scm_ptob_descriptor.  */
-static scm_t_bits stdio_port_desc;
+#if PORTS_V22
+typedef scm_t_port_type *port_type_t;
+#else
+typedef scm_t_bits port_type_t;
+#endif
+
+/* This is the stdio port type.  */
+static port_type_t stdio_port_type;
 
 /* Note: scm_make_port_type takes a char * instead of a const char *.  */
-static /*const*/ char stdio_port_desc_name[] = "gdb:stdio-port";
+static /*const*/ char stdio_port_type_name[] = "gdb:stdio-port";
 
 /* Names of each gdb port.  */
 static const char input_port_name[] = "gdb:stdin";
@@ -101,11 +116,18 @@  static SCM error_port_scm;
 /* Internal enum for specifying output port.  */
 enum oport { GDB_STDOUT, GDB_STDERR };
 
-/* This is the memory port descriptor, scm_ptob_descriptor.  */
-static scm_t_bits memory_port_desc;
+/* This is the memory port type.  */
+static port_type_t memory_port_type;
 
 /* Note: scm_make_port_type takes a char * instead of a const char *.  */
-static /*const*/ char memory_port_desc_name[] = "gdb:memory-port";
+static /*const*/ char memory_port_type_name[] = "gdb:memory-port";
+
+#if PORTS_V22
+
+/* The maximum values to use for get_natural_buffer_sizes.  */
+static const unsigned natural_buf_size = 16;
+
+#else
 
 /* The default amount of memory to fetch for each read/write request.
    Scheme ports don't provide a way to specify the size of a read,
@@ -120,6 +142,8 @@  static const unsigned default_write_buf_size = 16;
 static const unsigned min_memory_port_buf_size = 1;
 static const unsigned max_memory_port_buf_size = 4096;
 
+#endif
+
 /* "out of range" error message for buf sizes.  */
 static char *out_of_range_buf_size;
 
@@ -132,7 +156,7 @@  static SCM size_keyword;
    Newer versions of Guile (2.1.x) have scm_c_make_port.  */
 
 static SCM
-ioscm_open_port (scm_t_bits port_type, long mode_bits)
+ioscm_open_port (port_type_t port_type, long mode_bits, scm_t_bits stream)
 {
   SCM port;
 
@@ -140,9 +164,13 @@  ioscm_open_port (scm_t_bits port_type, long mode_bits)
   scm_i_scm_pthread_mutex_lock (&scm_i_port_table_mutex);
 #endif
 
+#if PORTS_V22
+  port = scm_c_make_port (port_type, mode_bits, stream);
+#else
   port = scm_new_port_table_entry (port_type);
-
   SCM_SET_CELL_TYPE (port, port_type | mode_bits);
+  SCM_SETSTREAM(port, stream);
+#endif
 
 #if 0 /* TODO: Guile doesn't export this.  What to do?  */
   scm_i_pthread_mutex_unlock (&scm_i_port_table_mutex);
@@ -150,6 +178,23 @@  ioscm_open_port (scm_t_bits port_type, long mode_bits)
 
   return port;
 }
+
+/* Like fputstrn_filtered, but don't escape characters, except nul.
+   Also like fputs_filtered, but a length is specified.  */
+
+static void
+fputsn_filtered (const char *s, size_t size, struct ui_file *stream)
+{
+  size_t i;
+
+  for (i = 0; i < size; ++i)
+    {
+      if (s[i] == '\0')
+	fputs_filtered ("\\000", stream);
+      else
+	fputc_filtered (s[i], stream);
+    }
+}
 
 /* Support for connecting Guile's stdio ports to GDB's stdio ports.  */
 
@@ -184,11 +229,10 @@  ioscm_input_waiting (SCM port)
 
     if (use_poll)
       {
-	/* Guile doesn't export SIGINT hooks like Python does.
-	   For now pass EINTR to scm_syserror, that's what fports.c does.  */
 	if (poll (&pollfd, 1, 0) < 0)
-	  scm_syserror (FUNC_NAME);
-
+	  {
+	    scm_syserror (FUNC_NAME);
+	  }
 	return pollfd.revents & POLLIN ? 1 : 0;
       }
   }
@@ -210,14 +254,79 @@  ioscm_input_waiting (SCM port)
 				      &timeout);
     if (num_found < 0)
       {
-	/* Guile doesn't export SIGINT hooks like Python does.
-	   For now pass EINTR to scm_syserror, that's what fports.c does.  */
         scm_syserror (FUNC_NAME);
       }
     return num_found > 0 && FD_ISSET (fdes, &input_fds);
   }
 }
 
+#if PORTS_V22
+
+static size_t
+ioscm_read (SCM port, SCM dst, size_t start, size_t count)
+{
+  /* Borrowed from libguile/fports.c.  */
+  auto ptr = reinterpret_cast<char*>(SCM_BYTEVECTOR_CONTENTS (dst) + start);
+  ssize_t ret;
+
+  /* If we're called on stdout,stderr, punt.  */
+  if (! scm_is_eq (port, input_port_scm))
+    return 0; /* EOF */
+
+  gdb_flush (gdb_stdout);
+  gdb_flush (gdb_stderr);
+
+ retry:
+  ret = ui_file_read (gdb_stdin, ptr, count);
+  if (ret < 0)
+    {
+      if (errno == EINTR)
+        {
+          scm_async_tick ();
+          goto retry;
+        }
+      if (errno == EWOULDBLOCK || errno == EAGAIN)
+	{
+	  /* See the discussion of non-blocking I/O in the Guile manual.  */
+	  return -1;
+	}
+      scm_syserror ("ioscm_read");
+    }
+  return ret;
+}
+
+static size_t
+ioscm_write (SCM port, SCM src, size_t start, size_t count)
+{
+  /* Borrowed from libguile/fports.c.  */
+  auto ptr = reinterpret_cast<char*>(SCM_BYTEVECTOR_CONTENTS (src) + start);
+  ssize_t ret;
+
+  /* If we're called on stdin, punt.  */
+  if (scm_is_eq (port, input_port_scm))
+    {
+      errno = EIO;
+      scm_syserror("ioscm_write");
+    }
+
+  TRY
+    {
+      if (scm_is_eq (port, error_port_scm))
+	fputsn_filtered (ptr, count, gdb_stderr);
+      else
+	fputsn_filtered (ptr, count, gdb_stdout);
+    }
+  CATCH (except, RETURN_MASK_ALL)
+    {
+      GDBSCM_HANDLE_GDB_EXCEPTION (except);
+    }
+  END_CATCH
+
+  return count;
+}
+
+#else /* !PORTS_V22 */
+
 /* The scm_t_ptob_descriptor.fill_input "method".  */
 
 static int
@@ -245,29 +354,11 @@  ioscm_fill_input (SCM port)
   return *pt->read_buf;
 }
 
-/* Like fputstrn_filtered, but don't escape characters, except nul.
-   Also like fputs_filtered, but a length is specified.  */
-
-static void
-fputsn_filtered (const char *s, size_t size, struct ui_file *stream)
-{
-  size_t i;
-
-  for (i = 0; i < size; ++i)
-    {
-      if (s[i] == '\0')
-	fputs_filtered ("\\000", stream);
-      else
-	fputc_filtered (s[i], stream);
-    }
-}
-
 /* Write to gdb's stdout or stderr.  */
 
 static void
 ioscm_write (SCM port, const void *data, size_t size)
 {
-
   /* If we're called on stdin, punt.  */
   if (scm_is_eq (port, input_port_scm))
     return;
@@ -301,6 +392,8 @@  ioscm_flush (SCM port)
     gdb_flush (gdb_stdout);
 }
 
+#endif
+
 /* Initialize the gdb stdio port type.
 
    N.B. isatty? will fail on these ports, it is only supported for file
@@ -309,13 +402,23 @@  ioscm_flush (SCM port)
 static void
 ioscm_init_gdb_stdio_port (void)
 {
-  stdio_port_desc = scm_make_port_type (stdio_port_desc_name,
-					ioscm_fill_input, ioscm_write);
+  stdio_port_type = scm_make_port_type (stdio_port_type_name,
+#if PORTS_V22
+					ioscm_read,
+#else
+					ioscm_fill_input,
+#endif
+					ioscm_write);
+
+  scm_set_port_input_waiting (stdio_port_type, ioscm_input_waiting);
 
-  scm_set_port_input_waiting (stdio_port_desc, ioscm_input_waiting);
-  scm_set_port_flush (stdio_port_desc, ioscm_flush);
+#if !PORTS_V22
+  scm_set_port_flush (stdio_port_type, ioscm_flush);
+#endif
 }
 
+#if !PORTS_V22
+
 /* Subroutine of ioscm_make_gdb_stdio_port to simplify it.
    Set up the buffers of port PORT.
    MODE_BITS are the mode bits of PORT.  */
@@ -358,6 +461,8 @@  ioscm_init_stdio_buffers (SCM port, long mode_bits)
   pt->write_end = pt->write_buf + pt->write_buf_size;
 }
 
+#endif
+
 /* Create a gdb stdio port.  */
 
 static SCM
@@ -388,23 +493,36 @@  ioscm_make_gdb_stdio_port (int fd)
     }
 
   mode_bits = scm_mode_bits ((char *) mode_str);
-  port = ioscm_open_port (stdio_port_desc, mode_bits);
+  port = ioscm_open_port (stdio_port_type, mode_bits, 0);
 
   scm_set_port_filename_x (port, gdbscm_scm_from_c_string (name));
 
+#if !PORTS_V22
   ioscm_init_stdio_buffers (port, mode_bits);
+#endif
 
   return port;
 }
 
+/* Return non-zero if OBJ is a stdio port.  */
+
+static int
+gdbscm_is_stdio_port (SCM obj)
+{
+  /* This is copied from SCM_FPORTP.  */
+#if PORTS_V22
+  return SCM_PORTP (obj) && SCM_PORT_TYPE (obj) == stdio_port_type;
+#else
+  return !SCM_IMP (obj) && SCM_TYP16 (obj) == stdio_port_type;
+#endif
+}
+
 /* (stdio-port? object) -> boolean */
 
 static SCM
-gdbscm_stdio_port_p (SCM scm)
+gdbscm_stdio_port_p (SCM obj)
 {
-  /* This is copied from SCM_FPORTP.  */
-  return scm_from_bool (!SCM_IMP (scm)
-			&& (SCM_TYP16 (scm) == stdio_port_desc));
+  return scm_from_bool (gdbscm_is_stdio_port (obj));
 }
 
 /* GDB's ports are accessed via functions to keep them read-only.  */
@@ -567,6 +685,94 @@  ioscm_lseek_address (ioscm_memory_port *iomem, LONGEST offset, int whence)
   return 1;
 }
 
+#if PORTS_V22
+
+/* The semantics get weird if the buffer size is larger than the port range,
+   so provide a better default buffer size.  */
+
+static void
+gdbscm_get_natural_buffer_sizes (SCM port, size_t *read_size,
+				 size_t *write_size)
+{
+  ioscm_memory_port *iomem = (ioscm_memory_port *) SCM_STREAM (port);
+
+  size_t size = natural_buf_size;
+  if (iomem->size < size)
+    size = iomem->size;
+  *read_size = *write_size = size;
+}
+
+static scm_t_off
+gdbscm_memory_port_seek (SCM port, scm_t_off offset, int whence)
+{
+  ioscm_memory_port *iomem = (ioscm_memory_port *) SCM_STREAM (port);
+  scm_t_off result;
+  int rc;
+
+  if (ioscm_lseek_address (iomem, offset, whence) == 0)
+    {
+      gdbscm_out_of_range_error (FUNC_NAME, 0,
+				 gdbscm_scm_from_longest (offset),
+				 _("bad seek"));
+    }
+
+  return iomem->current;
+}
+
+static size_t
+gdbscm_memory_port_read (SCM port, SCM dst, size_t start, size_t count)
+{
+  /* Borrowed from libguile/fports.c.  */
+  auto ptr = reinterpret_cast<gdb_byte*>(SCM_BYTEVECTOR_CONTENTS (dst) + start);
+  ioscm_memory_port *iomem = (ioscm_memory_port *) SCM_STREAM (port);
+  size_t to_read;
+
+  /* "current" is the offset of the first byte we want to read.  */
+  gdb_assert (iomem->current <= iomem->size);
+  if (iomem->current == iomem->size)
+    return 0; /* EOF */
+
+  /* Don't read outside the allowed memory range.  */
+  to_read = count;
+  if (to_read > iomem->size - iomem->current)
+    to_read = iomem->size - iomem->current;
+
+  if (target_read_memory (iomem->start + iomem->current, ptr, to_read) != 0)
+    gdbscm_memory_error (FUNC_NAME, _("error reading memory"), SCM_EOL);
+
+  iomem->current += to_read;
+  return to_read;
+}
+
+static size_t
+gdbscm_memory_port_write (SCM port, SCM src, size_t start, size_t count)
+{
+  /* Borrowed from libguile/fports.c.  */
+  auto ptr = reinterpret_cast<const gdb_byte*>(SCM_BYTEVECTOR_CONTENTS (src) +
+					       start);
+  ioscm_memory_port *iomem = (ioscm_memory_port *) SCM_STREAM (port);
+  ssize_t ret;
+
+  /* "current" is the offset of the first byte we want to read.  */
+  gdb_assert (iomem->current <= iomem->size);
+
+  /* There's no way to indicate a short write, so if the request goes past
+     the end of the port's memory range, flag an error.  */
+  if (count > iomem->size - iomem->current)
+    {
+      gdbscm_out_of_range_error (FUNC_NAME, 0, gdbscm_scm_from_ulongest (count),
+				 _("writing beyond end of memory range"));
+    }
+
+  if (target_write_memory (iomem->start + iomem->current, ptr, count) != 0)
+    gdbscm_memory_error (FUNC_NAME, _("error writing memory"), SCM_EOL);
+  iomem->current += count;
+
+  return count;
+}
+
+#else /* !PORTS_V22 */
+
 /* "fill_input" method for memory ports.  */
 
 static int
@@ -850,18 +1056,19 @@  gdbscm_memory_port_free (SCM port)
   return 0;
 }
 
+#endif
+
 /* "print" method for memory ports.  */
 
 static int
 gdbscm_memory_port_print (SCM exp, SCM port, scm_print_state *pstate)
 {
   ioscm_memory_port *iomem = (ioscm_memory_port *) SCM_STREAM (exp);
-  char *type = SCM_PTOBNAME (SCM_PTOBNUM (exp));
 
   scm_puts ("#<", port);
   scm_print_port_mode (exp, port);
   /* scm_print_port_mode includes a trailing space.  */
-  gdbscm_printf (port, "%s %s-%s", type,
+  gdbscm_printf (port, "%s %s-%s", memory_port_type_name,
 		 hex_string (iomem->start), hex_string (iomem->end));
   scm_putc ('>', port);
   return 1;
@@ -872,16 +1079,25 @@  gdbscm_memory_port_print (SCM exp, SCM port, scm_print_state *pstate)
 static void
 ioscm_init_memory_port_type (void)
 {
-  memory_port_desc = scm_make_port_type (memory_port_desc_name,
+  memory_port_type = scm_make_port_type (memory_port_type_name,
+#if PORTS_V22
+					 gdbscm_memory_port_read,
+#else
 					 gdbscm_memory_port_fill_input,
+#endif
 					 gdbscm_memory_port_write);
 
-  scm_set_port_end_input (memory_port_desc, gdbscm_memory_port_end_input);
-  scm_set_port_flush (memory_port_desc, gdbscm_memory_port_flush);
-  scm_set_port_seek (memory_port_desc, gdbscm_memory_port_seek);
-  scm_set_port_close (memory_port_desc, gdbscm_memory_port_close);
-  scm_set_port_free (memory_port_desc, gdbscm_memory_port_free);
-  scm_set_port_print (memory_port_desc, gdbscm_memory_port_print);
+#if PORTS_V22
+  scm_set_port_get_natural_buffer_sizes (memory_port_type,
+					 gdbscm_get_natural_buffer_sizes);
+#else
+  scm_set_port_end_input (memory_port_type, gdbscm_memory_port_end_input);
+  scm_set_port_flush (memory_port_type, gdbscm_memory_port_flush);
+  scm_set_port_free (memory_port_type, gdbscm_memory_port_free);
+  scm_set_port_close (memory_port_type, gdbscm_memory_port_close);
+#endif
+  scm_set_port_seek (memory_port_type, gdbscm_memory_port_seek);
+  scm_set_port_print (memory_port_type, gdbscm_memory_port_print);
 }
 
 /* Helper for gdbscm_open_memory to parse the mode bits.
@@ -921,27 +1137,20 @@  ioscm_parse_mode_bits (const char *func_name, const char *mode)
   return mode_bits;
 }
 
-/* Helper for gdbscm_open_memory to finish initializing the port.
-   The port has address range [start,end).
-   This means that address of 0xff..ff is not accessible.
-   I can live with that.  */
-
-static void
-ioscm_init_memory_port (SCM port, CORE_ADDR start, CORE_ADDR end)
+static scm_t_bits
+ioscm_init_memory_stream (bool buffered, CORE_ADDR start, CORE_ADDR end)
 {
-  scm_t_port *pt;
-  ioscm_memory_port *iomem;
-  int buffered = (SCM_CELL_WORD_0 (port) & SCM_BUF0) == 0;
+  auto iomem = reinterpret_cast<ioscm_memory_port *>(
+    scm_gc_malloc_pointerless (sizeof (ioscm_memory_port), "memory port"));
 
   gdb_assert (start <= end);
 
-  iomem = (ioscm_memory_port *) scm_gc_malloc_pointerless (sizeof (*iomem),
-							   "memory port");
-
   iomem->start = start;
   iomem->end = end;
   iomem->size = end - start;
   iomem->current = 0;
+
+#if !PORTS_V22
   if (buffered)
     {
       iomem->read_buf_size = default_read_buf_size;
@@ -952,6 +1161,24 @@  ioscm_init_memory_port (SCM port, CORE_ADDR start, CORE_ADDR end)
       iomem->read_buf_size = 1;
       iomem->write_buf_size = 1;
     }
+#endif
+
+  return reinterpret_cast<scm_t_bits>(iomem);
+}
+
+#if !PORTS_V22
+
+/* Helper for gdbscm_open_memory to finish initializing the port.
+   The port has address range [start,end).
+   This means that address of 0xff..ff is not accessible.
+   I can live with that.  */
+
+static void
+ioscm_init_memory_port_buffers (SCM port)
+{
+  scm_t_port *pt;
+  auto iomem = reinterpret_cast<const ioscm_memory_port *>(SCM_STREAM (port));
+  bool buffered = (SCM_CELL_WORD_0 (port) & SCM_BUF0) == 0;
 
   pt = SCM_PTAB_ENTRY (port);
   /* Match the expectation of `binary-port?'.  */
@@ -972,8 +1199,6 @@  ioscm_init_memory_port (SCM port, CORE_ADDR start, CORE_ADDR end)
   pt->read_pos = pt->read_end = pt->read_buf;
   pt->write_pos = pt->write_buf;
   pt->write_end = pt->write_buf + pt->write_buf_size;
-
-  SCM_SETSTREAM (port, iomem);
 }
 
 /* Re-initialize a memory port, updating its read/write buffer sizes.
@@ -1041,6 +1266,8 @@  ioscm_reinit_memory_port (SCM port, size_t read_buf_size,
     }
 }
 
+#endif /* !PORTS_V22 */
+
 /* (open-memory [#:mode string] [#:start address] [#:size integer]) -> port
    Return a port that can be used for reading and writing memory.
    MODE is a string, and must be one of "r", "w", or "r+".
@@ -1107,10 +1334,19 @@  gdbscm_open_memory (SCM rest)
     end = ~(CORE_ADDR) 0;
 
   mode_bits = ioscm_parse_mode_bits (FUNC_NAME, mode);
-
-  port = ioscm_open_port (memory_port_desc, mode_bits);
-
-  ioscm_init_memory_port (port, start, end);
+  /* Edge case: empty range -> unbuffered.
+     There's no need to disallow empty ranges, but we need an unbuffered port
+     to get the semantics right.  */
+  if (size == 0)
+    mode_bits |= SCM_BUF0;
+
+  bool buffered = (mode_bits & SCM_BUF0) == 0;
+  auto stream = ioscm_init_memory_stream(buffered, start, end);
+  port = ioscm_open_port (memory_port_type, mode_bits, stream);
+
+#if !PORTS_V22
+  ioscm_init_memory_port_buffers (port);
+#endif
 
   scm_dynwind_end ();
 
@@ -1123,7 +1359,12 @@  gdbscm_open_memory (SCM rest)
 static int
 gdbscm_is_memory_port (SCM obj)
 {
-  return !SCM_IMP (obj) && (SCM_TYP16 (obj) == memory_port_desc);
+  /* This is copied from SCM_FPORTP.  */
+#if PORTS_V22
+  return SCM_PORTP (obj) && SCM_PORT_TYPE (obj) == memory_port_type;
+#else
+  return !SCM_IMP (obj) && SCM_TYP16 (obj) == memory_port_type;
+#endif
 }
 
 /* (memory-port? obj) -> boolean */
@@ -1142,13 +1383,15 @@  gdbscm_memory_port_range (SCM port)
   ioscm_memory_port *iomem;
 
   SCM_ASSERT_TYPE (gdbscm_is_memory_port (port), port, SCM_ARG1, FUNC_NAME,
-		   memory_port_desc_name);
+		   memory_port_type_name);
 
   iomem = (ioscm_memory_port *) SCM_STREAM (port);
   return scm_list_2 (gdbscm_scm_from_ulongest (iomem->start),
 		     gdbscm_scm_from_ulongest (iomem->end));
 }
 
+#if !PORTS_V22
+
 /* (memory-port-read-buffer-size port) -> integer */
 
 static SCM
@@ -1157,7 +1400,7 @@  gdbscm_memory_port_read_buffer_size (SCM port)
   ioscm_memory_port *iomem;
 
   SCM_ASSERT_TYPE (gdbscm_is_memory_port (port), port, SCM_ARG1, FUNC_NAME,
-		   memory_port_desc_name);
+		   memory_port_type_name);
 
   iomem = (ioscm_memory_port *) SCM_STREAM (port);
   return scm_from_uint (iomem->read_buf_size);
@@ -1173,7 +1416,7 @@  gdbscm_set_memory_port_read_buffer_size_x (SCM port, SCM size)
   ioscm_memory_port *iomem;
 
   SCM_ASSERT_TYPE (gdbscm_is_memory_port (port), port, SCM_ARG1, FUNC_NAME,
-		   memory_port_desc_name);
+		   memory_port_type_name);
   SCM_ASSERT_TYPE (scm_is_integer (size), size, SCM_ARG2, FUNC_NAME,
 		   _("integer"));
 
@@ -1199,7 +1442,7 @@  gdbscm_memory_port_write_buffer_size (SCM port)
   ioscm_memory_port *iomem;
 
   SCM_ASSERT_TYPE (gdbscm_is_memory_port (port), port, SCM_ARG1, FUNC_NAME,
-		   memory_port_desc_name);
+		   memory_port_type_name);
 
   iomem = (ioscm_memory_port *) SCM_STREAM (port);
   return scm_from_uint (iomem->write_buf_size);
@@ -1215,7 +1458,7 @@  gdbscm_set_memory_port_write_buffer_size_x (SCM port, SCM size)
   ioscm_memory_port *iomem;
 
   SCM_ASSERT_TYPE (gdbscm_is_memory_port (port), port, SCM_ARG1, FUNC_NAME,
-		   memory_port_desc_name);
+		   memory_port_type_name);
   SCM_ASSERT_TYPE (scm_is_integer (size), size, SCM_ARG2, FUNC_NAME,
 		   _("integer"));
 
@@ -1232,6 +1475,8 @@  gdbscm_set_memory_port_write_buffer_size_x (SCM port, SCM size)
 
   return SCM_UNSPECIFIED;
 }
+
+#endif /* !PORTS_V22 */
 
 /* Initialize gdb ports.  */
 
@@ -1268,6 +1513,7 @@  Return #t if the object is a memory port." },
     "\
 Return the memory range of the port as (start end)." },
 
+#if !PORTS_V22
   { "memory-port-read-buffer-size", 1, 0, 0,
     as_a_scm_t_subr (gdbscm_memory_port_read_buffer_size),
     "\
@@ -1293,6 +1539,7 @@  Set the size of the write buffer for the memory port.\n\
 \n\
   Arguments: port integer\n\
   Returns: unspecified." },
+#endif
 
   END_FUNCTIONS
 };
@@ -1365,9 +1612,11 @@  gdbscm_initialize_ports (void)
   start_keyword = scm_from_latin1_keyword ("start");
   size_keyword = scm_from_latin1_keyword ("size");
 
+#if !PORTS_V22
   /* Error message text for "out of range" memory port buffer sizes.  */
 
   out_of_range_buf_size = xstrprintf ("size not between %u - %u",
 				      min_memory_port_buf_size,
 				      max_memory_port_buf_size);
+#endif
 }