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.
47 #include "jimautoconf.h"
49 #if defined(HAVE_SYS_SOCKET_H) && defined(HAVE_SELECT) && defined(HAVE_NETINET_IN_H) && defined(HAVE_NETDB_H) && defined(HAVE_ARPA_INET_H)
50 #include <sys/socket.h>
51 #include <netinet/in.h>
52 #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 if (!(af
->OpenFlags
& AIO_KEEPOPEN
)) {
284 Jim_DecrRefCount(interp
, af
->filename
);
286 #ifdef jim_ext_eventloop
287 /* remove existing EventHandlers */
289 Jim_DeleteFileHandler(interp
, af
->fp
);
292 Jim_DeleteFileHandler(interp
, af
->fp
);
295 Jim_DeleteFileHandler(interp
, af
->fp
);
301 static int aio_cmd_read(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
303 AioFile
*af
= Jim_CmdPrivData(interp
);
304 char buf
[AIO_BUF_LEN
];
307 int neededLen
= -1; /* -1 is "read as much as possible" */
309 if (argc
&& Jim_CompareStringImmediate(interp
, argv
[0], "-nonewline")) {
317 if (Jim_GetWide(interp
, argv
[0], &wideValue
) != JIM_OK
)
320 Jim_SetResultString(interp
, "invalid parameter: negative len", -1);
323 neededLen
= (int)wideValue
;
328 objPtr
= Jim_NewStringObj(interp
, NULL
, 0);
329 while (neededLen
!= 0) {
333 if (neededLen
== -1) {
334 readlen
= AIO_BUF_LEN
;
337 readlen
= (neededLen
> AIO_BUF_LEN
? AIO_BUF_LEN
: neededLen
);
339 retval
= fread(buf
, 1, readlen
, af
->fp
);
341 Jim_AppendString(interp
, objPtr
, buf
, retval
);
342 if (neededLen
!= -1) {
346 if (retval
!= readlen
)
349 /* Check for error conditions */
350 if (ferror(af
->fp
)) {
352 /* eof and EAGAIN are not error conditions */
353 if (!feof(af
->fp
) && errno
!= EAGAIN
) {
355 Jim_FreeNewObj(interp
, objPtr
);
356 JimAioSetError(interp
, af
->filename
);
362 const char *s
= Jim_GetString(objPtr
, &len
);
364 if (len
> 0 && s
[len
- 1] == '\n') {
366 objPtr
->bytes
[objPtr
->length
] = '\0';
369 Jim_SetResult(interp
, objPtr
);
373 static int aio_cmd_copy(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
375 AioFile
*af
= Jim_CmdPrivData(interp
);
377 long maxlen
= LONG_MAX
;
378 FILE *outfh
= Jim_AioFilehandle(interp
, argv
[0]);
385 if (Jim_GetLong(interp
, argv
[1], &maxlen
) != JIM_OK
) {
390 while (count
< maxlen
) {
391 int ch
= fgetc(af
->fp
);
393 if (ch
== EOF
|| fputc(ch
, outfh
) == EOF
) {
399 if (ferror(af
->fp
)) {
400 Jim_SetResultFormatted(interp
, "error while reading: %s", strerror(errno
));
406 Jim_SetResultFormatted(interp
, "error while writing: %s", strerror(errno
));
411 Jim_SetResultInt(interp
, count
);
416 static int aio_cmd_gets(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
418 AioFile
*af
= Jim_CmdPrivData(interp
);
419 char buf
[AIO_BUF_LEN
];
425 objPtr
= Jim_NewStringObj(interp
, NULL
, 0);
427 buf
[AIO_BUF_LEN
- 1] = '_';
428 if (fgets(buf
, AIO_BUF_LEN
, af
->fp
) == NULL
)
431 if (buf
[AIO_BUF_LEN
- 1] == '\0' && buf
[AIO_BUF_LEN
- 2] != '\n') {
432 Jim_AppendString(interp
, objPtr
, buf
, AIO_BUF_LEN
- 1);
437 if (len
&& (buf
[len
- 1] == '\n')) {
442 Jim_AppendString(interp
, objPtr
, buf
, len
);
446 if (ferror(af
->fp
) && errno
!= EAGAIN
&& errno
!= EINTR
) {
448 Jim_FreeNewObj(interp
, objPtr
);
449 JimAioSetError(interp
, af
->filename
);
455 if (Jim_SetVariable(interp
, argv
[0], objPtr
) != JIM_OK
) {
456 Jim_FreeNewObj(interp
, objPtr
);
460 len
= Jim_Length(objPtr
);
462 if (len
== 0 && feof(af
->fp
)) {
463 /* On EOF returns -1 if varName was specified */
466 Jim_SetResultInt(interp
, len
);
469 Jim_SetResult(interp
, objPtr
);
474 static int aio_cmd_puts(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
476 AioFile
*af
= Jim_CmdPrivData(interp
);
482 if (!Jim_CompareStringImmediate(interp
, argv
[0], "-nonewline")) {
491 wdata
= Jim_GetString(strObj
, &wlen
);
492 if (fwrite(wdata
, 1, wlen
, af
->fp
) == (unsigned)wlen
) {
493 if (argc
== 2 || putc('\n', af
->fp
) != EOF
) {
497 JimAioSetError(interp
, af
->filename
);
501 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
502 static int aio_cmd_recvfrom(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
504 AioFile
*af
= Jim_CmdPrivData(interp
);
506 union sockaddr_any sa
;
508 socklen_t salen
= sizeof(sa
);
511 if (Jim_GetLong(interp
, argv
[0], &len
) != JIM_OK
) {
515 buf
= Jim_Alloc(len
+ 1);
517 rlen
= recvfrom(fileno(af
->fp
), buf
, len
, 0, &sa
.sa
, &salen
);
520 JimAioSetError(interp
, NULL
);
524 Jim_SetResult(interp
, Jim_NewStringObjNoAlloc(interp
, buf
, rlen
));
527 /* INET6_ADDRSTRLEN is 46. Add some for [] and port */
531 if (sa
.sa
.sa_family
== PF_INET6
) {
533 /* Allow 9 for []:65535\0 */
534 inet_ntop(sa
.sa
.sa_family
, &sa
.sin6
.sin6_addr
, addrbuf
+ 1, sizeof(addrbuf
) - 9);
535 snprintf(addrbuf
+ strlen(addrbuf
), 8, "]:%d", ntohs(sa
.sin
.sin_port
));
540 /* Allow 7 for :65535\0 */
541 inet_ntop(sa
.sa
.sa_family
, &sa
.sin
.sin_addr
, addrbuf
, sizeof(addrbuf
) - 7);
542 snprintf(addrbuf
+ strlen(addrbuf
), 7, ":%d", ntohs(sa
.sin
.sin_port
));
545 if (Jim_SetVariable(interp
, argv
[1], Jim_NewStringObj(interp
, addrbuf
, -1)) != JIM_OK
) {
554 static int aio_cmd_sendto(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
556 AioFile
*af
= Jim_CmdPrivData(interp
);
560 union sockaddr_any sa
;
561 const char *addr
= Jim_String(argv
[1]);
564 if (IPV6
&& af
->addr_family
== PF_INET6
) {
565 if (JimParseIPv6Address(interp
, addr
, &sa
, &salen
) != JIM_OK
) {
569 else if (JimParseIpAddress(interp
, addr
, &sa
, &salen
) != JIM_OK
) {
572 wdata
= Jim_GetString(argv
[0], &wlen
);
574 /* Note that we don't validate the socket type. Rely on sendto() failing if appropriate */
575 len
= sendto(fileno(af
->fp
), wdata
, wlen
, 0, &sa
.sa
, salen
);
577 JimAioSetError(interp
, NULL
);
580 Jim_SetResultInt(interp
, len
);
584 static int aio_cmd_accept(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
586 AioFile
*af
= Jim_CmdPrivData(interp
);
588 union sockaddr_any sa
;
589 socklen_t addrlen
= sizeof(sa
);
591 sock
= accept(af
->fd
, &sa
.sa
, &addrlen
);
593 JimAioSetError(interp
, NULL
);
597 /* Create the file command */
598 return JimMakeChannel(interp
, NULL
, sock
, Jim_NewStringObj(interp
, "accept", -1),
599 "aio.sockstream%ld", af
->addr_family
, "r+");
602 static int aio_cmd_listen(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
604 AioFile
*af
= Jim_CmdPrivData(interp
);
607 if (Jim_GetLong(interp
, argv
[0], &backlog
) != JIM_OK
) {
611 if (listen(af
->fd
, backlog
)) {
612 JimAioSetError(interp
, NULL
);
618 #endif /* JIM_BOOTSTRAP */
620 static int aio_cmd_flush(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
622 AioFile
*af
= Jim_CmdPrivData(interp
);
624 if (fflush(af
->fp
) == EOF
) {
625 JimAioSetError(interp
, af
->filename
);
631 static int aio_cmd_eof(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
633 AioFile
*af
= Jim_CmdPrivData(interp
);
635 Jim_SetResultInt(interp
, feof(af
->fp
));
639 static int aio_cmd_close(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
641 Jim_DeleteCommand(interp
, Jim_String(argv
[0]));
645 static int aio_cmd_seek(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
647 AioFile
*af
= Jim_CmdPrivData(interp
);
652 if (Jim_CompareStringImmediate(interp
, argv
[1], "start"))
654 else if (Jim_CompareStringImmediate(interp
, argv
[1], "current"))
656 else if (Jim_CompareStringImmediate(interp
, argv
[1], "end"))
662 if (Jim_GetLong(interp
, argv
[0], &offset
) != JIM_OK
) {
665 if (fseek(af
->fp
, offset
, orig
) == -1) {
666 JimAioSetError(interp
, af
->filename
);
672 static int aio_cmd_tell(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
674 AioFile
*af
= Jim_CmdPrivData(interp
);
676 Jim_SetResultInt(interp
, ftell(af
->fp
));
680 static int aio_cmd_filename(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
682 AioFile
*af
= Jim_CmdPrivData(interp
);
684 Jim_SetResult(interp
, af
->filename
);
689 static int aio_cmd_ndelay(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
691 AioFile
*af
= Jim_CmdPrivData(interp
);
693 int fmode
= af
->flags
;
698 if (Jim_GetLong(interp
, argv
[0], &nb
) != JIM_OK
) {
707 fcntl(af
->fd
, F_SETFL
, fmode
);
710 Jim_SetResultInt(interp
, (fmode
& O_NONBLOCK
) ? 1 : 0);
715 static int aio_cmd_buffering(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
717 AioFile
*af
= Jim_CmdPrivData(interp
);
719 static const char * const options
[] = {
733 if (Jim_GetEnum(interp
, argv
[0], options
, &option
, NULL
, JIM_ERRMSG
) != JIM_OK
) {
738 setvbuf(af
->fp
, NULL
, _IONBF
, 0);
741 setvbuf(af
->fp
, NULL
, _IOLBF
, BUFSIZ
);
744 setvbuf(af
->fp
, NULL
, _IOFBF
, BUFSIZ
);
750 #ifdef jim_ext_eventloop
751 static void JimAioFileEventFinalizer(Jim_Interp
*interp
, void *clientData
)
753 Jim_Obj
*objPtr
= clientData
;
755 Jim_DecrRefCount(interp
, objPtr
);
758 static int JimAioFileEventHandler(Jim_Interp
*interp
, void *clientData
, int mask
)
760 Jim_Obj
*objPtr
= clientData
;
762 return Jim_EvalObjBackground(interp
, objPtr
);
765 static int aio_eventinfo(Jim_Interp
*interp
, AioFile
* af
, unsigned mask
, Jim_Obj
**scriptHandlerObj
,
766 int argc
, Jim_Obj
* const *argv
)
771 /* Return current script */
772 if (*scriptHandlerObj
) {
773 Jim_SetResult(interp
, *scriptHandlerObj
);
778 if (*scriptHandlerObj
) {
779 /* Delete old handler */
780 Jim_DeleteFileHandler(interp
, af
->fp
);
781 *scriptHandlerObj
= NULL
;
784 /* Now possibly add the new script(s) */
785 Jim_GetString(argv
[0], &scriptlen
);
786 if (scriptlen
== 0) {
787 /* Empty script, so done */
791 /* A new script to add */
792 Jim_IncrRefCount(argv
[0]);
793 *scriptHandlerObj
= argv
[0];
795 Jim_CreateFileHandler(interp
, af
->fp
, mask
,
796 JimAioFileEventHandler
, *scriptHandlerObj
, JimAioFileEventFinalizer
);
801 static int aio_cmd_readable(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
803 AioFile
*af
= Jim_CmdPrivData(interp
);
805 return aio_eventinfo(interp
, af
, JIM_EVENT_READABLE
, &af
->rEvent
, argc
, argv
);
808 static int aio_cmd_writable(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
810 AioFile
*af
= Jim_CmdPrivData(interp
);
812 return aio_eventinfo(interp
, af
, JIM_EVENT_WRITABLE
, &af
->wEvent
, argc
, argv
);
815 static int aio_cmd_onexception(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
817 AioFile
*af
= Jim_CmdPrivData(interp
);
819 return aio_eventinfo(interp
, af
, JIM_EVENT_EXCEPTION
, &af
->wEvent
, argc
, argv
);
823 static const jim_subcmd_type aio_command_table
[] = {
825 "?-nonewline? ?len?",
829 /* Description: Read and return bytes from the stream. To eof if no len. */
836 /* Description: Copy up to 'size' bytes to the given filehandle, or to eof if no size. */
843 /* Description: Read one line and return it or store it in the var */
850 /* Description: Write the string, with newline unless -nonewline */
852 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
858 /* Description: Receive up to 'len' bytes on the socket. Sets 'addrvar' with receive address, if set */
865 /* Description: Send 'str' to the given address (dgram only) */
872 /* Description: Server socket only: Accept a connection and return stream */
879 /* Description: Set the listen backlog for server socket */
881 #endif /* JIM_BOOTSTRAP */
887 /* Description: Flush the stream */
894 /* Description: Returns 1 if stream is at eof */
901 JIM_MODFLAG_FULLARGV
,
902 /* Description: Closes the stream */
905 "offset ?start|current|end",
909 /* Description: Seeks in the stream (default 'current') */
916 /* Description: Returns the current seek position */
923 /* Description: Returns the original filename */
931 /* Description: Set O_NDELAY (if arg). Returns current/new setting. */
939 /* Description: Sets buffering */
941 #ifdef jim_ext_eventloop
947 /* Description: Returns script, or invoke readable-script when readable, {} to remove */
954 /* Description: Returns script, or invoke writable-script when writable, {} to remove */
957 "?exception-script?",
961 /* Description: Returns script, or invoke exception-script when oob data, {} to remove */
967 static int JimAioSubCmdProc(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
969 return Jim_CallSubCmd(interp
, Jim_ParseSubCmd(interp
, aio_command_table
, argc
, argv
), argc
, argv
);
972 static int JimAioOpenCommand(Jim_Interp
*interp
, int argc
,
973 Jim_Obj
*const *argv
)
976 const char *filename
;
978 if (argc
!= 2 && argc
!= 3) {
979 Jim_WrongNumArgs(interp
, 1, argv
, "filename ?mode?");
983 mode
= (argc
== 3) ? Jim_String(argv
[2]) : "r";
984 filename
= Jim_String(argv
[1]);
986 #ifdef jim_ext_tclcompat
987 /* If the filename starts with '|', use popen instead */
988 if (*filename
== '|') {
991 evalObj
[0] = Jim_NewStringObj(interp
, "popen", -1);
992 evalObj
[1] = Jim_NewStringObj(interp
, filename
+ 1, -1);
993 evalObj
[2] = Jim_NewStringObj(interp
, mode
, -1);
995 return Jim_EvalObjVector(interp
, 3, evalObj
);
998 return JimMakeChannel(interp
, NULL
, -1, argv
[1], "aio.handle%ld", 0, mode
);
1002 * Creates a channel for fh/fd/filename.
1004 * If fh is not NULL, uses that as the channel (and set AIO_KEEPOPEN).
1005 * Otherwise, if fd is >= 0, uses that as the chanel.
1006 * Otherwise opens 'filename' with mode 'mode'.
1008 * hdlfmt is a sprintf format for the filehandle. Anything with %ld at the end will do.
1009 * mode is used for open or fdopen.
1011 * Creates the command and sets the name as the current result.
1013 static int JimMakeChannel(Jim_Interp
*interp
, FILE *fh
, int fd
, Jim_Obj
*filename
,
1014 const char *hdlfmt
, int family
, const char *mode
)
1017 char buf
[AIO_CMD_LEN
];
1020 if (filename
== NULL
) {
1021 filename
= Jim_NewStringObj(interp
, hdlfmt
, -1);
1024 Jim_IncrRefCount(filename
);
1028 fh
= fopen(Jim_String(filename
), mode
);
1031 fh
= fdopen(fd
, mode
);
1035 OpenFlags
= AIO_KEEPOPEN
;
1039 JimAioSetError(interp
, filename
);
1043 Jim_DecrRefCount(interp
, filename
);
1047 /* Create the file command */
1048 af
= Jim_Alloc(sizeof(*af
));
1049 memset(af
, 0, sizeof(*af
));
1051 af
->fd
= fileno(fh
);
1052 af
->filename
= filename
;
1054 if ((OpenFlags
& AIO_KEEPOPEN
) == 0) {
1055 fcntl(af
->fd
, F_SETFD
, FD_CLOEXEC
);
1058 af
->OpenFlags
= OpenFlags
;
1060 af
->flags
= fcntl(af
->fd
, F_GETFL
);
1062 af
->addr_family
= family
;
1063 snprintf(buf
, sizeof(buf
), hdlfmt
, Jim_GetId(interp
));
1064 Jim_CreateCommand(interp
, buf
, JimAioSubCmdProc
, af
, JimAioDelProc
);
1066 Jim_SetResultString(interp
, buf
, -1);
1071 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
1073 static int JimAioSockCommand(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1075 const char *hdlfmt
= "aio.unknown%ld";
1076 const char *socktypes
[] = {
1097 SOCK_STREAM6_CLIENT
,
1098 SOCK_STREAM6_SERVER
,
1102 const char *hostportarg
= NULL
;
1105 const char *mode
= "r+";
1106 int family
= PF_INET
;
1107 Jim_Obj
*argv0
= argv
[0];
1110 if (argc
> 1 && Jim_CompareStringImmediate(interp
, argv
[1], "-ipv6")) {
1112 Jim_SetResultString(interp
, "ipv6 not supported", -1);
1123 Jim_WrongNumArgs(interp
, 1, &argv0
, "?-ipv6? type ?address?");
1127 if (Jim_GetEnum(interp
, argv
[1], socktypes
, &socktype
, "socket type", JIM_ERRMSG
) != JIM_OK
)
1130 Jim_SetEmptyResult(interp
);
1132 hdlfmt
= "aio.sock%ld";
1135 hostportarg
= Jim_String(argv
[2]);
1139 case SOCK_DGRAM_CLIENT
:
1141 /* No address, so an unconnected dgram socket */
1142 sock
= socket(family
, SOCK_DGRAM
, 0);
1144 JimAioSetError(interp
, NULL
);
1150 case SOCK_STREAM_CLIENT
:
1152 union sockaddr_any sa
;
1160 if (JimParseIPv6Address(interp
, hostportarg
, &sa
, &salen
) != JIM_OK
) {
1164 else if (JimParseIpAddress(interp
, hostportarg
, &sa
, &salen
) != JIM_OK
) {
1167 sock
= socket(family
, (socktype
== SOCK_DGRAM_CLIENT
) ? SOCK_DGRAM
: SOCK_STREAM
, 0);
1169 JimAioSetError(interp
, NULL
);
1172 res
= connect(sock
, &sa
.sa
, salen
);
1174 JimAioSetError(interp
, argv
[2]);
1181 case SOCK_STREAM_SERVER
:
1182 case SOCK_DGRAM_SERVER
:
1184 union sockaddr_any sa
;
1192 if (JimParseIPv6Address(interp
, hostportarg
, &sa
, &salen
) != JIM_OK
) {
1196 else if (JimParseIpAddress(interp
, hostportarg
, &sa
, &salen
) != JIM_OK
) {
1199 sock
= socket(family
, (socktype
== SOCK_DGRAM_SERVER
) ? SOCK_DGRAM
: SOCK_STREAM
, 0);
1201 JimAioSetError(interp
, NULL
);
1205 /* Enable address reuse */
1206 setsockopt(sock
, SOL_SOCKET
, SO_REUSEADDR
, (void *)&on
, sizeof(on
));
1208 res
= bind(sock
, &sa
.sa
, salen
);
1210 JimAioSetError(interp
, argv
[2]);
1214 if (socktype
== SOCK_STREAM_SERVER
) {
1215 res
= listen(sock
, 5);
1217 JimAioSetError(interp
, NULL
);
1222 hdlfmt
= "aio.socksrv%ld";
1226 #ifdef HAVE_SYS_UN_H
1229 struct sockaddr_un sa
;
1232 if (argc
!= 3 || ipv6
) {
1236 if (JimParseDomainAddress(interp
, hostportarg
, &sa
) != JIM_OK
) {
1237 JimAioSetError(interp
, argv
[2]);
1241 sock
= socket(PF_UNIX
, SOCK_STREAM
, 0);
1243 JimAioSetError(interp
, NULL
);
1246 len
= strlen(sa
.sun_path
) + 1 + sizeof(sa
.sun_family
);
1247 res
= connect(sock
, (struct sockaddr
*)&sa
, len
);
1249 JimAioSetError(interp
, argv
[2]);
1253 hdlfmt
= "aio.sockunix%ld";
1257 case SOCK_UNIX_SERVER
:
1259 struct sockaddr_un sa
;
1262 if (argc
!= 3 || ipv6
) {
1266 if (JimParseDomainAddress(interp
, hostportarg
, &sa
) != JIM_OK
) {
1267 JimAioSetError(interp
, argv
[2]);
1271 sock
= socket(PF_UNIX
, SOCK_STREAM
, 0);
1273 JimAioSetError(interp
, NULL
);
1276 len
= strlen(sa
.sun_path
) + 1 + sizeof(sa
.sun_family
);
1277 res
= bind(sock
, (struct sockaddr
*)&sa
, len
);
1279 JimAioSetError(interp
, argv
[2]);
1283 res
= listen(sock
, 5);
1285 JimAioSetError(interp
, NULL
);
1289 hdlfmt
= "aio.sockunixsrv%ld";
1295 case SOCK_STREAM_PIPE
:
1299 if (argc
!= 2 || ipv6
) {
1304 JimAioSetError(interp
, NULL
);
1308 if (JimMakeChannel(interp
, NULL
, p
[0], argv
[1], "aio.pipe%ld", 0, "r") == JIM_OK
) {
1309 Jim_Obj
*objPtr
= Jim_NewListObj(interp
, NULL
, 0);
1310 Jim_ListAppendElement(interp
, objPtr
, Jim_GetResult(interp
));
1312 if (JimMakeChannel(interp
, NULL
, p
[1], argv
[1], "aio.pipe%ld", 0, "w") == JIM_OK
) {
1313 Jim_ListAppendElement(interp
, objPtr
, Jim_GetResult(interp
));
1314 Jim_SetResult(interp
, objPtr
);
1318 /* Can only be here if fdopen() failed */
1321 JimAioSetError(interp
, NULL
);
1327 Jim_SetResultString(interp
, "Unsupported socket type", -1);
1331 return JimMakeChannel(interp
, NULL
, sock
, argv
[1], hdlfmt
, family
, mode
);
1333 #endif /* JIM_BOOTSTRAP */
1335 FILE *Jim_AioFilehandle(Jim_Interp
*interp
, Jim_Obj
*command
)
1337 Jim_Cmd
*cmdPtr
= Jim_GetCommand(interp
, command
, JIM_ERRMSG
);
1339 if (cmdPtr
&& !cmdPtr
->isproc
&& cmdPtr
->u
.native
.cmdProc
== JimAioSubCmdProc
) {
1340 return ((AioFile
*) cmdPtr
->u
.native
.privData
)->fp
;
1342 Jim_SetResultFormatted(interp
, "Not a filehandle: \"%#s\"", command
);
1346 int Jim_aioInit(Jim_Interp
*interp
)
1348 if (Jim_PackageProvide(interp
, "aio", "1.0", JIM_ERRMSG
))
1351 Jim_CreateCommand(interp
, "open", JimAioOpenCommand
, NULL
, NULL
);
1353 Jim_CreateCommand(interp
, "socket", JimAioSockCommand
, NULL
, NULL
);
1356 /* Create filehandles for stdin, stdout and stderr */
1357 JimMakeChannel(interp
, stdin
, -1, NULL
, "stdin", 0, "r");
1358 JimMakeChannel(interp
, stdout
, -1, NULL
, "stdout", 0, "w");
1359 JimMakeChannel(interp
, stderr
, -1, NULL
, "stderr", 0, "w");