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 - Ø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>
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above
19 * copyright notice, this list of conditions and the following
20 * disclaimer in the documentation and/or other materials
21 * provided with the distribution.
23 * THIS SOFTWARE IS PROVIDED BY THE JIM TCL PROJECT ``AS IS'' AND ANY
24 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
25 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
26 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27 * JIM TCL PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
28 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
32 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
34 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 * The views and conclusions contained in the software and documentation
37 * are those of the authors and should not be interpreted as representing
38 * official policies, either expressed or implied, of the Jim Tcl Project.
48 #include "jimautoconf.h"
50 #if defined(HAVE_SYS_SOCKET_H) && defined(HAVE_SELECT) && defined(HAVE_NETINET_IN_H) && defined(HAVE_NETDB_H) && defined(HAVE_ARPA_INET_H)
51 #include <sys/socket.h>
52 #include <netinet/in.h>
53 #include <arpa/inet.h>
62 #include "jim-eventloop.h"
63 #include "jim-subcmd.h"
65 #define AIO_CMD_LEN 32 /* e.g. aio.handleXXXXXX */
66 #define AIO_BUF_LEN 256 /* Can keep this small and rely on stdio buffering */
68 #define AIO_KEEPOPEN 1
79 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
82 struct sockaddr_in sin
;
84 struct sockaddr_in6 sin6
;
88 #ifndef HAVE_INET_NTOP
89 const char *inet_ntop(int af
, const void *src
, char *dst
, int size
)
94 snprintf(dst
, size
, "%s", inet_ntoa(((struct sockaddr_in
*)src
)->sin_addr
));
98 #endif /* JIM_BOOTSTRAP */
100 typedef struct AioFile
105 int OpenFlags
; /* AIO_KEEPOPEN? keep FILE* */
116 static int JimAioSubCmdProc(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
);
117 static int JimMakeChannel(Jim_Interp
*interp
, FILE *fh
, int fd
, Jim_Obj
*filename
,
118 const char *hdlfmt
, int family
, const char *mode
);
120 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
121 static int JimParseIPv6Address(Jim_Interp
*interp
, const char *hostport
, union sockaddr_any
*sa
, int *salen
)
125 * An IPv6 addr/port looks like:
128 * [fe80::223:6cff:fe95:bdc0%en1]:2000
132 * Note that the "any" address is ::, which is the same as when no address is specified.
140 stport
= strrchr(hostport
, ':');
142 /* No : so, the whole thing is the port */
145 sthost
= Jim_StrDup(hostport
);
151 if (*hostport
== '[') {
152 /* This is a numeric ipv6 address */
153 char *pt
= strchr(++hostport
, ']');
155 sthost
= Jim_StrDupLen(hostport
, pt
- hostport
);
160 sthost
= Jim_StrDupLen(hostport
, stport
- hostport
- 1);
163 memset(&req
, '\0', sizeof(req
));
164 req
.ai_family
= PF_INET6
;
166 if (getaddrinfo(sthost
, NULL
, &req
, &ai
)) {
167 Jim_SetResultFormatted(interp
, "Not a valid address: %s", hostport
);
171 memcpy(&sa
->sin
, ai
->ai_addr
, ai
->ai_addrlen
);
172 *salen
= ai
->ai_addrlen
;
174 sa
->sin
.sin_port
= htons(atoi(stport
));
182 Jim_SetResultString(interp
, "ipv6 not supported", -1);
187 static int JimParseIpAddress(Jim_Interp
*interp
, const char *hostport
, union sockaddr_any
*sa
, int *salen
)
189 /* An IPv4 addr/port looks like:
194 * If the address is missing, INADDR_ANY is used.
195 * If the port is missing, 0 is used (only useful for server sockets).
201 stport
= strrchr(hostport
, ':');
203 /* No : so, the whole thing is the port */
205 sthost
= Jim_StrDup("0.0.0.0");
208 sthost
= Jim_StrDupLen(hostport
, stport
- hostport
);
213 #ifdef HAVE_GETADDRINFO
216 memset(&req
, '\0', sizeof(req
));
217 req
.ai_family
= PF_INET
;
219 if (getaddrinfo(sthost
, NULL
, &req
, &ai
)) {
223 memcpy(&sa
->sin
, ai
->ai_addr
, ai
->ai_addrlen
);
224 *salen
= ai
->ai_addrlen
;
232 if ((he
= gethostbyname(sthost
)) != NULL
) {
233 if (he
->h_length
== sizeof(sa
->sin
.sin_addr
)) {
234 *salen
= sizeof(sa
->sin
);
235 sa
->sin
.sin_family
= he
->h_addrtype
;
236 memcpy(&sa
->sin
.sin_addr
, he
->h_addr
, he
->h_length
); /* set address */
242 sa
->sin
.sin_port
= htons(atoi(stport
));
247 Jim_SetResultFormatted(interp
, "Not a valid address: %s", hostport
);
254 static int JimParseDomainAddress(Jim_Interp
*interp
, const char *path
, struct sockaddr_un
*sa
)
256 sa
->sun_family
= PF_UNIX
;
257 snprintf(sa
->sun_path
, sizeof(sa
->sun_path
), "%s", path
);
262 #endif /* JIM_BOOTSTRAP */
264 static void JimAioSetError(Jim_Interp
*interp
, Jim_Obj
*name
)
267 Jim_SetResultFormatted(interp
, "%#s: %s", name
, strerror(errno
));
270 Jim_SetResultString(interp
, strerror(errno
), -1);
274 static void JimAioDelProc(Jim_Interp
*interp
, void *privData
)
276 AioFile
*af
= privData
;
280 Jim_DecrRefCount(interp
, af
->filename
);
282 if (!(af
->OpenFlags
& AIO_KEEPOPEN
)) {
285 #ifdef jim_ext_eventloop
286 /* remove existing EventHandlers */
288 Jim_DeleteFileHandler(interp
, af
->fp
);
291 Jim_DeleteFileHandler(interp
, af
->fp
);
294 Jim_DeleteFileHandler(interp
, af
->fp
);
300 static int aio_cmd_read(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
302 AioFile
*af
= Jim_CmdPrivData(interp
);
303 char buf
[AIO_BUF_LEN
];
306 int neededLen
= -1; /* -1 is "read as much as possible" */
308 if (argc
&& Jim_CompareStringImmediate(interp
, argv
[0], "-nonewline")) {
316 if (Jim_GetWide(interp
, argv
[0], &wideValue
) != JIM_OK
)
319 Jim_SetResultString(interp
, "invalid parameter: negative len", -1);
322 neededLen
= (int)wideValue
;
327 objPtr
= Jim_NewStringObj(interp
, NULL
, 0);
328 while (neededLen
!= 0) {
332 if (neededLen
== -1) {
333 readlen
= AIO_BUF_LEN
;
336 readlen
= (neededLen
> AIO_BUF_LEN
? AIO_BUF_LEN
: neededLen
);
338 retval
= fread(buf
, 1, readlen
, af
->fp
);
340 Jim_AppendString(interp
, objPtr
, buf
, retval
);
341 if (neededLen
!= -1) {
345 if (retval
!= readlen
)
348 /* Check for error conditions */
349 if (ferror(af
->fp
)) {
351 /* eof and EAGAIN are not error conditions */
352 if (!feof(af
->fp
) && errno
!= EAGAIN
) {
354 Jim_FreeNewObj(interp
, objPtr
);
355 JimAioSetError(interp
, af
->filename
);
361 const char *s
= Jim_GetString(objPtr
, &len
);
363 if (len
> 0 && s
[len
- 1] == '\n') {
365 objPtr
->bytes
[objPtr
->length
] = '\0';
368 Jim_SetResult(interp
, objPtr
);
372 static int aio_cmd_copy(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
374 AioFile
*af
= Jim_CmdPrivData(interp
);
376 long maxlen
= LONG_MAX
;
377 FILE *outfh
= Jim_AioFilehandle(interp
, argv
[0]);
384 if (Jim_GetLong(interp
, argv
[1], &maxlen
) != JIM_OK
) {
389 while (count
< maxlen
) {
390 int ch
= fgetc(af
->fp
);
392 if (ch
== EOF
|| fputc(ch
, outfh
) == EOF
) {
398 if (ferror(af
->fp
)) {
399 Jim_SetResultFormatted(interp
, "error while reading: %s", strerror(errno
));
405 Jim_SetResultFormatted(interp
, "error while writing: %s", strerror(errno
));
410 Jim_SetResultInt(interp
, count
);
415 static int aio_cmd_gets(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
417 AioFile
*af
= Jim_CmdPrivData(interp
);
418 char buf
[AIO_BUF_LEN
];
424 objPtr
= Jim_NewStringObj(interp
, NULL
, 0);
426 buf
[AIO_BUF_LEN
- 1] = '_';
427 if (fgets(buf
, AIO_BUF_LEN
, af
->fp
) == NULL
)
430 if (buf
[AIO_BUF_LEN
- 1] == '\0' && buf
[AIO_BUF_LEN
- 2] != '\n') {
431 Jim_AppendString(interp
, objPtr
, buf
, AIO_BUF_LEN
- 1);
436 if (len
&& (buf
[len
- 1] == '\n')) {
441 Jim_AppendString(interp
, objPtr
, buf
, len
);
445 if (ferror(af
->fp
) && errno
!= EAGAIN
&& errno
!= EINTR
) {
447 Jim_FreeNewObj(interp
, objPtr
);
448 JimAioSetError(interp
, af
->filename
);
454 if (Jim_SetVariable(interp
, argv
[0], objPtr
) != JIM_OK
) {
455 Jim_FreeNewObj(interp
, objPtr
);
459 len
= Jim_Length(objPtr
);
461 if (len
== 0 && feof(af
->fp
)) {
462 /* On EOF returns -1 if varName was specified */
465 Jim_SetResultInt(interp
, len
);
468 Jim_SetResult(interp
, objPtr
);
473 static int aio_cmd_puts(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
475 AioFile
*af
= Jim_CmdPrivData(interp
);
481 if (!Jim_CompareStringImmediate(interp
, argv
[0], "-nonewline")) {
490 wdata
= Jim_GetString(strObj
, &wlen
);
491 if (fwrite(wdata
, 1, wlen
, af
->fp
) == (unsigned)wlen
) {
492 if (argc
== 2 || putc('\n', af
->fp
) != EOF
) {
496 JimAioSetError(interp
, af
->filename
);
500 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
501 static int aio_cmd_recvfrom(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
503 AioFile
*af
= Jim_CmdPrivData(interp
);
505 union sockaddr_any sa
;
507 socklen_t salen
= sizeof(sa
);
510 if (Jim_GetLong(interp
, argv
[0], &len
) != JIM_OK
) {
514 buf
= Jim_Alloc(len
+ 1);
516 rlen
= recvfrom(fileno(af
->fp
), buf
, len
, 0, &sa
.sa
, &salen
);
519 JimAioSetError(interp
, NULL
);
523 Jim_SetResult(interp
, Jim_NewStringObjNoAlloc(interp
, buf
, rlen
));
526 /* INET6_ADDRSTRLEN is 46. Add some for [] and port */
530 if (sa
.sa
.sa_family
== PF_INET6
) {
532 /* Allow 9 for []:65535\0 */
533 inet_ntop(sa
.sa
.sa_family
, &sa
.sin6
.sin6_addr
, addrbuf
+ 1, sizeof(addrbuf
) - 9);
534 snprintf(addrbuf
+ strlen(addrbuf
), 8, "]:%d", ntohs(sa
.sin
.sin_port
));
539 /* Allow 7 for :65535\0 */
540 inet_ntop(sa
.sa
.sa_family
, &sa
.sin
.sin_addr
, addrbuf
, sizeof(addrbuf
) - 7);
541 snprintf(addrbuf
+ strlen(addrbuf
), 7, ":%d", ntohs(sa
.sin
.sin_port
));
544 if (Jim_SetVariable(interp
, argv
[1], Jim_NewStringObj(interp
, addrbuf
, -1)) != JIM_OK
) {
553 static int aio_cmd_sendto(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
555 AioFile
*af
= Jim_CmdPrivData(interp
);
559 union sockaddr_any sa
;
560 const char *addr
= Jim_String(argv
[1]);
563 if (IPV6
&& af
->addr_family
== PF_INET6
) {
564 if (JimParseIPv6Address(interp
, addr
, &sa
, &salen
) != JIM_OK
) {
568 else if (JimParseIpAddress(interp
, addr
, &sa
, &salen
) != JIM_OK
) {
571 wdata
= Jim_GetString(argv
[0], &wlen
);
573 /* Note that we don't validate the socket type. Rely on sendto() failing if appropriate */
574 len
= sendto(fileno(af
->fp
), wdata
, wlen
, 0, &sa
.sa
, salen
);
576 JimAioSetError(interp
, NULL
);
579 Jim_SetResultInt(interp
, len
);
583 static int aio_cmd_accept(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
585 AioFile
*af
= Jim_CmdPrivData(interp
);
587 union sockaddr_any sa
;
588 socklen_t addrlen
= sizeof(sa
);
590 sock
= accept(af
->fd
, &sa
.sa
, &addrlen
);
592 JimAioSetError(interp
, NULL
);
596 /* Create the file command */
597 return JimMakeChannel(interp
, NULL
, sock
, Jim_NewStringObj(interp
, "accept", -1),
598 "aio.sockstream%ld", af
->addr_family
, "r+");
601 static int aio_cmd_listen(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
603 AioFile
*af
= Jim_CmdPrivData(interp
);
606 if (Jim_GetLong(interp
, argv
[0], &backlog
) != JIM_OK
) {
610 if (listen(af
->fd
, backlog
)) {
611 JimAioSetError(interp
, NULL
);
617 #endif /* JIM_BOOTSTRAP */
619 static int aio_cmd_flush(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
621 AioFile
*af
= Jim_CmdPrivData(interp
);
623 if (fflush(af
->fp
) == EOF
) {
624 JimAioSetError(interp
, af
->filename
);
630 static int aio_cmd_eof(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
632 AioFile
*af
= Jim_CmdPrivData(interp
);
634 Jim_SetResultInt(interp
, feof(af
->fp
));
638 static int aio_cmd_close(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
640 Jim_DeleteCommand(interp
, Jim_String(argv
[0]));
644 static int aio_cmd_seek(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
646 AioFile
*af
= Jim_CmdPrivData(interp
);
651 if (Jim_CompareStringImmediate(interp
, argv
[1], "start"))
653 else if (Jim_CompareStringImmediate(interp
, argv
[1], "current"))
655 else if (Jim_CompareStringImmediate(interp
, argv
[1], "end"))
661 if (Jim_GetLong(interp
, argv
[0], &offset
) != JIM_OK
) {
664 if (fseek(af
->fp
, offset
, orig
) == -1) {
665 JimAioSetError(interp
, af
->filename
);
671 static int aio_cmd_tell(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
673 AioFile
*af
= Jim_CmdPrivData(interp
);
675 Jim_SetResultInt(interp
, ftell(af
->fp
));
679 static int aio_cmd_filename(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
681 AioFile
*af
= Jim_CmdPrivData(interp
);
683 Jim_SetResult(interp
, af
->filename
);
688 static int aio_cmd_ndelay(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
690 AioFile
*af
= Jim_CmdPrivData(interp
);
692 int fmode
= af
->flags
;
697 if (Jim_GetLong(interp
, argv
[0], &nb
) != JIM_OK
) {
706 fcntl(af
->fd
, F_SETFL
, fmode
);
709 Jim_SetResultInt(interp
, (fmode
& O_NONBLOCK
) ? 1 : 0);
714 static int aio_cmd_buffering(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
716 AioFile
*af
= Jim_CmdPrivData(interp
);
718 static const char * const options
[] = {
732 if (Jim_GetEnum(interp
, argv
[0], options
, &option
, NULL
, JIM_ERRMSG
) != JIM_OK
) {
737 setvbuf(af
->fp
, NULL
, _IONBF
, 0);
740 setvbuf(af
->fp
, NULL
, _IOLBF
, BUFSIZ
);
743 setvbuf(af
->fp
, NULL
, _IOFBF
, BUFSIZ
);
749 #ifdef jim_ext_eventloop
750 static void JimAioFileEventFinalizer(Jim_Interp
*interp
, void *clientData
)
752 Jim_Obj
*objPtr
= clientData
;
754 Jim_DecrRefCount(interp
, objPtr
);
757 static int JimAioFileEventHandler(Jim_Interp
*interp
, void *clientData
, int mask
)
759 Jim_Obj
*objPtr
= clientData
;
761 return Jim_EvalObjBackground(interp
, objPtr
);
764 static int aio_eventinfo(Jim_Interp
*interp
, AioFile
* af
, unsigned mask
, Jim_Obj
**scriptHandlerObj
,
765 int argc
, Jim_Obj
* const *argv
)
770 /* Return current script */
771 if (*scriptHandlerObj
) {
772 Jim_SetResult(interp
, *scriptHandlerObj
);
777 if (*scriptHandlerObj
) {
778 /* Delete old handler */
779 Jim_DeleteFileHandler(interp
, af
->fp
);
780 *scriptHandlerObj
= NULL
;
783 /* Now possibly add the new script(s) */
784 Jim_GetString(argv
[0], &scriptlen
);
785 if (scriptlen
== 0) {
786 /* Empty script, so done */
790 /* A new script to add */
791 Jim_IncrRefCount(argv
[0]);
792 *scriptHandlerObj
= argv
[0];
794 Jim_CreateFileHandler(interp
, af
->fp
, mask
,
795 JimAioFileEventHandler
, *scriptHandlerObj
, JimAioFileEventFinalizer
);
800 static int aio_cmd_readable(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
802 AioFile
*af
= Jim_CmdPrivData(interp
);
804 return aio_eventinfo(interp
, af
, JIM_EVENT_READABLE
, &af
->rEvent
, argc
, argv
);
807 static int aio_cmd_writable(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
809 AioFile
*af
= Jim_CmdPrivData(interp
);
811 return aio_eventinfo(interp
, af
, JIM_EVENT_WRITABLE
, &af
->wEvent
, argc
, argv
);
814 static int aio_cmd_onexception(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
816 AioFile
*af
= Jim_CmdPrivData(interp
);
818 return aio_eventinfo(interp
, af
, JIM_EVENT_EXCEPTION
, &af
->wEvent
, argc
, argv
);
822 static const jim_subcmd_type aio_command_table
[] = {
824 .args
= "?-nonewline? ?len?",
825 .function
= aio_cmd_read
,
828 .description
= "Read and return bytes from the stream. To eof if no len."
831 .args
= "handle ?size?",
832 .function
= aio_cmd_copy
,
835 .description
= "Copy up to 'size' bytes to the given filehandle, or to eof if no size."
839 .function
= aio_cmd_gets
,
842 .description
= "Read one line and return it or store it in the var"
845 .args
= "?-nonewline? str",
846 .function
= aio_cmd_puts
,
849 .description
= "Write the string, with newline unless -nonewline"
851 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
853 .args
= "len ?addrvar?",
854 .function
= aio_cmd_recvfrom
,
857 .description
= "Receive up to 'len' bytes on the socket. Sets 'addrvar' with receive address, if set"
860 .args
= "str address",
861 .function
= aio_cmd_sendto
,
864 .description
= "Send 'str' to the given address (dgram only)"
867 .function
= aio_cmd_accept
,
868 .description
= "Server socket only: Accept a connection and return stream"
872 .function
= aio_cmd_listen
,
875 .description
= "Set the listen backlog for server socket"
877 #endif /* JIM_BOOTSTRAP */
879 .function
= aio_cmd_flush
,
880 .description
= "Flush the stream"
883 .function
= aio_cmd_eof
,
884 .description
= "Returns 1 if stream is at eof"
887 .flags
= JIM_MODFLAG_FULLARGV
,
888 .function
= aio_cmd_close
,
889 .description
= "Closes the stream"
892 .args
= "offset ?start|current|end",
893 .function
= aio_cmd_seek
,
896 .description
= "Seeks in the stream (default 'current')"
899 .function
= aio_cmd_tell
,
900 .description
= "Returns the current seek position"
903 .function
= aio_cmd_filename
,
904 .description
= "Returns the original filename"
909 .function
= aio_cmd_ndelay
,
912 .description
= "Set O_NDELAY (if arg). Returns current/new setting."
915 { .cmd
= "buffering",
916 .args
= "none|line|full",
917 .function
= aio_cmd_buffering
,
920 .description
= "Sets buffering"
922 #ifdef jim_ext_eventloop
924 .args
= "?readable-script?",
927 .function
= aio_cmd_readable
,
928 .description
= "Returns script, or invoke readable-script when readable, {} to remove",
931 .args
= "?writable-script?",
934 .function
= aio_cmd_writable
,
935 .description
= "Returns script, or invoke writable-script when writable, {} to remove",
937 { .cmd
= "onexception",
938 .args
= "?exception-script?",
941 .function
= aio_cmd_onexception
,
942 .description
= "Returns script, or invoke exception-script when oob data, {} to remove",
948 static int JimAioSubCmdProc(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
950 return Jim_CallSubCmd(interp
, Jim_ParseSubCmd(interp
, aio_command_table
, argc
, argv
), argc
, argv
);
953 static int JimAioOpenCommand(Jim_Interp
*interp
, int argc
,
954 Jim_Obj
*const *argv
)
960 if (argc
!= 2 && argc
!= 3) {
961 Jim_WrongNumArgs(interp
, 1, argv
, "filename ?mode?");
965 mode
= (argc
== 3) ? Jim_String(argv
[2]) : "r";
966 hdlfmt
= Jim_String(argv
[1]);
967 if (Jim_CompareStringImmediate(interp
, argv
[1], "stdin")) {
970 else if (Jim_CompareStringImmediate(interp
, argv
[1], "stdout")) {
973 else if (Jim_CompareStringImmediate(interp
, argv
[1], "stderr")) {
977 const char *filename
= Jim_String(argv
[1]);
980 #ifdef jim_ext_tclcompat
981 /* If the filename starts with '|', use popen instead */
982 if (*filename
== '|') {
985 evalObj
[0] = Jim_NewStringObj(interp
, "popen", -1);
986 evalObj
[1] = Jim_NewStringObj(interp
, filename
+ 1, -1);
987 evalObj
[2] = Jim_NewStringObj(interp
, mode
, -1);
989 return Jim_EvalObjVector(interp
, 3, evalObj
);
992 hdlfmt
= "aio.handle%ld";
996 /* Create the file command */
997 return JimMakeChannel(interp
, fp
, -1, argv
[1], hdlfmt
, 0, mode
);
1001 * Creates a channel for fh/fd/filename.
1003 * If fh is not NULL, uses that as the channel (and set AIO_KEEPOPEN).
1004 * Otherwise, if fd is >= 0, uses that as the chanel.
1005 * Otherwise opens 'filename' with mode 'mode'.
1007 * hdlfmt is a sprintf format for the filehandle. Anything with %ld at the end will do.
1008 * mode is used for open or fdopen.
1010 * Creates the command and sets the name as the current result.
1012 static int JimMakeChannel(Jim_Interp
*interp
, FILE *fh
, int fd
, Jim_Obj
*filename
,
1013 const char *hdlfmt
, int family
, const char *mode
)
1016 char buf
[AIO_CMD_LEN
];
1019 Jim_IncrRefCount(filename
);
1023 fh
= fopen(Jim_String(filename
), mode
);
1026 fh
= fdopen(fd
, mode
);
1030 OpenFlags
= AIO_KEEPOPEN
;
1034 JimAioSetError(interp
, filename
);
1036 Jim_DecrRefCount(interp
, filename
);
1040 /* Create the file command */
1041 af
= Jim_Alloc(sizeof(*af
));
1042 memset(af
, 0, sizeof(*af
));
1044 af
->fd
= fileno(fh
);
1045 af
->filename
= filename
;
1047 if ((OpenFlags
& AIO_KEEPOPEN
) == 0) {
1048 fcntl(af
->fd
, F_SETFD
, FD_CLOEXEC
);
1049 af
->OpenFlags
= OpenFlags
;
1053 af
->flags
= fcntl(af
->fd
, F_GETFL
);
1055 af
->addr_family
= family
;
1056 snprintf(buf
, sizeof(buf
), hdlfmt
, Jim_GetId(interp
));
1057 Jim_CreateCommand(interp
, buf
, JimAioSubCmdProc
, af
, JimAioDelProc
);
1059 Jim_SetResultString(interp
, buf
, -1);
1064 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
1066 static int JimAioSockCommand(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1068 const char *hdlfmt
= "aio.unknown%ld";
1069 const char *socktypes
[] = {
1090 SOCK_STREAM6_CLIENT
,
1091 SOCK_STREAM6_SERVER
,
1095 const char *hostportarg
= NULL
;
1098 const char *mode
= "r+";
1099 int family
= PF_INET
;
1100 Jim_Obj
*argv0
= argv
[0];
1103 if (argc
> 1 && Jim_CompareStringImmediate(interp
, argv
[1], "-ipv6")) {
1105 Jim_SetResultString(interp
, "ipv6 not supported", -1);
1116 Jim_WrongNumArgs(interp
, 1, &argv0
, "?-ipv6? type ?address?");
1120 if (Jim_GetEnum(interp
, argv
[1], socktypes
, &socktype
, "socket type", JIM_ERRMSG
) != JIM_OK
)
1123 Jim_SetEmptyResult(interp
);
1125 hdlfmt
= "aio.sock%ld";
1128 hostportarg
= Jim_String(argv
[2]);
1132 case SOCK_DGRAM_CLIENT
:
1134 /* No address, so an unconnected dgram socket */
1135 sock
= socket(family
, SOCK_DGRAM
, 0);
1137 JimAioSetError(interp
, NULL
);
1143 case SOCK_STREAM_CLIENT
:
1145 union sockaddr_any sa
;
1153 if (JimParseIPv6Address(interp
, hostportarg
, &sa
, &salen
) != JIM_OK
) {
1157 else if (JimParseIpAddress(interp
, hostportarg
, &sa
, &salen
) != JIM_OK
) {
1160 sock
= socket(family
, (socktype
== SOCK_DGRAM_CLIENT
) ? SOCK_DGRAM
: SOCK_STREAM
, 0);
1162 JimAioSetError(interp
, NULL
);
1165 res
= connect(sock
, &sa
.sa
, salen
);
1167 JimAioSetError(interp
, argv
[2]);
1174 case SOCK_STREAM_SERVER
:
1175 case SOCK_DGRAM_SERVER
:
1177 union sockaddr_any sa
;
1185 if (JimParseIPv6Address(interp
, hostportarg
, &sa
, &salen
) != JIM_OK
) {
1189 else if (JimParseIpAddress(interp
, hostportarg
, &sa
, &salen
) != JIM_OK
) {
1192 sock
= socket(family
, (socktype
== SOCK_DGRAM_SERVER
) ? SOCK_DGRAM
: SOCK_STREAM
, 0);
1194 JimAioSetError(interp
, NULL
);
1198 /* Enable address reuse */
1199 setsockopt(sock
, SOL_SOCKET
, SO_REUSEADDR
, (void *)&on
, sizeof(on
));
1201 res
= bind(sock
, &sa
.sa
, salen
);
1203 JimAioSetError(interp
, argv
[2]);
1207 if (socktype
== SOCK_STREAM_SERVER
) {
1208 res
= listen(sock
, 5);
1210 JimAioSetError(interp
, NULL
);
1215 hdlfmt
= "aio.socksrv%ld";
1219 #ifdef HAVE_SYS_UN_H
1222 struct sockaddr_un sa
;
1225 if (argc
!= 3 || ipv6
) {
1229 if (JimParseDomainAddress(interp
, hostportarg
, &sa
) != JIM_OK
) {
1230 JimAioSetError(interp
, argv
[2]);
1234 sock
= socket(PF_UNIX
, SOCK_STREAM
, 0);
1236 JimAioSetError(interp
, NULL
);
1239 len
= strlen(sa
.sun_path
) + 1 + sizeof(sa
.sun_family
);
1240 res
= connect(sock
, (struct sockaddr
*)&sa
, len
);
1242 JimAioSetError(interp
, argv
[2]);
1246 hdlfmt
= "aio.sockunix%ld";
1250 case SOCK_UNIX_SERVER
:
1252 struct sockaddr_un sa
;
1255 if (argc
!= 3 || ipv6
) {
1259 if (JimParseDomainAddress(interp
, hostportarg
, &sa
) != JIM_OK
) {
1260 JimAioSetError(interp
, argv
[2]);
1264 sock
= socket(PF_UNIX
, SOCK_STREAM
, 0);
1266 JimAioSetError(interp
, NULL
);
1269 len
= strlen(sa
.sun_path
) + 1 + sizeof(sa
.sun_family
);
1270 res
= bind(sock
, (struct sockaddr
*)&sa
, len
);
1272 JimAioSetError(interp
, argv
[2]);
1276 res
= listen(sock
, 5);
1278 JimAioSetError(interp
, NULL
);
1282 hdlfmt
= "aio.sockunixsrv%ld";
1288 case SOCK_STREAM_PIPE
:
1292 if (argc
!= 2 || ipv6
) {
1297 JimAioSetError(interp
, NULL
);
1301 if (JimMakeChannel(interp
, NULL
, p
[0], argv
[1], "aio.pipe%ld", 0, "r") == JIM_OK
) {
1302 Jim_Obj
*objPtr
= Jim_NewListObj(interp
, NULL
, 0);
1303 Jim_ListAppendElement(interp
, objPtr
, Jim_GetResult(interp
));
1305 if (JimMakeChannel(interp
, NULL
, p
[1], argv
[1], "aio.pipe%ld", 0, "w") == JIM_OK
) {
1306 Jim_ListAppendElement(interp
, objPtr
, Jim_GetResult(interp
));
1307 Jim_SetResult(interp
, objPtr
);
1311 /* Can only be here if fdopen() failed */
1314 JimAioSetError(interp
, NULL
);
1320 Jim_SetResultString(interp
, "Unsupported socket type", -1);
1324 return JimMakeChannel(interp
, NULL
, sock
, argv
[1], hdlfmt
, family
, mode
);
1326 #endif /* JIM_BOOTSTRAP */
1328 FILE *Jim_AioFilehandle(Jim_Interp
*interp
, Jim_Obj
*command
)
1330 Jim_Cmd
*cmdPtr
= Jim_GetCommand(interp
, command
, JIM_ERRMSG
);
1332 if (cmdPtr
&& !cmdPtr
->isproc
&& cmdPtr
->u
.native
.cmdProc
== JimAioSubCmdProc
) {
1333 return ((AioFile
*) cmdPtr
->u
.native
.privData
)->fp
;
1335 Jim_SetResultFormatted(interp
, "Not a filehandle: \"%#s\"", command
);
1339 int Jim_aioInit(Jim_Interp
*interp
)
1341 if (Jim_PackageProvide(interp
, "aio", "1.0", JIM_ERRMSG
))
1344 Jim_CreateCommand(interp
, "open", JimAioOpenCommand
, NULL
, NULL
);
1346 Jim_CreateCommand(interp
, "socket", JimAioSockCommand
, NULL
, NULL
);
1349 /* Takeover stdin, stdout and stderr */
1350 Jim_EvalGlobal(interp
, "open stdin; open stdout; open stderr");