Index: ChangeLog
===================================================================
RCS file: /cvsroot/tcl/tcl/ChangeLog,v
retrieving revision 1.416
diff -u -r1.416 ChangeLog
--- ChangeLog	2001/04/09 23:02:21	1.416
+++ ChangeLog	2001/04/09 23:10:12
@@ -1,3 +1,10 @@
+2001-04-10  Andreas Kupries  <andreas_kupries@users.sourceforge.net>
+
+	* unix/tclUnixChan (TclpOpenFileChannel, Tcl_MakeFileChannel):
+	  Extended to detect the type of the opened/specified file
+	  descriptor (socket, tty, ...) and to create the appropriate tcl
+	  channel for it. [Bug #219146, #219269].
+
 2001-04-09  Kevin B. Kenny    <kennykb@acm.org>
 
 	* unix/tcl.m4: Added _REENTRANT to Solaris build so that thread
Index: unix/tclUnixChan.c
===================================================================
RCS file: /cvsroot/tcl/tcl/unix/tclUnixChan.c,v
retrieving revision 1.19
diff -u -r1.19 tclUnixChan.c
--- unix/tclUnixChan.c	2000/10/28 00:29:58	1.19
+++ unix/tclUnixChan.c	2001/04/09 23:10:20
@@ -178,6 +178,7 @@
 static int		CreateSocketAddress _ANSI_ARGS_(
 			    (struct sockaddr_in *sockaddrPtr,
 			    char *host, int port));
+static Tcl_Channel      CreateSocketForFd _ANSI_ARGS_ ((int fd));
 static int		FileBlockModeProc _ANSI_ARGS_((
     			    ClientData instanceData, int mode));
 static int		FileCloseProc _ANSI_ARGS_((ClientData instanceData,
@@ -193,6 +194,7 @@
 			    long offset, int mode, int *errorCode));
 static void		FileWatchProc _ANSI_ARGS_((ClientData instanceData,
 		            int mask));
+static Tcl_Channel      CreateFileForFd _ANSI_ARGS_ ((int fd, int mode));
 static void		TcpAccept _ANSI_ARGS_((ClientData data, int mask));
 static int		TcpBlockModeProc _ANSI_ARGS_((ClientData data,
         		    int mode));
@@ -226,6 +228,7 @@
 static int		TtySetOptionProc _ANSI_ARGS_((ClientData instanceData,
 			    Tcl_Interp *interp, char *optionName, 
 			    char *value));
+static Tcl_Channel       CreateTtyForFd _ANSI_ARGS_ ((int fd, int mode));
 #endif	/* SUPPORTS_TTY */
 static int		WaitForConnect _ANSI_ARGS_((TcpState *statePtr,
 		            int *errorCodePtr));
@@ -605,6 +608,49 @@
     }
 }
 
