Index: src/buffer.c
===================================================================
RCS file: /cvsroot/cvs/ccvs/src/buffer.c,v
retrieving revision 1.66
diff -u -p -r1.66 buffer.c
--- src/buffer.c	27 Jan 2006 17:15:33 -0000	1.66
+++ src/buffer.c	26 Mar 2006 03:43:36 -0000
@@ -1728,6 +1728,8 @@ struct fd_buffer
     int fd;
     /* Nonzero if the file descriptor is in blocking mode.  */
     int blocking;
+    /* Time to wait for reads and writes to succeed.  */
+    long timeout;
     /* The child process id when fd is a pipe.  */
     pid_t child_pid;
     /* The connection info, when fd is a pipe to a server.  */
@@ -1742,18 +1744,19 @@ static int fd_buffer_get_fd (void *);
 static int fd_buffer_shutdown (struct buffer *);
 
 /* Initialize a buffer built on a file descriptor.  FD is the file
-   descriptor.  INPUT is nonzero if this is for input, zero if this is
+   descriptor.  INPUT is true if this is for input, false if this is
    for output.  MEMORY is the function to call when a memory error
    occurs.  */
 
 struct buffer *
 fd_buffer_initialize (int fd, pid_t child_pid, cvsroot_t *root, bool input,
-                      void (*memory) (struct buffer *))
+                      long timeout, void (*memory) (struct buffer *))
 {
     struct fd_buffer *n;
 
     n = xmalloc (sizeof *n);
     n->fd = fd;
+    n->timeout = timeout;
     n->child_pid = child_pid;
     n->root = root;
     fd_buffer_block (n, true);
@@ -1797,6 +1800,7 @@ fd_buffer_initialize (int fd, pid_t chil
  *
  * RETURNS
  *   errno	On error.
+ *   -3		On timeout.
  *   -1		On EOF.
  *   0		Otherwise.
  *
@@ -1809,7 +1813,7 @@ fd_buffer_input (void *closure, char *da
 		 size_t *got)
 {
     struct fd_buffer *fb = closure;
-    int nbytes;
+    ssize_t nbytes;
 
     assert (need <= size);
 
@@ -1819,10 +1823,14 @@ fd_buffer_input (void *closure, char *da
     {
 	int status;
 	fd_set readfds;
+	struct timeval timeout;
+	struct timeval *timeout_p;
 
 	/* Set non-block.  */
         status = fd_buffer_block (fb, false);
 	if (status != 0) return status;
+	if (fb->timeout) timeout_p = &timeout;
+	else timeout_p = NULL;
 
 	FD_ZERO (&readfds);
 	FD_SET (fb->fd, &readfds);
@@ -1830,16 +1838,29 @@ fd_buffer_input (void *closure, char *da
 	{
 	    int numfds;
 
-	    do {
+	    do
+	    {
+		if (timeout_p)
+		{
+		    timeout.tv_sec = fb->timeout;
+		    timeout.tv_usec = 0;
+		}
+
 		/* This used to select on exceptions too, but as far
 		   as I know there was never any reason to do that and
 		   SCO doesn't let you select on exceptions on pipes.  */
-		numfds = fd_select (fb->fd + 1, &readfds, NULL, NULL, NULL);
+		numfds = fd_select (fb->fd + 1, &readfds, NULL, NULL,
+				    timeout_p);
 		if (numfds < 0 && errno != EINTR)
 		{
 		    status = errno;
 		    goto block_done;
 		}
+		else if (numfds == 0)
+		{
+		    status = -3;
+		    goto block_done;
+		}
 	    } while (numfds < 0);
 
 	    nbytes = read (fb->fd, data + *got, size - *got);
@@ -1917,44 +1938,100 @@ block_done:
 
 
 /* The buffer output function for a buffer built on a file descriptor.  */
-
 static int
 fd_buffer_output (void *closure, const char *data, size_t have, size_t *wrote)
 {
-    struct fd_buffer *fd = closure;
+    struct fd_buffer *fb = closure;
+    ssize_t nbytes;
 
     *wrote = 0;
 
-    while (have > 0)
+    if (fb->blocking)
     {
-	int nbytes;
+	int status;
+	fd_set writefds;
+	struct timeval timeout;
+	struct timeval *timeout_p;
 
-	nbytes = write (fd->fd, data, have);
+	/* Set non-block.  */
+        status = fd_buffer_block (fb, false);
+	if (status != 0) return status;
+	if (fb->timeout) timeout_p = &timeout;
+	else timeout_p = NULL;
 
-	if (nbytes <= 0)
+	FD_ZERO (&writefds);
+	FD_SET (fb->fd, &writefds);
+	do
 	{
-	    if (! fd->blocking
-		&& (nbytes == 0 || blocking_error (errno)))
+	    int numfds;
+
+	    do
+	    {
+		if (timeout_p)
+		{
+		    timeout.tv_sec = fb->timeout;
+		    timeout.tv_usec = 0;
+		}
+
+		/* This used to select on exceptions too, but as far
+		   as I know there was never any reason to do that and
+		   SCO doesn't let you select on exceptions on pipes.  */
+		numfds = fd_select (fb->fd + 1, NULL, &writefds, NULL,
+				    timeout_p);
+		if (numfds < 0 && errno != EINTR)
+		{
+		    status = errno;
+		    goto block_done;
+		}
+		else if (numfds == 0)
+		{
+		    status = -3;
+		    goto block_done;
+		}
+	    } while (numfds < 0);
+
+	    nbytes = write (fb->fd, data + *wrote, have - *wrote);
+
+	    if (nbytes < 0)
 	    {
-		/* A nonblocking write failed to write any data.  Just
-		   return.  */
-		return 0;
+		/* Some error occurred.  */
+		if (!blocking_error (errno))
+		{
+		    status = errno;
+		    break;
+		}
+		/* else Everything's fine, we just didn't get any data.  */
 	    }
 
-	    /* Some sort of error occurred.  */
+	    *wrote += nbytes;
+	} while (*wrote < have);
 
-	    if (nbytes == 0)
-		return EIO;
+block_done:
+	if (status == 0)
+	{
+	    int newstatus;
 
-	    return errno;
+	    /* OK - Reset block.  */
+	    newstatus = fd_buffer_block (fb, true);
+	    if (newstatus) status = newstatus;
 	}
+	return status;
+    }
+
+    /* The above will always return.  Handle non-blocking read.  */
+    nbytes = write (fb->fd, data, have);
 
-	*wrote += nbytes;
-	data += nbytes;
-	have -= nbytes;
+    if (nbytes >= 0)
+    {
+	*wrote = nbytes;
+	return 0;
     }
 
-    return 0;
+    if (blocking_error (errno))
+	/* Everything's fine, we just didn't get any data.  */
+	return 0;
+
+    return errno;
 }
 
 
Index: src/buffer.h
===================================================================
RCS file: /cvsroot/cvs/ccvs/src/buffer.h,v
retrieving revision 1.26
diff -u -p -r1.26 buffer.h
--- src/buffer.h	13 Jul 2005 17:06:35 -0000	1.26
+++ src/buffer.h	26 Mar 2006 03:43:36 -0000
@@ -184,7 +184,7 @@ int buf_count_mem (struct buffer *);
 
 struct buffer *
 fd_buffer_initialize (int fd, pid_t child_pid, cvsroot_t *root, bool input,
-                      void (*memory) (struct buffer *));
+                      long timeout, void (*memory) (struct buffer *));
 
 /* EWOULDBLOCK is not defined by POSIX, but some BSD systems will
    return it, rather than EAGAIN, for nonblocking writes.  */
Index: src/client.c
===================================================================
RCS file: /cvsroot/cvs/ccvs/src/client.c,v
retrieving revision 1.440
diff -u -p -r1.440 client.c
--- src/client.c	26 Feb 2006 21:30:10 -0000	1.440
+++ src/client.c	26 Mar 2006 03:43:36 -0000
@@ -15,11 +15,16 @@
 #endif /* HAVE_CONFIG_H */
 
 #include "cvs.h"
+
+/* GNULIB Headers.  */
 #include "getline.h"
-#include "edit.h"
-#include "buffer.h"
 #include "save-cwd.h"
 
+/* CVS Headers.  */
+#include "buffer.h"
+#include "command_line_opt.h"
+#include "edit.h"
+
 #ifdef CLIENT_SUPPORT
 
 # include "log-buffer.h"
@@ -3431,9 +3436,10 @@ make_bufs_from_fds(int tofd, int fromfd,
 	 * child_pid in there.  In theory, it should be stored in both
 	 * buffers with a ref count...
 	 */
-	*to_server_p = fd_buffer_initialize (tofd, 0, root, false, NULL);
+	*to_server_p = fd_buffer_initialize (tofd, 0, root, false,
+					     connection_timeout, NULL);
 	*from_server_p = fd_buffer_initialize (fromfd, child_pid, root,
-                                               true, NULL);
+                                               true, connection_timeout, NULL);
     }
 }
 #endif /* defined (AUTH_CLIENT_SUPPORT) || defined (SERVER_SUPPORT) || defined (HAVE_KERBEROS) || defined(HAVE_GSSAPI) */
Index: src/command_line_opt.h
===================================================================
RCS file: src/command_line_opt.h
diff -N src/command_line_opt.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/command_line_opt.h	26 Mar 2006 03:43:36 -0000
@@ -0,0 +1,15 @@
+/*
+ * Copyright (C) 2006 The 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
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+extern long connection_timeout;
Index: src/log-buffer.c
===================================================================
RCS file: /cvsroot/cvs/ccvs/src/log-buffer.c,v
retrieving revision 1.16
diff -u -p -r1.16 log-buffer.c
--- src/log-buffer.c	16 Mar 2005 15:43:19 -0000	1.16
+++ src/log-buffer.c	26 Mar 2006 03:43:36 -0000
@@ -384,7 +384,7 @@ log_buffer_rewind (struct buffer *buf)
     }
 
     /* Create a new fd buffer around the log.  */
-    retbuf = fd_buffer_initialize (fd, 0, NULL, true, buf->memory_error);
+    retbuf = fd_buffer_initialize (fd, 0, NULL, true, 0, buf->memory_error);
 
     {
 	struct buffer *tmp;
Index: src/main.c
===================================================================
RCS file: /cvsroot/cvs/ccvs/src/main.c,v
retrieving revision 1.263
diff -u -p -r1.263 main.c
--- src/main.c	25 Feb 2006 07:41:50 -0000	1.263
+++ src/main.c	26 Mar 2006 03:43:37 -0000
@@ -1,7 +1,7 @@
 /*
- * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
+ * Copyright (C) 1986-2006 The Free Software Foundation, Inc.
  *
- * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
+ * Portions Copyright (C) 1998-2006 Derek Price, Ximbiot <http://ximbiot.com>,
  *                                  and others.
  *
  * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
@@ -19,11 +19,17 @@
 
 #include "cvs.h"
 
+/* GNULIB Headers.  */
 #include "closeout.h"
 #include "setenv.h"
 #include "strftime.h"
 #include "xgethostname.h"
 
+/* CVS Headers.  */
+#include "command_line_opt.h"
+
+
+
 const char *program_name;
 const char *program_path;
 const char *cvs_cmd_name;
@@ -43,6 +49,7 @@ int trace = 0;
 int noexec = 0;
 int readonlyfs = 0;
 int logoff = 0;
+long connection_timeout = 0;
 
 /*
  * Zero if compression isn't supported or requested; non-zero to indicate
@@ -526,6 +533,7 @@ main (int argc, char **argv)
 	{"help-options", 0, NULL, 4},
 #ifdef SERVER_SUPPORT
 	{"allow-root", required_argument, NULL, 3},
+	{"timeout", required_argument, NULL, 5},
 #endif /* SERVER_SUPPORT */
         {0, 0, 0, 0}
     };
@@ -650,6 +658,13 @@ main (int argc, char **argv)
 		/* --allow-root */
 		root_allow_add (optarg, gConfigPath);
 		break;
+	    case 5:
+		/* --timeout */
+		connection_timeout = strtol (optarg, &end, 10);
+		if (*end != '\0' || connection_timeout < 0)
+		  error (1, 0,
+"argument to --timeout must be greater than or equal to 0");
+		break;
 #endif /* SERVER_SUPPORT */
 	    case 'Q':
 		really_quiet = 1;
Index: src/server.c
===================================================================
RCS file: /cvsroot/cvs/ccvs/src/server.c,v
retrieving revision 1.455
diff -u -p -r1.455 server.c
--- src/server.c	2 Feb 2006 13:16:03 -0000	1.455
+++ src/server.c	26 Mar 2006 03:43:38 -0000
@@ -11,6 +11,7 @@
 #include "cvs.h"
 
 /* CVS */
+#include "command_line_opt.h"
 #include "edit.h"
 #include "fileattr.h"
 #include "watch.h"
@@ -3707,7 +3708,7 @@ error  \n");
 	error_use_protocol = 0;
 
 	protocol = fd_buffer_initialize (protocol_pipe[1], 0, NULL, false,
-					 protocol_memory_error);
+					 0, protocol_memory_error);
 
 	/* At this point we should no longer be using buf_to_net and
 	   buf_from_net.  Instead, everything should go through
@@ -3844,13 +3845,13 @@ error  \n");
 	}
 
 	stdoutbuf = fd_buffer_initialize (stdout_pipe[0], 0, NULL, true,
-					  input_memory_error);
+					  0, input_memory_error);
 
 	stderrbuf = fd_buffer_initialize (stderr_pipe[0], 0, NULL, true,
-					  input_memory_error);
+					  0, input_memory_error);
 
 	protocol_inbuf = fd_buffer_initialize (protocol_pipe[0], 0, NULL, true,
-					       input_memory_error);
+					       0, input_memory_error);
 
 	set_nonblock (buf_to_net);
 	set_nonblock (stdoutbuf);
@@ -6371,8 +6372,10 @@ server (int argc, char **argv)
     if (!buf_to_net)
     {
 	buf_to_net = fd_buffer_initialize (STDOUT_FILENO, 0, NULL, false,
+					   connection_timeout,
 					   outbuf_memory_error);
 	buf_from_net = fd_buffer_initialize (STDIN_FILENO, 0, NULL, true,
+					     connection_timeout,
 					     outbuf_memory_error);
     }
 
@@ -6445,6 +6448,12 @@ server (int argc, char **argv)
 error ENOMEM Virtual memory exhausted.\n");
 	    break;
 	}
+	if (status == -3)
+	{
+	    buf_output0 (buf_to_net, "E Fatal server error, aborting.\n\
+error ETIMEOUT Connection timed out.\n");
+	    break;
+	}
 	if (status != 0)
 	    break;
 
@@ -7218,8 +7227,10 @@ pserver_authenticate_connection (void)
 
     /* Initialize buffers.  */
     buf_to_net = fd_buffer_initialize (STDOUT_FILENO, 0, NULL, false,
+				       connection_timeout,
 				       outbuf_memory_error);
     buf_from_net = fd_buffer_initialize (STDIN_FILENO, 0, NULL, true,
+				         connection_timeout,
 					 outbuf_memory_error);
 
 #ifdef SO_KEEPALIVE
