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"
52 #if defined(HAVE_SYS_SOCKET_H) && defined(HAVE_SELECT) && defined(HAVE_NETINET_IN_H) && defined(HAVE_NETDB_H) && defined(HAVE_ARPA_INET_H)
53 #include <sys/socket.h>
54 #include <netinet/in.h>
55 #include <arpa/inet.h>
65 #include "jim-eventloop.h"
66 #include "jim-subcmd.h"
68 #define AIO_CMD_LEN 32 /* e.g. aio.handleXXXXXX */
69 #define AIO_BUF_LEN 256 /* Can keep this small and rely on stdio buffering */
78 #define AIO_KEEPOPEN 1
89 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
92 struct sockaddr_in sin
;
94 struct sockaddr_in6 sin6
;
98 #ifndef HAVE_INET_NTOP
99 const char *inet_ntop(int af
, const void *src
, char *dst
, int size
)
104 snprintf(dst
, size
, "%s", inet_ntoa(((struct sockaddr_in
*)src
)->sin_addr
));
108 #endif /* JIM_BOOTSTRAP */
110 typedef struct AioFile
115 int OpenFlags
; /* AIO_KEEPOPEN? keep FILE* */
123 static int JimAioSubCmdProc(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
);
124 static int JimMakeChannel(Jim_Interp
*interp
, FILE *fh
, int fd
, Jim_Obj
*filename
,
125 const char *hdlfmt
, int family
, const char *mode
);
127 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
128 static int JimParseIPv6Address(Jim_Interp
*interp
, const char *hostport
, union sockaddr_any
*sa
, int *salen
)
132 * An IPv6 addr/port looks like:
135 * [fe80::223:6cff:fe95:bdc0%en1]:2000
139 * Note that the "any" address is ::, which is the same as when no address is specified.
147 stport
= strrchr(hostport
, ':');
149 /* No : so, the whole thing is the port */
152 sthost
= Jim_StrDup(hostport
);
158 if (*hostport
== '[') {
159 /* This is a numeric ipv6 address */
160 char *pt
= strchr(++hostport
, ']');
162 sthost
= Jim_StrDupLen(hostport
, pt
- hostport
);
167 sthost
= Jim_StrDupLen(hostport
, stport
- hostport
- 1);
170 memset(&req
, '\0', sizeof(req
));
171 req
.ai_family
= PF_INET6
;
173 if (getaddrinfo(sthost
, NULL
, &req
, &ai
)) {
174 Jim_SetResultFormatted(interp
, "Not a valid address: %s", hostport
);
178 memcpy(&sa
->sin
, ai
->ai_addr
, ai
->ai_addrlen
);
179 *salen
= ai
->ai_addrlen
;
181 sa
->sin
.sin_port
= htons(atoi(stport
));
189 Jim_SetResultString(interp
, "ipv6 not supported", -1);
194 static int JimParseIpAddress(Jim_Interp
*interp
, const char *hostport
, union sockaddr_any
*sa
, int *salen
)
196 /* An IPv4 addr/port looks like:
201 * If the address is missing, INADDR_ANY is used.
202 * If the port is missing, 0 is used (only useful for server sockets).
208 stport
= strrchr(hostport
, ':');
210 /* No : so, the whole thing is the port */
212 sthost
= Jim_StrDup("0.0.0.0");
215 sthost
= Jim_StrDupLen(hostport
, stport
- hostport
);
220 #ifdef HAVE_GETADDRINFO
223 memset(&req
, '\0', sizeof(req
));
224 req
.ai_family
= PF_INET
;
226 if (getaddrinfo(sthost
, NULL
, &req
, &ai
)) {
230 memcpy(&sa
->sin
, ai
->ai_addr
, ai
->ai_addrlen
);
231 *salen
= ai
->ai_addrlen
;
239 if ((he
= gethostbyname(sthost
)) != NULL
) {
240 if (he
->h_length
== sizeof(sa
->sin
.sin_addr
)) {
241 *salen
= sizeof(sa
->sin
);
242 sa
->sin
.sin_family
= he
->h_addrtype
;
243 memcpy(&sa
->sin
.sin_addr
, he
->h_addr
, he
->h_length
); /* set address */
249 sa
->sin
.sin_port
= htons(atoi(stport
));
254 Jim_SetResultFormatted(interp
, "Not a valid address: %s", hostport
);
261 static int JimParseDomainAddress(Jim_Interp
*interp
, const char *path
, struct sockaddr_un
*sa
)
263 sa
->sun_family
= PF_UNIX
;
264 snprintf(sa
->sun_path
, sizeof(sa
->sun_path
), "%s", path
);
269 #endif /* JIM_BOOTSTRAP */
271 static void JimAioSetError(Jim_Interp
*interp
, Jim_Obj
*name
)
274 Jim_SetResultFormatted(interp
, "%#s: %s", name
, strerror(errno
));
277 Jim_SetResultString(interp
, strerror(errno
), -1);
281 static void JimAioDelProc(Jim_Interp
*interp
, void *privData
)
283 AioFile
*af
= privData
;
287 if (!(af
->OpenFlags
& AIO_KEEPOPEN
)) {
291 Jim_DecrRefCount(interp
, af
->filename
);
293 #ifdef jim_ext_eventloop
294 /* remove all existing EventHandlers */
295 Jim_DeleteFileHandler(interp
, af
->fp
, JIM_EVENT_READABLE
| JIM_EVENT_WRITABLE
| JIM_EVENT_EXCEPTION
);
300 static int JimCheckStreamError(Jim_Interp
*interp
, AioFile
*af
)
302 if (!ferror(af
->fp
)) {
306 /* EAGAIN and similar are not error conditions. Just treat them like eof */
307 if (feof(af
->fp
) || errno
== EAGAIN
|| errno
== EINTR
) {
311 if (errno
== ECONNRESET
) {
316 if (errno
!= ECONNABORTED
) {
320 JimAioSetError(interp
, af
->filename
);
324 static int aio_cmd_read(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
326 AioFile
*af
= Jim_CmdPrivData(interp
);
327 char buf
[AIO_BUF_LEN
];
330 jim_wide neededLen
= -1; /* -1 is "read as much as possible" */
332 if (argc
&& Jim_CompareStringImmediate(interp
, argv
[0], "-nonewline")) {
338 if (Jim_GetWide(interp
, argv
[0], &neededLen
) != JIM_OK
)
341 Jim_SetResultString(interp
, "invalid parameter: negative len", -1);
348 objPtr
= Jim_NewStringObj(interp
, NULL
, 0);
349 while (neededLen
!= 0) {
353 if (neededLen
== -1) {
354 readlen
= AIO_BUF_LEN
;
357 readlen
= (neededLen
> AIO_BUF_LEN
? AIO_BUF_LEN
: neededLen
);
359 retval
= fread(buf
, 1, readlen
, af
->fp
);
361 Jim_AppendString(interp
, objPtr
, buf
, retval
);
362 if (neededLen
!= -1) {
366 if (retval
!= readlen
)
369 /* Check for error conditions */
370 if (JimCheckStreamError(interp
, af
)) {
371 Jim_FreeNewObj(interp
, objPtr
);
376 const char *s
= Jim_GetString(objPtr
, &len
);
378 if (len
> 0 && s
[len
- 1] == '\n') {
380 objPtr
->bytes
[objPtr
->length
] = '\0';
383 Jim_SetResult(interp
, objPtr
);
387 static int aio_cmd_copy(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
389 AioFile
*af
= Jim_CmdPrivData(interp
);
391 jim_wide maxlen
= JIM_WIDE_MAX
;
392 FILE *outfh
= Jim_AioFilehandle(interp
, argv
[0]);
399 if (Jim_GetWide(interp
, argv
[1], &maxlen
) != JIM_OK
) {
404 while (count
< maxlen
) {
405 int ch
= fgetc(af
->fp
);
407 if (ch
== EOF
|| fputc(ch
, outfh
) == EOF
) {
413 if (ferror(af
->fp
)) {
414 Jim_SetResultFormatted(interp
, "error while reading: %s", strerror(errno
));
420 Jim_SetResultFormatted(interp
, "error while writing: %s", strerror(errno
));
425 Jim_SetResultInt(interp
, count
);
430 static int aio_cmd_gets(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
432 AioFile
*af
= Jim_CmdPrivData(interp
);
433 char buf
[AIO_BUF_LEN
];
439 objPtr
= Jim_NewStringObj(interp
, NULL
, 0);
441 buf
[AIO_BUF_LEN
- 1] = '_';
442 if (fgets(buf
, AIO_BUF_LEN
, af
->fp
) == NULL
)
445 if (buf
[AIO_BUF_LEN
- 1] == '\0' && buf
[AIO_BUF_LEN
- 2] != '\n') {
446 Jim_AppendString(interp
, objPtr
, buf
, AIO_BUF_LEN
- 1);
451 if (len
&& (buf
[len
- 1] == '\n')) {
456 Jim_AppendString(interp
, objPtr
, buf
, len
);
460 if (JimCheckStreamError(interp
, af
)) {
462 Jim_FreeNewObj(interp
, objPtr
);
467 if (Jim_SetVariable(interp
, argv
[0], objPtr
) != JIM_OK
) {
468 Jim_FreeNewObj(interp
, objPtr
);
472 len
= Jim_Length(objPtr
);
474 if (len
== 0 && feof(af
->fp
)) {
475 /* On EOF returns -1 if varName was specified */
478 Jim_SetResultInt(interp
, len
);
481 Jim_SetResult(interp
, objPtr
);
486 static int aio_cmd_puts(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
488 AioFile
*af
= Jim_CmdPrivData(interp
);
494 if (!Jim_CompareStringImmediate(interp
, argv
[0], "-nonewline")) {
503 wdata
= Jim_GetString(strObj
, &wlen
);
504 if (fwrite(wdata
, 1, wlen
, af
->fp
) == (unsigned)wlen
) {
505 if (argc
== 2 || putc('\n', af
->fp
) != EOF
) {
509 JimAioSetError(interp
, af
->filename
);
513 static int aio_cmd_isatty(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
516 AioFile
*af
= Jim_CmdPrivData(interp
);
517 Jim_SetResultInt(interp
, isatty(fileno(af
->fp
)));
519 Jim_SetResultInt(interp
, 0);
525 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
526 static int aio_cmd_recvfrom(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
528 AioFile
*af
= Jim_CmdPrivData(interp
);
530 union sockaddr_any sa
;
532 socklen_t salen
= sizeof(sa
);
535 if (Jim_GetLong(interp
, argv
[0], &len
) != JIM_OK
) {
539 buf
= Jim_Alloc(len
+ 1);
541 rlen
= recvfrom(fileno(af
->fp
), buf
, len
, 0, &sa
.sa
, &salen
);
544 JimAioSetError(interp
, NULL
);
548 Jim_SetResult(interp
, Jim_NewStringObjNoAlloc(interp
, buf
, rlen
));
551 /* INET6_ADDRSTRLEN is 46. Add some for [] and port */
555 if (sa
.sa
.sa_family
== PF_INET6
) {
557 /* Allow 9 for []:65535\0 */
558 inet_ntop(sa
.sa
.sa_family
, &sa
.sin6
.sin6_addr
, addrbuf
+ 1, sizeof(addrbuf
) - 9);
559 snprintf(addrbuf
+ strlen(addrbuf
), 8, "]:%d", ntohs(sa
.sin
.sin_port
));
563 if (sa
.sa
.sa_family
== PF_INET
) {
564 /* Allow 7 for :65535\0 */
565 inet_ntop(sa
.sa
.sa_family
, &sa
.sin
.sin_addr
, addrbuf
, sizeof(addrbuf
) - 7);
566 snprintf(addrbuf
+ strlen(addrbuf
), 7, ":%d", ntohs(sa
.sin
.sin_port
));
569 /* recvfrom still works on unix domain sockets, etc */
573 if (Jim_SetVariable(interp
, argv
[1], Jim_NewStringObj(interp
, addrbuf
, -1)) != JIM_OK
) {
582 static int aio_cmd_sendto(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
584 AioFile
*af
= Jim_CmdPrivData(interp
);
588 union sockaddr_any sa
;
589 const char *addr
= Jim_String(argv
[1]);
592 if (IPV6
&& af
->addr_family
== PF_INET6
) {
593 if (JimParseIPv6Address(interp
, addr
, &sa
, &salen
) != JIM_OK
) {
597 else if (JimParseIpAddress(interp
, addr
, &sa
, &salen
) != JIM_OK
) {
600 wdata
= Jim_GetString(argv
[0], &wlen
);
602 /* Note that we don't validate the socket type. Rely on sendto() failing if appropriate */
603 len
= sendto(fileno(af
->fp
), wdata
, wlen
, 0, &sa
.sa
, salen
);
605 JimAioSetError(interp
, NULL
);
608 Jim_SetResultInt(interp
, len
);
612 static int aio_cmd_accept(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
614 AioFile
*af
= Jim_CmdPrivData(interp
);
616 union sockaddr_any sa
;
617 socklen_t addrlen
= sizeof(sa
);
619 sock
= accept(af
->fd
, &sa
.sa
, &addrlen
);
621 JimAioSetError(interp
, NULL
);
625 /* Create the file command */
626 return JimMakeChannel(interp
, NULL
, sock
, Jim_NewStringObj(interp
, "accept", -1),
627 "aio.sockstream%ld", af
->addr_family
, "r+");
630 static int aio_cmd_listen(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
632 AioFile
*af
= Jim_CmdPrivData(interp
);
635 if (Jim_GetLong(interp
, argv
[0], &backlog
) != JIM_OK
) {
639 if (listen(af
->fd
, backlog
)) {
640 JimAioSetError(interp
, NULL
);
646 #endif /* JIM_BOOTSTRAP */
648 static int aio_cmd_flush(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
650 AioFile
*af
= Jim_CmdPrivData(interp
);
652 if (fflush(af
->fp
) == EOF
) {
653 JimAioSetError(interp
, af
->filename
);
659 static int aio_cmd_eof(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
661 AioFile
*af
= Jim_CmdPrivData(interp
);
663 Jim_SetResultInt(interp
, feof(af
->fp
));
667 static int aio_cmd_close(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
670 #if !defined(JIM_ANSIC) && defined(HAVE_SHUTDOWN)
671 static const char * const options
[] = { "r", "w", NULL
};
672 enum { OPT_R
, OPT_W
, };
674 AioFile
*af
= Jim_CmdPrivData(interp
);
676 if (Jim_GetEnum(interp
, argv
[2], options
, &option
, NULL
, JIM_ERRMSG
) != JIM_OK
) {
679 if (shutdown(af
->fd
, option
== OPT_R
? SHUT_RD
: SHUT_WR
) == 0) {
682 JimAioSetError(interp
, NULL
);
684 Jim_SetResultString(interp
, "async close not supported", -1);
689 return Jim_DeleteCommand(interp
, Jim_String(argv
[0]));
692 static int aio_cmd_seek(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
694 AioFile
*af
= Jim_CmdPrivData(interp
);
699 if (Jim_CompareStringImmediate(interp
, argv
[1], "start"))
701 else if (Jim_CompareStringImmediate(interp
, argv
[1], "current"))
703 else if (Jim_CompareStringImmediate(interp
, argv
[1], "end"))
709 if (Jim_GetWide(interp
, argv
[0], &offset
) != JIM_OK
) {
712 if (fseeko(af
->fp
, offset
, orig
) == -1) {
713 JimAioSetError(interp
, af
->filename
);
719 static int aio_cmd_tell(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
721 AioFile
*af
= Jim_CmdPrivData(interp
);
723 Jim_SetResultInt(interp
, ftello(af
->fp
));
727 static int aio_cmd_filename(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
729 AioFile
*af
= Jim_CmdPrivData(interp
);
731 Jim_SetResult(interp
, af
->filename
);
736 static int aio_cmd_ndelay(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
738 AioFile
*af
= Jim_CmdPrivData(interp
);
740 int fmode
= fcntl(af
->fd
, F_GETFL
);
745 if (Jim_GetLong(interp
, argv
[0], &nb
) != JIM_OK
) {
754 fcntl(af
->fd
, F_SETFL
, fmode
);
756 Jim_SetResultInt(interp
, (fmode
& O_NONBLOCK
) ? 1 : 0);
761 static int aio_cmd_buffering(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
763 AioFile
*af
= Jim_CmdPrivData(interp
);
765 static const char * const options
[] = {
779 if (Jim_GetEnum(interp
, argv
[0], options
, &option
, NULL
, JIM_ERRMSG
) != JIM_OK
) {
784 setvbuf(af
->fp
, NULL
, _IONBF
, 0);
787 setvbuf(af
->fp
, NULL
, _IOLBF
, BUFSIZ
);
790 setvbuf(af
->fp
, NULL
, _IOFBF
, BUFSIZ
);
796 #ifdef jim_ext_eventloop
797 static void JimAioFileEventFinalizer(Jim_Interp
*interp
, void *clientData
)
799 Jim_Obj
**objPtrPtr
= clientData
;
801 Jim_DecrRefCount(interp
, *objPtrPtr
);
805 static int JimAioFileEventHandler(Jim_Interp
*interp
, void *clientData
, int mask
)
807 Jim_Obj
**objPtrPtr
= clientData
;
809 return Jim_EvalObjBackground(interp
, *objPtrPtr
);
812 static int aio_eventinfo(Jim_Interp
*interp
, AioFile
* af
, unsigned mask
, Jim_Obj
**scriptHandlerObj
,
813 int argc
, Jim_Obj
* const *argv
)
816 /* Return current script */
817 if (*scriptHandlerObj
) {
818 Jim_SetResult(interp
, *scriptHandlerObj
);
823 if (*scriptHandlerObj
) {
824 /* Delete old handler */
825 Jim_DeleteFileHandler(interp
, af
->fp
, mask
);
828 /* Now possibly add the new script(s) */
829 if (Jim_Length(argv
[0]) == 0) {
830 /* Empty script, so done */
834 /* A new script to add */
835 Jim_IncrRefCount(argv
[0]);
836 *scriptHandlerObj
= argv
[0];
838 Jim_CreateFileHandler(interp
, af
->fp
, mask
,
839 JimAioFileEventHandler
, scriptHandlerObj
, JimAioFileEventFinalizer
);
844 static int aio_cmd_readable(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
846 AioFile
*af
= Jim_CmdPrivData(interp
);
848 return aio_eventinfo(interp
, af
, JIM_EVENT_READABLE
, &af
->rEvent
, argc
, argv
);
851 static int aio_cmd_writable(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
853 AioFile
*af
= Jim_CmdPrivData(interp
);
855 return aio_eventinfo(interp
, af
, JIM_EVENT_WRITABLE
, &af
->wEvent
, argc
, argv
);
858 static int aio_cmd_onexception(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
860 AioFile
*af
= Jim_CmdPrivData(interp
);
862 return aio_eventinfo(interp
, af
, JIM_EVENT_EXCEPTION
, &af
->wEvent
, argc
, argv
);
866 static const jim_subcmd_type aio_command_table
[] = {
868 "?-nonewline? ?len?",
872 /* Description: Read and return bytes from the stream. To eof if no len. */
879 /* Description: Copy up to 'size' bytes to the given filehandle, or to eof if no size. */
886 /* Description: Read one line and return it or store it in the var */
893 /* Description: Write the string, with newline unless -nonewline */
900 /* Description: Is the file descriptor a tty? */
902 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
908 /* Description: Receive up to 'len' bytes on the socket. Sets 'addrvar' with receive address, if set */
915 /* Description: Send 'str' to the given address (dgram only) */
922 /* Description: Server socket only: Accept a connection and return stream */
929 /* Description: Set the listen backlog for server socket */
931 #endif /* JIM_BOOTSTRAP */
937 /* Description: Flush the stream */
944 /* Description: Returns 1 if stream is at eof */
951 JIM_MODFLAG_FULLARGV
,
952 /* Description: Closes the stream. */
955 "offset ?start|current|end",
959 /* Description: Seeks in the stream (default 'current') */
966 /* Description: Returns the current seek position */
973 /* Description: Returns the original filename */
981 /* Description: Set O_NDELAY (if arg). Returns current/new setting. */
989 /* Description: Sets buffering */
991 #ifdef jim_ext_eventloop
997 /* Description: Returns script, or invoke readable-script when readable, {} to remove */
1000 "?writable-script?",
1004 /* Description: Returns script, or invoke writable-script when writable, {} to remove */
1007 "?exception-script?",
1008 aio_cmd_onexception
,
1011 /* Description: Returns script, or invoke exception-script when oob data, {} to remove */
1017 static int JimAioSubCmdProc(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1019 return Jim_CallSubCmd(interp
, Jim_ParseSubCmd(interp
, aio_command_table
, argc
, argv
), argc
, argv
);
1022 static int JimAioOpenCommand(Jim_Interp
*interp
, int argc
,
1023 Jim_Obj
*const *argv
)
1026 const char *filename
;
1028 if (argc
!= 2 && argc
!= 3) {
1029 Jim_WrongNumArgs(interp
, 1, argv
, "filename ?mode?");
1033 mode
= (argc
== 3) ? Jim_String(argv
[2]) : "r";
1034 filename
= Jim_String(argv
[1]);
1036 #ifdef jim_ext_tclcompat
1037 /* If the filename starts with '|', use popen instead */
1038 if (*filename
== '|') {
1039 Jim_Obj
*evalObj
[3];
1041 evalObj
[0] = Jim_NewStringObj(interp
, "::popen", -1);
1042 evalObj
[1] = Jim_NewStringObj(interp
, filename
+ 1, -1);
1043 evalObj
[2] = Jim_NewStringObj(interp
, mode
, -1);
1045 return Jim_EvalObjVector(interp
, 3, evalObj
);
1048 return JimMakeChannel(interp
, NULL
, -1, argv
[1], "aio.handle%ld", 0, mode
);
1052 * Creates a channel for fh/fd/filename.
1054 * If fh is not NULL, uses that as the channel (and set AIO_KEEPOPEN).
1055 * Otherwise, if fd is >= 0, uses that as the chanel.
1056 * Otherwise opens 'filename' with mode 'mode'.
1058 * hdlfmt is a sprintf format for the filehandle. Anything with %ld at the end will do.
1059 * mode is used for open or fdopen.
1061 * Creates the command and sets the name as the current result.
1063 static int JimMakeChannel(Jim_Interp
*interp
, FILE *fh
, int fd
, Jim_Obj
*filename
,
1064 const char *hdlfmt
, int family
, const char *mode
)
1067 char buf
[AIO_CMD_LEN
];
1071 filename
= Jim_NewStringObj(interp
, hdlfmt
, -1);
1072 OpenFlags
= AIO_KEEPOPEN
;
1075 Jim_IncrRefCount(filename
);
1078 #if !defined(JIM_ANSIC)
1080 fh
= fdopen(fd
, mode
);
1084 fh
= fopen(Jim_String(filename
), mode
);
1087 JimAioSetError(interp
, filename
);
1088 #if !defined(JIM_ANSIC)
1093 Jim_DecrRefCount(interp
, filename
);
1098 /* Create the file command */
1099 af
= Jim_Alloc(sizeof(*af
));
1100 memset(af
, 0, sizeof(*af
));
1102 af
->fd
= fileno(fh
);
1103 af
->filename
= filename
;
1105 if ((OpenFlags
& AIO_KEEPOPEN
) == 0) {
1106 fcntl(af
->fd
, F_SETFD
, FD_CLOEXEC
);
1109 af
->OpenFlags
= OpenFlags
;
1110 af
->addr_family
= family
;
1111 snprintf(buf
, sizeof(buf
), hdlfmt
, Jim_GetId(interp
));
1112 Jim_CreateCommand(interp
, buf
, JimAioSubCmdProc
, af
, JimAioDelProc
);
1114 /* Note that the command must use the global namespace, even if
1115 * the current namespace is something different
1117 Jim_SetResult(interp
, Jim_MakeGlobalNamespaceName(interp
, Jim_NewStringObj(interp
, buf
, -1)));
1122 static int JimMakeChannelPair(Jim_Interp
*interp
, int p
[2], Jim_Obj
*filename
,
1123 const char *hdlfmt
, int family
, const char *mode
[2])
1125 if (JimMakeChannel(interp
, NULL
, p
[0], filename
, hdlfmt
, family
, mode
[0]) == JIM_OK
) {
1126 Jim_Obj
*objPtr
= Jim_NewListObj(interp
, NULL
, 0);
1127 Jim_ListAppendElement(interp
, objPtr
, Jim_GetResult(interp
));
1129 if (JimMakeChannel(interp
, NULL
, p
[1], filename
, hdlfmt
, family
, mode
[1]) == JIM_OK
) {
1130 Jim_ListAppendElement(interp
, objPtr
, Jim_GetResult(interp
));
1131 Jim_SetResult(interp
, objPtr
);
1136 /* Can only be here if fdopen() failed */
1139 JimAioSetError(interp
, NULL
);
1143 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
1145 static int JimAioSockCommand(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1147 const char *hdlfmt
= "aio.unknown%ld";
1148 const char *socktypes
[] = {
1168 SOCK_STREAM_SOCKETPAIR
,
1172 const char *hostportarg
= NULL
;
1175 const char *mode
= "r+";
1176 int family
= PF_INET
;
1177 Jim_Obj
*argv0
= argv
[0];
1180 if (argc
> 1 && Jim_CompareStringImmediate(interp
, argv
[1], "-ipv6")) {
1182 Jim_SetResultString(interp
, "ipv6 not supported", -1);
1193 Jim_WrongNumArgs(interp
, 1, &argv0
, "?-ipv6? type ?address?");
1197 if (Jim_GetEnum(interp
, argv
[1], socktypes
, &socktype
, "socket type", JIM_ERRMSG
) != JIM_OK
)
1200 Jim_SetEmptyResult(interp
);
1202 hdlfmt
= "aio.sock%ld";
1205 hostportarg
= Jim_String(argv
[2]);
1209 case SOCK_DGRAM_CLIENT
:
1211 /* No address, so an unconnected dgram socket */
1212 sock
= socket(family
, SOCK_DGRAM
, 0);
1214 JimAioSetError(interp
, NULL
);
1220 case SOCK_STREAM_CLIENT
:
1222 union sockaddr_any sa
;
1230 if (JimParseIPv6Address(interp
, hostportarg
, &sa
, &salen
) != JIM_OK
) {
1234 else if (JimParseIpAddress(interp
, hostportarg
, &sa
, &salen
) != JIM_OK
) {
1237 sock
= socket(family
, (socktype
== SOCK_DGRAM_CLIENT
) ? SOCK_DGRAM
: SOCK_STREAM
, 0);
1239 JimAioSetError(interp
, NULL
);
1242 res
= connect(sock
, &sa
.sa
, salen
);
1244 JimAioSetError(interp
, argv
[2]);
1251 case SOCK_STREAM_SERVER
:
1252 case SOCK_DGRAM_SERVER
:
1254 union sockaddr_any sa
;
1262 if (JimParseIPv6Address(interp
, hostportarg
, &sa
, &salen
) != JIM_OK
) {
1266 else if (JimParseIpAddress(interp
, hostportarg
, &sa
, &salen
) != JIM_OK
) {
1269 sock
= socket(family
, (socktype
== SOCK_DGRAM_SERVER
) ? SOCK_DGRAM
: SOCK_STREAM
, 0);
1271 JimAioSetError(interp
, NULL
);
1275 /* Enable address reuse */
1276 setsockopt(sock
, SOL_SOCKET
, SO_REUSEADDR
, (void *)&on
, sizeof(on
));
1278 res
= bind(sock
, &sa
.sa
, salen
);
1280 JimAioSetError(interp
, argv
[2]);
1284 if (socktype
== SOCK_STREAM_SERVER
) {
1285 res
= listen(sock
, 5);
1287 JimAioSetError(interp
, NULL
);
1292 hdlfmt
= "aio.socksrv%ld";
1296 #ifdef HAVE_SYS_UN_H
1299 struct sockaddr_un sa
;
1302 if (argc
!= 3 || ipv6
) {
1306 if (JimParseDomainAddress(interp
, hostportarg
, &sa
) != JIM_OK
) {
1307 JimAioSetError(interp
, argv
[2]);
1311 sock
= socket(PF_UNIX
, SOCK_STREAM
, 0);
1313 JimAioSetError(interp
, NULL
);
1316 len
= strlen(sa
.sun_path
) + 1 + sizeof(sa
.sun_family
);
1317 res
= connect(sock
, (struct sockaddr
*)&sa
, len
);
1319 JimAioSetError(interp
, argv
[2]);
1323 hdlfmt
= "aio.sockunix%ld";
1327 case SOCK_UNIX_SERVER
:
1329 struct sockaddr_un sa
;
1332 if (argc
!= 3 || ipv6
) {
1336 if (JimParseDomainAddress(interp
, hostportarg
, &sa
) != JIM_OK
) {
1337 JimAioSetError(interp
, argv
[2]);
1341 sock
= socket(PF_UNIX
, SOCK_STREAM
, 0);
1343 JimAioSetError(interp
, NULL
);
1346 len
= strlen(sa
.sun_path
) + 1 + sizeof(sa
.sun_family
);
1347 res
= bind(sock
, (struct sockaddr
*)&sa
, len
);
1349 JimAioSetError(interp
, argv
[2]);
1353 res
= listen(sock
, 5);
1355 JimAioSetError(interp
, NULL
);
1359 hdlfmt
= "aio.sockunixsrv%ld";
1364 #if defined(HAVE_SOCKETPAIR) && defined(HAVE_SYS_UN_H)
1365 case SOCK_STREAM_SOCKETPAIR
:
1368 static const char *mode
[2] = { "r+", "r+" };
1370 if (argc
!= 2 || ipv6
) {
1374 if (socketpair(PF_UNIX
, SOCK_STREAM
, 0, p
) < 0) {
1375 JimAioSetError(interp
, NULL
);
1378 return JimMakeChannelPair(interp
, p
, argv
[1], "aio.sockpair%ld", PF_UNIX
, mode
);
1383 #if defined(HAVE_PIPE)
1384 case SOCK_STREAM_PIPE
:
1387 static const char *mode
[2] = { "r", "w" };
1389 if (argc
!= 2 || ipv6
) {
1394 JimAioSetError(interp
, NULL
);
1398 return JimMakeChannelPair(interp
, p
, argv
[1], "aio.pipe%ld", 0, mode
);
1404 Jim_SetResultString(interp
, "Unsupported socket type", -1);
1408 return JimMakeChannel(interp
, NULL
, sock
, argv
[1], hdlfmt
, family
, mode
);
1410 #endif /* JIM_BOOTSTRAP */
1412 FILE *Jim_AioFilehandle(Jim_Interp
*interp
, Jim_Obj
*command
)
1414 Jim_Cmd
*cmdPtr
= Jim_GetCommand(interp
, command
, JIM_ERRMSG
);
1416 if (cmdPtr
&& !cmdPtr
->isproc
&& cmdPtr
->u
.native
.cmdProc
== JimAioSubCmdProc
) {
1417 return ((AioFile
*) cmdPtr
->u
.native
.privData
)->fp
;
1419 Jim_SetResultFormatted(interp
, "Not a filehandle: \"%#s\"", command
);
1423 int Jim_aioInit(Jim_Interp
*interp
)
1425 if (Jim_PackageProvide(interp
, "aio", "1.0", JIM_ERRMSG
))
1428 Jim_CreateCommand(interp
, "open", JimAioOpenCommand
, NULL
, NULL
);
1430 Jim_CreateCommand(interp
, "socket", JimAioSockCommand
, NULL
, NULL
);
1433 /* Create filehandles for stdin, stdout and stderr */
1434 JimMakeChannel(interp
, stdin
, -1, NULL
, "stdin", 0, "r");
1435 JimMakeChannel(interp
, stdout
, -1, NULL
, "stdout", 0, "w");
1436 JimMakeChannel(interp
, stderr
, -1, NULL
, "stderr", 0, "w");