[gnome-builder/wip/gtk4-port: 1701/1774] libide/io: pty terminology was always bullshit




commit 8b375d6e6bc2d962f24a8551eba461c53a442ffe
Author: Christian Hergert <chergert redhat com>
Date:   Tue Jun 28 12:42:36 2022 -0700

    libide/io: pty terminology was always bullshit

 src/libide/foundry/ide-pipeline.c       |  59 ++++++------
 src/libide/foundry/ide-test-manager.c   |   2 +-
 src/libide/io/ide-pty-intercept.c       | 160 +++++++++++++++-----------------
 src/libide/io/ide-pty-intercept.h       |   9 +-
 src/libide/terminal/ide-terminal-util.c |  10 +-
 5 files changed, 110 insertions(+), 130 deletions(-)
---
diff --git a/src/libide/foundry/ide-pipeline.c b/src/libide/foundry/ide-pipeline.c
index c03f3fb75..a5ce8c92b 100644
--- a/src/libide/foundry/ide-pipeline.c
+++ b/src/libide/foundry/ide-pipeline.c
@@ -222,18 +222,17 @@ struct _IdePipeline
   guint   errfmt_seqnum;
 
   /*
-   * The VtePty is used to connect to a VteTerminal. It's basically
-   * just a wrapper around a PTY master. We then add a IdePtyIntercept
-   * to proxy PTY data while allowing us to tap into the content being
-   * transmitted. We can use that to run regexes against and perform
-   * additional error extraction. Finally, pty_slave is the PTY device
-   * we created that will get attached to stdin/stdout/stderr in our
-   * spawned subprocesses. It is a slave to the PTY master owned by
-   * the IdePtyIntercept.
+   * The VtePty is used to connect to a VteTerminal. It's basically just a
+   * wrapper around a PTY consumer. We then add a IdePtyIntercept to proxy
+   * PTY data while allowing us to tap into the content being transmitted.
+   * We can use that to run regexes against and perform additional error
+   * extraction. Finally, pty_producer is the PTY device we created that
+   * will get attached to stdin/stdout/stderr in our spawned subprocesses.
+   * It is a producer to the PTY consumer owned by the IdePtyIntercept.
    */
   VtePty          *pty;
   IdePtyIntercept  intercept;
