Index: ChangeLog
===================================================================
RCS file: /cvsroot/tcl/tcl/ChangeLog,v
retrieving revision 1.627
diff -u -r1.627 ChangeLog
--- ChangeLog	2001/09/21 19:09:03	1.627
+++ ChangeLog	2001/09/27 01:46:48
@@ -1,3 +1,27 @@
+2001-09-26  Andreas Kupries  <andreas_kupries@users.sourceforge.net> 
+
+	* The changes below fix [Bug #462317] where Expect tried to read
+	  more than was in the buffers and then blocked in the OS call as
+	  its pty channel driver provides no blockmodeproc through which
+	  the OS could be notified of blocking-behaviour. Because of this
+	  the general I/O core has to take more care than usual to
+	  preserve the semantics of non-blocking channels.
+
+	* generic/tclIO.c (Tcl_ReadRaw): Do not read from the driver if
+	  the channel is non-blocking and the fileevent causing the read
+	  was generated by a timer. We do not know if there is data
+	  available from the OS. Instead of going to the OS for more and
+	  potentially blocking we simply signal EWOULDBLOCK to the higher
+	  levels to cause the system to wait for true fileevents.
+	  (GetInput): Same as before.
+	  (ChannelTimerProc): Added set and clear of CHANNEL_TIMER_FEV.
+
+	* generic/tclIO.h (CHANNEL_TIMER_FEV): New flag for channels. Is
+	  set if a fileevent was generated by a timer, the channel is not
+	  blocking and the driver did not provide a blockmodeproc. In that
+	  case the I/O core has to be especially careful about going to
+	  the driver for more data.
+
 2001-09-21  Jeff Hobbs  <jeffh@ActiveState.com>
 
 	* generic/tclExecute.c (TclExecuteByteCode): corrected
Index: generic/tclIO.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclIO.c,v
retrieving revision 1.35
diff -u -r1.35 tclIO.c
--- generic/tclIO.c	2001/09/06 09:35:39	1.35
+++ generic/tclIO.c	2001/09/27 01:46:49
@@ -4149,17 +4149,23 @@
                 statePtr->flags &= (~(CHANNEL_BLOCKED));
             }
 
-	    /*
-	     * Now go to the driver to get as much as is possible to
-	     * fill the remaining request. Do all the error handling
-	     * by ourselves.  The code was stolen from 'GetInput' and
-	     * slightly adapted (different return value here).
-	     *
-	     * The case of 'bytesToRead == 0' at this point cannot happen.
-	     */
+	    if ((statePtr->flags & CHANNEL_TIMER_FEV) &&
+		(statePtr->flags & CHANNEL_NONBLOCKING)) {
+	        nread  = -1;
+	        result = EWOULDBLOCK;
+	    } else {
+	      /*
+	       * Now go to the driver to get as much as is possible to
+	       * fill the remaining request. Do all the error handling
+	       * by ourselves.  The code was stolen from 'GetInput' and
+	       * slightly adapted (different return value here).
+	       *
+	       * The case of 'bytesToRead == 0' at this point cannot happen.
+	       */
 
-	    nread = (chanPtr->typePtr->inputProc)(chanPtr->instanceData,
+	      nread = (chanPtr->typePtr->inputProc)(chanPtr->instanceData,
 			  bufPtr + copied, bytesToRead - copied, &result);
+	    }
 	    if (nread > 0) {
 	        /*
 		 * If we get a short read, signal up that we may be
@@ -5185,7 +5191,7 @@
         }
         statePtr->inQueueTail = bufPtr;
     }
-      
+
     /*
      * If EOF is set, we should avoid calling the driver because on some
      * platforms it is impossible to read from a device after EOF.
@@ -5195,8 +5201,14 @@
 	return 0;
     }
 
-    nread = (chanPtr->typePtr->inputProc)(chanPtr->instanceData,
-	    bufPtr->buf + bufPtr->nextAdded, toRead, &result);
+    if ((statePtr->flags & CHANNEL_TIMER_FEV) &&
+	(statePtr->flags & CHANNEL_NONBLOCKING)) {
+        nread = -1;
+        result = EWOULDBLOCK;
+    } else {
+        nread = (chanPtr->typePtr->inputProc)(chanPtr->instanceData,
+		    bufPtr->buf + bufPtr->nextAdded, toRead, &result);
+    }
 
     if (nread > 0) {
 	bufPtr->nextAdded += nread;
@@ -5221,7 +5233,7 @@
 	}
 	Tcl_SetErrno(result);
 	return result;
-    } 
+    }
     return 0;
 }
 
@@ -6711,8 +6723,22 @@
 
 	statePtr->timer = Tcl_CreateTimerHandler(0, ChannelTimerProc,
 		(ClientData) chanPtr);
+
+	/* Set the TIMER flag to notify the higher levels that the
+	 * driver might have no data for us. We do this only if we are
+	 * in non-blocking mode and the driver has no BlockModeProc
+	 * because only then we really don't know if the driver will
+	 * block or not. A similar test is done in "PeekAhead".
+	 */
+
+	if ((statePtr->flags & CHANNEL_NONBLOCKING) &&
+	    (Tcl_ChannelBlockModeProc(chanPtr->typePtr) == NULL)) {
+	    statePtr->flags |= CHANNEL_TIMER_FEV;
+	}
+
 	Tcl_NotifyChannel((Tcl_Channel)chanPtr, TCL_READABLE);
- 
+
+	statePtr->flags &= ~CHANNEL_TIMER_FEV; 
     } else {
 	statePtr->timer = NULL;
 	UpdateInterest(chanPtr);
Index: generic/tclIO.h
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclIO.h,v
retrieving revision 1.3
diff -u -r1.3 tclIO.h
--- generic/tclIO.h	2001/03/30 23:06:40	1.3
+++ generic/tclIO.h	2001/09/27 01:46:50
@@ -296,6 +296,17 @@
 					 * the state of the channel changes. */
 #define CHANNEL_RAW_MODE	(1<<16)	/* When set, notes that the Raw API is
 					 * being used. */
+#define CHANNEL_TIMER_FEV       (1<<17) /* When set the event we are
+					 * notified by is a fileevent
+					 * generated by a timer. We
+					 * don't know if the driver
+					 * has more data and should
+					 * not try to read from it. If
+					 * the system needs more than
+					 * is in the buffers out read
+					 * routines will simulate a
+					 * short read (0 characters
+					 * read) */
 
 /*
  * For each channel handler registered in a call to Tcl_CreateChannelHandler,
