1
0
This repository has been archived on 2024-07-22. You can view files and clone it, but cannot push or open issues or pull requests.
TP-Link_Archer-XR500v/EN7526G_3.18Kernel_SDK/apps/public/usb-modeswitch-1.2.3/jim/jim-aio.c
2024-07-22 01:58:46 -03:00

1354 lines
37 KiB
C
Executable File

/* Jim - A small embeddable Tcl interpreter
*
* Copyright 2005 Salvatore Sanfilippo <antirez@invece.org>
* Copyright 2005 Clemens Hintze <c.hintze@gmx.net>
* Copyright 2005 patthoyts - Pat Thoyts <patthoyts@users.sf.net>
* Copyright 2008 oharboe - Øyvind Harboe - oyvind.harboe@zylin.com
* Copyright 2008 Andrew Lunn <andrew@lunn.ch>
* Copyright 2008 Duane Ellis <openocd@duaneellis.com>
* Copyright 2008 Uwe Klein <uklein@klein-messgeraete.de>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE JIM TCL PROJECT ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* JIM TCL PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation
* are those of the authors and should not be interpreted as representing
* official policies, either expressed or implied, of the Jim Tcl Project.
**/
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include "jim.h"
#include "jimautoconf.h"
#if defined(HAVE_SYS_SOCKET_H) && defined(HAVE_SELECT) && defined(HAVE_NETINET_IN_H) && defined(HAVE_NETDB_H) && defined(HAVE_ARPA_INET_H)
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#ifdef HAVE_SYS_UN_H
#include <sys/un.h>
#endif
#else
#define JIM_ANSIC
#endif
#include "jim-eventloop.h"
#include "jim-subcmd.h"
#define AIO_CMD_LEN 32 /* e.g. aio.handleXXXXXX */
#define AIO_BUF_LEN 256 /* Can keep this small and rely on stdio buffering */
#define AIO_KEEPOPEN 1
#if defined(JIM_IPV6)
#define IPV6 1
#else
#define IPV6 0
#ifndef PF_INET6
#define PF_INET6 0
#endif
#endif
#if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
union sockaddr_any {
struct sockaddr sa;
struct sockaddr_in sin;
#if IPV6
struct sockaddr_in6 sin6;
#endif
};
#ifndef HAVE_INET_NTOP
const char *inet_ntop(int af, const void *src, char *dst, int size)
{
if (af != PF_INET) {
return NULL;
}
snprintf(dst, size, "%s", inet_ntoa(((struct sockaddr_in *)src)->sin_addr));
return dst;
}
#endif
#endif /* JIM_BOOTSTRAP */
typedef struct AioFile
{
FILE *fp;
Jim_Obj *filename;
int type;
int OpenFlags; /* AIO_KEEPOPEN? keep FILE* */
int fd;
#ifdef O_NDELAY
int flags;
#endif
Jim_Obj *rEvent;
Jim_Obj *wEvent;
Jim_Obj *eEvent;
int addr_family;
} AioFile;
static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
static int JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *filename,
const char *hdlfmt, int family, const char *mode);
#if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
static int JimParseIPv6Address(Jim_Interp *interp, const char *hostport, union sockaddr_any *sa, int *salen)
{
#if IPV6
/*
* An IPv6 addr/port looks like:
* [::1]
* [::1]:2000
* [fe80::223:6cff:fe95:bdc0%en1]:2000
* [::]:2000
* 2000
*
* Note that the "any" address is ::, which is the same as when no address is specified.
*/
char *sthost = NULL;
const char *stport;
int ret = JIM_OK;
struct addrinfo req;
struct addrinfo *ai;
stport = strrchr(hostport, ':');
if (!stport) {
/* No : so, the whole thing is the port */
stport = hostport;
hostport = "::";
sthost = Jim_StrDup(hostport);
}
else {
stport++;
}
if (*hostport == '[') {
/* This is a numeric ipv6 address */
char *pt = strchr(++hostport, ']');
if (pt) {
sthost = Jim_StrDupLen(hostport, pt - hostport);
}
}
if (!sthost) {
sthost = Jim_StrDupLen(hostport, stport - hostport - 1);
}
memset(&req, '\0', sizeof(req));
req.ai_family = PF_INET6;
if (getaddrinfo(sthost, NULL, &req, &ai)) {
Jim_SetResultFormatted(interp, "Not a valid address: %s", hostport);
ret = JIM_ERR;
}
else {
memcpy(&sa->sin, ai->ai_addr, ai->ai_addrlen);
*salen = ai->ai_addrlen;
sa->sin.sin_port = htons(atoi(stport));
freeaddrinfo(ai);
}
Jim_Free(sthost);
return ret;
#else
Jim_SetResultString(interp, "ipv6 not supported", -1);
return JIM_ERR;
#endif
}
static int JimParseIpAddress(Jim_Interp *interp, const char *hostport, union sockaddr_any *sa, int *salen)
{
/* An IPv4 addr/port looks like:
* 192.168.1.5
* 192.168.1.5:2000
* 2000
*
* If the address is missing, INADDR_ANY is used.
* If the port is missing, 0 is used (only useful for server sockets).
*/
char *sthost = NULL;
const char *stport;
int ret = JIM_OK;
stport = strrchr(hostport, ':');
if (!stport) {
/* No : so, the whole thing is the port */
stport = hostport;
sthost = Jim_StrDup("0.0.0.0");
}
else {
sthost = Jim_StrDupLen(hostport, stport - hostport);
stport++;
}
{
#ifdef HAVE_GETADDRINFO
struct addrinfo req;
struct addrinfo *ai;
memset(&req, '\0', sizeof(req));
req.ai_family = PF_INET;
if (getaddrinfo(sthost, NULL, &req, &ai)) {
ret = JIM_ERR;
}
else {
memcpy(&sa->sin, ai->ai_addr, ai->ai_addrlen);
*salen = ai->ai_addrlen;
freeaddrinfo(ai);
}
#else
struct hostent *he;
ret = JIM_ERR;
if ((he = gethostbyname(sthost)) != NULL) {
if (he->h_length == sizeof(sa->sin.sin_addr)) {
*salen = sizeof(sa->sin);
sa->sin.sin_family= he->h_addrtype;
memcpy(&sa->sin.sin_addr, he->h_addr, he->h_length); /* set address */
ret = JIM_OK;
}
}
#endif
sa->sin.sin_port = htons(atoi(stport));
}
Jim_Free(sthost);
if (ret != JIM_OK) {
Jim_SetResultFormatted(interp, "Not a valid address: %s", hostport);
}
return ret;
}
#ifdef HAVE_SYS_UN_H
static int JimParseDomainAddress(Jim_Interp *interp, const char *path, struct sockaddr_un *sa)
{
sa->sun_family = PF_UNIX;
snprintf(sa->sun_path, sizeof(sa->sun_path), "%s", path);
return JIM_OK;
}
#endif
#endif /* JIM_BOOTSTRAP */
static void JimAioSetError(Jim_Interp *interp, Jim_Obj *name)
{
if (name) {
Jim_SetResultFormatted(interp, "%#s: %s", name, strerror(errno));
}
else {
Jim_SetResultString(interp, strerror(errno), -1);
}
}
static void JimAioDelProc(Jim_Interp *interp, void *privData)
{
AioFile *af = privData;
JIM_NOTUSED(interp);
Jim_DecrRefCount(interp, af->filename);
if (!(af->OpenFlags & AIO_KEEPOPEN)) {
fclose(af->fp);
}
#ifdef jim_ext_eventloop
/* remove existing EventHandlers */
if (af->rEvent) {
Jim_DeleteFileHandler(interp, af->fp);
}
if (af->wEvent) {
Jim_DeleteFileHandler(interp, af->fp);
}
if (af->eEvent) {
Jim_DeleteFileHandler(interp, af->fp);
}
#endif
Jim_Free(af);
}
static int aio_cmd_read(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
char buf[AIO_BUF_LEN];
Jim_Obj *objPtr;
int nonewline = 0;
int neededLen = -1; /* -1 is "read as much as possible" */
if (argc && Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) {
nonewline = 1;
argv++;
argc--;
}
if (argc == 1) {
jim_wide wideValue;
if (Jim_GetWide(interp, argv[0], &wideValue) != JIM_OK)
return JIM_ERR;
if (wideValue < 0) {
Jim_SetResultString(interp, "invalid parameter: negative len", -1);
return JIM_ERR;
}
neededLen = (int)wideValue;
}
else if (argc) {
return -1;
}
objPtr = Jim_NewStringObj(interp, NULL, 0);
while (neededLen != 0) {
int retval;
int readlen;
if (neededLen == -1) {
readlen = AIO_BUF_LEN;
}
else {
readlen = (neededLen > AIO_BUF_LEN ? AIO_BUF_LEN : neededLen);
}
retval = fread(buf, 1, readlen, af->fp);
if (retval > 0) {
Jim_AppendString(interp, objPtr, buf, retval);
if (neededLen != -1) {
neededLen -= retval;
}
}
if (retval != readlen)
break;
}
/* Check for error conditions */
if (ferror(af->fp)) {
clearerr(af->fp);
/* eof and EAGAIN are not error conditions */
if (!feof(af->fp) && errno != EAGAIN) {
/* I/O error */
Jim_FreeNewObj(interp, objPtr);
JimAioSetError(interp, af->filename);
return JIM_ERR;
}
}
if (nonewline) {
int len;
const char *s = Jim_GetString(objPtr, &len);
if (len > 0 && s[len - 1] == '\n') {
objPtr->length--;
objPtr->bytes[objPtr->length] = '\0';
}
}
Jim_SetResult(interp, objPtr);
return JIM_OK;
}
static int aio_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
long count = 0;
long maxlen = LONG_MAX;
FILE *outfh = Jim_AioFilehandle(interp, argv[0]);
if (outfh == NULL) {
return JIM_ERR;
}
if (argc == 2) {
if (Jim_GetLong(interp, argv[1], &maxlen) != JIM_OK) {
return JIM_ERR;
}
}
while (count < maxlen) {
int ch = fgetc(af->fp);
if (ch == EOF || fputc(ch, outfh) == EOF) {
break;
}
count++;
}
if (ferror(af->fp)) {
Jim_SetResultFormatted(interp, "error while reading: %s", strerror(errno));
clearerr(af->fp);
return JIM_ERR;
}
if (ferror(outfh)) {
Jim_SetResultFormatted(interp, "error while writing: %s", strerror(errno));
clearerr(outfh);
return JIM_ERR;
}
Jim_SetResultInt(interp, count);
return JIM_OK;
}
static int aio_cmd_gets(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
char buf[AIO_BUF_LEN];
Jim_Obj *objPtr;
int len;
errno = 0;
objPtr = Jim_NewStringObj(interp, NULL, 0);
while (1) {
buf[AIO_BUF_LEN - 1] = '_';
if (fgets(buf, AIO_BUF_LEN, af->fp) == NULL)
break;
if (buf[AIO_BUF_LEN - 1] == '\0' && buf[AIO_BUF_LEN - 2] != '\n') {
Jim_AppendString(interp, objPtr, buf, AIO_BUF_LEN - 1);
}
else {
len = strlen(buf);
if (len && (buf[len - 1] == '\n')) {
/* strip "\n" */
len--;
}
Jim_AppendString(interp, objPtr, buf, len);
break;
}
}
if (ferror(af->fp) && errno != EAGAIN && errno != EINTR) {
/* I/O error */
Jim_FreeNewObj(interp, objPtr);
JimAioSetError(interp, af->filename);
clearerr(af->fp);
return JIM_ERR;
}
if (argc) {
if (Jim_SetVariable(interp, argv[0], objPtr) != JIM_OK) {
Jim_FreeNewObj(interp, objPtr);
return JIM_ERR;
}
len = Jim_Length(objPtr);
if (len == 0 && feof(af->fp)) {
/* On EOF returns -1 if varName was specified */
len = -1;
}
Jim_SetResultInt(interp, len);
}
else {
Jim_SetResult(interp, objPtr);
}
return JIM_OK;
}
static int aio_cmd_puts(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
int wlen;
const char *wdata;
Jim_Obj *strObj;
if (argc == 2) {
if (!Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) {
return -1;
}
strObj = argv[1];
}
else {
strObj = argv[0];
}
wdata = Jim_GetString(strObj, &wlen);
if (fwrite(wdata, 1, wlen, af->fp) == (unsigned)wlen) {
if (argc == 2 || putc('\n', af->fp) != EOF) {
return JIM_OK;
}
}
JimAioSetError(interp, af->filename);
return JIM_ERR;
}
#if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
static int aio_cmd_recvfrom(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
char *buf;
union sockaddr_any sa;
long len;
socklen_t salen = sizeof(sa);
int rlen;
if (Jim_GetLong(interp, argv[0], &len) != JIM_OK) {
return JIM_ERR;
}
buf = Jim_Alloc(len + 1);
rlen = recvfrom(fileno(af->fp), buf, len, 0, &sa.sa, &salen);
if (rlen < 0) {
Jim_Free(buf);
JimAioSetError(interp, NULL);
return JIM_ERR;
}
buf[rlen] = 0;
Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, rlen));
if (argc > 1) {
/* INET6_ADDRSTRLEN is 46. Add some for [] and port */
char addrbuf[60];
#if IPV6
if (sa.sa.sa_family == PF_INET6) {
addrbuf[0] = '[';
/* Allow 9 for []:65535\0 */
inet_ntop(sa.sa.sa_family, &sa.sin6.sin6_addr, addrbuf + 1, sizeof(addrbuf) - 9);
snprintf(addrbuf + strlen(addrbuf), 8, "]:%d", ntohs(sa.sin.sin_port));
}
else
#endif
{
/* Allow 7 for :65535\0 */
inet_ntop(sa.sa.sa_family, &sa.sin.sin_addr, addrbuf, sizeof(addrbuf) - 7);
snprintf(addrbuf + strlen(addrbuf), 7, ":%d", ntohs(sa.sin.sin_port));
}
if (Jim_SetVariable(interp, argv[1], Jim_NewStringObj(interp, addrbuf, -1)) != JIM_OK) {
return JIM_ERR;
}
}
return JIM_OK;
}
static int aio_cmd_sendto(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
int wlen;
int len;
const char *wdata;
union sockaddr_any sa;
const char *addr = Jim_String(argv[1]);
int salen;
if (IPV6 && af->addr_family == PF_INET6) {
if (JimParseIPv6Address(interp, addr, &sa, &salen) != JIM_OK) {
return JIM_ERR;
}
}
else if (JimParseIpAddress(interp, addr, &sa, &salen) != JIM_OK) {
return JIM_ERR;
}
wdata = Jim_GetString(argv[0], &wlen);
/* Note that we don't validate the socket type. Rely on sendto() failing if appropriate */
len = sendto(fileno(af->fp), wdata, wlen, 0, &sa.sa, salen);
if (len < 0) {
JimAioSetError(interp, NULL);
return JIM_ERR;
}
Jim_SetResultInt(interp, len);
return JIM_OK;
}
static int aio_cmd_accept(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
int sock;
union sockaddr_any sa;
socklen_t addrlen = sizeof(sa);
sock = accept(af->fd, &sa.sa, &addrlen);
if (sock < 0) {
JimAioSetError(interp, NULL);
return JIM_ERR;
}
/* Create the file command */
return JimMakeChannel(interp, NULL, sock, Jim_NewStringObj(interp, "accept", -1),
"aio.sockstream%ld", af->addr_family, "r+");
}
static int aio_cmd_listen(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
long backlog;
if (Jim_GetLong(interp, argv[0], &backlog) != JIM_OK) {
return JIM_ERR;
}
if (listen(af->fd, backlog)) {
JimAioSetError(interp, NULL);
return JIM_ERR;
}
return JIM_OK;
}
#endif /* JIM_BOOTSTRAP */
static int aio_cmd_flush(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
if (fflush(af->fp) == EOF) {
JimAioSetError(interp, af->filename);
return JIM_ERR;
}
return JIM_OK;
}
static int aio_cmd_eof(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
Jim_SetResultInt(interp, feof(af->fp));
return JIM_OK;
}
static int aio_cmd_close(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_DeleteCommand(interp, Jim_String(argv[0]));
return JIM_OK;
}
static int aio_cmd_seek(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
int orig = SEEK_SET;
long offset;
if (argc == 2) {
if (Jim_CompareStringImmediate(interp, argv[1], "start"))
orig = SEEK_SET;
else if (Jim_CompareStringImmediate(interp, argv[1], "current"))
orig = SEEK_CUR;
else if (Jim_CompareStringImmediate(interp, argv[1], "end"))
orig = SEEK_END;
else {
return -1;
}
}
if (Jim_GetLong(interp, argv[0], &offset) != JIM_OK) {
return JIM_ERR;
}
if (fseek(af->fp, offset, orig) == -1) {
JimAioSetError(interp, af->filename);
return JIM_ERR;
}
return JIM_OK;
}
static int aio_cmd_tell(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
Jim_SetResultInt(interp, ftell(af->fp));
return JIM_OK;
}
static int aio_cmd_filename(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
Jim_SetResult(interp, af->filename);
return JIM_OK;
}
#ifdef O_NDELAY
static int aio_cmd_ndelay(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
int fmode = af->flags;
if (argc) {
long nb;
if (Jim_GetLong(interp, argv[0], &nb) != JIM_OK) {
return JIM_ERR;
}
if (nb) {
fmode |= O_NDELAY;
}
else {
fmode &= ~O_NDELAY;
}
fcntl(af->fd, F_SETFL, fmode);
af->flags = fmode;
}
Jim_SetResultInt(interp, (fmode & O_NONBLOCK) ? 1 : 0);
return JIM_OK;
}
#endif
static int aio_cmd_buffering(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
static const char * const options[] = {
"none",
"line",
"full",
NULL
};
enum
{
OPT_NONE,
OPT_LINE,
OPT_FULL,
};
int option;
if (Jim_GetEnum(interp, argv[0], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
return JIM_ERR;
}
switch (option) {
case OPT_NONE:
setvbuf(af->fp, NULL, _IONBF, 0);
break;
case OPT_LINE:
setvbuf(af->fp, NULL, _IOLBF, BUFSIZ);
break;
case OPT_FULL:
setvbuf(af->fp, NULL, _IOFBF, BUFSIZ);
break;
}
return JIM_OK;
}
#ifdef jim_ext_eventloop
static void JimAioFileEventFinalizer(Jim_Interp *interp, void *clientData)
{
Jim_Obj *objPtr = clientData;
Jim_DecrRefCount(interp, objPtr);
}
static int JimAioFileEventHandler(Jim_Interp *interp, void *clientData, int mask)
{
Jim_Obj *objPtr = clientData;
return Jim_EvalObjBackground(interp, objPtr);
}
static int aio_eventinfo(Jim_Interp *interp, AioFile * af, unsigned mask, Jim_Obj **scriptHandlerObj,
int argc, Jim_Obj * const *argv)
{
int scriptlen = 0;
if (argc == 0) {
/* Return current script */
if (*scriptHandlerObj) {
Jim_SetResult(interp, *scriptHandlerObj);
}
return JIM_OK;
}
if (*scriptHandlerObj) {
/* Delete old handler */
Jim_DeleteFileHandler(interp, af->fp);
*scriptHandlerObj = NULL;
}
/* Now possibly add the new script(s) */
Jim_GetString(argv[0], &scriptlen);
if (scriptlen == 0) {
/* Empty script, so done */
return JIM_OK;
}
/* A new script to add */
Jim_IncrRefCount(argv[0]);
*scriptHandlerObj = argv[0];
Jim_CreateFileHandler(interp, af->fp, mask,
JimAioFileEventHandler, *scriptHandlerObj, JimAioFileEventFinalizer);
return JIM_OK;
}
static int aio_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
return aio_eventinfo(interp, af, JIM_EVENT_READABLE, &af->rEvent, argc, argv);
}
static int aio_cmd_writable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
return aio_eventinfo(interp, af, JIM_EVENT_WRITABLE, &af->wEvent, argc, argv);
}
static int aio_cmd_onexception(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
return aio_eventinfo(interp, af, JIM_EVENT_EXCEPTION, &af->wEvent, argc, argv);
}
#endif
static const jim_subcmd_type aio_command_table[] = {
{ .cmd = "read",
.args = "?-nonewline? ?len?",
.function = aio_cmd_read,
.minargs = 0,
.maxargs = 2,
.description = "Read and return bytes from the stream. To eof if no len."
},
{ .cmd = "copyto",
.args = "handle ?size?",
.function = aio_cmd_copy,
.minargs = 1,
.maxargs = 2,
.description = "Copy up to 'size' bytes to the given filehandle, or to eof if no size."
},
{ .cmd = "gets",
.args = "?var?",
.function = aio_cmd_gets,
.minargs = 0,
.maxargs = 1,
.description = "Read one line and return it or store it in the var"
},
{ .cmd = "puts",
.args = "?-nonewline? str",
.function = aio_cmd_puts,
.minargs = 1,
.maxargs = 2,
.description = "Write the string, with newline unless -nonewline"
},
#if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
{ .cmd = "recvfrom",
.args = "len ?addrvar?",
.function = aio_cmd_recvfrom,
.minargs = 1,
.maxargs = 2,
.description = "Receive up to 'len' bytes on the socket. Sets 'addrvar' with receive address, if set"
},
{ .cmd = "sendto",
.args = "str address",
.function = aio_cmd_sendto,
.minargs = 2,
.maxargs = 2,
.description = "Send 'str' to the given address (dgram only)"
},
{ .cmd = "accept",
.function = aio_cmd_accept,
.description = "Server socket only: Accept a connection and return stream"
},
{ .cmd = "listen",
.args = "backlog",
.function = aio_cmd_listen,
.minargs = 1,
.maxargs = 1,
.description = "Set the listen backlog for server socket"
},
#endif /* JIM_BOOTSTRAP */
{ .cmd = "flush",
.function = aio_cmd_flush,
.description = "Flush the stream"
},
{ .cmd = "eof",
.function = aio_cmd_eof,
.description = "Returns 1 if stream is at eof"
},
{ .cmd = "close",
.flags = JIM_MODFLAG_FULLARGV,
.function = aio_cmd_close,
.description = "Closes the stream"
},
{ .cmd = "seek",
.args = "offset ?start|current|end",
.function = aio_cmd_seek,
.minargs = 1,
.maxargs = 2,
.description = "Seeks in the stream (default 'current')"
},
{ .cmd = "tell",
.function = aio_cmd_tell,
.description = "Returns the current seek position"
},
{ .cmd = "filename",
.function = aio_cmd_filename,
.description = "Returns the original filename"
},
#ifdef O_NDELAY
{ .cmd = "ndelay",
.args = "?0|1?",
.function = aio_cmd_ndelay,
.minargs = 0,
.maxargs = 1,
.description = "Set O_NDELAY (if arg). Returns current/new setting."
},
#endif
{ .cmd = "buffering",
.args = "none|line|full",
.function = aio_cmd_buffering,
.minargs = 1,
.maxargs = 1,
.description = "Sets buffering"
},
#ifdef jim_ext_eventloop
{ .cmd = "readable",
.args = "?readable-script?",
.minargs = 0,
.maxargs = 1,
.function = aio_cmd_readable,
.description = "Returns script, or invoke readable-script when readable, {} to remove",
},
{ .cmd = "writable",
.args = "?writable-script?",
.minargs = 0,
.maxargs = 1,
.function = aio_cmd_writable,
.description = "Returns script, or invoke writable-script when writable, {} to remove",
},
{ .cmd = "onexception",
.args = "?exception-script?",
.minargs = 0,
.maxargs = 1,
.function = aio_cmd_onexception,
.description = "Returns script, or invoke exception-script when oob data, {} to remove",
},
#endif
{ 0 }
};
static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
return Jim_CallSubCmd(interp, Jim_ParseSubCmd(interp, aio_command_table, argc, argv), argc, argv);
}
static int JimAioOpenCommand(Jim_Interp *interp, int argc,
Jim_Obj *const *argv)
{
FILE *fp;
const char *hdlfmt;
const char *mode;
if (argc != 2 && argc != 3) {
Jim_WrongNumArgs(interp, 1, argv, "filename ?mode?");
return JIM_ERR;
}
mode = (argc == 3) ? Jim_String(argv[2]) : "r";
hdlfmt = Jim_String(argv[1]);
if (Jim_CompareStringImmediate(interp, argv[1], "stdin")) {
fp = stdin;
}
else if (Jim_CompareStringImmediate(interp, argv[1], "stdout")) {
fp = stdout;
}
else if (Jim_CompareStringImmediate(interp, argv[1], "stderr")) {
fp = stderr;
}
else {
const char *filename = Jim_String(argv[1]);
#ifdef jim_ext_tclcompat
/* If the filename starts with '|', use popen instead */
if (*filename == '|') {
Jim_Obj *evalObj[3];
evalObj[0] = Jim_NewStringObj(interp, "popen", -1);
evalObj[1] = Jim_NewStringObj(interp, filename + 1, -1);
evalObj[2] = Jim_NewStringObj(interp, mode, -1);
return Jim_EvalObjVector(interp, 3, evalObj);
}
#endif
hdlfmt = "aio.handle%ld";
fp = NULL;
}
/* Create the file command */
return JimMakeChannel(interp, fp, -1, argv[1], hdlfmt, 0, mode);
}
/**
* Creates a channel for fh/fd/filename.
*
* If fh is not NULL, uses that as the channel (and set AIO_KEEPOPEN).
* Otherwise, if fd is >= 0, uses that as the chanel.
* Otherwise opens 'filename' with mode 'mode'.
*
* hdlfmt is a sprintf format for the filehandle. Anything with %ld at the end will do.
* mode is used for open or fdopen.
*
* Creates the command and sets the name as the current result.
*/
static int JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *filename,
const char *hdlfmt, int family, const char *mode)
{
AioFile *af;
char buf[AIO_CMD_LEN];
int OpenFlags = 0;
Jim_IncrRefCount(filename);
if (fh == NULL) {
if (fd < 0) {
fh = fopen(Jim_String(filename), mode);
}
else {
fh = fdopen(fd, mode);
}
}
else {
OpenFlags = AIO_KEEPOPEN;
}
if (fh == NULL) {
JimAioSetError(interp, filename);
close(fd);
Jim_DecrRefCount(interp, filename);
return JIM_ERR;
}
/* Create the file command */
af = Jim_Alloc(sizeof(*af));
memset(af, 0, sizeof(*af));
af->fp = fh;
af->fd = fileno(fh);
af->filename = filename;
#ifdef FD_CLOEXEC
if ((OpenFlags & AIO_KEEPOPEN) == 0) {
fcntl(af->fd, F_SETFD, FD_CLOEXEC);
af->OpenFlags = OpenFlags;
}
#endif
#ifdef O_NDELAY
af->flags = fcntl(af->fd, F_GETFL);
#endif
af->addr_family = family;
snprintf(buf, sizeof(buf), hdlfmt, Jim_GetId(interp));
Jim_CreateCommand(interp, buf, JimAioSubCmdProc, af, JimAioDelProc);
Jim_SetResultString(interp, buf, -1);
return JIM_OK;
}
#if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
static int JimAioSockCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
const char *hdlfmt = "aio.unknown%ld";
const char *socktypes[] = {
"unix",
"unix.server",
"dgram",
"dgram.server",
"stream",
"stream.server",
"pipe",
NULL
};
enum
{
SOCK_UNIX,
SOCK_UNIX_SERVER,
SOCK_DGRAM_CLIENT,
SOCK_DGRAM_SERVER,
SOCK_STREAM_CLIENT,
SOCK_STREAM_SERVER,
SOCK_STREAM_PIPE,
SOCK_DGRAM6_CLIENT,
SOCK_DGRAM6_SERVER,
SOCK_STREAM6_CLIENT,
SOCK_STREAM6_SERVER,
};
int socktype;
int sock;
const char *hostportarg = NULL;
int res;
int on = 1;
const char *mode = "r+";
int family = PF_INET;
Jim_Obj *argv0 = argv[0];
int ipv6 = 0;
if (argc > 1 && Jim_CompareStringImmediate(interp, argv[1], "-ipv6")) {
if (!IPV6) {
Jim_SetResultString(interp, "ipv6 not supported", -1);
return JIM_ERR;
}
ipv6 = 1;
family = PF_INET6;
}
argc -= ipv6;
argv += ipv6;
if (argc < 2) {
wrongargs:
Jim_WrongNumArgs(interp, 1, &argv0, "?-ipv6? type ?address?");
return JIM_ERR;
}
if (Jim_GetEnum(interp, argv[1], socktypes, &socktype, "socket type", JIM_ERRMSG) != JIM_OK)
return JIM_ERR;
Jim_SetEmptyResult(interp);
hdlfmt = "aio.sock%ld";
if (argc > 2) {
hostportarg = Jim_String(argv[2]);
}
switch (socktype) {
case SOCK_DGRAM_CLIENT:
if (argc == 2) {
/* No address, so an unconnected dgram socket */
sock = socket(family, SOCK_DGRAM, 0);
if (sock < 0) {
JimAioSetError(interp, NULL);
return JIM_ERR;
}
break;
}
/* fall through */
case SOCK_STREAM_CLIENT:
{
union sockaddr_any sa;
int salen;
if (argc != 3) {
goto wrongargs;
}
if (ipv6) {
if (JimParseIPv6Address(interp, hostportarg, &sa, &salen) != JIM_OK) {
return JIM_ERR;
}
}
else if (JimParseIpAddress(interp, hostportarg, &sa, &salen) != JIM_OK) {
return JIM_ERR;
}
sock = socket(family, (socktype == SOCK_DGRAM_CLIENT) ? SOCK_DGRAM : SOCK_STREAM, 0);
if (sock < 0) {
JimAioSetError(interp, NULL);
return JIM_ERR;
}
res = connect(sock, &sa.sa, salen);
if (res) {
JimAioSetError(interp, argv[2]);
close(sock);
return JIM_ERR;
}
}
break;
case SOCK_STREAM_SERVER:
case SOCK_DGRAM_SERVER:
{
union sockaddr_any sa;
int salen;
if (argc != 3) {
goto wrongargs;
}
if (ipv6) {
if (JimParseIPv6Address(interp, hostportarg, &sa, &salen) != JIM_OK) {
return JIM_ERR;
}
}
else if (JimParseIpAddress(interp, hostportarg, &sa, &salen) != JIM_OK) {
return JIM_ERR;
}
sock = socket(family, (socktype == SOCK_DGRAM_SERVER) ? SOCK_DGRAM : SOCK_STREAM, 0);
if (sock < 0) {
JimAioSetError(interp, NULL);
return JIM_ERR;
}
/* Enable address reuse */
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on));
res = bind(sock, &sa.sa, salen);
if (res) {
JimAioSetError(interp, argv[2]);
close(sock);
return JIM_ERR;
}
if (socktype == SOCK_STREAM_SERVER) {
res = listen(sock, 5);
if (res) {
JimAioSetError(interp, NULL);
close(sock);
return JIM_ERR;
}
}
hdlfmt = "aio.socksrv%ld";
}
break;
#ifdef HAVE_SYS_UN_H
case SOCK_UNIX:
{
struct sockaddr_un sa;
socklen_t len;
if (argc != 3 || ipv6) {
goto wrongargs;
}
if (JimParseDomainAddress(interp, hostportarg, &sa) != JIM_OK) {
JimAioSetError(interp, argv[2]);
return JIM_ERR;
}
family = PF_UNIX;
sock = socket(PF_UNIX, SOCK_STREAM, 0);
if (sock < 0) {
JimAioSetError(interp, NULL);
return JIM_ERR;
}
len = strlen(sa.sun_path) + 1 + sizeof(sa.sun_family);
res = connect(sock, (struct sockaddr *)&sa, len);
if (res) {
JimAioSetError(interp, argv[2]);
close(sock);
return JIM_ERR;
}
hdlfmt = "aio.sockunix%ld";
break;
}
case SOCK_UNIX_SERVER:
{
struct sockaddr_un sa;
socklen_t len;
if (argc != 3 || ipv6) {
goto wrongargs;
}
if (JimParseDomainAddress(interp, hostportarg, &sa) != JIM_OK) {
JimAioSetError(interp, argv[2]);
return JIM_ERR;
}
family = PF_UNIX;
sock = socket(PF_UNIX, SOCK_STREAM, 0);
if (sock < 0) {
JimAioSetError(interp, NULL);
return JIM_ERR;
}
len = strlen(sa.sun_path) + 1 + sizeof(sa.sun_family);
res = bind(sock, (struct sockaddr *)&sa, len);
if (res) {
JimAioSetError(interp, argv[2]);
close(sock);
return JIM_ERR;
}
res = listen(sock, 5);
if (res) {
JimAioSetError(interp, NULL);
close(sock);
return JIM_ERR;
}
hdlfmt = "aio.sockunixsrv%ld";
break;
}
#endif
#ifdef HAVE_PIPE
case SOCK_STREAM_PIPE:
{
int p[2];
if (argc != 2 || ipv6) {
goto wrongargs;
}
if (pipe(p) < 0) {
JimAioSetError(interp, NULL);
return JIM_ERR;
}
if (JimMakeChannel(interp, NULL, p[0], argv[1], "aio.pipe%ld", 0, "r") == JIM_OK) {
Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
if (JimMakeChannel(interp, NULL, p[1], argv[1], "aio.pipe%ld", 0, "w") == JIM_OK) {
Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
Jim_SetResult(interp, objPtr);
return JIM_OK;
}
}
/* Can only be here if fdopen() failed */
close(p[0]);
close(p[1]);
JimAioSetError(interp, NULL);
return JIM_ERR;
}
break;
#endif
default:
Jim_SetResultString(interp, "Unsupported socket type", -1);
return JIM_ERR;
}
return JimMakeChannel(interp, NULL, sock, argv[1], hdlfmt, family, mode);
}
#endif /* JIM_BOOTSTRAP */
FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command)
{
Jim_Cmd *cmdPtr = Jim_GetCommand(interp, command, JIM_ERRMSG);
if (cmdPtr && !cmdPtr->isproc && cmdPtr->u.native.cmdProc == JimAioSubCmdProc) {
return ((AioFile *) cmdPtr->u.native.privData)->fp;
}
Jim_SetResultFormatted(interp, "Not a filehandle: \"%#s\"", command);
return NULL;
}
int Jim_aioInit(Jim_Interp *interp)
{
if (Jim_PackageProvide(interp, "aio", "1.0", JIM_ERRMSG))
return JIM_ERR;
Jim_CreateCommand(interp, "open", JimAioOpenCommand, NULL, NULL);
#ifndef JIM_ANSIC
Jim_CreateCommand(interp, "socket", JimAioSockCommand, NULL, NULL);
#endif
/* Takeover stdin, stdout and stderr */
Jim_EvalGlobal(interp, "open stdin; open stdout; open stderr");
return JIM_OK;
}