-  IdePtyFd         pty_slave;
+  IdePtyFd         pty_producer;
 
   /*
    * If the terminal interpreting our Pty has received a terminal
@@ -773,11 +772,11 @@ ide_pipeline_log_observer (IdeBuildLogStream  stream,
 }
 
 static void
-ide_pipeline_intercept_pty_master_cb (const IdePtyIntercept     *intercept,
-                                      const IdePtyInterceptSide *side,
-                                      const guint8              *data,
-                                      gsize                      len,
-                                      gpointer                   user_data)
+ide_pipeline_intercept_pty_consumer_cb (const IdePtyIntercept     *intercept,
+                                        const IdePtyInterceptSide *side,
+                                        const guint8              *data,
+                                        gsize                      len,
+                                        gpointer                   user_data)
 {
   IdePipeline *self = user_data;
 
@@ -1505,7 +1504,7 @@ ide_pipeline_destroy (IdeObject *object)
   g_clear_pointer (&self->message, g_free);
 
   g_clear_object (&self->pty);
-  fd = pty_fd_steal (&self->pty_slave);
+  fd = pty_fd_steal (&self->pty_producer);
 
   if (IDE_IS_PTY_INTERCEPT (&self->intercept))
     ide_pty_intercept_clear (&self->intercept);
@@ -1521,7 +1520,7 @@ ide_pipeline_initable_init (GInitable     *initable,
                             GError       **error)
 {
   IdePipeline *self = (IdePipeline *)initable;
-  IdePtyFd master_fd;
+  IdePtyFd consumer_fd;
 
   IDE_ENTRY;
 
@@ -1551,9 +1550,9 @@ ide_pipeline_initable_init (GInitable     *initable,
 
   vte_pty_set_utf8 (self->pty, TRUE, NULL);
 
-  master_fd = vte_pty_get_fd (self->pty);
+  consumer_fd = vte_pty_get_fd (self->pty);
 
-  if (!ide_pty_intercept_init (&self->intercept, master_fd, NULL))
+  if (!ide_pty_intercept_init (&self->intercept, consumer_fd, NULL))
     {
       g_set_error_literal (error,
                            G_IO_ERROR,
@@ -1563,9 +1562,9 @@ ide_pipeline_initable_init (GInitable     *initable,
     }
 
   ide_pty_intercept_set_callback (&self->intercept,
-                              &self->intercept.master,
-                              ide_pipeline_intercept_pty_master_cb,
-                              self);
+                                  &self->intercept.consumer,
+                                  ide_pipeline_intercept_pty_consumer_cb,
+                                  self);
 
   g_signal_connect_object (self->config,
                            "notify::ready",
@@ -1852,7 +1851,7 @@ ide_pipeline_init (IdePipeline *self)
   self->cancellable = g_cancellable_new ();
 
   self->position = -1;
-  self->pty_slave = -1;
+  self->pty_producer = -1;
 
   self->best_strategy_priority = G_MAXINT;
   self->best_strategy = ide_local_deploy_strategy_new ();
@@ -3022,13 +3021,13 @@ ide_pipeline_attach_pty (IdePipeline           *self,
   g_return_if_fail (IDE_IS_PIPELINE (self));
   g_return_if_fail (IDE_IS_SUBPROCESS_LAUNCHER (launcher));
 
-  if (self->pty_slave == -1)
+  if (self->pty_producer == -1)
     {
-      IdePtyFd master_fd = ide_pty_intercept_get_fd (&self->intercept);
-      self->pty_slave = ide_pty_intercept_create_slave (master_fd, TRUE);
+      IdePtyFd consumer_fd = ide_pty_intercept_get_fd (&self->intercept);
+      self->pty_producer = ide_pty_intercept_create_producer (consumer_fd, TRUE);
     }
 
-  if (self->pty_slave == -1)
+  if (self->pty_producer == -1)
     {
       ide_object_warning (self, _("Pseudo terminal creation failed. Terminal features will be limited."));
       return;
@@ -3041,10 +3040,10 @@ ide_pipeline_attach_pty (IdePipeline           *self,
              G_SUBPROCESS_FLAGS_STDIN_PIPE);
   ide_subprocess_launcher_set_flags (launcher, flags);
 
-  /* Assign slave device */
-  ide_subprocess_launcher_take_stdin_fd (launcher, dup (self->pty_slave));
-  ide_subprocess_launcher_take_stdout_fd (launcher, dup (self->pty_slave));
-  ide_subprocess_launcher_take_stderr_fd (launcher, dup (self->pty_slave));
+  /* Assign producer device */
+  ide_subprocess_launcher_take_stdin_fd (launcher, dup (self->pty_producer));
+  ide_subprocess_launcher_take_stdout_fd (launcher, dup (self->pty_producer));
+  ide_subprocess_launcher_take_stderr_fd (launcher, dup (self->pty_producer));
 
   /* Ensure a terminal type is set */
   ide_subprocess_launcher_setenv (launcher, "TERM", "xterm-256color", FALSE);
diff --git a/src/libide/foundry/ide-test-manager.c b/src/libide/foundry/ide-test-manager.c
index 187fc9ba7..e9d9a27b4 100644
--- a/src/libide/foundry/ide-test-manager.c
+++ b/src/libide/foundry/ide-test-manager.c
@@ -1097,7 +1097,7 @@ ide_test_manager_open_pty (IdeTestManager *self)
   if (self->child_pty == -1)
     {
       VtePty *pty = ide_test_manager_get_pty (self);
-      self->child_pty = ide_pty_intercept_create_slave (vte_pty_get_fd (pty), TRUE);
+      self->child_pty = ide_pty_intercept_create_producer (vte_pty_get_fd (pty), TRUE);
     }
 
   return dup (self->child_pty);
diff --git a/src/libide/io/ide-pty-intercept.c b/src/libide/io/ide-pty-intercept.c
index dacb13647..aa55b4281 100644
--- a/src/libide/io/ide-pty-intercept.c
+++ b/src/libide/io/ide-pty-intercept.c
@@ -83,12 +83,12 @@ _ide_pty_intercept_set_raw (IdePtyFd fd)
  * @consumer_fd: a pty
  * @blocking: use %FALSE to set O_NONBLOCK
  *