+/*
+ *----------------------------------------------------------------------
+ *
+ * CreateFileForFd --
+ *
+ *	This function creates and initializes a file channel for an
+ *	existing file descriptor.
+ *
+ * Results:
+ *	A reference to the new channel or NULL.
+ *
+ * Side effects:
+ *	
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Tcl_Channel
+CreateFileForFd (fd, mode)
+     int          fd;
+     int          mode;
+{
+    FileState *fsPtr;
+    char channelName[16 + TCL_INTEGER_SPACE];
+
+    fsPtr = (FileState *) ckalloc((unsigned) sizeof(FileState));
+    if (fsPtr == NULL) {
+      return NULL;
+    }
+
+#ifdef DEPRECATED
+    fsPtr->nextPtr = tsdPtr->firstFilePtr;
+    tsdPtr->firstFilePtr = fsPtr;
+#endif
+    fsPtr->fd        = fd;
+    fsPtr->validMask = mode | TCL_EXCEPTION;
+
+    sprintf(channelName, "file%d", fd);
+    fsPtr->channel = Tcl_CreateChannel(&fileChannelType, channelName,
+				       (ClientData) fsPtr, mode);
+    return fsPtr->channel;
+}
+
 #ifdef SUPPORTS_TTY 
 
 /*
@@ -1203,7 +1249,7 @@
  *	Tcl can talk to a device located on the serial port.
  *
  * Results:
- *	None.
+ *	A reference to the allocated TtyState.
  *
  * Side effects:
  *	Serial device initialized to non-blocking raw mode, similar to
@@ -1257,6 +1303,65 @@
 /*
  *----------------------------------------------------------------------
  *
+ * CreateTtyForFd --
+ *
+ *	This function creates and initializes a tty channel for an
+ *	existing file descriptor.
+ *
+ * Results:
+ *	A reference to the new channel or NULL.
+ *
+ * Side effects:
+ *	
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Tcl_Channel
+CreateTtyForFd (fd, mode)
+     int         fd;
+     int         mode;
+{
+    /*
+     * Initialize the serial port to a set of sane parameters.
+     * Especially important if the remote device is set to echo and
+     * the serial port driver was also set to echo -- as soon as a char
+     * were sent to the serial port, the remote device would echo it,
+     * then the serial driver would echo it back to the device, etc.
+     */
+
+    FileState *ttyPtr;
+    char channelName [16 + TCL_INTEGER_SPACE];
+
+    ttyPtr = TtyInit(fd); /* Actually a (TtyState*)! */
+
+    ttyPtr->validMask = mode | TCL_EXCEPTION;
+    ttyPtr->fd        = fd;
+    
+    sprintf(channelName, "file%d", fd);
+    ttyPtr->channel = Tcl_CreateChannel(&ttyChannelType, channelName,
+				       (ClientData) ttyPtr, mode);
+
+    /*
+     * Gotcha.  Most modems need a "\r" at the end of the command
+     * sequence.  If you just send "at\n", the modem will not respond
+     * with "OK" because it never got a "\r" to actually invoke the
+     * command.  So, by default, newlines are translated to "\r\n" on
+     * output to avoid "bug" reports that the serial port isn't working.
+     */
+	 
+    if (Tcl_SetChannelOption(NULL, ttyPtr->channel, "-translation",
+			     "auto crlf") != TCL_OK) {
+        Tcl_Close(NULL, ttyPtr->channel);
+	return NULL;
+    }
+
+    return ttyPtr->channel;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
  * TclpOpenFileChannel --
  *
  *	Open an file based channel on Unix systems.
@@ -1285,11 +1390,9 @@
                                          * it? */
 {
     int fd, seekFlag, mode, channelPermissions;
-    FileState *fsPtr;
-    char *native, *translation;
-    char channelName[16 + TCL_INTEGER_SPACE];
+    char *native;
     Tcl_DString ds, buffer;
-    Tcl_ChannelType *channelTypePtr;
+    Tcl_Channel chan;
 #ifdef DEPRECATED
     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
 #endif
@@ -1340,67 +1443,39 @@
   
     fcntl(fd, F_SETFD, FD_CLOEXEC);
     
-    sprintf(channelName, "file%d", fd);
-    
 #ifdef SUPPORTS_TTY
     if (isatty(fd)) {
-	/*
-	 * Initialize the serial port to a set of sane parameters.
-	 * Especially important if the remote device is set to echo and
-	 * the serial port driver was also set to echo -- as soon as a char
-	 * were sent to the serial port, the remote device would echo it,
-	 * then the serial driver would echo it back to the device, etc.
-	 */
-	 
-	translation = "auto crlf";
-	channelTypePtr = &ttyChannelType;
-	fsPtr = TtyInit(fd);
-    } else 
+        chan = CreateTtyForFd (fd, channelPermissions);
+    } else
 #endif	/* SUPPORTS_TTY */
     {
-	translation = NULL;
-	channelTypePtr = &fileChannelType;
-	fsPtr = (FileState *) ckalloc((unsigned) sizeof(FileState));
+        struct stat buf;
+	fstat (fd, &buf);
+
+	if (S_ISSOCK(buf.st_mode)) {
+	    chan = CreateSocketForFd (fd);
+	} else {
+	    chan = CreateFileForFd (fd, channelPermissions);
+	}
     }
 
