forked from len0rd/rockbox
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@21070 a1c6a512-1295-4272-9138-f99709370657
326 lines
6.8 KiB
C
326 lines
6.8 KiB
C
/* Copyright (c) 2000 Miller Puckette.
|
|
* For information on usage and redistribution, and for a DISCLAIMER OF ALL
|
|
* WARRANTIES, see the file, "LICENSE.txt," in the Pd distribution. */
|
|
|
|
/* the "pdreceive" command. This is a standalone program that receives messages
|
|
from Pd via the netsend/netreceive ("FUDI") protocol, and copies them to
|
|
standard output. */
|
|
|
|
#include <sys/types.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#ifdef UNIX
|
|
#include <sys/time.h>
|
|
#include <unistd.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <netdb.h>
|
|
#define SOCKET_ERROR -1
|
|
#else
|
|
#include <winsock.h>
|
|
#endif
|
|
|
|
typedef struct _fdpoll
|
|
{
|
|
int fdp_fd;
|
|
char *fdp_inbuf;
|
|
int fdp_inhead;
|
|
int fdp_intail;
|
|
int fdp_udp;
|
|
} t_fdpoll;
|
|
|
|
static int nfdpoll;
|
|
static t_fdpoll *fdpoll;
|
|
static int maxfd;
|
|
static int sockfd;
|
|
static int protocol;
|
|
|
|
static void sockerror(char *s);
|
|
static void x_closesocket(int fd);
|
|
static void dopoll(void);
|
|
#define BUFSIZE 4096
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
int portno;
|
|
struct sockaddr_in server;
|
|
int nretry = 10;
|
|
#ifdef MSW
|
|
short version = MAKEWORD(2, 0);
|
|
WSADATA nobby;
|
|
#endif
|
|
if (argc < 2 || sscanf(argv[1], "%d", &portno) < 1 || portno <= 0)
|
|
goto usage;
|
|
if (argc >= 3)
|
|
{
|
|
if (!strcmp(argv[2], "tcp"))
|
|
protocol = SOCK_STREAM;
|
|
else if (!strcmp(argv[2], "udp"))
|
|
protocol = SOCK_DGRAM;
|
|
else goto usage;
|
|
}
|
|
else protocol = SOCK_STREAM;
|
|
#ifdef MSW
|
|
if (WSAStartup(version, &nobby)) sockerror("WSAstartup");
|
|
#endif
|
|
sockfd = socket(AF_INET, protocol, 0);
|
|
if (sockfd < 0)
|
|
{
|
|
sockerror("socket()");
|
|
exit(1);
|
|
}
|
|
maxfd = sockfd + 1;
|
|
server.sin_family = AF_INET;
|
|
server.sin_addr.s_addr = INADDR_ANY;
|
|
|
|
#ifdef IRIX
|
|
/* this seems to work only in IRIX but is unnecessary in
|
|
Linux. Not sure what MSW needs in place of this. */
|
|
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, 0, 0) < 0)
|
|
post("setsockopt failed\n");
|
|
#endif
|
|
|
|
/* assign client port number */
|
|
server.sin_port = htons((unsigned short)portno);
|
|
|
|
/* name the socket */
|
|
if (bind(sockfd, (struct sockaddr *)&server, sizeof(server)) < 0)
|
|
{
|
|
sockerror("bind");
|
|
x_closesocket(sockfd);
|
|
return (0);
|
|
}
|
|
if (protocol == SOCK_STREAM)
|
|
{
|
|
if (listen(sockfd, 5) < 0)
|
|
{
|
|
sockerror("listen");
|
|
x_closesocket(sockfd);
|
|
exit(1);
|
|
}
|
|
}
|
|
/* now loop forever selecting on sockets */
|
|
while (1)
|
|
dopoll();
|
|
|
|
usage:
|
|
fprintf(stderr, "usage: pdreceive <portnumber> [udp|tcp]\n");
|
|
fprintf(stderr, "(default is tcp)\n");
|
|
exit(1);
|
|
}
|
|
|
|
static void addport(int fd)
|
|
{
|
|
int nfd = nfdpoll;
|
|
t_fdpoll *fp;
|
|
fdpoll = (t_fdpoll *)realloc(fdpoll,
|
|
(nfdpoll+1) * sizeof(t_fdpoll));
|
|
fp = fdpoll + nfdpoll;
|
|
fp->fdp_fd = fd;
|
|
nfdpoll++;
|
|
if (fd >= maxfd) maxfd = fd + 1;
|
|
fp->fdp_inhead = fp->fdp_intail = 0;
|
|
if (!(fp->fdp_inbuf = malloc(BUFSIZE)))
|
|
{
|
|
fprintf(stderr, "out of memory");
|
|
exit(1);
|
|
}
|
|
printf("number_connected %d;\n", nfdpoll);
|
|
}
|
|
|
|
static void rmport(t_fdpoll *x)
|
|
{
|
|
int nfd = nfdpoll;
|
|
int i, size = nfdpoll * sizeof(t_fdpoll);
|
|
t_fdpoll *fp;
|
|
for (i = nfdpoll, fp = fdpoll; i--; fp++)
|
|
{
|
|
if (fp == x)
|
|
{
|
|
x_closesocket(fp->fdp_fd);
|
|
free(fp->fdp_inbuf);
|
|
while (i--)
|
|
{
|
|
fp[0] = fp[1];
|
|
fp++;
|
|
}
|
|
fdpoll = (t_fdpoll *)realloc(fdpoll,
|
|
(nfdpoll-1) * sizeof(t_fdpoll));
|
|
nfdpoll--;
|
|
printf("number_connected %d;\n", nfdpoll);
|
|
return;
|
|
}
|
|
}
|
|
fprintf(stderr, "warning: item removed from poll list but not found");
|
|
}
|
|
|
|
static void doconnect(void)
|
|
{
|
|
int fd = accept(sockfd, 0, 0);
|
|
if (fd < 0)
|
|
perror("accept");
|
|
else addport(fd);
|
|
}
|
|
|
|
static void udpread(void)
|
|
{
|
|
char buf[BUFSIZE];
|
|
int ret = recv(sockfd, buf, BUFSIZE, 0);
|
|
if (ret < 0)
|
|
{
|
|
sockerror("recv (udp)");
|
|
x_closesocket(sockfd);
|
|
exit(1);
|
|
}
|
|
else if (ret > 0)
|
|
{
|
|
#ifdef UNIX
|
|
if (write(1, buf, ret) < ret)
|
|
{
|
|
perror("write");
|
|
exit(1);
|
|
}
|
|
#else
|
|
int j;
|
|
for (j = 0; j < ret; j++)
|
|
putchar(buf[j]);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static int tcpmakeoutput(t_fdpoll *x)
|
|
{
|
|
char messbuf[BUFSIZE+1], *bp = messbuf;
|
|
int indx;
|
|
int inhead = x->fdp_inhead;
|
|
int intail = x->fdp_intail;
|
|
char *inbuf = x->fdp_inbuf;
|
|
if (intail == inhead)
|
|
return (0);
|
|
for (indx = intail; indx != inhead; indx = (indx+1)&(BUFSIZE-1))
|
|
{
|
|
/* search for a semicolon. */
|
|
char c = *bp++ = inbuf[indx];
|
|
if (c == ';')
|
|
{
|
|
intail = (indx+1)&(BUFSIZE-1);
|
|
if (inbuf[intail] == '\n')
|
|
intail = (intail+1)&(BUFSIZE-1);
|
|
*bp++ = '\n';
|
|
#ifdef UNIX
|
|
write(1, messbuf, bp - messbuf);
|
|
#else
|
|
{
|
|
int j;
|
|
for (j = 0; j < bp - messbuf; j++)
|
|
putchar(messbuf[j]);
|
|
}
|
|
#endif
|
|
x->fdp_inhead = inhead;
|
|
x->fdp_intail = intail;
|
|
return (1);
|
|
}
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
static void tcpread(t_fdpoll *x)
|
|
{
|
|
int readto =
|
|
(x->fdp_inhead >= x->fdp_intail ? BUFSIZE : x->fdp_intail-1);
|
|
int ret;
|
|
|
|
/* the input buffer might be full. If so, drop the whole thing */
|
|
if (readto == x->fdp_inhead)
|
|
{
|
|
fprintf(stderr, "pd: dropped message from gui\n");
|
|
x->fdp_inhead = x->fdp_intail = 0;
|
|
readto = BUFSIZE;
|
|
}
|
|
else
|
|
{
|
|
ret = recv(x->fdp_fd, x->fdp_inbuf + x->fdp_inhead,
|
|
readto - x->fdp_inhead, 0);
|
|
if (ret < 0)
|
|
{
|
|
sockerror("recv (tcp)");
|
|
rmport(x);
|
|
}
|
|
else if (ret == 0)
|
|
rmport(x);
|
|
else
|
|
{
|
|
x->fdp_inhead += ret;
|
|
if (x->fdp_inhead >= BUFSIZE)
|
|
x->fdp_inhead = 0;
|
|
while (tcpmakeoutput(x))
|
|
;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void dopoll(void)
|
|
{
|
|
int i;
|
|
t_fdpoll *fp;
|
|
fd_set readset, writeset, exceptset;
|
|
FD_ZERO(&writeset);
|
|
FD_ZERO(&readset);
|
|
FD_ZERO(&exceptset);
|
|
|
|
FD_SET(sockfd, &readset);
|
|
if (protocol == SOCK_STREAM)
|
|
{
|
|
for (fp = fdpoll, i = nfdpoll; i--; fp++)
|
|
FD_SET(fp->fdp_fd, &readset);
|
|
}
|
|
if (select(maxfd+1, &readset, &writeset, &exceptset, 0) < 0)
|
|
{
|
|
perror("select");
|
|
exit(1);
|
|
}
|
|
if (protocol == SOCK_STREAM)
|
|
{
|
|
for (i = 0; i < nfdpoll; i++)
|
|
if (FD_ISSET(fdpoll[i].fdp_fd, &readset))
|
|
tcpread(&fdpoll[i]);
|
|
if (FD_ISSET(sockfd, &readset))
|
|
doconnect();
|
|
}
|
|
else
|
|
{
|
|
if (FD_ISSET(sockfd, &readset))
|
|
udpread();
|
|
}
|
|
}
|
|
|
|
|
|
static void sockerror(char *s)
|
|
{
|
|
#ifdef MSW
|
|
int err = WSAGetLastError();
|
|
if (err == 10054) return;
|
|
else if (err == 10044)
|
|
{
|
|
fprintf(stderr,
|
|
"Warning: you might not have TCP/IP \"networking\" turned on\n");
|
|
}
|
|
#endif
|
|
#ifdef UNIX
|
|
int err = errno;
|
|
#endif
|
|
fprintf(stderr, "%s: %s (%d)\n", s, strerror(err), err);
|
|
}
|
|
|
|
static void x_closesocket(int fd)
|
|
{
|
|
#ifdef UNIX
|
|
close(fd);
|
|
#endif
|
|
#ifdef MSW
|
|
closesocket(fd);
|
|
#endif
|
|
}
|
|
|