- * This creates a new slave to the PTY master @master_fd.
+ * This creates a new producer to the PTY consumer @consumer_fd.
  *
  * This uses grantpt(), unlockpt(), and ptsname() to open a new
- * PTY slave.
+ * PTY producer.
  *
- * Returns: a FD for the slave PTY that should be closed with close().
+ * Returns: a FD for the producer PTY that should be closed with close().
  *   Upon error, %IDE_PTY_FD_INVALID (-1) is returned.
  */
 IdePtyFd
@@ -156,13 +156,6 @@ ide_pty_intercept_create_producer (IdePtyFd consumer_fd,
   return pty_fd_steal (&ret);
 }
 
-IdePtyFd
-ide_pty_intercept_create_slave (IdePtyFd consumer_fd,
-                                gboolean blocking)
-{
-  return ide_pty_intercept_create_producer (consumer_fd, blocking);
-}
-
 /**
  * ide_pty_intercept_create_consumer:
  *
@@ -176,47 +169,40 @@ ide_pty_intercept_create_slave (IdePtyFd consumer_fd,
 IdePtyFd
 ide_pty_intercept_create_consumer (void)
 {
-  g_auto(IdePtyFd) master_fd = IDE_PTY_FD_INVALID;
+  g_auto(IdePtyFd) consumer_fd = IDE_PTY_FD_INVALID;
 
-  master_fd = posix_openpt (O_RDWR | O_NOCTTY | O_NONBLOCK | O_CLOEXEC);
+  consumer_fd = posix_openpt (O_RDWR | O_NOCTTY | O_NONBLOCK | O_CLOEXEC);
 
 #ifndef __linux__
   /* Fallback for operating systems that don't support
    * O_NONBLOCK and O_CLOEXEC when opening.
    */
-  if (master_fd == IDE_PTY_FD_INVALID && errno == EINVAL)
+  if (consumer_fd == IDE_PTY_FD_INVALID && errno == EINVAL)
     {
-      master_fd = posix_openpt (O_RDWR | O_NOCTTY | O_CLOEXEC);
+      consumer_fd = posix_openpt (O_RDWR | O_NOCTTY | O_CLOEXEC);
 
-      if (master_fd == IDE_PTY_FD_INVALID && errno == EINVAL)
+      if (consumer_fd == IDE_PTY_FD_INVALID && errno == EINVAL)
         {
           gint flags;
 
-          master_fd = posix_openpt (O_RDWR | O_NOCTTY);
-          if (master_fd == -1)
+          consumer_fd = posix_openpt (O_RDWR | O_NOCTTY);
+          if (consumer_fd == -1)
             return IDE_PTY_FD_INVALID;
 
-          flags = fcntl (master_fd, F_GETFD, 0);
+          flags = fcntl (consumer_fd, F_GETFD, 0);
           if (flags < 0)
             return IDE_PTY_FD_INVALID;
 
-          if (fcntl (master_fd, F_SETFD, flags | FD_CLOEXEC) < 0)
+          if (fcntl (consumer_fd, F_SETFD, flags | FD_CLOEXEC) < 0)
             return IDE_PTY_FD_INVALID;
         }
 
-      if (!g_unix_set_fd_nonblocking (master_fd, TRUE, NULL))
+      if (!g_unix_set_fd_nonblocking (consumer_fd, TRUE, NULL))
         return IDE_PTY_FD_INVALID;
     }
 #endif
 
-  return pty_fd_steal (&master_fd);
-}
-
-/* Until we can port code over */
-IdePtyFd
-ide_pty_intercept_create_master (void)
-{
-  return ide_pty_intercept_create_consumer ();
+  return pty_fd_steal (&consumer_fd);
 }
 
 static void
