Add a general purpose hashtable pattern matcher
[jimtcl.git] / jim-aio.c
blob4240311c702f0fcdab1a6a72dbf7e8905309e400
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
14 * are met:
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.
39 **/
41 #include <stdio.h>
42 #include <string.h>
43 #include <errno.h>
44 #include <fcntl.h>
46 #include "jim.h"
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>
53 #include <netdb.h>
54 #include <unistd.h>
55 #ifdef HAVE_SYS_UN_H
56 #include <sys/un.h>
57 #endif
58 #else
59 #define JIM_ANSIC
60 #endif
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
70 #if defined(JIM_IPV6)
71 #define IPV6 1
72 #else
73 #define IPV6 0
74 #ifndef PF_INET6
75 #define PF_INET6 0
76 #endif
77 #endif
79 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
80 union sockaddr_any {
81 struct sockaddr sa;
82 struct sockaddr_in sin;
83 #if IPV6
84 struct sockaddr_in6 sin6;
85 #endif
88 #ifndef HAVE_INET_NTOP
89 const char *inet_ntop(int af, const void *src, char *dst, int size)
91 if (af != PF_INET) {
92 return NULL;
94 snprintf(dst, size, "%s", inet_ntoa(((struct sockaddr_in *)src)->sin_addr));
95 return dst;
97 #endif
98 #endif /* JIM_BOOTSTRAP */
100 typedef struct AioFile
102 FILE *fp;
103 Jim_Obj *filename;
104 int type;
105 int OpenFlags; /* AIO_KEEPOPEN? keep FILE* */
106 int fd;
107 #ifdef O_NDELAY
108 int flags;
109 #endif
110 Jim_Obj *rEvent;
111 Jim_Obj *wEvent;
112 Jim_Obj *eEvent;
113 int addr_family;
114 } AioFile;
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)
123 #if IPV6
125 * An IPv6 addr/port looks like:
126 * [::1]
127 * [::1]:2000
128 * [fe80::223:6cff:fe95:bdc0%en1]:2000
129 * [::]:2000
130 * 2000
132 * Note that the "any" address is ::, which is the same as when no address is specified.
134 char *sthost = NULL;
135 const char *stport;
136 int ret = JIM_OK;
137 struct addrinfo req;
138 struct addrinfo *ai;
140 stport = strrchr(hostport, ':');
141 if (!stport) {
142 /* No : so, the whole thing is the port */
143 stport = hostport;
144 hostport = "::";
145 sthost = Jim_StrDup(hostport);
147 else {
148 stport++;
151 if (*hostport == '[') {
152 /* This is a numeric ipv6 address */
153 char *pt = strchr(++hostport, ']');
154 if (pt) {
155 sthost = Jim_StrDupLen(hostport, pt - hostport);
159 if (!sthost) {
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);
168 ret = JIM_ERR;
170 else {
171 memcpy(&sa->sin, ai->ai_addr, ai->ai_addrlen);
172 *salen = ai->ai_addrlen;
174 sa->sin.sin_port = htons(atoi(stport));
176 freeaddrinfo(ai);
178 Jim_Free(sthost);
180 return ret;
181 #else
182 Jim_SetResultString(interp, "ipv6 not supported", -1);
183 return JIM_ERR;
184 #endif
187 static int JimParseIpAddress(Jim_Interp *interp, const char *hostport, union sockaddr_any *sa, int *salen)
189 /* An IPv4 addr/port looks like:
190 * 192.168.1.5
191 * 192.168.1.5:2000
192 * 2000
194 * If the address is missing, INADDR_ANY is used.
195 * If the port is missing, 0 is used (only useful for server sockets).
197 char *sthost = NULL;
198 const char *stport;
199 int ret = JIM_OK;
201 stport = strrchr(hostport, ':');
202 if (!stport) {
203 /* No : so, the whole thing is the port */
204 stport = hostport;
205 sthost = Jim_StrDup("0.0.0.0");
207 else {
208 sthost = Jim_StrDupLen(hostport, stport - hostport);
209 stport++;
213 #ifdef HAVE_GETADDRINFO
214 struct addrinfo req;
215 struct addrinfo *ai;
216 memset(&req, '\0', sizeof(req));
217 req.ai_family = PF_INET;
219 if (getaddrinfo(sthost, NULL, &req, &ai)) {
220 ret = JIM_ERR;
222 else {
223 memcpy(&sa->sin, ai->ai_addr, ai->ai_addrlen);
224 *salen = ai->ai_addrlen;
225 freeaddrinfo(ai);
227 #else
228 struct hostent *he;
230 ret = JIM_ERR;
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 */
237 ret = JIM_OK;
240 #endif
242 sa->sin.sin_port = htons(atoi(stport));
244 Jim_Free(sthost);
246 if (ret != JIM_OK) {
247 Jim_SetResultFormatted(interp, "Not a valid address: %s", hostport);
250 return ret;
253 #ifdef HAVE_SYS_UN_H
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);
259 return JIM_OK;
261 #endif
262 #endif /* JIM_BOOTSTRAP */
264 static void JimAioSetError(Jim_Interp *interp, Jim_Obj *name)
266 if (name) {
267 Jim_SetResultFormatted(interp, "%#s: %s", name, strerror(errno));
269 else {
270 Jim_SetResultString(interp, strerror(errno), -1);
274 static void JimAioDelProc(Jim_Interp *interp, void *privData)
276 AioFile *af = privData;
278 JIM_NOTUSED(interp);
280 if (!(af->OpenFlags & AIO_KEEPOPEN)) {
281 fclose(af->fp);
284 Jim_DecrRefCount(interp, af->filename);
286 #ifdef jim_ext_eventloop
287 /* remove existing EventHandlers */
288 if (af->rEvent) {
289 Jim_DeleteFileHandler(interp, af->fp);
291 if (af->wEvent) {
292 Jim_DeleteFileHandler(interp, af->fp);
294 if (af->eEvent) {
295 Jim_DeleteFileHandler(interp, af->fp);
297 #endif
298 Jim_Free(af);
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];
305 Jim_Obj *objPtr;
306 int nonewline = 0;
307 int neededLen = -1; /* -1 is "read as much as possible" */
309 if (argc && Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) {
310 nonewline = 1;
311 argv++;
312 argc--;
314 if (argc == 1) {
315 jim_wide wideValue;
317 if (Jim_GetWide(interp, argv[0], &wideValue) != JIM_OK)
318 return JIM_ERR;
319 if (wideValue < 0) {
320 Jim_SetResultString(interp, "invalid parameter: negative len", -1);
321 return JIM_ERR;
323 neededLen = (int)wideValue;
325 else if (argc) {
326 return -1;
328 objPtr = Jim_NewStringObj(interp, NULL, 0);
329 while (neededLen != 0) {
330 int retval;
331 int readlen;
333 if (neededLen == -1) {
334 readlen = AIO_BUF_LEN;
336 else {
337 readlen = (neededLen > AIO_BUF_LEN ? AIO_BUF_LEN : neededLen);
339 retval = fread(buf, 1, readlen, af->fp);
340 if (retval > 0) {
341 Jim_AppendString(interp, objPtr, buf, retval);
342 if (neededLen != -1) {
343 neededLen -= retval;
346 if (retval != readlen)
347 break;
349 /* Check for error conditions */
350 if (ferror(af->fp)) {
351 clearerr(af->fp);
352 /* eof and EAGAIN are not error conditions */
353 if (!feof(af->fp) && errno != EAGAIN) {
354 /* I/O error */
355 Jim_FreeNewObj(interp, objPtr);
356 JimAioSetError(interp, af->filename);
357 return JIM_ERR;
360 if (nonewline) {
361 int len;
362 const char *s = Jim_GetString(objPtr, &len);
364 if (len > 0 && s[len - 1] == '\n') {
365 objPtr->length--;
366 objPtr->bytes[objPtr->length] = '\0';
369 Jim_SetResult(interp, objPtr);
370 return JIM_OK;
373 static int aio_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
375 AioFile *af = Jim_CmdPrivData(interp);
376 long count = 0;
377 long maxlen = LONG_MAX;
378 FILE *outfh = Jim_AioFilehandle(interp, argv[0]);
380 if (outfh == NULL) {
381 return JIM_ERR;
384 if (argc == 2) {
385 if (Jim_GetLong(interp, argv[1], &maxlen) != JIM_OK) {
386 return JIM_ERR;
390 while (count < maxlen) {
391 int ch = fgetc(af->fp);
393 if (ch == EOF || fputc(ch, outfh) == EOF) {
394 break;
396 count++;
399 if (ferror(af->fp)) {
400 Jim_SetResultFormatted(interp, "error while reading: %s", strerror(errno));
401 clearerr(af->fp);
402 return JIM_ERR;
405 if (ferror(outfh)) {
406 Jim_SetResultFormatted(interp, "error while writing: %s", strerror(errno));
407 clearerr(outfh);
408 return JIM_ERR;
411 Jim_SetResultInt(interp, count);
413 return JIM_OK;
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];
420 Jim_Obj *objPtr;
421 int len;
423 errno = 0;
425 objPtr = Jim_NewStringObj(interp, NULL, 0);
426 while (1) {
427 buf[AIO_BUF_LEN - 1] = '_';
428 if (fgets(buf, AIO_BUF_LEN, af->fp) == NULL)
429 break;
431 if (buf[AIO_BUF_LEN - 1] == '\0' && buf[AIO_BUF_LEN - 2] != '\n') {
432 Jim_AppendString(interp, objPtr, buf, AIO_BUF_LEN - 1);
434 else {
435 len = strlen(buf);
437 if (len && (buf[len - 1] == '\n')) {
438 /* strip "\n" */
439 len--;
442 Jim_AppendString(interp, objPtr, buf, len);
443 break;
446 if (ferror(af->fp) && errno != EAGAIN && errno != EINTR) {
447 /* I/O error */
448 Jim_FreeNewObj(interp, objPtr);
449 JimAioSetError(interp, af->filename);
450 clearerr(af->fp);
451 return JIM_ERR;
454 if (argc) {
455 if (Jim_SetVariable(interp, argv[0], objPtr) != JIM_OK) {
456 Jim_FreeNewObj(interp, objPtr);
457 return JIM_ERR;
460 len = Jim_Length(objPtr);
462 if (len == 0 && feof(af->fp)) {
463 /* On EOF returns -1 if varName was specified */
464 len = -1;
466 Jim_SetResultInt(interp, len);
468 else {
469 Jim_SetResult(interp, objPtr);
471 return JIM_OK;
474 static int aio_cmd_puts(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
476 AioFile *af = Jim_CmdPrivData(interp);
477 int wlen;
478 const char *wdata;
479 Jim_Obj *strObj;
481 if (argc == 2) {
482 if (!Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) {
483 return -1;
485 strObj = argv[1];
487 else {
488 strObj = argv[0];
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) {
494 return JIM_OK;
497 JimAioSetError(interp, af->filename);
498 return JIM_ERR;
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);
505 char *buf;
506 union sockaddr_any sa;
507 long len;
508 socklen_t salen = sizeof(sa);
509 int rlen;
511 if (Jim_GetLong(interp, argv[0], &len) != JIM_OK) {
512 return JIM_ERR;
515 buf = Jim_Alloc(len + 1);
517 rlen = recvfrom(fileno(af->fp), buf, len, 0, &sa.sa, &salen);
518 if (rlen < 0) {
519 Jim_Free(buf);
520 JimAioSetError(interp, NULL);
521 return JIM_ERR;
523 buf[rlen] = 0;
524 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, rlen));
526 if (argc > 1) {
527 /* INET6_ADDRSTRLEN is 46. Add some for [] and port */
528 char addrbuf[60];
530 #if IPV6
531 if (sa.sa.sa_family == PF_INET6) {
532 addrbuf[0] = '[';
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));
537 else
538 #endif
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) {
546 return JIM_ERR;
550 return JIM_OK;
554 static int aio_cmd_sendto(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
556 AioFile *af = Jim_CmdPrivData(interp);
557 int wlen;
558 int len;
559 const char *wdata;
560 union sockaddr_any sa;
561 const char *addr = Jim_String(argv[1]);
562 int salen;
564 if (IPV6 && af->addr_family == PF_INET6) {
565 if (JimParseIPv6Address(interp, addr, &sa, &salen) != JIM_OK) {
566 return JIM_ERR;
569 else if (JimParseIpAddress(interp, addr, &sa, &salen) != JIM_OK) {
570 return JIM_ERR;
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);
576 if (len < 0) {
577 JimAioSetError(interp, NULL);
578 return JIM_ERR;
580 Jim_SetResultInt(interp, len);
581 return JIM_OK;
584 static int aio_cmd_accept(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
586 AioFile *af = Jim_CmdPrivData(interp);
587 int sock;
588 union sockaddr_any sa;
589 socklen_t addrlen = sizeof(sa);
591 sock = accept(af->fd, &sa.sa, &addrlen);
592 if (sock < 0) {
593 JimAioSetError(interp, NULL);
594 return JIM_ERR;
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);
605 long backlog;
607 if (Jim_GetLong(interp, argv[0], &backlog) != JIM_OK) {
608 return JIM_ERR;
611 if (listen(af->fd, backlog)) {
612 JimAioSetError(interp, NULL);
613 return JIM_ERR;
616 return JIM_OK;
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);
626 return JIM_ERR;
628 return JIM_OK;
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));
636 return JIM_OK;
639 static int aio_cmd_close(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
641 Jim_DeleteCommand(interp, Jim_String(argv[0]));
642 return JIM_OK;
645 static int aio_cmd_seek(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
647 AioFile *af = Jim_CmdPrivData(interp);
648 int orig = SEEK_SET;
649 long offset;
651 if (argc == 2) {
652 if (Jim_CompareStringImmediate(interp, argv[1], "start"))
653 orig = SEEK_SET;
654 else if (Jim_CompareStringImmediate(interp, argv[1], "current"))
655 orig = SEEK_CUR;
656 else if (Jim_CompareStringImmediate(interp, argv[1], "end"))
657 orig = SEEK_END;
658 else {
659 return -1;
662 if (Jim_GetLong(interp, argv[0], &offset) != JIM_OK) {
663 return JIM_ERR;
665 if (fseek(af->fp, offset, orig) == -1) {
666 JimAioSetError(interp, af->filename);
667 return JIM_ERR;
669 return JIM_OK;
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));
677 return JIM_OK;
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);
685 return JIM_OK;
688 #ifdef O_NDELAY
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;
695 if (argc) {
696 long nb;
698 if (Jim_GetLong(interp, argv[0], &nb) != JIM_OK) {
699 return JIM_ERR;
701 if (nb) {
702 fmode |= O_NDELAY;
704 else {
705 fmode &= ~O_NDELAY;
707 fcntl(af->fd, F_SETFL, fmode);
708 af->flags = fmode;
710 Jim_SetResultInt(interp, (fmode & O_NONBLOCK) ? 1 : 0);
711 return JIM_OK;
713 #endif
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[] = {
720 "none",
721 "line",
722 "full",
723 NULL
725 enum
727 OPT_NONE,
728 OPT_LINE,
729 OPT_FULL,
731 int option;
733 if (Jim_GetEnum(interp, argv[0], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
734 return JIM_ERR;
736 switch (option) {
737 case OPT_NONE:
738 setvbuf(af->fp, NULL, _IONBF, 0);
739 break;
740 case OPT_LINE:
741 setvbuf(af->fp, NULL, _IOLBF, BUFSIZ);
742 break;
743 case OPT_FULL:
744 setvbuf(af->fp, NULL, _IOFBF, BUFSIZ);
745 break;
747 return JIM_OK;
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)
768 int scriptlen = 0;
770 if (argc == 0) {
771 /* Return current script */
772 if (*scriptHandlerObj) {
773 Jim_SetResult(interp, *scriptHandlerObj);
775 return JIM_OK;
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 */
788 return JIM_OK;
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);
798 return JIM_OK;
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);
821 #endif
823 static const jim_subcmd_type aio_command_table[] = {
824 { "read",
825 "?-nonewline? ?len?",
826 aio_cmd_read,
829 /* Description: Read and return bytes from the stream. To eof if no len. */
831 { "copyto",
832 "handle ?size?",
833 aio_cmd_copy,
836 /* Description: Copy up to 'size' bytes to the given filehandle, or to eof if no size. */
838 { "gets",
839 "?var?",
840 aio_cmd_gets,
843 /* Description: Read one line and return it or store it in the var */
845 { "puts",
846 "?-nonewline? str",
847 aio_cmd_puts,
850 /* Description: Write the string, with newline unless -nonewline */
852 #if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
853 { "recvfrom",
854 "len ?addrvar?",
855 aio_cmd_recvfrom,
858 /* Description: Receive up to 'len' bytes on the socket. Sets 'addrvar' with receive address, if set */
860 { "sendto",
861 "str address",
862 aio_cmd_sendto,
865 /* Description: Send 'str' to the given address (dgram only) */
867 { "accept",
868 NULL,
869 aio_cmd_accept,
872 /* Description: Server socket only: Accept a connection and return stream */
874 { "listen",
875 "backlog",
876 aio_cmd_listen,
879 /* Description: Set the listen backlog for server socket */
881 #endif /* JIM_BOOTSTRAP */
882 { "flush",
883 NULL,
884 aio_cmd_flush,
887 /* Description: Flush the stream */
889 { "eof",
890 NULL,
891 aio_cmd_eof,
894 /* Description: Returns 1 if stream is at eof */
896 { "close",
897 NULL,
898 aio_cmd_close,
901 JIM_MODFLAG_FULLARGV,
902 /* Description: Closes the stream */
904 { "seek",
905 "offset ?start|current|end",
906 aio_cmd_seek,
909 /* Description: Seeks in the stream (default 'current') */
911 { "tell",
912 NULL,
913 aio_cmd_tell,
916 /* Description: Returns the current seek position */
918 { "filename",
919 NULL,
920 aio_cmd_filename,
923 /* Description: Returns the original filename */
925 #ifdef O_NDELAY
926 { "ndelay",
927 "?0|1?",
928 aio_cmd_ndelay,
931 /* Description: Set O_NDELAY (if arg). Returns current/new setting. */
933 #endif
934 { "buffering",
935 "none|line|full",
936 aio_cmd_buffering,
939 /* Description: Sets buffering */
941 #ifdef jim_ext_eventloop
942 { "readable",
943 "?readable-script?",
944 aio_cmd_readable,
947 /* Description: Returns script, or invoke readable-script when readable, {} to remove */
949 { "writable",
950 "?writable-script?",
951 aio_cmd_writable,
954 /* Description: Returns script, or invoke writable-script when writable, {} to remove */
956 { "onexception",
957 "?exception-script?",
958 aio_cmd_onexception,
961 /* Description: Returns script, or invoke exception-script when oob data, {} to remove */
963 #endif
964 { NULL }
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)
975 const char *mode;
976 const char *filename;
978 if (argc != 2 && argc != 3) {
979 Jim_WrongNumArgs(interp, 1, argv, "filename ?mode?");
980 return JIM_ERR;
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 == '|') {
989 Jim_Obj *evalObj[3];
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);
997 #endif
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)
1016 AioFile *af;
1017 char buf[AIO_CMD_LEN];
1018 int OpenFlags = 0;
1020 if (filename == NULL) {
1021 filename = Jim_NewStringObj(interp, hdlfmt, -1);
1024 Jim_IncrRefCount(filename);
1026 if (fh == NULL) {
1027 if (fd < 0) {
1028 fh = fopen(Jim_String(filename), mode);
1030 else {
1031 fh = fdopen(fd, mode);
1034 else {
1035 OpenFlags = AIO_KEEPOPEN;
1038 if (fh == NULL) {
1039 JimAioSetError(interp, filename);
1040 if (fd >= 0) {
1041 close(fd);
1043 Jim_DecrRefCount(interp, filename);
1044 return JIM_ERR;
1047 /* Create the file command */
1048 af = Jim_Alloc(sizeof(*af));
1049 memset(af, 0, sizeof(*af));
1050 af->fp = fh;
1051 af->fd = fileno(fh);
1052 af->filename = filename;
1053 #ifdef FD_CLOEXEC
1054 if ((OpenFlags & AIO_KEEPOPEN) == 0) {
1055 fcntl(af->fd, F_SETFD, FD_CLOEXEC);
1057 #endif
1058 af->OpenFlags = OpenFlags;
1059 #ifdef O_NDELAY
1060 af->flags = fcntl(af->fd, F_GETFL);
1061 #endif
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);
1068 return JIM_OK;
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[] = {
1077 "unix",
1078 "unix.server",
1079 "dgram",
1080 "dgram.server",
1081 "stream",
1082 "stream.server",
1083 "pipe",
1084 NULL
1086 enum
1088 SOCK_UNIX,
1089 SOCK_UNIX_SERVER,
1090 SOCK_DGRAM_CLIENT,
1091 SOCK_DGRAM_SERVER,
1092 SOCK_STREAM_CLIENT,
1093 SOCK_STREAM_SERVER,
1094 SOCK_STREAM_PIPE,
1095 SOCK_DGRAM6_CLIENT,
1096 SOCK_DGRAM6_SERVER,
1097 SOCK_STREAM6_CLIENT,
1098 SOCK_STREAM6_SERVER,
1100 int socktype;
1101 int sock;
1102 const char *hostportarg = NULL;
1103 int res;
1104 int on = 1;
1105 const char *mode = "r+";
1106 int family = PF_INET;
1107 Jim_Obj *argv0 = argv[0];
1108 int ipv6 = 0;
1110 if (argc > 1 && Jim_CompareStringImmediate(interp, argv[1], "-ipv6")) {
1111 if (!IPV6) {
1112 Jim_SetResultString(interp, "ipv6 not supported", -1);
1113 return JIM_ERR;
1115 ipv6 = 1;
1116 family = PF_INET6;
1118 argc -= ipv6;
1119 argv += ipv6;
1121 if (argc < 2) {
1122 wrongargs:
1123 Jim_WrongNumArgs(interp, 1, &argv0, "?-ipv6? type ?address?");
1124 return JIM_ERR;
1127 if (Jim_GetEnum(interp, argv[1], socktypes, &socktype, "socket type", JIM_ERRMSG) != JIM_OK)
1128 return JIM_ERR;
1130 Jim_SetEmptyResult(interp);
1132 hdlfmt = "aio.sock%ld";
1134 if (argc > 2) {
1135 hostportarg = Jim_String(argv[2]);
1138 switch (socktype) {
1139 case SOCK_DGRAM_CLIENT:
1140 if (argc == 2) {
1141 /* No address, so an unconnected dgram socket */
1142 sock = socket(family, SOCK_DGRAM, 0);
1143 if (sock < 0) {
1144 JimAioSetError(interp, NULL);
1145 return JIM_ERR;
1147 break;
1149 /* fall through */
1150 case SOCK_STREAM_CLIENT:
1152 union sockaddr_any sa;
1153 int salen;
1155 if (argc != 3) {
1156 goto wrongargs;
1159 if (ipv6) {
1160 if (JimParseIPv6Address(interp, hostportarg, &sa, &salen) != JIM_OK) {
1161 return JIM_ERR;
1164 else if (JimParseIpAddress(interp, hostportarg, &sa, &salen) != JIM_OK) {
1165 return JIM_ERR;
1167 sock = socket(family, (socktype == SOCK_DGRAM_CLIENT) ? SOCK_DGRAM : SOCK_STREAM, 0);
1168 if (sock < 0) {
1169 JimAioSetError(interp, NULL);
1170 return JIM_ERR;
1172 res = connect(sock, &sa.sa, salen);
1173 if (res) {
1174 JimAioSetError(interp, argv[2]);
1175 close(sock);
1176 return JIM_ERR;
1179 break;
1181 case SOCK_STREAM_SERVER:
1182 case SOCK_DGRAM_SERVER:
1184 union sockaddr_any sa;
1185 int salen;
1187 if (argc != 3) {
1188 goto wrongargs;
1191 if (ipv6) {
1192 if (JimParseIPv6Address(interp, hostportarg, &sa, &salen) != JIM_OK) {
1193 return JIM_ERR;
1196 else if (JimParseIpAddress(interp, hostportarg, &sa, &salen) != JIM_OK) {
1197 return JIM_ERR;
1199 sock = socket(family, (socktype == SOCK_DGRAM_SERVER) ? SOCK_DGRAM : SOCK_STREAM, 0);
1200 if (sock < 0) {
1201 JimAioSetError(interp, NULL);
1202 return JIM_ERR;
1205 /* Enable address reuse */
1206 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on));
1208 res = bind(sock, &sa.sa, salen);
1209 if (res) {
1210 JimAioSetError(interp, argv[2]);
1211 close(sock);
1212 return JIM_ERR;
1214 if (socktype == SOCK_STREAM_SERVER) {
1215 res = listen(sock, 5);
1216 if (res) {
1217 JimAioSetError(interp, NULL);
1218 close(sock);
1219 return JIM_ERR;
1222 hdlfmt = "aio.socksrv%ld";
1224 break;
1226 #ifdef HAVE_SYS_UN_H
1227 case SOCK_UNIX:
1229 struct sockaddr_un sa;
1230 socklen_t len;
1232 if (argc != 3 || ipv6) {
1233 goto wrongargs;
1236 if (JimParseDomainAddress(interp, hostportarg, &sa) != JIM_OK) {
1237 JimAioSetError(interp, argv[2]);
1238 return JIM_ERR;
1240 family = PF_UNIX;
1241 sock = socket(PF_UNIX, SOCK_STREAM, 0);
1242 if (sock < 0) {
1243 JimAioSetError(interp, NULL);
1244 return JIM_ERR;
1246 len = strlen(sa.sun_path) + 1 + sizeof(sa.sun_family);
1247 res = connect(sock, (struct sockaddr *)&sa, len);
1248 if (res) {
1249 JimAioSetError(interp, argv[2]);
1250 close(sock);
1251 return JIM_ERR;
1253 hdlfmt = "aio.sockunix%ld";
1254 break;
1257 case SOCK_UNIX_SERVER:
1259 struct sockaddr_un sa;
1260 socklen_t len;
1262 if (argc != 3 || ipv6) {
1263 goto wrongargs;
1266 if (JimParseDomainAddress(interp, hostportarg, &sa) != JIM_OK) {
1267 JimAioSetError(interp, argv[2]);
1268 return JIM_ERR;
1270 family = PF_UNIX;
1271 sock = socket(PF_UNIX, SOCK_STREAM, 0);
1272 if (sock < 0) {
1273 JimAioSetError(interp, NULL);
1274 return JIM_ERR;
1276 len = strlen(sa.sun_path) + 1 + sizeof(sa.sun_family);
1277 res = bind(sock, (struct sockaddr *)&sa, len);
1278 if (res) {
1279 JimAioSetError(interp, argv[2]);
1280 close(sock);
1281 return JIM_ERR;
1283 res = listen(sock, 5);
1284 if (res) {
1285 JimAioSetError(interp, NULL);
1286 close(sock);
1287 return JIM_ERR;
1289 hdlfmt = "aio.sockunixsrv%ld";
1290 break;
1292 #endif
1294 #ifdef HAVE_PIPE
1295 case SOCK_STREAM_PIPE:
1297 int p[2];
1299 if (argc != 2 || ipv6) {
1300 goto wrongargs;
1303 if (pipe(p) < 0) {
1304 JimAioSetError(interp, NULL);
1305 return JIM_ERR;
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);
1315 return JIM_OK;
1318 /* Can only be here if fdopen() failed */
1319 close(p[0]);
1320 close(p[1]);
1321 JimAioSetError(interp, NULL);
1322 return JIM_ERR;
1324 break;
1325 #endif
1326 default:
1327 Jim_SetResultString(interp, "Unsupported socket type", -1);
1328 return JIM_ERR;
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);
1343 return NULL;
1346 int Jim_aioInit(Jim_Interp *interp)
1348 if (Jim_PackageProvide(interp, "aio", "1.0", JIM_ERRMSG))
1349 return JIM_ERR;
1351 Jim_CreateCommand(interp, "open", JimAioOpenCommand, NULL, NULL);
1352 #ifndef JIM_ANSIC
1353 Jim_CreateCommand(interp, "socket", JimAioSockCommand, NULL, NULL);
1354 #endif
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");
1361 return JIM_OK;