os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/unix/tclUnixChan.c
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/unix/tclUnixChan.c Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,3366 @@
1.4 +/*
1.5 + * tclUnixChan.c
1.6 + *
1.7 + * Common channel driver for Unix channels based on files, command
1.8 + * pipes and TCP sockets.
1.9 + *
1.10 + * Copyright (c) 1995-1997 Sun Microsystems, Inc.
1.11 + * Copyright (c) 1998-1999 by Scriptics Corporation.
1.12 + * Portions Copyright (c) 2007-2008 Nokia Corporation and/or its subsidiaries. All rights reserved.
1.13 + *
1.14 + * See the file "license.terms" for information on usage and redistribution
1.15 + * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
1.16 + *
1.17 + * RCS: @(#) $Id: tclUnixChan.c,v 1.42.2.10 2006/11/28 16:29:48 kennykb Exp $
1.18 + */
1.19 +
1.20 +#include "tclInt.h" /* Internal definitions for Tcl. */
1.21 +#include "tclPort.h" /* Portability features for Tcl. */
1.22 +#include "tclIO.h" /* To get Channel type declaration. */
1.23 +
1.24 +/*
1.25 + * sys/ioctl.h has already been included by tclPort.h. Including termios.h
1.26 + * or termio.h causes a bunch of warning messages because some duplicate
1.27 + * (but not contradictory) #defines exist in termios.h and/or termio.h
1.28 + */
1.29 +#undef NL0
1.30 +#undef NL1
1.31 +#undef CR0
1.32 +#undef CR1
1.33 +#undef CR2
1.34 +#undef CR3
1.35 +#undef TAB0
1.36 +#undef TAB1
1.37 +#undef TAB2
1.38 +#undef XTABS
1.39 +#undef BS0
1.40 +#undef BS1
1.41 +#undef FF0
1.42 +#undef FF1
1.43 +#undef ECHO
1.44 +#undef NOFLSH
1.45 +#undef TOSTOP
1.46 +#undef FLUSHO
1.47 +#undef PENDIN
1.48 +
1.49 +#define SUPPORTS_TTY
1.50 +
1.51 +#ifdef USE_TERMIOS
1.52 +# include <termios.h>
1.53 +# ifdef HAVE_SYS_IOCTL_H
1.54 +# include <sys/ioctl.h>
1.55 +# endif /* HAVE_SYS_IOCTL_H */
1.56 +# ifdef HAVE_SYS_MODEM_H
1.57 +# include <sys/modem.h>
1.58 +# endif /* HAVE_SYS_MODEM_H */
1.59 +# define IOSTATE struct termios
1.60 +# define GETIOSTATE(fd, statePtr) tcgetattr((fd), (statePtr))
1.61 +# define SETIOSTATE(fd, statePtr) tcsetattr((fd), TCSADRAIN, (statePtr))
1.62 +# define GETCONTROL(fd, intPtr) ioctl((fd), TIOCMGET, (intPtr))
1.63 +# define SETCONTROL(fd, intPtr) ioctl((fd), TIOCMSET, (intPtr))
1.64 + /*
1.65 + * TIP #35 introduced a different on exit flush/close behavior that
1.66 + * doesn't work correctly with standard channels on all systems.
1.67 + * The problem is tcflush throws away waiting channel data. This may
1.68 + * be necessary for true serial channels that may block, but isn't
1.69 + * correct in the standard case. This might be replaced with tcdrain
1.70 + * instead, but that can block. For now, we revert to making this do
1.71 + * nothing, and TtyOutputProc being the same old FileOutputProc.
1.72 + * -- hobbs [Bug #525783]
1.73 + */
1.74 +# define BAD_TIP35_FLUSH 0
1.75 +# if BAD_TIP35_FLUSH
1.76 +# define TTYFLUSH(fd) tcflush((fd), TCIOFLUSH);
1.77 +# else
1.78 +# define TTYFLUSH(fd)
1.79 +# endif /* BAD_TIP35_FLUSH */
1.80 +# ifdef FIONREAD
1.81 +# define GETREADQUEUE(fd, int) ioctl((fd), FIONREAD, &(int))
1.82 +# elif defined(FIORDCHK)
1.83 +# define GETREADQUEUE(fd, int) int = ioctl((fd), FIORDCHK, NULL)
1.84 +# endif /* FIONREAD */
1.85 +# ifdef TIOCOUTQ
1.86 +# define GETWRITEQUEUE(fd, int) ioctl((fd), TIOCOUTQ, &(int))
1.87 +# endif /* TIOCOUTQ */
1.88 +# if defined(TIOCSBRK) && defined(TIOCCBRK)
1.89 +/*
1.90 + * Can't use ?: operator below because that messes up types on either
1.91 + * Linux or Solaris (the two are mutually exclusive!)
1.92 + */
1.93 +# define SETBREAK(fd, flag) \
1.94 + if (flag) { \
1.95 + ioctl((fd), TIOCSBRK, NULL); \
1.96 + } else { \
1.97 + ioctl((fd), TIOCCBRK, NULL); \
1.98 + }
1.99 +# endif /* TIOCSBRK&TIOCCBRK */
1.100 +# if !defined(CRTSCTS) && defined(CNEW_RTSCTS)
1.101 +# define CRTSCTS CNEW_RTSCTS
1.102 +# endif /* !CRTSCTS&CNEW_RTSCTS */
1.103 +#else /* !USE_TERMIOS */
1.104 +
1.105 +#ifdef USE_TERMIO
1.106 +# include <termio.h>
1.107 +# define IOSTATE struct termio
1.108 +# define GETIOSTATE(fd, statePtr) ioctl((fd), TCGETA, (statePtr))
1.109 +# define SETIOSTATE(fd, statePtr) ioctl((fd), TCSETAW, (statePtr))
1.110 +#else /* !USE_TERMIO */
1.111 +
1.112 +#ifdef USE_SGTTY
1.113 +# include <sgtty.h>
1.114 +# define IOSTATE struct sgttyb
1.115 +# define GETIOSTATE(fd, statePtr) ioctl((fd), TIOCGETP, (statePtr))
1.116 +# define SETIOSTATE(fd, statePtr) ioctl((fd), TIOCSETP, (statePtr))
1.117 +#else /* !USE_SGTTY */
1.118 +# undef SUPPORTS_TTY
1.119 +#endif /* !USE_SGTTY */
1.120 +
1.121 +#endif /* !USE_TERMIO */
1.122 +#endif /* !USE_TERMIOS */
1.123 +
1.124 +/*
1.125 + * This structure describes per-instance state of a file based channel.
1.126 + */
1.127 +
1.128 +typedef struct FileState {
1.129 + Tcl_Channel channel; /* Channel associated with this file. */
1.130 + int fd; /* File handle. */
1.131 + int validMask; /* OR'ed combination of TCL_READABLE,
1.132 + * TCL_WRITABLE, or TCL_EXCEPTION: indicates
1.133 + * which operations are valid on the file. */
1.134 +#ifdef DEPRECATED
1.135 + struct FileState *nextPtr; /* Pointer to next file in list of all
1.136 + * file channels. */
1.137 +#endif /* DEPRECATED */
1.138 +} FileState;
1.139 +
1.140 +#ifdef SUPPORTS_TTY
1.141 +
1.142 +/*
1.143 + * The following structure describes per-instance state of a tty-based
1.144 + * channel.
1.145 + */
1.146 +
1.147 +typedef struct TtyState {
1.148 + FileState fs; /* Per-instance state of the file
1.149 + * descriptor. Must be the first field. */
1.150 + int stateUpdated; /* Flag to say if the state has been
1.151 + * modified and needs resetting. */
1.152 + IOSTATE savedState; /* Initial state of device. Used to reset
1.153 + * state when device closed. */
1.154 +} TtyState;
1.155 +
1.156 +/*
1.157 + * The following structure is used to set or get the serial port
1.158 + * attributes in a platform-independant manner.
1.159 + */
1.160 +
1.161 +typedef struct TtyAttrs {
1.162 + int baud;
1.163 + int parity;
1.164 + int data;
1.165 + int stop;
1.166 +} TtyAttrs;
1.167 +
1.168 +#endif /* !SUPPORTS_TTY */
1.169 +
1.170 +#define UNSUPPORTED_OPTION(detail) \
1.171 + if (interp) { \
1.172 + Tcl_AppendResult(interp, (detail), \
1.173 + " not supported for this platform", (char *) NULL); \
1.174 + }
1.175 +
1.176 +#ifdef DEPRECATED
1.177 +typedef struct ThreadSpecificData {
1.178 + /*
1.179 + * List of all file channels currently open. This is per thread and is
1.180 + * used to match up fd's to channels, which rarely occurs.
1.181 + */
1.182 +
1.183 + FileState *firstFilePtr;
1.184 +} ThreadSpecificData;
1.185 +
1.186 +static Tcl_ThreadDataKey dataKey;
1.187 +#endif /* DEPRECATED */
1.188 +
1.189 +/*
1.190 + * This structure describes per-instance state of a tcp based channel.
1.191 + */
1.192 +
1.193 +typedef struct TcpState {
1.194 + Tcl_Channel channel; /* Channel associated with this file. */
1.195 + int fd; /* The socket itself. */
1.196 + int flags; /* ORed combination of the bitfields
1.197 + * defined below. */
1.198 + Tcl_TcpAcceptProc *acceptProc;
1.199 + /* Proc to call on accept. */
1.200 + ClientData acceptProcData; /* The data for the accept proc. */
1.201 +} TcpState;
1.202 +
1.203 +/*
1.204 + * These bits may be ORed together into the "flags" field of a TcpState
1.205 + * structure.
1.206 + */
1.207 +
1.208 +#define TCP_ASYNC_SOCKET (1<<0) /* Asynchronous socket. */
1.209 +#define TCP_ASYNC_CONNECT (1<<1) /* Async connect in progress. */
1.210 +
1.211 +/*
1.212 + * The following defines the maximum length of the listen queue. This is
1.213 + * the number of outstanding yet-to-be-serviced requests for a connection
1.214 + * on a server socket, more than this number of outstanding requests and
1.215 + * the connection request will fail.
1.216 + */
1.217 +
1.218 +#ifndef SOMAXCONN
1.219 +# define SOMAXCONN 100
1.220 +#endif /* SOMAXCONN */
1.221 +
1.222 +#if (SOMAXCONN < 100)
1.223 +# undef SOMAXCONN
1.224 +# define SOMAXCONN 100
1.225 +#endif /* SOMAXCONN < 100 */
1.226 +
1.227 +/*
1.228 + * The following defines how much buffer space the kernel should maintain
1.229 + * for a socket.
1.230 + */
1.231 +
1.232 +#define SOCKET_BUFSIZE 4096
1.233 +
1.234 +/*
1.235 + * Static routines for this file:
1.236 + */
1.237 +
1.238 +static TcpState * CreateSocket _ANSI_ARGS_((Tcl_Interp *interp,
1.239 + int port, CONST char *host, int server,
1.240 + CONST char *myaddr, int myport, int async));
1.241 +static int CreateSocketAddress _ANSI_ARGS_(
1.242 + (struct sockaddr_in *sockaddrPtr,
1.243 + CONST char *host, int port));
1.244 +static int FileBlockModeProc _ANSI_ARGS_((
1.245 + ClientData instanceData, int mode));
1.246 +static int FileCloseProc _ANSI_ARGS_((ClientData instanceData,
1.247 + Tcl_Interp *interp));
1.248 +static int FileGetHandleProc _ANSI_ARGS_((ClientData instanceData,
1.249 + int direction, ClientData *handlePtr));
1.250 +static int FileInputProc _ANSI_ARGS_((ClientData instanceData,
1.251 + char *buf, int toRead, int *errorCode));
1.252 +static int FileOutputProc _ANSI_ARGS_((
1.253 + ClientData instanceData, CONST char *buf,
1.254 + int toWrite, int *errorCode));
1.255 +static int FileSeekProc _ANSI_ARGS_((ClientData instanceData,
1.256 + long offset, int mode, int *errorCode));
1.257 +#ifdef DEPRECATED
1.258 +static void FileThreadActionProc _ANSI_ARGS_ ((
1.259 + ClientData instanceData, int action));
1.260 +#endif
1.261 +static Tcl_WideInt FileWideSeekProc _ANSI_ARGS_((ClientData instanceData,
1.262 + Tcl_WideInt offset, int mode, int *errorCode));
1.263 +static void FileWatchProc _ANSI_ARGS_((ClientData instanceData,
1.264 + int mask));
1.265 +static void TcpAccept _ANSI_ARGS_((ClientData data, int mask));
1.266 +static int TcpBlockModeProc _ANSI_ARGS_((ClientData data,
1.267 + int mode));
1.268 +static int TcpCloseProc _ANSI_ARGS_((ClientData instanceData,
1.269 + Tcl_Interp *interp));
1.270 +static int TcpGetHandleProc _ANSI_ARGS_((ClientData instanceData,
1.271 + int direction, ClientData *handlePtr));
1.272 +static int TcpGetOptionProc _ANSI_ARGS_((ClientData instanceData,
1.273 + Tcl_Interp *interp, CONST char *optionName,
1.274 + Tcl_DString *dsPtr));
1.275 +static int TcpInputProc _ANSI_ARGS_((ClientData instanceData,
1.276 + char *buf, int toRead, int *errorCode));
1.277 +static int TcpOutputProc _ANSI_ARGS_((ClientData instanceData,
1.278 + CONST char *buf, int toWrite, int *errorCode));
1.279 +static void TcpWatchProc _ANSI_ARGS_((ClientData instanceData,
1.280 + int mask));
1.281 +#ifdef SUPPORTS_TTY
1.282 +static int TtyCloseProc _ANSI_ARGS_((ClientData instanceData,
1.283 + Tcl_Interp *interp));
1.284 +static void TtyGetAttributes _ANSI_ARGS_((int fd,
1.285 + TtyAttrs *ttyPtr));
1.286 +static int TtyGetOptionProc _ANSI_ARGS_((ClientData instanceData,
1.287 + Tcl_Interp *interp, CONST char *optionName,
1.288 + Tcl_DString *dsPtr));
1.289 +static FileState * TtyInit _ANSI_ARGS_((int fd, int initialize));
1.290 +#if BAD_TIP35_FLUSH
1.291 +static int TtyOutputProc _ANSI_ARGS_((ClientData instanceData,
1.292 + CONST char *buf, int toWrite, int *errorCode));
1.293 +#endif /* BAD_TIP35_FLUSH */
1.294 +static int TtyParseMode _ANSI_ARGS_((Tcl_Interp *interp,
1.295 + CONST char *mode, int *speedPtr, int *parityPtr,
1.296 + int *dataPtr, int *stopPtr));
1.297 +static void TtySetAttributes _ANSI_ARGS_((int fd,
1.298 + TtyAttrs *ttyPtr));
1.299 +static int TtySetOptionProc _ANSI_ARGS_((ClientData instanceData,
1.300 + Tcl_Interp *interp, CONST char *optionName,
1.301 + CONST char *value));
1.302 +#endif /* SUPPORTS_TTY */
1.303 +static int WaitForConnect _ANSI_ARGS_((TcpState *statePtr,
1.304 + int *errorCodePtr));
1.305 +static Tcl_Channel MakeTcpClientChannelMode _ANSI_ARGS_(
1.306 + (ClientData tcpSocket,
1.307 + int mode));
1.308 +
1.309 +
1.310 +/*
1.311 + * This structure describes the channel type structure for file based IO:
1.312 + */
1.313 +
1.314 +static Tcl_ChannelType fileChannelType = {
1.315 + "file", /* Type name. */
1.316 + TCL_CHANNEL_VERSION_4, /* v4 channel */
1.317 + FileCloseProc, /* Close proc. */
1.318 + FileInputProc, /* Input proc. */
1.319 + FileOutputProc, /* Output proc. */
1.320 + FileSeekProc, /* Seek proc. */
1.321 + NULL, /* Set option proc. */
1.322 + NULL, /* Get option proc. */
1.323 + FileWatchProc, /* Initialize notifier. */
1.324 + FileGetHandleProc, /* Get OS handles out of channel. */
1.325 + NULL, /* close2proc. */
1.326 + FileBlockModeProc, /* Set blocking or non-blocking mode.*/
1.327 + NULL, /* flush proc. */
1.328 + NULL, /* handler proc. */
1.329 + FileWideSeekProc, /* wide seek proc. */
1.330 +#ifdef DEPRECATED
1.331 + FileThreadActionProc, /* thread actions */
1.332 +#else
1.333 + NULL,
1.334 +#endif
1.335 +};
1.336 +
1.337 +#ifdef SUPPORTS_TTY
1.338 +/*
1.339 + * This structure describes the channel type structure for serial IO.
1.340 + * Note that this type is a subclass of the "file" type.
1.341 + */
1.342 +
1.343 +static Tcl_ChannelType ttyChannelType = {
1.344 + "tty", /* Type name. */
1.345 + TCL_CHANNEL_VERSION_4, /* v4 channel */
1.346 + TtyCloseProc, /* Close proc. */
1.347 + FileInputProc, /* Input proc. */
1.348 +#if BAD_TIP35_FLUSH
1.349 + TtyOutputProc, /* Output proc. */
1.350 +#else /* !BAD_TIP35_FLUSH */
1.351 + FileOutputProc, /* Output proc. */
1.352 +#endif /* BAD_TIP35_FLUSH */
1.353 + NULL, /* Seek proc. */
1.354 + TtySetOptionProc, /* Set option proc. */
1.355 + TtyGetOptionProc, /* Get option proc. */
1.356 + FileWatchProc, /* Initialize notifier. */
1.357 + FileGetHandleProc, /* Get OS handles out of channel. */
1.358 + NULL, /* close2proc. */
1.359 + FileBlockModeProc, /* Set blocking or non-blocking mode.*/
1.360 + NULL, /* flush proc. */
1.361 + NULL, /* handler proc. */
1.362 + NULL, /* wide seek proc. */
1.363 + NULL, /* thread action proc. */
1.364 +};
1.365 +#endif /* SUPPORTS_TTY */
1.366 +
1.367 +/*
1.368 + * This structure describes the channel type structure for TCP socket
1.369 + * based IO:
1.370 + */
1.371 +
1.372 +static Tcl_ChannelType tcpChannelType = {
1.373 + "tcp", /* Type name. */
1.374 + TCL_CHANNEL_VERSION_4, /* v4 channel */
1.375 + TcpCloseProc, /* Close proc. */
1.376 + TcpInputProc, /* Input proc. */
1.377 + TcpOutputProc, /* Output proc. */
1.378 + NULL, /* Seek proc. */
1.379 + NULL, /* Set option proc. */
1.380 + TcpGetOptionProc, /* Get option proc. */
1.381 + TcpWatchProc, /* Initialize notifier. */
1.382 + TcpGetHandleProc, /* Get OS handles out of channel. */
1.383 + NULL, /* close2proc. */
1.384 + TcpBlockModeProc, /* Set blocking or non-blocking mode.*/
1.385 + NULL, /* flush proc. */
1.386 + NULL, /* handler proc. */
1.387 + NULL, /* wide seek proc. */
1.388 + NULL, /* thread action proc. */
1.389 +};
1.390 +
1.391 +
1.392 +/*
1.393 + *----------------------------------------------------------------------
1.394 + *
1.395 + * FileBlockModeProc --
1.396 + *
1.397 + * Helper procedure to set blocking and nonblocking modes on a
1.398 + * file based channel. Invoked by generic IO level code.
1.399 + *
1.400 + * Results:
1.401 + * 0 if successful, errno when failed.
1.402 + *
1.403 + * Side effects:
1.404 + * Sets the device into blocking or non-blocking mode.
1.405 + *
1.406 + *----------------------------------------------------------------------
1.407 + */
1.408 +
1.409 + /* ARGSUSED */
1.410 +static int
1.411 +FileBlockModeProc(instanceData, mode)
1.412 + ClientData instanceData; /* File state. */
1.413 + int mode; /* The mode to set. Can be one of
1.414 + * TCL_MODE_BLOCKING or
1.415 + * TCL_MODE_NONBLOCKING. */
1.416 +{
1.417 + FileState *fsPtr = (FileState *) instanceData;
1.418 + int curStatus;
1.419 +
1.420 +#ifndef USE_FIONBIO
1.421 + curStatus = fcntl(fsPtr->fd, F_GETFL);
1.422 + if (mode == TCL_MODE_BLOCKING) {
1.423 + curStatus &= (~(O_NONBLOCK));
1.424 + } else {
1.425 + curStatus |= O_NONBLOCK;
1.426 + }
1.427 + if (fcntl(fsPtr->fd, F_SETFL, curStatus) < 0) {
1.428 + return errno;
1.429 + }
1.430 + curStatus = fcntl(fsPtr->fd, F_GETFL);
1.431 +#else /* USE_FIONBIO */
1.432 + if (mode == TCL_MODE_BLOCKING) {
1.433 + curStatus = 0;
1.434 + } else {
1.435 + curStatus = 1;
1.436 + }
1.437 + if (ioctl(fsPtr->fd, (int) FIONBIO, &curStatus) < 0) {
1.438 + return errno;
1.439 + }
1.440 +#endif /* !USE_FIONBIO */
1.441 + return 0;
1.442 +}
1.443 +
1.444 +/*
1.445 + *----------------------------------------------------------------------
1.446 + *
1.447 + * FileInputProc --
1.448 + *
1.449 + * This procedure is invoked from the generic IO level to read
1.450 + * input from a file based channel.
1.451 + *
1.452 + * Results:
1.453 + * The number of bytes read is returned or -1 on error. An output
1.454 + * argument contains a POSIX error code if an error occurs, or zero.
1.455 + *
1.456 + * Side effects:
1.457 + * Reads input from the input device of the channel.
1.458 + *
1.459 + *----------------------------------------------------------------------
1.460 + */
1.461 +
1.462 +static int
1.463 +FileInputProc(instanceData, buf, toRead, errorCodePtr)
1.464 + ClientData instanceData; /* File state. */
1.465 + char *buf; /* Where to store data read. */
1.466 + int toRead; /* How much space is available
1.467 + * in the buffer? */
1.468 + int *errorCodePtr; /* Where to store error code. */
1.469 +{
1.470 + FileState *fsPtr = (FileState *) instanceData;
1.471 + int bytesRead; /* How many bytes were actually
1.472 + * read from the input device? */
1.473 +
1.474 + *errorCodePtr = 0;
1.475 +
1.476 + /*
1.477 + * Assume there is always enough input available. This will block
1.478 + * appropriately, and read will unblock as soon as a short read is
1.479 + * possible, if the channel is in blocking mode. If the channel is
1.480 + * nonblocking, the read will never block.
1.481 + */
1.482 +
1.483 + bytesRead = read(fsPtr->fd, buf, (size_t) toRead);
1.484 + if (bytesRead > -1) {
1.485 + return bytesRead;
1.486 + }
1.487 + *errorCodePtr = errno;
1.488 + return -1;
1.489 +}
1.490 +
1.491 +/*
1.492 + *----------------------------------------------------------------------
1.493 + *
1.494 + * FileOutputProc--
1.495 + *
1.496 + * This procedure is invoked from the generic IO level to write
1.497 + * output to a file channel.
1.498 + *
1.499 + * Results:
1.500 + * The number of bytes written is returned or -1 on error. An
1.501 + * output argument contains a POSIX error code if an error occurred,
1.502 + * or zero.
1.503 + *
1.504 + * Side effects:
1.505 + * Writes output on the output device of the channel.
1.506 + *
1.507 + *----------------------------------------------------------------------
1.508 + */
1.509 +
1.510 +static int
1.511 +FileOutputProc(instanceData, buf, toWrite, errorCodePtr)
1.512 + ClientData instanceData; /* File state. */
1.513 + CONST char *buf; /* The data buffer. */
1.514 + int toWrite; /* How many bytes to write? */
1.515 + int *errorCodePtr; /* Where to store error code. */
1.516 +{
1.517 + FileState *fsPtr = (FileState *) instanceData;
1.518 + int written;
1.519 +
1.520 + *errorCodePtr = 0;
1.521 +
1.522 + if (toWrite == 0) {
1.523 + /*
1.524 + * SF Tcl Bug 465765.
1.525 + * Do not try to write nothing into a file. STREAM based
1.526 + * implementations will considers this as EOF (if there is a
1.527 + * pipe behind the file).
1.528 + */
1.529 +
1.530 + return 0;
1.531 + }
1.532 + written = write(fsPtr->fd, buf, (size_t) toWrite);
1.533 + if (written > -1) {
1.534 + return written;
1.535 + }
1.536 + *errorCodePtr = errno;
1.537 + return -1;
1.538 +}
1.539 +
1.540 +/*
1.541 + *----------------------------------------------------------------------
1.542 + *
1.543 + * FileCloseProc --
1.544 + *
1.545 + * This procedure is called from the generic IO level to perform
1.546 + * channel-type-specific cleanup when a file based channel is closed.
1.547 + *
1.548 + * Results:
1.549 + * 0 if successful, errno if failed.
1.550 + *
1.551 + * Side effects:
1.552 + * Closes the device of the channel.
1.553 + *
1.554 + *----------------------------------------------------------------------
1.555 + */
1.556 +
1.557 +static int
1.558 +FileCloseProc(instanceData, interp)
1.559 + ClientData instanceData; /* File state. */
1.560 + Tcl_Interp *interp; /* For error reporting - unused. */
1.561 +{
1.562 + FileState *fsPtr = (FileState *) instanceData;
1.563 + int errorCode = 0;
1.564 +#ifdef DEPRECATED
1.565 + FileState **nextPtrPtr;
1.566 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.567 +#endif /* DEPRECATED */
1.568 + Tcl_DeleteFileHandler(fsPtr->fd);
1.569 +
1.570 + /*
1.571 + * Do not close standard channels while in thread-exit.
1.572 + */
1.573 +
1.574 + if (!TclInThreadExit()
1.575 + || ((fsPtr->fd != 0) && (fsPtr->fd != 1) && (fsPtr->fd != 2))) {
1.576 + if (close(fsPtr->fd) < 0) {
1.577 + errorCode = errno;
1.578 + }
1.579 + }
1.580 + ckfree((char *) fsPtr);
1.581 + return errorCode;
1.582 +}
1.583 +
1.584 +/*
1.585 + *----------------------------------------------------------------------
1.586 + *
1.587 + * FileSeekProc --
1.588 + *
1.589 + * This procedure is called by the generic IO level to move the
1.590 + * access point in a file based channel.
1.591 + *
1.592 + * Results:
1.593 + * -1 if failed, the new position if successful. An output
1.594 + * argument contains the POSIX error code if an error occurred,
1.595 + * or zero.
1.596 + *
1.597 + * Side effects:
1.598 + * Moves the location at which the channel will be accessed in
1.599 + * future operations.
1.600 + *
1.601 + *----------------------------------------------------------------------
1.602 + */
1.603 +
1.604 +static int
1.605 +FileSeekProc(instanceData, offset, mode, errorCodePtr)
1.606 + ClientData instanceData; /* File state. */
1.607 + long offset; /* Offset to seek to. */
1.608 + int mode; /* Relative to where should we seek? Can be
1.609 + * one of SEEK_START, SEEK_SET or SEEK_END. */
1.610 + int *errorCodePtr; /* To store error code. */
1.611 +{
1.612 + FileState *fsPtr = (FileState *) instanceData;
1.613 + Tcl_WideInt oldLoc, newLoc;
1.614 +
1.615 + /*
1.616 + * Save our current place in case we need to roll-back the seek.
1.617 + */
1.618 + oldLoc = TclOSseek(fsPtr->fd, (Tcl_SeekOffset) 0, SEEK_CUR);
1.619 + if (oldLoc == Tcl_LongAsWide(-1)) {
1.620 + /*
1.621 + * Bad things are happening. Error out...
1.622 + */
1.623 + *errorCodePtr = errno;
1.624 + return -1;
1.625 + }
1.626 +
1.627 + newLoc = TclOSseek(fsPtr->fd, (Tcl_SeekOffset) offset, mode);
1.628 +
1.629 + /*
1.630 + * Check for expressability in our return type, and roll-back otherwise.
1.631 + */
1.632 + if (newLoc > Tcl_LongAsWide(INT_MAX)) {
1.633 + *errorCodePtr = EOVERFLOW;
1.634 + TclOSseek(fsPtr->fd, (Tcl_SeekOffset) oldLoc, SEEK_SET);
1.635 + return -1;
1.636 + } else {
1.637 + *errorCodePtr = (newLoc == Tcl_LongAsWide(-1)) ? errno : 0;
1.638 + }
1.639 + return (int) Tcl_WideAsLong(newLoc);
1.640 +}
1.641 +
1.642 +/*
1.643 + *----------------------------------------------------------------------
1.644 + *
1.645 + * FileWideSeekProc --
1.646 + *
1.647 + * This procedure is called by the generic IO level to move the
1.648 + * access point in a file based channel, with offsets expressed
1.649 + * as wide integers.
1.650 + *
1.651 + * Results:
1.652 + * -1 if failed, the new position if successful. An output
1.653 + * argument contains the POSIX error code if an error occurred,
1.654 + * or zero.
1.655 + *
1.656 + * Side effects:
1.657 + * Moves the location at which the channel will be accessed in
1.658 + * future operations.
1.659 + *
1.660 + *----------------------------------------------------------------------
1.661 + */
1.662 +
1.663 +static Tcl_WideInt
1.664 +FileWideSeekProc(instanceData, offset, mode, errorCodePtr)
1.665 + ClientData instanceData; /* File state. */
1.666 + Tcl_WideInt offset; /* Offset to seek to. */
1.667 + int mode; /* Relative to where should we seek? Can be
1.668 + * one of SEEK_START, SEEK_CUR or SEEK_END. */
1.669 + int *errorCodePtr; /* To store error code. */
1.670 +{
1.671 + FileState *fsPtr = (FileState *) instanceData;
1.672 + Tcl_WideInt newLoc;
1.673 +
1.674 + newLoc = TclOSseek(fsPtr->fd, (Tcl_SeekOffset) offset, mode);
1.675 +
1.676 + *errorCodePtr = (newLoc == -1) ? errno : 0;
1.677 + return newLoc;
1.678 +}
1.679 +
1.680 +/*
1.681 + *----------------------------------------------------------------------
1.682 + *
1.683 + * FileWatchProc --
1.684 + *
1.685 + * Initialize the notifier to watch the fd from this channel.
1.686 + *
1.687 + * Results:
1.688 + * None.
1.689 + *
1.690 + * Side effects:
1.691 + * Sets up the notifier so that a future event on the channel will
1.692 + * be seen by Tcl.
1.693 + *
1.694 + *----------------------------------------------------------------------
1.695 + */
1.696 +
1.697 +static void
1.698 +FileWatchProc(instanceData, mask)
1.699 + ClientData instanceData; /* The file state. */
1.700 + int mask; /* Events of interest; an OR-ed
1.701 + * combination of TCL_READABLE,
1.702 + * TCL_WRITABLE and TCL_EXCEPTION. */
1.703 +{
1.704 + FileState *fsPtr = (FileState *) instanceData;
1.705 +
1.706 + /*
1.707 + * Make sure we only register for events that are valid on this file.
1.708 + * Note that we are passing Tcl_NotifyChannel directly to
1.709 + * Tcl_CreateFileHandler with the channel pointer as the client data.
1.710 + */
1.711 +
1.712 + mask &= fsPtr->validMask;
1.713 + if (mask) {
1.714 + Tcl_CreateFileHandler(fsPtr->fd, mask,
1.715 + (Tcl_FileProc *) Tcl_NotifyChannel,
1.716 + (ClientData) fsPtr->channel);
1.717 + } else {
1.718 + Tcl_DeleteFileHandler(fsPtr->fd);
1.719 + }
1.720 +}
1.721 +
1.722 +/*
1.723 + *----------------------------------------------------------------------
1.724 + *
1.725 + * FileGetHandleProc --
1.726 + *
1.727 + * Called from Tcl_GetChannelHandle to retrieve OS handles from
1.728 + * a file based channel.
1.729 + *
1.730 + * Results:
1.731 + * Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if
1.732 + * there is no handle for the specified direction.
1.733 + *
1.734 + * Side effects:
1.735 + * None.
1.736 + *
1.737 + *----------------------------------------------------------------------
1.738 + */
1.739 +
1.740 +static int
1.741 +FileGetHandleProc(instanceData, direction, handlePtr)
1.742 + ClientData instanceData; /* The file state. */
1.743 + int direction; /* TCL_READABLE or TCL_WRITABLE */
1.744 + ClientData *handlePtr; /* Where to store the handle. */
1.745 +{
1.746 + FileState *fsPtr = (FileState *) instanceData;
1.747 +
1.748 + if (direction & fsPtr->validMask) {
1.749 + *handlePtr = (ClientData) fsPtr->fd;
1.750 + return TCL_OK;
1.751 + } else {
1.752 + return TCL_ERROR;
1.753 + }
1.754 +}
1.755 +
1.756 +#ifdef SUPPORTS_TTY
1.757 +
1.758 +/*
1.759 + *----------------------------------------------------------------------
1.760 + *
1.761 + * TtyCloseProc --
1.762 + *
1.763 + * This procedure is called from the generic IO level to perform
1.764 + * channel-type-specific cleanup when a tty based channel is closed.
1.765 + *
1.766 + * Results:
1.767 + * 0 if successful, errno if failed.
1.768 + *
1.769 + * Side effects:
1.770 + * Closes the device of the channel.
1.771 + *
1.772 + *----------------------------------------------------------------------
1.773 + */
1.774 +static int
1.775 +TtyCloseProc(instanceData, interp)
1.776 + ClientData instanceData; /* Tty state. */
1.777 + Tcl_Interp *interp; /* For error reporting - unused. */
1.778 +{
1.779 +#if BAD_TIP35_FLUSH
1.780 + TtyState *ttyPtr = (TtyState *) instanceData;
1.781 +#endif /* BAD_TIP35_FLUSH */
1.782 +#ifdef TTYFLUSH
1.783 + TTYFLUSH(ttyPtr->fs.fd);
1.784 +#endif /* TTYFLUSH */
1.785 +#if 0
1.786 + /*
1.787 + * TIP#35 agreed to remove the unsave so that TCL could be used as a
1.788 + * simple stty.
1.789 + * It would be cleaner to remove all the stuff related to
1.790 + * TtyState.stateUpdated
1.791 + * TtyState.savedState
1.792 + * Then the structure TtyState would be the same as FileState.
1.793 + * IMO this cleanup could better be done for the final 8.4 release
1.794 + * after nobody complained about the missing unsave. -- schroedter
1.795 + */
1.796 + if (ttyPtr->stateUpdated) {
1.797 + SETIOSTATE(ttyPtr->fs.fd, &ttyPtr->savedState);
1.798 + }
1.799 +#endif
1.800 + return FileCloseProc(instanceData, interp);
1.801 +}
1.802 +
1.803 +/*
1.804 + *----------------------------------------------------------------------
1.805 + *
1.806 + * TtyOutputProc--
1.807 + *
1.808 + * This procedure is invoked from the generic IO level to write
1.809 + * output to a TTY channel.
1.810 + *
1.811 + * Results:
1.812 + * The number of bytes written is returned or -1 on error. An
1.813 + * output argument contains a POSIX error code if an error occurred,
1.814 + * or zero.
1.815 + *
1.816 + * Side effects:
1.817 + * Writes output on the output device of the channel
1.818 + * if the channel is not designated to be closed.
1.819 + *
1.820 + *----------------------------------------------------------------------
1.821 + */
1.822 +
1.823 +#if BAD_TIP35_FLUSH
1.824 +static int
1.825 +TtyOutputProc(instanceData, buf, toWrite, errorCodePtr)
1.826 + ClientData instanceData; /* File state. */
1.827 + CONST char *buf; /* The data buffer. */
1.828 + int toWrite; /* How many bytes to write? */
1.829 + int *errorCodePtr; /* Where to store error code. */
1.830 +{
1.831 + if (TclInExit()) {
1.832 + /*
1.833 + * Do not write data during Tcl exit.
1.834 + * Serial port may block preventing Tcl from exit.
1.835 + */
1.836 + return toWrite;
1.837 + } else {
1.838 + return FileOutputProc(instanceData, buf, toWrite, errorCodePtr);
1.839 + }
1.840 +}
1.841 +#endif /* BAD_TIP35_FLUSH */
1.842 +
1.843 +#ifdef USE_TERMIOS
1.844 +/*
1.845 + *----------------------------------------------------------------------
1.846 + *
1.847 + * TtyModemStatusStr --
1.848 + *
1.849 + * Converts a RS232 modem status list of readable flags
1.850 + *
1.851 + *----------------------------------------------------------------------
1.852 + */
1.853 +static void
1.854 +TtyModemStatusStr(status, dsPtr)
1.855 + int status; /* RS232 modem status */
1.856 + Tcl_DString *dsPtr; /* Where to store string */
1.857 +{
1.858 +#ifdef TIOCM_CTS
1.859 + Tcl_DStringAppendElement(dsPtr, "CTS");
1.860 + Tcl_DStringAppendElement(dsPtr, (status & TIOCM_CTS) ? "1" : "0");
1.861 +#endif /* TIOCM_CTS */
1.862 +#ifdef TIOCM_DSR
1.863 + Tcl_DStringAppendElement(dsPtr, "DSR");
1.864 + Tcl_DStringAppendElement(dsPtr, (status & TIOCM_DSR) ? "1" : "0");
1.865 +#endif /* TIOCM_DSR */
1.866 +#ifdef TIOCM_RNG
1.867 + Tcl_DStringAppendElement(dsPtr, "RING");
1.868 + Tcl_DStringAppendElement(dsPtr, (status & TIOCM_RNG) ? "1" : "0");
1.869 +#endif /* TIOCM_RNG */
1.870 +#ifdef TIOCM_CD
1.871 + Tcl_DStringAppendElement(dsPtr, "DCD");
1.872 + Tcl_DStringAppendElement(dsPtr, (status & TIOCM_CD) ? "1" : "0");
1.873 +#endif /* TIOCM_CD */
1.874 +}
1.875 +#endif /* USE_TERMIOS */
1.876 +
1.877 +/*
1.878 + *----------------------------------------------------------------------
1.879 + *
1.880 + * TtySetOptionProc --
1.881 + *
1.882 + * Sets an option on a channel.
1.883 + *
1.884 + * Results:
1.885 + * A standard Tcl result. Also sets the interp's result on error if
1.886 + * interp is not NULL.
1.887 + *
1.888 + * Side effects:
1.889 + * May modify an option on a device.
1.890 + * Sets Error message if needed (by calling Tcl_BadChannelOption).
1.891 + *
1.892 + *----------------------------------------------------------------------
1.893 + */
1.894 +
1.895 +static int
1.896 +TtySetOptionProc(instanceData, interp, optionName, value)
1.897 + ClientData instanceData; /* File state. */
1.898 + Tcl_Interp *interp; /* For error reporting - can be NULL. */
1.899 + CONST char *optionName; /* Which option to set? */
1.900 + CONST char *value; /* New value for option. */
1.901 +{
1.902 + FileState *fsPtr = (FileState *) instanceData;
1.903 + unsigned int len, vlen;
1.904 + TtyAttrs tty;
1.905 +#ifdef USE_TERMIOS
1.906 + int flag, control, argc;
1.907 + CONST char **argv;
1.908 + IOSTATE iostate;
1.909 +#endif /* USE_TERMIOS */
1.910 +
1.911 + len = strlen(optionName);
1.912 + vlen = strlen(value);
1.913 +
1.914 + /*
1.915 + * Option -mode baud,parity,databits,stopbits
1.916 + */
1.917 + if ((len > 2) && (strncmp(optionName, "-mode", len) == 0)) {
1.918 + if (TtyParseMode(interp, value, &tty.baud, &tty.parity, &tty.data,
1.919 + &tty.stop) != TCL_OK) {
1.920 + return TCL_ERROR;
1.921 + }
1.922 + /*
1.923 + * system calls results should be checked there. -- dl
1.924 + */
1.925 +
1.926 + TtySetAttributes(fsPtr->fd, &tty);
1.927 + ((TtyState *) fsPtr)->stateUpdated = 1;
1.928 + return TCL_OK;
1.929 + }
1.930 +
1.931 +#ifdef USE_TERMIOS
1.932 +
1.933 + /*
1.934 + * Option -handshake none|xonxoff|rtscts|dtrdsr
1.935 + */
1.936 + if ((len > 1) && (strncmp(optionName, "-handshake", len) == 0)) {
1.937 + /*
1.938 + * Reset all handshake options
1.939 + * DTR and RTS are ON by default
1.940 + */
1.941 + GETIOSTATE(fsPtr->fd, &iostate);
1.942 + iostate.c_iflag &= ~(IXON | IXOFF | IXANY);
1.943 +#ifdef CRTSCTS
1.944 + iostate.c_cflag &= ~CRTSCTS;
1.945 +#endif /* CRTSCTS */
1.946 + if (strncasecmp(value, "NONE", vlen) == 0) {
1.947 + /* leave all handshake options disabled */
1.948 + } else if (strncasecmp(value, "XONXOFF", vlen) == 0) {
1.949 + iostate.c_iflag |= (IXON | IXOFF | IXANY);
1.950 + } else if (strncasecmp(value, "RTSCTS", vlen) == 0) {
1.951 +#ifdef CRTSCTS
1.952 + iostate.c_cflag |= CRTSCTS;
1.953 +#else /* !CRTSTS */
1.954 + UNSUPPORTED_OPTION("-handshake RTSCTS");
1.955 + return TCL_ERROR;
1.956 +#endif /* CRTSCTS */
1.957 + } else if (strncasecmp(value, "DTRDSR", vlen) == 0) {
1.958 + UNSUPPORTED_OPTION("-handshake DTRDSR");
1.959 + return TCL_ERROR;
1.960 + } else {
1.961 + if (interp) {
1.962 + Tcl_AppendResult(interp, "bad value for -handshake: ",
1.963 + "must be one of xonxoff, rtscts, dtrdsr or none",
1.964 + (char *) NULL);
1.965 + }
1.966 + return TCL_ERROR;
1.967 + }
1.968 + SETIOSTATE(fsPtr->fd, &iostate);
1.969 + return TCL_OK;
1.970 + }
1.971 +
1.972 + /*
1.973 + * Option -xchar {\x11 \x13}
1.974 + */
1.975 + if ((len > 1) && (strncmp(optionName, "-xchar", len) == 0)) {
1.976 + GETIOSTATE(fsPtr->fd, &iostate);
1.977 + if (Tcl_SplitList(interp, value, &argc, &argv) == TCL_ERROR) {
1.978 + return TCL_ERROR;
1.979 + }
1.980 + if (argc == 2) {
1.981 + iostate.c_cc[VSTART] = argv[0][0];
1.982 + iostate.c_cc[VSTOP] = argv[1][0];
1.983 + } else {
1.984 + if (interp) {
1.985 + Tcl_AppendResult(interp,
1.986 + "bad value for -xchar: should be a list of two elements",
1.987 + (char *) NULL);
1.988 + }
1.989 + ckfree((char *) argv);
1.990 + return TCL_ERROR;
1.991 + }
1.992 + SETIOSTATE(fsPtr->fd, &iostate);
1.993 + ckfree((char *) argv);
1.994 + return TCL_OK;
1.995 + }
1.996 +
1.997 + /*
1.998 + * Option -timeout msec
1.999 + */
1.1000 + if ((len > 2) && (strncmp(optionName, "-timeout", len) == 0)) {
1.1001 + int msec;
1.1002 +
1.1003 + GETIOSTATE(fsPtr->fd, &iostate);
1.1004 + if (Tcl_GetInt(interp, value, &msec) != TCL_OK) {
1.1005 + return TCL_ERROR;
1.1006 + }
1.1007 + iostate.c_cc[VMIN] = 0;
1.1008 + iostate.c_cc[VTIME] = (msec == 0) ? 0 : (msec < 100) ? 1 : (msec+50)/100;
1.1009 + SETIOSTATE(fsPtr->fd, &iostate);
1.1010 + return TCL_OK;
1.1011 + }
1.1012 +
1.1013 + /*
1.1014 + * Option -ttycontrol {DTR 1 RTS 0 BREAK 0}
1.1015 + */
1.1016 + if ((len > 4) && (strncmp(optionName, "-ttycontrol", len) == 0)) {
1.1017 + int i;
1.1018 + if (Tcl_SplitList(interp, value, &argc, &argv) == TCL_ERROR) {
1.1019 + return TCL_ERROR;
1.1020 + }
1.1021 + if ((argc % 2) == 1) {
1.1022 + if (interp) {
1.1023 + Tcl_AppendResult(interp,
1.1024 + "bad value for -ttycontrol: should be a list of",
1.1025 + "signal,value pairs", (char *) NULL);
1.1026 + }
1.1027 + ckfree((char *) argv);
1.1028 + return TCL_ERROR;
1.1029 + }
1.1030 +
1.1031 + GETCONTROL(fsPtr->fd, &control);
1.1032 + for (i = 0; i < argc-1; i += 2) {
1.1033 + if (Tcl_GetBoolean(interp, argv[i+1], &flag) == TCL_ERROR) {
1.1034 + ckfree((char *) argv);
1.1035 + return TCL_ERROR;
1.1036 + }
1.1037 + if (strncasecmp(argv[i], "DTR", strlen(argv[i])) == 0) {
1.1038 +#ifdef TIOCM_DTR
1.1039 + if (flag) {
1.1040 + control |= TIOCM_DTR;
1.1041 + } else {
1.1042 + control &= ~TIOCM_DTR;
1.1043 + }
1.1044 +#else /* !TIOCM_DTR */
1.1045 + UNSUPPORTED_OPTION("-ttycontrol DTR");
1.1046 + ckfree((char *) argv);
1.1047 + return TCL_ERROR;
1.1048 +#endif /* TIOCM_DTR */
1.1049 + } else if (strncasecmp(argv[i], "RTS", strlen(argv[i])) == 0) {
1.1050 +#ifdef TIOCM_RTS
1.1051 + if (flag) {
1.1052 + control |= TIOCM_RTS;
1.1053 + } else {
1.1054 + control &= ~TIOCM_RTS;
1.1055 + }
1.1056 +#else /* !TIOCM_RTS*/
1.1057 + UNSUPPORTED_OPTION("-ttycontrol RTS");
1.1058 + ckfree((char *) argv);
1.1059 + return TCL_ERROR;
1.1060 +#endif /* TIOCM_RTS*/
1.1061 + } else if (strncasecmp(argv[i], "BREAK", strlen(argv[i])) == 0) {
1.1062 +#ifdef SETBREAK
1.1063 + SETBREAK(fsPtr->fd, flag);
1.1064 +#else /* !SETBREAK */
1.1065 + UNSUPPORTED_OPTION("-ttycontrol BREAK");
1.1066 + ckfree((char *) argv);
1.1067 + return TCL_ERROR;
1.1068 +#endif /* SETBREAK */
1.1069 + } else {
1.1070 + if (interp) {
1.1071 + Tcl_AppendResult(interp, "bad signal \"", argv[i],
1.1072 + "\" for -ttycontrol: must be ",
1.1073 + "DTR, RTS or BREAK", (char *) NULL);
1.1074 + }
1.1075 + ckfree((char *) argv);
1.1076 + return TCL_ERROR;
1.1077 + }
1.1078 + } /* -ttycontrol options loop */
1.1079 +
1.1080 + SETCONTROL(fsPtr->fd, &control);
1.1081 + ckfree((char *) argv);
1.1082 + return TCL_OK;
1.1083 + }
1.1084 +
1.1085 + return Tcl_BadChannelOption(interp, optionName,
1.1086 + "mode handshake timeout ttycontrol xchar ");
1.1087 +
1.1088 +#else /* !USE_TERMIOS */
1.1089 + return Tcl_BadChannelOption(interp, optionName, "mode");
1.1090 +#endif /* USE_TERMIOS */
1.1091 +}
1.1092 +
1.1093 +/*
1.1094 + *----------------------------------------------------------------------
1.1095 + *
1.1096 + * TtyGetOptionProc --
1.1097 + *
1.1098 + * Gets a mode associated with an IO channel. If the optionName arg
1.1099 + * is non NULL, retrieves the value of that option. If the optionName
1.1100 + * arg is NULL, retrieves a list of alternating option names and
1.1101 + * values for the given channel.
1.1102 + *
1.1103 + * Results:
1.1104 + * A standard Tcl result. Also sets the supplied DString to the
1.1105 + * string value of the option(s) returned.
1.1106 + *
1.1107 + * Side effects:
1.1108 + * The string returned by this function is in static storage and
1.1109 + * may be reused at any time subsequent to the call.
1.1110 + * Sets Error message if needed (by calling Tcl_BadChannelOption).
1.1111 + *
1.1112 + *----------------------------------------------------------------------
1.1113 + */
1.1114 +
1.1115 +static int
1.1116 +TtyGetOptionProc(instanceData, interp, optionName, dsPtr)
1.1117 + ClientData instanceData; /* File state. */
1.1118 + Tcl_Interp *interp; /* For error reporting - can be NULL. */
1.1119 + CONST char *optionName; /* Option to get. */
1.1120 + Tcl_DString *dsPtr; /* Where to store value(s). */
1.1121 +{
1.1122 + FileState *fsPtr = (FileState *) instanceData;
1.1123 + unsigned int len;
1.1124 + char buf[3 * TCL_INTEGER_SPACE + 16];
1.1125 + TtyAttrs tty;
1.1126 + int valid = 0; /* flag if valid option parsed */
1.1127 +
1.1128 + if (optionName == NULL) {
1.1129 + len = 0;
1.1130 + } else {
1.1131 + len = strlen(optionName);
1.1132 + }
1.1133 + if (len == 0) {
1.1134 + Tcl_DStringAppendElement(dsPtr, "-mode");
1.1135 + }
1.1136 + if (len==0 || (len>2 && strncmp(optionName, "-mode", len)==0)) {
1.1137 + valid = 1;
1.1138 + TtyGetAttributes(fsPtr->fd, &tty);
1.1139 + sprintf(buf, "%d,%c,%d,%d", tty.baud, tty.parity, tty.data, tty.stop);
1.1140 + Tcl_DStringAppendElement(dsPtr, buf);
1.1141 + }
1.1142 +
1.1143 +#ifdef USE_TERMIOS
1.1144 + /*
1.1145 + * get option -xchar
1.1146 + */
1.1147 + if (len == 0) {
1.1148 + Tcl_DStringAppendElement(dsPtr, "-xchar");
1.1149 + Tcl_DStringStartSublist(dsPtr);
1.1150 + }
1.1151 + if (len==0 || (len>1 && strncmp(optionName, "-xchar", len)==0)) {
1.1152 + IOSTATE iostate;
1.1153 + valid = 1;
1.1154 +
1.1155 + GETIOSTATE(fsPtr->fd, &iostate);
1.1156 + sprintf(buf, "%c", iostate.c_cc[VSTART]);
1.1157 + Tcl_DStringAppendElement(dsPtr, buf);
1.1158 + sprintf(buf, "%c", iostate.c_cc[VSTOP]);
1.1159 + Tcl_DStringAppendElement(dsPtr, buf);
1.1160 + }
1.1161 + if (len == 0) {
1.1162 + Tcl_DStringEndSublist(dsPtr);
1.1163 + }
1.1164 +
1.1165 + /*
1.1166 + * get option -queue
1.1167 + * option is readonly and returned by [fconfigure chan -queue]
1.1168 + * but not returned by unnamed [fconfigure chan]
1.1169 + */
1.1170 + if ((len > 1) && (strncmp(optionName, "-queue", len) == 0)) {
1.1171 + int inQueue=0, outQueue=0;
1.1172 + int inBuffered, outBuffered;
1.1173 + valid = 1;
1.1174 +#ifdef GETREADQUEUE
1.1175 + GETREADQUEUE(fsPtr->fd, inQueue);
1.1176 +#endif /* GETREADQUEUE */
1.1177 +#ifdef GETWRITEQUEUE
1.1178 + GETWRITEQUEUE(fsPtr->fd, outQueue);
1.1179 +#endif /* GETWRITEQUEUE */
1.1180 + inBuffered = Tcl_InputBuffered(fsPtr->channel);
1.1181 + outBuffered = Tcl_OutputBuffered(fsPtr->channel);
1.1182 +
1.1183 + sprintf(buf, "%d", inBuffered+inQueue);
1.1184 + Tcl_DStringAppendElement(dsPtr, buf);
1.1185 + sprintf(buf, "%d", outBuffered+outQueue);
1.1186 + Tcl_DStringAppendElement(dsPtr, buf);
1.1187 + }
1.1188 +
1.1189 + /*
1.1190 + * get option -ttystatus
1.1191 + * option is readonly and returned by [fconfigure chan -ttystatus]
1.1192 + * but not returned by unnamed [fconfigure chan]
1.1193 + */
1.1194 + if ((len > 4) && (strncmp(optionName, "-ttystatus", len) == 0)) {
1.1195 + int status;
1.1196 + valid = 1;
1.1197 + GETCONTROL(fsPtr->fd, &status);
1.1198 + TtyModemStatusStr(status, dsPtr);
1.1199 + }
1.1200 +#endif /* USE_TERMIOS */
1.1201 +
1.1202 + if (valid) {
1.1203 + return TCL_OK;
1.1204 + } else {
1.1205 + return Tcl_BadChannelOption(interp, optionName,
1.1206 +#ifdef USE_TERMIOS
1.1207 + "mode queue ttystatus xchar");
1.1208 +#else /* !USE_TERMIOS */
1.1209 + "mode");
1.1210 +#endif /* USE_TERMIOS */
1.1211 + }
1.1212 +}
1.1213 +
1.1214 +#undef DIRECT_BAUD
1.1215 +#ifdef B4800
1.1216 +# if (B4800 == 4800)
1.1217 +# define DIRECT_BAUD
1.1218 +# endif /* B4800 == 4800 */
1.1219 +#endif /* B4800 */
1.1220 +
1.1221 +#ifdef DIRECT_BAUD
1.1222 +# define TtyGetSpeed(baud) ((unsigned) (baud))
1.1223 +# define TtyGetBaud(speed) ((int) (speed))
1.1224 +#else /* !DIRECT_BAUD */
1.1225 +
1.1226 +static struct {int baud; unsigned long speed;} speeds[] = {
1.1227 +#ifdef B0
1.1228 + {0, B0},
1.1229 +#endif
1.1230 +#ifdef B50
1.1231 + {50, B50},
1.1232 +#endif
1.1233 +#ifdef B75
1.1234 + {75, B75},
1.1235 +#endif
1.1236 +#ifdef B110
1.1237 + {110, B110},
1.1238 +#endif
1.1239 +#ifdef B134
1.1240 + {134, B134},
1.1241 +#endif
1.1242 +#ifdef B150
1.1243 + {150, B150},
1.1244 +#endif
1.1245 +#ifdef B200
1.1246 + {200, B200},
1.1247 +#endif
1.1248 +#ifdef B300
1.1249 + {300, B300},
1.1250 +#endif
1.1251 +#ifdef B600
1.1252 + {600, B600},
1.1253 +#endif
1.1254 +#ifdef B1200
1.1255 + {1200, B1200},
1.1256 +#endif
1.1257 +#ifdef B1800
1.1258 + {1800, B1800},
1.1259 +#endif
1.1260 +#ifdef B2400
1.1261 + {2400, B2400},
1.1262 +#endif
1.1263 +#ifdef B4800
1.1264 + {4800, B4800},
1.1265 +#endif
1.1266 +#ifdef B9600
1.1267 + {9600, B9600},
1.1268 +#endif
1.1269 +#ifdef B14400
1.1270 + {14400, B14400},
1.1271 +#endif
1.1272 +#ifdef B19200
1.1273 + {19200, B19200},
1.1274 +#endif
1.1275 +#ifdef EXTA
1.1276 + {19200, EXTA},
1.1277 +#endif
1.1278 +#ifdef B28800
1.1279 + {28800, B28800},
1.1280 +#endif
1.1281 +#ifdef B38400
1.1282 + {38400, B38400},
1.1283 +#endif
1.1284 +#ifdef EXTB
1.1285 + {38400, EXTB},
1.1286 +#endif
1.1287 +#ifdef B57600
1.1288 + {57600, B57600},
1.1289 +#endif
1.1290 +#ifdef _B57600
1.1291 + {57600, _B57600},
1.1292 +#endif
1.1293 +#ifdef B76800
1.1294 + {76800, B76800},
1.1295 +#endif
1.1296 +#ifdef B115200
1.1297 + {115200, B115200},
1.1298 +#endif
1.1299 +#ifdef _B115200
1.1300 + {115200, _B115200},
1.1301 +#endif
1.1302 +#ifdef B153600
1.1303 + {153600, B153600},
1.1304 +#endif
1.1305 +#ifdef B230400
1.1306 + {230400, B230400},
1.1307 +#endif
1.1308 +#ifdef B307200
1.1309 + {307200, B307200},
1.1310 +#endif
1.1311 +#ifdef B460800
1.1312 + {460800, B460800},
1.1313 +#endif
1.1314 + {-1, 0}
1.1315 +};
1.1316 +
1.1317 +/*
1.1318 + *---------------------------------------------------------------------------
1.1319 + *
1.1320 + * TtyGetSpeed --
1.1321 + *
1.1322 + * Given a baud rate, get the mask value that should be stored in
1.1323 + * the termios, termio, or sgttyb structure in order to select that
1.1324 + * baud rate.
1.1325 + *
1.1326 + * Results:
1.1327 + * As above.
1.1328 + *
1.1329 + * Side effects:
1.1330 + * None.
1.1331 + *
1.1332 + *---------------------------------------------------------------------------
1.1333 + */
1.1334 +
1.1335 +static unsigned long
1.1336 +TtyGetSpeed(baud)
1.1337 + int baud; /* The baud rate to look up. */
1.1338 +{
1.1339 + int bestIdx, bestDiff, i, diff;
1.1340 +
1.1341 + bestIdx = 0;
1.1342 + bestDiff = 1000000;
1.1343 +
1.1344 + /*
1.1345 + * If the baud rate does not correspond to one of the known mask values,
1.1346 + * choose the mask value whose baud rate is closest to the specified
1.1347 + * baud rate.
1.1348 + */
1.1349 +
1.1350 + for (i = 0; speeds[i].baud >= 0; i++) {
1.1351 + diff = speeds[i].baud - baud;
1.1352 + if (diff < 0) {
1.1353 + diff = -diff;
1.1354 + }
1.1355 + if (diff < bestDiff) {
1.1356 + bestIdx = i;
1.1357 + bestDiff = diff;
1.1358 + }
1.1359 + }
1.1360 + return speeds[bestIdx].speed;
1.1361 +}
1.1362 +
1.1363 +/*
1.1364 + *---------------------------------------------------------------------------
1.1365 + *
1.1366 + * TtyGetBaud --
1.1367 + *
1.1368 + * Given a speed mask value from a termios, termio, or sgttyb
1.1369 + * structure, get the baus rate that corresponds to that mask value.
1.1370 + *
1.1371 + * Results:
1.1372 + * As above. If the mask value was not recognized, 0 is returned.
1.1373 + *
1.1374 + * Side effects:
1.1375 + * None.
1.1376 + *
1.1377 + *---------------------------------------------------------------------------
1.1378 + */
1.1379 +
1.1380 +static int
1.1381 +TtyGetBaud(speed)
1.1382 + unsigned long speed; /* Speed mask value to look up. */
1.1383 +{
1.1384 + int i;
1.1385 +
1.1386 + for (i = 0; speeds[i].baud >= 0; i++) {
1.1387 + if (speeds[i].speed == speed) {
1.1388 + return speeds[i].baud;
1.1389 + }
1.1390 + }
1.1391 + return 0;
1.1392 +}
1.1393 +
1.1394 +#endif /* !DIRECT_BAUD */
1.1395 +
1.1396 +
1.1397 +/*
1.1398 + *---------------------------------------------------------------------------
1.1399 + *
1.1400 + * TtyGetAttributes --
1.1401 + *
1.1402 + * Get the current attributes of the specified serial device.
1.1403 + *
1.1404 + * Results:
1.1405 + * None.
1.1406 + *
1.1407 + * Side effects:
1.1408 + * None.
1.1409 + *
1.1410 + *---------------------------------------------------------------------------
1.1411 + */
1.1412 +
1.1413 +static void
1.1414 +TtyGetAttributes(fd, ttyPtr)
1.1415 + int fd; /* Open file descriptor for serial port to
1.1416 + * be queried. */
1.1417 + TtyAttrs *ttyPtr; /* Buffer filled with serial port
1.1418 + * attributes. */
1.1419 +{
1.1420 + IOSTATE iostate;
1.1421 + int baud, parity, data, stop;
1.1422 +
1.1423 + GETIOSTATE(fd, &iostate);
1.1424 +
1.1425 +#ifdef USE_TERMIOS
1.1426 + baud = TtyGetBaud(cfgetospeed(&iostate));
1.1427 +
1.1428 + parity = 'n';
1.1429 +#ifdef PAREXT
1.1430 + switch ((int) (iostate.c_cflag & (PARENB | PARODD | PAREXT))) {
1.1431 + case PARENB : parity = 'e'; break;
1.1432 + case PARENB | PARODD : parity = 'o'; break;
1.1433 + case PARENB | PAREXT : parity = 's'; break;
1.1434 + case PARENB | PARODD | PAREXT : parity = 'm'; break;
1.1435 + }
1.1436 +#else /* !PAREXT */
1.1437 + switch ((int) (iostate.c_cflag & (PARENB | PARODD))) {
1.1438 + case PARENB : parity = 'e'; break;
1.1439 + case PARENB | PARODD : parity = 'o'; break;
1.1440 + }
1.1441 +#endif /* !PAREXT */
1.1442 +
1.1443 + data = iostate.c_cflag & CSIZE;
1.1444 + data = (data == CS5) ? 5 : (data == CS6) ? 6 : (data == CS7) ? 7 : 8;
1.1445 +
1.1446 + stop = (iostate.c_cflag & CSTOPB) ? 2 : 1;
1.1447 +#endif /* USE_TERMIOS */
1.1448 +
1.1449 +#ifdef USE_TERMIO
1.1450 + baud = TtyGetBaud(iostate.c_cflag & CBAUD);
1.1451 +
1.1452 + parity = 'n';
1.1453 + switch (iostate.c_cflag & (PARENB | PARODD | PAREXT)) {
1.1454 + case PARENB : parity = 'e'; break;
1.1455 + case PARENB | PARODD : parity = 'o'; break;
1.1456 + case PARENB | PAREXT : parity = 's'; break;
1.1457 + case PARENB | PARODD | PAREXT : parity = 'm'; break;
1.1458 + }
1.1459 +
1.1460 + data = iostate.c_cflag & CSIZE;
1.1461 + data = (data == CS5) ? 5 : (data == CS6) ? 6 : (data == CS7) ? 7 : 8;
1.1462 +
1.1463 + stop = (iostate.c_cflag & CSTOPB) ? 2 : 1;
1.1464 +#endif /* USE_TERMIO */
1.1465 +
1.1466 +#ifdef USE_SGTTY
1.1467 + baud = TtyGetBaud(iostate.sg_ospeed);
1.1468 +
1.1469 + parity = 'n';
1.1470 + if (iostate.sg_flags & EVENP) {
1.1471 + parity = 'e';
1.1472 + } else if (iostate.sg_flags & ODDP) {
1.1473 + parity = 'o';
1.1474 + }
1.1475 +
1.1476 + data = (iostate.sg_flags & (EVENP | ODDP)) ? 7 : 8;
1.1477 +
1.1478 + stop = 1;
1.1479 +#endif /* USE_SGTTY */
1.1480 +
1.1481 + ttyPtr->baud = baud;
1.1482 + ttyPtr->parity = parity;
1.1483 + ttyPtr->data = data;
1.1484 + ttyPtr->stop = stop;
1.1485 +}
1.1486 +
1.1487 +/*
1.1488 + *---------------------------------------------------------------------------
1.1489 + *
1.1490 + * TtySetAttributes --
1.1491 + *
1.1492 + * Set the current attributes of the specified serial device.
1.1493 + *
1.1494 + * Results:
1.1495 + * None.
1.1496 + *
1.1497 + * Side effects:
1.1498 + * None.
1.1499 + *
1.1500 + *---------------------------------------------------------------------------
1.1501 + */
1.1502 +
1.1503 +static void
1.1504 +TtySetAttributes(fd, ttyPtr)
1.1505 + int fd; /* Open file descriptor for serial port to
1.1506 + * be modified. */
1.1507 + TtyAttrs *ttyPtr; /* Buffer containing new attributes for
1.1508 + * serial port. */
1.1509 +{
1.1510 + IOSTATE iostate;
1.1511 +
1.1512 +#ifdef USE_TERMIOS
1.1513 + int parity, data, flag;
1.1514 +
1.1515 + GETIOSTATE(fd, &iostate);
1.1516 + cfsetospeed(&iostate, TtyGetSpeed(ttyPtr->baud));
1.1517 + cfsetispeed(&iostate, TtyGetSpeed(ttyPtr->baud));
1.1518 +
1.1519 + flag = 0;
1.1520 + parity = ttyPtr->parity;
1.1521 + if (parity != 'n') {
1.1522 + flag |= PARENB;
1.1523 +#ifdef PAREXT
1.1524 + iostate.c_cflag &= ~PAREXT;
1.1525 + if ((parity == 'm') || (parity == 's')) {
1.1526 + flag |= PAREXT;
1.1527 + }
1.1528 +#endif /* PAREXT */
1.1529 + if ((parity == 'm') || (parity == 'o')) {
1.1530 + flag |= PARODD;
1.1531 + }
1.1532 + }
1.1533 + data = ttyPtr->data;
1.1534 + flag |= (data == 5) ? CS5 : (data == 6) ? CS6 : (data == 7) ? CS7 : CS8;
1.1535 + if (ttyPtr->stop == 2) {
1.1536 + flag |= CSTOPB;
1.1537 + }
1.1538 +
1.1539 + iostate.c_cflag &= ~(PARENB | PARODD | CSIZE | CSTOPB);
1.1540 + iostate.c_cflag |= flag;
1.1541 +
1.1542 +#endif /* USE_TERMIOS */
1.1543 +
1.1544 +#ifdef USE_TERMIO
1.1545 + int parity, data, flag;
1.1546 +
1.1547 + GETIOSTATE(fd, &iostate);
1.1548 + iostate.c_cflag &= ~CBAUD;
1.1549 + iostate.c_cflag |= TtyGetSpeed(ttyPtr->baud);
1.1550 +
1.1551 + flag = 0;
1.1552 + parity = ttyPtr->parity;
1.1553 + if (parity != 'n') {
1.1554 + flag |= PARENB;
1.1555 + if ((parity == 'm') || (parity == 's')) {
1.1556 + flag |= PAREXT;
1.1557 + }
1.1558 + if ((parity == 'm') || (parity == 'o')) {
1.1559 + flag |= PARODD;
1.1560 + }
1.1561 + }
1.1562 + data = ttyPtr->data;
1.1563 + flag |= (data == 5) ? CS5 : (data == 6) ? CS6 : (data == 7) ? CS7 : CS8;
1.1564 + if (ttyPtr->stop == 2) {
1.1565 + flag |= CSTOPB;
1.1566 + }
1.1567 +
1.1568 + iostate.c_cflag &= ~(PARENB | PARODD | PAREXT | CSIZE | CSTOPB);
1.1569 + iostate.c_cflag |= flag;
1.1570 +
1.1571 +#endif /* USE_TERMIO */
1.1572 +
1.1573 +#ifdef USE_SGTTY
1.1574 + int parity;
1.1575 +
1.1576 + GETIOSTATE(fd, &iostate);
1.1577 + iostate.sg_ospeed = TtyGetSpeed(ttyPtr->baud);
1.1578 + iostate.sg_ispeed = TtyGetSpeed(ttyPtr->baud);
1.1579 +
1.1580 + parity = ttyPtr->parity;
1.1581 + if (parity == 'e') {
1.1582 + iostate.sg_flags &= ~ODDP;
1.1583 + iostate.sg_flags |= EVENP;
1.1584 + } else if (parity == 'o') {
1.1585 + iostate.sg_flags &= ~EVENP;
1.1586 + iostate.sg_flags |= ODDP;
1.1587 + }
1.1588 +#endif /* USE_SGTTY */
1.1589 +
1.1590 + SETIOSTATE(fd, &iostate);
1.1591 +}
1.1592 +
1.1593 +/*
1.1594 + *---------------------------------------------------------------------------
1.1595 + *
1.1596 + * TtyParseMode --
1.1597 + *
1.1598 + * Parse the "-mode" argument to the fconfigure command. The argument
1.1599 + * is of the form baud,parity,data,stop.
1.1600 + *
1.1601 + * Results:
1.1602 + * The return value is TCL_OK if the argument was successfully
1.1603 + * parsed, TCL_ERROR otherwise. If TCL_ERROR is returned, an
1.1604 + * error message is left in the interp's result (if interp is non-NULL).
1.1605 + *
1.1606 + * Side effects:
1.1607 + * None.
1.1608 + *
1.1609 + *---------------------------------------------------------------------------
1.1610 + */
1.1611 +
1.1612 +static int
1.1613 +TtyParseMode(interp, mode, speedPtr, parityPtr, dataPtr, stopPtr)
1.1614 + Tcl_Interp *interp; /* If non-NULL, interp for error return. */
1.1615 + CONST char *mode; /* Mode string to be parsed. */
1.1616 + int *speedPtr; /* Filled with baud rate from mode string. */
1.1617 + int *parityPtr; /* Filled with parity from mode string. */
1.1618 + int *dataPtr; /* Filled with data bits from mode string. */
1.1619 + int *stopPtr; /* Filled with stop bits from mode string. */
1.1620 +{
1.1621 + int i, end;
1.1622 + char parity;
1.1623 + static char *bad = "bad value for -mode";
1.1624 +
1.1625 + i = sscanf(mode, "%d,%c,%d,%d%n", speedPtr, &parity, dataPtr,
1.1626 + stopPtr, &end);
1.1627 + if ((i != 4) || (mode[end] != '\0')) {
1.1628 + if (interp != NULL) {
1.1629 + Tcl_AppendResult(interp, bad, ": should be baud,parity,data,stop",
1.1630 + NULL);
1.1631 + }
1.1632 + return TCL_ERROR;
1.1633 + }
1.1634 + /*
1.1635 + * Only allow setting mark/space parity on platforms that support it
1.1636 + * Make sure to allow for the case where strchr is a macro.
1.1637 + * [Bug: 5089]
1.1638 + */
1.1639 + if (
1.1640 +#if defined(PAREXT) || defined(USE_TERMIO)
1.1641 + strchr("noems", parity) == NULL
1.1642 +#else
1.1643 + strchr("noe", parity) == NULL
1.1644 +#endif /* PAREXT|USE_TERMIO */
1.1645 + ) {
1.1646 + if (interp != NULL) {
1.1647 + Tcl_AppendResult(interp, bad,
1.1648 +#if defined(PAREXT) || defined(USE_TERMIO)
1.1649 + " parity: should be n, o, e, m, or s",
1.1650 +#else
1.1651 + " parity: should be n, o, or e",
1.1652 +#endif /* PAREXT|USE_TERMIO */
1.1653 + NULL);
1.1654 + }
1.1655 + return TCL_ERROR;
1.1656 + }
1.1657 + *parityPtr = parity;
1.1658 + if ((*dataPtr < 5) || (*dataPtr > 8)) {
1.1659 + if (interp != NULL) {
1.1660 + Tcl_AppendResult(interp, bad, " data: should be 5, 6, 7, or 8",
1.1661 + NULL);
1.1662 + }
1.1663 + return TCL_ERROR;
1.1664 + }
1.1665 + if ((*stopPtr < 0) || (*stopPtr > 2)) {
1.1666 + if (interp != NULL) {
1.1667 + Tcl_AppendResult(interp, bad, " stop: should be 1 or 2", NULL);
1.1668 + }
1.1669 + return TCL_ERROR;
1.1670 + }
1.1671 + return TCL_OK;
1.1672 +}
1.1673 +
1.1674 +/*
1.1675 + *---------------------------------------------------------------------------
1.1676 + *
1.1677 + * TtyInit --
1.1678 + *
1.1679 + * Given file descriptor that refers to a serial port,
1.1680 + * initialize the serial port to a set of sane values so that
1.1681 + * Tcl can talk to a device located on the serial port.
1.1682 + * Note that no initialization happens if the initialize flag
1.1683 + * is not set; this is necessary for the correct handling of
1.1684 + * UNIX console TTYs at startup.
1.1685 + *
1.1686 + * Results:
1.1687 + * A pointer to a FileState suitable for use with Tcl_CreateChannel
1.1688 + * and the ttyChannelType structure.
1.1689 + *
1.1690 + * Side effects:
1.1691 + * Serial device initialized to non-blocking raw mode, similar to
1.1692 + * sockets (if initialize flag is non-zero.) All other modes can
1.1693 + * be simulated on top of this in Tcl.
1.1694 + *
1.1695 + *---------------------------------------------------------------------------
1.1696 + */
1.1697 +
1.1698 +static FileState *
1.1699 +TtyInit(fd, initialize)
1.1700 + int fd; /* Open file descriptor for serial port to
1.1701 + * be initialized. */
1.1702 + int initialize;
1.1703 +{
1.1704 + TtyState *ttyPtr;
1.1705 +
1.1706 + ttyPtr = (TtyState *) ckalloc((unsigned) sizeof(TtyState));
1.1707 + GETIOSTATE(fd, &ttyPtr->savedState);
1.1708 + ttyPtr->stateUpdated = 0;
1.1709 + if (initialize) {
1.1710 + IOSTATE iostate = ttyPtr->savedState;
1.1711 +
1.1712 +#if defined(USE_TERMIOS) || defined(USE_TERMIO)
1.1713 + if (iostate.c_iflag != IGNBRK ||
1.1714 + iostate.c_oflag != 0 ||
1.1715 + iostate.c_lflag != 0 ||
1.1716 + iostate.c_cflag & CREAD ||
1.1717 + iostate.c_cc[VMIN] != 1 ||
1.1718 + iostate.c_cc[VTIME] != 0) {
1.1719 + ttyPtr->stateUpdated = 1;
1.1720 + }
1.1721 + iostate.c_iflag = IGNBRK;
1.1722 + iostate.c_oflag = 0;
1.1723 + iostate.c_lflag = 0;
1.1724 + iostate.c_cflag |= CREAD;
1.1725 + iostate.c_cc[VMIN] = 1;
1.1726 + iostate.c_cc[VTIME] = 0;
1.1727 +#endif /* USE_TERMIOS|USE_TERMIO */
1.1728 +
1.1729 +#ifdef USE_SGTTY
1.1730 + if ((iostate.sg_flags & (EVENP | ODDP)) ||
1.1731 + !(iostate.sg_flags & RAW)) {
1.1732 + ttyPtr->stateUpdated = 1;
1.1733 + }
1.1734 + iostate.sg_flags &= (EVENP | ODDP);
1.1735 + iostate.sg_flags |= RAW;
1.1736 +#endif /* USE_SGTTY */
1.1737 +
1.1738 + /*
1.1739 + * Only update if we're changing anything to avoid possible
1.1740 + * blocking.
1.1741 + */
1.1742 + if (ttyPtr->stateUpdated) {
1.1743 + SETIOSTATE(fd, &iostate);
1.1744 + }
1.1745 + }
1.1746 +
1.1747 + return &ttyPtr->fs;
1.1748 +}
1.1749 +#endif /* SUPPORTS_TTY */
1.1750 +
1.1751 +/*
1.1752 + *----------------------------------------------------------------------
1.1753 + *
1.1754 + * TclpOpenFileChannel --
1.1755 + *
1.1756 + * Open an file based channel on Unix systems.
1.1757 + *
1.1758 + * Results:
1.1759 + * The new channel or NULL. If NULL, the output argument
1.1760 + * errorCodePtr is set to a POSIX error and an error message is
1.1761 + * left in the interp's result if interp is not NULL.
1.1762 + *
1.1763 + * Side effects:
1.1764 + * May open the channel and may cause creation of a file on the
1.1765 + * file system.
1.1766 + *
1.1767 + *----------------------------------------------------------------------
1.1768 + */
1.1769 +
1.1770 +Tcl_Channel
1.1771 +TclpOpenFileChannel(interp, pathPtr, mode, permissions)
1.1772 + Tcl_Interp *interp; /* Interpreter for error reporting;
1.1773 + * can be NULL. */
1.1774 + Tcl_Obj *pathPtr; /* Name of file to open. */
1.1775 + int mode; /* POSIX open mode. */
1.1776 + int permissions; /* If the open involves creating a
1.1777 + * file, with what modes to create
1.1778 + * it? */
1.1779 +{
1.1780 + int fd, channelPermissions;
1.1781 + FileState *fsPtr;
1.1782 + CONST char *native, *translation;
1.1783 + char channelName[16 + TCL_INTEGER_SPACE];
1.1784 + Tcl_ChannelType *channelTypePtr;
1.1785 +#ifdef SUPPORTS_TTY
1.1786 + int ctl_tty;
1.1787 +#endif /* SUPPORTS_TTY */
1.1788 +#ifdef DEPRECATED
1.1789 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.1790 +#endif /* DEPRECATED */
1.1791 +
1.1792 + switch (mode & (O_RDONLY | O_WRONLY | O_RDWR)) {
1.1793 + case O_RDONLY:
1.1794 + channelPermissions = TCL_READABLE;
1.1795 + break;
1.1796 + case O_WRONLY:
1.1797 + channelPermissions = TCL_WRITABLE;
1.1798 + break;
1.1799 + case O_RDWR:
1.1800 + channelPermissions = (TCL_READABLE | TCL_WRITABLE);
1.1801 + break;
1.1802 + default:
1.1803 + /*
1.1804 + * This may occurr if modeString was "", for example.
1.1805 + */
1.1806 + panic("TclpOpenFileChannel: invalid mode value");
1.1807 + return NULL;
1.1808 + }
1.1809 +
1.1810 + native = Tcl_FSGetNativePath(pathPtr);
1.1811 + if (native == NULL) {
1.1812 + return NULL;
1.1813 + }
1.1814 + fd = TclOSopen(native, mode, permissions);
1.1815 +#ifdef SUPPORTS_TTY
1.1816 + ctl_tty = (strcmp (native, "/dev/tty") == 0);
1.1817 +#endif /* SUPPORTS_TTY */
1.1818 +
1.1819 + if (fd < 0) {
1.1820 + if (interp != (Tcl_Interp *) NULL) {
1.1821 + Tcl_AppendResult(interp, "couldn't open \"",
1.1822 + Tcl_GetString(pathPtr), "\": ",
1.1823 + Tcl_PosixError(interp), (char *) NULL);
1.1824 + }
1.1825 + return NULL;
1.1826 + }
1.1827 +
1.1828 + /*
1.1829 + * Set close-on-exec flag on the fd so that child processes will not
1.1830 + * inherit this fd.
1.1831 + */
1.1832 +
1.1833 + fcntl(fd, F_SETFD, FD_CLOEXEC);
1.1834 +
1.1835 + sprintf(channelName, "file%d", fd);
1.1836 +
1.1837 +#ifdef SUPPORTS_TTY
1.1838 + if (!ctl_tty && isatty(fd)) {
1.1839 + /*
1.1840 + * Initialize the serial port to a set of sane parameters.
1.1841 + * Especially important if the remote device is set to echo and
1.1842 + * the serial port driver was also set to echo -- as soon as a char
1.1843 + * were sent to the serial port, the remote device would echo it,
1.1844 + * then the serial driver would echo it back to the device, etc.
1.1845 + */
1.1846 +
1.1847 + translation = "auto crlf";
1.1848 + channelTypePtr = &ttyChannelType;
1.1849 + fsPtr = TtyInit(fd, 1);
1.1850 + } else
1.1851 +#endif /* SUPPORTS_TTY */
1.1852 + {
1.1853 + translation = NULL;
1.1854 + channelTypePtr = &fileChannelType;
1.1855 + fsPtr = (FileState *) ckalloc((unsigned) sizeof(FileState));
1.1856 + }
1.1857 +
1.1858 +#ifdef DEPRECATED
1.1859 + if (channelTypePtr == &fileChannelType) {
1.1860 + /* TIP #218. Removed the code inserting the new structure
1.1861 + * into the global list. This is now handled in the thread
1.1862 + * action callbacks, and only there.
1.1863 + */
1.1864 + fsPtr->nextPtr = NULL;
1.1865 + }
1.1866 +#endif /* DEPRECATED */
1.1867 + fsPtr->validMask = channelPermissions | TCL_EXCEPTION;
1.1868 + fsPtr->fd = fd;
1.1869 +
1.1870 + fsPtr->channel = Tcl_CreateChannel(channelTypePtr, channelName,
1.1871 + (ClientData) fsPtr, channelPermissions);
1.1872 +
1.1873 + if (translation != NULL) {
1.1874 + /*
1.1875 + * Gotcha. Most modems need a "\r" at the end of the command
1.1876 + * sequence. If you just send "at\n", the modem will not respond
1.1877 + * with "OK" because it never got a "\r" to actually invoke the
1.1878 + * command. So, by default, newlines are translated to "\r\n" on
1.1879 + * output to avoid "bug" reports that the serial port isn't working.
1.1880 + */
1.1881 +
1.1882 + if (Tcl_SetChannelOption(interp, fsPtr->channel, "-translation",
1.1883 + translation) != TCL_OK) {
1.1884 + Tcl_Close(NULL, fsPtr->channel);
1.1885 + return NULL;
1.1886 + }
1.1887 + }
1.1888 +
1.1889 + return fsPtr->channel;
1.1890 +}
1.1891 +
1.1892 +/*
1.1893 + *----------------------------------------------------------------------
1.1894 + *
1.1895 + * Tcl_MakeFileChannel --
1.1896 + *
1.1897 + * Makes a Tcl_Channel from an existing OS level file handle.
1.1898 + *
1.1899 + * Results:
1.1900 + * The Tcl_Channel created around the preexisting OS level file handle.
1.1901 + *
1.1902 + * Side effects:
1.1903 + * None.
1.1904 + *
1.1905 + *----------------------------------------------------------------------
1.1906 + */
1.1907 +
1.1908 +EXPORT_C Tcl_Channel
1.1909 +Tcl_MakeFileChannel(handle, mode)
1.1910 + ClientData handle; /* OS level handle. */
1.1911 + int mode; /* ORed combination of TCL_READABLE and
1.1912 + * TCL_WRITABLE to indicate file mode. */
1.1913 +{
1.1914 + FileState *fsPtr;
1.1915 + char channelName[16 + TCL_INTEGER_SPACE];
1.1916 + int fd = (int) handle;
1.1917 + Tcl_ChannelType *channelTypePtr;
1.1918 +#ifdef DEPRECATED
1.1919 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.1920 +#endif /* DEPRECATED */
1.1921 + struct sockaddr sockaddr;
1.1922 + socklen_t sockaddrLen = sizeof(sockaddr);
1.1923 +
1.1924 + if (mode == 0) {
1.1925 + return NULL;
1.1926 + }
1.1927 +
1.1928 +
1.1929 + /*
1.1930 + * Look to see if a channel with this fd and the same mode already exists.
1.1931 + * If the fd is used, but the mode doesn't match, return NULL.
1.1932 + */
1.1933 +
1.1934 +#ifdef DEPRECATED
1.1935 + for (fsPtr = tsdPtr->firstFilePtr; fsPtr != NULL; fsPtr = fsPtr->nextPtr) {
1.1936 + if (fsPtr->fd == fd) {
1.1937 + return ((mode|TCL_EXCEPTION) == fsPtr->validMask) ?
1.1938 + fsPtr->channel : NULL;
1.1939 + }
1.1940 + }
1.1941 +#endif /* DEPRECATED */
1.1942 +
1.1943 + sockaddr.sa_family = AF_UNSPEC;
1.1944 +
1.1945 +#ifdef SUPPORTS_TTY
1.1946 + if (isatty(fd)) {
1.1947 + fsPtr = TtyInit(fd, 0);
1.1948 + channelTypePtr = &ttyChannelType;
1.1949 + sprintf(channelName, "serial%d", fd);
1.1950 + } else
1.1951 +#endif /* SUPPORTS_TTY */
1.1952 + if (getsockname(fd, (struct sockaddr *)&sockaddr, &sockaddrLen) == 0
1.1953 + && sockaddrLen > 0
1.1954 + && sockaddr.sa_family == AF_INET) {
1.1955 + return MakeTcpClientChannelMode((ClientData) fd, mode);
1.1956 + } else {
1.1957 + channelTypePtr = &fileChannelType;
1.1958 + fsPtr = (FileState *) ckalloc((unsigned) sizeof(FileState));
1.1959 + sprintf(channelName, "file%d", fd);
1.1960 + }
1.1961 +
1.1962 +#ifdef DEPRECATED
1.1963 + if (channelTypePtr == &fileChannelType) {
1.1964 + fsPtr->nextPtr = tsdPtr->firstFilePtr;
1.1965 + tsdPtr->firstFilePtr = fsPtr;
1.1966 + }
1.1967 +#endif /* DEPRECATED */
1.1968 + fsPtr->fd = fd;
1.1969 + fsPtr->validMask = mode | TCL_EXCEPTION;
1.1970 + fsPtr->channel = Tcl_CreateChannel(channelTypePtr, channelName,
1.1971 + (ClientData) fsPtr, mode);
1.1972 +
1.1973 + return fsPtr->channel;
1.1974 +}
1.1975 +
1.1976 +/*
1.1977 + *----------------------------------------------------------------------
1.1978 + *
1.1979 + * TcpBlockModeProc --
1.1980 + *
1.1981 + * This procedure is invoked by the generic IO level to set blocking
1.1982 + * and nonblocking mode on a TCP socket based channel.
1.1983 + *
1.1984 + * Results:
1.1985 + * 0 if successful, errno when failed.
1.1986 + *
1.1987 + * Side effects:
1.1988 + * Sets the device into blocking or nonblocking mode.
1.1989 + *
1.1990 + *----------------------------------------------------------------------
1.1991 + */
1.1992 +
1.1993 + /* ARGSUSED */
1.1994 +static int
1.1995 +TcpBlockModeProc(instanceData, mode)
1.1996 + ClientData instanceData; /* Socket state. */
1.1997 + int mode; /* The mode to set. Can be one of
1.1998 + * TCL_MODE_BLOCKING or
1.1999 + * TCL_MODE_NONBLOCKING. */
1.2000 +{
1.2001 + TcpState *statePtr = (TcpState *) instanceData;
1.2002 + int setting;
1.2003 +
1.2004 +#ifndef USE_FIONBIO
1.2005 + setting = fcntl(statePtr->fd, F_GETFL);
1.2006 + if (mode == TCL_MODE_BLOCKING) {
1.2007 + statePtr->flags &= (~(TCP_ASYNC_SOCKET));
1.2008 + setting &= (~(O_NONBLOCK));
1.2009 + } else {
1.2010 + statePtr->flags |= TCP_ASYNC_SOCKET;
1.2011 + setting |= O_NONBLOCK;
1.2012 + }
1.2013 + if (fcntl(statePtr->fd, F_SETFL, setting) < 0) {
1.2014 + return errno;
1.2015 + }
1.2016 +#else /* USE_FIONBIO */
1.2017 + if (mode == TCL_MODE_BLOCKING) {
1.2018 + statePtr->flags &= (~(TCP_ASYNC_SOCKET));
1.2019 + setting = 0;
1.2020 + if (ioctl(statePtr->fd, (int) FIONBIO, &setting) == -1) {
1.2021 + return errno;
1.2022 + }
1.2023 + } else {
1.2024 + statePtr->flags |= TCP_ASYNC_SOCKET;
1.2025 + setting = 1;
1.2026 + if (ioctl(statePtr->fd, (int) FIONBIO, &setting) == -1) {
1.2027 + return errno;
1.2028 + }
1.2029 + }
1.2030 +#endif /* !USE_FIONBIO */
1.2031 +
1.2032 + return 0;
1.2033 +}
1.2034 +
1.2035 +/*
1.2036 + *----------------------------------------------------------------------
1.2037 + *
1.2038 + * WaitForConnect --
1.2039 + *
1.2040 + * Waits for a connection on an asynchronously opened socket to
1.2041 + * be completed.
1.2042 + *
1.2043 + * Results:
1.2044 + * None.
1.2045 + *
1.2046 + * Side effects:
1.2047 + * The socket is connected after this function returns.
1.2048 + *
1.2049 + *----------------------------------------------------------------------
1.2050 + */
1.2051 +
1.2052 +static int
1.2053 +WaitForConnect(statePtr, errorCodePtr)
1.2054 + TcpState *statePtr; /* State of the socket. */
1.2055 + int *errorCodePtr; /* Where to store errors? */
1.2056 +{
1.2057 + int timeOut; /* How long to wait. */
1.2058 + int state; /* Of calling TclWaitForFile. */
1.2059 + int flags; /* fcntl flags for the socket. */
1.2060 +
1.2061 + /*
1.2062 + * If an asynchronous connect is in progress, attempt to wait for it
1.2063 + * to complete before reading.
1.2064 + */
1.2065 +
1.2066 + if (statePtr->flags & TCP_ASYNC_CONNECT) {
1.2067 + if (statePtr->flags & TCP_ASYNC_SOCKET) {
1.2068 + timeOut = 0;
1.2069 + } else {
1.2070 + timeOut = -1;
1.2071 + }
1.2072 + errno = 0;
1.2073 + state = TclUnixWaitForFile(statePtr->fd,
1.2074 + TCL_WRITABLE | TCL_EXCEPTION, timeOut);
1.2075 + if (!(statePtr->flags & TCP_ASYNC_SOCKET)) {
1.2076 +#ifndef USE_FIONBIO
1.2077 + flags = fcntl(statePtr->fd, F_GETFL);
1.2078 + flags &= (~(O_NONBLOCK));
1.2079 + (void) fcntl(statePtr->fd, F_SETFL, flags);
1.2080 +#else /* USE_FIONBIO */
1.2081 + flags = 0;
1.2082 + (void) ioctl(statePtr->fd, FIONBIO, &flags);
1.2083 +#endif /* !USE_FIONBIO */
1.2084 + }
1.2085 + if (state & TCL_EXCEPTION) {
1.2086 + return -1;
1.2087 + }
1.2088 + if (state & TCL_WRITABLE) {
1.2089 + statePtr->flags &= (~(TCP_ASYNC_CONNECT));
1.2090 + } else if (timeOut == 0) {
1.2091 + *errorCodePtr = errno = EWOULDBLOCK;
1.2092 + return -1;
1.2093 + }
1.2094 + }
1.2095 + return 0;
1.2096 +}
1.2097 +
1.2098 +/*
1.2099 + *----------------------------------------------------------------------
1.2100 + *
1.2101 + * TcpInputProc --
1.2102 + *
1.2103 + * This procedure is invoked by the generic IO level to read input
1.2104 + * from a TCP socket based channel.
1.2105 + *
1.2106 + * NOTE: We cannot share code with FilePipeInputProc because here
1.2107 + * we must use recv to obtain the input from the channel, not read.
1.2108 + *
1.2109 + * Results:
1.2110 + * The number of bytes read is returned or -1 on error. An output
1.2111 + * argument contains the POSIX error code on error, or zero if no
1.2112 + * error occurred.
1.2113 + *
1.2114 + * Side effects:
1.2115 + * Reads input from the input device of the channel.
1.2116 + *
1.2117 + *----------------------------------------------------------------------
1.2118 + */
1.2119 +
1.2120 + /* ARGSUSED */
1.2121 +static int
1.2122 +TcpInputProc(instanceData, buf, bufSize, errorCodePtr)
1.2123 + ClientData instanceData; /* Socket state. */
1.2124 + char *buf; /* Where to store data read. */
1.2125 + int bufSize; /* How much space is available
1.2126 + * in the buffer? */
1.2127 + int *errorCodePtr; /* Where to store error code. */
1.2128 +{
1.2129 + TcpState *statePtr = (TcpState *) instanceData;
1.2130 + int bytesRead, state;
1.2131 +
1.2132 + *errorCodePtr = 0;
1.2133 + state = WaitForConnect(statePtr, errorCodePtr);
1.2134 + if (state != 0) {
1.2135 + return -1;
1.2136 + }
1.2137 + bytesRead = recv(statePtr->fd, buf, (size_t) bufSize, 0);
1.2138 + if (bytesRead > -1) {
1.2139 + return bytesRead;
1.2140 + }
1.2141 + if (errno == ECONNRESET) {
1.2142 + /*
1.2143 + * Turn ECONNRESET into a soft EOF condition.
1.2144 + */
1.2145 +
1.2146 + return 0;
1.2147 + }
1.2148 + *errorCodePtr = errno;
1.2149 + return -1;
1.2150 +}
1.2151 +
1.2152 +/*
1.2153 + *----------------------------------------------------------------------
1.2154 + *
1.2155 + * TcpOutputProc --
1.2156 + *
1.2157 + * This procedure is invoked by the generic IO level to write output
1.2158 + * to a TCP socket based channel.
1.2159 + *
1.2160 + * NOTE: We cannot share code with FilePipeOutputProc because here
1.2161 + * we must use send, not write, to get reliable error reporting.
1.2162 + *
1.2163 + * Results:
1.2164 + * The number of bytes written is returned. An output argument is
1.2165 + * set to a POSIX error code if an error occurred, or zero.
1.2166 + *
1.2167 + * Side effects:
1.2168 + * Writes output on the output device of the channel.
1.2169 + *
1.2170 + *----------------------------------------------------------------------
1.2171 + */
1.2172 +
1.2173 +static int
1.2174 +TcpOutputProc(instanceData, buf, toWrite, errorCodePtr)
1.2175 + ClientData instanceData; /* Socket state. */
1.2176 + CONST char *buf; /* The data buffer. */
1.2177 + int toWrite; /* How many bytes to write? */
1.2178 + int *errorCodePtr; /* Where to store error code. */
1.2179 +{
1.2180 + TcpState *statePtr = (TcpState *) instanceData;
1.2181 + int written;
1.2182 + int state; /* Of waiting for connection. */
1.2183 +
1.2184 + *errorCodePtr = 0;
1.2185 + state = WaitForConnect(statePtr, errorCodePtr);
1.2186 + if (state != 0) {
1.2187 + return -1;
1.2188 + }
1.2189 + written = send(statePtr->fd, buf, (size_t) toWrite, 0);
1.2190 + if (written > -1) {
1.2191 + return written;
1.2192 + }
1.2193 + *errorCodePtr = errno;
1.2194 + return -1;
1.2195 +}
1.2196 +
1.2197 +/*
1.2198 + *----------------------------------------------------------------------
1.2199 + *
1.2200 + * TcpCloseProc --
1.2201 + *
1.2202 + * This procedure is invoked by the generic IO level to perform
1.2203 + * channel-type-specific cleanup when a TCP socket based channel
1.2204 + * is closed.
1.2205 + *
1.2206 + * Results:
1.2207 + * 0 if successful, the value of errno if failed.
1.2208 + *
1.2209 + * Side effects:
1.2210 + * Closes the socket of the channel.
1.2211 + *
1.2212 + *----------------------------------------------------------------------
1.2213 + */
1.2214 +
1.2215 + /* ARGSUSED */
1.2216 +static int
1.2217 +TcpCloseProc(instanceData, interp)
1.2218 + ClientData instanceData; /* The socket to close. */
1.2219 + Tcl_Interp *interp; /* For error reporting - unused. */
1.2220 +{
1.2221 + TcpState *statePtr = (TcpState *) instanceData;
1.2222 + int errorCode = 0;
1.2223 +
1.2224 + /*
1.2225 + * Delete a file handler that may be active for this socket if this
1.2226 + * is a server socket - the file handler was created automatically
1.2227 + * by Tcl as part of the mechanism to accept new client connections.
1.2228 + * Channel handlers are already deleted in the generic IO channel
1.2229 + * closing code that called this function, so we do not have to
1.2230 + * delete them here.
1.2231 + */
1.2232 +
1.2233 + Tcl_DeleteFileHandler(statePtr->fd);
1.2234 +
1.2235 + if (close(statePtr->fd) < 0) {
1.2236 + errorCode = errno;
1.2237 + }
1.2238 + ckfree((char *) statePtr);
1.2239 +
1.2240 + return errorCode;
1.2241 +}
1.2242 +
1.2243 +/*
1.2244 + *----------------------------------------------------------------------
1.2245 + *
1.2246 + * TcpGetOptionProc --
1.2247 + *
1.2248 + * Computes an option value for a TCP socket based channel, or a
1.2249 + * list of all options and their values.
1.2250 + *
1.2251 + * Note: This code is based on code contributed by John Haxby.
1.2252 + *
1.2253 + * Results:
1.2254 + * A standard Tcl result. The value of the specified option or a
1.2255 + * list of all options and their values is returned in the
1.2256 + * supplied DString. Sets Error message if needed.
1.2257 + *
1.2258 + * Side effects:
1.2259 + * None.
1.2260 + *
1.2261 + *----------------------------------------------------------------------
1.2262 + */
1.2263 +
1.2264 +static int
1.2265 +TcpGetOptionProc(instanceData, interp, optionName, dsPtr)
1.2266 + ClientData instanceData; /* Socket state. */
1.2267 + Tcl_Interp *interp; /* For error reporting - can be NULL. */
1.2268 + CONST char *optionName; /* Name of the option to
1.2269 + * retrieve the value for, or
1.2270 + * NULL to get all options and
1.2271 + * their values. */
1.2272 + Tcl_DString *dsPtr; /* Where to store the computed
1.2273 + * value; initialized by caller. */
1.2274 +{
1.2275 + TcpState *statePtr = (TcpState *) instanceData;
1.2276 + struct sockaddr_in sockname;
1.2277 + struct sockaddr_in peername;
1.2278 + struct hostent *hostEntPtr;
1.2279 + socklen_t size = sizeof(struct sockaddr_in);
1.2280 + size_t len = 0;
1.2281 + char buf[TCL_INTEGER_SPACE];
1.2282 +
1.2283 + if (optionName != (char *) NULL) {
1.2284 + len = strlen(optionName);
1.2285 + }
1.2286 +
1.2287 + if ((len > 1) && (optionName[1] == 'e') &&
1.2288 + (strncmp(optionName, "-error", len) == 0)) {
1.2289 + socklen_t optlen = sizeof(int);
1.2290 + int err, ret;
1.2291 + err = 0;
1.2292 +
1.2293 +
1.2294 + ret = getsockopt(statePtr->fd, SOL_SOCKET, SO_ERROR,
1.2295 + (char *)&err, &optlen);
1.2296 + if (ret < 0) {
1.2297 + err = errno;
1.2298 + }
1.2299 + if (err != 0) {
1.2300 + Tcl_DStringAppend(dsPtr, Tcl_ErrnoMsg(err), -1);
1.2301 + }
1.2302 + return TCL_OK;
1.2303 + }
1.2304 +
1.2305 + if ((len == 0) ||
1.2306 + ((len > 1) && (optionName[1] == 'p') &&
1.2307 + (strncmp(optionName, "-peername", len) == 0))) {
1.2308 + if (getpeername(statePtr->fd, (struct sockaddr *) &peername,
1.2309 + &size) >= 0) {
1.2310 + if (len == 0) {
1.2311 + Tcl_DStringAppendElement(dsPtr, "-peername");
1.2312 + Tcl_DStringStartSublist(dsPtr);
1.2313 + }
1.2314 + Tcl_DStringAppendElement(dsPtr, inet_ntoa(peername.sin_addr));
1.2315 + hostEntPtr = TclpGetHostByAddr( /* INTL: Native. */
1.2316 + (char *) &peername.sin_addr,
1.2317 + sizeof(peername.sin_addr), AF_INET);
1.2318 + if (hostEntPtr != (struct hostent *) NULL) {
1.2319 + Tcl_DString ds;
1.2320 +
1.2321 + Tcl_ExternalToUtfDString(NULL, hostEntPtr->h_name, -1, &ds);
1.2322 + Tcl_DStringAppendElement(dsPtr, Tcl_DStringValue(&ds));
1.2323 + Tcl_DStringFree(&ds);
1.2324 + } else {
1.2325 + Tcl_DStringAppendElement(dsPtr, inet_ntoa(peername.sin_addr));
1.2326 + }
1.2327 + TclFormatInt(buf, ntohs(peername.sin_port));
1.2328 + Tcl_DStringAppendElement(dsPtr, buf);
1.2329 + if (len == 0) {
1.2330 + Tcl_DStringEndSublist(dsPtr);
1.2331 + } else {
1.2332 + return TCL_OK;
1.2333 + }
1.2334 + } else {
1.2335 + /*
1.2336 + * getpeername failed - but if we were asked for all the options
1.2337 + * (len==0), don't flag an error at that point because it could
1.2338 + * be an fconfigure request on a server socket. (which have
1.2339 + * no peer). same must be done on win&mac.
1.2340 + */
1.2341 +
1.2342 + if (len) {
1.2343 + if (interp) {
1.2344 + Tcl_AppendResult(interp, "can't get peername: ",
1.2345 + Tcl_PosixError(interp), (char *) NULL);
1.2346 + }
1.2347 + return TCL_ERROR;
1.2348 + }
1.2349 + }
1.2350 + }
1.2351 +
1.2352 + if ((len == 0) ||
1.2353 + ((len > 1) && (optionName[1] == 's') &&
1.2354 + (strncmp(optionName, "-sockname", len) == 0))) {
1.2355 + if (getsockname(statePtr->fd, (struct sockaddr *) &sockname,
1.2356 + &size) >= 0) {
1.2357 + if (len == 0) {
1.2358 + Tcl_DStringAppendElement(dsPtr, "-sockname");
1.2359 + Tcl_DStringStartSublist(dsPtr);
1.2360 + }
1.2361 + Tcl_DStringAppendElement(dsPtr, inet_ntoa(sockname.sin_addr));
1.2362 + hostEntPtr = TclpGetHostByAddr( /* INTL: Native. */
1.2363 + (char *) &sockname.sin_addr,
1.2364 + sizeof(sockname.sin_addr), AF_INET);
1.2365 + if (hostEntPtr != (struct hostent *) NULL) {
1.2366 + Tcl_DString ds;
1.2367 +
1.2368 + Tcl_ExternalToUtfDString(NULL, hostEntPtr->h_name, -1, &ds);
1.2369 + Tcl_DStringAppendElement(dsPtr, Tcl_DStringValue(&ds));
1.2370 + Tcl_DStringFree(&ds);
1.2371 + } else {
1.2372 + Tcl_DStringAppendElement(dsPtr, inet_ntoa(sockname.sin_addr));
1.2373 + }
1.2374 + TclFormatInt(buf, ntohs(sockname.sin_port));
1.2375 + Tcl_DStringAppendElement(dsPtr, buf);
1.2376 + if (len == 0) {
1.2377 + Tcl_DStringEndSublist(dsPtr);
1.2378 + } else {
1.2379 + return TCL_OK;
1.2380 + }
1.2381 + } else {
1.2382 + if (interp) {
1.2383 + Tcl_AppendResult(interp, "can't get sockname: ",
1.2384 + Tcl_PosixError(interp), (char *) NULL);
1.2385 + }
1.2386 + return TCL_ERROR;
1.2387 + }
1.2388 + }
1.2389 +
1.2390 + if (len > 0) {
1.2391 + return Tcl_BadChannelOption(interp, optionName, "peername sockname");
1.2392 + }
1.2393 +
1.2394 + return TCL_OK;
1.2395 +}
1.2396 +
1.2397 +/*
1.2398 + *----------------------------------------------------------------------
1.2399 + *
1.2400 + * TcpWatchProc --
1.2401 + *
1.2402 + * Initialize the notifier to watch the fd from this channel.
1.2403 + *
1.2404 + * Results:
1.2405 + * None.
1.2406 + *
1.2407 + * Side effects:
1.2408 + * Sets up the notifier so that a future event on the channel will
1.2409 + * be seen by Tcl.
1.2410 + *
1.2411 + *----------------------------------------------------------------------
1.2412 + */
1.2413 +
1.2414 +static void
1.2415 +TcpWatchProc(instanceData, mask)
1.2416 + ClientData instanceData; /* The socket state. */
1.2417 + int mask; /* Events of interest; an OR-ed
1.2418 + * combination of TCL_READABLE,
1.2419 + * TCL_WRITABLE and TCL_EXCEPTION. */
1.2420 +{
1.2421 + TcpState *statePtr = (TcpState *) instanceData;
1.2422 +
1.2423 + /*
1.2424 + * Make sure we don't mess with server sockets since they will never
1.2425 + * be readable or writable at the Tcl level. This keeps Tcl scripts
1.2426 + * from interfering with the -accept behavior.
1.2427 + */
1.2428 +
1.2429 + if (!statePtr->acceptProc) {
1.2430 + if (mask) {
1.2431 + Tcl_CreateFileHandler(statePtr->fd, mask,
1.2432 + (Tcl_FileProc *) Tcl_NotifyChannel,
1.2433 + (ClientData) statePtr->channel);
1.2434 + } else {
1.2435 + Tcl_DeleteFileHandler(statePtr->fd);
1.2436 + }
1.2437 + }
1.2438 +}
1.2439 +
1.2440 +/*
1.2441 + *----------------------------------------------------------------------
1.2442 + *
1.2443 + * TcpGetHandleProc --
1.2444 + *
1.2445 + * Called from Tcl_GetChannelHandle to retrieve OS handles from inside
1.2446 + * a TCP socket based channel.
1.2447 + *
1.2448 + * Results:
1.2449 + * Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if
1.2450 + * there is no handle for the specified direction.
1.2451 + *
1.2452 + * Side effects:
1.2453 + * None.
1.2454 + *
1.2455 + *----------------------------------------------------------------------
1.2456 + */
1.2457 +
1.2458 + /* ARGSUSED */
1.2459 +static int
1.2460 +TcpGetHandleProc(instanceData, direction, handlePtr)
1.2461 + ClientData instanceData; /* The socket state. */
1.2462 + int direction; /* Not used. */
1.2463 + ClientData *handlePtr; /* Where to store the handle. */
1.2464 +{
1.2465 + TcpState *statePtr = (TcpState *) instanceData;
1.2466 +
1.2467 + *handlePtr = (ClientData)statePtr->fd;
1.2468 + return TCL_OK;
1.2469 +}
1.2470 +
1.2471 +/*
1.2472 + *----------------------------------------------------------------------
1.2473 + *
1.2474 + * CreateSocket --
1.2475 + *
1.2476 + * This function opens a new socket in client or server mode
1.2477 + * and initializes the TcpState structure.
1.2478 + *
1.2479 + * Results:
1.2480 + * Returns a new TcpState, or NULL with an error in the interp's
1.2481 + * result, if interp is not NULL.
1.2482 + *
1.2483 + * Side effects:
1.2484 + * Opens a socket.
1.2485 + *
1.2486 + *----------------------------------------------------------------------
1.2487 + */
1.2488 +
1.2489 +static TcpState *
1.2490 +CreateSocket(interp, port, host, server, myaddr, myport, async)
1.2491 + Tcl_Interp *interp; /* For error reporting; can be NULL. */
1.2492 + int port; /* Port number to open. */
1.2493 + CONST char *host; /* Name of host on which to open port.
1.2494 + * NULL implies INADDR_ANY */
1.2495 + int server; /* 1 if socket should be a server socket,
1.2496 + * else 0 for a client socket. */
1.2497 + CONST char *myaddr; /* Optional client-side address */
1.2498 + int myport; /* Optional client-side port */
1.2499 + int async; /* If nonzero and creating a client socket,
1.2500 + * attempt to do an async connect. Otherwise
1.2501 + * do a synchronous connect or bind. */
1.2502 +{
1.2503 + int status, sock, asyncConnect, curState, origState;
1.2504 + struct sockaddr_in sockaddr; /* socket address */
1.2505 + struct sockaddr_in mysockaddr; /* Socket address for client */
1.2506 + TcpState *statePtr;
1.2507 +
1.2508 + sock = -1;
1.2509 + origState = 0;
1.2510 + if (! CreateSocketAddress(&sockaddr, host, port)) {
1.2511 + goto addressError;
1.2512 + }
1.2513 + if ((myaddr != NULL || myport != 0) &&
1.2514 + ! CreateSocketAddress(&mysockaddr, myaddr, myport)) {
1.2515 + goto addressError;
1.2516 + }
1.2517 +
1.2518 + sock = socket(AF_INET, SOCK_STREAM, 0);
1.2519 + if (sock < 0) {
1.2520 + goto addressError;
1.2521 + }
1.2522 +
1.2523 + /*
1.2524 + * Set the close-on-exec flag so that the socket will not get
1.2525 + * inherited by child processes.
1.2526 + */
1.2527 +
1.2528 + fcntl(sock, F_SETFD, FD_CLOEXEC);
1.2529 +
1.2530 + /*
1.2531 + * Set kernel space buffering
1.2532 + */
1.2533 +
1.2534 + TclSockMinimumBuffers(sock, SOCKET_BUFSIZE);
1.2535 +
1.2536 + asyncConnect = 0;
1.2537 + status = 0;
1.2538 + if (server) {
1.2539 + /*
1.2540 + * Set up to reuse server addresses automatically and bind to the
1.2541 + * specified port.
1.2542 + */
1.2543 +
1.2544 + status = 1;
1.2545 + (void) setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &status,
1.2546 + sizeof(status));
1.2547 + status = bind(sock, (struct sockaddr *) &sockaddr,
1.2548 + sizeof(struct sockaddr));
1.2549 + if (status != -1) {
1.2550 + status = listen(sock, SOMAXCONN);
1.2551 + }
1.2552 + } else {
1.2553 + if (myaddr != NULL || myport != 0) {
1.2554 + curState = 1;
1.2555 + (void) setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
1.2556 + (char *) &curState, sizeof(curState));
1.2557 + status = bind(sock, (struct sockaddr *) &mysockaddr,
1.2558 + sizeof(struct sockaddr));
1.2559 + if (status < 0) {
1.2560 + goto bindError;
1.2561 + }
1.2562 + }
1.2563 +
1.2564 + /*
1.2565 + * Attempt to connect. The connect may fail at present with an
1.2566 + * EINPROGRESS but at a later time it will complete. The caller
1.2567 + * will set up a file handler on the socket if she is interested in
1.2568 + * being informed when the connect completes.
1.2569 + */
1.2570 +
1.2571 + if (async) {
1.2572 +#ifndef USE_FIONBIO
1.2573 + origState = fcntl(sock, F_GETFL);
1.2574 + curState = origState | O_NONBLOCK;
1.2575 + status = fcntl(sock, F_SETFL, curState);
1.2576 +#else /* USE_FIONBIO */
1.2577 + curState = 1;
1.2578 + status = ioctl(sock, FIONBIO, &curState);
1.2579 +#endif /* !USE_FIONBIO */
1.2580 + } else {
1.2581 + status = 0;
1.2582 + }
1.2583 + if (status > -1) {
1.2584 + status = connect(sock, (struct sockaddr *) &sockaddr,
1.2585 + sizeof(sockaddr));
1.2586 + if (status < 0) {
1.2587 + if (errno == EINPROGRESS) {
1.2588 + asyncConnect = 1;
1.2589 + status = 0;
1.2590 + }
1.2591 + } else {
1.2592 + /*
1.2593 + * Here we are if the connect succeeds. In case of an
1.2594 + * asynchronous connect we have to reset the channel to
1.2595 + * blocking mode. This appears to happen not very often,
1.2596 + * but e.g. on a HP 9000/800 under HP-UX B.11.00 we enter
1.2597 + * this stage. [Bug: 4388]
1.2598 + */
1.2599 + if (async) {
1.2600 +#ifndef USE_FIONBIO
1.2601 + origState = fcntl(sock, F_GETFL);
1.2602 + curState = origState & ~(O_NONBLOCK);
1.2603 + status = fcntl(sock, F_SETFL, curState);
1.2604 +#else /* USE_FIONBIO */
1.2605 + curState = 0;
1.2606 + status = ioctl(sock, FIONBIO, &curState);
1.2607 +#endif /* !USE_FIONBIO */
1.2608 + }
1.2609 + }
1.2610 + }
1.2611 + }
1.2612 +
1.2613 +bindError:
1.2614 + if (status < 0) {
1.2615 + if (interp != NULL) {
1.2616 + Tcl_AppendResult(interp, "couldn't open socket: ",
1.2617 + Tcl_PosixError(interp), (char *) NULL);
1.2618 + }
1.2619 + if (sock != -1) {
1.2620 + close(sock);
1.2621 + }
1.2622 + return NULL;
1.2623 + }
1.2624 +
1.2625 + /*
1.2626 + * Allocate a new TcpState for this socket.
1.2627 + */
1.2628 +
1.2629 + statePtr = (TcpState *) ckalloc((unsigned) sizeof(TcpState));
1.2630 + statePtr->flags = 0;
1.2631 + if (asyncConnect) {
1.2632 + statePtr->flags = TCP_ASYNC_CONNECT;
1.2633 + }
1.2634 + statePtr->fd = sock;
1.2635 +
1.2636 + return statePtr;
1.2637 +
1.2638 +addressError:
1.2639 + if (sock != -1) {
1.2640 + close(sock);
1.2641 + }
1.2642 + if (interp != NULL) {
1.2643 + Tcl_AppendResult(interp, "couldn't open socket: ",
1.2644 + Tcl_PosixError(interp), (char *) NULL);
1.2645 + }
1.2646 + return NULL;
1.2647 +}
1.2648 +
1.2649 +/*
1.2650 + *----------------------------------------------------------------------
1.2651 + *
1.2652 + * CreateSocketAddress --
1.2653 + *
1.2654 + * This function initializes a sockaddr structure for a host and port.
1.2655 + *
1.2656 + * Results:
1.2657 + * 1 if the host was valid, 0 if the host could not be converted to
1.2658 + * an IP address.
1.2659 + *
1.2660 + * Side effects:
1.2661 + * Fills in the *sockaddrPtr structure.
1.2662 + *
1.2663 + *----------------------------------------------------------------------
1.2664 + */
1.2665 +
1.2666 +static int
1.2667 +CreateSocketAddress(sockaddrPtr, host, port)
1.2668 + struct sockaddr_in *sockaddrPtr; /* Socket address */
1.2669 + CONST char *host; /* Host. NULL implies INADDR_ANY */
1.2670 + int port; /* Port number */
1.2671 +{
1.2672 + struct hostent *hostent; /* Host database entry */
1.2673 + struct in_addr addr; /* For 64/32 bit madness */
1.2674 +
1.2675 +#ifdef __SYMBIAN32__
1.2676 + if (host && !strcmp(host, "localhost")) {
1.2677 + char* loc = strstr(host, "localhost");
1.2678 + memcpy(loc, "127.0.0.1", 9);
1.2679 + }
1.2680 +#endif
1.2681 + (void) memset((VOID *) sockaddrPtr, '\0', sizeof(struct sockaddr_in));
1.2682 + sockaddrPtr->sin_family = AF_INET;
1.2683 + sockaddrPtr->sin_port = htons((unsigned short) (port & 0xFFFF));
1.2684 + if (host == NULL) {
1.2685 + addr.s_addr = INADDR_ANY;
1.2686 + } else {
1.2687 + Tcl_DString ds;
1.2688 + CONST char *native;
1.2689 +
1.2690 + if (host == NULL) {
1.2691 + native = NULL;
1.2692 + } else {
1.2693 + native = Tcl_UtfToExternalDString(NULL, host, -1, &ds);
1.2694 + }
1.2695 + addr.s_addr = inet_addr(native); /* INTL: Native. */
1.2696 + /*
1.2697 + * This is 0xFFFFFFFF to ensure that it compares as a 32bit -1
1.2698 + * on either 32 or 64 bits systems.
1.2699 + */
1.2700 + if (addr.s_addr == 0xFFFFFFFF) {
1.2701 + hostent = TclpGetHostByName(native); /* INTL: Native. */
1.2702 + if (hostent != (struct hostent *) NULL) {
1.2703 + memcpy((VOID *) &addr,
1.2704 + (VOID *) hostent->h_addr_list[0],
1.2705 + (size_t) hostent->h_length);
1.2706 + } else {
1.2707 +#ifdef EHOSTUNREACH
1.2708 + errno = EHOSTUNREACH;
1.2709 +#else /* !EHOSTUNREACH */
1.2710 +#ifdef ENXIO
1.2711 + errno = ENXIO;
1.2712 +#endif /* ENXIO */
1.2713 +#endif /* EHOSTUNREACH */
1.2714 + if (native != NULL) {
1.2715 + Tcl_DStringFree(&ds);
1.2716 + }
1.2717 + return 0; /* error */
1.2718 + }
1.2719 + }
1.2720 + if (native != NULL) {
1.2721 + Tcl_DStringFree(&ds);
1.2722 + }
1.2723 + }
1.2724 +
1.2725 + /*
1.2726 + * NOTE: On 64 bit machines the assignment below is rumored to not
1.2727 + * do the right thing. Please report errors related to this if you
1.2728 + * observe incorrect behavior on 64 bit machines such as DEC Alphas.
1.2729 + * Should we modify this code to do an explicit memcpy?
1.2730 + */
1.2731 +
1.2732 + sockaddrPtr->sin_addr.s_addr = addr.s_addr;
1.2733 + return 1; /* Success. */
1.2734 +}
1.2735 +
1.2736 +/*
1.2737 + *----------------------------------------------------------------------
1.2738 + *
1.2739 + * Tcl_OpenTcpClient --
1.2740 + *
1.2741 + * Opens a TCP client socket and creates a channel around it.
1.2742 + *
1.2743 + * Results:
1.2744 + * The channel or NULL if failed. An error message is returned
1.2745 + * in the interpreter on failure.
1.2746 + *
1.2747 + * Side effects:
1.2748 + * Opens a client socket and creates a new channel.
1.2749 + *
1.2750 + *----------------------------------------------------------------------
1.2751 + */
1.2752 +
1.2753 +EXPORT_C Tcl_Channel
1.2754 +Tcl_OpenTcpClient(interp, port, host, myaddr, myport, async)
1.2755 + Tcl_Interp *interp; /* For error reporting; can be NULL. */
1.2756 + int port; /* Port number to open. */
1.2757 + CONST char *host; /* Host on which to open port. */
1.2758 + CONST char *myaddr; /* Client-side address */
1.2759 + int myport; /* Client-side port */
1.2760 + int async; /* If nonzero, attempt to do an
1.2761 + * asynchronous connect. Otherwise
1.2762 + * we do a blocking connect. */
1.2763 +{
1.2764 + TcpState *statePtr;
1.2765 + char channelName[16 + TCL_INTEGER_SPACE];
1.2766 +
1.2767 + /*
1.2768 + * Create a new client socket and wrap it in a channel.
1.2769 + */
1.2770 +
1.2771 + statePtr = CreateSocket(interp, port, host, 0, myaddr, myport, async);
1.2772 + if (statePtr == NULL) {
1.2773 + return NULL;
1.2774 + }
1.2775 +
1.2776 + statePtr->acceptProc = NULL;
1.2777 + statePtr->acceptProcData = (ClientData) NULL;
1.2778 +
1.2779 + sprintf(channelName, "sock%d", statePtr->fd);
1.2780 +
1.2781 + statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
1.2782 + (ClientData) statePtr, (TCL_READABLE | TCL_WRITABLE));
1.2783 + if (Tcl_SetChannelOption(interp, statePtr->channel, "-translation",
1.2784 + "auto crlf") == TCL_ERROR) {
1.2785 + Tcl_Close((Tcl_Interp *) NULL, statePtr->channel);
1.2786 + return NULL;
1.2787 + }
1.2788 + return statePtr->channel;
1.2789 +}
1.2790 +
1.2791 +/*
1.2792 + *----------------------------------------------------------------------
1.2793 + *
1.2794 + * Tcl_MakeTcpClientChannel --
1.2795 + *
1.2796 + * Creates a Tcl_Channel from an existing client TCP socket.
1.2797 + *
1.2798 + * Results:
1.2799 + * The Tcl_Channel wrapped around the preexisting TCP socket.
1.2800 + *
1.2801 + * Side effects:
1.2802 + * None.
1.2803 + *
1.2804 + *----------------------------------------------------------------------
1.2805 + */
1.2806 +
1.2807 +EXPORT_C Tcl_Channel
1.2808 +Tcl_MakeTcpClientChannel(sock)
1.2809 + ClientData sock; /* The socket to wrap up into a channel. */
1.2810 +{
1.2811 + return MakeTcpClientChannelMode(sock, (TCL_READABLE | TCL_WRITABLE));
1.2812 +}
1.2813 +
1.2814 +/*
1.2815 + *----------------------------------------------------------------------
1.2816 + *
1.2817 + * MakeTcpClientChannelMode --
1.2818 + *
1.2819 + * Creates a Tcl_Channel from an existing client TCP socket
1.2820 + * with given mode.
1.2821 + *
1.2822 + * Results:
1.2823 + * The Tcl_Channel wrapped around the preexisting TCP socket.
1.2824 + *
1.2825 + * Side effects:
1.2826 + * None.
1.2827 + *
1.2828 + *----------------------------------------------------------------------
1.2829 + */
1.2830 +
1.2831 +static Tcl_Channel
1.2832 +MakeTcpClientChannelMode(sock, mode)
1.2833 + ClientData sock; /* The socket to wrap up into a channel. */
1.2834 + int mode; /* ORed combination of TCL_READABLE and
1.2835 + * TCL_WRITABLE to indicate file mode. */
1.2836 +{
1.2837 + TcpState *statePtr;
1.2838 + char channelName[16 + TCL_INTEGER_SPACE];
1.2839 +
1.2840 + statePtr = (TcpState *) ckalloc((unsigned) sizeof(TcpState));
1.2841 + statePtr->fd = (int) sock;
1.2842 + statePtr->flags = 0;
1.2843 + statePtr->acceptProc = NULL;
1.2844 + statePtr->acceptProcData = (ClientData) NULL;
1.2845 +
1.2846 + sprintf(channelName, "sock%d", statePtr->fd);
1.2847 +
1.2848 + statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
1.2849 + (ClientData) statePtr, mode);
1.2850 + if (Tcl_SetChannelOption((Tcl_Interp *) NULL, statePtr->channel,
1.2851 + "-translation", "auto crlf") == TCL_ERROR) {
1.2852 + Tcl_Close((Tcl_Interp *) NULL, statePtr->channel);
1.2853 + return NULL;
1.2854 + }
1.2855 + return statePtr->channel;
1.2856 +}
1.2857 +
1.2858 +/*
1.2859 + *----------------------------------------------------------------------
1.2860 + *
1.2861 + * Tcl_OpenTcpServer --
1.2862 + *
1.2863 + * Opens a TCP server socket and creates a channel around it.
1.2864 + *
1.2865 + * Results:
1.2866 + * The channel or NULL if failed. If an error occurred, an
1.2867 + * error message is left in the interp's result if interp is
1.2868 + * not NULL.
1.2869 + *
1.2870 + * Side effects:
1.2871 + * Opens a server socket and creates a new channel.
1.2872 + *
1.2873 + *----------------------------------------------------------------------
1.2874 + */
1.2875 +
1.2876 +EXPORT_C Tcl_Channel
1.2877 +Tcl_OpenTcpServer(interp, port, myHost, acceptProc, acceptProcData)
1.2878 + Tcl_Interp *interp; /* For error reporting - may be
1.2879 + * NULL. */
1.2880 + int port; /* Port number to open. */
1.2881 + CONST char *myHost; /* Name of local host. */
1.2882 + Tcl_TcpAcceptProc *acceptProc; /* Callback for accepting connections
1.2883 + * from new clients. */
1.2884 + ClientData acceptProcData; /* Data for the callback. */
1.2885 +{
1.2886 + TcpState *statePtr;
1.2887 + char channelName[16 + TCL_INTEGER_SPACE];
1.2888 +
1.2889 + /*
1.2890 + * Create a new client socket and wrap it in a channel.
1.2891 + */
1.2892 +
1.2893 + statePtr = CreateSocket(interp, port, myHost, 1, NULL, 0, 0);
1.2894 + if (statePtr == NULL) {
1.2895 + return NULL;
1.2896 + }
1.2897 +
1.2898 + statePtr->acceptProc = acceptProc;
1.2899 + statePtr->acceptProcData = acceptProcData;
1.2900 +
1.2901 + /*
1.2902 + * Set up the callback mechanism for accepting connections
1.2903 + * from new clients.
1.2904 + */
1.2905 +
1.2906 + Tcl_CreateFileHandler(statePtr->fd, TCL_READABLE, TcpAccept,
1.2907 + (ClientData) statePtr);
1.2908 + sprintf(channelName, "sock%d", statePtr->fd);
1.2909 + statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
1.2910 + (ClientData) statePtr, 0);
1.2911 + return statePtr->channel;
1.2912 +}
1.2913 +
1.2914 +/*
1.2915 + *----------------------------------------------------------------------
1.2916 + *
1.2917 + * TcpAccept --
1.2918 + * Accept a TCP socket connection. This is called by the event loop.
1.2919 + *
1.2920 + * Results:
1.2921 + * None.
1.2922 + *
1.2923 + * Side effects:
1.2924 + * Creates a new connection socket. Calls the registered callback
1.2925 + * for the connection acceptance mechanism.
1.2926 + *
1.2927 + *----------------------------------------------------------------------
1.2928 + */
1.2929 +
1.2930 + /* ARGSUSED */
1.2931 +static void
1.2932 +TcpAccept(data, mask)
1.2933 + ClientData data; /* Callback token. */
1.2934 + int mask; /* Not used. */
1.2935 +{
1.2936 + TcpState *sockState; /* Client data of server socket. */
1.2937 + int newsock; /* The new client socket */
1.2938 + TcpState *newSockState; /* State for new socket. */
1.2939 + struct sockaddr_in addr; /* The remote address */
1.2940 + socklen_t len; /* For accept interface */
1.2941 + char channelName[16 + TCL_INTEGER_SPACE];
1.2942 +
1.2943 + sockState = (TcpState *) data;
1.2944 +
1.2945 + len = sizeof(struct sockaddr_in);
1.2946 + newsock = accept(sockState->fd, (struct sockaddr *) &addr, &len);
1.2947 + if (newsock < 0) {
1.2948 + return;
1.2949 + }
1.2950 +
1.2951 + /*
1.2952 + * Set close-on-exec flag to prevent the newly accepted socket from
1.2953 + * being inherited by child processes.
1.2954 + */
1.2955 +
1.2956 + (void) fcntl(newsock, F_SETFD, FD_CLOEXEC);
1.2957 +
1.2958 + newSockState = (TcpState *) ckalloc((unsigned) sizeof(TcpState));
1.2959 +
1.2960 + newSockState->flags = 0;
1.2961 + newSockState->fd = newsock;
1.2962 + newSockState->acceptProc = NULL;
1.2963 + newSockState->acceptProcData = NULL;
1.2964 +
1.2965 + sprintf(channelName, "sock%d", newsock);
1.2966 + newSockState->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
1.2967 + (ClientData) newSockState, (TCL_READABLE | TCL_WRITABLE));
1.2968 +
1.2969 + Tcl_SetChannelOption(NULL, newSockState->channel, "-translation",
1.2970 + "auto crlf");
1.2971 +
1.2972 + if (sockState->acceptProc != NULL) {
1.2973 + (*sockState->acceptProc)(sockState->acceptProcData,
1.2974 + newSockState->channel, inet_ntoa(addr.sin_addr),
1.2975 + ntohs(addr.sin_port));
1.2976 + }
1.2977 +}
1.2978 +
1.2979 +/*
1.2980 + *----------------------------------------------------------------------
1.2981 + *
1.2982 + * TclpGetDefaultStdChannel --
1.2983 + *
1.2984 + * Creates channels for standard input, standard output or standard
1.2985 + * error output if they do not already exist.
1.2986 + *
1.2987 + * Results:
1.2988 + * Returns the specified default standard channel, or NULL.
1.2989 + *
1.2990 + * Side effects:
1.2991 + * May cause the creation of a standard channel and the underlying
1.2992 + * file.
1.2993 + *
1.2994 + *----------------------------------------------------------------------
1.2995 + */
1.2996 +
1.2997 +Tcl_Channel
1.2998 +TclpGetDefaultStdChannel(type)
1.2999 + int type; /* One of TCL_STDIN, TCL_STDOUT, TCL_STDERR. */
1.3000 +{
1.3001 + Tcl_Channel channel = NULL;
1.3002 + int fd = 0; /* Initializations needed to prevent */
1.3003 + int mode = 0; /* compiler warning (used before set). */
1.3004 + char *bufMode = NULL;
1.3005 +
1.3006 + /*
1.3007 + * Some #def's to make the code a little clearer!
1.3008 + */
1.3009 +#define ZERO_OFFSET ((Tcl_SeekOffset) 0)
1.3010 +#define ERROR_OFFSET ((Tcl_SeekOffset) -1)
1.3011 +
1.3012 + switch (type) {
1.3013 + case TCL_STDIN:
1.3014 + if ((TclOSseek(0, ZERO_OFFSET, SEEK_CUR) == ERROR_OFFSET)
1.3015 + && (errno == EBADF)) {
1.3016 + return (Tcl_Channel) NULL;
1.3017 + }
1.3018 + fd = 0;
1.3019 + mode = TCL_READABLE;
1.3020 + bufMode = "line";
1.3021 + break;
1.3022 + case TCL_STDOUT:
1.3023 + if ((TclOSseek(1, ZERO_OFFSET, SEEK_CUR) == ERROR_OFFSET)
1.3024 + && (errno == EBADF)) {
1.3025 + return (Tcl_Channel) NULL;
1.3026 + }
1.3027 + fd = 1;
1.3028 + mode = TCL_WRITABLE;
1.3029 + bufMode = "line";
1.3030 + break;
1.3031 + case TCL_STDERR:
1.3032 + if ((TclOSseek(2, ZERO_OFFSET, SEEK_CUR) == ERROR_OFFSET)
1.3033 + && (errno == EBADF)) {
1.3034 + return (Tcl_Channel) NULL;
1.3035 + }
1.3036 + fd = 2;
1.3037 + mode = TCL_WRITABLE;
1.3038 + bufMode = "none";
1.3039 + break;
1.3040 + default:
1.3041 + panic("TclGetDefaultStdChannel: Unexpected channel type");
1.3042 + break;
1.3043 + }
1.3044 +
1.3045 +#undef ZERO_OFFSET
1.3046 +#undef ERROR_OFFSET
1.3047 +
1.3048 + channel = Tcl_MakeFileChannel((ClientData) fd, mode);
1.3049 + if (channel == NULL) {
1.3050 + return NULL;
1.3051 + }
1.3052 +
1.3053 + /*
1.3054 + * Set up the normal channel options for stdio handles.
1.3055 + */
1.3056 +
1.3057 + if (Tcl_GetChannelType(channel) == &fileChannelType) {
1.3058 + Tcl_SetChannelOption(NULL, channel, "-translation", "auto");
1.3059 + } else {
1.3060 + Tcl_SetChannelOption(NULL, channel, "-translation", "auto crlf");
1.3061 + }
1.3062 + Tcl_SetChannelOption(NULL, channel, "-buffering", bufMode);
1.3063 + return channel;
1.3064 +}
1.3065 +
1.3066 +/*
1.3067 + *----------------------------------------------------------------------
1.3068 + *
1.3069 + * Tcl_GetOpenFile --
1.3070 + *
1.3071 + * Given a name of a channel registered in the given interpreter,
1.3072 + * returns a FILE * for it.
1.3073 + *
1.3074 + * Results:
1.3075 + * A standard Tcl result. If the channel is registered in the given
1.3076 + * interpreter and it is managed by the "file" channel driver, and
1.3077 + * it is open for the requested mode, then the output parameter
1.3078 + * filePtr is set to a FILE * for the underlying file. On error, the
1.3079 + * filePtr is not set, TCL_ERROR is returned and an error message is
1.3080 + * left in the interp's result.
1.3081 + *
1.3082 + * Side effects:
1.3083 + * May invoke fdopen to create the FILE * for the requested file.
1.3084 + *
1.3085 + *----------------------------------------------------------------------
1.3086 + */
1.3087 +
1.3088 +EXPORT_C int
1.3089 +Tcl_GetOpenFile(interp, string, forWriting, checkUsage, filePtr)
1.3090 + Tcl_Interp *interp; /* Interpreter in which to find file. */
1.3091 + CONST char *string; /* String that identifies file. */
1.3092 + int forWriting; /* 1 means the file is going to be used
1.3093 + * for writing, 0 means for reading. */
1.3094 + int checkUsage; /* 1 means verify that the file was opened
1.3095 + * in a mode that allows the access specified
1.3096 + * by "forWriting". Ignored, we always
1.3097 + * check that the channel is open for the
1.3098 + * requested mode. */
1.3099 + ClientData *filePtr; /* Store pointer to FILE structure here. */
1.3100 +{
1.3101 + Tcl_Channel chan;
1.3102 + int chanMode;
1.3103 + Tcl_ChannelType *chanTypePtr;
1.3104 + ClientData data;
1.3105 + int fd;
1.3106 + FILE *f;
1.3107 +
1.3108 + chan = Tcl_GetChannel(interp, string, &chanMode);
1.3109 + if (chan == (Tcl_Channel) NULL) {
1.3110 + return TCL_ERROR;
1.3111 + }
1.3112 + if ((forWriting) && ((chanMode & TCL_WRITABLE) == 0)) {
1.3113 + Tcl_AppendResult(interp,
1.3114 + "\"", string, "\" wasn't opened for writing", (char *) NULL);
1.3115 + return TCL_ERROR;
1.3116 + } else if ((!(forWriting)) && ((chanMode & TCL_READABLE) == 0)) {
1.3117 + Tcl_AppendResult(interp,
1.3118 + "\"", string, "\" wasn't opened for reading", (char *) NULL);
1.3119 + return TCL_ERROR;
1.3120 + }
1.3121 +
1.3122 + /*
1.3123 + * We allow creating a FILE * out of file based, pipe based and socket
1.3124 + * based channels. We currently do not allow any other channel types,
1.3125 + * because it is likely that stdio will not know what to do with them.
1.3126 + */
1.3127 +
1.3128 + chanTypePtr = Tcl_GetChannelType(chan);
1.3129 + if ((chanTypePtr == &fileChannelType)
1.3130 +#ifdef SUPPORTS_TTY
1.3131 + || (chanTypePtr == &ttyChannelType)
1.3132 +#endif /* SUPPORTS_TTY */
1.3133 + || (chanTypePtr == &tcpChannelType)
1.3134 + || (strcmp(chanTypePtr->typeName, "pipe") == 0)) {
1.3135 + if (Tcl_GetChannelHandle(chan,
1.3136 + (forWriting ? TCL_WRITABLE : TCL_READABLE),
1.3137 + (ClientData*) &data) == TCL_OK) {
1.3138 + fd = (int) data;
1.3139 +
1.3140 + /*
1.3141 + * The call to fdopen below is probably dangerous, since it will
1.3142 + * truncate an existing file if the file is being opened
1.3143 + * for writing....
1.3144 + */
1.3145 +
1.3146 + f = fdopen(fd, (forWriting ? "w" : "r"));
1.3147 + if (f == NULL) {
1.3148 + Tcl_AppendResult(interp, "cannot get a FILE * for \"", string,
1.3149 + "\"", (char *) NULL);
1.3150 + return TCL_ERROR;
1.3151 + }
1.3152 + *filePtr = (ClientData) f;
1.3153 + return TCL_OK;
1.3154 + }
1.3155 + }
1.3156 +
1.3157 + Tcl_AppendResult(interp, "\"", string,
1.3158 + "\" cannot be used to get a FILE *", (char *) NULL);
1.3159 + return TCL_ERROR;
1.3160 +}
1.3161 +
1.3162 +/*
1.3163 + *----------------------------------------------------------------------
1.3164 + *
1.3165 + * TclUnixWaitForFile --
1.3166 + *
1.3167 + * This procedure waits synchronously for a file to become readable
1.3168 + * or writable, with an optional timeout.
1.3169 + *
1.3170 + * Results:
1.3171 + * The return value is an OR'ed combination of TCL_READABLE,
1.3172 + * TCL_WRITABLE, and TCL_EXCEPTION, indicating the conditions
1.3173 + * that are present on file at the time of the return. This
1.3174 + * procedure will not return until either "timeout" milliseconds
1.3175 + * have elapsed or at least one of the conditions given by mask
1.3176 + * has occurred for file (a return value of 0 means that a timeout
1.3177 + * occurred). No normal events will be serviced during the
1.3178 + * execution of this procedure.
1.3179 + *
1.3180 + * Side effects:
1.3181 + * Time passes.
1.3182 + *
1.3183 + *----------------------------------------------------------------------
1.3184 + */
1.3185 +
1.3186 +int
1.3187 +TclUnixWaitForFile(fd, mask, timeout)
1.3188 + int fd; /* Handle for file on which to wait. */
1.3189 + int mask; /* What to wait for: OR'ed combination of
1.3190 + * TCL_READABLE, TCL_WRITABLE, and
1.3191 + * TCL_EXCEPTION. */
1.3192 + int timeout; /* Maximum amount of time to wait for one
1.3193 + * of the conditions in mask to occur, in
1.3194 + * milliseconds. A value of 0 means don't
1.3195 + * wait at all, and a value of -1 means
1.3196 + * wait forever. */
1.3197 +{
1.3198 + Tcl_Time abortTime = {0, 0}, now; /* silence gcc 4 warning */
1.3199 + struct timeval blockTime, *timeoutPtr;
1.3200 + int index, numFound, result = 0;
1.3201 + fd_mask bit;
1.3202 + fd_mask readyMasks[3*MASK_SIZE];
1.3203 + /* This array reflects the readable/writable
1.3204 + * conditions that were found to exist by the
1.3205 + * last call to select. */
1.3206 +
1.3207 + /*
1.3208 + * If there is a non-zero finite timeout, compute the time when
1.3209 + * we give up.
1.3210 + */
1.3211 +
1.3212 + if (timeout > 0) {
1.3213 + Tcl_GetTime(&now);
1.3214 + abortTime.sec = now.sec + timeout/1000;
1.3215 + abortTime.usec = now.usec + (timeout%1000)*1000;
1.3216 + if (abortTime.usec >= 1000000) {
1.3217 + abortTime.usec -= 1000000;
1.3218 + abortTime.sec += 1;
1.3219 + }
1.3220 + timeoutPtr = &blockTime;
1.3221 + } else if (timeout == 0) {
1.3222 + timeoutPtr = &blockTime;
1.3223 + blockTime.tv_sec = 0;
1.3224 + blockTime.tv_usec = 0;
1.3225 + } else {
1.3226 + timeoutPtr = NULL;
1.3227 + }
1.3228 +
1.3229 + /*
1.3230 + * Initialize the ready masks and compute the mask offsets.
1.3231 + */
1.3232 +
1.3233 + if (fd >= FD_SETSIZE) {
1.3234 + panic("TclWaitForFile can't handle file id %d", fd);
1.3235 + }
1.3236 + memset((VOID *) readyMasks, 0, 3*MASK_SIZE*sizeof(fd_mask));
1.3237 + index = fd/(NBBY*sizeof(fd_mask));
1.3238 + bit = ((fd_mask) 1) << (fd%(NBBY*sizeof(fd_mask)));
1.3239 +
1.3240 + /*
1.3241 + * Loop in a mini-event loop of our own, waiting for either the
1.3242 + * file to become ready or a timeout to occur.
1.3243 + */
1.3244 +
1.3245 + while (1) {
1.3246 + if (timeout > 0) {
1.3247 + blockTime.tv_sec = abortTime.sec - now.sec;
1.3248 + blockTime.tv_usec = abortTime.usec - now.usec;
1.3249 + if (blockTime.tv_usec < 0) {
1.3250 + blockTime.tv_sec -= 1;
1.3251 + blockTime.tv_usec += 1000000;
1.3252 + }
1.3253 + if (blockTime.tv_sec < 0) {
1.3254 + blockTime.tv_sec = 0;
1.3255 + blockTime.tv_usec = 0;
1.3256 + }
1.3257 + }
1.3258 +
1.3259 + /*
1.3260 + * Set the appropriate bit in the ready masks for the fd.
1.3261 + */
1.3262 +
1.3263 + if (mask & TCL_READABLE) {
1.3264 + readyMasks[index] |= bit;
1.3265 + }
1.3266 + if (mask & TCL_WRITABLE) {
1.3267 + (readyMasks+MASK_SIZE)[index] |= bit;
1.3268 + }
1.3269 + if (mask & TCL_EXCEPTION) {
1.3270 + (readyMasks+2*(MASK_SIZE))[index] |= bit;
1.3271 + }
1.3272 +
1.3273 + /*
1.3274 + * Wait for the event or a timeout.
1.3275 + */
1.3276 +
1.3277 + numFound = select(fd+1, (SELECT_MASK *) &readyMasks[0],
1.3278 + (SELECT_MASK *) &readyMasks[MASK_SIZE],
1.3279 + (SELECT_MASK *) &readyMasks[2*MASK_SIZE], timeoutPtr);
1.3280 + if (numFound == 1) {
1.3281 + if (readyMasks[index] & bit) {
1.3282 + result |= TCL_READABLE;
1.3283 + }
1.3284 + if ((readyMasks+MASK_SIZE)[index] & bit) {
1.3285 + result |= TCL_WRITABLE;
1.3286 + }
1.3287 + if ((readyMasks+2*(MASK_SIZE))[index] & bit) {
1.3288 + result |= TCL_EXCEPTION;
1.3289 + }
1.3290 + result &= mask;
1.3291 + if (result) {
1.3292 + break;
1.3293 + }
1.3294 + }
1.3295 + if (timeout == 0) {
1.3296 + break;
1.3297 + }
1.3298 + if (timeout < 0) {
1.3299 + continue;
1.3300 + }
1.3301 +
1.3302 + /*
1.3303 + * The select returned early, so we need to recompute the timeout.
1.3304 + */
1.3305 +
1.3306 + Tcl_GetTime(&now);
1.3307 + if ((abortTime.sec < now.sec)
1.3308 + || ((abortTime.sec == now.sec)
1.3309 + && (abortTime.usec <= now.usec))) {
1.3310 + break;
1.3311 + }
1.3312 + }
1.3313 + return result;
1.3314 +}
1.3315 +
1.3316 +#ifdef DEPRECATED
1.3317 +/*
1.3318 + *----------------------------------------------------------------------
1.3319 + *
1.3320 + * FileThreadActionProc --
1.3321 + *
1.3322 + * Insert or remove any thread local refs to this channel.
1.3323 + *
1.3324 + * Results:
1.3325 + * None.
1.3326 + *
1.3327 + * Side effects:
1.3328 + * Changes thread local list of valid channels.
1.3329 + *
1.3330 + *----------------------------------------------------------------------
1.3331 + */
1.3332 +
1.3333 +static void
1.3334 +FileThreadActionProc (instanceData, action)
1.3335 + ClientData instanceData;
1.3336 + int action;
1.3337 +{
1.3338 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.3339 + FileState *fsPtr = (FileState *) instanceData;
1.3340 +
1.3341 + if (action == TCL_CHANNEL_THREAD_INSERT) {
1.3342 + fsPtr->nextPtr = tsdPtr->firstFilePtr;
1.3343 + tsdPtr->firstFilePtr = fsPtr;
1.3344 + } else {
1.3345 + FileState **nextPtrPtr;
1.3346 + int removed = 0;
1.3347 +
1.3348 + for (nextPtrPtr = &(tsdPtr->firstFilePtr); (*nextPtrPtr) != NULL;
1.3349 + nextPtrPtr = &((*nextPtrPtr)->nextPtr)) {
1.3350 + if ((*nextPtrPtr) == fsPtr) {
1.3351 + (*nextPtrPtr) = fsPtr->nextPtr;
1.3352 + removed = 1;
1.3353 + break;
1.3354 + }
1.3355 + }
1.3356 +
1.3357 + /*
1.3358 + * This could happen if the channel was created in one
1.3359 + * thread and then moved to another without updating
1.3360 + * the thread local data in each thread.
1.3361 + */
1.3362 +
1.3363 + if (!removed) {
1.3364 + panic("file info ptr not on thread channel list");
1.3365 + }
1.3366 + }
1.3367 +}
1.3368 +#endif /* DEPRECATED */
1.3369 +