-#ifdef DEPRECATED
-    fsPtr->nextPtr = tsdPtr->firstFilePtr;
-    tsdPtr->firstFilePtr = fsPtr;
-#endif
-    fsPtr->validMask = channelPermissions | TCL_EXCEPTION;
-    fsPtr->fd = fd;
-    
-    fsPtr->channel = Tcl_CreateChannel(channelTypePtr, channelName,
-	    (ClientData) fsPtr, channelPermissions);
+    if (chan == (Tcl_Channel) NULL) {
+        return chan;
+    }
 
     if (seekFlag) {
-        if (Tcl_Seek(fsPtr->channel, 0, SEEK_END) < 0) {
-            if (interp != (Tcl_Interp *) NULL) {
+        if (Tcl_Seek(chan, 0, SEEK_END) < 0) {
+	    if (interp != (Tcl_Interp *) NULL) {
                 Tcl_AppendResult(interp, "couldn't seek to end of file on \"",
-                        channelName, "\": ", Tcl_PosixError(interp), NULL);
+				 Tcl_GetChannelName (chan), "\": ",
+				 Tcl_PosixError(interp), NULL);
             }
-            Tcl_Close(NULL, fsPtr->channel);
+            Tcl_Close(NULL, chan);
             return NULL;
         }
     }
-
-    if (translation != NULL) {
-	/*
-	 * Gotcha.  Most modems need a "\r" at the end of the command
-	 * sequence.  If you just send "at\n", the modem will not respond
-	 * with "OK" because it never got a "\r" to actually invoke the
-	 * command.  So, by default, newlines are translated to "\r\n" on
-	 * output to avoid "bug" reports that the serial port isn't working.
-	 */
-	 
-	if (Tcl_SetChannelOption(interp, fsPtr->channel, "-translation",
-		translation) != TCL_OK) {
-	    Tcl_Close(NULL, fsPtr->channel);
-	    return NULL;
-	}
-    }
 
-    return fsPtr->channel;
+    return chan;
 }
 
 /*
@@ -1425,8 +1500,6 @@
     int mode;			/* ORed combination of TCL_READABLE and
                                  * TCL_WRITABLE to indicate file mode. */
 {
-    FileState *fsPtr;
-    char channelName[16 + TCL_INTEGER_SPACE];
     int fd = (int) handle;
 #ifdef DEPRECATED
     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
@@ -1436,9 +1509,6 @@
         return NULL;
     }
 
-    sprintf(channelName, "file%d", fd);
-
-
     /*
      * Look to see if a channel with this fd and the same mode already exists.
      * If the fd is used, but the mode doesn't match, return NULL.
@@ -1453,18 +1523,32 @@
     }
 #endif
 
-    fsPtr = (FileState *) ckalloc((unsigned) sizeof(FileState));
+    /*
+     * Try to determine the type of this fd (file, socket, tty, ...)
+     * and create the corresponding channel
+     */
 
-#ifdef DEPRECATED
-    fsPtr->nextPtr = tsdPtr->firstFilePtr;
-    tsdPtr->firstFilePtr = fsPtr;
-#endif
-    fsPtr->fd = fd;
-    fsPtr->validMask = mode | TCL_EXCEPTION;
-    fsPtr->channel = Tcl_CreateChannel(&fileChannelType, channelName,
-            (ClientData) fsPtr, mode);
-    
-    return fsPtr->channel;
+#ifdef SUPPORTS_TTY
+    if (isatty(fd)) {
+      return CreateTtyForFd (fd, mode);
+    }
+#endif	/* SUPPORTS_TTY */
+    {
+        struct stat buf;
+	fstat (fd, &buf);
+
+	if (S_ISSOCK(buf.st_mode)) {
+	    return CreateSocketForFd (fd);
+	}
+
+	return CreateFileForFd (fd, mode);
+    }
+
+    /*
+     * Shouldn't be reached, ever.
+     */
+
+    return NULL;
 }
 
 /*
@@ -2228,6 +2312,57 @@
 
     sockaddrPtr->sin_addr.s_addr = addr.s_addr;
     return 1;	/* Success. */
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CreateSocketForFd --
+ *
+ *	This function creates and initializes a tcp channel for an
+ *	existing file descriptor.
+ *
+ * Results:
+ *	A reference to the new channel or NULL.
+ *
+ * Side effects:
+ *	
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Tcl_Channel
+CreateSocketForFd (fd)
+     int         fd;
+{
+    TcpState *tcpPtr;
+    char channelName [16 + TCL_INTEGER_SPACE];
+
+    tcpPtr = (TcpState *) ckalloc((unsigned) sizeof(TcpState));
+    if (tcpPtr == NULL) {
+        return NULL;
+    }
+
+    tcpPtr->flags          = 0;
+    tcpPtr->fd             = fd;
+    tcpPtr->acceptProc     = NULL;
+    tcpPtr->acceptProcData = (ClientData) NULL;
+
+    sprintf(channelName, "sock%d", fd);
+    tcpPtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
+					  (ClientData) tcpPtr,
+					  (TCL_READABLE | TCL_WRITABLE));
+    /*
+     * Sockets connections use CR+LF as line separator (outgoing).
+     */
+	 
+    if (Tcl_SetChannelOption(NULL, tcpPtr->channel, "-translation",
+			     "auto crlf") != TCL_OK) {
+        Tcl_Close(NULL, tcpPtr->channel);
+	return NULL;
+    }
+
+    return tcpPtr->channel;
 }
 
 /*
