1/*
2 * tclUnixChan.c
3 *
4 *	Common channel driver for Unix channels based on files, command
5 *	pipes and TCP sockets.
6 *
7 * Copyright (c) 1995-1997 Sun Microsystems, Inc.
8 * Copyright (c) 1998-1999 by Scriptics Corporation.
9 *
10 * See the file "license.terms" for information on usage and redistribution
11 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
12 *
13 * RCS: @(#) $Id: tclUnixChan.c,v 1.42.2.11 2008/03/03 15:01:14 rmax Exp $
14 */
15
16#include "tclInt.h"	/* Internal definitions for Tcl. */
17#include "tclPort.h"	/* Portability features for Tcl. */
18#include "tclIO.h"	/* To get Channel type declaration. */
19
20/*
21 * sys/ioctl.h has already been included by tclPort.h.	Including termios.h
22 * or termio.h causes a bunch of warning messages because some duplicate
23 * (but not contradictory) #defines exist in termios.h and/or termio.h
24 */
25#undef NL0
26#undef NL1
27#undef CR0
28#undef CR1
29#undef CR2
30#undef CR3
31#undef TAB0
32#undef TAB1
33#undef TAB2
34#undef XTABS
35#undef BS0
36#undef BS1
37#undef FF0
38#undef FF1
39#undef ECHO
40#undef NOFLSH
41#undef TOSTOP
42#undef FLUSHO
43#undef PENDIN
44
45#define SUPPORTS_TTY
46
47#ifdef USE_TERMIOS
48#   include <termios.h>
49#   ifdef HAVE_SYS_IOCTL_H
50#	include <sys/ioctl.h>
51#   endif /* HAVE_SYS_IOCTL_H */
52#   ifdef HAVE_SYS_MODEM_H
53#	include <sys/modem.h>
54#   endif /* HAVE_SYS_MODEM_H */
55#   define IOSTATE			struct termios
56#   define GETIOSTATE(fd, statePtr)	tcgetattr((fd), (statePtr))
57#   define SETIOSTATE(fd, statePtr)	tcsetattr((fd), TCSADRAIN, (statePtr))
58#   define GETCONTROL(fd, intPtr)	ioctl((fd), TIOCMGET, (intPtr))
59#   define SETCONTROL(fd, intPtr)	ioctl((fd), TIOCMSET, (intPtr))
60    /*
61     * TIP #35 introduced a different on exit flush/close behavior that
62     * doesn't work correctly with standard channels on all systems.
63     * The problem is tcflush throws away waiting channel data.	 This may
64     * be necessary for true serial channels that may block, but isn't
65     * correct in the standard case.  This might be replaced with tcdrain
66     * instead, but that can block.  For now, we revert to making this do
67     * nothing, and TtyOutputProc being the same old FileOutputProc.
68     * -- hobbs [Bug #525783]
69     */
70#   define BAD_TIP35_FLUSH 0
71#   if BAD_TIP35_FLUSH
72#	define TTYFLUSH(fd)		tcflush((fd), TCIOFLUSH);
73#   else
74#	define TTYFLUSH(fd)
75#   endif /* BAD_TIP35_FLUSH */
76#   ifdef FIONREAD
77#	define GETREADQUEUE(fd, int)	ioctl((fd), FIONREAD, &(int))
78#   elif defined(FIORDCHK)
79#	define GETREADQUEUE(fd, int)	int = ioctl((fd), FIORDCHK, NULL)
80#   endif /* FIONREAD */
81#   ifdef TIOCOUTQ
82#	define GETWRITEQUEUE(fd, int)	ioctl((fd), TIOCOUTQ, &(int))
83#   endif /* TIOCOUTQ */
84#   if defined(TIOCSBRK) && defined(TIOCCBRK)
85/*
86 * Can't use ?: operator below because that messes up types on either
87 * Linux or Solaris (the two are mutually exclusive!)
88 */
89#	define SETBREAK(fd, flag) \
90		if (flag) {				\
91		    ioctl((fd), TIOCSBRK, NULL);	\
92		} else {				\
93		    ioctl((fd), TIOCCBRK, NULL);	\
94		}
95#   endif /* TIOCSBRK&TIOCCBRK */
96#   if !defined(CRTSCTS) && defined(CNEW_RTSCTS)
97#	define CRTSCTS CNEW_RTSCTS
98#   endif /* !CRTSCTS&CNEW_RTSCTS */
99#   if !defined(PAREXT) && defined(CMSPAR)
100#	define PAREXT CMSPAR
101#   endif /* !PAREXT&&CMSPAR */
102#else	/* !USE_TERMIOS */
103
104#ifdef USE_TERMIO
105#   include <termio.h>
106#   define IOSTATE			struct termio
107#   define GETIOSTATE(fd, statePtr)	ioctl((fd), TCGETA, (statePtr))
108#   define SETIOSTATE(fd, statePtr)	ioctl((fd), TCSETAW, (statePtr))
109#else	/* !USE_TERMIO */
110
111#ifdef USE_SGTTY
112#   include <sgtty.h>
113#   define IOSTATE			struct sgttyb
114#   define GETIOSTATE(fd, statePtr)	ioctl((fd), TIOCGETP, (statePtr))
115#   define SETIOSTATE(fd, statePtr)	ioctl((fd), TIOCSETP, (statePtr))
116#else	/* !USE_SGTTY */
117#   undef SUPPORTS_TTY
118#endif	/* !USE_SGTTY */
119
120#endif	/* !USE_TERMIO */
121#endif	/* !USE_TERMIOS */
122
123/*
124 * This structure describes per-instance state of a file based channel.
125 */
126
127typedef struct FileState {
128    Tcl_Channel channel;	/* Channel associated with this file. */
129    int fd;			/* File handle. */
130    int validMask;		/* OR'ed combination of TCL_READABLE,
131				 * TCL_WRITABLE, or TCL_EXCEPTION: indicates
132				 * which operations are valid on the file. */
133#ifdef DEPRECATED
134    struct FileState *nextPtr;	/* Pointer to next file in list of all
135				 * file channels. */
136#endif /* DEPRECATED */
137} FileState;
138
139#ifdef SUPPORTS_TTY
140
141/*
142 * The following structure describes per-instance state of a tty-based
143 * channel.
144 */
145
146typedef struct TtyState {
147    FileState fs;		/* Per-instance state of the file
148				 * descriptor.	Must be the first field. */
149    int stateUpdated;		/* Flag to say if the state has been
150				 * modified and needs resetting. */
151    IOSTATE savedState;		/* Initial state of device.  Used to reset
152				 * state when device closed. */
153} TtyState;
154
155/*
156 * The following structure is used to set or get the serial port
157 * attributes in a platform-independant manner.
158 */
159
160typedef struct TtyAttrs {
161    int baud;
162    int parity;
163    int data;
164    int stop;
165} TtyAttrs;
166
167#endif	/* !SUPPORTS_TTY */
168
169#define UNSUPPORTED_OPTION(detail) \
170	if (interp) {							\
171	    Tcl_AppendResult(interp, (detail),				\
172		    " not supported for this platform", (char *) NULL); \
173	}
174
175#ifdef DEPRECATED
176typedef struct ThreadSpecificData {
177    /*
178     * List of all file channels currently open.  This is per thread and is
179     * used to match up fd's to channels, which rarely occurs.
180     */
181
182    FileState *firstFilePtr;
183} ThreadSpecificData;
184
185static Tcl_ThreadDataKey dataKey;
186#endif /* DEPRECATED */
187
188/*
189 * This structure describes per-instance state of a tcp based channel.
190 */
191
192typedef struct TcpState {
193    Tcl_Channel channel;	/* Channel associated with this file. */
194    int fd;			/* The socket itself. */
195    int flags;			/* ORed combination of the bitfields
196				 * defined below. */
197    Tcl_TcpAcceptProc *acceptProc;
198				/* Proc to call on accept. */
199    ClientData acceptProcData;	/* The data for the accept proc. */
200} TcpState;
201
202/*
203 * These bits may be ORed together into the "flags" field of a TcpState
204 * structure.
205 */
206
207#define TCP_ASYNC_SOCKET	(1<<0)	/* Asynchronous socket. */
208#define TCP_ASYNC_CONNECT	(1<<1)	/* Async connect in progress. */
209
210/*
211 * The following defines the maximum length of the listen queue. This is
212 * the number of outstanding yet-to-be-serviced requests for a connection
213 * on a server socket, more than this number of outstanding requests and
214 * the connection request will fail.
215 */
216
217#ifndef SOMAXCONN
218#   define SOMAXCONN	100
219#endif /* SOMAXCONN */
220
221#if (SOMAXCONN < 100)
222#   undef  SOMAXCONN
223#   define SOMAXCONN	100
224#endif /* SOMAXCONN < 100 */
225
226/*
227 * The following defines how much buffer space the kernel should maintain
228 * for a socket.
229 */
230
231#define SOCKET_BUFSIZE	4096
232
233/*
234 * Static routines for this file:
235 */
236
237static TcpState *	CreateSocket _ANSI_ARGS_((Tcl_Interp *interp,
238			    int port, CONST char *host, int server,
239			    CONST char *myaddr, int myport, int async));
240static int		CreateSocketAddress _ANSI_ARGS_(
241			    (struct sockaddr_in *sockaddrPtr,
242			    CONST char *host, int port));
243static int		FileBlockModeProc _ANSI_ARGS_((
244			    ClientData instanceData, int mode));
245static int		FileCloseProc _ANSI_ARGS_((ClientData instanceData,
246			    Tcl_Interp *interp));
247static int		FileGetHandleProc _ANSI_ARGS_((ClientData instanceData,
248			    int direction, ClientData *handlePtr));
249static int		FileInputProc _ANSI_ARGS_((ClientData instanceData,
250			    char *buf, int toRead, int *errorCode));
251static int		FileOutputProc _ANSI_ARGS_((
252			    ClientData instanceData, CONST char *buf,
253			    int toWrite, int *errorCode));
254static int		FileSeekProc _ANSI_ARGS_((ClientData instanceData,
255			    long offset, int mode, int *errorCode));
256#ifdef DEPRECATED
257static void             FileThreadActionProc _ANSI_ARGS_ ((
258			   ClientData instanceData, int action));
259#endif
260static Tcl_WideInt	FileWideSeekProc _ANSI_ARGS_((ClientData instanceData,
261			    Tcl_WideInt offset, int mode, int *errorCode));
262static void		FileWatchProc _ANSI_ARGS_((ClientData instanceData,
263			    int mask));
264static void		TcpAccept _ANSI_ARGS_((ClientData data, int mask));
265static int		TcpBlockModeProc _ANSI_ARGS_((ClientData data,
266			    int mode));
267static int		TcpCloseProc _ANSI_ARGS_((ClientData instanceData,
268			    Tcl_Interp *interp));
269static int		TcpGetHandleProc _ANSI_ARGS_((ClientData instanceData,
270			    int direction, ClientData *handlePtr));
271static int		TcpGetOptionProc _ANSI_ARGS_((ClientData instanceData,
272			    Tcl_Interp *interp, CONST char *optionName,
273			    Tcl_DString *dsPtr));
274static int		TcpInputProc _ANSI_ARGS_((ClientData instanceData,
275			    char *buf, int toRead,  int *errorCode));
276static int		TcpOutputProc _ANSI_ARGS_((ClientData instanceData,
277			    CONST char *buf, int toWrite, int *errorCode));
278static void		TcpWatchProc _ANSI_ARGS_((ClientData instanceData,
279			    int mask));
280#ifdef SUPPORTS_TTY
281static int		TtyCloseProc _ANSI_ARGS_((ClientData instanceData,
282			    Tcl_Interp *interp));
283static void		TtyGetAttributes _ANSI_ARGS_((int fd,
284			    TtyAttrs *ttyPtr));
285static int		TtyGetOptionProc _ANSI_ARGS_((ClientData instanceData,
286			    Tcl_Interp *interp, CONST char *optionName,
287			    Tcl_DString *dsPtr));
288static FileState *	TtyInit _ANSI_ARGS_((int fd, int initialize));
289#if BAD_TIP35_FLUSH
290static int		TtyOutputProc _ANSI_ARGS_((ClientData instanceData,
291			    CONST char *buf, int toWrite, int *errorCode));
292#endif /* BAD_TIP35_FLUSH */
293static int		TtyParseMode _ANSI_ARGS_((Tcl_Interp *interp,
294			    CONST char *mode, int *speedPtr, int *parityPtr,
295			    int *dataPtr, int *stopPtr));
296static void		TtySetAttributes _ANSI_ARGS_((int fd,
297			    TtyAttrs *ttyPtr));
298static int		TtySetOptionProc _ANSI_ARGS_((ClientData instanceData,
299			    Tcl_Interp *interp, CONST char *optionName,
300			    CONST char *value));
301#endif	/* SUPPORTS_TTY */
302static int		WaitForConnect _ANSI_ARGS_((TcpState *statePtr,
303			    int *errorCodePtr));
304static Tcl_Channel	MakeTcpClientChannelMode _ANSI_ARGS_(
305			    (ClientData tcpSocket,
306			    int mode));
307
308
309/*
310 * This structure describes the channel type structure for file based IO:
311 */
312
313static Tcl_ChannelType fileChannelType = {
314    "file",			/* Type name. */
315    TCL_CHANNEL_VERSION_4,	/* v4 channel */
316    FileCloseProc,		/* Close proc. */
317    FileInputProc,		/* Input proc. */
318    FileOutputProc,		/* Output proc. */
319    FileSeekProc,		/* Seek proc. */
320    NULL,			/* Set option proc. */
321    NULL,			/* Get option proc. */
322    FileWatchProc,		/* Initialize notifier. */
323    FileGetHandleProc,		/* Get OS handles out of channel. */
324    NULL,			/* close2proc. */
325    FileBlockModeProc,		/* Set blocking or non-blocking mode.*/
326    NULL,			/* flush proc. */
327    NULL,			/* handler proc. */
328    FileWideSeekProc,		/* wide seek proc. */
329#ifdef DEPRECATED
330    FileThreadActionProc,       /* thread actions */
331#else
332    NULL,
333#endif
334};
335
336#ifdef SUPPORTS_TTY
337/*
338 * This structure describes the channel type structure for serial IO.
339 * Note that this type is a subclass of the "file" type.
340 */
341
342static Tcl_ChannelType ttyChannelType = {
343    "tty",			/* Type name. */
344    TCL_CHANNEL_VERSION_4,	/* v4 channel */
345    TtyCloseProc,		/* Close proc. */
346    FileInputProc,		/* Input proc. */
347#if BAD_TIP35_FLUSH
348    TtyOutputProc,		/* Output proc. */
349#else /* !BAD_TIP35_FLUSH */
350    FileOutputProc,		/* Output proc. */
351#endif /* BAD_TIP35_FLUSH */
352    NULL,			/* Seek proc. */
353    TtySetOptionProc,		/* Set option proc. */
354    TtyGetOptionProc,		/* Get option proc. */
355    FileWatchProc,		/* Initialize notifier. */
356    FileGetHandleProc,		/* Get OS handles out of channel. */
357    NULL,			/* close2proc. */
358    FileBlockModeProc,		/* Set blocking or non-blocking mode.*/
359    NULL,			/* flush proc. */
360    NULL,			/* handler proc. */
361    NULL,			/* wide seek proc. */
362    NULL,			/* thread action proc. */
363};
364#endif	/* SUPPORTS_TTY */
365
366/*
367 * This structure describes the channel type structure for TCP socket
368 * based IO:
369 */
370
371static Tcl_ChannelType tcpChannelType = {
372    "tcp",			/* Type name. */
373    TCL_CHANNEL_VERSION_4,	/* v4 channel */
374    TcpCloseProc,		/* Close proc. */
375    TcpInputProc,		/* Input proc. */
376    TcpOutputProc,		/* Output proc. */
377    NULL,			/* Seek proc. */
378    NULL,			/* Set option proc. */
379    TcpGetOptionProc,		/* Get option proc. */
380    TcpWatchProc,		/* Initialize notifier. */
381    TcpGetHandleProc,		/* Get OS handles out of channel. */
382    NULL,			/* close2proc. */
383    TcpBlockModeProc,		/* Set blocking or non-blocking mode.*/
384    NULL,			/* flush proc. */
385    NULL,			/* handler proc. */
386    NULL,			/* wide seek proc. */
387    NULL,			/* thread action proc. */
388};
389
390
391/*
392 *----------------------------------------------------------------------
393 *
394 * FileBlockModeProc --
395 *
396 *	Helper procedure to set blocking and nonblocking modes on a
397 *	file based channel. Invoked by generic IO level code.
398 *
399 * Results:
400 *	0 if successful, errno when failed.
401 *
402 * Side effects:
403 *	Sets the device into blocking or non-blocking mode.
404 *
405 *----------------------------------------------------------------------
406 */
407
408	/* ARGSUSED */
409static int
410FileBlockModeProc(instanceData, mode)
411    ClientData instanceData;		/* File state. */
412    int mode;				/* The mode to set. Can be one of
413					 * TCL_MODE_BLOCKING or
414					 * TCL_MODE_NONBLOCKING. */
415{
416    FileState *fsPtr = (FileState *) instanceData;
417    int curStatus;
418
419#ifndef USE_FIONBIO
420    curStatus = fcntl(fsPtr->fd, F_GETFL);
421    if (mode == TCL_MODE_BLOCKING) {
422	curStatus &= (~(O_NONBLOCK));
423    } else {
424	curStatus |= O_NONBLOCK;
425    }
426    if (fcntl(fsPtr->fd, F_SETFL, curStatus) < 0) {
427	return errno;
428    }
429    curStatus = fcntl(fsPtr->fd, F_GETFL);
430#else /* USE_FIONBIO */
431    if (mode == TCL_MODE_BLOCKING) {
432	curStatus = 0;
433    } else {
434	curStatus = 1;
435    }
436    if (ioctl(fsPtr->fd, (int) FIONBIO, &curStatus) < 0) {
437	return errno;
438    }
439#endif /* !USE_FIONBIO */
440    return 0;
441}
442
443/*
444 *----------------------------------------------------------------------
445 *
446 * FileInputProc --
447 *
448 *	This procedure is invoked from the generic IO level to read
449 *	input from a file based channel.
450 *
451 * Results:
452 *	The number of bytes read is returned or -1 on error. An output
453 *	argument contains a POSIX error code if an error occurs, or zero.
454 *
455 * Side effects:
456 *	Reads input from the input device of the channel.
457 *
458 *----------------------------------------------------------------------
459 */
460
461static int
462FileInputProc(instanceData, buf, toRead, errorCodePtr)
463    ClientData instanceData;		/* File state. */
464    char *buf;				/* Where to store data read. */
465    int toRead;				/* How much space is available
466					 * in the buffer? */
467    int *errorCodePtr;			/* Where to store error code. */
468{
469    FileState *fsPtr = (FileState *) instanceData;
470    int bytesRead;			/* How many bytes were actually
471					 * read from the input device? */
472
473    *errorCodePtr = 0;
474
475    /*
476     * Assume there is always enough input available. This will block
477     * appropriately, and read will unblock as soon as a short read is
478     * possible, if the channel is in blocking mode. If the channel is
479     * nonblocking, the read will never block.
480     */
481
482    bytesRead = read(fsPtr->fd, buf, (size_t) toRead);
483    if (bytesRead > -1) {
484	return bytesRead;
485    }
486    *errorCodePtr = errno;
487    return -1;
488}
489
490/*
491 *----------------------------------------------------------------------
492 *
493 * FileOutputProc--
494 *
495 *	This procedure is invoked from the generic IO level to write
496 *	output to a file channel.
497 *
498 * Results:
499 *	The number of bytes written is returned or -1 on error. An
500 *	output argument contains a POSIX error code if an error occurred,
501 *	or zero.
502 *
503 * Side effects:
504 *	Writes output on the output device of the channel.
505 *
506 *----------------------------------------------------------------------
507 */
508
509static int
510FileOutputProc(instanceData, buf, toWrite, errorCodePtr)
511    ClientData instanceData;		/* File state. */
512    CONST char *buf;			/* The data buffer. */
513    int toWrite;			/* How many bytes to write? */
514    int *errorCodePtr;			/* Where to store error code. */
515{
516    FileState *fsPtr = (FileState *) instanceData;
517    int written;
518
519    *errorCodePtr = 0;
520
521    if (toWrite == 0) {
522	/*
523	 * SF Tcl Bug 465765.
524	 * Do not try to write nothing into a file. STREAM based
525	 * implementations will considers this as EOF (if there is a
526	 * pipe behind the file).
527	 */
528
529	return 0;
530    }
531    written = write(fsPtr->fd, buf, (size_t) toWrite);
532    if (written > -1) {
533	return written;
534    }
535    *errorCodePtr = errno;
536    return -1;
537}
538
539/*
540 *----------------------------------------------------------------------
541 *
542 * FileCloseProc --
543 *
544 *	This procedure is called from the generic IO level to perform
545 *	channel-type-specific cleanup when a file based channel is closed.
546 *
547 * Results:
548 *	0 if successful, errno if failed.
549 *
550 * Side effects:
551 *	Closes the device of the channel.
552 *
553 *----------------------------------------------------------------------
554 */
555
556static int
557FileCloseProc(instanceData, interp)
558    ClientData instanceData;	/* File state. */
559    Tcl_Interp *interp;		/* For error reporting - unused. */
560{
561    FileState *fsPtr = (FileState *) instanceData;
562    int errorCode = 0;
563#ifdef DEPRECATED
564    FileState **nextPtrPtr;
565    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
566#endif /* DEPRECATED */
567    Tcl_DeleteFileHandler(fsPtr->fd);
568
569    /*
570     * Do not close standard channels while in thread-exit.
571     */
572
573    if (!TclInThreadExit()
574	    || ((fsPtr->fd != 0) && (fsPtr->fd != 1) && (fsPtr->fd != 2))) {
575	if (close(fsPtr->fd) < 0) {
576	    errorCode = errno;
577	}
578    }
579    ckfree((char *) fsPtr);
580    return errorCode;
581}
582
583/*
584 *----------------------------------------------------------------------
585 *
586 * FileSeekProc --
587 *
588 *	This procedure is called by the generic IO level to move the
589 *	access point in a file based channel.
590 *
591 * Results:
592 *	-1 if failed, the new position if successful. An output
593 *	argument contains the POSIX error code if an error occurred,
594 *	or zero.
595 *
596 * Side effects:
597 *	Moves the location at which the channel will be accessed in
598 *	future operations.
599 *
600 *----------------------------------------------------------------------
601 */
602
603static int
604FileSeekProc(instanceData, offset, mode, errorCodePtr)
605    ClientData instanceData;	/* File state. */
606    long offset;		/* Offset to seek to. */
607    int mode;			/* Relative to where should we seek? Can be
608				 * one of SEEK_START, SEEK_SET or SEEK_END. */
609    int *errorCodePtr;		/* To store error code. */
610{
611    FileState *fsPtr = (FileState *) instanceData;
612    Tcl_WideInt oldLoc, newLoc;
613
614    /*
615     * Save our current place in case we need to roll-back the seek.
616     */
617    oldLoc = TclOSseek(fsPtr->fd, (Tcl_SeekOffset) 0, SEEK_CUR);
618    if (oldLoc == Tcl_LongAsWide(-1)) {
619	/*
620	 * Bad things are happening.  Error out...
621	 */
622	*errorCodePtr = errno;
623	return -1;
624    }
625
626    newLoc = TclOSseek(fsPtr->fd, (Tcl_SeekOffset) offset, mode);
627
628    /*
629     * Check for expressability in our return type, and roll-back otherwise.
630     */
631    if (newLoc > Tcl_LongAsWide(INT_MAX)) {
632	*errorCodePtr = EOVERFLOW;
633	TclOSseek(fsPtr->fd, (Tcl_SeekOffset) oldLoc, SEEK_SET);
634	return -1;
635    } else {
636	*errorCodePtr = (newLoc == Tcl_LongAsWide(-1)) ? errno : 0;
637    }
638    return (int) Tcl_WideAsLong(newLoc);
639}
640
641/*
642 *----------------------------------------------------------------------
643 *
644 * FileWideSeekProc --
645 *
646 *	This procedure is called by the generic IO level to move the
647 *	access point in a file based channel, with offsets expressed
648 *	as wide integers.
649 *
650 * Results:
651 *	-1 if failed, the new position if successful. An output
652 *	argument contains the POSIX error code if an error occurred,
653 *	or zero.
654 *
655 * Side effects:
656 *	Moves the location at which the channel will be accessed in
657 *	future operations.
658 *
659 *----------------------------------------------------------------------
660 */
661
662static Tcl_WideInt
663FileWideSeekProc(instanceData, offset, mode, errorCodePtr)
664    ClientData instanceData;	/* File state. */
665    Tcl_WideInt offset;		/* Offset to seek to. */
666    int mode;			/* Relative to where should we seek? Can be
667				 * one of SEEK_START, SEEK_CUR or SEEK_END. */
668    int *errorCodePtr;		/* To store error code. */
669{
670    FileState *fsPtr = (FileState *) instanceData;
671    Tcl_WideInt newLoc;
672
673    newLoc = TclOSseek(fsPtr->fd, (Tcl_SeekOffset) offset, mode);
674
675    *errorCodePtr = (newLoc == -1) ? errno : 0;
676    return newLoc;
677}
678
679/*
680 *----------------------------------------------------------------------
681 *
682 * FileWatchProc --
683 *
684 *	Initialize the notifier to watch the fd from this channel.
685 *
686 * Results:
687 *	None.
688 *
689 * Side effects:
690 *	Sets up the notifier so that a future event on the channel will
691 *	be seen by Tcl.
692 *
693 *----------------------------------------------------------------------
694 */
695
696static void
697FileWatchProc(instanceData, mask)
698    ClientData instanceData;		/* The file state. */
699    int mask;				/* Events of interest; an OR-ed
700					 * combination of TCL_READABLE,
701					 * TCL_WRITABLE and TCL_EXCEPTION. */
702{
703    FileState *fsPtr = (FileState *) instanceData;
704
705    /*
706     * Make sure we only register for events that are valid on this file.
707     * Note that we are passing Tcl_NotifyChannel directly to
708     * Tcl_CreateFileHandler with the channel pointer as the client data.
709     */
710
711    mask &= fsPtr->validMask;
712    if (mask) {
713	Tcl_CreateFileHandler(fsPtr->fd, mask,
714		(Tcl_FileProc *) Tcl_NotifyChannel,
715		(ClientData) fsPtr->channel);
716    } else {
717	Tcl_DeleteFileHandler(fsPtr->fd);
718    }
719}
720
721/*
722 *----------------------------------------------------------------------
723 *
724 * FileGetHandleProc --
725 *
726 *	Called from Tcl_GetChannelHandle to retrieve OS handles from
727 *	a file based channel.
728 *
729 * Results:
730 *	Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if
731 *	there is no handle for the specified direction.
732 *
733 * Side effects:
734 *	None.
735 *
736 *----------------------------------------------------------------------
737 */
738
739static int
740FileGetHandleProc(instanceData, direction, handlePtr)
741    ClientData instanceData;	/* The file state. */
742    int direction;		/* TCL_READABLE or TCL_WRITABLE */
743    ClientData *handlePtr;	/* Where to store the handle.  */
744{
745    FileState *fsPtr = (FileState *) instanceData;
746
747    if (direction & fsPtr->validMask) {
748	*handlePtr = (ClientData) fsPtr->fd;
749	return TCL_OK;
750    } else {
751	return TCL_ERROR;
752    }
753}
754
755#ifdef SUPPORTS_TTY
756
757/*
758 *----------------------------------------------------------------------
759 *
760 * TtyCloseProc --
761 *
762 *	This procedure is called from the generic IO level to perform
763 *	channel-type-specific cleanup when a tty based channel is closed.
764 *
765 * Results:
766 *	0 if successful, errno if failed.
767 *
768 * Side effects:
769 *	Closes the device of the channel.
770 *
771 *----------------------------------------------------------------------
772 */
773static int
774TtyCloseProc(instanceData, interp)
775    ClientData instanceData;	/* Tty state. */
776    Tcl_Interp *interp;		/* For error reporting - unused. */
777{
778#if BAD_TIP35_FLUSH
779    TtyState *ttyPtr = (TtyState *) instanceData;
780#endif /* BAD_TIP35_FLUSH */
781#ifdef TTYFLUSH
782    TTYFLUSH(ttyPtr->fs.fd);
783#endif /* TTYFLUSH */
784#if 0
785    /*
786     * TIP#35 agreed to remove the unsave so that TCL could be used as a
787     * simple stty.
788     * It would be cleaner to remove all the stuff related to
789     *	  TtyState.stateUpdated
790     *	  TtyState.savedState
791     * Then the structure TtyState would be the same as FileState.
792     * IMO this cleanup could better be done for the final 8.4 release
793     * after nobody complained about the missing unsave. -- schroedter
794     */
795    if (ttyPtr->stateUpdated) {
796	SETIOSTATE(ttyPtr->fs.fd, &ttyPtr->savedState);
797    }
798#endif
799    return FileCloseProc(instanceData, interp);
800}
801
802/*
803 *----------------------------------------------------------------------
804 *
805 * TtyOutputProc--
806 *
807 *	This procedure is invoked from the generic IO level to write
808 *	output to a TTY channel.
809 *
810 * Results:
811 *	The number of bytes written is returned or -1 on error. An
812 *	output argument contains a POSIX error code if an error occurred,
813 *	or zero.
814 *
815 * Side effects:
816 *	Writes output on the output device of the channel
817 *	if the channel is not designated to be closed.
818 *
819 *----------------------------------------------------------------------
820 */
821
822#if BAD_TIP35_FLUSH
823static int
824TtyOutputProc(instanceData, buf, toWrite, errorCodePtr)
825    ClientData instanceData;		/* File state. */
826    CONST char *buf;			/* The data buffer. */
827    int toWrite;			/* How many bytes to write? */
828    int *errorCodePtr;			/* Where to store error code. */
829{
830    if (TclInExit()) {
831	/*
832	 * Do not write data during Tcl exit.
833	 * Serial port may block preventing Tcl from exit.
834	 */
835	return toWrite;
836    } else {
837	return FileOutputProc(instanceData, buf, toWrite, errorCodePtr);
838    }
839}
840#endif /* BAD_TIP35_FLUSH */
841
842#ifdef USE_TERMIOS
843/*
844 *----------------------------------------------------------------------
845 *
846 * TtyModemStatusStr --
847 *
848 *  Converts a RS232 modem status list of readable flags
849 *
850 *----------------------------------------------------------------------
851 */
852static void
853TtyModemStatusStr(status, dsPtr)
854    int status;		   /* RS232 modem status */
855    Tcl_DString *dsPtr;	   /* Where to store string */
856{
857#ifdef TIOCM_CTS
858    Tcl_DStringAppendElement(dsPtr, "CTS");
859    Tcl_DStringAppendElement(dsPtr, (status & TIOCM_CTS) ? "1" : "0");
860#endif /* TIOCM_CTS */
861#ifdef TIOCM_DSR
862    Tcl_DStringAppendElement(dsPtr, "DSR");
863    Tcl_DStringAppendElement(dsPtr, (status & TIOCM_DSR) ? "1" : "0");
864#endif /* TIOCM_DSR */
865#ifdef TIOCM_RNG
866    Tcl_DStringAppendElement(dsPtr, "RING");
867    Tcl_DStringAppendElement(dsPtr, (status & TIOCM_RNG) ? "1" : "0");
868#endif /* TIOCM_RNG */
869#ifdef TIOCM_CD
870    Tcl_DStringAppendElement(dsPtr, "DCD");
871    Tcl_DStringAppendElement(dsPtr, (status & TIOCM_CD) ? "1" : "0");
872#endif /* TIOCM_CD */
873}
874#endif /* USE_TERMIOS */
875
876/*
877 *----------------------------------------------------------------------
878 *
879 * TtySetOptionProc --
880 *
881 *	Sets an option on a channel.
882 *
883 * Results:
884 *	A standard Tcl result. Also sets the interp's result on error if
885 *	interp is not NULL.
886 *
887 * Side effects:
888 *	May modify an option on a device.
889 *	Sets Error message if needed (by calling Tcl_BadChannelOption).
890 *
891 *----------------------------------------------------------------------
892 */
893
894static int
895TtySetOptionProc(instanceData, interp, optionName, value)
896    ClientData instanceData;	/* File state. */
897    Tcl_Interp *interp;		/* For error reporting - can be NULL. */
898    CONST char *optionName;	/* Which option to set? */
899    CONST char *value;		/* New value for option. */
900{
901    FileState *fsPtr = (FileState *) instanceData;
902    unsigned int len, vlen;
903    TtyAttrs tty;
904#ifdef USE_TERMIOS
905    int flag, control, argc;
906    CONST char **argv;
907    IOSTATE iostate;
908#endif /* USE_TERMIOS */
909
910    len = strlen(optionName);
911    vlen = strlen(value);
912
913    /*
914     * Option -mode baud,parity,databits,stopbits
915     */
916    if ((len > 2) && (strncmp(optionName, "-mode", len) == 0)) {
917	if (TtyParseMode(interp, value, &tty.baud, &tty.parity, &tty.data,
918		&tty.stop) != TCL_OK) {
919	    return TCL_ERROR;
920	}
921	/*
922	 * system calls results should be checked there. -- dl
923	 */
924
925	TtySetAttributes(fsPtr->fd, &tty);
926	((TtyState *) fsPtr)->stateUpdated = 1;
927	return TCL_OK;
928    }
929
930#ifdef USE_TERMIOS
931
932    /*
933     * Option -handshake none|xonxoff|rtscts|dtrdsr
934     */
935    if ((len > 1) && (strncmp(optionName, "-handshake", len) == 0)) {
936	/*
937	 * Reset all handshake options
938	 * DTR and RTS are ON by default
939	 */
940	GETIOSTATE(fsPtr->fd, &iostate);
941	iostate.c_iflag &= ~(IXON | IXOFF | IXANY);
942#ifdef CRTSCTS
943	iostate.c_cflag &= ~CRTSCTS;
944#endif /* CRTSCTS */
945	if (strncasecmp(value, "NONE", vlen) == 0) {
946	    /* leave all handshake options disabled */
947	} else if (strncasecmp(value, "XONXOFF", vlen) == 0) {
948	    iostate.c_iflag |= (IXON | IXOFF | IXANY);
949	} else if (strncasecmp(value, "RTSCTS", vlen) == 0) {
950#ifdef CRTSCTS
951	    iostate.c_cflag |= CRTSCTS;
952#else /* !CRTSTS */
953	    UNSUPPORTED_OPTION("-handshake RTSCTS");
954	    return TCL_ERROR;
955#endif /* CRTSCTS */
956	} else if (strncasecmp(value, "DTRDSR", vlen) == 0) {
957	    UNSUPPORTED_OPTION("-handshake DTRDSR");
958	    return TCL_ERROR;
959	} else {
960	    if (interp) {
961		Tcl_AppendResult(interp, "bad value for -handshake: ",
962			"must be one of xonxoff, rtscts, dtrdsr or none",
963			(char *) NULL);
964	    }
965	    return TCL_ERROR;
966	}
967	SETIOSTATE(fsPtr->fd, &iostate);
968	return TCL_OK;
969    }
970
971    /*
972     * Option -xchar {\x11 \x13}
973     */
974    if ((len > 1) && (strncmp(optionName, "-xchar", len) == 0)) {
975	GETIOSTATE(fsPtr->fd, &iostate);
976	if (Tcl_SplitList(interp, value, &argc, &argv) == TCL_ERROR) {
977	    return TCL_ERROR;
978	}
979	if (argc == 2) {
980	    iostate.c_cc[VSTART] = argv[0][0];
981	    iostate.c_cc[VSTOP]	 = argv[1][0];
982	} else {
983	    if (interp) {
984		Tcl_AppendResult(interp,
985		    "bad value for -xchar: should be a list of two elements",
986		    (char *) NULL);
987	    }
988	    ckfree((char *) argv);
989	    return TCL_ERROR;
990	}
991	SETIOSTATE(fsPtr->fd, &iostate);
992	ckfree((char *) argv);
993	return TCL_OK;
994    }
995
996    /*
997     * Option -timeout msec
998     */
999    if ((len > 2) && (strncmp(optionName, "-timeout", len) == 0)) {
1000	int msec;
1001
1002	GETIOSTATE(fsPtr->fd, &iostate);
1003	if (Tcl_GetInt(interp, value, &msec) != TCL_OK) {
1004	    return TCL_ERROR;
1005	}
1006	iostate.c_cc[VMIN]  = 0;
1007	iostate.c_cc[VTIME] = (msec == 0) ? 0 : (msec < 100) ? 1 : (msec+50)/100;
1008	SETIOSTATE(fsPtr->fd, &iostate);
1009	return TCL_OK;
1010    }
1011
1012    /*
1013     * Option -ttycontrol {DTR 1 RTS 0 BREAK 0}
1014     */
1015    if ((len > 4) && (strncmp(optionName, "-ttycontrol", len) == 0)) {
1016	int i;
1017	if (Tcl_SplitList(interp, value, &argc, &argv) == TCL_ERROR) {
1018	    return TCL_ERROR;
1019	}
1020	if ((argc % 2) == 1) {
1021	    if (interp) {
1022		Tcl_AppendResult(interp,
1023			"bad value for -ttycontrol: should be a list of",
1024			"signal,value pairs", (char *) NULL);
1025	    }
1026	    ckfree((char *) argv);
1027	    return TCL_ERROR;
1028	}
1029
1030	GETCONTROL(fsPtr->fd, &control);
1031	for (i = 0; i < argc-1; i += 2) {
1032	    if (Tcl_GetBoolean(interp, argv[i+1], &flag) == TCL_ERROR) {
1033		ckfree((char *) argv);
1034		return TCL_ERROR;
1035	    }
1036	    if (strncasecmp(argv[i], "DTR", strlen(argv[i])) == 0) {
1037#ifdef TIOCM_DTR
1038		if (flag) {
1039		    control |= TIOCM_DTR;
1040		} else {
1041		    control &= ~TIOCM_DTR;
1042		}
1043#else /* !TIOCM_DTR */
1044		UNSUPPORTED_OPTION("-ttycontrol DTR");
1045		ckfree((char *) argv);
1046		return TCL_ERROR;
1047#endif /* TIOCM_DTR */
1048	    } else if (strncasecmp(argv[i], "RTS", strlen(argv[i])) == 0) {
1049#ifdef TIOCM_RTS
1050		if (flag) {
1051		    control |= TIOCM_RTS;
1052		} else {
1053		    control &= ~TIOCM_RTS;
1054		}
1055#else /* !TIOCM_RTS*/
1056		UNSUPPORTED_OPTION("-ttycontrol RTS");
1057		ckfree((char *) argv);
1058		return TCL_ERROR;
1059#endif /* TIOCM_RTS*/
1060	    } else if (strncasecmp(argv[i], "BREAK", strlen(argv[i])) == 0) {
1061#ifdef SETBREAK
1062		SETBREAK(fsPtr->fd, flag);
1063#else /* !SETBREAK */
1064		UNSUPPORTED_OPTION("-ttycontrol BREAK");
1065		ckfree((char *) argv);
1066		return TCL_ERROR;
1067#endif /* SETBREAK */
1068	    } else {
1069		if (interp) {
1070		    Tcl_AppendResult(interp, "bad signal \"", argv[i],
1071			    "\" for -ttycontrol: must be ",
1072			    "DTR, RTS or BREAK", (char *) NULL);
1073		}
1074		ckfree((char *) argv);
1075		return TCL_ERROR;
1076	    }
1077	} /* -ttycontrol options loop */
1078
1079	SETCONTROL(fsPtr->fd, &control);
1080	ckfree((char *) argv);
1081	return TCL_OK;
1082    }
1083
1084    return Tcl_BadChannelOption(interp, optionName,
1085	    "mode handshake timeout ttycontrol xchar ");
1086
1087#else /* !USE_TERMIOS */
1088    return Tcl_BadChannelOption(interp, optionName, "mode");
1089#endif /* USE_TERMIOS */
1090}
1091
1092/*
1093 *----------------------------------------------------------------------
1094 *
1095 * TtyGetOptionProc --
1096 *
1097 *	Gets a mode associated with an IO channel. If the optionName arg
1098 *	is non NULL, retrieves the value of that option. If the optionName
1099 *	arg is NULL, retrieves a list of alternating option names and
1100 *	values for the given channel.
1101 *
1102 * Results:
1103 *	A standard Tcl result. Also sets the supplied DString to the
1104 *	string value of the option(s) returned.
1105 *
1106 * Side effects:
1107 *	The string returned by this function is in static storage and
1108 *	may be reused at any time subsequent to the call.
1109 *	Sets Error message if needed (by calling Tcl_BadChannelOption).
1110 *
1111 *----------------------------------------------------------------------
1112 */
1113
1114static int
1115TtyGetOptionProc(instanceData, interp, optionName, dsPtr)
1116    ClientData instanceData;	/* File state. */
1117    Tcl_Interp *interp;		/* For error reporting - can be NULL. */
1118    CONST char *optionName;	/* Option to get. */
1119    Tcl_DString *dsPtr;		/* Where to store value(s). */
1120{
1121    FileState *fsPtr = (FileState *) instanceData;
1122    unsigned int len;
1123    char buf[3 * TCL_INTEGER_SPACE + 16];
1124    TtyAttrs tty;
1125    int valid = 0;  /* flag if valid option parsed */
1126
1127    if (optionName == NULL) {
1128	len = 0;
1129    } else {
1130	len = strlen(optionName);
1131    }
1132    if (len == 0) {
1133	Tcl_DStringAppendElement(dsPtr, "-mode");
1134    }
1135    if (len==0 || (len>2 && strncmp(optionName, "-mode", len)==0)) {
1136	valid = 1;
1137	TtyGetAttributes(fsPtr->fd, &tty);
1138	sprintf(buf, "%d,%c,%d,%d", tty.baud, tty.parity, tty.data, tty.stop);
1139	Tcl_DStringAppendElement(dsPtr, buf);
1140    }
1141
1142#ifdef USE_TERMIOS
1143    /*
1144     * get option -xchar
1145     */
1146    if (len == 0) {
1147	Tcl_DStringAppendElement(dsPtr, "-xchar");
1148	Tcl_DStringStartSublist(dsPtr);
1149    }
1150    if (len==0 || (len>1 && strncmp(optionName, "-xchar", len)==0)) {
1151	IOSTATE iostate;
1152	valid = 1;
1153
1154	GETIOSTATE(fsPtr->fd, &iostate);
1155	sprintf(buf, "%c", iostate.c_cc[VSTART]);
1156	Tcl_DStringAppendElement(dsPtr, buf);
1157	sprintf(buf, "%c", iostate.c_cc[VSTOP]);
1158	Tcl_DStringAppendElement(dsPtr, buf);
1159    }
1160    if (len == 0) {
1161	Tcl_DStringEndSublist(dsPtr);
1162    }
1163
1164    /*
1165     * get option -queue
1166     * option is readonly and returned by [fconfigure chan -queue]
1167     * but not returned by unnamed [fconfigure chan]
1168     */
1169    if ((len > 1) && (strncmp(optionName, "-queue", len) == 0)) {
1170	int inQueue=0, outQueue=0;
1171	int inBuffered, outBuffered;
1172	valid = 1;
1173#ifdef GETREADQUEUE
1174	GETREADQUEUE(fsPtr->fd, inQueue);
1175#endif /* GETREADQUEUE */
1176#ifdef GETWRITEQUEUE
1177	GETWRITEQUEUE(fsPtr->fd, outQueue);
1178#endif /* GETWRITEQUEUE */
1179	inBuffered  = Tcl_InputBuffered(fsPtr->channel);
1180	outBuffered = Tcl_OutputBuffered(fsPtr->channel);
1181
1182	sprintf(buf, "%d", inBuffered+inQueue);
1183	Tcl_DStringAppendElement(dsPtr, buf);
1184	sprintf(buf, "%d", outBuffered+outQueue);
1185	Tcl_DStringAppendElement(dsPtr, buf);
1186    }
1187
1188    /*
1189     * get option -ttystatus
1190     * option is readonly and returned by [fconfigure chan -ttystatus]
1191     * but not returned by unnamed [fconfigure chan]
1192     */
1193    if ((len > 4) && (strncmp(optionName, "-ttystatus", len) == 0)) {
1194	int status;
1195	valid = 1;
1196	GETCONTROL(fsPtr->fd, &status);
1197	TtyModemStatusStr(status, dsPtr);
1198    }
1199#endif /* USE_TERMIOS */
1200
1201    if (valid) {
1202	return TCL_OK;
1203    } else {
1204	return Tcl_BadChannelOption(interp, optionName,
1205#ifdef USE_TERMIOS
1206	    "mode queue ttystatus xchar");
1207#else /* !USE_TERMIOS */
1208	    "mode");
1209#endif /* USE_TERMIOS */
1210    }
1211}
1212
1213#undef DIRECT_BAUD
1214#ifdef B4800
1215#   if (B4800 == 4800)
1216#	define DIRECT_BAUD
1217#   endif /* B4800 == 4800 */
1218#endif /* B4800 */
1219
1220#ifdef DIRECT_BAUD
1221#   define TtyGetSpeed(baud)   ((unsigned) (baud))
1222#   define TtyGetBaud(speed)   ((int) (speed))
1223#else /* !DIRECT_BAUD */
1224
1225static struct {int baud; unsigned long speed;} speeds[] = {
1226#ifdef B0
1227    {0, B0},
1228#endif
1229#ifdef B50
1230    {50, B50},
1231#endif
1232#ifdef B75
1233    {75, B75},
1234#endif
1235#ifdef B110
1236    {110, B110},
1237#endif
1238#ifdef B134
1239    {134, B134},
1240#endif
1241#ifdef B150
1242    {150, B150},
1243#endif
1244#ifdef B200
1245    {200, B200},
1246#endif
1247#ifdef B300
1248    {300, B300},
1249#endif
1250#ifdef B600
1251    {600, B600},
1252#endif
1253#ifdef B1200
1254    {1200, B1200},
1255#endif
1256#ifdef B1800
1257    {1800, B1800},
1258#endif
1259#ifdef B2400
1260    {2400, B2400},
1261#endif
1262#ifdef B4800
1263    {4800, B4800},
1264#endif
1265#ifdef B9600
1266    {9600, B9600},
1267#endif
1268#ifdef B14400
1269    {14400, B14400},
1270#endif
1271#ifdef B19200
1272    {19200, B19200},
1273#endif
1274#ifdef EXTA
1275    {19200, EXTA},
1276#endif
1277#ifdef B28800
1278    {28800, B28800},
1279#endif
1280#ifdef B38400
1281    {38400, B38400},
1282#endif
1283#ifdef EXTB
1284    {38400, EXTB},
1285#endif
1286#ifdef B57600
1287    {57600, B57600},
1288#endif
1289#ifdef _B57600
1290    {57600, _B57600},
1291#endif
1292#ifdef B76800
1293    {76800, B76800},
1294#endif
1295#ifdef B115200
1296    {115200, B115200},
1297#endif
1298#ifdef _B115200
1299    {115200, _B115200},
1300#endif
1301#ifdef B153600
1302    {153600, B153600},
1303#endif
1304#ifdef B230400
1305    {230400, B230400},
1306#endif
1307#ifdef B307200
1308    {307200, B307200},
1309#endif
1310#ifdef B460800
1311    {460800, B460800},
1312#endif
1313    {-1, 0}
1314};
1315
1316/*
1317 *---------------------------------------------------------------------------
1318 *
1319 * TtyGetSpeed --
1320 *
1321 *	Given a baud rate, get the mask value that should be stored in
1322 *	the termios, termio, or sgttyb structure in order to select that
1323 *	baud rate.
1324 *
1325 * Results:
1326 *	As above.
1327 *
1328 * Side effects:
1329 *	None.
1330 *
1331 *---------------------------------------------------------------------------
1332 */
1333
1334static unsigned long
1335TtyGetSpeed(baud)
1336    int baud;			/* The baud rate to look up. */
1337{
1338    int bestIdx, bestDiff, i, diff;
1339
1340    bestIdx = 0;
1341    bestDiff = 1000000;
1342
1343    /*
1344     * If the baud rate does not correspond to one of the known mask values,
1345     * choose the mask value whose baud rate is closest to the specified
1346     * baud rate.
1347     */
1348
1349    for (i = 0; speeds[i].baud >= 0; i++) {
1350	diff = speeds[i].baud - baud;
1351	if (diff < 0) {
1352	    diff = -diff;
1353	}
1354	if (diff < bestDiff) {
1355	    bestIdx = i;
1356	    bestDiff = diff;
1357	}
1358    }
1359    return speeds[bestIdx].speed;
1360}
1361
1362/*
1363 *---------------------------------------------------------------------------
1364 *
1365 * TtyGetBaud --
1366 *
1367 *	Given a speed mask value from a termios, termio, or sgttyb
1368 *	structure, get the baus rate that corresponds to that mask value.
1369 *
1370 * Results:
1371 *	As above.  If the mask value was not recognized, 0 is returned.
1372 *
1373 * Side effects:
1374 *	None.
1375 *
1376 *---------------------------------------------------------------------------
1377 */
1378
1379static int
1380TtyGetBaud(speed)
1381    unsigned long speed;	/* Speed mask value to look up. */
1382{
1383    int i;
1384
1385    for (i = 0; speeds[i].baud >= 0; i++) {
1386	if (speeds[i].speed == speed) {
1387	    return speeds[i].baud;
1388	}
1389    }
1390    return 0;
1391}
1392
1393#endif /* !DIRECT_BAUD */
1394
1395
1396/*
1397 *---------------------------------------------------------------------------
1398 *
1399 * TtyGetAttributes --
1400 *
1401 *	Get the current attributes of the specified serial device.
1402 *
1403 * Results:
1404 *	None.
1405 *
1406 * Side effects:
1407 *	None.
1408 *
1409 *---------------------------------------------------------------------------
1410 */
1411
1412static void
1413TtyGetAttributes(fd, ttyPtr)
1414    int fd;			/* Open file descriptor for serial port to
1415				 * be queried. */
1416    TtyAttrs *ttyPtr;		/* Buffer filled with serial port
1417				 * attributes. */
1418{
1419    IOSTATE iostate;
1420    int baud, parity, data, stop;
1421
1422    GETIOSTATE(fd, &iostate);
1423
1424#ifdef USE_TERMIOS
1425    baud = TtyGetBaud(cfgetospeed(&iostate));
1426
1427    parity = 'n';
1428#ifdef PAREXT
1429    switch ((int) (iostate.c_cflag & (PARENB | PARODD | PAREXT))) {
1430	case PARENB		      : parity = 'e'; break;
1431	case PARENB | PARODD	      : parity = 'o'; break;
1432	case PARENB |	       PAREXT : parity = 's'; break;
1433	case PARENB | PARODD | PAREXT : parity = 'm'; break;
1434    }
1435#else /* !PAREXT */
1436    switch ((int) (iostate.c_cflag & (PARENB | PARODD))) {
1437	case PARENB		      : parity = 'e'; break;
1438	case PARENB | PARODD	      : parity = 'o'; break;
1439    }
1440#endif /* !PAREXT */
1441
1442    data = iostate.c_cflag & CSIZE;
1443    data = (data == CS5) ? 5 : (data == CS6) ? 6 : (data == CS7) ? 7 : 8;
1444
1445    stop = (iostate.c_cflag & CSTOPB) ? 2 : 1;
1446#endif /* USE_TERMIOS */
1447
1448#ifdef USE_TERMIO
1449    baud = TtyGetBaud(iostate.c_cflag & CBAUD);
1450
1451    parity = 'n';
1452    switch (iostate.c_cflag & (PARENB | PARODD | PAREXT)) {
1453	case PARENB		      : parity = 'e'; break;
1454	case PARENB | PARODD	      : parity = 'o'; break;
1455	case PARENB |	       PAREXT : parity = 's'; break;
1456	case PARENB | PARODD | PAREXT : parity = 'm'; break;
1457    }
1458
1459    data = iostate.c_cflag & CSIZE;
1460    data = (data == CS5) ? 5 : (data == CS6) ? 6 : (data == CS7) ? 7 : 8;
1461
1462    stop = (iostate.c_cflag & CSTOPB) ? 2 : 1;
1463#endif /* USE_TERMIO */
1464
1465#ifdef USE_SGTTY
1466    baud = TtyGetBaud(iostate.sg_ospeed);
1467
1468    parity = 'n';
1469    if (iostate.sg_flags & EVENP) {
1470	parity = 'e';
1471    } else if (iostate.sg_flags & ODDP) {
1472	parity = 'o';
1473    }
1474
1475    data = (iostate.sg_flags & (EVENP | ODDP)) ? 7 : 8;
1476
1477    stop = 1;
1478#endif /* USE_SGTTY */
1479
1480    ttyPtr->baud    = baud;
1481    ttyPtr->parity  = parity;
1482    ttyPtr->data    = data;
1483    ttyPtr->stop    = stop;
1484}
1485
1486/*
1487 *---------------------------------------------------------------------------
1488 *
1489 * TtySetAttributes --
1490 *
1491 *	Set the current attributes of the specified serial device.
1492 *
1493 * Results:
1494 *	None.
1495 *
1496 * Side effects:
1497 *	None.
1498 *
1499 *---------------------------------------------------------------------------
1500 */
1501
1502static void
1503TtySetAttributes(fd, ttyPtr)
1504    int fd;			/* Open file descriptor for serial port to
1505				 * be modified. */
1506    TtyAttrs *ttyPtr;		/* Buffer containing new attributes for
1507				 * serial port. */
1508{
1509    IOSTATE iostate;
1510
1511#ifdef USE_TERMIOS
1512    int parity, data, flag;
1513
1514    GETIOSTATE(fd, &iostate);
1515    cfsetospeed(&iostate, TtyGetSpeed(ttyPtr->baud));
1516    cfsetispeed(&iostate, TtyGetSpeed(ttyPtr->baud));
1517
1518    flag = 0;
1519    parity = ttyPtr->parity;
1520    if (parity != 'n') {
1521	flag |= PARENB;
1522#ifdef PAREXT
1523	iostate.c_cflag &= ~PAREXT;
1524	if ((parity == 'm') || (parity == 's')) {
1525	    flag |= PAREXT;
1526	}
1527#endif /* PAREXT */
1528	if ((parity == 'm') || (parity == 'o')) {
1529	    flag |= PARODD;
1530	}
1531    }
1532    data = ttyPtr->data;
1533    flag |= (data == 5) ? CS5 : (data == 6) ? CS6 : (data == 7) ? CS7 : CS8;
1534    if (ttyPtr->stop == 2) {
1535	flag |= CSTOPB;
1536    }
1537
1538    iostate.c_cflag &= ~(PARENB | PARODD | CSIZE | CSTOPB);
1539    iostate.c_cflag |= flag;
1540
1541#endif	/* USE_TERMIOS */
1542
1543#ifdef USE_TERMIO
1544    int parity, data, flag;
1545
1546    GETIOSTATE(fd, &iostate);
1547    iostate.c_cflag &= ~CBAUD;
1548    iostate.c_cflag |= TtyGetSpeed(ttyPtr->baud);
1549
1550    flag = 0;
1551    parity = ttyPtr->parity;
1552    if (parity != 'n') {
1553	flag |= PARENB;
1554	if ((parity == 'm') || (parity == 's')) {
1555	    flag |= PAREXT;
1556	}
1557	if ((parity == 'm') || (parity == 'o')) {
1558	    flag |= PARODD;
1559	}
1560    }
1561    data = ttyPtr->data;
1562    flag |= (data == 5) ? CS5 : (data == 6) ? CS6 : (data == 7) ? CS7 : CS8;
1563    if (ttyPtr->stop == 2) {
1564	flag |= CSTOPB;
1565    }
1566
1567    iostate.c_cflag &= ~(PARENB | PARODD | PAREXT | CSIZE | CSTOPB);
1568    iostate.c_cflag |= flag;
1569
1570#endif	/* USE_TERMIO */
1571
1572#ifdef USE_SGTTY
1573    int parity;
1574
1575    GETIOSTATE(fd, &iostate);
1576    iostate.sg_ospeed = TtyGetSpeed(ttyPtr->baud);
1577    iostate.sg_ispeed = TtyGetSpeed(ttyPtr->baud);
1578
1579    parity = ttyPtr->parity;
1580    if (parity == 'e') {
1581	iostate.sg_flags &= ~ODDP;
1582	iostate.sg_flags |= EVENP;
1583    } else if (parity == 'o') {
1584	iostate.sg_flags &= ~EVENP;
1585	iostate.sg_flags |= ODDP;
1586    }
1587#endif	/* USE_SGTTY */
1588
1589    SETIOSTATE(fd, &iostate);
1590}
1591
1592/*
1593 *---------------------------------------------------------------------------
1594 *
1595 * TtyParseMode --
1596 *
1597 *	Parse the "-mode" argument to the fconfigure command.  The argument
1598 *	is of the form baud,parity,data,stop.
1599 *
1600 * Results:
1601 *	The return value is TCL_OK if the argument was successfully
1602 *	parsed, TCL_ERROR otherwise.  If TCL_ERROR is returned, an
1603 *	error message is left in the interp's result (if interp is non-NULL).
1604 *
1605 * Side effects:
1606 *	None.
1607 *
1608 *---------------------------------------------------------------------------
1609 */
1610
1611static int
1612TtyParseMode(interp, mode, speedPtr, parityPtr, dataPtr, stopPtr)
1613    Tcl_Interp *interp;		/* If non-NULL, interp for error return. */
1614    CONST char *mode;		/* Mode string to be parsed. */
1615    int *speedPtr;		/* Filled with baud rate from mode string. */
1616    int *parityPtr;		/* Filled with parity from mode string. */
1617    int *dataPtr;		/* Filled with data bits from mode string. */
1618    int *stopPtr;		/* Filled with stop bits from mode string. */
1619{
1620    int i, end;
1621    char parity;
1622    static char *bad = "bad value for -mode";
1623
1624    i = sscanf(mode, "%d,%c,%d,%d%n", speedPtr, &parity, dataPtr,
1625	    stopPtr, &end);
1626    if ((i != 4) || (mode[end] != '\0')) {
1627	if (interp != NULL) {
1628	    Tcl_AppendResult(interp, bad, ": should be baud,parity,data,stop",
1629		    NULL);
1630	}
1631	return TCL_ERROR;
1632    }
1633    /*
1634     * Only allow setting mark/space parity on platforms that support it
1635     * Make sure to allow for the case where strchr is a macro.
1636     * [Bug: 5089]
1637     */
1638    if (
1639#if defined(PAREXT) || defined(USE_TERMIO)
1640	strchr("noems", parity) == NULL
1641#else
1642	strchr("noe", parity) == NULL
1643#endif /* PAREXT|USE_TERMIO */
1644	) {
1645	if (interp != NULL) {
1646	    Tcl_AppendResult(interp, bad,
1647#if defined(PAREXT) || defined(USE_TERMIO)
1648		    " parity: should be n, o, e, m, or s",
1649#else
1650		    " parity: should be n, o, or e",
1651#endif /* PAREXT|USE_TERMIO */
1652		    NULL);
1653	}
1654	return TCL_ERROR;
1655    }
1656    *parityPtr = parity;
1657    if ((*dataPtr < 5) || (*dataPtr > 8)) {
1658	if (interp != NULL) {
1659	    Tcl_AppendResult(interp, bad, " data: should be 5, 6, 7, or 8",
1660		    NULL);
1661	}
1662	return TCL_ERROR;
1663    }
1664    if ((*stopPtr < 0) || (*stopPtr > 2)) {
1665	if (interp != NULL) {
1666	    Tcl_AppendResult(interp, bad, " stop: should be 1 or 2", NULL);
1667	}
1668	return TCL_ERROR;
1669    }
1670    return TCL_OK;
1671}
1672
1673/*
1674 *---------------------------------------------------------------------------
1675 *
1676 * TtyInit --
1677 *
1678 *	Given file descriptor that refers to a serial port,
1679 *	initialize the serial port to a set of sane values so that
1680 *	Tcl can talk to a device located on the serial port.
1681 *	Note that no initialization happens if the initialize flag
1682 *	is not set; this is necessary for the correct handling of
1683 *	UNIX console TTYs at startup.
1684 *
1685 * Results:
1686 *	A pointer to a FileState suitable for use with Tcl_CreateChannel
1687 *	and the ttyChannelType structure.
1688 *
1689 * Side effects:
1690 *	Serial device initialized to non-blocking raw mode, similar to
1691 *	sockets (if initialize flag is non-zero.)  All other modes can
1692 *	be simulated on top of this in Tcl.
1693 *
1694 *---------------------------------------------------------------------------
1695 */
1696
1697static FileState *
1698TtyInit(fd, initialize)
1699    int fd;			/* Open file descriptor for serial port to
1700				 * be initialized. */
1701    int initialize;
1702{
1703    TtyState *ttyPtr;
1704
1705    ttyPtr = (TtyState *) ckalloc((unsigned) sizeof(TtyState));
1706    GETIOSTATE(fd, &ttyPtr->savedState);
1707    ttyPtr->stateUpdated = 0;
1708    if (initialize) {
1709	IOSTATE iostate = ttyPtr->savedState;
1710
1711#if defined(USE_TERMIOS) || defined(USE_TERMIO)
1712	if (iostate.c_iflag != IGNBRK ||
1713		iostate.c_oflag != 0 ||
1714		iostate.c_lflag != 0 ||
1715		iostate.c_cflag & CREAD ||
1716		iostate.c_cc[VMIN] != 1 ||
1717		iostate.c_cc[VTIME] != 0) {
1718	    ttyPtr->stateUpdated = 1;
1719	}
1720	iostate.c_iflag = IGNBRK;
1721	iostate.c_oflag = 0;
1722	iostate.c_lflag = 0;
1723	iostate.c_cflag |= CREAD;
1724	iostate.c_cc[VMIN] = 1;
1725	iostate.c_cc[VTIME] = 0;
1726#endif	/* USE_TERMIOS|USE_TERMIO */
1727
1728#ifdef USE_SGTTY
1729	if ((iostate.sg_flags & (EVENP | ODDP)) ||
1730		!(iostate.sg_flags & RAW)) {
1731	    ttyPtr->stateUpdated = 1;
1732	}
1733	iostate.sg_flags &= (EVENP | ODDP);
1734	iostate.sg_flags |= RAW;
1735#endif	/* USE_SGTTY */
1736
1737	/*
1738	 * Only update if we're changing anything to avoid possible
1739	 * blocking.
1740	 */
1741	if (ttyPtr->stateUpdated) {
1742	    SETIOSTATE(fd, &iostate);
1743	}
1744    }
1745
1746    return &ttyPtr->fs;
1747}
1748#endif	/* SUPPORTS_TTY */
1749
1750/*
1751 *----------------------------------------------------------------------
1752 *
1753 * TclpOpenFileChannel --
1754 *
1755 *	Open an file based channel on Unix systems.
1756 *
1757 * Results:
1758 *	The new channel or NULL. If NULL, the output argument
1759 *	errorCodePtr is set to a POSIX error and an error message is
1760 *	left in the interp's result if interp is not NULL.
1761 *
1762 * Side effects:
1763 *	May open the channel and may cause creation of a file on the
1764 *	file system.
1765 *
1766 *----------------------------------------------------------------------
1767 */
1768
1769Tcl_Channel
1770TclpOpenFileChannel(interp, pathPtr, mode, permissions)
1771    Tcl_Interp *interp;			/* Interpreter for error reporting;
1772					 * can be NULL. */
1773    Tcl_Obj *pathPtr;			/* Name of file to open. */
1774    int mode;				/* POSIX open mode. */
1775    int permissions;			/* If the open involves creating a
1776					 * file, with what modes to create
1777					 * it? */
1778{
1779    int fd, channelPermissions;
1780    FileState *fsPtr;
1781    CONST char *native, *translation;
1782    char channelName[16 + TCL_INTEGER_SPACE];
1783    Tcl_ChannelType *channelTypePtr;
1784#ifdef SUPPORTS_TTY
1785    int ctl_tty;
1786#endif /* SUPPORTS_TTY */
1787#ifdef DEPRECATED
1788    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1789#endif /* DEPRECATED */
1790
1791    switch (mode & (O_RDONLY | O_WRONLY | O_RDWR)) {
1792	case O_RDONLY:
1793	    channelPermissions = TCL_READABLE;
1794	    break;
1795	case O_WRONLY:
1796	    channelPermissions = TCL_WRITABLE;
1797	    break;
1798	case O_RDWR:
1799	    channelPermissions = (TCL_READABLE | TCL_WRITABLE);
1800	    break;
1801	default:
1802	    /*
1803	     * This may occurr if modeString was "", for example.
1804	     */
1805	    panic("TclpOpenFileChannel: invalid mode value");
1806	    return NULL;
1807    }
1808
1809    native = Tcl_FSGetNativePath(pathPtr);
1810    if (native == NULL) {
1811	return NULL;
1812    }
1813    fd = TclOSopen(native, mode, permissions);
1814#ifdef SUPPORTS_TTY
1815    ctl_tty = (strcmp (native, "/dev/tty") == 0);
1816#endif /* SUPPORTS_TTY */
1817
1818    if (fd < 0) {
1819	if (interp != (Tcl_Interp *) NULL) {
1820	    Tcl_AppendResult(interp, "couldn't open \"",
1821		    Tcl_GetString(pathPtr), "\": ",
1822		    Tcl_PosixError(interp), (char *) NULL);
1823	}
1824	return NULL;
1825    }
1826
1827    /*
1828     * Set close-on-exec flag on the fd so that child processes will not
1829     * inherit this fd.
1830     */
1831
1832    fcntl(fd, F_SETFD, FD_CLOEXEC);
1833
1834    sprintf(channelName, "file%d", fd);
1835
1836#ifdef SUPPORTS_TTY
1837    if (!ctl_tty && isatty(fd)) {
1838	/*
1839	 * Initialize the serial port to a set of sane parameters.
1840	 * Especially important if the remote device is set to echo and
1841	 * the serial port driver was also set to echo -- as soon as a char
1842	 * were sent to the serial port, the remote device would echo it,
1843	 * then the serial driver would echo it back to the device, etc.
1844	 */
1845
1846	translation = "auto crlf";
1847	channelTypePtr = &ttyChannelType;
1848	fsPtr = TtyInit(fd, 1);
1849    } else
1850#endif	/* SUPPORTS_TTY */
1851    {
1852	translation = NULL;
1853	channelTypePtr = &fileChannelType;
1854	fsPtr = (FileState *) ckalloc((unsigned) sizeof(FileState));
1855    }
1856
1857#ifdef DEPRECATED
1858    if (channelTypePtr == &fileChannelType) {
1859        /* TIP #218. Removed the code inserting the new structure
1860	 * into the global list. This is now handled in the thread
1861	 * action callbacks, and only there.
1862	 */
1863        fsPtr->nextPtr = NULL;
1864    }
1865#endif /* DEPRECATED */
1866    fsPtr->validMask = channelPermissions | TCL_EXCEPTION;
1867    fsPtr->fd = fd;
1868
1869    fsPtr->channel = Tcl_CreateChannel(channelTypePtr, channelName,
1870	    (ClientData) fsPtr, channelPermissions);
1871
1872    if (translation != NULL) {
1873	/*
1874	 * Gotcha.  Most modems need a "\r" at the end of the command
1875	 * sequence.  If you just send "at\n", the modem will not respond
1876	 * with "OK" because it never got a "\r" to actually invoke the
1877	 * command.  So, by default, newlines are translated to "\r\n" on
1878	 * output to avoid "bug" reports that the serial port isn't working.
1879	 */
1880
1881	if (Tcl_SetChannelOption(interp, fsPtr->channel, "-translation",
1882		translation) != TCL_OK) {
1883	    Tcl_Close(NULL, fsPtr->channel);
1884	    return NULL;
1885	}
1886    }
1887
1888    return fsPtr->channel;
1889}
1890
1891/*
1892 *----------------------------------------------------------------------
1893 *
1894 * Tcl_MakeFileChannel --
1895 *
1896 *	Makes a Tcl_Channel from an existing OS level file handle.
1897 *
1898 * Results:
1899 *	The Tcl_Channel created around the preexisting OS level file handle.
1900 *
1901 * Side effects:
1902 *	None.
1903 *
1904 *----------------------------------------------------------------------
1905 */
1906
1907Tcl_Channel
1908Tcl_MakeFileChannel(handle, mode)
1909    ClientData handle;		/* OS level handle. */
1910    int mode;			/* ORed combination of TCL_READABLE and
1911				 * TCL_WRITABLE to indicate file mode. */
1912{
1913    FileState *fsPtr;
1914    char channelName[16 + TCL_INTEGER_SPACE];
1915    int fd = (int) handle;
1916    Tcl_ChannelType *channelTypePtr;
1917#ifdef DEPRECATED
1918    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1919#endif /* DEPRECATED */
1920    struct sockaddr sockaddr;
1921    socklen_t sockaddrLen = sizeof(sockaddr);
1922
1923    if (mode == 0) {
1924	return NULL;
1925    }
1926
1927
1928    /*
1929     * Look to see if a channel with this fd and the same mode already exists.
1930     * If the fd is used, but the mode doesn't match, return NULL.
1931     */
1932
1933#ifdef DEPRECATED
1934    for (fsPtr = tsdPtr->firstFilePtr; fsPtr != NULL; fsPtr = fsPtr->nextPtr) {
1935	if (fsPtr->fd == fd) {
1936	    return ((mode|TCL_EXCEPTION) == fsPtr->validMask) ?
1937		    fsPtr->channel : NULL;
1938	}
1939    }
1940#endif /* DEPRECATED */
1941
1942    sockaddr.sa_family = AF_UNSPEC;
1943
1944#ifdef SUPPORTS_TTY
1945    if (isatty(fd)) {
1946	fsPtr = TtyInit(fd, 0);
1947	channelTypePtr = &ttyChannelType;
1948	sprintf(channelName, "serial%d", fd);
1949    } else
1950#endif /* SUPPORTS_TTY */
1951    if (getsockname(fd, (struct sockaddr *)&sockaddr, &sockaddrLen) == 0
1952            && sockaddrLen > 0
1953            && sockaddr.sa_family == AF_INET) {
1954        return MakeTcpClientChannelMode((ClientData) fd, mode);
1955    } else {
1956        channelTypePtr = &fileChannelType;
1957        fsPtr = (FileState *) ckalloc((unsigned) sizeof(FileState));
1958        sprintf(channelName, "file%d", fd);
1959    }
1960
1961#ifdef DEPRECATED
1962    if (channelTypePtr == &fileChannelType) {
1963        fsPtr->nextPtr = tsdPtr->firstFilePtr;
1964        tsdPtr->firstFilePtr = fsPtr;
1965    }
1966#endif /* DEPRECATED */
1967    fsPtr->fd = fd;
1968    fsPtr->validMask = mode | TCL_EXCEPTION;
1969    fsPtr->channel = Tcl_CreateChannel(channelTypePtr, channelName,
1970	    (ClientData) fsPtr, mode);
1971
1972    return fsPtr->channel;
1973}
1974
1975/*
1976 *----------------------------------------------------------------------
1977 *
1978 * TcpBlockModeProc --
1979 *
1980 *	This procedure is invoked by the generic IO level to set blocking
1981 *	and nonblocking mode on a TCP socket based channel.
1982 *
1983 * Results:
1984 *	0 if successful, errno when failed.
1985 *
1986 * Side effects:
1987 *	Sets the device into blocking or nonblocking mode.
1988 *
1989 *----------------------------------------------------------------------
1990 */
1991
1992	/* ARGSUSED */
1993static int
1994TcpBlockModeProc(instanceData, mode)
1995    ClientData instanceData;		/* Socket state. */
1996    int mode;				/* The mode to set. Can be one of
1997					 * TCL_MODE_BLOCKING or
1998					 * TCL_MODE_NONBLOCKING. */
1999{
2000    TcpState *statePtr = (TcpState *) instanceData;
2001    int setting;
2002
2003#ifndef USE_FIONBIO
2004    setting = fcntl(statePtr->fd, F_GETFL);
2005    if (mode == TCL_MODE_BLOCKING) {
2006	statePtr->flags &= (~(TCP_ASYNC_SOCKET));
2007	setting &= (~(O_NONBLOCK));
2008    } else {
2009	statePtr->flags |= TCP_ASYNC_SOCKET;
2010	setting |= O_NONBLOCK;
2011    }
2012    if (fcntl(statePtr->fd, F_SETFL, setting) < 0) {
2013	return errno;
2014    }
2015#else /* USE_FIONBIO */
2016    if (mode == TCL_MODE_BLOCKING) {
2017	statePtr->flags &= (~(TCP_ASYNC_SOCKET));
2018	setting = 0;
2019	if (ioctl(statePtr->fd, (int) FIONBIO, &setting) == -1) {
2020	    return errno;
2021	}
2022    } else {
2023	statePtr->flags |= TCP_ASYNC_SOCKET;
2024	setting = 1;
2025	if (ioctl(statePtr->fd, (int) FIONBIO, &setting) == -1) {
2026	    return errno;
2027	}
2028    }
2029#endif /* !USE_FIONBIO */
2030
2031    return 0;
2032}
2033
2034/*
2035 *----------------------------------------------------------------------
2036 *
2037 * WaitForConnect --
2038 *
2039 *	Waits for a connection on an asynchronously opened socket to
2040 *	be completed.
2041 *
2042 * Results:
2043 *	None.
2044 *
2045 * Side effects:
2046 *	The socket is connected after this function returns.
2047 *
2048 *----------------------------------------------------------------------
2049 */
2050
2051static int
2052WaitForConnect(statePtr, errorCodePtr)
2053    TcpState *statePtr;		/* State of the socket. */
2054    int *errorCodePtr;		/* Where to store errors? */
2055{
2056    int timeOut;		/* How long to wait. */
2057    int state;			/* Of calling TclWaitForFile. */
2058    int flags;			/* fcntl flags for the socket. */
2059
2060    /*
2061     * If an asynchronous connect is in progress, attempt to wait for it
2062     * to complete before reading.
2063     */
2064
2065    if (statePtr->flags & TCP_ASYNC_CONNECT) {
2066	if (statePtr->flags & TCP_ASYNC_SOCKET) {
2067	    timeOut = 0;
2068	} else {
2069	    timeOut = -1;
2070	}
2071	errno = 0;
2072	state = TclUnixWaitForFile(statePtr->fd,
2073		TCL_WRITABLE | TCL_EXCEPTION, timeOut);
2074	if (!(statePtr->flags & TCP_ASYNC_SOCKET)) {
2075#ifndef USE_FIONBIO
2076	    flags = fcntl(statePtr->fd, F_GETFL);
2077	    flags &= (~(O_NONBLOCK));
2078	    (void) fcntl(statePtr->fd, F_SETFL, flags);
2079#else /* USE_FIONBIO */
2080	    flags = 0;
2081	    (void) ioctl(statePtr->fd, FIONBIO, &flags);
2082#endif /* !USE_FIONBIO */
2083	}
2084	if (state & TCL_EXCEPTION) {
2085	    return -1;
2086	}
2087	if (state & TCL_WRITABLE) {
2088	    statePtr->flags &= (~(TCP_ASYNC_CONNECT));
2089	} else if (timeOut == 0) {
2090	    *errorCodePtr = errno = EWOULDBLOCK;
2091	    return -1;
2092	}
2093    }
2094    return 0;
2095}
2096
2097/*
2098 *----------------------------------------------------------------------
2099 *
2100 * TcpInputProc --
2101 *
2102 *	This procedure is invoked by the generic IO level to read input
2103 *	from a TCP socket based channel.
2104 *
2105 *	NOTE: We cannot share code with FilePipeInputProc because here
2106 *	we must use recv to obtain the input from the channel, not read.
2107 *
2108 * Results:
2109 *	The number of bytes read is returned or -1 on error. An output
2110 *	argument contains the POSIX error code on error, or zero if no
2111 *	error occurred.
2112 *
2113 * Side effects:
2114 *	Reads input from the input device of the channel.
2115 *
2116 *----------------------------------------------------------------------
2117 */
2118
2119	/* ARGSUSED */
2120static int
2121TcpInputProc(instanceData, buf, bufSize, errorCodePtr)
2122    ClientData instanceData;		/* Socket state. */
2123    char *buf;				/* Where to store data read. */
2124    int bufSize;			/* How much space is available
2125					 * in the buffer? */
2126    int *errorCodePtr;			/* Where to store error code. */
2127{
2128    TcpState *statePtr = (TcpState *) instanceData;
2129    int bytesRead, state;
2130
2131    *errorCodePtr = 0;
2132    state = WaitForConnect(statePtr, errorCodePtr);
2133    if (state != 0) {
2134	return -1;
2135    }
2136    bytesRead = recv(statePtr->fd, buf, (size_t) bufSize, 0);
2137    if (bytesRead > -1) {
2138	return bytesRead;
2139    }
2140    if (errno == ECONNRESET) {
2141	/*
2142	 * Turn ECONNRESET into a soft EOF condition.
2143	 */
2144
2145	return 0;
2146    }
2147    *errorCodePtr = errno;
2148    return -1;
2149}
2150
2151/*
2152 *----------------------------------------------------------------------
2153 *
2154 * TcpOutputProc --
2155 *
2156 *	This procedure is invoked by the generic IO level to write output
2157 *	to a TCP socket based channel.
2158 *
2159 *	NOTE: We cannot share code with FilePipeOutputProc because here
2160 *	we must use send, not write, to get reliable error reporting.
2161 *
2162 * Results:
2163 *	The number of bytes written is returned. An output argument is
2164 *	set to a POSIX error code if an error occurred, or zero.
2165 *
2166 * Side effects:
2167 *	Writes output on the output device of the channel.
2168 *
2169 *----------------------------------------------------------------------
2170 */
2171
2172static int
2173TcpOutputProc(instanceData, buf, toWrite, errorCodePtr)
2174    ClientData instanceData;		/* Socket state. */
2175    CONST char *buf;			/* The data buffer. */
2176    int toWrite;			/* How many bytes to write? */
2177    int *errorCodePtr;			/* Where to store error code. */
2178{
2179    TcpState *statePtr = (TcpState *) instanceData;
2180    int written;
2181    int state;				/* Of waiting for connection. */
2182
2183    *errorCodePtr = 0;
2184    state = WaitForConnect(statePtr, errorCodePtr);
2185    if (state != 0) {
2186	return -1;
2187    }
2188    written = send(statePtr->fd, buf, (size_t) toWrite, 0);
2189    if (written > -1) {
2190	return written;
2191    }
2192    *errorCodePtr = errno;
2193    return -1;
2194}
2195
2196/*
2197 *----------------------------------------------------------------------
2198 *
2199 * TcpCloseProc --
2200 *
2201 *	This procedure is invoked by the generic IO level to perform
2202 *	channel-type-specific cleanup when a TCP socket based channel
2203 *	is closed.
2204 *
2205 * Results:
2206 *	0 if successful, the value of errno if failed.
2207 *
2208 * Side effects:
2209 *	Closes the socket of the channel.
2210 *
2211 *----------------------------------------------------------------------
2212 */
2213
2214	/* ARGSUSED */
2215static int
2216TcpCloseProc(instanceData, interp)
2217    ClientData instanceData;	/* The socket to close. */
2218    Tcl_Interp *interp;		/* For error reporting - unused. */
2219{
2220    TcpState *statePtr = (TcpState *) instanceData;
2221    int errorCode = 0;
2222
2223    /*
2224     * Delete a file handler that may be active for this socket if this
2225     * is a server socket - the file handler was created automatically
2226     * by Tcl as part of the mechanism to accept new client connections.
2227     * Channel handlers are already deleted in the generic IO channel
2228     * closing code that called this function, so we do not have to
2229     * delete them here.
2230     */
2231
2232    Tcl_DeleteFileHandler(statePtr->fd);
2233
2234    if (close(statePtr->fd) < 0) {
2235	errorCode = errno;
2236    }
2237    ckfree((char *) statePtr);
2238
2239    return errorCode;
2240}
2241
2242/*
2243 *----------------------------------------------------------------------
2244 *
2245 * TcpGetOptionProc --
2246 *
2247 *	Computes an option value for a TCP socket based channel, or a
2248 *	list of all options and their values.
2249 *
2250 *	Note: This code is based on code contributed by John Haxby.
2251 *
2252 * Results:
2253 *	A standard Tcl result. The value of the specified option or a
2254 *	list of all options and their values is returned in the
2255 *	supplied DString. Sets Error message if needed.
2256 *
2257 * Side effects:
2258 *	None.
2259 *
2260 *----------------------------------------------------------------------
2261 */
2262
2263static int
2264TcpGetOptionProc(instanceData, interp, optionName, dsPtr)
2265    ClientData instanceData;	 /* Socket state. */
2266    Tcl_Interp *interp;		 /* For error reporting - can be NULL. */
2267    CONST char *optionName;	 /* Name of the option to
2268				  * retrieve the value for, or
2269				  * NULL to get all options and
2270				  * their values. */
2271    Tcl_DString *dsPtr;		 /* Where to store the computed
2272				  * value; initialized by caller. */
2273{
2274    TcpState *statePtr = (TcpState *) instanceData;
2275    struct sockaddr_in sockname;
2276    struct sockaddr_in peername;
2277    struct hostent *hostEntPtr;
2278    socklen_t size = sizeof(struct sockaddr_in);
2279    size_t len = 0;
2280    char buf[TCL_INTEGER_SPACE];
2281
2282    if (optionName != (char *) NULL) {
2283	len = strlen(optionName);
2284    }
2285
2286    if ((len > 1) && (optionName[1] == 'e') &&
2287	    (strncmp(optionName, "-error", len) == 0)) {
2288	socklen_t optlen = sizeof(int);
2289	int err, ret;
2290
2291	ret = getsockopt(statePtr->fd, SOL_SOCKET, SO_ERROR,
2292		(char *)&err, &optlen);
2293	if (ret < 0) {
2294	    err = errno;
2295	}
2296	if (err != 0) {
2297	    Tcl_DStringAppend(dsPtr, Tcl_ErrnoMsg(err), -1);
2298	}
2299	return TCL_OK;
2300    }
2301
2302    if ((len == 0) ||
2303	    ((len > 1) && (optionName[1] == 'p') &&
2304		    (strncmp(optionName, "-peername", len) == 0))) {
2305	if (getpeername(statePtr->fd, (struct sockaddr *) &peername,
2306		&size) >= 0) {
2307	    if (len == 0) {
2308		Tcl_DStringAppendElement(dsPtr, "-peername");
2309		Tcl_DStringStartSublist(dsPtr);
2310	    }
2311	    Tcl_DStringAppendElement(dsPtr, inet_ntoa(peername.sin_addr));
2312	    hostEntPtr = TclpGetHostByAddr(			/* INTL: Native. */
2313		    (char *) &peername.sin_addr,
2314		    sizeof(peername.sin_addr), AF_INET);
2315	    if (hostEntPtr != (struct hostent *) NULL) {
2316		Tcl_DString ds;
2317
2318		Tcl_ExternalToUtfDString(NULL, hostEntPtr->h_name, -1, &ds);
2319		Tcl_DStringAppendElement(dsPtr, Tcl_DStringValue(&ds));
2320		Tcl_DStringFree(&ds);
2321	    } else {
2322		Tcl_DStringAppendElement(dsPtr, inet_ntoa(peername.sin_addr));
2323	    }
2324	    TclFormatInt(buf, ntohs(peername.sin_port));
2325	    Tcl_DStringAppendElement(dsPtr, buf);
2326	    if (len == 0) {
2327		Tcl_DStringEndSublist(dsPtr);
2328	    } else {
2329		return TCL_OK;
2330	    }
2331	} else {
2332	    /*
2333	     * getpeername failed - but if we were asked for all the options
2334	     * (len==0), don't flag an error at that point because it could
2335	     * be an fconfigure request on a server socket. (which have
2336	     * no peer). same must be done on win&mac.
2337	     */
2338
2339	    if (len) {
2340		if (interp) {
2341		    Tcl_AppendResult(interp, "can't get peername: ",
2342			    Tcl_PosixError(interp), (char *) NULL);
2343		}
2344		return TCL_ERROR;
2345	    }
2346	}
2347    }
2348
2349    if ((len == 0) ||
2350	    ((len > 1) && (optionName[1] == 's') &&
2351	    (strncmp(optionName, "-sockname", len) == 0))) {
2352	if (getsockname(statePtr->fd, (struct sockaddr *) &sockname,
2353		&size) >= 0) {
2354	    if (len == 0) {
2355		Tcl_DStringAppendElement(dsPtr, "-sockname");
2356		Tcl_DStringStartSublist(dsPtr);
2357	    }
2358	    Tcl_DStringAppendElement(dsPtr, inet_ntoa(sockname.sin_addr));
2359            if (sockname.sin_addr.s_addr == INADDR_ANY) {
2360		/*
2361		 * We don't want to resolve INADDR_ANY; it can sometimes cause
2362		 * problems (and never has a name).
2363		 */
2364
2365                hostEntPtr = NULL;
2366            } else {
2367                hostEntPtr = TclpGetHostByAddr(		/* INTL: Native. */
2368                                               (char *) &sockname.sin_addr,
2369                                               sizeof(sockname.sin_addr), AF_INET);
2370            }
2371	    if (hostEntPtr != (struct hostent *) NULL) {
2372		Tcl_DString ds;
2373
2374		Tcl_ExternalToUtfDString(NULL, hostEntPtr->h_name, -1, &ds);
2375		Tcl_DStringAppendElement(dsPtr, Tcl_DStringValue(&ds));
2376		Tcl_DStringFree(&ds);
2377	    } else {
2378		Tcl_DStringAppendElement(dsPtr, inet_ntoa(sockname.sin_addr));
2379	    }
2380	    TclFormatInt(buf, ntohs(sockname.sin_port));
2381	    Tcl_DStringAppendElement(dsPtr, buf);
2382	    if (len == 0) {
2383		Tcl_DStringEndSublist(dsPtr);
2384	    } else {
2385		return TCL_OK;
2386	    }
2387	} else {
2388	    if (interp) {
2389		Tcl_AppendResult(interp, "can't get sockname: ",
2390			Tcl_PosixError(interp), (char *) NULL);
2391	    }
2392	    return TCL_ERROR;
2393	}
2394    }
2395
2396    if (len > 0) {
2397	return Tcl_BadChannelOption(interp, optionName, "peername sockname");
2398    }
2399
2400    return TCL_OK;
2401}
2402
2403/*
2404 *----------------------------------------------------------------------
2405 *
2406 * TcpWatchProc --
2407 *
2408 *	Initialize the notifier to watch the fd from this channel.
2409 *
2410 * Results:
2411 *	None.
2412 *
2413 * Side effects:
2414 *	Sets up the notifier so that a future event on the channel will
2415 *	be seen by Tcl.
2416 *
2417 *----------------------------------------------------------------------
2418 */
2419
2420static void
2421TcpWatchProc(instanceData, mask)
2422    ClientData instanceData;		/* The socket state. */
2423    int mask;				/* Events of interest; an OR-ed
2424					 * combination of TCL_READABLE,
2425					 * TCL_WRITABLE and TCL_EXCEPTION. */
2426{
2427    TcpState *statePtr = (TcpState *) instanceData;
2428
2429    /*
2430     * Make sure we don't mess with server sockets since they will never
2431     * be readable or writable at the Tcl level.  This keeps Tcl scripts
2432     * from interfering with the -accept behavior.
2433     */
2434
2435    if (!statePtr->acceptProc) {
2436	if (mask) {
2437	    Tcl_CreateFileHandler(statePtr->fd, mask,
2438		    (Tcl_FileProc *) Tcl_NotifyChannel,
2439		    (ClientData) statePtr->channel);
2440	} else {
2441	    Tcl_DeleteFileHandler(statePtr->fd);
2442	}
2443    }
2444}
2445
2446/*
2447 *----------------------------------------------------------------------
2448 *
2449 * TcpGetHandleProc --
2450 *
2451 *	Called from Tcl_GetChannelHandle to retrieve OS handles from inside
2452 *	a TCP socket based channel.
2453 *
2454 * Results:
2455 *	Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if
2456 *	there is no handle for the specified direction.
2457 *
2458 * Side effects:
2459 *	None.
2460 *
2461 *----------------------------------------------------------------------
2462 */
2463
2464	/* ARGSUSED */
2465static int
2466TcpGetHandleProc(instanceData, direction, handlePtr)
2467    ClientData instanceData;	/* The socket state. */
2468    int direction;		/* Not used. */
2469    ClientData *handlePtr;	/* Where to store the handle.  */
2470{
2471    TcpState *statePtr = (TcpState *) instanceData;
2472
2473    *handlePtr = (ClientData)statePtr->fd;
2474    return TCL_OK;
2475}
2476
2477/*
2478 *----------------------------------------------------------------------
2479 *
2480 * CreateSocket --
2481 *
2482 *	This function opens a new socket in client or server mode
2483 *	and initializes the TcpState structure.
2484 *
2485 * Results:
2486 *	Returns a new TcpState, or NULL with an error in the interp's
2487 *	result, if interp is not NULL.
2488 *
2489 * Side effects:
2490 *	Opens a socket.
2491 *
2492 *----------------------------------------------------------------------
2493 */
2494
2495static TcpState *
2496CreateSocket(interp, port, host, server, myaddr, myport, async)
2497    Tcl_Interp *interp;		/* For error reporting; can be NULL. */
2498    int port;			/* Port number to open. */
2499    CONST char *host;		/* Name of host on which to open port.
2500				 * NULL implies INADDR_ANY */
2501    int server;			/* 1 if socket should be a server socket,
2502				 * else 0 for a client socket. */
2503    CONST char *myaddr;		/* Optional client-side address */
2504    int myport;			/* Optional client-side port */
2505    int async;			/* If nonzero and creating a client socket,
2506				 * attempt to do an async connect. Otherwise
2507				 * do a synchronous connect or bind. */
2508{
2509    int status, sock, asyncConnect, curState, origState;
2510    struct sockaddr_in sockaddr;	/* socket address */
2511    struct sockaddr_in mysockaddr;	/* Socket address for client */
2512    TcpState *statePtr;
2513
2514    sock = -1;
2515    origState = 0;
2516    if (! CreateSocketAddress(&sockaddr, host, port)) {
2517	goto addressError;
2518    }
2519    if ((myaddr != NULL || myport != 0) &&
2520	    ! CreateSocketAddress(&mysockaddr, myaddr, myport)) {
2521	goto addressError;
2522    }
2523
2524    sock = socket(AF_INET, SOCK_STREAM, 0);
2525    if (sock < 0) {
2526	goto addressError;
2527    }
2528
2529    /*
2530     * Set the close-on-exec flag so that the socket will not get
2531     * inherited by child processes.
2532     */
2533
2534    fcntl(sock, F_SETFD, FD_CLOEXEC);
2535
2536    /*
2537     * Set kernel space buffering
2538     */
2539
2540    TclSockMinimumBuffers(sock, SOCKET_BUFSIZE);
2541
2542    asyncConnect = 0;
2543    status = 0;
2544    if (server) {
2545	/*
2546	 * Set up to reuse server addresses automatically and bind to the
2547	 * specified port.
2548	 */
2549
2550	status = 1;
2551	(void) setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &status,
2552		sizeof(status));
2553	status = bind(sock, (struct sockaddr *) &sockaddr,
2554		sizeof(struct sockaddr));
2555	if (status != -1) {
2556	    status = listen(sock, SOMAXCONN);
2557	}
2558    } else {
2559	if (myaddr != NULL || myport != 0) {
2560	    curState = 1;
2561	    (void) setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
2562		    (char *) &curState, sizeof(curState));
2563	    status = bind(sock, (struct sockaddr *) &mysockaddr,
2564		    sizeof(struct sockaddr));
2565	    if (status < 0) {
2566		goto bindError;
2567	    }
2568	}
2569
2570	/*
2571	 * Attempt to connect. The connect may fail at present with an
2572	 * EINPROGRESS but at a later time it will complete. The caller
2573	 * will set up a file handler on the socket if she is interested in
2574	 * being informed when the connect completes.
2575	 */
2576
2577	if (async) {
2578#ifndef USE_FIONBIO
2579	    origState = fcntl(sock, F_GETFL);
2580	    curState = origState | O_NONBLOCK;
2581	    status = fcntl(sock, F_SETFL, curState);
2582#else /* USE_FIONBIO */
2583	    curState = 1;
2584	    status = ioctl(sock, FIONBIO, &curState);
2585#endif /* !USE_FIONBIO */
2586	} else {
2587	    status = 0;
2588	}
2589	if (status > -1) {
2590	    status = connect(sock, (struct sockaddr *) &sockaddr,
2591		    sizeof(sockaddr));
2592	    if (status < 0) {
2593		if (errno == EINPROGRESS) {
2594		    asyncConnect = 1;
2595		    status = 0;
2596		}
2597	    } else {
2598		/*
2599		 * Here we are if the connect succeeds. In case of an
2600		 * asynchronous connect we have to reset the channel to
2601		 * blocking mode.  This appears to happen not very often,
2602		 * but e.g. on a HP 9000/800 under HP-UX B.11.00 we enter
2603		 * this stage. [Bug: 4388]
2604		 */
2605		if (async) {
2606#ifndef USE_FIONBIO
2607		    origState = fcntl(sock, F_GETFL);
2608		    curState = origState & ~(O_NONBLOCK);
2609		    status = fcntl(sock, F_SETFL, curState);
2610#else /* USE_FIONBIO */
2611		    curState = 0;
2612		    status = ioctl(sock, FIONBIO, &curState);
2613#endif /* !USE_FIONBIO */
2614		}
2615	    }
2616	}
2617    }
2618
2619bindError:
2620    if (status < 0) {
2621	if (interp != NULL) {
2622	    Tcl_AppendResult(interp, "couldn't open socket: ",
2623		    Tcl_PosixError(interp), (char *) NULL);
2624	}
2625	if (sock != -1) {
2626	    close(sock);
2627	}
2628	return NULL;
2629    }
2630
2631    /*
2632     * Allocate a new TcpState for this socket.
2633     */
2634
2635    statePtr = (TcpState *) ckalloc((unsigned) sizeof(TcpState));
2636    statePtr->flags = 0;
2637    if (asyncConnect) {
2638	statePtr->flags = TCP_ASYNC_CONNECT;
2639    }
2640    statePtr->fd = sock;
2641
2642    return statePtr;
2643
2644addressError:
2645    if (sock != -1) {
2646	close(sock);
2647    }
2648    if (interp != NULL) {
2649	Tcl_AppendResult(interp, "couldn't open socket: ",
2650		Tcl_PosixError(interp), (char *) NULL);
2651    }
2652    return NULL;
2653}
2654
2655/*
2656 *----------------------------------------------------------------------
2657 *
2658 * CreateSocketAddress --
2659 *
2660 *	This function initializes a sockaddr structure for a host and port.
2661 *
2662 * Results:
2663 *	1 if the host was valid, 0 if the host could not be converted to
2664 *	an IP address.
2665 *
2666 * Side effects:
2667 *	Fills in the *sockaddrPtr structure.
2668 *
2669 *----------------------------------------------------------------------
2670 */
2671
2672static int
2673CreateSocketAddress(sockaddrPtr, host, port)
2674    struct sockaddr_in *sockaddrPtr;	/* Socket address */
2675    CONST char *host;			/* Host.  NULL implies INADDR_ANY */
2676    int port;				/* Port number */
2677{
2678    struct hostent *hostent;		/* Host database entry */
2679    struct in_addr addr;		/* For 64/32 bit madness */
2680
2681    (void) memset((VOID *) sockaddrPtr, '\0', sizeof(struct sockaddr_in));
2682    sockaddrPtr->sin_family = AF_INET;
2683    sockaddrPtr->sin_port = htons((unsigned short) (port & 0xFFFF));
2684    if (host == NULL) {
2685	addr.s_addr = INADDR_ANY;
2686    } else {
2687	Tcl_DString ds;
2688	CONST char *native;
2689
2690	if (host == NULL) {
2691	    native = NULL;
2692	} else {
2693	    native = Tcl_UtfToExternalDString(NULL, host, -1, &ds);
2694	}
2695	addr.s_addr = inet_addr(native);		/* INTL: Native. */
2696	/*
2697	 * This is 0xFFFFFFFF to ensure that it compares as a 32bit -1
2698	 * on either 32 or 64 bits systems.
2699	 */
2700	if (addr.s_addr == 0xFFFFFFFF) {
2701	    hostent = TclpGetHostByName(native);		/* INTL: Native. */
2702	    if (hostent != (struct hostent *) NULL) {
2703		memcpy((VOID *) &addr,
2704			(VOID *) hostent->h_addr_list[0],
2705			(size_t) hostent->h_length);
2706	    } else {
2707#ifdef	EHOSTUNREACH
2708		errno = EHOSTUNREACH;
2709#else /* !EHOSTUNREACH */
2710#ifdef ENXIO
2711		errno = ENXIO;
2712#endif /* ENXIO */
2713#endif /* EHOSTUNREACH */
2714		if (native != NULL) {
2715		    Tcl_DStringFree(&ds);
2716		}
2717		return 0;	/* error */
2718	    }
2719	}
2720	if (native != NULL) {
2721	    Tcl_DStringFree(&ds);
2722	}
2723    }
2724
2725    /*
2726     * NOTE: On 64 bit machines the assignment below is rumored to not
2727     * do the right thing. Please report errors related to this if you
2728     * observe incorrect behavior on 64 bit machines such as DEC Alphas.
2729     * Should we modify this code to do an explicit memcpy?
2730     */
2731
2732    sockaddrPtr->sin_addr.s_addr = addr.s_addr;
2733    return 1;	/* Success. */
2734}
2735
2736/*
2737 *----------------------------------------------------------------------
2738 *
2739 * Tcl_OpenTcpClient --
2740 *
2741 *	Opens a TCP client socket and creates a channel around it.
2742 *
2743 * Results:
2744 *	The channel or NULL if failed.	An error message is returned
2745 *	in the interpreter on failure.
2746 *
2747 * Side effects:
2748 *	Opens a client socket and creates a new channel.
2749 *
2750 *----------------------------------------------------------------------
2751 */
2752
2753Tcl_Channel
2754Tcl_OpenTcpClient(interp, port, host, myaddr, myport, async)
2755    Tcl_Interp *interp;			/* For error reporting; can be NULL. */
2756    int port;				/* Port number to open. */
2757    CONST char *host;			/* Host on which to open port. */
2758    CONST char *myaddr;			/* Client-side address */
2759    int myport;				/* Client-side port */
2760    int async;				/* If nonzero, attempt to do an
2761					 * asynchronous connect. Otherwise
2762					 * we do a blocking connect. */
2763{
2764    TcpState *statePtr;
2765    char channelName[16 + TCL_INTEGER_SPACE];
2766
2767    /*
2768     * Create a new client socket and wrap it in a channel.
2769     */
2770
2771    statePtr = CreateSocket(interp, port, host, 0, myaddr, myport, async);
2772    if (statePtr == NULL) {
2773	return NULL;
2774    }
2775
2776    statePtr->acceptProc = NULL;
2777    statePtr->acceptProcData = (ClientData) NULL;
2778
2779    sprintf(channelName, "sock%d", statePtr->fd);
2780
2781    statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
2782	    (ClientData) statePtr, (TCL_READABLE | TCL_WRITABLE));
2783    if (Tcl_SetChannelOption(interp, statePtr->channel, "-translation",
2784	    "auto crlf") == TCL_ERROR) {
2785	Tcl_Close((Tcl_Interp *) NULL, statePtr->channel);
2786	return NULL;
2787    }
2788    return statePtr->channel;
2789}
2790
2791/*
2792 *----------------------------------------------------------------------
2793 *
2794 * Tcl_MakeTcpClientChannel --
2795 *
2796 *	Creates a Tcl_Channel from an existing client TCP socket.
2797 *
2798 * Results:
2799 *	The Tcl_Channel wrapped around the preexisting TCP socket.
2800 *
2801 * Side effects:
2802 *	None.
2803 *
2804 *----------------------------------------------------------------------
2805 */
2806
2807Tcl_Channel
2808Tcl_MakeTcpClientChannel(sock)
2809    ClientData sock;		/* The socket to wrap up into a channel. */
2810{
2811    return MakeTcpClientChannelMode(sock, (TCL_READABLE | TCL_WRITABLE));
2812}
2813
2814/*
2815 *----------------------------------------------------------------------
2816 *
2817 * MakeTcpClientChannelMode --
2818 *
2819 *	Creates a Tcl_Channel from an existing client TCP socket
2820 *	with given mode.
2821 *
2822 * Results:
2823 *	The Tcl_Channel wrapped around the preexisting TCP socket.
2824 *
2825 * Side effects:
2826 *	None.
2827 *
2828 *----------------------------------------------------------------------
2829 */
2830
2831static Tcl_Channel
2832MakeTcpClientChannelMode(sock, mode)
2833    ClientData sock;		/* The socket to wrap up into a channel. */
2834    int mode;			/* ORed combination of TCL_READABLE and
2835				 * TCL_WRITABLE to indicate file mode. */
2836{
2837    TcpState *statePtr;
2838    char channelName[16 + TCL_INTEGER_SPACE];
2839
2840    statePtr = (TcpState *) ckalloc((unsigned) sizeof(TcpState));
2841    statePtr->fd = (int) sock;
2842    statePtr->flags = 0;
2843    statePtr->acceptProc = NULL;
2844    statePtr->acceptProcData = (ClientData) NULL;
2845
2846    sprintf(channelName, "sock%d", statePtr->fd);
2847
2848    statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
2849	    (ClientData) statePtr, mode);
2850    if (Tcl_SetChannelOption((Tcl_Interp *) NULL, statePtr->channel,
2851	    "-translation", "auto crlf") == TCL_ERROR) {
2852	Tcl_Close((Tcl_Interp *) NULL, statePtr->channel);
2853	return NULL;
2854    }
2855    return statePtr->channel;
2856}
2857
2858/*
2859 *----------------------------------------------------------------------
2860 *
2861 * Tcl_OpenTcpServer --
2862 *
2863 *	Opens a TCP server socket and creates a channel around it.
2864 *
2865 * Results:
2866 *	The channel or NULL if failed. If an error occurred, an
2867 *	error message is left in the interp's result if interp is
2868 *	not NULL.
2869 *
2870 * Side effects:
2871 *	Opens a server socket and creates a new channel.
2872 *
2873 *----------------------------------------------------------------------
2874 */
2875
2876Tcl_Channel
2877Tcl_OpenTcpServer(interp, port, myHost, acceptProc, acceptProcData)
2878    Tcl_Interp *interp;			/* For error reporting - may be
2879					 * NULL. */
2880    int port;				/* Port number to open. */
2881    CONST char *myHost;			/* Name of local host. */
2882    Tcl_TcpAcceptProc *acceptProc;	/* Callback for accepting connections
2883					 * from new clients. */
2884    ClientData acceptProcData;		/* Data for the callback. */
2885{
2886    TcpState *statePtr;
2887    char channelName[16 + TCL_INTEGER_SPACE];
2888
2889    /*
2890     * Create a new client socket and wrap it in a channel.
2891     */
2892
2893    statePtr = CreateSocket(interp, port, myHost, 1, NULL, 0, 0);
2894    if (statePtr == NULL) {
2895	return NULL;
2896    }
2897
2898    statePtr->acceptProc = acceptProc;
2899    statePtr->acceptProcData = acceptProcData;
2900
2901    /*
2902     * Set up the callback mechanism for accepting connections
2903     * from new clients.
2904     */
2905
2906    Tcl_CreateFileHandler(statePtr->fd, TCL_READABLE, TcpAccept,
2907	    (ClientData) statePtr);
2908    sprintf(channelName, "sock%d", statePtr->fd);
2909    statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
2910	    (ClientData) statePtr, 0);
2911    return statePtr->channel;
2912}
2913
2914/*
2915 *----------------------------------------------------------------------
2916 *
2917 * TcpAccept --
2918 *	Accept a TCP socket connection.	 This is called by the event loop.
2919 *
2920 * Results:
2921 *	None.
2922 *
2923 * Side effects:
2924 *	Creates a new connection socket. Calls the registered callback
2925 *	for the connection acceptance mechanism.
2926 *
2927 *----------------------------------------------------------------------
2928 */
2929
2930	/* ARGSUSED */
2931static void
2932TcpAccept(data, mask)
2933    ClientData data;			/* Callback token. */
2934    int mask;				/* Not used. */
2935{
2936    TcpState *sockState;		/* Client data of server socket. */
2937    int newsock;			/* The new client socket */
2938    TcpState *newSockState;		/* State for new socket. */
2939    struct sockaddr_in addr;		/* The remote address */
2940    socklen_t len;				/* For accept interface */
2941    char channelName[16 + TCL_INTEGER_SPACE];
2942
2943    sockState = (TcpState *) data;
2944
2945    len = sizeof(struct sockaddr_in);
2946    newsock = accept(sockState->fd, (struct sockaddr *) &addr, &len);
2947    if (newsock < 0) {
2948	return;
2949    }
2950
2951    /*
2952     * Set close-on-exec flag to prevent the newly accepted socket from
2953     * being inherited by child processes.
2954     */
2955
2956    (void) fcntl(newsock, F_SETFD, FD_CLOEXEC);
2957
2958    newSockState = (TcpState *) ckalloc((unsigned) sizeof(TcpState));
2959
2960    newSockState->flags = 0;
2961    newSockState->fd = newsock;
2962    newSockState->acceptProc = NULL;
2963    newSockState->acceptProcData = NULL;
2964
2965    sprintf(channelName, "sock%d", newsock);
2966    newSockState->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
2967	    (ClientData) newSockState, (TCL_READABLE | TCL_WRITABLE));
2968
2969    Tcl_SetChannelOption(NULL, newSockState->channel, "-translation",
2970	    "auto crlf");
2971
2972    if (sockState->acceptProc != NULL) {
2973	(*sockState->acceptProc)(sockState->acceptProcData,
2974		newSockState->channel, inet_ntoa(addr.sin_addr),
2975		ntohs(addr.sin_port));
2976    }
2977}
2978
2979/*
2980 *----------------------------------------------------------------------
2981 *
2982 * TclpGetDefaultStdChannel --
2983 *
2984 *	Creates channels for standard input, standard output or standard
2985 *	error output if they do not already exist.
2986 *
2987 * Results:
2988 *	Returns the specified default standard channel, or NULL.
2989 *
2990 * Side effects:
2991 *	May cause the creation of a standard channel and the underlying
2992 *	file.
2993 *
2994 *----------------------------------------------------------------------
2995 */
2996
2997Tcl_Channel
2998TclpGetDefaultStdChannel(type)
2999    int type;			/* One of TCL_STDIN, TCL_STDOUT, TCL_STDERR. */
3000{
3001    Tcl_Channel channel = NULL;
3002    int fd = 0;			/* Initializations needed to prevent */
3003    int mode = 0;		/* compiler warning (used before set). */
3004    char *bufMode = NULL;
3005
3006    /*
3007     * Some #def's to make the code a little clearer!
3008     */
3009#define ZERO_OFFSET	((Tcl_SeekOffset) 0)
3010#define ERROR_OFFSET	((Tcl_SeekOffset) -1)
3011
3012    switch (type) {
3013	case TCL_STDIN:
3014	    if ((TclOSseek(0, ZERO_OFFSET, SEEK_CUR) == ERROR_OFFSET)
3015		    && (errno == EBADF)) {
3016		return (Tcl_Channel) NULL;
3017	    }
3018	    fd = 0;
3019	    mode = TCL_READABLE;
3020	    bufMode = "line";
3021	    break;
3022	case TCL_STDOUT:
3023	    if ((TclOSseek(1, ZERO_OFFSET, SEEK_CUR) == ERROR_OFFSET)
3024		    && (errno == EBADF)) {
3025		return (Tcl_Channel) NULL;
3026	    }
3027	    fd = 1;
3028	    mode = TCL_WRITABLE;
3029	    bufMode = "line";
3030	    break;
3031	case TCL_STDERR:
3032	    if ((TclOSseek(2, ZERO_OFFSET, SEEK_CUR) == ERROR_OFFSET)
3033		    && (errno == EBADF)) {
3034		return (Tcl_Channel) NULL;
3035	    }
3036	    fd = 2;
3037	    mode = TCL_WRITABLE;
3038	    bufMode = "none";
3039	    break;
3040	default:
3041	    panic("TclGetDefaultStdChannel: Unexpected channel type");
3042	    break;
3043    }
3044
3045#undef ZERO_OFFSET
3046#undef ERROR_OFFSET
3047
3048    channel = Tcl_MakeFileChannel((ClientData) fd, mode);
3049    if (channel == NULL) {
3050	return NULL;
3051    }
3052
3053    /*
3054     * Set up the normal channel options for stdio handles.
3055     */
3056
3057    if (Tcl_GetChannelType(channel) == &fileChannelType) {
3058	Tcl_SetChannelOption(NULL, channel, "-translation", "auto");
3059    } else {
3060	Tcl_SetChannelOption(NULL, channel, "-translation", "auto crlf");
3061    }
3062    Tcl_SetChannelOption(NULL, channel, "-buffering", bufMode);
3063    return channel;
3064}
3065
3066/*
3067 *----------------------------------------------------------------------
3068 *
3069 * Tcl_GetOpenFile --
3070 *
3071 *	Given a name of a channel registered in the given interpreter,
3072 *	returns a FILE * for it.
3073 *
3074 * Results:
3075 *	A standard Tcl result. If the channel is registered in the given
3076 *	interpreter and it is managed by the "file" channel driver, and
3077 *	it is open for the requested mode, then the output parameter
3078 *	filePtr is set to a FILE * for the underlying file. On error, the
3079 *	filePtr is not set, TCL_ERROR is returned and an error message is
3080 *	left in the interp's result.
3081 *
3082 * Side effects:
3083 *	May invoke fdopen to create the FILE * for the requested file.
3084 *
3085 *----------------------------------------------------------------------
3086 */
3087
3088int
3089Tcl_GetOpenFile(interp, string, forWriting, checkUsage, filePtr)
3090    Tcl_Interp *interp;		/* Interpreter in which to find file. */
3091    CONST char *string;		/* String that identifies file. */
3092    int forWriting;		/* 1 means the file is going to be used
3093				 * for writing, 0 means for reading. */
3094    int checkUsage;		/* 1 means verify that the file was opened
3095				 * in a mode that allows the access specified
3096				 * by "forWriting". Ignored, we always
3097				 * check that the channel is open for the
3098				 * requested mode. */
3099    ClientData *filePtr;	/* Store pointer to FILE structure here. */
3100{
3101    Tcl_Channel chan;
3102    int chanMode;
3103    Tcl_ChannelType *chanTypePtr;
3104    ClientData data;
3105    int fd;
3106    FILE *f;
3107
3108    chan = Tcl_GetChannel(interp, string, &chanMode);
3109    if (chan == (Tcl_Channel) NULL) {
3110	return TCL_ERROR;
3111    }
3112    if ((forWriting) && ((chanMode & TCL_WRITABLE) == 0)) {
3113	Tcl_AppendResult(interp,
3114		"\"", string, "\" wasn't opened for writing", (char *) NULL);
3115	return TCL_ERROR;
3116    } else if ((!(forWriting)) && ((chanMode & TCL_READABLE) == 0)) {
3117	Tcl_AppendResult(interp,
3118		"\"", string, "\" wasn't opened for reading", (char *) NULL);
3119	return TCL_ERROR;
3120    }
3121
3122    /*
3123     * We allow creating a FILE * out of file based, pipe based and socket
3124     * based channels. We currently do not allow any other channel types,
3125     * because it is likely that stdio will not know what to do with them.
3126     */
3127
3128    chanTypePtr = Tcl_GetChannelType(chan);
3129    if ((chanTypePtr == &fileChannelType)
3130#ifdef SUPPORTS_TTY
3131	    || (chanTypePtr == &ttyChannelType)
3132#endif /* SUPPORTS_TTY */
3133	    || (chanTypePtr == &tcpChannelType)
3134	    || (strcmp(chanTypePtr->typeName, "pipe") == 0)) {
3135	if (Tcl_GetChannelHandle(chan,
3136		(forWriting ? TCL_WRITABLE : TCL_READABLE),
3137		(ClientData*) &data) == TCL_OK) {
3138	    fd = (int) data;
3139
3140	    /*
3141	     * The call to fdopen below is probably dangerous, since it will
3142	     * truncate an existing file if the file is being opened
3143	     * for writing....
3144	     */
3145
3146	    f = fdopen(fd, (forWriting ? "w" : "r"));
3147	    if (f == NULL) {
3148		Tcl_AppendResult(interp, "cannot get a FILE * for \"", string,
3149			"\"", (char *) NULL);
3150		return TCL_ERROR;
3151	    }
3152	    *filePtr = (ClientData) f;
3153	    return TCL_OK;
3154	}
3155    }
3156
3157    Tcl_AppendResult(interp, "\"", string,
3158	    "\" cannot be used to get a FILE *", (char *) NULL);
3159    return TCL_ERROR;
3160}
3161
3162/*
3163 *----------------------------------------------------------------------
3164 *
3165 * TclUnixWaitForFile --
3166 *
3167 *	This procedure waits synchronously for a file to become readable
3168 *	or writable, with an optional timeout.
3169 *
3170 * Results:
3171 *	The return value is an OR'ed combination of TCL_READABLE,
3172 *	TCL_WRITABLE, and TCL_EXCEPTION, indicating the conditions
3173 *	that are present on file at the time of the return.  This
3174 *	procedure will not return until either "timeout" milliseconds
3175 *	have elapsed or at least one of the conditions given by mask
3176 *	has occurred for file (a return value of 0 means that a timeout
3177 *	occurred).  No normal events will be serviced during the
3178 *	execution of this procedure.
3179 *
3180 * Side effects:
3181 *	Time passes.
3182 *
3183 *----------------------------------------------------------------------
3184 */
3185
3186int
3187TclUnixWaitForFile(fd, mask, timeout)
3188    int fd;			/* Handle for file on which to wait. */
3189    int mask;			/* What to wait for: OR'ed combination of
3190				 * TCL_READABLE, TCL_WRITABLE, and
3191				 * TCL_EXCEPTION. */
3192    int timeout;		/* Maximum amount of time to wait for one
3193				 * of the conditions in mask to occur, in
3194				 * milliseconds.  A value of 0 means don't
3195				 * wait at all, and a value of -1 means
3196				 * wait forever. */
3197{
3198    Tcl_Time abortTime = {0, 0}, now; /* silence gcc 4 warning */
3199    struct timeval blockTime, *timeoutPtr;
3200    int index, numFound, result = 0;
3201    fd_mask bit;
3202    fd_mask readyMasks[3*MASK_SIZE];
3203				/* This array reflects the readable/writable
3204				 * conditions that were found to exist by the
3205				 * last call to select. */
3206
3207    /*
3208     * If there is a non-zero finite timeout, compute the time when
3209     * we give up.
3210     */
3211
3212    if (timeout > 0) {
3213	Tcl_GetTime(&now);
3214	abortTime.sec = now.sec + timeout/1000;
3215	abortTime.usec = now.usec + (timeout%1000)*1000;
3216	if (abortTime.usec >= 1000000) {
3217	    abortTime.usec -= 1000000;
3218	    abortTime.sec += 1;
3219	}
3220	timeoutPtr = &blockTime;
3221    } else if (timeout == 0) {
3222	timeoutPtr = &blockTime;
3223	blockTime.tv_sec = 0;
3224	blockTime.tv_usec = 0;
3225    } else {
3226	timeoutPtr = NULL;
3227    }
3228
3229    /*
3230     * Initialize the ready masks and compute the mask offsets.
3231     */
3232
3233    if (fd >= FD_SETSIZE) {
3234	panic("TclWaitForFile can't handle file id %d", fd);
3235    }
3236    memset((VOID *) readyMasks, 0, 3*MASK_SIZE*sizeof(fd_mask));
3237    index = fd/(NBBY*sizeof(fd_mask));
3238    bit = ((fd_mask) 1) << (fd%(NBBY*sizeof(fd_mask)));
3239
3240    /*
3241     * Loop in a mini-event loop of our own, waiting for either the
3242     * file to become ready or a timeout to occur.
3243     */
3244
3245    while (1) {
3246	if (timeout > 0) {
3247	    blockTime.tv_sec = abortTime.sec - now.sec;
3248	    blockTime.tv_usec = abortTime.usec - now.usec;
3249	    if (blockTime.tv_usec < 0) {
3250		blockTime.tv_sec -= 1;
3251		blockTime.tv_usec += 1000000;
3252	    }
3253	    if (blockTime.tv_sec < 0) {
3254		blockTime.tv_sec = 0;
3255		blockTime.tv_usec = 0;
3256	    }
3257	}
3258
3259	/*
3260	 * Set the appropriate bit in the ready masks for the fd.
3261	 */
3262
3263	if (mask & TCL_READABLE) {
3264	    readyMasks[index] |= bit;
3265	}
3266	if (mask & TCL_WRITABLE) {
3267	    (readyMasks+MASK_SIZE)[index] |= bit;
3268	}
3269	if (mask & TCL_EXCEPTION) {
3270	    (readyMasks+2*(MASK_SIZE))[index] |= bit;
3271	}
3272
3273	/*
3274	 * Wait for the event or a timeout.
3275	 */
3276
3277	numFound = select(fd+1, (SELECT_MASK *) &readyMasks[0],
3278		(SELECT_MASK *) &readyMasks[MASK_SIZE],
3279		(SELECT_MASK *) &readyMasks[2*MASK_SIZE], timeoutPtr);
3280	if (numFound == 1) {
3281	    if (readyMasks[index] & bit) {
3282		result |= TCL_READABLE;
3283	    }
3284	    if ((readyMasks+MASK_SIZE)[index] & bit) {
3285		result |= TCL_WRITABLE;
3286	    }
3287	    if ((readyMasks+2*(MASK_SIZE))[index] & bit) {
3288		result |= TCL_EXCEPTION;
3289	    }
3290	    result &= mask;
3291	    if (result) {
3292		break;
3293	    }
3294	}
3295	if (timeout == 0) {
3296	    break;
3297	}
3298	if (timeout < 0) {
3299	    continue;
3300	}
3301
3302	/*
3303	 * The select returned early, so we need to recompute the timeout.
3304	 */
3305
3306	Tcl_GetTime(&now);
3307	if ((abortTime.sec < now.sec)
3308		|| ((abortTime.sec == now.sec)
3309		&& (abortTime.usec <= now.usec))) {
3310	    break;
3311	}
3312    }
3313    return result;
3314}
3315
3316#ifdef DEPRECATED
3317/*
3318 *----------------------------------------------------------------------
3319 *
3320 * FileThreadActionProc --
3321 *
3322 *	Insert or remove any thread local refs to this channel.
3323 *
3324 * Results:
3325 *	None.
3326 *
3327 * Side effects:
3328 *	Changes thread local list of valid channels.
3329 *
3330 *----------------------------------------------------------------------
3331 */
3332
3333static void
3334FileThreadActionProc (instanceData, action)
3335     ClientData instanceData;
3336     int action;
3337{
3338    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
3339    FileState *fsPtr = (FileState *) instanceData;
3340
3341    if (action == TCL_CHANNEL_THREAD_INSERT) {
3342        fsPtr->nextPtr       = tsdPtr->firstFilePtr;
3343	tsdPtr->firstFilePtr = fsPtr;
3344    } else {
3345        FileState **nextPtrPtr;
3346	int removed = 0;
3347
3348	for (nextPtrPtr = &(tsdPtr->firstFilePtr); (*nextPtrPtr) != NULL;
3349	     nextPtrPtr = &((*nextPtrPtr)->nextPtr)) {
3350	    if ((*nextPtrPtr) == fsPtr) {
3351	        (*nextPtrPtr) = fsPtr->nextPtr;
3352		removed = 1;
3353		break;
3354	    }
3355	}
3356
3357	/*
3358	 * This could happen if the channel was created in one
3359	 * thread and then moved to another without updating
3360	 * the thread local data in each thread.
3361	 */
3362
3363	if (!removed) {
3364	  panic("file info ptr not on thread channel list");
3365	}
3366    }
3367}
3368#endif /* DEPRECATED */
3369
3370/*
3371 * Local Variables:
3372 * mode: c
3373 * c-basic-offset: 4
3374 * fill-column: 78
3375 * tab-width: 8
3376 * indent-tabs-mode: nil
3377 * End:
3378 */
3379
3380