1 /* Jim - A small embeddable Tcl interpreter
3 * Copyright 2005 Salvatore Sanfilippo <antirez@invece.org>
4 * Copyright 2005 Clemens Hintze <c.hintze@gmx.net>
5 * Copyright 2005 patthoyts - Pat Thoyts <patthoyts@users.sf.net>
6 * Copyright 2008 oharboe - Øyvind Harboe - oyvind.harboe@zylin.com
7 * Copyright 2008 Andrew Lunn <andrew@lunn.ch>
8 * Copyright 2008 Duane Ellis <openocd@duaneellis.com>
9 * Copyright 2008 Uwe Klein <uklein@klein-messgeraete.de>
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above
18 * copyright notice, this list of conditions and the following
19 * disclaimer in the documentation and/or other materials
20 * provided with the distribution.
22 * THIS SOFTWARE IS PROVIDED BY THE JIM TCL PROJECT ``AS IS'' AND ANY
23 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
24 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
25 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26 * JIM TCL PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
27 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
33 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 * The views and conclusions contained in the software and documentation
36 * are those of the authors and should not be interpreted as representing
37 * official policies, either expressed or implied, of the Jim Tcl Project.
40 #include "jimautoconf.h"
54 #if defined(HAVE_SYS_SOCKET_H) && defined(HAVE_SELECT) && defined(HAVE_NETINET_IN_H) && defined(HAVE_NETDB_H) && defined(HAVE_ARPA_INET_H)
55 #include <sys/socket.h>
56 #include <netinet/in.h>
57 #include <arpa/inet.h>
67 #include <openssl/ssl.h>
68 #include <openssl/err.h>
75 #include "jim-eventloop.h"
76 #include "jim-subcmd.h"
78 #define AIO_CMD_LEN 32 /* e.g. aio.handleXXXXXX */
79 #define AIO_BUF_LEN 256 /* Can keep this small and rely on stdio buffering */
88 #define AIO_KEEPOPEN 1
99 #define JimCheckStreamError(interp, af) af->fops->error(af)
101 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
104 struct sockaddr_in sin
;
106 struct sockaddr_in6 sin6
;
110 #ifndef HAVE_INET_NTOP
111 const char *inet_ntop(int af
, const void *src
, char *dst
, int size
)
116 snprintf(dst
, size
, "%s", inet_ntoa(((struct sockaddr_in
*)src
)->sin_addr
));
120 #endif /* JIM_BOOTSTRAP */
125 int (*writer
)(struct AioFile
*af
, const char *buf
, int len
);
126 int (*reader
)(struct AioFile
*af
, char *buf
, int len
);
127 const char *(*getline
)(struct AioFile
*af
, char *buf
, int len
);
128 int (*error
)(const struct AioFile
*af
);
129 const char *(*strerror
)(struct AioFile
*af
);
130 int (*verify
)(struct AioFile
*af
);
133 typedef struct AioFile
138 int openFlags
; /* AIO_KEEPOPEN? keep FILE* */
145 const JimAioFopsType
*fops
;
148 static int stdio_writer(struct AioFile
*af
, const char *buf
, int len
)
150 return fwrite(buf
, 1, len
, af
->fp
);
153 static int stdio_reader(struct AioFile
*af
, char *buf
, int len
)
155 return fread(buf
, 1, len
, af
->fp
);
158 static const char *stdio_getline(struct AioFile
*af
, char *buf
, int len
)
160 return fgets(buf
, len
, af
->fp
);
163 static int stdio_error(const AioFile
*af
)
165 if (!ferror(af
->fp
)) {
169 /* EAGAIN and similar are not error conditions. Just treat them like eof */
170 if (feof(af
->fp
) || errno
== EAGAIN
|| errno
== EINTR
) {
174 if (errno
== ECONNRESET
) {
179 if (errno
!= ECONNABORTED
) {
186 static const char *stdio_strerror(struct AioFile
*af
)
188 return strerror(errno
);
191 static const JimAioFopsType stdio_fops
= {
200 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
202 static SSL_CTX
*JimAioSslCtx(Jim_Interp
*interp
);
204 static int ssl_writer(struct AioFile
*af
, const char *buf
, int len
)
206 return SSL_write(af
->ssl
, buf
, len
);
209 static int ssl_reader(struct AioFile
*af
, char *buf
, int len
)
211 return SSL_read(af
->ssl
, buf
, len
);
214 static const char *ssl_getline(struct AioFile
*af
, char *buf
, int len
)
217 for (i
= 0; i
< len
+ 1; i
++) {
218 if (SSL_read(af
->ssl
, &buf
[i
], 1) != 1) {
224 if (buf
[i
] == '\n') {
232 static int ssl_error(const struct AioFile
*af
)
234 if (ERR_peek_error() == 0) {
241 static const char *ssl_strerror(struct AioFile
*af
)
243 int err
= ERR_get_error();
246 return ERR_error_string(err
, NULL
);
249 /* should not happen */
250 return "unknown SSL error";
253 static int ssl_verify(struct AioFile
*af
)
257 cert
= SSL_get_peer_certificate(af
->ssl
);
263 if (SSL_get_verify_result(af
->ssl
) == X509_V_OK
) {
270 static const JimAioFopsType ssl_fops
= {
278 #endif /* JIM_BOOTSTRAP */
280 static int JimAioSubCmdProc(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
);
281 static AioFile
*JimMakeChannel(Jim_Interp
*interp
, FILE *fh
, int fd
, Jim_Obj
*filename
,
282 const char *hdlfmt
, int family
, const char *mode
);
284 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
285 static int JimParseIPv6Address(Jim_Interp
*interp
, const char *hostport
, union sockaddr_any
*sa
, int *salen
)
289 * An IPv6 addr/port looks like:
292 * [fe80::223:6cff:fe95:bdc0%en1]:2000
296 * Note that the "any" address is ::, which is the same as when no address is specified.
304 stport
= strrchr(hostport
, ':');
306 /* No : so, the whole thing is the port */
309 sthost
= Jim_StrDup(hostport
);
315 if (*hostport
== '[') {
316 /* This is a numeric ipv6 address */
317 char *pt
= strchr(++hostport
, ']');
319 sthost
= Jim_StrDupLen(hostport
, pt
- hostport
);
324 sthost
= Jim_StrDupLen(hostport
, stport
- hostport
- 1);
327 memset(&req
, '\0', sizeof(req
));
328 req
.ai_family
= PF_INET6
;
330 if (getaddrinfo(sthost
, NULL
, &req
, &ai
)) {
331 Jim_SetResultFormatted(interp
, "Not a valid address: %s", hostport
);
335 memcpy(&sa
->sin
, ai
->ai_addr
, ai
->ai_addrlen
);
336 *salen
= ai
->ai_addrlen
;
338 sa
->sin
.sin_port
= htons(atoi(stport
));
346 Jim_SetResultString(interp
, "ipv6 not supported", -1);
351 static int JimParseIpAddress(Jim_Interp
*interp
, const char *hostport
, union sockaddr_any
*sa
, int *salen
)
353 /* An IPv4 addr/port looks like:
358 * If the address is missing, INADDR_ANY is used.
359 * If the port is missing, 0 is used (only useful for server sockets).
365 stport
= strrchr(hostport
, ':');
367 /* No : so, the whole thing is the port */
369 sthost
= Jim_StrDup("0.0.0.0");
372 sthost
= Jim_StrDupLen(hostport
, stport
- hostport
);
377 #ifdef HAVE_GETADDRINFO
380 memset(&req
, '\0', sizeof(req
));
381 req
.ai_family
= PF_INET
;
383 if (getaddrinfo(sthost
, NULL
, &req
, &ai
)) {
387 memcpy(&sa
->sin
, ai
->ai_addr
, ai
->ai_addrlen
);
388 *salen
= ai
->ai_addrlen
;
396 if ((he
= gethostbyname(sthost
)) != NULL
) {
397 if (he
->h_length
== sizeof(sa
->sin
.sin_addr
)) {
398 *salen
= sizeof(sa
->sin
);
399 sa
->sin
.sin_family
= he
->h_addrtype
;
400 memcpy(&sa
->sin
.sin_addr
, he
->h_addr
, he
->h_length
); /* set address */
406 sa
->sin
.sin_port
= htons(atoi(stport
));
411 Jim_SetResultFormatted(interp
, "Not a valid address: %s", hostport
);
418 static int JimParseDomainAddress(Jim_Interp
*interp
, const char *path
, struct sockaddr_un
*sa
)
420 sa
->sun_family
= PF_UNIX
;
421 snprintf(sa
->sun_path
, sizeof(sa
->sun_path
), "%s", path
);
428 * Format that address in 'sa' as a string and store in variable 'varObjPtr'
430 static int JimFormatIpAddress(Jim_Interp
*interp
, Jim_Obj
*varObjPtr
, const union sockaddr_any
*sa
)
432 /* INET6_ADDRSTRLEN is 46. Add some for [] and port */
436 if (sa
->sa
.sa_family
== PF_INET6
) {
438 /* Allow 9 for []:65535\0 */
439 inet_ntop(sa
->sa
.sa_family
, &sa
->sin6
.sin6_addr
, addrbuf
+ 1, sizeof(addrbuf
) - 9);
440 snprintf(addrbuf
+ strlen(addrbuf
), 8, "]:%d", ntohs(sa
->sin
.sin_port
));
444 if (sa
->sa
.sa_family
== PF_INET
) {
445 /* Allow 7 for :65535\0 */
446 inet_ntop(sa
->sa
.sa_family
, &sa
->sin
.sin_addr
, addrbuf
, sizeof(addrbuf
) - 7);
447 snprintf(addrbuf
+ strlen(addrbuf
), 7, ":%d", ntohs(sa
->sin
.sin_port
));
450 /* recvfrom still works on unix domain sockets, etc */
454 return Jim_SetVariable(interp
, varObjPtr
, Jim_NewStringObj(interp
, addrbuf
, -1));
457 #endif /* JIM_BOOTSTRAP */
459 static const char *JimAioErrorString(AioFile
*af
)
462 return af
->fops
->strerror(af
);
464 return strerror(errno
);
467 static void JimAioSetError(Jim_Interp
*interp
, Jim_Obj
*name
)
469 AioFile
*af
= Jim_CmdPrivData(interp
);
472 Jim_SetResultFormatted(interp
, "%#s: %s", name
, JimAioErrorString(af
));
475 Jim_SetResultString(interp
, JimAioErrorString(af
), -1);
479 static void JimAioDelProc(Jim_Interp
*interp
, void *privData
)
481 AioFile
*af
= privData
;
485 Jim_DecrRefCount(interp
, af
->filename
);
487 #ifdef jim_ext_eventloop
488 /* remove all existing EventHandlers */
489 Jim_DeleteFileHandler(interp
, af
->fd
, JIM_EVENT_READABLE
| JIM_EVENT_WRITABLE
| JIM_EVENT_EXCEPTION
);
493 if (af
->ssl
!= NULL
) {
497 if (!(af
->openFlags
& AIO_KEEPOPEN
)) {
504 static int aio_cmd_read(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
506 AioFile
*af
= Jim_CmdPrivData(interp
);
507 char buf
[AIO_BUF_LEN
];
510 jim_wide neededLen
= -1; /* -1 is "read as much as possible" */
512 if (argc
&& Jim_CompareStringImmediate(interp
, argv
[0], "-nonewline")) {
518 if (Jim_GetWide(interp
, argv
[0], &neededLen
) != JIM_OK
)
521 Jim_SetResultString(interp
, "invalid parameter: negative len", -1);
528 objPtr
= Jim_NewStringObj(interp
, NULL
, 0);
529 while (neededLen
!= 0) {
533 if (neededLen
== -1) {
534 readlen
= AIO_BUF_LEN
;
537 readlen
= (neededLen
> AIO_BUF_LEN
? AIO_BUF_LEN
: neededLen
);
539 retval
= af
->fops
->reader(af
, buf
, readlen
);
541 Jim_AppendString(interp
, objPtr
, buf
, retval
);
542 if (neededLen
!= -1) {
546 if (retval
!= readlen
)
549 /* Check for error conditions */
550 if (JimCheckStreamError(interp
, af
)) {
551 Jim_FreeNewObj(interp
, objPtr
);
556 const char *s
= Jim_GetString(objPtr
, &len
);
558 if (len
> 0 && s
[len
- 1] == '\n') {
560 objPtr
->bytes
[objPtr
->length
] = '\0';
563 Jim_SetResult(interp
, objPtr
);
567 AioFile
*Jim_AioFile(Jim_Interp
*interp
, Jim_Obj
*command
)
569 Jim_Cmd
*cmdPtr
= Jim_GetCommand(interp
, command
, JIM_ERRMSG
);
571 /* XXX: There ought to be a supported API for this */
572 if (cmdPtr
&& !cmdPtr
->isproc
&& cmdPtr
->u
.native
.cmdProc
== JimAioSubCmdProc
) {
573 return (AioFile
*) cmdPtr
->u
.native
.privData
;
575 Jim_SetResultFormatted(interp
, "Not a filehandle: \"%#s\"", command
);
579 FILE *Jim_AioFilehandle(Jim_Interp
*interp
, Jim_Obj
*command
)
583 af
= Jim_AioFile(interp
, command
);
591 static int aio_cmd_copy(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
593 AioFile
*af
= Jim_CmdPrivData(interp
);
595 jim_wide maxlen
= JIM_WIDE_MAX
;
596 AioFile
*outf
= Jim_AioFile(interp
, argv
[0]);
603 if (Jim_GetWide(interp
, argv
[1], &maxlen
) != JIM_OK
) {
608 while (count
< maxlen
) {
611 if (af
->fops
->reader(af
, &ch
, 1) != 1) {
614 if (outf
->fops
->writer(outf
, &ch
, 1) != 1) {
620 if (JimCheckStreamError(interp
, af
) || JimCheckStreamError(interp
, outf
)) {
624 Jim_SetResultInt(interp
, count
);
629 static int aio_cmd_gets(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
631 AioFile
*af
= Jim_CmdPrivData(interp
);
632 char buf
[AIO_BUF_LEN
];
638 objPtr
= Jim_NewStringObj(interp
, NULL
, 0);
640 buf
[AIO_BUF_LEN
- 1] = '_';
642 if (af
->fops
->getline(af
, buf
, AIO_BUF_LEN
) == NULL
)
645 if (buf
[AIO_BUF_LEN
- 1] == '\0' && buf
[AIO_BUF_LEN
- 2] != '\n') {
646 Jim_AppendString(interp
, objPtr
, buf
, AIO_BUF_LEN
- 1);
651 if (len
&& (buf
[len
- 1] == '\n')) {
656 Jim_AppendString(interp
, objPtr
, buf
, len
);
661 if (JimCheckStreamError(interp
, af
)) {
663 Jim_FreeNewObj(interp
, objPtr
);
668 if (Jim_SetVariable(interp
, argv
[0], objPtr
) != JIM_OK
) {
669 Jim_FreeNewObj(interp
, objPtr
);
673 len
= Jim_Length(objPtr
);
675 if (len
== 0 && feof(af
->fp
)) {
676 /* On EOF returns -1 if varName was specified */
679 Jim_SetResultInt(interp
, len
);
682 Jim_SetResult(interp
, objPtr
);
687 static int aio_cmd_puts(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
689 AioFile
*af
= Jim_CmdPrivData(interp
);
695 if (!Jim_CompareStringImmediate(interp
, argv
[0], "-nonewline")) {
704 wdata
= Jim_GetString(strObj
, &wlen
);
705 if (af
->fops
->writer(af
, wdata
, wlen
) == wlen
) {
706 if (argc
== 2 || af
->fops
->writer(af
, "\n", 1) == 1) {
710 JimAioSetError(interp
, af
->filename
);
714 static int aio_cmd_isatty(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
717 AioFile
*af
= Jim_CmdPrivData(interp
);
718 Jim_SetResultInt(interp
, isatty(fileno(af
->fp
)));
720 Jim_SetResultInt(interp
, 0);
726 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
727 static int aio_cmd_recvfrom(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
729 AioFile
*af
= Jim_CmdPrivData(interp
);
731 union sockaddr_any sa
;
733 socklen_t salen
= sizeof(sa
);
736 if (Jim_GetLong(interp
, argv
[0], &len
) != JIM_OK
) {
740 buf
= Jim_Alloc(len
+ 1);
742 rlen
= recvfrom(fileno(af
->fp
), buf
, len
, 0, &sa
.sa
, &salen
);
745 JimAioSetError(interp
, NULL
);
749 Jim_SetResult(interp
, Jim_NewStringObjNoAlloc(interp
, buf
, rlen
));
752 return JimFormatIpAddress(interp
, argv
[1], &sa
);
759 static int aio_cmd_sendto(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
761 AioFile
*af
= Jim_CmdPrivData(interp
);
765 union sockaddr_any sa
;
766 const char *addr
= Jim_String(argv
[1]);
769 if (IPV6
&& af
->addr_family
== PF_INET6
) {
770 if (JimParseIPv6Address(interp
, addr
, &sa
, &salen
) != JIM_OK
) {
774 else if (JimParseIpAddress(interp
, addr
, &sa
, &salen
) != JIM_OK
) {
777 wdata
= Jim_GetString(argv
[0], &wlen
);
779 /* Note that we don't validate the socket type. Rely on sendto() failing if appropriate */
780 len
= sendto(fileno(af
->fp
), wdata
, wlen
, 0, &sa
.sa
, salen
);
782 JimAioSetError(interp
, NULL
);
785 Jim_SetResultInt(interp
, len
);
789 static int aio_cmd_accept(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
791 AioFile
*af
= Jim_CmdPrivData(interp
);
793 union sockaddr_any sa
;
794 socklen_t addrlen
= sizeof(sa
);
796 sock
= accept(af
->fd
, &sa
.sa
, &addrlen
);
798 JimAioSetError(interp
, NULL
);
803 if (JimFormatIpAddress(interp
, argv
[0], &sa
) != JIM_OK
) {
808 /* Create the file command */
809 return JimMakeChannel(interp
, NULL
, sock
, Jim_NewStringObj(interp
, "accept", -1),
810 "aio.sockstream%ld", af
->addr_family
, "r+") ? JIM_OK
: JIM_ERR
;
813 static int aio_cmd_listen(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
815 AioFile
*af
= Jim_CmdPrivData(interp
);
818 if (Jim_GetLong(interp
, argv
[0], &backlog
) != JIM_OK
) {
822 if (listen(af
->fd
, backlog
)) {
823 JimAioSetError(interp
, NULL
);
829 #endif /* JIM_BOOTSTRAP */
831 static int aio_cmd_flush(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
833 AioFile
*af
= Jim_CmdPrivData(interp
);
835 if (fflush(af
->fp
) == EOF
) {
836 JimAioSetError(interp
, af
->filename
);
842 static int aio_cmd_eof(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
844 AioFile
*af
= Jim_CmdPrivData(interp
);
846 Jim_SetResultInt(interp
, feof(af
->fp
));
850 static int aio_cmd_close(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
853 #if !defined(JIM_ANSIC) && defined(HAVE_SHUTDOWN)
854 static const char * const options
[] = { "r", "w", NULL
};
855 enum { OPT_R
, OPT_W
, };
857 AioFile
*af
= Jim_CmdPrivData(interp
);
859 if (Jim_GetEnum(interp
, argv
[2], options
, &option
, NULL
, JIM_ERRMSG
) != JIM_OK
) {
862 if (shutdown(af
->fd
, option
== OPT_R
? SHUT_RD
: SHUT_WR
) == 0) {
865 JimAioSetError(interp
, NULL
);
867 Jim_SetResultString(interp
, "async close not supported", -1);
872 return Jim_DeleteCommand(interp
, Jim_String(argv
[0]));
875 static int aio_cmd_seek(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
877 AioFile
*af
= Jim_CmdPrivData(interp
);
882 if (Jim_CompareStringImmediate(interp
, argv
[1], "start"))
884 else if (Jim_CompareStringImmediate(interp
, argv
[1], "current"))
886 else if (Jim_CompareStringImmediate(interp
, argv
[1], "end"))
892 if (Jim_GetWide(interp
, argv
[0], &offset
) != JIM_OK
) {
895 if (fseeko(af
->fp
, offset
, orig
) == -1) {
896 JimAioSetError(interp
, af
->filename
);
902 static int aio_cmd_tell(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
904 AioFile
*af
= Jim_CmdPrivData(interp
);
906 Jim_SetResultInt(interp
, ftello(af
->fp
));
910 static int aio_cmd_filename(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
912 AioFile
*af
= Jim_CmdPrivData(interp
);
914 Jim_SetResult(interp
, af
->filename
);
919 static int aio_cmd_ndelay(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
921 AioFile
*af
= Jim_CmdPrivData(interp
);
923 int fmode
= fcntl(af
->fd
, F_GETFL
);
928 if (Jim_GetLong(interp
, argv
[0], &nb
) != JIM_OK
) {
937 (void)fcntl(af
->fd
, F_SETFL
, fmode
);
939 Jim_SetResultInt(interp
, (fmode
& O_NONBLOCK
) ? 1 : 0);
945 static int aio_cmd_sync(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
947 AioFile
*af
= Jim_CmdPrivData(interp
);
955 static int aio_cmd_buffering(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
957 AioFile
*af
= Jim_CmdPrivData(interp
);
959 static const char * const options
[] = {
973 if (Jim_GetEnum(interp
, argv
[0], options
, &option
, NULL
, JIM_ERRMSG
) != JIM_OK
) {
978 setvbuf(af
->fp
, NULL
, _IONBF
, 0);
981 setvbuf(af
->fp
, NULL
, _IOLBF
, BUFSIZ
);
984 setvbuf(af
->fp
, NULL
, _IOFBF
, BUFSIZ
);
990 #ifdef jim_ext_eventloop
991 static void JimAioFileEventFinalizer(Jim_Interp
*interp
, void *clientData
)
993 Jim_Obj
**objPtrPtr
= clientData
;
995 Jim_DecrRefCount(interp
, *objPtrPtr
);
999 static int JimAioFileEventHandler(Jim_Interp
*interp
, void *clientData
, int mask
)
1001 Jim_Obj
**objPtrPtr
= clientData
;
1003 return Jim_EvalObjBackground(interp
, *objPtrPtr
);
1006 static int aio_eventinfo(Jim_Interp
*interp
, AioFile
* af
, unsigned mask
, Jim_Obj
**scriptHandlerObj
,
1007 int argc
, Jim_Obj
* const *argv
)
1010 /* Return current script */
1011 if (*scriptHandlerObj
) {
1012 Jim_SetResult(interp
, *scriptHandlerObj
);
1017 if (*scriptHandlerObj
) {
1018 /* Delete old handler */
1019 Jim_DeleteFileHandler(interp
, af
->fd
, mask
);
1022 /* Now possibly add the new script(s) */
1023 if (Jim_Length(argv
[0]) == 0) {
1024 /* Empty script, so done */
1028 /* A new script to add */
1029 Jim_IncrRefCount(argv
[0]);
1030 *scriptHandlerObj
= argv
[0];
1032 Jim_CreateFileHandler(interp
, af
->fd
, mask
,
1033 JimAioFileEventHandler
, scriptHandlerObj
, JimAioFileEventFinalizer
);
1038 static int aio_cmd_readable(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1040 AioFile
*af
= Jim_CmdPrivData(interp
);
1042 return aio_eventinfo(interp
, af
, JIM_EVENT_READABLE
, &af
->rEvent
, argc
, argv
);
1045 static int aio_cmd_writable(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1047 AioFile
*af
= Jim_CmdPrivData(interp
);
1049 return aio_eventinfo(interp
, af
, JIM_EVENT_WRITABLE
, &af
->wEvent
, argc
, argv
);
1052 static int aio_cmd_onexception(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1054 AioFile
*af
= Jim_CmdPrivData(interp
);
1056 return aio_eventinfo(interp
, af
, JIM_EVENT_EXCEPTION
, &af
->eEvent
, argc
, argv
);
1060 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
1061 static int aio_cmd_ssl(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1063 AioFile
*af
= Jim_CmdPrivData(interp
);
1069 if (!Jim_CompareStringImmediate(interp
, argv
[2], "-server")) {
1074 else if (argc
!= 2) {
1075 Jim_WrongNumArgs(interp
, 2, argv
, "?-server cert priv?");
1079 fd
= fileno(af
->fp
);
1080 #if defined(HAVE_DUP)
1086 ssl_ctx
= JimAioSslCtx(interp
);
1087 if (ssl_ctx
== NULL
) {
1091 ssl
= SSL_new(ssl_ctx
);
1093 #if defined(HAVE_DUP)
1096 Jim_SetResultString(interp
, ERR_error_string(ERR_get_error(), NULL
), -1);
1100 SSL_set_cipher_list(ssl
, "ALL");
1102 if (SSL_set_fd(ssl
, fileno(af
->fp
)) == 0) {
1107 if (SSL_use_certificate_file(ssl
, Jim_String(argv
[3]), SSL_FILETYPE_PEM
) != 1) {
1111 if (SSL_use_PrivateKey_file(ssl
, Jim_String(argv
[4]), SSL_FILETYPE_PEM
) != 1) {
1115 if (SSL_accept(ssl
) != 1) {
1120 if (SSL_connect(ssl
) != 1) {
1125 af
= JimMakeChannel(interp
, NULL
, fd
, NULL
, "aio.sslstream%ld", af
->addr_family
, "r+");
1131 af
->fops
= &ssl_fops
;
1136 #if defined(HAVE_DUP)
1140 Jim_SetResultString(interp
, ERR_error_string(ERR_get_error(), NULL
), -1);
1144 static int aio_cmd_verify(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1146 AioFile
*af
= Jim_CmdPrivData(interp
);
1149 if (!af
->fops
->verify
) {
1153 ret
= af
->fops
->verify(af
);
1154 if (ret
!= JIM_OK
) {
1155 if (JimCheckStreamError(interp
, af
)) {
1156 JimAioSetError(interp
, af
->filename
);
1158 Jim_SetResultString(interp
, "failed to verify the connection authenticity", -1);
1163 #endif /* JIM_BOOTSTRAP */
1165 #if defined(HAVE_STRUCT_FLOCK) && !defined(JIM_BOOTSTRAP)
1166 static int aio_cmd_lock(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1168 AioFile
*af
= Jim_CmdPrivData(interp
);
1173 fl
.l_type
= F_WRLCK
;
1174 fl
.l_whence
= SEEK_SET
;
1176 switch (fcntl(af
->fd
, F_SETLK
, &fl
))
1179 Jim_SetResultInt(interp
, 1);
1182 if (errno
== EACCES
|| errno
== EAGAIN
)
1183 Jim_SetResultInt(interp
, 0);
1186 Jim_SetResultFormatted(interp
, "lock failed: %s",
1192 Jim_SetResultInt(interp
, 0);
1199 static int aio_cmd_unlock(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1201 AioFile
*af
= Jim_CmdPrivData(interp
);
1205 fl
.l_type
= F_UNLCK
;
1206 fl
.l_whence
= SEEK_SET
;
1208 Jim_SetResultInt(interp
, fcntl(af
->fd
, F_SETLK
, &fl
) == 0);
1211 #endif /* JIM_BOOTSTRAP */
1213 #if defined(HAVE_TERMIOS_H) && !defined(JIM_BOOTSTRAP)
1214 static int aio_cmd_tty(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1216 AioFile
*af
= Jim_CmdPrivData(interp
);
1217 Jim_Obj
*dictObjPtr
;
1221 /* get the current settings as a dictionary */
1222 dictObjPtr
= Jim_GetTtySettings(interp
, af
->fd
);
1223 if (dictObjPtr
== NULL
) {
1224 JimAioSetError(interp
, NULL
);
1227 Jim_SetResult(interp
, dictObjPtr
);
1232 /* Convert name value arguments to a dictionary */
1233 dictObjPtr
= Jim_NewListObj(interp
, argv
, argc
);
1236 /* The settings are already given as a list */
1237 dictObjPtr
= argv
[0];
1239 Jim_IncrRefCount(dictObjPtr
);
1241 if (Jim_ListLength(interp
, dictObjPtr
) % 2) {
1242 /* Must be a valid dictionary */
1243 Jim_DecrRefCount(interp
, dictObjPtr
);
1247 ret
= Jim_SetTtySettings(interp
, af
->fd
, dictObjPtr
);
1249 JimAioSetError(interp
, NULL
);
1252 Jim_DecrRefCount(interp
, dictObjPtr
);
1256 #endif /* JIM_BOOTSTRAP */
1258 static const jim_subcmd_type aio_command_table
[] = {
1260 "?-nonewline? ?len?",
1264 /* Description: Read and return bytes from the stream. To eof if no len. */
1271 /* Description: Copy up to 'size' bytes to the given filehandle, or to eof if no size. */
1278 /* Description: Read one line and return it or store it in the var */
1285 /* Description: Write the string, with newline unless -nonewline */
1292 /* Description: Is the file descriptor a tty? */
1294 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
1300 /* Description: Receive up to 'len' bytes on the socket. Sets 'addrvar' with receive address, if set */
1307 /* Description: Send 'str' to the given address (dgram only) */
1314 /* Description: Server socket only: Accept a connection and return stream */
1321 /* Description: Set the listen backlog for server socket */
1323 #endif /* JIM_BOOTSTRAP */
1329 /* Description: Flush the stream */
1336 /* Description: Returns 1 if stream is at eof */
1343 JIM_MODFLAG_FULLARGV
,
1344 /* Description: Closes the stream. */
1347 "offset ?start|current|end",
1351 /* Description: Seeks in the stream (default 'current') */
1358 /* Description: Returns the current seek position */
1365 /* Description: Returns the original filename */
1373 /* Description: Set O_NDELAY (if arg). Returns current/new setting. */
1382 /* Description: Flush and fsync() the stream */
1390 /* Description: Sets buffering */
1392 #ifdef jim_ext_eventloop
1394 "?readable-script?",
1398 /* Description: Returns script, or invoke readable-script when readable, {} to remove */
1401 "?writable-script?",
1405 /* Description: Returns script, or invoke writable-script when writable, {} to remove */
1408 "?exception-script?",
1409 aio_cmd_onexception
,
1412 /* Description: Returns script, or invoke exception-script when oob data, {} to remove */
1415 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
1417 "?-server cert priv?",
1421 JIM_MODFLAG_FULLARGV
1422 /* Description: Wraps a stream socket with SSL/TLS and returns a new channel */
1429 /* Description: Verifies the certificate of a SSL/TLS channel */
1431 #endif /* JIM_BOOTSTRAP */
1432 #if defined(HAVE_STRUCT_FLOCK) && !defined(JIM_BOOTSTRAP)
1438 /* Description: Attempt to get a lock. */
1445 /* Description: Relase a lock. */
1447 #endif /* JIM_BOOTSTRAP */
1448 #if defined(HAVE_TERMIOS_H) && !defined(JIM_BOOTSTRAP)
1450 "?baud rate? ?data bits? ?stop bits? ?parity even|odd|none? ?handshake xonxoff|rtscts|none? ?input raw|cooked? ?output raw|cooked? ?vmin n? ?vtime n?",
1454 /* Description: Get or set tty settings - valid only on a tty */
1456 #endif /* JIM_BOOTSTRAP */
1460 static int JimAioSubCmdProc(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1462 return Jim_CallSubCmd(interp
, Jim_ParseSubCmd(interp
, aio_command_table
, argc
, argv
), argc
, argv
);
1465 static int JimAioOpenCommand(Jim_Interp
*interp
, int argc
,
1466 Jim_Obj
*const *argv
)
1470 if (argc
!= 2 && argc
!= 3) {
1471 Jim_WrongNumArgs(interp
, 1, argv
, "filename ?mode?");
1475 mode
= (argc
== 3) ? Jim_String(argv
[2]) : "r";
1477 #ifdef jim_ext_tclcompat
1479 const char *filename
= Jim_String(argv
[1]);
1481 /* If the filename starts with '|', use popen instead */
1482 if (*filename
== '|') {
1483 Jim_Obj
*evalObj
[3];
1485 evalObj
[0] = Jim_NewStringObj(interp
, "::popen", -1);
1486 evalObj
[1] = Jim_NewStringObj(interp
, filename
+ 1, -1);
1487 evalObj
[2] = Jim_NewStringObj(interp
, mode
, -1);
1489 return Jim_EvalObjVector(interp
, 3, evalObj
);
1493 return JimMakeChannel(interp
, NULL
, -1, argv
[1], "aio.handle%ld", 0, mode
) ? JIM_OK
: JIM_ERR
;
1496 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
1497 static void JimAioSslContextDelProc(struct Jim_Interp
*interp
, void *privData
)
1499 SSL_CTX_free((SSL_CTX
*)privData
);
1503 static SSL_CTX
*JimAioSslCtx(Jim_Interp
*interp
)
1505 SSL_CTX
*ssl_ctx
= (SSL_CTX
*)Jim_GetAssocData(interp
, "ssl_ctx");
1506 if (ssl_ctx
== NULL
) {
1507 SSL_load_error_strings();
1509 ssl_ctx
= SSL_CTX_new(TLSv1_2_method());
1510 if (ssl_ctx
&& SSL_CTX_set_default_verify_paths(ssl_ctx
)) {
1511 SSL_CTX_set_verify(ssl_ctx
, SSL_VERIFY_NONE
, NULL
);
1512 Jim_SetAssocData(interp
, "ssl_ctx", JimAioSslContextDelProc
, ssl_ctx
);
1514 Jim_SetResultString(interp
, ERR_error_string(ERR_get_error(), NULL
), -1);
1519 #endif /* JIM_BOOTSTRAP */
1522 * Creates a channel for fh/fd/filename.
1524 * If fh is not NULL, uses that as the channel (and sets AIO_KEEPOPEN).
1525 * Otherwise, if fd is >= 0, uses that as the channel.
1526 * Otherwise opens 'filename' with mode 'mode'.
1528 * hdlfmt is a sprintf format for the filehandle. Anything with %ld at the end will do.
1529 * mode is used for open or fdopen.
1531 * Creates the command and sets the name as the current result.
1532 * Returns the AioFile pointer on sucess or NULL on failure.
1534 static AioFile
*JimMakeChannel(Jim_Interp
*interp
, FILE *fh
, int fd
, Jim_Obj
*filename
,
1535 const char *hdlfmt
, int family
, const char *mode
)
1538 char buf
[AIO_CMD_LEN
];
1541 snprintf(buf
, sizeof(buf
), hdlfmt
, Jim_GetId(interp
));
1544 openFlags
= AIO_KEEPOPEN
;
1547 snprintf(buf
, sizeof(buf
), hdlfmt
, Jim_GetId(interp
));
1549 filename
= Jim_NewStringObj(interp
, buf
, -1);
1552 Jim_IncrRefCount(filename
);
1555 #if !defined(JIM_ANSIC)
1557 fh
= fdopen(fd
, mode
);
1561 fh
= fopen(Jim_String(filename
), mode
);
1564 JimAioSetError(interp
, filename
);
1565 #if !defined(JIM_ANSIC)
1570 Jim_DecrRefCount(interp
, filename
);
1575 /* Create the file command */
1576 af
= Jim_Alloc(sizeof(*af
));
1577 memset(af
, 0, sizeof(*af
));
1579 af
->fd
= fileno(fh
);
1580 af
->filename
= filename
;
1582 if ((openFlags
& AIO_KEEPOPEN
) == 0) {
1583 (void)fcntl(af
->fd
, F_SETFD
, FD_CLOEXEC
);
1586 af
->openFlags
= openFlags
;
1587 af
->addr_family
= family
;
1588 af
->fops
= &stdio_fops
;
1591 Jim_CreateCommand(interp
, buf
, JimAioSubCmdProc
, af
, JimAioDelProc
);
1593 /* Note that the command must use the global namespace, even if
1594 * the current namespace is something different
1596 Jim_SetResult(interp
, Jim_MakeGlobalNamespaceName(interp
, Jim_NewStringObj(interp
, buf
, -1)));
1601 #if defined(HAVE_PIPE) || (defined(HAVE_SOCKETPAIR) && defined(HAVE_SYS_UN_H))
1603 * Create a pair of channels. e.g. from pipe() or socketpair()
1605 static int JimMakeChannelPair(Jim_Interp
*interp
, int p
[2], Jim_Obj
*filename
,
1606 const char *hdlfmt
, int family
, const char *mode
[2])
1608 if (JimMakeChannel(interp
, NULL
, p
[0], filename
, hdlfmt
, family
, mode
[0])) {
1609 Jim_Obj
*objPtr
= Jim_NewListObj(interp
, NULL
, 0);
1610 Jim_ListAppendElement(interp
, objPtr
, Jim_GetResult(interp
));
1612 if (JimMakeChannel(interp
, NULL
, p
[1], filename
, hdlfmt
, family
, mode
[1])) {
1613 Jim_ListAppendElement(interp
, objPtr
, Jim_GetResult(interp
));
1614 Jim_SetResult(interp
, objPtr
);
1619 /* Can only be here if fdopen() failed */
1622 JimAioSetError(interp
, NULL
);
1627 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
1629 static int JimAioSockCommand(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1631 const char *hdlfmt
= "aio.unknown%ld";
1632 const char *socktypes
[] = {
1652 SOCK_STREAM_SOCKETPAIR
,
1656 const char *hostportarg
= NULL
;
1659 const char *mode
= "r+";
1660 int family
= PF_INET
;
1661 Jim_Obj
*argv0
= argv
[0];
1664 if (argc
> 1 && Jim_CompareStringImmediate(interp
, argv
[1], "-ipv6")) {
1666 Jim_SetResultString(interp
, "ipv6 not supported", -1);
1677 Jim_WrongNumArgs(interp
, 1, &argv0
, "?-ipv6? type ?address?");
1681 if (Jim_GetEnum(interp
, argv
[1], socktypes
, &socktype
, "socket type", JIM_ERRMSG
) != JIM_OK
)
1682 return Jim_CheckShowCommands(interp
, argv
[1], socktypes
);
1684 Jim_SetEmptyResult(interp
);
1686 hdlfmt
= "aio.sock%ld";
1689 hostportarg
= Jim_String(argv
[2]);
1693 case SOCK_DGRAM_CLIENT
:
1695 /* No address, so an unconnected dgram socket */
1696 sock
= socket(family
, SOCK_DGRAM
, 0);
1698 JimAioSetError(interp
, NULL
);
1704 case SOCK_STREAM_CLIENT
:
1706 union sockaddr_any sa
;
1714 if (JimParseIPv6Address(interp
, hostportarg
, &sa
, &salen
) != JIM_OK
) {
1718 else if (JimParseIpAddress(interp
, hostportarg
, &sa
, &salen
) != JIM_OK
) {
1721 sock
= socket(family
, (socktype
== SOCK_DGRAM_CLIENT
) ? SOCK_DGRAM
: SOCK_STREAM
, 0);
1723 JimAioSetError(interp
, NULL
);
1726 res
= connect(sock
, &sa
.sa
, salen
);
1728 JimAioSetError(interp
, argv
[2]);
1735 case SOCK_STREAM_SERVER
:
1736 case SOCK_DGRAM_SERVER
:
1738 union sockaddr_any sa
;
1746 if (JimParseIPv6Address(interp
, hostportarg
, &sa
, &salen
) != JIM_OK
) {
1750 else if (JimParseIpAddress(interp
, hostportarg
, &sa
, &salen
) != JIM_OK
) {
1753 sock
= socket(family
, (socktype
== SOCK_DGRAM_SERVER
) ? SOCK_DGRAM
: SOCK_STREAM
, 0);
1755 JimAioSetError(interp
, NULL
);
1759 /* Enable address reuse */
1760 setsockopt(sock
, SOL_SOCKET
, SO_REUSEADDR
, (void *)&on
, sizeof(on
));
1762 res
= bind(sock
, &sa
.sa
, salen
);
1764 JimAioSetError(interp
, argv
[2]);
1768 if (socktype
== SOCK_STREAM_SERVER
) {
1769 res
= listen(sock
, 5);
1771 JimAioSetError(interp
, NULL
);
1776 hdlfmt
= "aio.socksrv%ld";
1780 #ifdef HAVE_SYS_UN_H
1783 struct sockaddr_un sa
;
1786 if (argc
!= 3 || ipv6
) {
1790 if (JimParseDomainAddress(interp
, hostportarg
, &sa
) != JIM_OK
) {
1791 JimAioSetError(interp
, argv
[2]);
1795 sock
= socket(PF_UNIX
, SOCK_STREAM
, 0);
1797 JimAioSetError(interp
, NULL
);
1800 len
= strlen(sa
.sun_path
) + 1 + sizeof(sa
.sun_family
);
1801 res
= connect(sock
, (struct sockaddr
*)&sa
, len
);
1803 JimAioSetError(interp
, argv
[2]);
1807 hdlfmt
= "aio.sockunix%ld";
1811 case SOCK_UNIX_SERVER
:
1813 struct sockaddr_un sa
;
1816 if (argc
!= 3 || ipv6
) {
1820 if (JimParseDomainAddress(interp
, hostportarg
, &sa
) != JIM_OK
) {
1821 JimAioSetError(interp
, argv
[2]);
1825 sock
= socket(PF_UNIX
, SOCK_STREAM
, 0);
1827 JimAioSetError(interp
, NULL
);
1830 len
= strlen(sa
.sun_path
) + 1 + sizeof(sa
.sun_family
);
1831 res
= bind(sock
, (struct sockaddr
*)&sa
, len
);
1833 JimAioSetError(interp
, argv
[2]);
1837 res
= listen(sock
, 5);
1839 JimAioSetError(interp
, NULL
);
1843 hdlfmt
= "aio.sockunixsrv%ld";
1848 #if defined(HAVE_SOCKETPAIR) && defined(HAVE_SYS_UN_H)
1849 case SOCK_STREAM_SOCKETPAIR
:
1852 static const char *mode
[2] = { "r+", "r+" };
1854 if (argc
!= 2 || ipv6
) {
1858 if (socketpair(PF_UNIX
, SOCK_STREAM
, 0, p
) < 0) {
1859 JimAioSetError(interp
, NULL
);
1862 return JimMakeChannelPair(interp
, p
, argv
[1], "aio.sockpair%ld", PF_UNIX
, mode
);
1867 #if defined(HAVE_PIPE)
1868 case SOCK_STREAM_PIPE
:
1871 static const char *mode
[2] = { "r", "w" };
1873 if (argc
!= 2 || ipv6
) {
1878 JimAioSetError(interp
, NULL
);
1882 return JimMakeChannelPair(interp
, p
, argv
[1], "aio.pipe%ld", 0, mode
);
1888 Jim_SetResultString(interp
, "Unsupported socket type", -1);
1892 return JimMakeChannel(interp
, NULL
, sock
, argv
[1], hdlfmt
, family
, mode
) ? JIM_OK
: JIM_ERR
;
1894 #endif /* JIM_BOOTSTRAP */
1897 * Returns the file descriptor of a writable, newly created temp file
1900 * On success, leaves the filename in the interpreter result, otherwise
1901 * leaves an error message.
1903 int Jim_MakeTempFile(Jim_Interp
*interp
, const char *template)
1908 Jim_Obj
*filenameObj
;
1910 if (template == NULL
) {
1911 const char *tmpdir
= getenv("TMPDIR");
1912 if (tmpdir
== NULL
|| *tmpdir
== '\0' || access(tmpdir
, W_OK
) != 0) {
1915 filenameObj
= Jim_NewStringObj(interp
, tmpdir
, -1);
1916 if (tmpdir
[0] && tmpdir
[strlen(tmpdir
) - 1] != '/') {
1917 Jim_AppendString(interp
, filenameObj
, "/", 1);
1919 Jim_AppendString(interp
, filenameObj
, "tcl.tmp.XXXXXX", -1);
1922 filenameObj
= Jim_NewStringObj(interp
, template, -1);
1925 /* Update the template name directly with the filename */
1926 mask
= umask(S_IXUSR
| S_IRWXG
| S_IRWXO
);
1927 fd
= mkstemp(filenameObj
->bytes
);
1930 JimAioSetError(interp
, filenameObj
);
1931 Jim_FreeNewObj(interp
, filenameObj
);
1935 Jim_SetResult(interp
, filenameObj
);
1938 Jim_SetResultString(interp
, "platform has no tempfile support", -1);
1943 #if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
1944 static int JimAioLoadSSLCertsCommand(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1949 Jim_WrongNumArgs(interp
, 1, argv
, "dir");
1953 ssl_ctx
= JimAioSslCtx(interp
);
1957 if (SSL_CTX_load_verify_locations(ssl_ctx
, NULL
, Jim_String(argv
[1])) == 1) {
1960 Jim_SetResultString(interp
, ERR_error_string(ERR_get_error(), NULL
), -1);
1963 #endif /* JIM_BOOTSTRAP */
1965 int Jim_aioInit(Jim_Interp
*interp
)
1967 if (Jim_PackageProvide(interp
, "aio", "1.0", JIM_ERRMSG
))
1970 #if defined(JIM_SSL)
1971 Jim_CreateCommand(interp
, "load_ssl_certs", JimAioLoadSSLCertsCommand
, NULL
, NULL
);
1974 Jim_CreateCommand(interp
, "open", JimAioOpenCommand
, NULL
, NULL
);
1976 Jim_CreateCommand(interp
, "socket", JimAioSockCommand
, NULL
, NULL
);
1979 /* Create filehandles for stdin, stdout and stderr */
1980 JimMakeChannel(interp
, stdin
, -1, NULL
, "stdin", 0, "r");
1981 JimMakeChannel(interp
, stdout
, -1, NULL
, "stdout", 0, "w");
1982 JimMakeChannel(interp
, stderr
, -1, NULL
, "stderr", 0, "w");