/*
* ...and:
* Copyright (C) 1998 Dave Cridland
* Copyright (C) 1998 Stanley J. Brooks
*/
/*
* This file implements a special-purpose streams-like file interface
* for use with S-Lang programs. It supports normal files, and TCP
* and UDP sockets, at the moment. Will hopefully be extended to handle
* serial devices in the near/distant future.
* It adds some Select-like calls, adjusted to be more slangy.
* It adds some generic networking stuff... Like Service and DNS
* lookups, etc.
* Note that the hton stuff is done here, instead of in the SLang files
* themselves... Much easier, but could be a bit of a pain.
* hton* and ntoh* are both interfaced here, too, indirectly via the
* array_get_u32() and array_put_u32() intrinsics. These let us
* read/write an integer in network-order (MSB 1st) from/to some
* offset in a Char_Type array. Well, we do got a dependancy on
* int being 32 bit here :(
*
* The ideas herein are heavily borrowed from John E. Davis's slfile.c,
* from slang-1.0.3. Many header declarations are copied from J.E.D.'s
* _slang.h file, since we need some of the hidden intrinsics in order
* to deal with:
* (1) Char_Type arrays -- vf_read(file,array,n) vf_write(file,array,n)
* (2) reference types -- vf_read(file,&buf,n)
* (3) SLang_Object_Type -- set_action(file,i,"function",cookie)
* where cookie is an arbitrary object to pass.
*
* Why do we need Char_Type arrays? Because strings can't handle
* embedded nulls, whereas binary files and sockets surely can and do.
* ^^^ somewhat outdated remark since slang-1.3.* now has BString_Type...
* so that vfile-module now needs some serious re-working and
* simplification.
*/
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/time.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>
#include <slang.h>
/* we need stripped-down version of SLang_Object_Type for cookies: */
typedef struct {
unsigned char data_type; /* SLANG_INT_TYPE, ... */
union {
void * p_val;
double f_val;
}v;
}
SLang_Object_Type;
/*
* some extra internals we need, ripped out of _slang.h
*/
/* From: _slang.h */
extern int SLang_pop(SLang_Object_Type *);
extern void SLang_free_object (SLang_Object_Type *);
#if 0
extern int SLang_push(SLang_Object_Type *); /* doesn't handle ref-cts, etc */
#endif
extern int _SLpush_slang_obj (SLang_Object_Type *);
extern int _SLang_deref_assign (SLang_Ref_Type *);
extern int _SLstack_depth(void);
/* End: _slang.h */
/*
*
* Begin vfile file interface code
*
*/
static int VFerrno = 0;
static int char_array_data(SLang_Array_Type *at, void **data, int len)
{
int ct;
if (at->data_type != SLANG_CHAR_TYPE) {
SLang_doerror("Operation requires character array");
return -1;
}
if (len<0) ct = at->num_elements;
else if (len > at->num_elements) {
SLang_doerror("Too much data for array size");
return -1;
}else ct = len;
*data = at->data;
return ct;
}
#ifndef AR_GETPUT
static void ar_get_string(void)
{
int ix,len = 0;
char *p,*s = NULL;
void *data;
SLang_Array_Type *at = NULL;
if (SLang_pop_integer(&ix)) goto free_fail;
if (-1 == SLang_pop_array (&at, 0)) goto free_fail;
len = char_array_data(at,&data,-1); /* len is size of array */
if (ix < 0 || ix > len) {
len = 0;
goto free_fail;
}
if (!(len -= ix)) goto free_fail;
s = (char*)data+ix;
p = memchr(s,0,len);
if (p) len = p - s;
free_fail:
if (at) SLang_free_array (at);
p = SLmake_nstring(s,len+1);
if (p) {
SLang_push_string(p);
SLfree(p);
}
return;
}
/* ar_get_u32() soon to be replaced by pack()/unpack() */
static int ar_get_u32(void)
{
int val=0,ix,len;
void *data;
SLang_Array_Type *at = NULL;
if (SLang_pop_integer(&ix)) goto free_fail;
if (-1 == SLang_pop_array (&at, 0)) goto free_fail;
len = char_array_data(at,&data,-1);
if (ix < 0 || len < (ix+4)) goto free_fail; /* ct = -1 */
val = *(int*)((char*)data+ix);
val = ntohl((unsigned long int)val);
free_fail:
if (at) SLang_free_array (at);
return val;
}
static void ar_put_u32(void)
{
int val,ix,len;
void *data;
SLang_Array_Type *at = NULL;
if (SLang_pop_integer(&val)) goto free_fail;
if (SLang_pop_integer(&ix)) goto free_fail;
if (-1 == SLang_pop_array (&at, 0)) goto free_fail;
len = char_array_data(at,&data,-1);
if (ix < 0 || len < (ix+4)) goto free_fail;
val = htonl((unsigned long int)val);
*(int*)((char*)data+ix) = val;
free_fail:
if (at) SLang_free_array (at);
return;
}
#endif
#define SJ_VFILE_TYPE 128
typedef struct _VFILE
{
struct _VFILE *next;
int fd; /* file# (0,1,2 are stdin,stdout,stderr) */
char *file; /* file name associated with pointer */
unsigned int flags; /* read,write,socket,etc */
SLang_MMT_Type *mmt; /* itself as an mmt */
char *rbuf0; /* address of alloc'd buff, or NULL */
char *rbufp; /* points 1st byte of read data */
char *rbufq; /* [bufp,bufq) has no rtermn char */
char *rbufr; /* [bufp,bufr) is data already read */
char *rbuft; /* above top-of-buffer (rbuf0+len(rbuf)) */
int rthrsh; /* event when this many bytes in rbuf */
int rtermn; /* event when this char is received (if>=0) */
int rerror; /* last error reported upon read */
int werror; /* last error reported upon write */
pid_t pid;
struct _VFILE *wrvf; /* if non-NULL, data read pipes to this VFILE */
struct _VFILE *rdvf; /* if non-NULL, data written as it arrives here */
struct sockaddr_in sin; /* remote sockaddr_in, when relevant */
SLang_Name_Type *fns[4]; /* the functions to execute */
SLang_Object_Type cookies[4]; /* cookie structs for functions */
}
VFILE;
static char* VFerrmsg = "";
/*
thoughts on linked pair rdvf,wrvf:
restrict to rtermn=-1, thrsh>0
will read into rdvf while r<top
will write when rdata >=thrsh
SO: (1) buffer refill is as normal,
(2) write-trigger is dependant on rdata>=thrsh
*/
static VFILE *vfile_last = NULL;
static int vfile_list_dirty = 0;
#define SJ_READ 0x01
#define SJ_WRITE 0x02
#define SJ_BINARY 0x04
#define SJ_TCP 0x08
#define SJ_UDPu 0x10
#define SJ_UDPc 0x20
#define SJ_SIN 0x80
#define SJ_PROC 0x100
#define SJ_POPEN 0x200
#define SJ_EOF 0x2000
#define SJ_ERROR 0x4000
/* UDPc is 'connected' (use send/recv) */
/* UDPu is not connected (use sendto/recvfrom) */
#define SJ_UDP 0x30
#define SJ_SOCKET 0x38
/* String_Type ftp_hostport(host,port)
* converts (int) host,port to the ascii h,h,h,h,p,p format
* which ftp PORT command wants
*/
static char *VF_ftp_hostport(int *host,int *port)
{
static char psz[32]; /* must be static! */
char *cp;
unsigned int v,i;
v = *host;
for (cp=psz,i = 4; i-- ; ) {
sprintf(cp,"%d,", v >> 24);
cp += strlen(cp);
v = v << 8;
}
v = *port & 0xffff;
sprintf(cp,"%d,%d", v >> 8, v);
return psz;
}
static int VF_getservbyname(char *service)
{
struct servent *sp;
if (NULL == (sp = getservbyname(service, "tcp")))
return -1;
return (ntohs(sp->s_port));
}
static int VF_inet_addr(char *DottedQuad)
{
return (int) ntohl(inet_addr(DottedQuad));
}
static char *VF_inet_ntoa(int *ipnum)
{
struct in_addr in;
in.s_addr = htonl(*ipnum);
return inet_ntoa(in);
}
static int VF_gethostbyname(char *hostname)
{
struct hostent *hp;
if (NULL == (hp = gethostbyname(hostname)))
return (-1);
return (ntohl(*(unsigned long *) hp->h_addr));
}
static char *VF_gethostbyaddr(int *host)
{
struct hostent *hp;
char *dummy;
int h;
h = htonl(*host);
if (NULL == (hp = gethostbyaddr((char *) &h, 4, AF_INET)))
return "";
dummy = SLmake_string(hp->h_name);
return dummy;
}
static int set_O_NONBLOCK(int fd)
{
int flags,r = -1;
if (-1 == (flags=fcntl(fd,F_GETFL)))
VFerrmsg="Couldn't fcntl(fd,F_GETFL) on fd.";
else
if (-1 == fcntl(fd,F_SETFL,flags|O_NONBLOCK))
VFerrmsg="Couldn't set O_NONBLOCK on fd";
else r = 0;
return r;
}
static int
udp_bind(unsigned long ip, int port, struct sockaddr_in *From)
{
int s;
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
VFerrno=errno;
VFerrmsg ="Unable to create socket";
return -3;
}
bzero((char *) From, sizeof(struct sockaddr_in));
From->sin_addr.s_addr = htonl(ip);
From->sin_family = AF_INET;
From->sin_port = htons(port);
if (bind(s, (struct sockaddr *) From, sizeof(struct sockaddr_in)) < 0) {
VFerrno=errno;
VFerrmsg="Couldn't bind socket.";
return -5;
}
if (set_O_NONBLOCK(s)) return -4;
return s;
}
static int
sock_connect(int type, unsigned long ip, int port, struct sockaddr_in *Rem)
{
int s;
bzero((char *) Rem, sizeof(struct sockaddr_in));
Rem->sin_addr.s_addr = htonl(ip);
Rem->sin_family = AF_INET;
Rem->sin_port = htons(port);
if ((s = socket(AF_INET, type, 0)) < 0) {
VFerrno=errno;
VFerrmsg ="Unable to create socket";
return -3;
}
if (connect(s, (struct sockaddr *) Rem, sizeof(struct sockaddr_in)) < 0) {
VFerrno=errno;
VFerrmsg="Unable to connect.";
return -5;
}
if (set_O_NONBLOCK(s)) return -4;
return s;
}
static int tcp_listen(void)
{
struct sockaddr_in Loc;
int s;
bzero((char *) &Loc, sizeof(Loc));
Loc.sin_addr.s_addr = INADDR_ANY;
Loc.sin_family = AF_INET;
Loc.sin_port = 0;
if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
VFerrno=errno;
VFerrmsg="Unable to create socket.";
return -3;
}
if (set_O_NONBLOCK(s)) return -4;
if (bind(s, (struct sockaddr *) &Loc, sizeof(Loc)) < 0) {
VFerrno=errno;
VFerrmsg="Couldn't bind socket.";
return -5;
}
if (listen(s, 5) < 0) {
VFerrno=errno;
VFerrmsg = "Couldn't listen socket.";
return -6;
}
return s;
}
static unsigned int convert_flags(int mode)
{
unsigned int flags;
switch (mode & O_ACCMODE) {
case O_RDONLY: flags = SJ_READ; break;
case O_WRONLY: flags = SJ_WRITE; break;
case O_RDWR: flags = SJ_READ|SJ_WRITE; break;
default:
SLang_verror(SL_INVALID_PARM, "invalid open mode %08x", mode);
flags=0;
}
return flags;
}
/* returns pointer to file entry if it is open and consistent with
flags. Returns NULL otherwise */
static SLang_MMT_Type *pop_vfd(unsigned int flags, VFILE ** t_ptr)
{
VFILE *t;
SLang_MMT_Type *mmt;
if (!(mmt = SLang_pop_mmt(SJ_VFILE_TYPE))) {
VFerrno = EBADF;
return NULL;
}
t = (VFILE *) SLang_object_from_mmt(mmt);
if ((*t_ptr = t)) {
if (t->flags & flags) return mmt;
VFerrno = EACCES;
}else
VFerrno = EBADF;
SLang_free_mmt(mmt);
return NULL;
}
static int close_vfile_type(SLang_MMT_Type *mmt, VFILE *t)
{
int i, r = -1, free_ct = 0;
VFerrno = 0;
if (t->fd < 0) {
/* SLang_doerror("file already closed"); */
r = 0; goto return_r;
}
if (t->rdvf || t->wrvf) {
SLang_doerror("You must set_wrdep(*,*,0) before close");
goto return_r;
}
r = close(t->fd);
if (r != -1) {
vfile_list_dirty = 1; /* only that this 1 file is closed */
t->fd = -1;
if (t->pid) { /* SJ_PROC this probably needs improvement: */
(void) kill(t->pid,SIGHUP);
(void) waitpid(t->pid,NULL,0);
t->pid = 0;
}
if (t->rbuf0) {
SLfree(t->rbuf0);
t->rbuf0 = NULL;
t->rthrsh = 0; /* just 2b safe */
}
for (i = 0; i < 4; i++) {
if (t->fns[i]) {
free_ct++; /* need more frees */
t->fns[i] = NULL;
}
if(t->cookies[i].data_type) {
SLang_free_object(t->cookies + i);
t->cookies[i].data_type = 0;
}
}
while(free_ct--) SLang_free_mmt(mmt);
/* need more thought -- really only the vfile_list action-stuff */
/* should guard files even when explicit ref's to them have vanished */
}else
VFerrno = errno;
return_r: return r;
}
static int destroy_vfile_type0(VFILE * t)
{
VFILE *p,*pl;
int r = -1;
if (t == NULL) return 0;
/* fprintf(stderr,"\ndestroy fd=%d\n",t->fd); fflush(stderr); */
if (close_vfile_type(t->mmt,t) < 0) goto return_r;
if (t->file)
SLang_free_slstring(t->file);
/* now we find it, cut it out of list, and free its mem */
p = pl = vfile_last;
do {
if (p->next == t) break;
p = p->next;
}while (p != pl);
if (p->next == t) {
if (p == t)
vfile_last = NULL;
else {
p->next = t->next;
if (pl == t) vfile_last = p;
}
r = 0;
}else
fprintf(stderr,"\nNot in list: destroy fd=%d\n",t->fd); fflush(stderr);
(void) SLfree((void*)t);
return_r: return r;
}
static void destroy_vfile_type(unsigned char type, VOID_STAR ptr)
{
(void) type;
(void) destroy_vfile_type0((VFILE *) ptr);
}
/* VF_reset should be called before main process exits
* in order that all the child processes be killed
*/
static int VF_reset(void)
{
VFILE *pl,*p,*pn;
int r = 0;
if ((pl = vfile_last)) {
for (p = pl->next; ;p = pn) {
pn = p->next;
if (destroy_vfile_type0(p)) r++;
if (p == pl) break; /* just did last one. */
}
}
return r;
}
static int VF_close(void)
{
SLang_MMT_Type *mmt;
VFILE *t;
int r = -1;
if ((mmt = pop_vfd(0xffff, &t))) {
r = close_vfile_type(mmt,t);
SLang_free_mmt(mmt);
}
return r;
}
static int VF_unlink(char * file)
{
int r = unlink(file);
VFerrno = errno;
return r;
}
static int VF_fileno(void)
{
SLang_MMT_Type *mmt;
VFILE *t;
int fd;
if (!(mmt=pop_vfd(0xFFFF, &t))) return -1;
fd = t->fd;
SLang_free_mmt (mmt);
return fd;
}
static int VF_size(void)
{
SLang_MMT_Type *mmt;
VFILE *t;
int fd,current,size,r=-1;
VFerrno = 0;
if (!(mmt=pop_vfd(0xFFFF, &t))) return -1;
fd = t->fd;
r = lseek(fd,0,SEEK_CUR);
if (r == -1) goto return_r;
current = r;
r = lseek(fd,0,SEEK_END);
if (r == -1) goto return_r;
size = r;
r = lseek(fd,current,SEEK_SET);
if (r == -1) goto return_r;
r = size;
return_r: if (r == -1) VFerrno = errno;
SLang_free_mmt (mmt);
return r;
}
static int VF_seek(int *disp, int *whence)
{
SLang_MMT_Type *mmt;
VFILE *t;
int r=-1;
VFerrno = 0;
if ((mmt=pop_vfd(0xFFFF, &t))) {
r = lseek(t->fd,*disp,*whence);
if (r == -1) VFerrno = errno;
SLang_free_mmt (mmt);
}
return r;
}
static int set_TCP_NODELAY(void)
{
int fd,flag,r;
if (SLang_pop_integer(&flag)) return -2;
if ((fd=VF_fileno()) < 0) return -2;
if (flag) flag = 1;
r = setsockopt(fd,IPPROTO_TCP,TCP_NODELAY,(char*)&flag,sizeof(flag));
if (r < 0) VFerrno = errno;
return r;
}
static void VF_clearerr(void)
{
SLang_MMT_Type *mmt;
VFILE *t;
if ((mmt = pop_vfd(0xffff, &t))) {
t->flags &= ~(SJ_ERROR|SJ_EOF);
SLang_free_mmt (mmt);
}
}
/* vf_error(VFile_Type)
* -1 is unknown error,
* 0 is no error,
* >0 is errno. (error will not be reported till all data read)
*/
static int VF_errno(void)
{
SLang_MMT_Type *mmt;
VFILE *t;
int r=0;
if (!(mmt = pop_vfd(0xffff, &t))) return -1;
if (t->flags & SJ_ERROR)
if (!t->rthrsh || t->rbufr == t->rbufp)
if (!(r = t->rerror)) r = -1;
SLang_free_mmt(mmt);
return r;
}
static char *VF_strerr(int *err)
{
return strerror(*err);
}
/* vf_eof(VFile_Type)
* -1 is invalid file,
* 0 is not EOF ( EOF doesn't appear till there's no more data to read)
* 1 is EOF
*/
static int VF_eof(void)
{
SLang_MMT_Type *mmt;
VFILE *t;
int r = 0;
if (!(mmt = pop_vfd(0xffff, &t))) return -1;
if (t->flags & SJ_EOF)
if (!t->rthrsh || t->rbufr == t->rbufp) r = 1;
SLang_free_mmt(mmt);
return r;
}
/* vf_rbuffct(VFile_Type)
* -1 is invalid file or not readable,
* >=0 is #bytes currently in r-buffer
*/
static int VF_rbuffct(void)
{
SLang_MMT_Type *mmt;
VFILE *t;
int r = -1;
if ((mmt = pop_vfd(SJ_READ, &t))) {
r = (t->rthrsh)? (t->rbufr == t->rbufp):0;
SLang_free_mmt(mmt);
}
return r;
}
/*
* failure returns NULL,
* success also pushes the mmt, if push != 0
*/
static SLang_MMT_Type*
add_to_vf_list(int fd,int flags,char* name,struct sockaddr_in *sin,pid_t pid,int push)
{
SLang_MMT_Type *mmt;
VFILE *t;
if (!(t = (VFILE*)SLmalloc(sizeof(VFILE)))) goto free0;
bzero((char *) t, sizeof(VFILE));
t->fd = fd;
if (sin) {
flags |= SJ_SIN;
memcpy(&t->sin,sin,sizeof(struct sockaddr_in));
}
if (pid) {
flags |= SJ_PROC;
t->pid = pid;
}
t->flags = flags;
t->rtermn = -1;
if (name && !(t->file = SLang_create_slstring(name))) goto free1;
t->mmt = mmt = SLang_create_mmt(SJ_VFILE_TYPE, (VOID_STAR) t);
if (!mmt) goto free1;
if (push && SLang_push_mmt(mmt)) goto free2;
# if 0
SLang_inc_mmt(mmt); /* because the list ref's it */
/* above needs more thought -- probably only wanted
* when there is an action set */
# endif
t->next = t;
if (vfile_last) {
t->next = vfile_last->next;
vfile_last->next = t;
}
vfile_last = t;
vfile_list_dirty = 1; /* an insert into list */
return (void*) mmt;
free2: SLang_free_mmt(mmt);
free1: SLfree((void*)t);
free0: return NULL;
}
/* returns -1 upon failure or returns a handle to file */
static void VF_open(char *file, int *uflags, int *mode)
{
unsigned int flags,fd;
VFerrno=0;
if (0 == (flags = convert_flags(*uflags))) goto null_fail;
fd = open(file, *uflags, *mode);
# if 0
fprintf(stderr,"open(\"%s\",%x,%o) returned %d\n",file,*uflags,*mode,fd);fflush(stderr);
# endif
if (fd==-1) {VFerrno=errno; goto null_fail;}
if (add_to_vf_list(fd,flags,file,NULL,0,1)) return;/* marks dirty if success */
/* failure */
close(fd);
null_fail:
(void) SLang_push_null();
}
/* extern int h_errno;
* HOST_NOT_FOUND
*/
static int child_HUPed = 0;
static void child_HUP(int signum)
{
child_HUPed = 1;
signal(SIGHUP,child_HUP);
}
/*
* this is a child process attached thru AF_UNIX socketpair
* to do host lookups without blocking main process.
* talk with it via a UDPc type VFile in main process
*
* requests/responses are structure:
* int ip; (net u32 IPnum or 0)
* int error; (net u32 input 0, output may be nonzero)
* char host[]; (hostname string w/0 any terminator... len is inferred)
*
* (1) Reverse DNS lookup:
* ----ip--- --error-- host---
* IN: request XXXX NONE (packet length is 8)
* Failure OUT: same h_errno NONE (packet length is 8)
* Success OUT: same 0 host (packet length > 8)
*
* (2) Forward DNS lookup:
* ----ip--- --error-- host---
* IN: XXXX XXXX host (packet length > 8)
* Failure OUT: -1 h_errno same (packet length same)
* Success OUT: valid ip 0 same (packet length same)
*
* Note: 'XXXX' means 'don't care'
*/
static void host_looker(int fd)
{
int r,w;
unsigned long ip;
static struct {
int ip;
int herror;
char host[1024];
} rb;
struct hostent *hp;
child_HUPed = 0; /* unnecessary */
(void) signal(SIGHUP,child_HUP);
(void) siginterrupt(SIGHUP,1);
/*
* we really want HUP to interrupt read() and write()
* which is default for POSIX, but i'm not positive how to
* do this for BSD. Is siginterrupt() the best way?
*/
for (r = 0; r<256; r++)
if (r != fd) close(r);
if (-1 == setsid()) goto my_exit;
while (1) {
do{
if (child_HUPed) goto my_exit;
r = recv(fd,(char*)&rb,sizeof(rb)-1,0);
}while (r == -1 && errno == EINTR);
if (r < 8) {
if (r <= 0) goto my_exit;
continue; /* hmm. what would be best in this odd event? */
}
rb.herror = 0;
if (r > 8) { /* gethostbyname */
((char*)&rb)[r] = 0;
if (-1 != (ip = inet_addr(rb.host)))
rb.ip = ip;
else if (!(hp = gethostbyname(rb.host))){
rb.ip = -1;
rb.herror = htonl(h_errno);
}else
rb.ip = *(unsigned long *) hp->h_addr;
}else{
/* gethostbyaddr */
if ((hp = gethostbyaddr((char*)&rb.ip, 4, AF_INET))) {
strcpy(rb.host,hp->h_name);
r += strlen(rb.host);
}else{
rb.herror = htonl(h_errno);
}
}
do{
if (child_HUPed) goto my_exit;
w = send(fd,(char*)&rb,r,0);
}while (r == -1 && errno == EINTR);
}
my_exit:
close(fd);
_exit(-1);
}
/* returns NULL upon failure or returns a VFile_Type */
static void VFopen_resolvr(void)
{
int r,fds[2];
pid_t pid;
VFerrno=0;
if ((r = socketpair(AF_UNIX,SOCK_DGRAM,0,fds))) {
VFerrno = errno;
goto null_fail;
}
pid = fork();
if (pid == 0) { /* child */
host_looker(fds[1]);
}else if (pid == -1) {
VFerrno = errno;
goto null_fail;
}
close(fds[1]);
if (add_to_vf_list(fds[0], SJ_UDPc|SJ_READ|SJ_WRITE, "UDPc:(Resolver)", NULL
,pid,1)) return;
close(fds[0]); /* need to kill pid also, fix me! */
null_fail:
(void) SLang_push_null();
return;
}
/* returns NULL upon failure or returns a VFile_Type */
static void VFudp_connect(int *ip, int *port)
{
int fd;
struct sockaddr_in Rem;
char name[64];
VFerrno=0;
if ((fd = sock_connect(SOCK_DGRAM, *ip, *port, &Rem)) < 0) goto null_fail;
sprintf(name, "UDPc:%s:%d",
inet_ntoa(Rem.sin_addr), *port);
if (add_to_vf_list(fd, SJ_UDPc|SJ_READ|SJ_WRITE, name, &Rem ,0,1)) return;
close(fd);
null_fail:
(void) SLang_push_null();
return;
}
static void VFudp_open(int *ip, int *port)
{
int fd;
struct sockaddr_in Loc;
char name[64];
VFerrno=0;
if ((fd = udp_bind(*ip, *port, &Loc)) < 0) goto null_fail;
sprintf(name, "UDPu:%s:%d", inet_ntoa(Loc.sin_addr), ntohs(Loc.sin_port));
if (add_to_vf_list(fd, SJ_UDPu|SJ_READ|SJ_WRITE, name,NULL,0,1)) return;
close(fd);
null_fail:
(void) SLang_push_null();
return;
}
/* returns NULL upon failure or returns a VFile_Type */
static void VFtcp_connect(int *ip, int *port)
{
int fd;
struct sockaddr_in Rem;
char name[64]; /* security: watch for buffer overflow! */
VFerrno=0;
if ((fd = sock_connect(SOCK_STREAM, *ip, *port, &Rem)) < 0) goto null_fail;
sprintf(name, "TCPc:%s:%d", inet_ntoa(Rem.sin_addr), *port);
if (add_to_vf_list(fd, SJ_TCP|SJ_READ|SJ_WRITE, name, &Rem,0,1)) return;
close(fd);
null_fail:
(void) SLang_push_null();
return;
}
/* returns NULL upon failure or returns a VFile_Type */
static void VFtcp_open(char *host, int *port)
{
struct hostent *hp;
int ip;
VFerrno=0;
if (NULL == (hp = gethostbyname(host))) {
VFerrmsg = "Unknown host";
(void) SLang_push_null();
}else{
ip = ntohl(*(unsigned long *) hp->h_addr);
VFtcp_connect(&ip,port);
}
return;
}
/* hmm... this opens a weird kinda read-socket */
static void VFtcp_listen(void)
{
int len,fd;
struct sockaddr_in Loc;
char name[64];
VFerrno=0;
if ((fd = tcp_listen()) < 0) goto null_fail;
len = sizeof(Loc);
if (getsockname(fd, (struct sockaddr *) &Loc, &len) < 0) {
VFerrmsg="Couldn't getsockname.";
VFerrno=errno;
}else{
int port = ntohs(Loc.sin_port);
SLang_push_integer(port);
sprintf(name, "TCPu:%s:%d", inet_ntoa(Loc.sin_addr), port);
if (add_to_vf_list(fd,SJ_TCP|SJ_READ,name,NULL,0,1)) return; /* success */
SLdo_pop(); /* the port pushed above */
}
close(fd);
null_fail:
(void) SLang_push_null();
return;
}
/*
* VFtcp_Type tcp_accept(VFile_Type listen_socket)
* failure: returns NULL
* success: returns VFile_Type new_socket
* AND (Integer_Type) remote port
* AND (String_Type) remote hostname
*/
static void VFtcp_accept(void)
{
struct sockaddr_in Rem;
struct hostent *hp;
char *remname;
int oldfd,fd;
int len = sizeof(Rem);
char name[64];
VFerrno=0;
if ((oldfd=VF_fileno()) < 0) goto null_fail;
fd = accept(oldfd, (struct sockaddr *) &Rem, &len);
if (fd < 0) {
VFerrno=errno;
goto null_fail;
}
(void)set_O_NONBLOCK(fd);
/* Note: It returns extra arguments IF successful. */
SLang_push_integer(ntohs(Rem.sin_port));
if ((hp = gethostbyaddr(
(char *) &Rem.sin_addr.s_addr,
sizeof(Rem.sin_addr.s_addr),
Rem.sin_family
))) {
remname = hp->h_name;
} else {
remname = inet_ntoa(Rem.sin_addr);
}
SLang_push_string(remname);
sprintf(name, "TCPc:%s:%d", inet_ntoa(Rem.sin_addr), ntohs(Rem.sin_port));
if (add_to_vf_list(fd, SJ_TCP|SJ_READ|SJ_WRITE, name, &Rem,0,1)) return;
/* failure */
SLdo_pop_n(2); /* the (host,port) pushed above */
close(fd);
null_fail:
(void) SLang_push_null();
return;
}
/* get_local_ipp(VFile_Type) */
/* returns (s_addr,s_port) of the socket (in host-order) */
static void VF_get_local_ipp(void)
{
int fd;
struct sockaddr_in sin;
int s = sizeof(sin);
unsigned long ip = -1;
unsigned short port = -1;
fd = VF_fileno(); /* get fileno by popping VFile_Type from stack */
if (fd >= 0 && !getsockname(fd, (struct sockaddr *) &sin, &s)) {
ip = ntohl(sin.sin_addr.s_addr);
port = ntohs(sin.sin_port);
}
SLang_push_integer(ip);
SLang_push_integer(port);
}
/* (IPnum,port) = get_remote_ipp(VFile_Type) */
/* returns (s_addr,s_port) of remote connection of socket (in host-order) */
/* or -1,-1 if error */
static void VF_get_remote_ipp(void)
{
SLang_MMT_Type *mmt;
VFILE *t;
unsigned long ip = -1;
unsigned short port = -1;
if ((mmt=pop_vfd(SJ_SOCKET, &t))) {
if (t->flags & SJ_SIN) {
port = ntohs(t->sin.sin_port);
ip = ntohl(t->sin.sin_addr.s_addr);
}
SLang_free_mmt(mmt);
}
SLang_push_integer((int) ip);
SLang_push_integer((int) port);
}
/* this is the ONLY place where data is pulled out of a rbuffer */
/* if data == NULL, just updates pointers, etc, like for reading */
static int copy_from_buf(VFILE *t,void * data,int ct)
{
char *p0,*p;
int m;
if (ct<=0) return 0;
p0 = t->rbuf0;
p = t->rbufp;
m = t->rbufr - p;
if (m < ct) return -1;
if (data) memcpy(data,p,ct);
p += ct;
t->rbufp = p;
t->rbufq = p;
if (p >= p0 + (t->rbuft - p0)/2) { /* copydown */
int l = t->rbufr - p; /* buff'd data */
memcpy(p0,p,l);
t->rbufp = p0;
t->rbufq = p0;
t->rbufr = p0 + l;
}
return ct;
}
/* this where unbuffered read() is done (rthrsh=0) */
static int read_to_mem(VFILE* t,void* p,int ct)
{
int r;
if (t->fd < 0 || t->flags & SJ_EOF || ct <= 0) return 0;
do {
if (t->flags & SJ_UDPc)
r = recv(t->fd,p,ct,0);
else
r = read(t->fd,p,ct);
}while(r<0 && errno==EINTR);
if (r<=0) {
if (!r)
t->flags |= SJ_EOF;
else {
t->flags |= SJ_ERROR;
t->rerror = errno;
}
}
return r;
}
/* this where read() is done for rthrsh>0 (buffered) */
static int read_into_buf(VFILE* t)
{
int l;
int fd = t->fd;
l = t->rbuft - t->rbufr;
if (l <= 0) { /* shouldn't happen */
SLang_doerror("read_into_buf() called with window=0");
return -1;
}
if (t->fd < 0 || t->flags & SJ_EOF) return 0; /* also shouldn't happen */
do {
l = read(fd,t->rbufr,l);
}while (l<0 && errno==EINTR);
/*fprintf(stderr,"fd=%d read %d bytes\n",fd,l); */
if (l>0)
t->rbufr += l;
else if (l==0)
t->flags |= SJ_EOF;
else {
t->rerror = errno;
t->flags |= SJ_ERROR;
}
return l;
}
/* this checks data already in rbuf to see if it would satisfy
* the 'readable' criteria, or has hit EOF or ERROR
* if not, returns -1
* if yes, returns the #bytes (>=0) which would be read
*/
static int check_buf_data(VFILE* t)
{
int l,m;
char *cp;
/* following might happen in the SLexecute loop of do_actions(): */
if (!t->rbuf0 || t->fd < 0) return -1;
l = t->rbufr - t->rbufp; /* buffer has l bytes of data */
if (!l || t->rtermn < 0 || !(m = t->rbufr - t->rbufq)) goto dflt_ret;
if (t->rbufq < t->rbufp) {
SLang_doerror("q<p");
t->rbufq = t->rbufp;
}
if ( (cp = (char*)memchr(t->rbufq,t->rtermn,m))
||(cp = (char*)memchr(t->rbufq,0,m)))
{
t->rbufq = cp;
return (++cp - t->rbufp);
}
t->rbufq = t->rbufr;
dflt_ret:
if (!(t->flags & (SJ_ERROR|SJ_EOF)) && (l < t->rthrsh)) l = -1;
return l;
}
/* vf_read(vfile,&stringbuf|char_array[,ct])
* Note:
* (1) vf_read(*,&buf) equiv to vf_read(*,&buf,4096)
* (2) vf_read(*,char_array) equiv to vf_read(*,char_array,sizeof(char_array))
*/
static int VF_read(void)
{
int fromlen,type,ct=-1,m,r=-1;
void *data;
char *s = NULL;
SLang_Array_Type *at = NULL;
SLang_MMT_Type *mmt = NULL;
VFILE *t;
SLang_Ref_Type *ref = NULL;
type = SLang_peek_at_stack();
if (type == SLANG_INT_TYPE) {
if (SLang_pop_integer(&ct)) return -1;
if (ct<0) ct = 0;
type = SLang_peek_at_stack();
}
if (type == SLANG_ARRAY_TYPE) {
if (-1 == SLang_pop_array (&at, 0)) return -1;
ct = char_array_data(at,&data,ct);
if (ct < 0) goto return_r; /* r = -1 */
}else{
if (-1 == SLang_pop_ref(&ref)) return -1;
if (ct<0) ct = 4*1024; /* dest is string w/o maxlen spec'd */
}
if (!(mmt = pop_vfd(SJ_READ, &t))) goto return_r;
/*fprintf(stderr,"fd=%d, rthrsh=%d, bytes=%d\n",t->fd,t->rthrsh,t->rbufr-t->rbufp); */
if (t->fd < 0) {
SLang_doerror("Read on closed VFile");
goto return_r;
}
if (t->flags & SJ_UDPu) {
/* an 'un-connected' UDP socket, so we recvfrom anywhere, */
/* and push the source ip#,port# back on stack. */
fromlen = sizeof(struct sockaddr_in);
t->sin.sin_addr.s_addr = INADDR_ANY;
t->sin.sin_family = AF_INET;
t->sin.sin_port = 0; /* means any */
if (!at) {
if(!(s = SLmalloc(ct+1))) goto return_r;
data = s;
}
do {
r = recvfrom(t->fd,data,ct,0,(struct sockaddr*)&t->sin,&fromlen);
}while(r<0 && errno==EINTR);
if (r < 0) goto return_r;
SLang_push_integer(ntohl(t->sin.sin_addr.s_addr));
SLang_push_integer(ntohs(t->sin.sin_port));
if (s) goto fin_s;
goto return_r;
}
if (!t->rthrsh) { /* unbuffered reads */
if (!at) {
if(!(s = SLmalloc(ct+1))) goto return_r;
data = s;
}
r = read_to_mem(t,data,ct);
if (s && r >= 0) goto fin_s;
goto return_r;
}
if (t->rtermn < 0)
m = t->rbufr - t->rbufp;
else
m = check_buf_data(t);
if (m < 0) m = 0;
else if (m > ct) m = ct;
if (at) {
r = copy_from_buf(t,data,m);
}else{
if(!(s = SLmalloc(ct+1))) goto return_r;
r = copy_from_buf(t,s,m);
fin_s: s[r] = 0;
if (SLang_push_string(s) || _SLang_deref_assign(ref))
r = -1;
}
return_r:
if (mmt) SLang_free_mmt(mmt);
if (at) SLang_free_array(at);
if (ref) SLang_free_ref(ref);
SLfree(s); /* NULL ok */
return r;
}
/* vf_write(vfile,string|bstring|char_array[,n]) (n is max bytes to write) */
/* vf_sendto(ip,port,vfile,string|char_array[,n]) (n is max bytes to write) */
/* Note: if n not specified, taken to be sizeof the (b)string or char_array */
static int VF_write(void)
{
int port,type,ct=-1,len=-1;
unsigned long ip;
void *data;
char *s = NULL;
SLang_BString_Type *bs = NULL;
SLang_Array_Type *at = NULL;
SLang_MMT_Type *mmt = NULL;
VFILE *t;
VFerrno = 0;
type = SLang_peek_at_stack();
if (type == SLANG_INT_TYPE) {
if (SLang_pop_integer(&len) || (len<0)) return -1;
type = SLang_peek_at_stack();
}
switch (type) {
case SLANG_ARRAY_TYPE:
if (-1 == SLang_pop_array (&at, 0)) return -1;
len = char_array_data(at,&data,len);
if (len < 0) goto free_fail; /* ct = -1 */
break;
case SLANG_STRING_TYPE:
if (SLang_pop_slstring(&s)) return -1;
type = strlen(s);
if (len < 0 || len > type) len = type;
data = s;
break;
case SLANG_BSTRING_TYPE:
if (SLang_pop_bstring(&bs)) return -1;
data = SLbstring_get_pointer(bs,&type);
if (len < 0 || len > type) len = type;
break;
default: return -1;
}
if (!(mmt = pop_vfd(SJ_WRITE, &t))) goto free_fail; /* ct = -1 */
if (t->fd < 0) {
SLang_doerror("Write on closed VFile");
goto free_fail;
}
if (t->flags & SJ_UDPu) {
if (SLang_pop_integer(&port)) goto free_fail;
if (SLang_pop_integer((int*)&ip)) goto free_fail;
t->sin.sin_addr.s_addr = htonl(ip);
t->sin.sin_family = AF_INET;
t->sin.sin_port = htons(port);
do {
ct = sendto(t->fd,data,len,0,(struct sockaddr*) &t->sin,sizeof(struct sockaddr_in));
}while(ct<0 && errno==EINTR);
}else{
do {
if (t->flags & SJ_UDPc)
ct = send(t->fd,data,len,0);
else
ct = write(t->fd,data,len);
}while(ct<0 && errno==EINTR);
}
if (-1 == ct) VFerrno = errno;
free_fail:
if (at) SLang_free_array (at);
if (bs) SLbstring_free(bs);
SLang_free_slstring(s); /* NULL is ok */
if (mmt) SLang_free_mmt(mmt);
return ct;
}
/* int VF_copybytes(VFile_Type dest, VFile_Type src, int count) */
/* copy up to count bytes from src to dest files -- */
/* [ they must have been linked by a prior set_wrdep(dest,src,1) ] */
/* return the number of bytes copied, */
/* or -1 in case of error */
/* or 0 maybe in case EOF or error on input file */
static int VF_copybytes(void)
{
int ct,m,r=-1;
SLang_MMT_Type *rd_mmt, *wr_mmt;
VFILE *rt,*wt;
VFerrno = 0;
if (SLang_pop_integer(&ct)) goto free0;
if (!(rd_mmt = pop_vfd(SJ_READ, &rt))) goto free0;
if (!(wr_mmt = pop_vfd(SJ_WRITE, &wt))) goto free1;
if (wt != rt->wrvf || rt != wt->rdvf) {
SLang_doerror("copybytes filepair not linked by set_wrdep()");
goto free2;
}
if (rt->rtermn < 0)
m = rt->rbufr - rt->rbufp;
else
m = check_buf_data(rt);
if (m <= 0) { r=m; goto free2; }
if (m > ct) m = ct;
do {
r = write(wt->fd,rt->rbufp,m);
}while(r < 0 && errno == EINTR);
if (r > 0)
(void) copy_from_buf(rt,NULL,r); /* this does copy-down & pointer updates */
else if (r < 0) {
wt->werror = errno;
VFerrno = errno;
}
free2: SLang_free_mmt(wr_mmt);
free1: SLang_free_mmt(rd_mmt);
free0: return r;
}
/*
* int tcp_is_readable(VFile_Type,int timeout)
* returns 1 if is readable,
* 0 if not, -- ah, inconsistency with check_buf_data
* -1 if error
* probably broken ATM
*/
static int VF_is_readable(void)
{
int fd,r;
struct timeval tv;
fd_set rfds;
SLang_MMT_Type *mmt = NULL;
VFILE *t;
VFerrno = 0;
tv.tv_usec = 0;
if (SLang_pop_integer((int *)&tv.tv_sec)) return -1;
if (!(mmt = pop_vfd(SJ_READ, &t))) return -1; /* ct = -1 */
if (t->fd < 0) {
SLang_doerror("Read on closed VFile");
r = -1; goto return_r;
}
r = check_buf_data(t);
if (r >= 0) goto return_r;
fd=t->fd;
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
r = select(fd + 1, &rfds, NULL, NULL, &tv);
if (r < 0) {
if (errno!=EINTR) VFerrno = errno;
goto return_r;
}
if (r>0 && t->rthrsh)
if (read_into_buf(t) > 0)
if (check_buf_data(t) < 0) r = 0;
return_r: SLang_free_mmt(mmt);
return r;
}
/* tcp_is_writeable() Like tcp_is_readable() */
static int VF_is_writeable(void)
{
int fd,r;
struct timeval tv;
fd_set rfds;
VFerrno = 0;
tv.tv_usec = 0;
if (SLang_pop_integer((int *)&tv.tv_sec)) return -1;
if ((fd=VF_fileno()) < 0) return -1;
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
#ifdef __linux__
do {
#endif
r = select(fd + 1, NULL, &rfds, NULL, &tv);
#ifdef __linux__
}while (r == -1 && errno == EINTR);
#endif
if (r < 0) VFerrno = errno; /* could be EINTR */
return r;
}
/* the next is a select on all read */
/* pushes a null, then all readable vfiles */
static void VF_select_on_all(int *secs)
{
fd_set rfds;
int fd,r,maxfd = -1;
VFILE *t,*tf;
struct timeval tv;
FD_ZERO(&rfds);
tv.tv_sec = *secs;
tv.tv_usec = 0;
(void) SLang_push_null(); /* mark top of list */
tf = NULL;
t = vfile_last;
do {
t = t->next;
if ((fd = t->fd) >= 0 && (t->flags & SJ_READ)) {
if(!t->rthrsh || t->rbufr < t->rbuft) {
if (maxfd < fd) maxfd = fd;
FD_SET(fd, &rfds);
}
if (check_buf_data(t) >= 0) {
SLang_push_mmt(t->mmt);
tf = t;
}
}
}while (t != vfile_last);
if (tf || maxfd < 0) return;
#ifdef __linux__
do {
#endif
r = select(maxfd + 1, &rfds, NULL, NULL, &tv);
#ifdef __linux__
}while(r == -1 && errno == EINTR);
#endif
if(r > 0) {
tf = NULL;
t = vfile_last;
do {
t = t->next;
if ((fd = t->fd) >= 0 && (t->flags & SJ_READ) && (FD_ISSET(fd,&rfds))){
if (t->rthrsh)
if (read_into_buf(t) > 0)
if (check_buf_data(t) < 0)
continue;
SLang_push_mmt(t->mmt);
}
}while (t != vfile_last);
}
return;
}
/*
* int set_wrdep(VFile wr,VFile rd,int y)
* y=0 clears, y!=0 sets.
* -1 is failure, 0 is success
* the read-file must be buffered (rthrsh>0)
* you must break the link before closing/destroying either file
*/
static int VF_set_wrdep(void)
{
SLang_MMT_Type *rd_mmt,*wr_mmt;
VFILE *r,*w;
int i,ret=-1;
if (SLang_pop_integer(&i)) goto free0;
if (!(rd_mmt = pop_vfd(SJ_READ, &r))) goto free0;
if (!(wr_mmt = pop_vfd(SJ_WRITE, &w))) goto free1;
if (r->fd < 0 || w->fd < 0) goto free2;
if (i) {
if (!r->rthrsh || r->fns[0] || r->wrvf || w->rdvf) goto free2;
r->wrvf = w;
w->rdvf = r;
vfile_list_dirty = 1; /* just the r/w pair */
return 0;
}
if (w != r->wrvf || r != w->rdvf) goto free2;
r->wrvf = NULL;
w->rdvf = NULL;
vfile_list_dirty = 1;
ret = 0;
SLang_free_mmt(wr_mmt);
SLang_free_mmt(rd_mmt);
free2: SLang_free_mmt(wr_mmt);
free1: SLang_free_mmt(rd_mmt);
free0: return ret;
}
static int VF_clr_action(void)
{
SLang_MMT_Type *mmt;
VFILE *t;
int i,rfch=1;
if (!(mmt = pop_vfd(0xffff, &t))) return -1;
/* go thru fns... */
for (i = 0; i < 4; i++) {
if(t->fns[i]) rfch++;
t->fns[i]=NULL;
if(t->cookies[i].data_type) {
SLang_free_object(t->cookies + i);
t->cookies[i].data_type = 0;
}
}
vfile_list_dirty = 1; /* just t */
while(rfch--) SLang_free_mmt(mmt);
return 0;
}
/*
* int set_action(VFile_Type,int which,NULL) clears action.which
* int set_action(VFile_Type,int which,String_Type func [,cookie] )
* like r = set_action(fserver,0,"doit")
* the 'which' is 0,1,2 corresponding to read,write,exception in select()
* returns <0 if error
* returns 0 on success
*/
static int VF_set_action(void)
{
char *func_name = NULL;
SLang_Name_Type *fnt = NULL;
int r=-1, vrfch=0, which;
SLang_MMT_Type *mmt = NULL;
VFILE *t;
SLang_Object_Type cookie;
cookie.data_type = 0; /* SLANG_UNDEFINED_TYPE */
if (SLang_Num_Function_Args > 3) {
if(SLang_pop(&cookie)) goto return_r; /*fail leaves 0 */
if (!cookie.data_type) {
SLang_verror(SL_INVALID_PARM,"Can't pass undefined parm as cookie");
SLang_free_object(&cookie);
cookie.data_type = SLANG_NULL_TYPE;
}
}
if (!cookie.data_type && SLang_peek_at_stack() == SLANG_NULL_TYPE)
SLdo_pop();
else
if (SLang_pop_slstring(&func_name)) goto return_r;
if (SLang_pop_integer(&which) || !(mmt = pop_vfd(0xffff, &t)))
goto return_r;
vrfch++;
if(func_name) {
fnt = SLang_get_function(func_name);
if (!fnt) {r = -2; goto return_r;}
if (t->fd < 0) {
SLang_doerror("Can't set_action() on closed VFile");
r = -3; goto return_r;
}
}
if (which < 0 || which > 4) goto return_r;
if(fnt && !which && t->wrvf) { r = -4; goto return_r;}
r = 0;
if(t->fns[which] != fnt) {
if ((fnt && !t->fns[which]) || (!fnt && t->fns[which])) {
vfile_list_dirty = 1; /* just t */
if(fnt) vrfch--;else vrfch++; /* need less/more free_mmt's */
}
t->fns[which] = fnt;
}
if (cookie.data_type || !fnt) {
/* changing cookie doesn't need to set vfile_list_dirty */
if (t->cookies[which].data_type)
SLang_free_object(t->cookies + which);
memcpy(t->cookies+which, &cookie, sizeof(SLang_Object_Type));
cookie.data_type = 0; /* so it's NOT free'd below */
}
return_r:
while(vrfch-->0) SLang_free_mmt(mmt);
SLang_free_slstring(func_name); /* NULL is ok */
if (cookie.data_type)
SLang_free_object(&cookie); /* only when error */
return r;
}
/*
* int vf_set_rtype(vfile,ev_ch,threshold)
* failure: returns -1,
* success: returns #bytes in rbuf
*/
static int VF_set_rmode(void)
{
int ch,ct,r=0,bufsize=1024;
SLang_MMT_Type *mmt;
VFILE *t;
if (SLang_pop_integer(&ct)
|| SLang_pop_integer(&ch)
|| !(mmt = pop_vfd(SJ_READ, &t))
|| ct < 0 || ct > 0x4000
) return -1;
if (t->flags & SJ_UDP) {
SLang_doerror("only default (unbuffered) rmode makes sense for UDP");
r = -1; goto return_r;
}
if (ch>255 || ch<0) ch=-1;
if (t->fd < 0) goto return_r;
if (!ct) {
if (t->rthrsh) { /* changing from buffered to unbuffered */
if (t->rbufr > t->rbufp) {
SLang_doerror("must read data before switching to unbuffered mode");
r = -1; goto return_r;
}
SLfree(t->rbuf0);
t->rbuf0 = NULL;
vfile_list_dirty = 1; /* just t */
}
if (ch>=0) {
SLang_doerror("termination char ignored in unbuffered mode");
ch = -1;
}
}
if (t->rtermn != ch) {
vfile_list_dirty = 1; /* just t */
t->rtermn = ch;
t->rbufq = t->rbufp;
}
if (t->rthrsh == ct) goto return_r;
if (!(t->rthrsh = ct)) goto return_r;
vfile_list_dirty = 1; /* just t */
if (bufsize<4*ct) bufsize = 4*ct; /* 2*ct is sufficient */
if (t->rbuf0 && bufsize <= (t->rbuft-t->rbuf0)) {
/* the old buffer is big enough */
r = t->rbufr - t->rbufp;
}else{
int q = 0;
char* newbuf = SLmalloc(bufsize);
if (!newbuf) {r = -1; goto return_r;}
if (t->rbuf0) { /* there was old buff */
if ((r = t->rbufr - t->rbufp) > 0) {
memcpy(newbuf,t->rbufp,r);
q = t->rbufq - t->rbufp;
}
SLfree(t->rbuf0); /* free old one */
}
t->rbuf0 = newbuf;
t->rbufp = newbuf;
t->rbufq = newbuf+q;
t->rbufr = newbuf+r;
t->rbuft = newbuf+bufsize;
}
return_r: SLang_free_mmt(mmt);
return r;
}
/*extern void SLirc_UpdateDisplay(void); */
/*void (*VF_do_actions_Hook)(void) = NULL; */
static int do_action_i(VFILE *t,int i)
{
int r,stkdep;
stkdep = _SLstack_depth();
SLang_start_arg_list();
if (i == 1 && t->rdvf) /* extra param (read's VFile) for copybytes */
SLang_push_mmt(t->rdvf->mmt);
SLang_push_mmt(t->mmt);
SLang_push_integer(i);
if (t->cookies[i].data_type)
_SLpush_slang_obj(t->cookies + i);
SLang_end_arg_list();
/* WARNING: this may change the list:
* in particular:
* it could close this (any) file,
* it could change one or more of actions for this (any) file
*/
r = SLexecute_function(t->fns[i]);
/*fprintf(stderr,"Did %s(%d,%d), r=%d\n",t->fns[i]->name,t->fd,i,r); */
stkdep = _SLstack_depth() - stkdep; /* % items left on stack. */
/* for some reason, this next seems to cause segfault when */
/* used from ./vf and do_actions() is called in SLang ?! */
if (stkdep && SLang_Error != USER_BREAK) {
SLang_verror(SL_APPLICATION_ERROR,
"Eeek... %s() left %d units on stack",t->fns[i]->name,stkdep);
if (stkdep>0) {
SLdo_pop_n(stkdep);
SLang_Error = 0; /* probably safe to keep going */
}
}
/* if (SLang_Error) { // tends to cause me to go nuts */
/* SLang_Error = 0; */
/* //SLirc_UpdateDisplay(); */
/* //if (VF_do_actions_Hook) (*VF_do_actions_Hook)(); */
/* } */
return r;
}
static int VF_rb_actions(void)
{
VFILE *t,*tn;
int ct0,ct=0;
char* rbufp;
/*int vf0 = vfile_list_dirty; */
do {
if(!(tn = vfile_last)) break;
vfile_list_dirty = 0;
ct0 = ct;
t = tn;
do {
t = t->next;
if (t->fd < 0 || !t->fns[0] || !t->rthrsh) continue;
if (check_buf_data(t) < 0) continue;
vfile_last = t;
rbufp = t->rbufp;
ct++;
do_action_i(t,0);
if (t->fd >= 0 && t->fns[0] && rbufp == t->rbufp && check_buf_data(t) >= 0) {
SLang_doerror("read-action did not handle data");
t->fns[0] = NULL; /* this to radically kill it */
}
}while (!vfile_list_dirty && t != tn);
}while (ct > ct0 || vfile_list_dirty);
/*if (ct&!vf0) fprintf(stderr,"Leaving rb_actions() ct=%d\n",ct); */
return ct;
}
/* int r = tcp_do_actions(int timeout) */
/* returns the number of actions actually executed */
static int VF_do_actions(int *timeout)
{
VFILE *t,*tn;
int i,r,rb,ct=0;
int max = -1;
/*int tim; */
struct timeval tv;
#ifndef HAVE_SELECT_HACK
struct timeval stv, ttv, rtv;
#endif
static int do_actions_dep = 0; /* static! init'd = 0 only once. */
static fd_set fds[3]; /* a big one, we could malloc */
if (do_actions_dep++) { /* we are NOT recursive! */
SLang_doerror("Can't call do_actions() recursively");
goto return_ct;
}
/*tim = (int)time(NULL); */
/*fprintf(stderr,"\n%d Enter do_actions, dirty=%d\n",tim,vfile_list_dirty); */
if (vfile_list_dirty) /* the outside world screwed around with vfile_list */
ct = VF_rb_actions();
if(!(tn = vfile_last)) goto return_ct;
if (ct) goto return_ct;
/*
* better exit & update display before long wait
* or we could do this way: update screen before the long wait!
* if (ct && VF_do_actions_Hook) (*VF_do_actions_Hook)();
*/
for (i = 0; i < 3; i++)
FD_ZERO(fds+i);
/* now figure out which fds bits to select on */
t = tn;
do {
int i;
t = t->next;
if (t->fd < 0) continue; /* closed file */
for (i = 0; i < 3; i++) {
switch(i) {
case 0: if (!(t->flags & SJ_READ)) continue;
if (t->flags & (SJ_EOF|SJ_ERROR)) continue;
if (t->rthrsh && t->rbufr >= t->rbuft) continue;
break;
case 1: if (!t->fns[1]) continue;
if (t->rdvf && check_buf_data(t->rdvf) < 0) continue;
break;
case 2: if (!t->fns[2]) continue;
}
FD_SET(t->fd, fds + i);
if (max < t->fd) max = t->fd;
}
}while(t != tn);
if (max<0) goto return_ct; /* none to watch for */
tv.tv_sec = *timeout;
tv.tv_usec = 0;
stv = tv;
#ifndef HAVE_SELECT_HACK
gettimeofday(&ttv, NULL);
#endif
do {
#ifndef HAVE_SELECT_HACK
stv = tv;
gettimeofday(&ttv, NULL);
#endif
r = select(max + 1, fds, fds+1, fds+2, &stv);
/* linux updates tv, so we can loop here - Only in v2.0 */
#ifndef HAVE_SELECT_HACK
gettimeofday(&rtv, NULL);
ttv.tv_sec-=rtv.tv_sec;
if(ttv.tv_usec < rtv.tv_usec)
ttv.tv_sec--;
tv.tv_usec-=ttv.tv_usec;
ttv.tv_sec-=ttv.tv_sec;
if(tv.tv_usec < ttv.tv_usec)
tv.tv_sec--;
tv.tv_usec-=ttv.tv_usec;
#endif
}while (r == -1 && errno == EINTR);
if (r<=0) goto return_ct;
rb = 0; /* will count # of read_into_buf's */
/* do all buffered reads 1st, since they can't mess up vfile_list */
/* t = tn; */
do {
t = t->next;
if (t->fd < 0) continue; /* this IS necessary */
if (FD_ISSET(t->fd, fds) && t->rthrsh) {
read_into_buf(t);
rb++;
}
}while (t != tn);
/*fprintf(stderr,"Post-select, r=%d, rb=%d\n",r,rb); */
if (r <= rb) goto return_ct_a; /* there was nothing BUT rb's */
if (vfile_list_dirty) fprintf(stderr,"Huh? dirty already!\n");
vfile_list_dirty = 0; /* it already is = 0, anyway */
/*t = tn; */
do {
int i;
t = t->next;
if (t->fd < 0) continue; /* necessary, as above */
for (i = 0; i < 3; i++) {
if (FD_ISSET(t->fd, fds+i) && t->fns[i]) {
/* above t->fns[i] is NOT redundant -- SLexecute below could change it */
if (!i && t->rthrsh) continue; /* buffer'd reads already handled */
ct++;
do_action_i(t,i); /* see warnings in this func! */
if (vfile_list_dirty) goto return_ct_a;
}
}
}while (t != tn);
return_ct_a: if (rb) {
/*vfile_list_dirty = 1; // just for debug */
ct += VF_rb_actions();
}
return_ct: do_actions_dep--;
/*tim = (int)time(NULL)-tim; */
/*fprintf(stderr,"Leave do_actions(%d), dirty=%d, ct=%d\n",tim,vfile_list_dirty,ct); */
return ct;
}
#define I SLANG_INT_TYPE
#define S SLANG_STRING_TYPE
#define V SLANG_VOID_TYPE
static SLang_Intrin_Fun_Type VFile_Fun_Table[] =
{
MAKE_INTRINSIC_S("getservbyname", VF_getservbyname, I),
MAKE_INTRINSIC_S("inet_addr", VF_inet_addr, I),
MAKE_INTRINSIC_I("inet_ntoa", VF_inet_ntoa, S),
MAKE_INTRINSIC_S("gethostbyname", VF_gethostbyname, I),
MAKE_INTRINSIC_I("gethostbyaddr", VF_gethostbyaddr, S),
# ifndef AR_GETPUT
MAKE_INTRINSIC_0("array_get_string", ar_get_string, V),
MAKE_INTRINSIC_0("array_get_u32", ar_get_u32, I),
MAKE_INTRINSIC_0("array_put_u32", ar_put_u32, V),
# endif
MAKE_INTRINSIC_S("unlink", VF_unlink, I),
MAKE_INTRINSIC_0("vf_fileno", VF_fileno, I),
MAKE_INTRINSIC_0("vf_size", VF_size, I),
MAKE_INTRINSIC_II("vf_seek", VF_seek, I),
MAKE_INTRINSIC_0("vf_error", VF_errno, I),
MAKE_INTRINSIC_I("vf_strerr", VF_strerr, S),
MAKE_INTRINSIC_0("vf_eof", VF_eof, I),
MAKE_INTRINSIC_0("vf_rbuffct", VF_rbuffct, I),
MAKE_INTRINSIC_0("vf_clearerr", VF_clearerr, V),
MAKE_INTRINSIC_0("set_nodelay", set_TCP_NODELAY, I),
MAKE_INTRINSIC_0("open_resolver", VFopen_resolvr, V),
MAKE_INTRINSIC_0("vf_reset", VF_reset, I),
MAKE_INTRINSIC_SII("vf_open", VF_open, V),
MAKE_INTRINSIC_0("vf_close", VF_close, I),
MAKE_INTRINSIC_0("vf_read", VF_read, I),
MAKE_INTRINSIC_0("vf_write", VF_write, I),
MAKE_INTRINSIC_II("udp_connect", VFudp_connect, V),
MAKE_INTRINSIC_II("udp_open", VFudp_open, V),
MAKE_INTRINSIC_II("tcp_connect", VFtcp_connect, V),
MAKE_INTRINSIC_SI("tcp_open", VFtcp_open, V),
MAKE_INTRINSIC_0("tcp_listen", VFtcp_listen, V),
MAKE_INTRINSIC_0("tcp_accept", VFtcp_accept, V),
MAKE_INTRINSIC_I("wait_next_readable", VF_select_on_all, V),
MAKE_INTRINSIC_0("is_readable", VF_is_readable, I),
MAKE_INTRINSIC_0("is_writeable", VF_is_writeable, I),
MAKE_INTRINSIC_0("set_rmode", VF_set_rmode, I),
MAKE_INTRINSIC_0("set_action", VF_set_action, I),
MAKE_INTRINSIC_0("clr_action", VF_clr_action, I),
MAKE_INTRINSIC_I("do_actions", VF_do_actions, I),
MAKE_INTRINSIC_0("set_wrdep", VF_set_wrdep, I),
MAKE_INTRINSIC_0("copy_bytes", VF_copybytes, I),
MAKE_INTRINSIC_0("get_local_ipp", VF_get_local_ipp, V),
MAKE_INTRINSIC_0("get_remote_ipp", VF_get_remote_ipp, V),
MAKE_INTRINSIC_II("ftp_hostport", VF_ftp_hostport, S),
SLANG_END_TABLE
};
static SLang_Intrin_Var_Type VFile_Var_Table[] =
{
MAKE_VARIABLE("vf_errno", &VFerrno, I, 1),
MAKE_VARIABLE("vf_errmsg", &VFerrmsg, S, 1),
SLANG_END_TABLE
};
#undef I
#undef S
#undef V
static SLang_IConstant_Type VFile_Constants [] =
{
#if 1 /* these are also in slang's slposio.c */
MAKE_ICONSTANT("O_RDONLY", O_RDONLY),
MAKE_ICONSTANT("O_WRONLY", O_WRONLY),
MAKE_ICONSTANT("O_RDWR", O_RDWR),
MAKE_ICONSTANT("O_CREAT", O_CREAT),
MAKE_ICONSTANT("O_EXCL", O_EXCL),
MAKE_ICONSTANT("O_NOCTTY", O_NOCTTY),
MAKE_ICONSTANT("O_TRUNC", O_TRUNC),
MAKE_ICONSTANT("O_APPEND", O_APPEND),
MAKE_ICONSTANT("O_NONBLOCK", O_NONBLOCK),
#endif
MAKE_ICONSTANT("O_NDELAY", O_NDELAY),
MAKE_ICONSTANT("O_FSYNC", O_FSYNC),
MAKE_ICONSTANT("SEEK_SET", SEEK_SET),
MAKE_ICONSTANT("SEEK_CUR", SEEK_CUR),
MAKE_ICONSTANT("SEEK_END", SEEK_END),
SLANG_END_TABLE
};
static void* add_std_to_vf_list(int fd,int flags,char* name)
{
SLang_MMT_Type *mmt;
VFILE *s;
mmt=add_to_vf_list(fd,flags,name,NULL,0,0);
if (mmt) {
SLang_inc_mmt(mmt); /* because the following ref's it */
s = (VFILE *) SLang_object_from_mmt(mmt);
if(-1 != SLadd_intrinsic_variable(s->file, (VOID_STAR) &s->mmt, SJ_VFILE_TYPE, 1))
return mmt;
SLang_free_mmt(mmt);
}
return NULL;
}
static char* vfile_string(unsigned char type, void *v)
{
SLang_MMT_Type *mmt;
VFILE *t;
char *name;
mmt = *(SLang_MMT_Type **)v;
t = (VFILE*) SLang_object_from_mmt(mmt);
name = t->file;
if (name) name = SLmake_string(name);
return name;
}
static int
char_to_int(unsigned char a_type, VOID_STAR ap, unsigned int na,
unsigned char b_type, VOID_STAR bp)
{
int *b;
unsigned char *s,*st;
(void) b_type; b = (int *) bp;
(void) a_type; s = (unsigned char *) ap;
st = s + na;
while(s<st) *b++ = *s++;
return 1;
}
static int
int_to_char(unsigned char a_type, VOID_STAR ap, unsigned int na,
unsigned char b_type, VOID_STAR bp)
{
unsigned char *b,*bt;
int *s;
(void) b_type; b = (unsigned char *) bp;
(void) a_type; s = (int *) ap;
bt = b + na;
while (b<bt) *b++ = *s++;
return 1;
}
int init_vfile_module(void) __attribute__((unused));
int init_vfile_module(void)
{
SLang_Class_Type *cl;
if (SLclass_add_typecast(SLANG_CHAR_TYPE, SLANG_INT_TYPE, char_to_int, 1)
|| SLclass_add_typecast(SLANG_INT_TYPE, SLANG_CHAR_TYPE, int_to_char, 1))
{
fprintf(stderr,"VFile: fail add_typecast(CHAR<->INT)\n");
return -1;
}
if (SLdefine_for_ifdef("VFILE")) {
fprintf(stderr,"VFile: fail define_for_isdef(VFILE)\n");
return -1;
}
if (SLadd_intrin_fun_table(VFile_Fun_Table, "_VFILE") ||
SLadd_intrin_var_table(VFile_Var_Table, NULL) ||
SLadd_iconstant_table (VFile_Constants, NULL))
return -1;
if (NULL == (cl = SLclass_allocate_class("VFile_Type")))
return -1;
cl->cl_destroy = destroy_vfile_type;
cl->cl_string = vfile_string;
if (-1 == SLclass_register_class(cl, SJ_VFILE_TYPE, sizeof(VFILE), SLANG_CLASS_TYPE_MMT))
return -1;
if ( !add_std_to_vf_list(0,SJ_READ,"StdIn")
|| !add_std_to_vf_list(1,SJ_WRITE,"StdOut")
|| !add_std_to_vf_list(2,SJ_WRITE,"StdErr")
) return -1;
return 1;
}
syntax highlighted by Code2HTML, v. 0.9.1