/* * librplay.c * * Copyright (c) 1992 by Mark Boyns * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, provided * that the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. This software is provided "as is" without express or * implied warranty. */ #include #include #include #include #include #include #include #include "version.h" #include "rplay.h" int rplay_errno; char *rplay_errlist[] = { "no error", /* RPLAY_ERROR_NONE */ "out of memory", /* RPLAY_ERROR_MEMORY */ "host not found", /* RPLAY_ERROR_HOST */ "rplay service not found", /* RPLAY_ERROR_SERVICE */ "cannot connect to socket", /* RPLAY_ERROR_CONNECT */ "cannot create socket", /* RPLAY_ERROR_SOCKET */ "unknown rplay attribute", /* RPLAY_ERROR_ATTRIBUTE */ "error writing to socket", /* RPLAY_ERROR_WRITE */ "error closing socket", /* RPLAY_ERROR_CLOSE */ "append error", /* RPLAY_ERROR_APPEND */ "max packet size exceeded", /* RPLAY_ERROR_PACKET_SIZE */ "cannot enable broadcast", /* RPLAY_ERROR_BROADCAST */ }; /* * macro used to gradually increase an rplay buffer by COPY_SIZE bytes */ #define COPY_SIZE 128 #define COPY(rp, ptr, n) \ rp->grow = 0; \ while(rp->len + n > rp->size) { \ rp->grow = 1; \ rp->size += COPY_SIZE; \ if (rp->size > MAX_PACKET) { \ rplay_errno = RPLAY_ERROR_PACKET_SIZE; \ return -1; \ } \ } \ if (rp->grow) { \ rp->buf = realloc(rp->buf, rp->size); \ if (rp->buf == NULL) { \ rplay_errno = RPLAY_ERROR_MEMORY; \ return -1; \ } \ } \ bcopy(ptr, rp->buf+rp->len, n); \ rp->len += n; \ /* * create an RPLAY instance */ RPLAY *rplay_create() { RPLAY *rp; rplay_errno = RPLAY_ERROR_NONE; rp = (RPLAY *)malloc(sizeof(RPLAY)); if (rp == NULL) { rplay_errno = RPLAY_ERROR_MEMORY; return NULL; } else { rp->len = 0; rp->buf = (char *)malloc(COPY_SIZE); if (rp->buf == NULL) { rplay_errno = RPLAY_ERROR_MEMORY; return NULL; } rp->size = COPY_SIZE; return rp; } } /* * free up memory used by rp */ int rplay_destroy(rp) RPLAY *rp; { free(rp->buf); free(rp); } /* * set rplay attributes */ int rplay_set(va_alist) va_dcl { va_list args; RPLAY *rp; char *p; unsigned char val, *valp = &val; int attr, append; rplay_errno = RPLAY_ERROR_NONE; va_start(args); rp = va_arg(args, RPLAY *); attr = va_arg(args, int); append = 0; switch(attr) { case RPLAY_APPEND_PLAY: if (rp->len <= 0 || rp->buf[0] != RPLAY_PLAY) { rplay_errno = RPLAY_ERROR_APPEND; return -1; } attr = RPLAY_PLAY; append = 1; break; case RPLAY_APPEND_PAUSE: if (rp->len <= 0 || rp->buf[0] != RPLAY_PAUSE) { rplay_errno = RPLAY_ERROR_APPEND; return -1; } attr = RPLAY_PAUSE; append = 1; break; case RPLAY_APPEND_STOP: if (rp->len <= 0 || rp->buf[0] != RPLAY_STOP) { rplay_errno = RPLAY_ERROR_APPEND; return -1; } attr = RPLAY_STOP; append = 1; break; case RPLAY_APPEND_CONTINUE: if (rp->len <= 0 || rp->buf[0] != RPLAY_CONTINUE) { rplay_errno = RPLAY_ERROR_APPEND; return -1; } attr = RPLAY_CONTINUE; append = 1; break; case RPLAY_APPEND_VOLUME_PLAY: if (rp->len <= 0 || rp->buf[0] != RPLAY_PLAY) { rplay_errno = RPLAY_ERROR_APPEND; return -1; } attr = RPLAY_VOLUME_PLAY; append = 1; break; } if (append) { rp->len--; } else { rp->len = 0; } switch(attr) { case RPLAY_PLAY: case RPLAY_PAUSE: case RPLAY_STOP: case RPLAY_CONTINUE: case RPLAY_VOLUME_PLAY: if (!append) { *valp = attr == RPLAY_VOLUME_PLAY ? RPLAY_PLAY : attr; COPY(rp, valp, 1); } for(;;) { p = va_arg(args, char *); if (p == NULL) { *valp = NULL; COPY(rp, valp, 1); break; } COPY(rp, p, strlen(p)+1); if (attr == RPLAY_VOLUME_PLAY) { *valp = (unsigned char)va_arg(args, int); COPY(rp, valp, 1); } else if (attr == RPLAY_PLAY) { *valp = RPLAY_DEFAULT_VOLUME; COPY(rp, valp, 1); } } break; default: rplay_errno = RPLAY_ERROR_ATTRIBUTE; return -1; } va_end(args); return 0; } /* * create a UDP socket for the given host */ int rplay_open(host) char *host; { struct hostent *hp; #ifdef INETD struct servent *sp; #endif INETD int rplay_fd; u_long addr; struct sockaddr_in s; int on = 1; rplay_errno = RPLAY_ERROR_NONE; #ifdef INETD sp = getservbyname("rplay", "udp"); if (sp == NULL) { rplay_errno = RPLAY_ERROR_SERVICE; return -1; } #endif INETD bzero(&s, sizeof(s)); addr = inet_addr(host); if (addr == 0xffffffff) { hp = gethostbyname(host); if (hp == NULL) { rplay_errno = RPLAY_ERROR_HOST; return -1; } bcopy(hp->h_addr, &s.sin_addr.s_addr, hp->h_length); } else { bcopy(&addr, &s.sin_addr.s_addr, sizeof(addr)); } #ifdef INETD s.sin_port = sp->s_port; #else s.sin_port = htons(RPLAY_PORT); #endif INETD s.sin_family = AF_INET; rplay_fd = socket(AF_INET, SOCK_DGRAM, 0); if (rplay_fd < 0) { rplay_errno = RPLAY_ERROR_SOCKET; return -1; } if (connect(rplay_fd, &s, sizeof(s)) < 0) { rplay_errno = RPLAY_ERROR_CONNECT; return -1; } /* * enable broadcasting */ if (setsockopt(rplay_fd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) { rplay_errno = RPLAY_ERROR_BROADCAST; return -1; } return rplay_fd; } /* * write the RPLAY packet to the given socket descriptor */ int rplay(rplay_fd, rp) int rplay_fd; RPLAY *rp; { rplay_errno = RPLAY_ERROR_NONE; if (write(rplay_fd, rp->buf, rp->len) != rp->len) { rplay_errno = RPLAY_ERROR_WRITE; return -1; } return 0; } /* * close the given rplay socket descriptor */ int rplay_close(rplay_fd) int rplay_fd; { rplay_errno = RPLAY_ERROR_NONE; if (close(rplay_fd) < 0) { rplay_errno = RPLAY_ERROR_CLOSE; return -1; } return 0; } /* * report an rplay error to stderr */ rplay_perror(s) char *s; { fprintf(stderr, "%s: %s\n", s, rplay_errlist[rplay_errno]); }