@@ -254,15 +240,15 @@ _ide_pty_intercept_out_cb (GIOChannel   *channel,
   g_assert (channel != NULL);
   g_assert (condition & (G_IO_ERR | G_IO_HUP | G_IO_OUT));
 
-  if (channel == self->master.channel)
+  if (channel == self->consumer.channel)
     {
-      us = &self->master;
-      them = &self->slave;
+      us = &self->consumer;
+      them = &self->producer;
     }
   else
     {
-      us = &self->slave;
-      them = &self->master;
+      us = &self->producer;
+      them = &self->consumer;
     }
 
   if ((condition & G_IO_OUT) == 0 ||
@@ -342,15 +328,15 @@ _ide_pty_intercept_in_cb (GIOChannel   *channel,
   g_assert (condition & (G_IO_ERR | G_IO_HUP | G_IO_IN));
   g_assert (IDE_IS_PTY_INTERCEPT (self));
 
-  if (channel == self->master.channel)
+  if (channel == self->consumer.channel)
     {
-      us = &self->master;
-      them = &self->slave;
+      us = &self->consumer;
+      them = &self->producer;
     }
   else
     {
-      us = &self->slave;
-      them = &self->master;
+      us = &self->producer;
+      them = &self->consumer;
     }
 
   g_assert (us->in_watch != 0);
@@ -437,9 +423,9 @@ ide_pty_intercept_set_size (IdePtyIntercept *self,
 
   g_return_val_if_fail (IDE_IS_PTY_INTERCEPT (self), FALSE);
 
-  if (self->master.channel != NULL)
+  if (self->consumer.channel != NULL)
     {
-      IdePtyFd fd = g_io_channel_unix_get_fd (self->master.channel);
+      IdePtyFd fd = g_io_channel_unix_get_fd (self->consumer.channel);
       struct winsize ws = {0};
 
       ws.ws_col = columns;
@@ -480,13 +466,13 @@ _g_io_add_watch_full_with_context (GMainContext   *main_context,
 /**
  * ide_pty_intercept_init:
  * @self: a location of memory to store a #IdePtyIntercept
- * @fd: the PTY master fd, possibly from a #VtePty
+ * @fd: the PTY consumer fd, possibly from a #VtePty
  * @main_context: (nullable): a #GMainContext or %NULL for thread-default
  *
- * Creates a enw #IdePtyIntercept using the PTY master fd @fd.
+ * Creates a enw #IdePtyIntercept using the PTY consumer fd @fd.
  *
- * A new PTY slave is created that will communicate with @fd.
- * Additionally, a new PTY master is created that can communicate
+ * A new PTY producer is created that will communicate with @fd.
+ * Additionally, a new PTY consumer is created that can communicate
  * with another side, and will pass that information to @fd after
  * extracting any necessary information.
  *
@@ -497,8 +483,8 @@ ide_pty_intercept_init (IdePtyIntercept *self,
                         int              fd,
                         GMainContext    *main_context)
 {
-  g_auto(IdePtyFd) slave_fd = IDE_PTY_FD_INVALID;
-  g_auto(IdePtyFd) master_fd = IDE_PTY_FD_INVALID;
+  g_auto(IdePtyFd) producer_fd = IDE_PTY_FD_INVALID;
+  g_auto(IdePtyFd) consumer_fd = IDE_PTY_FD_INVALID;
   struct winsize ws;
 
   g_return_val_if_fail (self != NULL, FALSE);
@@ -507,57 +493,57 @@ ide_pty_intercept_init (IdePtyIntercept *self,
   memset (self, 0, sizeof *self);
   self->magic = IDE_PTY_INTERCEPT_MAGIC;
 
-  slave_fd = ide_pty_intercept_create_producer (fd, FALSE);
-  if (slave_fd == IDE_PTY_FD_INVALID)
+  producer_fd = ide_pty_intercept_create_producer (fd, FALSE);
+  if (producer_fd == IDE_PTY_FD_INVALID)
     return FALSE;
 
-  /* Do not perform additional processing on the slave_fd created
-   * from the master we were provided. Otherwise, it will be happening
+  /* Do not perform additional processing on the producer_fd created
+   * from the consumer we were provided. Otherwise, it will be happening
    * twice instead of just once.
    */
-  if (!_ide_pty_intercept_set_raw (slave_fd))
+  if (!_ide_pty_intercept_set_raw (producer_fd))
     return FALSE;
 
-  master_fd = ide_pty_intercept_create_consumer ();
-  if (master_fd == IDE_PTY_FD_INVALID)
+  consumer_fd = ide_pty_intercept_create_consumer ();
+  if (consumer_fd == IDE_PTY_FD_INVALID)
     return FALSE;
 
   /* Copy the win size across */
-  if (ioctl (slave_fd, TIOCGWINSZ, &ws) >= 0)
-    ioctl (master_fd, TIOCSWINSZ, &ws);
+  if (ioctl (producer_fd, TIOCGWINSZ, &ws) >= 0)
+    ioctl (consumer_fd, TIOCSWINSZ, &ws);
 
   if (main_context == NULL)
     main_context = g_main_context_get_thread_default ();
 
-  self->master.read_prio = MASTER_READ_PRIORITY;
-  self->master.write_prio = MASTER_WRITE_PRIORITY;
-  self->slave.read_prio = SLAVE_READ_PRIORITY;
-  self->slave.write_prio = SLAVE_WRITE_PRIORITY;
+  self->consumer.read_prio = MASTER_READ_PRIORITY;
+  self->consumer.write_prio = MASTER_WRITE_PRIORITY;
+  self->producer.read_prio = SLAVE_READ_PRIORITY;
+  self->producer.write_prio = SLAVE_WRITE_PRIORITY;
 
-  self->master.channel = g_io_channel_unix_new (pty_fd_steal (&master_fd));
-  self->slave.channel = g_io_channel_unix_new (pty_fd_steal (&slave_fd));
+  self->consumer.channel = g_io_channel_unix_new (pty_fd_steal (&consumer_fd));
+  self->producer.channel = g_io_channel_unix_new (pty_fd_steal (&producer_fd));
 
-  g_io_channel_set_close_on_unref (self->master.channel, TRUE);
-  g_io_channel_set_close_on_unref (self->slave.channel, TRUE);
+  g_io_channel_set_close_on_unref (self->consumer.channel, TRUE);
+  g_io_channel_set_close_on_unref (self->producer.channel, TRUE);
 
-  g_io_channel_set_encoding (self->master.channel, NULL, NULL);
-  g_io_channel_set_encoding (self->slave.channel, NULL, NULL);
+  g_io_channel_set_encoding (self->consumer.channel, NULL, NULL);
+  g_io_channel_set_encoding (self->producer.channel, NULL, NULL);
 
-  g_io_channel_set_buffer_size (self->master.channel, CHANNEL_BUFFER_SIZE);
-  g_io_channel_set_buffer_size (self->slave.channel, CHANNEL_BUFFER_SIZE);
+  g_io_channel_set_buffer_size (self->consumer.channel, CHANNEL_BUFFER_SIZE);
+  g_io_channel_set_buffer_size (self->producer.channel, CHANNEL_BUFFER_SIZE);
 
-  self->master.in_watch =
+  self->consumer.in_watch =
     _g_io_add_watch_full_with_context (main_context,
-                                       self->master.channel,
-                                       self->master.read_prio,
+                                       self->consumer.channel,
+                                       self->consumer.read_prio,
                                        G_IO_IN | G_IO_ERR | G_IO_HUP,
                                        _ide_pty_intercept_in_cb,
                                        self, NULL);
 
-  self->slave.in_watch =
+  self->producer.in_watch =
     _g_io_add_watch_full_with_context (main_context,
-                                       self->slave.channel,
-                                       self->slave.read_prio,
+                                       self->producer.channel,
+                                       self->producer.read_prio,
                                        G_IO_IN | G_IO_ERR | G_IO_HUP,
                                        _ide_pty_intercept_in_cb,
                                        self, NULL);
@@ -582,15 +568,15 @@ ide_pty_intercept_clear (IdePtyIntercept *self)
 {
   g_return_if_fail (IDE_IS_PTY_INTERCEPT (self));
 
-  clear_source (&self->slave.in_watch);
-  clear_source (&self->slave.out_watch);
-  g_clear_pointer (&self->slave.channel, g_io_channel_unref);
-  g_clear_pointer (&self->slave.out_bytes, g_bytes_unref);
+  clear_source (&self->producer.in_watch);
+  clear_source (&self->producer.out_watch);
+  g_clear_pointer (&self->producer.channel, g_io_channel_unref);
+  g_clear_pointer (&self->producer.out_bytes, g_bytes_unref);
 
-  clear_source (&self->master.in_watch);
-  clear_source (&self->master.out_watch);
-  g_clear_pointer (&self->master.channel, g_io_channel_unref);
-  g_clear_pointer (&self->master.out_bytes, g_bytes_unref);
+  clear_source (&self->consumer.in_watch);
+  clear_source (&self->consumer.out_watch);
+  g_clear_pointer (&self->consumer.channel, g_io_channel_unref);
+  g_clear_pointer (&self->consumer.out_bytes, g_bytes_unref);
 
   memset (self, 0, sizeof *self);
 }
@@ -599,18 +585,18 @@ ide_pty_intercept_clear (IdePtyIntercept *self)
  * ide_pty_intercept_get_fd:
  * @self: a #IdePtyIntercept
  *
- * Gets a master PTY fd created by the #IdePtyIntercept. This is suitable
- * to use to create a slave fd which can be passed to a child process.
+ * Gets a consumer PTY fd created by the #IdePtyIntercept. This is suitable
+ * to use to create a producer fd which can be passed to a child process.
  *
- * Returns: A FD of a PTY master if successful, otherwise -1.
+ * Returns: A FD of a PTY consumer if successful, otherwise -1.
  */
 IdePtyFd
 ide_pty_intercept_get_fd (IdePtyIntercept *self)
 {
   g_return_val_if_fail (IDE_IS_PTY_INTERCEPT (self), IDE_PTY_FD_INVALID);
-  g_return_val_if_fail (self->master.channel != NULL, IDE_PTY_FD_INVALID);
+  g_return_val_if_fail (self->consumer.channel != NULL, IDE_PTY_FD_INVALID);
 
-  return g_io_channel_unix_get_fd (self->master.channel);
+  return g_io_channel_unix_get_fd (self->consumer.channel);
 }
 
 /**
@@ -632,7 +618,7 @@ ide_pty_intercept_set_callback (IdePtyIntercept         *self,
                                 gpointer                 callback_data)
 {
   g_return_if_fail (IDE_IS_PTY_INTERCEPT (self));
-  g_return_if_fail (side == &self->master || side == &self->slave);
+  g_return_if_fail (side == &self->consumer || side == &self->producer);
 
   side->callback = callback;
   side->callback_data = callback_data;
diff --git a/src/libide/io/ide-pty-intercept.h b/src/libide/io/ide-pty-intercept.h
index 9a1f92748..b920cbe76 100644
--- a/src/libide/io/ide-pty-intercept.h
+++ b/src/libide/io/ide-pty-intercept.h
@@ -57,8 +57,8 @@ struct _IdePtyInterceptSide
 struct _IdePtyIntercept
 {
   gsize               magic;
-  IdePtyInterceptSide master;
-  IdePtyInterceptSide slave;
+  IdePtyInterceptSide consumer;
+  IdePtyInterceptSide producer;
 };
 
 static inline IdePtyFd
@@ -82,11 +82,6 @@ pty_fd_clear (IdePtyFd *fd)
 
 G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC (IdePtyFd, pty_fd_clear)
 
-IDE_AVAILABLE_IN_ALL
-IdePtyFd ide_pty_intercept_create_master (void);
-IDE_AVAILABLE_IN_ALL
-IdePtyFd ide_pty_intercept_create_slave  (IdePtyFd                 master_fd,
-                                          gboolean                 blocking);
 IDE_AVAILABLE_IN_ALL
 IdePtyFd ide_pty_intercept_create_consumer (void);
 IDE_AVAILABLE_IN_ALL
diff --git a/src/libide/terminal/ide-terminal-util.c b/src/libide/terminal/ide-terminal-util.c
index fba13af4d..49f07ef06 100644
--- a/src/libide/terminal/ide-terminal-util.c
+++ b/src/libide/terminal/ide-terminal-util.c
@@ -31,16 +31,16 @@
 
 #include "ide-terminal-util.h"
 
-gint
+int
 ide_vte_pty_create_producer (VtePty *pty)
 {
-  gint master_fd;
+  int consumer_fd;
 
   g_return_val_if_fail (VTE_IS_PTY (pty), IDE_PTY_FD_INVALID);
 
-  master_fd = vte_pty_get_fd (pty);
-  if (master_fd == IDE_PTY_FD_INVALID)
+  consumer_fd = vte_pty_get_fd (pty);
+  if (consumer_fd == IDE_PTY_FD_INVALID)
     return IDE_PTY_FD_INVALID;
 
-  return ide_pty_intercept_create_slave (master_fd, TRUE);
+  return ide_pty_intercept_create_producer (consumer_fd, TRUE);
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]