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.
41 #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>
63 #include "jim-eventloop.h"
64 #include "jim-subcmd.h"
66 #define AIO_CMD_LEN 32 /* e.g. aio.handleXXXXXX */
67 #define AIO_BUF_LEN 256 /* Can keep this small and rely on stdio buffering */
76 #define AIO_KEEPOPEN 1
87 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
90 struct sockaddr_in sin
;
92 struct sockaddr_in6 sin6
;
96 #ifndef HAVE_INET_NTOP
97 const char *inet_ntop(int af
, const void *src
, char *dst
, int size
)
102 snprintf(dst
, size
, "%s", inet_ntoa(((struct sockaddr_in
*)src
)->sin_addr
));
106 #endif /* JIM_BOOTSTRAP */
108 typedef struct AioFile
113 int OpenFlags
; /* AIO_KEEPOPEN? keep FILE* */
124 static int JimAioSubCmdProc(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
);
125 static int JimMakeChannel(Jim_Interp
*interp
, FILE *fh
, int fd
, Jim_Obj
*filename
,
126 const char *hdlfmt
, int family
, const char *mode
);
128 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
129 static int JimParseIPv6Address(Jim_Interp
*interp
, const char *hostport
, union sockaddr_any
*sa
, int *salen
)
133 * An IPv6 addr/port looks like:
136 * [fe80::223:6cff:fe95:bdc0%en1]:2000
140 * Note that the "any" address is ::, which is the same as when no address is specified.
148 stport
= strrchr(hostport
, ':');
150 /* No : so, the whole thing is the port */
153 sthost
= Jim_StrDup(hostport
);
159 if (*hostport
== '[') {
160 /* This is a numeric ipv6 address */
161 char *pt
= strchr(++hostport
, ']');
163 sthost
= Jim_StrDupLen(hostport
, pt
- hostport
);
168 sthost
= Jim_StrDupLen(hostport
, stport
- hostport
- 1);
171 memset(&req
, '\0', sizeof(req
));
172 req
.ai_family
= PF_INET6
;
174 if (getaddrinfo(sthost
, NULL
, &req
, &ai
)) {
175 Jim_SetResultFormatted(interp
, "Not a valid address: %s", hostport
);
179 memcpy(&sa
->sin
, ai
->ai_addr
, ai
->ai_addrlen
);
180 *salen
= ai
->ai_addrlen
;
182 sa
->sin
.sin_port
= htons(atoi(stport
));
190 Jim_SetResultString(interp
, "ipv6 not supported", -1);
195 static int JimParseIpAddress(Jim_Interp
*interp
, const char *hostport
, union sockaddr_any
*sa
, int *salen
)
197 /* An IPv4 addr/port looks like:
202 * If the address is missing, INADDR_ANY is used.
203 * If the port is missing, 0 is used (only useful for server sockets).
209 stport
= strrchr(hostport
, ':');
211 /* No : so, the whole thing is the port */
213 sthost
= Jim_StrDup("0.0.0.0");
216 sthost
= Jim_StrDupLen(hostport
, stport
- hostport
);
221 #ifdef HAVE_GETADDRINFO
224 memset(&req
, '\0', sizeof(req
));
225 req
.ai_family
= PF_INET
;
227 if (getaddrinfo(sthost
, NULL
, &req
, &ai
)) {
231 memcpy(&sa
->sin
, ai
->ai_addr
, ai
->ai_addrlen
);
232 *salen
= ai
->ai_addrlen
;
240 if ((he
= gethostbyname(sthost
)) != NULL
) {
241 if (he
->h_length
== sizeof(sa
->sin
.sin_addr
)) {
242 *salen
= sizeof(sa
->sin
);
243 sa
->sin
.sin_family
= he
->h_addrtype
;
244 memcpy(&sa
->sin
.sin_addr
, he
->h_addr
, he
->h_length
); /* set address */
250 sa
->sin
.sin_port
= htons(atoi(stport
));
255 Jim_SetResultFormatted(interp
, "Not a valid address: %s", hostport
);
262 static int JimParseDomainAddress(Jim_Interp
*interp
, const char *path
, struct sockaddr_un
*sa
)
264 sa
->sun_family
= PF_UNIX
;
265 snprintf(sa
->sun_path
, sizeof(sa
->sun_path
), "%s", path
);
270 #endif /* JIM_BOOTSTRAP */
272 static void JimAioSetError(Jim_Interp
*interp
, Jim_Obj
*name
)
275 Jim_SetResultFormatted(interp
, "%#s: %s", name
, strerror(errno
));
278 Jim_SetResultString(interp
, strerror(errno
), -1);
282 static void JimAioDelProc(Jim_Interp
*interp
, void *privData
)
284 AioFile
*af
= privData
;
288 if (!(af
->OpenFlags
& AIO_KEEPOPEN
)) {
292 Jim_DecrRefCount(interp
, af
->filename
);
294 #ifdef jim_ext_eventloop
295 /* remove all existing EventHandlers */
296 Jim_DeleteFileHandler(interp
, af
->fp
, JIM_EVENT_READABLE
| JIM_EVENT_WRITABLE
| JIM_EVENT_EXCEPTION
);
301 static int JimCheckStreamError(Jim_Interp
*interp
, AioFile
*af
)
303 if (!ferror(af
->fp
)) {
307 /* EAGAIN and similar are not error conditions. Just treat them like eof */
308 if (feof(af
->fp
) || errno
== EAGAIN
|| errno
== EINTR
) {
312 if (errno
== ECONNRESET
) {
317 if (errno
!= ECONNABORTED
) {
321 JimAioSetError(interp
, af
->filename
);
325 static int aio_cmd_read(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
327 AioFile
*af
= Jim_CmdPrivData(interp
);
328 char buf
[AIO_BUF_LEN
];
331 jim_wide neededLen
= -1; /* -1 is "read as much as possible" */
333 if (argc
&& Jim_CompareStringImmediate(interp
, argv
[0], "-nonewline")) {
339 if (Jim_GetWide(interp
, argv
[0], &neededLen
) != JIM_OK
)
342 Jim_SetResultString(interp
, "invalid parameter: negative len", -1);
349 objPtr
= Jim_NewStringObj(interp
, NULL
, 0);
350 while (neededLen
!= 0) {
354 if (neededLen
== -1) {
355 readlen
= AIO_BUF_LEN
;
358 readlen
= (neededLen
> AIO_BUF_LEN
? AIO_BUF_LEN
: neededLen
);
360 retval
= fread(buf
, 1, readlen
, af
->fp
);
362 Jim_AppendString(interp
, objPtr
, buf
, retval
);
363 if (neededLen
!= -1) {
367 if (retval
!= readlen
)
370 /* Check for error conditions */
371 if (JimCheckStreamError(interp
, af
)) {
372 Jim_FreeNewObj(interp
, objPtr
);
377 const char *s
= Jim_GetString(objPtr
, &len
);
379 if (len
> 0 && s
[len
- 1] == '\n') {
381 objPtr
->bytes
[objPtr
->length
] = '\0';
384 Jim_SetResult(interp
, objPtr
);
388 static int aio_cmd_copy(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
390 AioFile
*af
= Jim_CmdPrivData(interp
);
392 jim_wide maxlen
= JIM_WIDE_MAX
;
393 FILE *outfh
= Jim_AioFilehandle(interp
, argv
[0]);
400 if (Jim_GetWide(interp
, argv
[1], &maxlen
) != JIM_OK
) {
405 while (count
< maxlen
) {
406 int ch
= fgetc(af
->fp
);
408 if (ch
== EOF
|| fputc(ch
, outfh
) == EOF
) {
414 if (ferror(af
->fp
)) {
415 Jim_SetResultFormatted(interp
, "error while reading: %s", strerror(errno
));
421 Jim_SetResultFormatted(interp
, "error while writing: %s", strerror(errno
));
426 Jim_SetResultInt(interp
, count
);
431 static int aio_cmd_gets(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
433 AioFile
*af
= Jim_CmdPrivData(interp
);
434 char buf
[AIO_BUF_LEN
];
440 objPtr
= Jim_NewStringObj(interp
, NULL
, 0);
442 buf
[AIO_BUF_LEN
- 1] = '_';
443 if (fgets(buf
, AIO_BUF_LEN
, af
->fp
) == NULL
)
446 if (buf
[AIO_BUF_LEN
- 1] == '\0' && buf
[AIO_BUF_LEN
- 2] != '\n') {
447 Jim_AppendString(interp
, objPtr
, buf
, AIO_BUF_LEN
- 1);
452 if (len
&& (buf
[len
- 1] == '\n')) {
457 Jim_AppendString(interp
, objPtr
, buf
, len
);
461 if (JimCheckStreamError(interp
, af
)) {
463 Jim_FreeNewObj(interp
, objPtr
);
468 if (Jim_SetVariable(interp
, argv
[0], objPtr
) != JIM_OK
) {
469 Jim_FreeNewObj(interp
, objPtr
);
473 len
= Jim_Length(objPtr
);
475 if (len
== 0 && feof(af
->fp
)) {
476 /* On EOF returns -1 if varName was specified */
479 Jim_SetResultInt(interp
, len
);
482 Jim_SetResult(interp
, objPtr
);
487 static int aio_cmd_puts(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
489 AioFile
*af
= Jim_CmdPrivData(interp
);
495 if (!Jim_CompareStringImmediate(interp
, argv
[0], "-nonewline")) {
504 wdata
= Jim_GetString(strObj
, &wlen
);
505 if (fwrite(wdata
, 1, wlen
, af
->fp
) == (unsigned)wlen
) {
506 if (argc
== 2 || putc('\n', af
->fp
) != EOF
) {
510 JimAioSetError(interp
, af
->filename
);
514 static int aio_cmd_isatty(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
517 AioFile
*af
= Jim_CmdPrivData(interp
);
518 Jim_SetResultInt(interp
, isatty(fileno(af
->fp
)));
520 Jim_SetResultInt(interp
, 0);
526 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
527 static int aio_cmd_recvfrom(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
529 AioFile
*af
= Jim_CmdPrivData(interp
);
531 union sockaddr_any sa
;
533 socklen_t salen
= sizeof(sa
);
536 if (Jim_GetLong(interp
, argv
[0], &len
) != JIM_OK
) {
540 buf
= Jim_Alloc(len
+ 1);
542 rlen
= recvfrom(fileno(af
->fp
), buf
, len
, 0, &sa
.sa
, &salen
);
545 JimAioSetError(interp
, NULL
);
549 Jim_SetResult(interp
, Jim_NewStringObjNoAlloc(interp
, buf
, rlen
));
552 /* INET6_ADDRSTRLEN is 46. Add some for [] and port */
556 if (sa
.sa
.sa_family
== PF_INET6
) {
558 /* Allow 9 for []:65535\0 */
559 inet_ntop(sa
.sa
.sa_family
, &sa
.sin6
.sin6_addr
, addrbuf
+ 1, sizeof(addrbuf
) - 9);
560 snprintf(addrbuf
+ strlen(addrbuf
), 8, "]:%d", ntohs(sa
.sin
.sin_port
));
565 /* Allow 7 for :65535\0 */
566 inet_ntop(sa
.sa
.sa_family
, &sa
.sin
.sin_addr
, addrbuf
, sizeof(addrbuf
) - 7);
567 snprintf(addrbuf
+ strlen(addrbuf
), 7, ":%d", ntohs(sa
.sin
.sin_port
));
570 if (Jim_SetVariable(interp
, argv
[1], Jim_NewStringObj(interp
, addrbuf
, -1)) != JIM_OK
) {
579 static int aio_cmd_sendto(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
581 AioFile
*af
= Jim_CmdPrivData(interp
);
585 union sockaddr_any sa
;
586 const char *addr
= Jim_String(argv
[1]);
589 if (IPV6
&& af
->addr_family
== PF_INET6
) {
590 if (JimParseIPv6Address(interp
, addr
, &sa
, &salen
) != JIM_OK
) {
594 else if (JimParseIpAddress(interp
, addr
, &sa
, &salen
) != JIM_OK
) {
597 wdata
= Jim_GetString(argv
[0], &wlen
);
599 /* Note that we don't validate the socket type. Rely on sendto() failing if appropriate */
600 len
= sendto(fileno(af
->fp
), wdata
, wlen
, 0, &sa
.sa
, salen
);
602 JimAioSetError(interp
, NULL
);
605 Jim_SetResultInt(interp
, len
);
609 static int aio_cmd_accept(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
611 AioFile
*af
= Jim_CmdPrivData(interp
);
613 union sockaddr_any sa
;
614 socklen_t addrlen
= sizeof(sa
);
616 sock
= accept(af
->fd
, &sa
.sa
, &addrlen
);
618 JimAioSetError(interp
, NULL
);
622 /* Create the file command */
623 return JimMakeChannel(interp
, NULL
, sock
, Jim_NewStringObj(interp
, "accept", -1),
624 "aio.sockstream%ld", af
->addr_family
, "r+");
627 static int aio_cmd_listen(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
629 AioFile
*af
= Jim_CmdPrivData(interp
);
632 if (Jim_GetLong(interp
, argv
[0], &backlog
) != JIM_OK
) {
636 if (listen(af
->fd
, backlog
)) {
637 JimAioSetError(interp
, NULL
);
643 #endif /* JIM_BOOTSTRAP */
645 static int aio_cmd_flush(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
647 AioFile
*af
= Jim_CmdPrivData(interp
);
649 if (fflush(af
->fp
) == EOF
) {
650 JimAioSetError(interp
, af
->filename
);
656 static int aio_cmd_eof(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
658 AioFile
*af
= Jim_CmdPrivData(interp
);
660 Jim_SetResultInt(interp
, feof(af
->fp
));
664 static int aio_cmd_close(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
666 Jim_DeleteCommand(interp
, Jim_String(argv
[0]));
670 static int aio_cmd_seek(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
672 AioFile
*af
= Jim_CmdPrivData(interp
);
677 if (Jim_CompareStringImmediate(interp
, argv
[1], "start"))
679 else if (Jim_CompareStringImmediate(interp
, argv
[1], "current"))
681 else if (Jim_CompareStringImmediate(interp
, argv
[1], "end"))
687 if (Jim_GetWide(interp
, argv
[0], &offset
) != JIM_OK
) {
690 if (fseeko(af
->fp
, offset
, orig
) == -1) {
691 JimAioSetError(interp
, af
->filename
);
697 static int aio_cmd_tell(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
699 AioFile
*af
= Jim_CmdPrivData(interp
);
701 Jim_SetResultInt(interp
, ftello(af
->fp
));
705 static int aio_cmd_filename(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
707 AioFile
*af
= Jim_CmdPrivData(interp
);
709 Jim_SetResult(interp
, af
->filename
);
714 static int aio_cmd_ndelay(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
716 AioFile
*af
= Jim_CmdPrivData(interp
);
718 int fmode
= af
->flags
;
723 if (Jim_GetLong(interp
, argv
[0], &nb
) != JIM_OK
) {
732 fcntl(af
->fd
, F_SETFL
, fmode
);
735 Jim_SetResultInt(interp
, (fmode
& O_NONBLOCK
) ? 1 : 0);
740 static int aio_cmd_buffering(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
742 AioFile
*af
= Jim_CmdPrivData(interp
);
744 static const char * const options
[] = {
758 if (Jim_GetEnum(interp
, argv
[0], options
, &option
, NULL
, JIM_ERRMSG
) != JIM_OK
) {
763 setvbuf(af
->fp
, NULL
, _IONBF
, 0);
766 setvbuf(af
->fp
, NULL
, _IOLBF
, BUFSIZ
);
769 setvbuf(af
->fp
, NULL
, _IOFBF
, BUFSIZ
);
775 #ifdef jim_ext_eventloop
776 static void JimAioFileEventFinalizer(Jim_Interp
*interp
, void *clientData
)
778 Jim_Obj
**objPtrPtr
= clientData
;
780 Jim_DecrRefCount(interp
, *objPtrPtr
);
784 static int JimAioFileEventHandler(Jim_Interp
*interp
, void *clientData
, int mask
)
786 Jim_Obj
**objPtrPtr
= clientData
;
788 return Jim_EvalObjBackground(interp
, *objPtrPtr
);
791 static int aio_eventinfo(Jim_Interp
*interp
, AioFile
* af
, unsigned mask
, Jim_Obj
**scriptHandlerObj
,
792 int argc
, Jim_Obj
* const *argv
)
795 /* Return current script */
796 if (*scriptHandlerObj
) {
797 Jim_SetResult(interp
, *scriptHandlerObj
);
802 if (*scriptHandlerObj
) {
803 /* Delete old handler */
804 Jim_DeleteFileHandler(interp
, af
->fp
, mask
);
807 /* Now possibly add the new script(s) */
808 if (Jim_Length(argv
[0]) == 0) {
809 /* Empty script, so done */
813 /* A new script to add */
814 Jim_IncrRefCount(argv
[0]);
815 *scriptHandlerObj
= argv
[0];
817 Jim_CreateFileHandler(interp
, af
->fp
, mask
,
818 JimAioFileEventHandler
, scriptHandlerObj
, JimAioFileEventFinalizer
);
823 static int aio_cmd_readable(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
825 AioFile
*af
= Jim_CmdPrivData(interp
);
827 return aio_eventinfo(interp
, af
, JIM_EVENT_READABLE
, &af
->rEvent
, argc
, argv
);
830 static int aio_cmd_writable(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
832 AioFile
*af
= Jim_CmdPrivData(interp
);
834 return aio_eventinfo(interp
, af
, JIM_EVENT_WRITABLE
, &af
->wEvent
, argc
, argv
);
837 static int aio_cmd_onexception(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
839 AioFile
*af
= Jim_CmdPrivData(interp
);
841 return aio_eventinfo(interp
, af
, JIM_EVENT_EXCEPTION
, &af
->wEvent
, argc
, argv
);
845 static const jim_subcmd_type aio_command_table
[] = {
847 "?-nonewline? ?len?",
851 /* Description: Read and return bytes from the stream. To eof if no len. */
858 /* Description: Copy up to 'size' bytes to the given filehandle, or to eof if no size. */
865 /* Description: Read one line and return it or store it in the var */
872 /* Description: Write the string, with newline unless -nonewline */
879 /* Description: Is the file descriptor a tty? */
881 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
887 /* Description: Receive up to 'len' bytes on the socket. Sets 'addrvar' with receive address, if set */
894 /* Description: Send 'str' to the given address (dgram only) */
901 /* Description: Server socket only: Accept a connection and return stream */
908 /* Description: Set the listen backlog for server socket */
910 #endif /* JIM_BOOTSTRAP */
916 /* Description: Flush the stream */
923 /* Description: Returns 1 if stream is at eof */
930 JIM_MODFLAG_FULLARGV
,
931 /* Description: Closes the stream */
934 "offset ?start|current|end",
938 /* Description: Seeks in the stream (default 'current') */
945 /* Description: Returns the current seek position */
952 /* Description: Returns the original filename */
960 /* Description: Set O_NDELAY (if arg). Returns current/new setting. */
968 /* Description: Sets buffering */
970 #ifdef jim_ext_eventloop
976 /* Description: Returns script, or invoke readable-script when readable, {} to remove */
983 /* Description: Returns script, or invoke writable-script when writable, {} to remove */
986 "?exception-script?",
990 /* Description: Returns script, or invoke exception-script when oob data, {} to remove */
996 static int JimAioSubCmdProc(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
998 return Jim_CallSubCmd(interp
, Jim_ParseSubCmd(interp
, aio_command_table
, argc
, argv
), argc
, argv
);
1001 static int JimAioOpenCommand(Jim_Interp
*interp
, int argc
,
1002 Jim_Obj
*const *argv
)
1005 const char *filename
;
1007 if (argc
!= 2 && argc
!= 3) {
1008 Jim_WrongNumArgs(interp
, 1, argv
, "filename ?mode?");
1012 mode
= (argc
== 3) ? Jim_String(argv
[2]) : "r";
1013 filename
= Jim_String(argv
[1]);
1015 #ifdef jim_ext_tclcompat
1016 /* If the filename starts with '|', use popen instead */
1017 if (*filename
== '|') {
1018 Jim_Obj
*evalObj
[3];
1020 evalObj
[0] = Jim_NewStringObj(interp
, "popen", -1);
1021 evalObj
[1] = Jim_NewStringObj(interp
, filename
+ 1, -1);
1022 evalObj
[2] = Jim_NewStringObj(interp
, mode
, -1);
1024 return Jim_EvalObjVector(interp
, 3, evalObj
);
1027 return JimMakeChannel(interp
, NULL
, -1, argv
[1], "aio.handle%ld", 0, mode
);
1031 * Creates a channel for fh/fd/filename.
1033 * If fh is not NULL, uses that as the channel (and set AIO_KEEPOPEN).
1034 * Otherwise, if fd is >= 0, uses that as the chanel.
1035 * Otherwise opens 'filename' with mode 'mode'.
1037 * hdlfmt is a sprintf format for the filehandle. Anything with %ld at the end will do.
1038 * mode is used for open or fdopen.
1040 * Creates the command and sets the name as the current result.
1042 static int JimMakeChannel(Jim_Interp
*interp
, FILE *fh
, int fd
, Jim_Obj
*filename
,
1043 const char *hdlfmt
, int family
, const char *mode
)
1046 char buf
[AIO_CMD_LEN
];
1049 if (filename
== NULL
) {
1050 filename
= Jim_NewStringObj(interp
, hdlfmt
, -1);
1053 Jim_IncrRefCount(filename
);
1057 fh
= fopen(Jim_String(filename
), mode
);
1060 fh
= fdopen(fd
, mode
);
1064 OpenFlags
= AIO_KEEPOPEN
;
1068 JimAioSetError(interp
, filename
);
1069 #if !defined(JIM_ANSIC)
1074 Jim_DecrRefCount(interp
, filename
);
1078 /* Create the file command */
1079 af
= Jim_Alloc(sizeof(*af
));
1080 memset(af
, 0, sizeof(*af
));
1082 af
->fd
= fileno(fh
);
1083 af
->filename
= filename
;
1085 if ((OpenFlags
& AIO_KEEPOPEN
) == 0) {
1086 fcntl(af
->fd
, F_SETFD
, FD_CLOEXEC
);
1089 af
->OpenFlags
= OpenFlags
;
1091 af
->flags
= fcntl(af
->fd
, F_GETFL
);
1093 af
->addr_family
= family
;
1094 snprintf(buf
, sizeof(buf
), hdlfmt
, Jim_GetId(interp
));
1095 Jim_CreateCommand(interp
, buf
, JimAioSubCmdProc
, af
, JimAioDelProc
);
1097 Jim_SetResultString(interp
, buf
, -1);
1102 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
1104 static int JimAioSockCommand(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1106 const char *hdlfmt
= "aio.unknown%ld";
1107 const char *socktypes
[] = {
1128 SOCK_STREAM6_CLIENT
,
1129 SOCK_STREAM6_SERVER
,
1133 const char *hostportarg
= NULL
;
1136 const char *mode
= "r+";
1137 int family
= PF_INET
;
1138 Jim_Obj
*argv0
= argv
[0];
1141 if (argc
> 1 && Jim_CompareStringImmediate(interp
, argv
[1], "-ipv6")) {
1143 Jim_SetResultString(interp
, "ipv6 not supported", -1);
1154 Jim_WrongNumArgs(interp
, 1, &argv0
, "?-ipv6? type ?address?");
1158 if (Jim_GetEnum(interp
, argv
[1], socktypes
, &socktype
, "socket type", JIM_ERRMSG
) != JIM_OK
)
1161 Jim_SetEmptyResult(interp
);
1163 hdlfmt
= "aio.sock%ld";
1166 hostportarg
= Jim_String(argv
[2]);
1170 case SOCK_DGRAM_CLIENT
:
1172 /* No address, so an unconnected dgram socket */
1173 sock
= socket(family
, SOCK_DGRAM
, 0);
1175 JimAioSetError(interp
, NULL
);
1181 case SOCK_STREAM_CLIENT
:
1183 union sockaddr_any sa
;
1191 if (JimParseIPv6Address(interp
, hostportarg
, &sa
, &salen
) != JIM_OK
) {
1195 else if (JimParseIpAddress(interp
, hostportarg
, &sa
, &salen
) != JIM_OK
) {
1198 sock
= socket(family
, (socktype
== SOCK_DGRAM_CLIENT
) ? SOCK_DGRAM
: SOCK_STREAM
, 0);
1200 JimAioSetError(interp
, NULL
);
1203 res
= connect(sock
, &sa
.sa
, salen
);
1205 JimAioSetError(interp
, argv
[2]);
1212 case SOCK_STREAM_SERVER
:
1213 case SOCK_DGRAM_SERVER
:
1215 union sockaddr_any sa
;
1223 if (JimParseIPv6Address(interp
, hostportarg
, &sa
, &salen
) != JIM_OK
) {
1227 else if (JimParseIpAddress(interp
, hostportarg
, &sa
, &salen
) != JIM_OK
) {
1230 sock
= socket(family
, (socktype
== SOCK_DGRAM_SERVER
) ? SOCK_DGRAM
: SOCK_STREAM
, 0);
1232 JimAioSetError(interp
, NULL
);
1236 /* Enable address reuse */
1237 setsockopt(sock
, SOL_SOCKET
, SO_REUSEADDR
, (void *)&on
, sizeof(on
));
1239 res
= bind(sock
, &sa
.sa
, salen
);
1241 JimAioSetError(interp
, argv
[2]);
1245 if (socktype
== SOCK_STREAM_SERVER
) {
1246 res
= listen(sock
, 5);
1248 JimAioSetError(interp
, NULL
);
1253 hdlfmt
= "aio.socksrv%ld";
1257 #ifdef HAVE_SYS_UN_H
1260 struct sockaddr_un sa
;
1263 if (argc
!= 3 || ipv6
) {
1267 if (JimParseDomainAddress(interp
, hostportarg
, &sa
) != JIM_OK
) {
1268 JimAioSetError(interp
, argv
[2]);
1272 sock
= socket(PF_UNIX
, SOCK_STREAM
, 0);
1274 JimAioSetError(interp
, NULL
);
1277 len
= strlen(sa
.sun_path
) + 1 + sizeof(sa
.sun_family
);
1278 res
= connect(sock
, (struct sockaddr
*)&sa
, len
);
1280 JimAioSetError(interp
, argv
[2]);
1284 hdlfmt
= "aio.sockunix%ld";
1288 case SOCK_UNIX_SERVER
:
1290 struct sockaddr_un sa
;
1293 if (argc
!= 3 || ipv6
) {
1297 if (JimParseDomainAddress(interp
, hostportarg
, &sa
) != JIM_OK
) {
1298 JimAioSetError(interp
, argv
[2]);
1302 sock
= socket(PF_UNIX
, SOCK_STREAM
, 0);
1304 JimAioSetError(interp
, NULL
);
1307 len
= strlen(sa
.sun_path
) + 1 + sizeof(sa
.sun_family
);
1308 res
= bind(sock
, (struct sockaddr
*)&sa
, len
);
1310 JimAioSetError(interp
, argv
[2]);
1314 res
= listen(sock
, 5);
1316 JimAioSetError(interp
, NULL
);
1320 hdlfmt
= "aio.sockunixsrv%ld";
1326 case SOCK_STREAM_PIPE
:
1330 if (argc
!= 2 || ipv6
) {
1335 JimAioSetError(interp
, NULL
);
1339 if (JimMakeChannel(interp
, NULL
, p
[0], argv
[1], "aio.pipe%ld", 0, "r") == JIM_OK
) {
1340 Jim_Obj
*objPtr
= Jim_NewListObj(interp
, NULL
, 0);
1341 Jim_ListAppendElement(interp
, objPtr
, Jim_GetResult(interp
));
1343 if (JimMakeChannel(interp
, NULL
, p
[1], argv
[1], "aio.pipe%ld", 0, "w") == JIM_OK
) {
1344 Jim_ListAppendElement(interp
, objPtr
, Jim_GetResult(interp
));
1345 Jim_SetResult(interp
, objPtr
);
1349 /* Can only be here if fdopen() failed */
1352 JimAioSetError(interp
, NULL
);
1358 Jim_SetResultString(interp
, "Unsupported socket type", -1);
1362 return JimMakeChannel(interp
, NULL
, sock
, argv
[1], hdlfmt
, family
, mode
);
1364 #endif /* JIM_BOOTSTRAP */
1366 FILE *Jim_AioFilehandle(Jim_Interp
*interp
, Jim_Obj
*command
)
1368 Jim_Cmd
*cmdPtr
= Jim_GetCommand(interp
, command
, JIM_ERRMSG
);
1370 if (cmdPtr
&& !cmdPtr
->isproc
&& cmdPtr
->u
.native
.cmdProc
== JimAioSubCmdProc
) {
1371 return ((AioFile
*) cmdPtr
->u
.native
.privData
)->fp
;
1373 Jim_SetResultFormatted(interp
, "Not a filehandle: \"%#s\"", command
);
1377 int Jim_aioInit(Jim_Interp
*interp
)
1379 if (Jim_PackageProvide(interp
, "aio", "1.0", JIM_ERRMSG
))
1382 Jim_CreateCommand(interp
, "open", JimAioOpenCommand
, NULL
, NULL
);
1384 Jim_CreateCommand(interp
, "socket", JimAioSockCommand
, NULL
, NULL
);
1387 /* Create filehandles for stdin, stdout and stderr */
1388 JimMakeChannel(interp
, stdin
, -1, NULL
, "stdin", 0, "r");
1389 JimMakeChannel(interp
, stdout
, -1, NULL
, "stdout", 0, "w");
1390 JimMakeChannel(interp
, stderr
, -1, NULL
, "stderr", 0, "w");