2 /* Jim - A small embeddable Tcl interpreter
4 * Copyright 2005 Salvatore Sanfilippo <antirez@invece.org>
5 * Copyright 2005 Clemens Hintze <c.hintze@gmx.net>
6 * Copyright 2005 patthoyts - Pat Thoyts <patthoyts@users.sf.net>
7 * Copyright 2008 oharboe - o/yvind Harboe - oyvind.harboe@zylin.com
8 * Copyright 2008 Andrew Lunn <andrew@lunn.ch>
9 * Copyright 2008 Duane Ellis <openocd@duaneellis.com>
10 * Copyright 2008 Uwe Klein <uklein@klein-messgeraete.de>
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
18 * 1. Redistributions of source code must retain the above copyright
19 * notice, this list of conditions and the following disclaimer.
20 * 2. Redistributions in binary form must reproduce the above
21 * copyright notice, this list of conditions and the following
22 * disclaimer in the documentation and/or other materials
23 * provided with the distribution.
25 * THIS SOFTWARE IS PROVIDED BY THE JIM TCL PROJECT ``AS IS'' AND ANY
26 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
27 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
28 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
29 * JIM TCL PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
30 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
31 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
34 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
36 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 * The views and conclusions contained in the software and documentation
39 * are those of the authors and should not be interpreted as representing
40 * official policies, either expressed or implied, of the Jim Tcl Project.
52 #include <sys/socket.h>
54 #include <netinet/in.h>
55 #include <arpa/inet.h>
59 #include "jim-eventloop.h"
60 #include "jim-subcmd.h"
63 #define AIO_CMD_LEN 128
64 #define AIO_BUF_LEN 1024
66 #define AIO_KEEPOPEN 1
80 struct sockaddr_in sin
;
82 struct sockaddr_in6 sin6
;
87 typedef struct AioFile
92 int OpenFlags
; /* AIO_KEEPOPEN? keep FILE* */
105 static int JimAioSubCmdProc(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
);
108 static int JimParseIPv6Address(Jim_Interp
*interp
, const char *hostport
, union sockaddr_any
*sa
, int *salen
)
112 * An IPv6 addr/port looks like:
115 * [fe80::223:6cff:fe95:bdc0%en1]:2000
119 * Note that the "any" address is ::, which is the same as when no address is specified.
127 stport
= strrchr(hostport
, ':');
129 /* No : so, the whole thing is the port */
132 sthost
= Jim_StrDup(hostport
);
138 if (*hostport
== '[') {
139 /* This is a numeric ipv6 address */
140 char *pt
= strchr(++hostport
, ']');
142 sthost
= Jim_StrDupLen(hostport
, pt
- hostport
);
147 sthost
= Jim_StrDupLen(hostport
, stport
- hostport
- 1);
150 memset(&req
, '\0', sizeof(req
));
151 req
.ai_family
= PF_INET6
;
153 if (getaddrinfo(sthost
, NULL
, &req
, &ai
)) {
154 Jim_SetResultFormatted(interp
, "Not a valid address: %s", hostport
);
158 memcpy(&sa
->sin
, ai
->ai_addr
, ai
->ai_addrlen
);
159 *salen
= ai
->ai_addrlen
;
161 sa
->sin
.sin_port
= htons(atoi(stport
));
169 Jim_SetResultString(interp
, "ipv6 not supported", -1);
174 static int JimParseIpAddress(Jim_Interp
*interp
, const char *hostport
, union sockaddr_any
*sa
, int *salen
)
176 /* An IPv4 addr/port looks like:
181 * If the address is missing, INADDR_ANY is used.
182 * If the port is missing, 0 is used (only useful for server sockets).
190 stport
= strrchr(hostport
, ':');
192 /* No : so, the whole thing is the port */
194 sthost
= Jim_StrDup("0.0.0.0");
197 sthost
= Jim_StrDupLen(hostport
, stport
- hostport
);
201 memset(&req
, '\0', sizeof(req
));
202 req
.ai_family
= PF_INET
;
204 if (getaddrinfo(sthost
, NULL
, &req
, &ai
)) {
205 Jim_SetResultFormatted(interp
, "Not a valid address: %s", hostport
);
209 memcpy(&sa
->sin
, ai
->ai_addr
, ai
->ai_addrlen
);
210 *salen
= ai
->ai_addrlen
;
212 sa
->sin
.sin_port
= htons(atoi(stport
));
221 static int JimParseDomainAddress(Jim_Interp
*interp
, const char *path
, struct sockaddr_un
*sa
)
223 sa
->sun_family
= PF_UNIX
;
224 strcpy(sa
->sun_path
, path
);
230 static void JimAioSetError(Jim_Interp
*interp
, Jim_Obj
*name
)
233 Jim_SetResultFormatted(interp
, "%#s: %s", name
, strerror(errno
));
236 Jim_SetResultString(interp
, strerror(errno
), -1);
240 static void JimAioDelProc(Jim_Interp
*interp
, void *privData
)
242 AioFile
*af
= privData
;
246 Jim_DecrRefCount(interp
, af
->filename
);
248 if (!(af
->OpenFlags
& AIO_KEEPOPEN
)) {
251 #ifdef jim_ext_eventloop
252 /* remove existing EventHandlers */
254 Jim_DeleteFileHandler(interp
, af
->fp
);
257 Jim_DeleteFileHandler(interp
, af
->fp
);
260 Jim_DeleteFileHandler(interp
, af
->fp
);
266 static int aio_cmd_read(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
268 AioFile
*af
= Jim_CmdPrivData(interp
);
269 char buf
[AIO_BUF_LEN
];
272 int neededLen
= -1; /* -1 is "read as much as possible" */
274 if (argc
&& Jim_CompareStringImmediate(interp
, argv
[0], "-nonewline")) {
282 if (Jim_GetWide(interp
, argv
[0], &wideValue
) != JIM_OK
)
285 Jim_SetResultString(interp
, "invalid parameter: negative len", -1);
288 neededLen
= (int)wideValue
;
293 objPtr
= Jim_NewStringObj(interp
, NULL
, 0);
294 while (neededLen
!= 0) {
298 if (neededLen
== -1) {
299 readlen
= AIO_BUF_LEN
;
302 readlen
= (neededLen
> AIO_BUF_LEN
? AIO_BUF_LEN
: neededLen
);
304 retval
= fread(buf
, 1, readlen
, af
->fp
);
306 Jim_AppendString(interp
, objPtr
, buf
, retval
);
307 if (neededLen
!= -1) {
311 if (retval
!= readlen
)
314 /* Check for error conditions */
315 if (ferror(af
->fp
)) {
317 Jim_FreeNewObj(interp
, objPtr
);
318 JimAioSetError(interp
, af
->filename
);
323 const char *s
= Jim_GetString(objPtr
, &len
);
325 if (len
> 0 && s
[len
- 1] == '\n') {
327 objPtr
->bytes
[objPtr
->length
] = '\0';
330 Jim_SetResult(interp
, objPtr
);
334 static int aio_cmd_gets(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
336 AioFile
*af
= Jim_CmdPrivData(interp
);
337 char buf
[AIO_BUF_LEN
];
340 objPtr
= Jim_NewStringObj(interp
, NULL
, 0);
344 buf
[AIO_BUF_LEN
- 1] = '_';
345 if (fgets(buf
, AIO_BUF_LEN
, af
->fp
) == NULL
)
347 if (buf
[AIO_BUF_LEN
- 1] == '\0' && buf
[AIO_BUF_LEN
- 2] != '\n')
350 Jim_AppendString(interp
, objPtr
, buf
, AIO_BUF_LEN
- 1);
353 int len
= strlen(buf
);
356 int hasnl
= (buf
[len
- 1] == '\n');
359 Jim_AppendString(interp
, objPtr
, buf
, strlen(buf
) - hasnl
);
365 if (ferror(af
->fp
) && (errno
!= EAGAIN
)) {
367 Jim_FreeNewObj(interp
, objPtr
);
368 JimAioSetError(interp
, af
->filename
);
371 /* On EOF returns -1 if varName was specified, or the empty string. */
372 if (feof(af
->fp
) && Jim_Length(objPtr
) == 0) {
373 Jim_FreeNewObj(interp
, objPtr
);
375 Jim_SetResultInt(interp
, -1);
382 Jim_GetString(objPtr
, &totLen
);
383 if (Jim_SetVariable(interp
, argv
[0], objPtr
) != JIM_OK
) {
384 Jim_FreeNewObj(interp
, objPtr
);
387 Jim_SetResultInt(interp
, totLen
);
390 Jim_SetResult(interp
, objPtr
);
395 static int aio_cmd_puts(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
397 AioFile
*af
= Jim_CmdPrivData(interp
);
403 if (!Jim_CompareStringImmediate(interp
, argv
[0], "-nonewline")) {
412 wdata
= Jim_GetString(strObj
, &wlen
);
413 if (fwrite(wdata
, 1, wlen
, af
->fp
) == (unsigned)wlen
) {
414 if (argc
== 2 || putc('\n', af
->fp
) != EOF
) {
418 JimAioSetError(interp
, af
->filename
);
423 static int aio_cmd_recvfrom(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
425 AioFile
*af
= Jim_CmdPrivData(interp
);
427 union sockaddr_any sa
;
429 socklen_t salen
= sizeof(sa
);
432 if (Jim_GetLong(interp
, argv
[0], &len
) != JIM_OK
) {
436 buf
= Jim_Alloc(len
+ 1);
438 rlen
= recvfrom(fileno(af
->fp
), buf
, len
, 0, &sa
.sa
, &salen
);
442 JimAioSetError(interp
, NULL
);
446 Jim_SetResult(interp
, Jim_NewStringObjNoAlloc(interp
, buf
, rlen
));
452 if (sa
.sa
.sa_family
== AF_INET6
) {
454 /* Allow 9 for []:65535\0 */
455 inet_ntop(sa
.sa
.sa_family
, &sa
.sin6
.sin6_addr
, addrbuf
+ 1, sizeof(addrbuf
) - 9);
456 snprintf(addrbuf
+ strlen(addrbuf
), 8, "]:%d", ntohs(sa
.sin
.sin_port
));
461 /* Allow 7 for :65535\0 */
462 inet_ntop(sa
.sa
.sa_family
, &sa
.sin
.sin_addr
, addrbuf
, sizeof(addrbuf
) - 7);
463 snprintf(addrbuf
+ strlen(addrbuf
), 7, ":%d", ntohs(sa
.sin
.sin_port
));
466 if (Jim_SetVariable(interp
, argv
[1], Jim_NewStringObj(interp
, addrbuf
, -1)) != JIM_OK
) {
475 static int aio_cmd_sendto(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
477 AioFile
*af
= Jim_CmdPrivData(interp
);
481 union sockaddr_any sa
;
482 const char *addr
= Jim_GetString(argv
[1], NULL
);
485 if (IPV6
&& af
->addr_family
== AF_INET6
) {
486 if (JimParseIPv6Address(interp
, addr
, &sa
, &salen
) != JIM_OK
) {
490 else if (JimParseIpAddress(interp
, addr
, &sa
, &salen
) != JIM_OK
) {
493 wdata
= Jim_GetString(argv
[0], &wlen
);
495 /* Note that we don't validate the socket type. Rely on sendto() failing if appropriate */
496 len
= sendto(fileno(af
->fp
), wdata
, wlen
, 0, &sa
.sa
, salen
);
499 JimAioSetError(interp
, NULL
);
502 Jim_SetResultInt(interp
, len
);
506 static int aio_cmd_accept(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
508 AioFile
*serv_af
= Jim_CmdPrivData(interp
);
510 union sockaddr_any sa
;
511 socklen_t addrlen
= sizeof(sa
);
513 char buf
[AIO_CMD_LEN
];
516 sock
= accept(serv_af
->fd
, &sa
.sa
, &addrlen
);
520 /* Get the next file id */
521 fileId
= Jim_GetId(interp
);
523 /* Create the file command */
524 af
= Jim_Alloc(sizeof(*af
));
526 af
->filename
= Jim_NewStringObj(interp
, "accept", -1);
527 Jim_IncrRefCount(af
->filename
);
528 af
->fp
= fdopen(sock
, "r+");
531 af
->flags
= fcntl(af
->fd
, F_GETFL
);
536 af
->addr_family
= serv_af
->addr_family
;
537 sprintf(buf
, "aio.sockstream%ld", fileId
);
538 Jim_CreateCommand(interp
, buf
, JimAioSubCmdProc
, af
, JimAioDelProc
);
539 Jim_SetResultString(interp
, buf
, -1);
545 static int aio_cmd_flush(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
547 AioFile
*af
= Jim_CmdPrivData(interp
);
549 if (fflush(af
->fp
) == EOF
) {
550 JimAioSetError(interp
, af
->filename
);
556 static int aio_cmd_eof(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
558 AioFile
*af
= Jim_CmdPrivData(interp
);
560 Jim_SetResultInt(interp
, feof(af
->fp
));
564 static int aio_cmd_close(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
566 Jim_DeleteCommand(interp
, Jim_GetString(argv
[0], NULL
));
570 static int aio_cmd_seek(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
572 AioFile
*af
= Jim_CmdPrivData(interp
);
577 if (Jim_CompareStringImmediate(interp
, argv
[1], "start"))
579 else if (Jim_CompareStringImmediate(interp
, argv
[1], "current"))
581 else if (Jim_CompareStringImmediate(interp
, argv
[1], "end"))
587 if (Jim_GetLong(interp
, argv
[0], &offset
) != JIM_OK
) {
590 if (fseek(af
->fp
, offset
, orig
) == -1) {
591 JimAioSetError(interp
, af
->filename
);
597 static int aio_cmd_tell(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
599 AioFile
*af
= Jim_CmdPrivData(interp
);
601 Jim_SetResultInt(interp
, ftell(af
->fp
));
606 static int aio_cmd_ndelay(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
608 AioFile
*af
= Jim_CmdPrivData(interp
);
610 int fmode
= af
->flags
;
615 if (Jim_GetLong(interp
, argv
[0], &nb
) != JIM_OK
) {
624 fcntl(af
->fd
, F_SETFL
, fmode
);
627 Jim_SetResultInt(interp
, (fmode
& O_NONBLOCK
) ? 1 : 0);
632 #ifdef jim_ext_eventloop
633 static void JimAioFileEventFinalizer(Jim_Interp
*interp
, void *clientData
)
635 Jim_Obj
*objPtr
= clientData
;
637 Jim_DecrRefCount(interp
, objPtr
);
640 static int JimAioFileEventHandler(Jim_Interp
*interp
, void *clientData
, int mask
)
642 Jim_Obj
*objPtr
= clientData
;
643 Jim_Obj
*scrPtr
= NULL
;
645 if (mask
== (JIM_EVENT_READABLE
| JIM_EVENT_FEOF
)) {
646 Jim_ListIndex(interp
, objPtr
, 1, &scrPtr
, 0);
649 Jim_ListIndex(interp
, objPtr
, 0, &scrPtr
, 0);
651 Jim_EvalObjBackground(interp
, scrPtr
);
655 static int aio_eventinfo(Jim_Interp
*interp
, AioFile
* af
, unsigned mask
, Jim_Obj
**scriptListObj
,
656 Jim_Obj
*script1
, Jim_Obj
*script2
)
660 if (script1
== NULL
) {
661 /* Return current script */
662 if (*scriptListObj
) {
663 Jim_SetResult(interp
, *scriptListObj
);
668 if (*scriptListObj
) {
669 /* Delete old handler */
670 Jim_DeleteFileHandler(interp
, af
->fp
);
671 *scriptListObj
= NULL
;
674 /* Now possibly add the new script(s) */
675 Jim_GetString(script1
, &scriptlen
);
676 if (scriptlen
== 0) {
677 /* Empty script, so done */
681 /* A new script to add */
682 *scriptListObj
= Jim_NewListObj(interp
, NULL
, 0);
683 Jim_IncrRefCount(*scriptListObj
);
685 if (Jim_IsShared(script1
)) {
686 script1
= Jim_DuplicateObj(interp
, script1
);
688 Jim_ListAppendElement(interp
, *scriptListObj
, script1
);
691 if (Jim_IsShared(script2
)) {
692 script2
= Jim_DuplicateObj(interp
, script2
);
694 Jim_ListAppendElement(interp
, *scriptListObj
, script2
);
697 Jim_CreateFileHandler(interp
, af
->fp
, mask
,
698 JimAioFileEventHandler
, *scriptListObj
, JimAioFileEventFinalizer
);
703 static int aio_cmd_readable(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
705 AioFile
*af
= Jim_CmdPrivData(interp
);
706 Jim_Obj
*eofScript
= NULL
;
707 int mask
= JIM_EVENT_READABLE
;
711 mask
|= JIM_EVENT_FEOF
;
715 return aio_eventinfo(interp
, af
, mask
, &af
->rEvent
, argc
? argv
[0] : NULL
, eofScript
);
718 static int aio_cmd_writable(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
720 AioFile
*af
= Jim_CmdPrivData(interp
);
721 int mask
= JIM_EVENT_WRITABLE
;
723 return aio_eventinfo(interp
, af
, mask
, &af
->wEvent
, argc
? argv
[0] : NULL
, NULL
);
726 static int aio_cmd_onexception(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
728 AioFile
*af
= Jim_CmdPrivData(interp
);
729 int mask
= JIM_EVENT_EXCEPTION
;
731 return aio_eventinfo(interp
, af
, mask
, &af
->eEvent
, argc
? argv
[0] : NULL
, NULL
);
735 static const jim_subcmd_type aio_command_table
[] = {
737 .args
= "?-nonewline? ?len?",
738 .function
= aio_cmd_read
,
741 .description
= "Read and return bytes from the stream. To eof if no len."
745 .function
= aio_cmd_gets
,
748 .description
= "Read one line and return it or store it in the var"
751 .args
= "?-nonewline? str",
752 .function
= aio_cmd_puts
,
755 .description
= "Write the string, with newline unless -nonewline"
759 .args
= "len ?addrvar?",
760 .function
= aio_cmd_recvfrom
,
763 .description
= "Receive up to 'len' bytes on the socket. Sets 'addrvar' with receive address, if set"
766 .args
= "str address",
767 .function
= aio_cmd_sendto
,
770 .description
= "Send 'str' to the given address (dgram only)"
773 .function
= aio_cmd_accept
,
774 .description
= "Server socket only: Accept a connection and return stream"
778 .function
= aio_cmd_flush
,
779 .description
= "Flush the stream"
782 .function
= aio_cmd_eof
,
783 .description
= "Returns 1 if stream is at eof"
786 .flags
= JIM_MODFLAG_FULLARGV
,
787 .function
= aio_cmd_close
,
788 .description
= "Closes the stream"
791 .args
= "offset ?start|current|end",
792 .function
= aio_cmd_seek
,
795 .description
= "Seeks in the stream (default 'current')"
798 .function
= aio_cmd_tell
,
799 .description
= "Returns the current seek position"
804 .function
= aio_cmd_ndelay
,
807 .description
= "Set O_NDELAY (if arg). Returns current/new setting."
810 #ifdef jim_ext_eventloop
812 .args
= "?readable-script ?eof-script??",
815 .function
= aio_cmd_readable
,
816 .description
= "Returns script, or invoke readable-script when readable, eof-script on eof, {} to remove",
819 .args
= "?writable-script?",
822 .function
= aio_cmd_writable
,
823 .description
= "Returns script, or invoke writable-script when writable, {} to remove",
825 { .cmd
= "onexception",
826 .args
= "?exception-script?",
829 .function
= aio_cmd_onexception
,
830 .description
= "Returns script, or invoke exception-script when oob data, {} to remove",
836 static int JimAioSubCmdProc(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
838 return Jim_CallSubCmd(interp
, Jim_ParseSubCmd(interp
, aio_command_table
, argc
, argv
), argc
, argv
);
841 static int JimAioOpenCommand(Jim_Interp
*interp
, int argc
,
842 Jim_Obj
*const *argv
)
846 char buf
[AIO_CMD_LEN
];
851 if (argc
!= 2 && argc
!= 3) {
852 Jim_WrongNumArgs(interp
, 1, argv
, "filename ?mode?");
855 cmdname
= Jim_GetString(argv
[1], NULL
);
856 if (Jim_CompareStringImmediate(interp
, argv
[1], "stdin")) {
857 OpenFlags
|= AIO_KEEPOPEN
;
860 else if (Jim_CompareStringImmediate(interp
, argv
[1], "stdout")) {
861 OpenFlags
|= AIO_KEEPOPEN
;
864 else if (Jim_CompareStringImmediate(interp
, argv
[1], "stderr")) {
865 OpenFlags
|= AIO_KEEPOPEN
;
869 const char *mode
= "r";
872 mode
= Jim_GetString(argv
[2], NULL
);
874 fp
= fopen(Jim_GetString(argv
[1], NULL
), mode
);
876 JimAioSetError(interp
, argv
[1]);
879 /* Get the next file id */
880 fileId
= Jim_GetId(interp
);
881 sprintf(buf
, "aio.handle%ld", fileId
);
885 /* Create the file command */
886 af
= Jim_Alloc(sizeof(*af
));
890 af
->flags
= fcntl(af
->fd
, F_GETFL
);
892 af
->filename
= argv
[1];
893 Jim_IncrRefCount(af
->filename
);
894 af
->OpenFlags
= OpenFlags
;
898 Jim_CreateCommand(interp
, cmdname
, JimAioSubCmdProc
, af
, JimAioDelProc
);
899 Jim_SetResultString(interp
, cmdname
, -1);
906 * Creates a channel for fd.
908 * hdlfmt is a sprintf format for the filehandle. Anything with %ld at the end will do.
909 * mode is usual "r+", but may be another fdopen() mode as required.
911 * Creates the command and lappends the name of the command to the current result.
914 static int JimMakeChannel(Jim_Interp
*interp
, Jim_Obj
*filename
, const char *hdlfmt
, int fd
, int family
,
919 char buf
[AIO_CMD_LEN
];
921 FILE *fp
= fdopen(fd
, mode
);
925 JimAioSetError(interp
, NULL
);
929 /* Get the next file id */
930 fileId
= Jim_GetId(interp
);
932 /* Create the file command */
933 af
= Jim_Alloc(sizeof(*af
));
937 af
->filename
= filename
;
938 Jim_IncrRefCount(af
->filename
);
940 af
->flags
= fcntl(af
->fd
, F_GETFL
);
945 af
->addr_family
= family
;
946 sprintf(buf
, hdlfmt
, fileId
);
947 Jim_CreateCommand(interp
, buf
, JimAioSubCmdProc
, af
, JimAioDelProc
);
949 Jim_ListAppendElement(interp
, Jim_GetResult(interp
), Jim_NewStringObj(interp
, buf
, -1));
954 static int JimAioSockCommand(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
956 const char *hdlfmt
= "aio.unknown%ld";
957 const char *socktypes
[] = {
983 const char *hostportarg
= NULL
;
986 const char *mode
= "r+";
987 int family
= AF_INET
;
988 Jim_Obj
*argv0
= argv
[0];
991 if (argc
> 1 && Jim_CompareStringImmediate(interp
, argv
[1], "-ipv6")) {
993 Jim_SetResultString(interp
, "ipv6 not supported", -1);
1004 Jim_WrongNumArgs(interp
, 1, &argv0
, "?-ipv6? type ?address?");
1008 if (Jim_GetEnum(interp
, argv
[1], socktypes
, &socktype
, "socket type", JIM_ERRMSG
) != JIM_OK
)
1011 Jim_SetResultString(interp
, "", 0);
1013 hdlfmt
= "aio.sock%ld";
1016 case SOCK_DGRAM_CLIENT
:
1018 /* No address, so an unconnected dgram socket */
1019 sock
= socket(family
, SOCK_DGRAM
, 0);
1021 JimAioSetError(interp
, NULL
);
1027 case SOCK_STREAM_CLIENT
:
1029 union sockaddr_any sa
;
1036 hostportarg
= Jim_GetString(argv
[2], NULL
);
1039 if (JimParseIPv6Address(interp
, hostportarg
, &sa
, &salen
) != JIM_OK
) {
1043 else if (JimParseIpAddress(interp
, hostportarg
, &sa
, &salen
) != JIM_OK
) {
1046 sock
= socket(family
, (socktype
== SOCK_DGRAM_CLIENT
) ? SOCK_DGRAM
: SOCK_STREAM
, 0);
1048 JimAioSetError(interp
, NULL
);
1051 res
= connect(sock
, &sa
.sa
, salen
);
1054 JimAioSetError(interp
, argv
[2]);
1061 case SOCK_STREAM_SERVER
:
1062 case SOCK_DGRAM_SERVER
:
1064 union sockaddr_any sa
;
1071 hostportarg
= Jim_GetString(argv
[2], NULL
);
1074 if (JimParseIPv6Address(interp
, hostportarg
, &sa
, &salen
) != JIM_OK
) {
1078 else if (JimParseIpAddress(interp
, hostportarg
, &sa
, &salen
) != JIM_OK
) {
1081 sock
= socket(family
, (socktype
== SOCK_DGRAM_SERVER
) ? SOCK_DGRAM
: SOCK_STREAM
, 0);
1083 JimAioSetError(interp
, NULL
);
1087 /* Enable address reuse */
1088 setsockopt(sock
, SOL_SOCKET
, SO_REUSEADDR
, &on
, sizeof(on
));
1090 res
= bind(sock
, &sa
.sa
, salen
);
1092 JimAioSetError(interp
, argv
[2]);
1096 if (socktype
== SOCK_STREAM_SERVER
) {
1097 res
= listen(sock
, 5);
1099 JimAioSetError(interp
, NULL
);
1104 hdlfmt
= "aio.socksrv%ld";
1110 struct sockaddr_un sa
;
1113 if (argc
!= 3 || ipv6
) {
1117 if (JimParseDomainAddress(interp
, hostportarg
, &sa
) != JIM_OK
) {
1118 JimAioSetError(interp
, argv
[2]);
1122 sock
= socket(PF_UNIX
, SOCK_STREAM
, 0);
1124 JimAioSetError(interp
, NULL
);
1127 len
= strlen(sa
.sun_path
) + 1 + sizeof(sa
.sun_family
);
1128 res
= connect(sock
, (struct sockaddr
*)&sa
, len
);
1130 JimAioSetError(interp
, argv
[2]);
1134 hdlfmt
= "aio.sockunix%ld";
1138 case SOCK_UNIX_SERVER
:
1140 struct sockaddr_un sa
;
1143 if (argc
!= 3 || ipv6
) {
1147 if (JimParseDomainAddress(interp
, hostportarg
, &sa
) != JIM_OK
) {
1148 JimAioSetError(interp
, argv
[2]);
1152 sock
= socket(PF_UNIX
, SOCK_STREAM
, 0);
1154 JimAioSetError(interp
, NULL
);
1157 len
= strlen(sa
.sun_path
) + 1 + sizeof(sa
.sun_family
);
1158 res
= bind(sock
, (struct sockaddr
*)&sa
, len
);
1160 JimAioSetError(interp
, argv
[2]);
1164 res
= listen(sock
, 5);
1166 JimAioSetError(interp
, NULL
);
1170 hdlfmt
= "aio.sockunixsrv%ld";
1174 case SOCK_STREAM_PIPE
:
1178 if (argc
!= 2 || ipv6
) {
1183 JimAioSetError(interp
, NULL
);
1187 hdlfmt
= "aio.pipe%ld";
1188 if (JimMakeChannel(interp
, argv
[1], hdlfmt
, p
[0], family
, "r") != JIM_OK
) {
1191 JimAioSetError(interp
, NULL
);
1194 /* Note, if this fails it will leave p[0] open, but this should never happen */
1201 Jim_SetResultString(interp
, "Unsupported socket type", -1);
1205 return JimMakeChannel(interp
, argv
[1], hdlfmt
, sock
, family
, mode
);
1209 FILE *Jim_AioFilehandle(Jim_Interp
*interp
, Jim_Obj
*command
)
1211 Jim_Cmd
*cmdPtr
= Jim_GetCommand(interp
, command
, JIM_ERRMSG
);
1213 if (cmdPtr
&& cmdPtr
->cmdProc
== JimAioSubCmdProc
) {
1214 return ((AioFile
*) cmdPtr
->privData
)->fp
;
1219 #ifdef JIM_TCL_COMPAT
1220 static int JimAioTclCmd(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1222 Jim_Obj
*newargv
[4];
1227 if (argc
> 1 && Jim_CompareStringImmediate(interp
, argv
[1], "-nonewline")) {
1230 if (argc
< 2 + nonewline
|| argc
> 4) {
1231 Jim_WrongNumArgs(interp
, 1, argv
, "channel");
1236 /* read -nonewline $f ... => $f read -nonewline ... */
1237 newargv
[0] = argv
[2];
1238 newargv
[1] = argv
[0];
1239 newargv
[2] = argv
[1];
1242 /* cmd $f ... => $f cmd ... */
1243 newargv
[0] = argv
[1];
1244 newargv
[1] = argv
[0];
1247 for (i
= 2 + nonewline
; i
< argc
; i
++) {
1248 newargv
[i
] = argv
[i
];
1251 ret
= Jim_EvalObjVector(interp
, argc
, newargv
);
1256 static int JimAioPutsCmd(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1258 Jim_Obj
*newargv
[4];
1263 if (argc
> 1 && Jim_CompareStringImmediate(interp
, argv
[1], "-nonewline")) {
1267 if (argc
< 2 + nonewline
|| argc
> 3 + nonewline
) {
1268 Jim_WrongNumArgs(interp
, 1, argv
, "?-nonewline? ?channel? string");
1273 newargv
[off
++] = argv
[0];
1276 newargv
[off
++] = argv
[1];
1282 /* Missing channel, so use stdout */
1283 newargv
[0] = Jim_NewStringObj(interp
, "stdout", -1);
1284 newargv
[off
++] = argv
[1];
1287 newargv
[0] = argv
[1];
1288 newargv
[off
++] = argv
[2];
1291 return Jim_EvalObjVector(interp
, off
, newargv
);
1294 static void JimAioTclCompat(Jim_Interp
*interp
)
1296 static const char *tclcmds
[] = { "read", "gets", "flush", "close", "eof", "seek", "tell", 0 };
1299 for (i
= 0; tclcmds
[i
]; i
++) {
1300 Jim_CreateCommand(interp
, tclcmds
[i
], JimAioTclCmd
, NULL
, NULL
);
1302 Jim_CreateCommand(interp
, "puts", JimAioPutsCmd
, NULL
, NULL
);
1306 int Jim_aioInit(Jim_Interp
*interp
)
1308 Jim_CreateCommand(interp
, "open", JimAioOpenCommand
, NULL
, NULL
);
1310 Jim_CreateCommand(interp
, "socket", JimAioSockCommand
, NULL
, NULL
);
1313 /* Takeover stdin, stdout and stderr */
1314 Jim_EvalGlobal(interp
, "open stdin; open stdout; open stderr");
1316 #ifdef JIM_TCL_COMPAT
1317 JimAioTclCompat(interp
);