Article 2718 of comp.sources.unix:
Path: nntpd.lkg.dec.com!crl.dec.com!crl.dec.com!bloom-beacon.mit.edu!news.kei.com!news.mathworks.com!uunet!in1.uu.net!vixie!vixie!not-for-mail
From: maffeis@acm.org (Silvano Maffeis)
Newsgroups: comp.sources.unix
Subject: v29i061: iphone - phone calls across the internet, Part02/02
Date: 7 Dec 1995 10:14:02 -0800
Organization: Vixie Enterprises
Lines: 1569
Sender: vixie@vix.com
Approved: vixie@gw.home.vix.com
Message-ID: <4a7ata$9a7@gw.home.vix.com>
References: <1.818360012.9510@gw.home.vix.com>
NNTP-Posting-Host: gw.home.vix.com

Submitted-By: maffeis@acm.org (Silvano Maffeis)
Posting-Number: Volume 29, Issue 61
Archive-Name: iphone/part02

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	audio.c
#	config.h
#	iserver.c
#	tcp.h
#	util.h
#	audio.h
#	filter.c
#	quality.c
#	url.c
#	filter.h
#	quality.h
#	url.h
#	iphone.c
#	tcp.c
#	util.c
# This archive created: Wed Nov 22 11:17:23 1995
# By:	Silvano Maffeis ()
export PATH; PATH=/bin:$PATH
if test -f 'audio.c'
then
	echo shar: will not over-write existing file "'audio.c'"
else
cat << \SHAR_EOF > 'audio.c'

/* $Id: audio.c,v 1.8 1995/11/22 13:00:43 maffeis Exp maffeis $
 *
 * Author: Silvano Maffeis, maffeis@acm.org.
 * Copyright (c) 1995 by Silvano Maffeis, maffeis@acm.org.
 * All rights reserved.
 * 
 * THIS IS FREE SOFTWARE.
 * Permission to use, copy, modify, and distribute this software and 
 * documentation in all settings is hereby granted, provided that no fee is 
 * charged for this software and provided that this copyright notice appears 
 * in all copies of any software which is or includes a copy or modification 
 * of this software.
 */

#include <stdio.h>
#include <fcntl.h>
#include <assert.h>
#include <sys/ioccom.h>

#ifdef SOLARIS
#include <sys/audioio.h>
#else
#include <sun/audioio.h>
#endif

#include "config.h"
#include "audio.h"
#include "util.h"

/* 
 * procedures that interact with the audio device. Most of the audio ioctls
 * are optional.
 */

/* #define AUDIO_DO_DRAIN /**/

/* read at most max_bytes from the audio device:
 */
int audio_read(int fd, char *buf, int max_bytes, int *read_bytes){
  if((*read_bytes = read(fd, buf, max_bytes)) == -1){
    perror("audio_read: read"); return FALSE;
  }

  /* decrease audio quality if we cannot send data across the net fast enough,
   * increase quality if throughput is ok:
   */
  if(audio_overflow(fd)) quality_dec();
  else quality_inc();

  return TRUE;
}

/* write buf to the audio device:
 */
int audio_write(int fd, char *buf, int size){
  if(write(fd, buf, size) != size){
    perror("audio_write: write"); return FALSE;
  }
  fsync(fd);
#ifdef AUDIO_DO_DRAIN
#ifdef sun
  if(ioctl(fd, AUDIO_DRAIN, 0) == -1){
    perror("audio_write: ioctl"); return FALSE;
  }
#endif /* sun */
#endif /* AUDIO_DO_DRAIN */
  return TRUE;
}

/* open the audio device:
 */
int audio_open(int *fd, char *audio_file, int flags){
  if((*fd = open(audio_file, flags)) == -1){
    perror("audio_open: open"); return FALSE;
  }
  return TRUE;
}

/* close the audio device:
 */
int audio_close(int fd){
  if(close(fd) == -1){
    perror("audio_close: close"); return FALSE;
  }
  return TRUE;
}

/* set the play volume:
 */
int audio_set_volume(int fd, int vol){
#ifdef sun
  audio_info_t ai;

  if(vol == -1) return TRUE;

  AUDIO_INITINFO(&ai);
  ai.play.gain = vol;
  if(ioctl(fd, AUDIO_SETINFO, &ai) == -1){
    perror("audio_set_volume: ioctl(AUDIO_SETINFO)");
    return FALSE;
  }
#endif /* sun */
  return TRUE;
}

/* direct audio output to jack (headphones):
 */
int audio_output_to_jack(int fd){
#ifdef sun
  audio_info_t ai;
  AUDIO_INITINFO(&ai);
  ai.play.port = AUDIO_HEADPHONE;
  if(ioctl(fd, AUDIO_SETINFO, &ai) == -1){
    perror("audio_output_to_jack: ioctl(AUDIO_SETINFO)");
    return FALSE;
  }
#endif /* sun */
  return TRUE;
}

/* simple silence suppression:
 * If we detect silence, still send a few packets.
 */
int audio_detect_silence(int inLen, int *outLen){
  static int silencePackets = 0;
  int i;
  double hit =(double)0;

  /* count the amount of audio samples that are smaller than AUDIO_SILENCE:
   */
  for(i = 0; i < inLen; i++) if(abs(audio_u2c(inbuf[i])) <= AUDIO_SILENCE)
    hit++;

  /* if enough samples are <= AUDIO_SILENCE, we could discard the packet.
   * However, if we detect silence we still deliver the next AUDIO_SILENCE_PKTS
   * packets to ensure a "smooth" transition:
   */
  if(hit/inLen >= AUDIO_SILENCE_HITRATIO){
#ifdef AUDIO_DEBUG
    fprintf(stderr, "silence: %lf\n", (double)hit/inLen);
#endif
    if(silencePackets >= AUDIO_SILENCE_PKTS) {
      *outLen = 0; return TRUE;
    }
    silencePackets++;
  } else {
    silencePackets = 0;
  }

  swapbuf(); *outLen = inLen; 

  return TRUE;
}

/* reconstruct the sample that is between from and to:
 */
char audio_interpolate(char from, char to){
  /*char newval = (audio_u2c(from) + audio_u2c(to))/2;
  return (char)audio_c2u(newval);*/
  if((from + to)%2 == 1) from++;
  return (from+to)/2;
}

/* check for overflow in the audio device. Overflow occurs when
 * we are too slow at reading the audio device, i.e., when TCP
 * bandwith drops:
 */
int audio_overflow(int fd){
  int ret = FALSE;
  static int skip_errors =0;

#ifdef sun
  /* check the record.error flag */
  audio_info_t ai;
  static int numErrors = 0;
  AUDIO_INITINFO(&ai);

  if(++skip_errors > (7 * AUDIO_SAMPLE_RATE/g.pktsize)){
    if(ioctl(fd, AUDIO_GETINFO, &ai) == -1){
      perror("audio_overflow: ioctl(AUDIO_GETINFO)");
      return FALSE;
    }
    ret = ai.record.error;
  }

  /* clear the error flag from time to time:
   */
  if(ret && ++numErrors == (7 * AUDIO_SAMPLE_RATE/g.pktsize)){
    ai.record.error = 0;
    numErrors = 0;
    if(ioctl(fd, AUDIO_SETINFO, &ai) == -1){
      perror("audio_overflow: ioctl(AUDIO_SETINFO)");
      return FALSE;
    }
  }
#endif /* sun */
  return ret;
}

/* arrays for audio_u2c etc:
 */
short _ulaw2linear [256] = {
  -32256, -31228, -30200, -29172, -28143, -27115, -26087, -25059, -24031, 
  -23002, -21974, -20946, -19918, -18889, -17861, -16833, -16062, -15548, 
  -15033, -14519, -14005, -13491, -12977, -12463, -11949, -11435, -10920, 
  -10406, -9892, -9378, -8864, -8350, -7964, -7707, -7450, -7193, -6936, 
  -6679, -6422, -6165, -5908, -5651, -5394, -5137, -4880, -4623, -4365, -4108, 
  -3916, -3787, -3659, -3530, -3402, -3273, -3144, -3016, -2887, -2759, -2630, 
  -2502, -2373, -2245, -2116, -1988, -1891, -1827, -1763, -1698, -1634, -1570, 
  -1506, -1441, -1377, -1313, -1249, -1184, -1120, -1056, -992, -927, -879, 
  -847, -815, -783, -751, -718, -686, -654, -622, -590, -558, -526, -494, -461, 
  -429, -397, -373, -357, -341, -325, -309, -293, -277, -261, -245, -228, -212, 
  -196, -180, -164, -148, -132, -120, -112, -104, -96, -88, -80, -72, -64, -56, 
  -48, -40, -32, -24, -16, -8, 0, 32256, 31228, 30200, 29172, 28143, 27115, 
  26087, 25059, 24031, 23002, 21974, 20946, 19918, 18889, 17861, 16833, 16062, 
  15548, 15033, 14519, 14005, 13491, 12977, 12463, 11949, 11435, 10920, 10406, 
  9892, 9378, 8864, 8350, 7964, 7707, 7450, 7193, 6936, 6679, 6422, 6165, 5908, 
  5651, 5394, 5137, 4880, 4623, 4365, 4108, 3916, 3787, 3659, 3530, 3402, 3273, 
  3144, 3016, 2887, 2759, 2630, 2502, 2373, 2245, 2116, 1988, 1891, 1827, 1763, 
  1698, 1634, 1570, 1506, 1441, 1377, 1313, 1249, 1184, 1120, 1056, 992, 927, 
  879, 847, 815, 783, 751, 718, 686, 654, 622, 590, 558, 526, 494, 461, 429, 
  397, 373, 357, 341, 325, 309, 293, 277, 261, 245, 228, 212, 196, 180, 164, 
  148, 132, 120, 112, 104, 96, 88, 80, 72, 64, 56, 48, 40, 32, 24, 16, 8, 0
};

unsigned char _linear2ulaw [136] = {
  0, 3, 176, 56, 0, 0, 0, 0, 0, 3, 192, 72, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 0, 0, 0, 239, 255, 241, 144, 0, 
  0, 0, 0, 0, 0, 0, 0, 0, 6, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
SHAR_EOF
fi # end of overwriting check
if test -f 'config.h'
then
	echo shar: will not over-write existing file "'config.h'"
else
cat << \SHAR_EOF > 'config.h'

/* $Id: config.h,v 1.8 1995/11/22 13:00:43 maffeis Exp maffeis $
 *
 * Author: Silvano Maffeis, maffeis@acm.org.
 * Copyright (c) 1995 by Silvano Maffeis, maffeis@acm.org.
 * All rights reserved.
 * 
 * THIS IS FREE SOFTWARE.
 * Permission to use, copy, modify, and distribute this software and 
 * documentation in all settings is hereby granted, provided that no fee is 
 * charged for this software and provided that this copyright notice appears 
 * in all copies of any software which is or includes a copy or modification 
 * of this software.
 */

#define TRUE                   1
#define FALSE                  0

#define VERSION                "1.0"

#define AUDIO_DEV              "/dev/audio"  /* default audio def.            */
#define AUDIO_BLKSZ            512           /* default audio packet size     */
#define AUDIO_MIN_BLKSZ        32            /* min. audio packet size        */
#define AUDIO_MAX_BLKSZ        16192         /* max. audio packet size        */
#define AUDIO_VOLUME           40            /* default volume                */
#define AUDIO_SAMPLE_RATE      8000
#define AUDIO_SILENCE          8             /* for ulaw silence detection    */
#define AUDIO_SILENCE_HITRATIO 0.90          /* (ditto)                       */
#define AUDIO_SILENCE_PKTS     (AUDIO_SAMPLE_RATE/AUDIO_BLKSZ)

#define QUALITY_WORST          3             /* worst quality level           */

#define TCP_DEFPORT            6371          /* default TCP port              */

/*#define ACK_SEND*/
#define ACK_INTERVAL           1

/* work area for compress etc.: */
extern char buf1 [];
extern char buf2 [];
extern char *inbuf, *outbuf;

void swapbuf();
SHAR_EOF
fi # end of overwriting check
if test -f 'iserver.c'
then
	echo shar: will not over-write existing file "'iserver.c'"
else
cat << \SHAR_EOF > 'iserver.c'

/* $Id: iserver.c,v 1.5 1995/11/22 13:00:43 maffeis Exp maffeis $
 *
 * Author: Silvano Maffeis, maffeis@acm.org.
 * Copyright (c) 1995 by Silvano Maffeis, maffeis@acm.org.
 * All rights reserved.
 * 
 * THIS IS FREE SOFTWARE.
 * Permission to use, copy, modify, and distribute this software and 
 * documentation in all settings is hereby granted, provided that no fee is 
 * charged for this software and provided that this copyright notice appears 
 * in all copies of any software which is or includes a copy or modification 
 * of this software.
 */

#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <assert.h>
#include <signal.h>

#include "config.h"
#include "tcp.h"
#include "audio.h"
#include "util.h"
#include "url.h"

int remoteClosed = FALSE;

void sigpipe_hdlr(){
  remoteClosed = TRUE;
}

int server_handle_tcp_input(int audioFd, int socket){
  int read_bytes;
  static int verboseCnt = 0;

  if(!tcp_read(socket, inbuf, g.pktsize, &read_bytes)) return FALSE;
  if(g.verbose && (++verboseCnt == 10)){
    fprintf(stderr, "r");
    verboseCnt = 0;
  }
  if(!filter_recv(read_bytes, &read_bytes)) return FALSE;
  if(read_bytes > 0){
    if(!audio_write(audioFd, inbuf, read_bytes)){
      return FALSE;
    }
  }

#ifdef ACK_SEND
  if(!tcp_read_ack(socket)) abort();
#endif

  return TRUE;
}

int server_handle_audio_input(int audioFd, int socket){
  int read_bytes;
  static int verboseCnt = 0;

  if(!audio_read(audioFd, inbuf, g.pktsize, &read_bytes)) return FALSE;
  if(!filter_send(read_bytes, &read_bytes)) return FALSE;

  if(read_bytes > 0){
    if(!tcp_write(socket, inbuf, read_bytes)){
      return FALSE;
    }
    if(g.verbose && (++verboseCnt == 10)){
      fprintf(stderr, "s");
      verboseCnt = 0;
    }
#ifdef ACK_SEND
    if(!tcp_send_ack(socket)) abort();
#endif  
  }
  return TRUE;
}

int server_run(int port, char *audiodev){
  int socket, audioFd, clientFd;
  fd_set readfds;
  
  FD_ZERO(&readfds);
  
  if(!audio_open(&audioFd, audiodev, O_RDWR)) return FALSE;
  if(!audio_output_to_jack(audioFd)) return FALSE;
  if(!audio_set_volume(audioFd, g.volume)) return FALSE;
  if(!tcp_listen(&socket, port)) return FALSE;

  if(g.verbose){
    fprintf(stderr, "%s: version %s. maffeis@acm.org\n", g.pgm, g.version);
    fprintf(stderr, "%s: listening on TCP port %d\n", g.pgm, port);
  }

  for(;;){
    if(!tcp_accept(socket, &clientFd)) return FALSE;
    
    if(!util_recv_configdata(clientFd)){
      fprintf(stderr, "%s: closing connection to client\n", g.pgm);
      close(clientFd);
      continue;
    }

    remoteClosed = FALSE;
    
    for(;;){
      FD_ZERO(&readfds);
      FD_SET(audioFd, &readfds);
      FD_SET(clientFd, &readfds);
      
      if(select(32, &readfds, 0, 0, 0) == -1){
	perror("server_run: select"); break;
      }

      if(remoteClosed) break;

      if(FD_ISSET(audioFd, &readfds))
	if(!server_handle_audio_input(audioFd, clientFd)) break;
      
      if(FD_ISSET(clientFd, &readfds))
	if(!server_handle_tcp_input(audioFd, clientFd)) break;
    }
    close(clientFd);
    fprintf(stderr, "%s: client has closed connection\n\n", g.pgm);
  }
  
  return FALSE;
}

void server_usage(char *pgm){
  fprintf(stderr, "usage: %s [-p port] [-a audiofile] [-v volume] [-V]\n", pgm);
  exit(1);
}


#ifdef hack
extern unsigned char _linear2ulaw2[];
extern short _ulaw2linear2 [];

test(){
  int i;
  int val;
  
  fprintf(stderr, "val = %d\n", _linear2ulaw[45]);
  
  for(i =0; i < 136; i++){
    val = (int)(_linear2ulaw[i]);
    fprintf(stderr, "%d, ", val);
  }
  fprintf(stderr, "\n\n");
  
  for(i =0; i < 136; i++){
    if(_linear2ulaw2[i] != _linear2ulaw[i]){
      fprintf(stderr, "%d, %d\n", _linear2ulaw2[i], _linear2ulaw[i]);
      fprintf(stderr, "%d\n", i); abort();
    }
  }
  
  for(i =0; i < 256; i++){
    if(_ulaw2linear[i] != _ulaw2linear2[i]){
      fprintf(stderr, "%d, %d\n", _ulaw2linear2[i], _ulaw2linear[i]);
      fprintf(stderr, "%d\n", i); abort();
    }
  }
}
#endif /* hack */

main(int argc, char **argv){
  int i, volume = AUDIO_VOLUME, verbose = FALSE;
  int port        = TCP_DEFPORT;
  char *audiofile = AUDIO_DEV;

  if(argc < 1) server_usage(argv[0]);

  for(i = 1; i < argc; i++){
    if(argv[i][0] != '-' || argv[i][2] != '\0') server_usage(argv[0]);
    switch(argv[i][1]){
    case 'a':
      if(argv[i+1] == 0) server_usage(argv[0]);
      audiofile = argv[i+1];
      i++;
      break;
    case 'p':
      if(argv[i+1] == 0) server_usage(argv[0]);
      port = atoi(argv[i+1]);
      i++;
      break;
    case 'v':
      if(argv[i+1] == 0) server_usage(argv[0]);
      volume = atoi(argv[i+1]);
      i++;
      break;
    case 'V':
      verbose = TRUE;
      break;
    default:
      server_usage(argv[0]);
    }
  }

  signal(SIGPIPE, sigpipe_hdlr);

  util_fill_global(argv[0], volume, verbose, AUDIO_BLKSZ);
  exit(server_run(port, audiofile));
}
SHAR_EOF
fi # end of overwriting check
if test -f 'tcp.h'
then
	echo shar: will not over-write existing file "'tcp.h'"
else
cat << \SHAR_EOF > 'tcp.h'

/* $Id: tcp.h,v 1.2 1995/11/16 19:17:23 maffeis Exp maffeis $
 *
 * Author: Silvano Maffeis, maffeis@acm.org.
 * Copyright (c) 1995 by Silvano Maffeis, maffeis@acm.org.
 * All rights reserved.
 * 
 * THIS IS FREE SOFTWARE.
 * Permission to use, copy, modify, and distribute this software and 
 * documentation in all settings is hereby granted, provided that no fee is 
 * charged for this software and provided that this copyright notice appears 
 * in all copies of any software which is or includes a copy or modification 
 * of this software.
 */

int tcp_listen(int *fd, int port);
int tcp_connect(int *fd, char *host, int port);
int tcp_write(int fd, char *buf, int size);
int tcp_read(int fd, char *buf, int max_bytes, int *bytes_read);
int tcp_send_ack(int fd);
int tcp_read_ack(int fd);
SHAR_EOF
fi # end of overwriting check
if test -f 'util.h'
then
	echo shar: will not over-write existing file "'util.h'"
else
cat << \SHAR_EOF > 'util.h'

/* $Id: util.h,v 1.5 1995/11/17 21:46:23 maffeis Exp maffeis $
 *
 * Author: Silvano Maffeis, maffeis@acm.org.
 * Copyright (c) 1995 by Silvano Maffeis, maffeis@acm.org.
 * All rights reserved.
 * 
 * THIS IS FREE SOFTWARE.
 * Permission to use, copy, modify, and distribute this software and 
 * documentation in all settings is hereby granted, provided that no fee is 
 * charged for this software and provided that this copyright notice appears 
 * in all copies of any software which is or includes a copy or modification 
 * of this software.
 */

struct util_global {
  char  myurl[64];
  char *pgm;
  int   quality;
  int   volume;
  int   verbose;
  int   pktsize;
  char *version;
};

void util_fill_global(char *pgm, int volume, int verbose, int pktsize);
int util_send_configdata(int fd);
int util_recv_configdata(int fd);
int util_load_rcfile(char *url);

extern struct util_global g;

#define UTIL_RCFILE ".iphonerc"
SHAR_EOF
fi # end of overwriting check
if test -f 'audio.h'
then
	echo shar: will not over-write existing file "'audio.h'"
else
cat << \SHAR_EOF > 'audio.h'

/* $Id: audio.h,v 1.3 1995/11/16 19:17:23 maffeis Exp maffeis $
 *
 * Author: Silvano Maffeis, maffeis@acm.org.
 * Copyright (c) 1995 by Silvano Maffeis, maffeis@acm.org.
 * All rights reserved.
 * 
 * THIS IS FREE SOFTWARE.
 * Permission to use, copy, modify, and distribute this software and 
 * documentation in all settings is hereby granted, provided that no fee is 
 * charged for this software and provided that this copyright notice appears 
 * in all copies of any software which is or includes a copy or modification 
 * of this software.
 */

int audio_read(int fd, char *buf, int max_bytes, int *read_bytes);
int audio_write(int fd, char *buf, int size);
int audio_open(int *fd, char *audio_file, int flags);
int audio_close(int fd);
int audio_set_volume(int fd, int vol);
int audio_output_to_jack(int fd);
int audio_detect_silence(int inLen, int *outLen);
char audio_interpolate(char from, char to);

extern short            _ulaw2linear[];         /* 8-bit u-law to 16-bit PCM */
extern unsigned char    _linear2ulaw[];         /* 13-bit PCM to 8-bit u-law */

#define audio_u2c(X)    ((char)(_ulaw2linear[(unsigned char) (X)] >> 8))
#define audio_u2s(X)    (_ulaw2linear[(unsigned char) (X)])
#define audio_u2l(X)    (((long)_ulaw2linear[(unsigned char) (X)]) << 16)

#define audio_c2u(X)    (_linear2ulaw[(((short)(X)) << 5) + 0xf])
#define audio_s2u(X)    (_linear2ulaw[((short)(X)) >> 3])
#define audio_l2u(X)    (_linear2ulaw[((long)(X)) >> 19])
SHAR_EOF
fi # end of overwriting check
if test -f 'filter.c'
then
	echo shar: will not over-write existing file "'filter.c'"
else
cat << \SHAR_EOF > 'filter.c'

/* $Id: filter.c,v 1.4 1995/11/16 21:42:59 maffeis Exp maffeis $
 *
 * Author: Silvano Maffeis, maffeis@acm.org.
 * Copyright (c) 1995 by Silvano Maffeis, maffeis@acm.org.
 * All rights reserved.
 * 
 * THIS IS FREE SOFTWARE.
 * Permission to use, copy, modify, and distribute this software and 
 * documentation in all settings is hereby granted, provided that no fee is 
 * charged for this software and provided that this copyright notice appears 
 * in all copies of any software which is or includes a copy or modification 
 * of this software.
 */

#include <stdio.h>

#include "config.h"
#include "filter.h"
#include "audio.h"
#include "compress.h"
#include "quality.h"

/* outgoing/incoming audio packets are processed
 * by the following filter procedures:
 */
filter_elem filter_table [] = {
  { audio_detect_silence, 0 }
  , { quality_shrink, quality_expand }
  , { compress_shrink, compress_expand }
};

/* apply filter procedures to outgoing packet:
 */
int filter_send(int inLen, int *outLen){
  int i;
  int filterApplied = FALSE;

  for(i =0; i < sizeof(filter_table)/sizeof(filter_elem); i++){
    if(!filter_table[i].send) continue;
    filterApplied = TRUE;
    if(!(*filter_table[i].send)(inLen, outLen)) return FALSE;
    /* was the packet discarded?: */
    if(*outLen == 0) return TRUE;
    swapbuf(); inLen = *outLen;
  }

  if(!filterApplied){
    *outLen = inLen;
  }

  return TRUE;
}

/* apply filter procedures to incoming packet:
 */
int filter_recv(int inLen, int *outLen){
  int i;
  int filterApplied = FALSE;

  for(i =sizeof(filter_table)/sizeof(filter_elem) -1; i >= 0; i--){
    if(!filter_table[i].recv) continue;
    filterApplied = TRUE;
    if(!(*filter_table[i].recv)(inLen, outLen)) return FALSE;
    /* was the packet discarded?: */
    if(*outLen == 0) return TRUE;
    swapbuf(); inLen = *outLen;
  }

  if(!filterApplied){
    *outLen = inLen;
  }

  return TRUE;
}
SHAR_EOF
fi # end of overwriting check
if test -f 'quality.c'
then
	echo shar: will not over-write existing file "'quality.c'"
else
cat << \SHAR_EOF > 'quality.c'

/* $Id: quality.c,v 1.5 1995/11/16 21:42:59 maffeis Exp maffeis $
 *
 * Author: Silvano Maffeis, maffeis@acm.org.
 * Copyright (c) 1995 by Silvano Maffeis, maffeis@acm.org.
 * All rights reserved.
 * 
 * THIS IS FREE SOFTWARE.
 * Permission to use, copy, modify, and distribute this software and 
 * documentation in all settings is hereby granted, provided that no fee is 
 * charged for this software and provided that this copyright notice appears 
 * in all copies of any software which is or includes a copy or modification 
 * of this software.
 */

#include <assert.h>
#include <stdio.h>

#include "config.h"
#include "quality.h"
#include "util.h"

/* simple quality control */

/* quality levels:
 * { level, throw away x samples, out of y }:
 */
quality_elem quality_table [] = {
  { 0, 0, 0 }, /* level 0: 100% quality                 */
  { 1, 1, 8 }, /* level 1: throw away 1 sample out of 8 */
  { 2, 1, 6 }, /* level 2: throw away 1 sample out of 6 */
  { 3, 1, 4 }, /* level 3: throw away 1 sample out of 4 */
  { 4, 1, 2 }  /* level 4: throw away 1 sample out of 2 */
};

/* decrease quality:
 */
void quality_dec(){
  if(g.quality < (sizeof(quality_table)/sizeof(quality_elem) -1) &&
     g.quality < QUALITY_WORST){
     ++g.quality;
     if(g.verbose) fprintf(stderr, "[%d]", g.quality);
  }
}

/* increase quality:
 */
void quality_inc(){
  if(g.quality > 0){
    --g.quality;
     if(g.verbose) fprintf(stderr, "[%d]", g.quality);
  }
}

/* throw away samples if g.quality > 0:
 */
int quality_shrink(int size, int *new_size){
  int i, j =0;
  char *newBufP = outbuf;

  *newBufP = quality_table[g.quality].tag;
  newBufP++;

  if(g.quality == 0){
    memcpy(newBufP, inbuf, size);
    *new_size = size +1;
    return TRUE;
  }

  assert(quality_table[g.quality].skip == 1);

  for(i =0; i < size; i++){
    if((i % quality_table[g.quality].out_of) == 0){
      i += quality_table[g.quality].skip;
    }
    newBufP[j++] = inbuf[i];
  }

  *new_size = j;

  return TRUE;
}

/* reconstruct samples that were thrown away by sender:
 */
int quality_expand(int size, int *new_size){
  int i, j =0;
  char *bufP = inbuf +1;

  if(g.quality != inbuf[0]){
    g.quality = inbuf[0];
  }

  if(g.quality == 0){
    memcpy(outbuf, bufP, size -1);
    *new_size = size -1;
    return TRUE;
  }

  assert(quality_table[g.quality].skip == 1);

  for(i =0; i < size -1; i++){
    if((i % quality_table[g.quality].out_of) == 0){
      outbuf[j++] = audio_interpolate(bufP[i-1], bufP[i]);
    }
    outbuf[j++] = bufP[i];
  }

  *new_size = j;

  return TRUE;
}
SHAR_EOF
fi # end of overwriting check
if test -f 'url.c'
then
	echo shar: will not over-write existing file "'url.c'"
else
cat << \SHAR_EOF > 'url.c'

/* $Id: url.c,v 1.2 1995/11/16 19:17:23 maffeis Exp maffeis $
 *
 * Author: Silvano Maffeis, maffeis@acm.org.
 * Copyright (c) 1995 by Silvano Maffeis, maffeis@acm.org.
 * All rights reserved.
 * 
 * THIS IS FREE SOFTWARE.
 * Permission to use, copy, modify, and distribute this software and 
 * documentation in all settings is hereby granted, provided that no fee is 
 * charged for this software and provided that this copyright notice appears 
 * in all copies of any software which is or includes a copy or modification 
 * of this software.
 */

#include <string.h>

#include "config.h"
#include "url.h"

int url_check(char *url){
  if(!url || strlen(url) == 0) return FALSE;
  if(strstr(url, URL_PREFIX) != url) return FALSE;
  if(strchr(url, '@') == 0) return FALSE;
  return TRUE;
}

int url_split(char *url, char *name, char *host, int *port){
  int i;
  char *oc = url;
  *port = TCP_DEFPORT;

  if(!url_check(url)) return FALSE;

  oc += strlen(URL_PREFIX);
  for(i =0; *oc != '@'; i++, oc++) name[i] = *oc;
  name[i] = '\0';

  oc++;
  for(i =0; *oc != '\0' && *oc != ':'; i++, oc++) host[i] = *oc;
  host[i] = '\0';

  if(*oc == ':'){
    oc++;
    *port = atoi(oc);
  }

  return TRUE;
}
SHAR_EOF
fi # end of overwriting check
if test -f 'filter.h'
then
	echo shar: will not over-write existing file "'filter.h'"
else
cat << \SHAR_EOF > 'filter.h'

/* $Id: filter.h,v 1.3 1995/11/16 19:17:23 maffeis Exp maffeis $
 *
 * Author: Silvano Maffeis, maffeis@acm.org.
 * Copyright (c) 1995 by Silvano Maffeis, maffeis@acm.org.
 * All rights reserved.
 * 
 * THIS IS FREE SOFTWARE.
 * Permission to use, copy, modify, and distribute this software and 
 * documentation in all settings is hereby granted, provided that no fee is 
 * charged for this software and provided that this copyright notice appears 
 * in all copies of any software which is or includes a copy or modification 
 * of this software.
 */

typedef struct filter_elem {
  int (*send)(int inLen, int *outLen);
  int (*recv)(int inLen, int *outLen);
} filter_elem;

extern filter_elem filter_table [];

int filter_send(int inLen, int *outLen);
int filter_recv(int inLen, int *outLen);
SHAR_EOF
fi # end of overwriting check
if test -f 'quality.h'
then
	echo shar: will not over-write existing file "'quality.h'"
else
cat << \SHAR_EOF > 'quality.h'

/* $Id: quality.h,v 1.3 1995/11/16 19:17:23 maffeis Exp maffeis $
 *
 * Author: Silvano Maffeis, maffeis@acm.org.
 * Copyright (c) 1995 by Silvano Maffeis, maffeis@acm.org.
 * All rights reserved.
 * 
 * THIS IS FREE SOFTWARE.
 * Permission to use, copy, modify, and distribute this software and 
 * documentation in all settings is hereby granted, provided that no fee is 
 * charged for this software and provided that this copyright notice appears 
 * in all copies of any software which is or includes a copy or modification 
 * of this software.
 */

typedef struct quality_elem {
  char  tag;
  short skip;
  short out_of;
} quality_elem;

extern quality_elem quality_table [];

void quality_inc();
void quality_dec();
int quality_shrink(int size, int *new_size);
int quality_expand(int size, int *new_size);
SHAR_EOF
fi # end of overwriting check
if test -f 'url.h'
then
	echo shar: will not over-write existing file "'url.h'"
else
cat << \SHAR_EOF > 'url.h'

/* $Id: url.h,v 1.2 1995/11/16 19:17:23 maffeis Exp maffeis $
 *
 * Author: Silvano Maffeis, maffeis@acm.org.
 * Copyright (c) 1995 by Silvano Maffeis, maffeis@acm.org.
 * All rights reserved.
 * 
 * THIS IS FREE SOFTWARE.
 * Permission to use, copy, modify, and distribute this software and 
 * documentation in all settings is hereby granted, provided that no fee is 
 * charged for this software and provided that this copyright notice appears 
 * in all copies of any software which is or includes a copy or modification 
 * of this software.
 */

int url_check(char *url);
int url_split(char *url, char *name, char *host, int *port);

#define URL_PREFIX "iphone://"
SHAR_EOF
fi # end of overwriting check
if test -f 'iphone.c'
then
	echo shar: will not over-write existing file "'iphone.c'"
else
cat << \SHAR_EOF > 'iphone.c'

/* $Id: iphone.c,v 1.5 1995/11/22 13:00:43 maffeis Exp maffeis $
 *
 * Author: Silvano Maffeis, maffeis@acm.org.
 * Copyright (c) 1995 by Silvano Maffeis, maffeis@acm.org.
 * All rights reserved.
 * 
 * THIS IS FREE SOFTWARE.
 * Permission to use, copy, modify, and distribute this software and 
 * documentation in all settings is hereby granted, provided that no fee is 
 * charged for this software and provided that this copyright notice appears 
 * in all copies of any software which is or includes a copy or modification 
 * of this software.
 */

#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <signal.h>

#include "config.h"
#include "tcp.h"
#include "audio.h"
#include "util.h"

int remoteClosed = FALSE;

void sigpipe_hdlr(){
  remoteClosed = TRUE;
}

int client_handle_tcp_input(int audioFd, int socket){
  int read_bytes;
  static int verboseCnt = 0;

  if(!tcp_read(socket, inbuf, g.pktsize, &read_bytes)) return FALSE;
  if(g.verbose && (++verboseCnt == 10)){
    fprintf(stderr, "r");
    verboseCnt = 0;
  }
  if(!filter_recv(read_bytes, &read_bytes)) return FALSE;

  if(read_bytes > 0){
    if(!audio_write(audioFd, inbuf, read_bytes)) return FALSE;
  }

#ifdef ACK_SEND
  if(!tcp_read_ack(socket)) abort();
#endif

  return TRUE;
}

int client_handle_audio_input(int audioFd, int socket){
  int read_bytes;
  double orgSize;
  static int verboseCnt = 0;

  if(!audio_read(audioFd, inbuf, g.pktsize, &read_bytes)) return FALSE;
  orgSize = (double)read_bytes;
  if(!filter_send(read_bytes, &read_bytes)) return FALSE;
  /*fprintf(stderr, "compress: %lf\n", 1.0 - read_bytes/orgSize);*/
  
  if(read_bytes > 0){
    if(!tcp_write(socket, inbuf, read_bytes)) return FALSE;
    if(g.verbose && (++verboseCnt == 10)){
      fprintf(stderr, "s");
      verboseCnt = 0;
    }
  }

#ifdef ACK_SEND
  if(!tcp_send_ack(socket)) abort();
#endif

  return TRUE;
}

int client_run(char *host, int port, char *audiodev){
  int socket, audioFd;
  fd_set readfds;

  if(g.verbose)
    fprintf(stderr, "%s: version %s. maffeis@acm.org\n", g.pgm, g.version);

  FD_ZERO(&readfds);

  if(!audio_open(&audioFd, audiodev, O_RDWR)) return FALSE;
  if(!audio_output_to_jack(audioFd)) return FALSE;
  if(!audio_set_volume(audioFd, g.volume)) return FALSE;
  if(!tcp_connect(&socket, host, port)) return FALSE;
  if(!util_send_configdata(socket)) return FALSE;

  for(;;){
    FD_ZERO(&readfds);
    FD_SET(audioFd, &readfds);
    FD_SET(socket, &readfds);

    if(select(32, &readfds, 0, 0, 0) == -1){
      perror("client_run: select"); return FALSE;
    }

    if(remoteClosed){
      fprintf(stderr, "%s: remote has closed connection\n");
      break;
    }

    if(FD_ISSET(socket, &readfds))
      if(!client_handle_tcp_input(audioFd, socket)) break;

    if(FD_ISSET(audioFd, &readfds))
      if(!client_handle_audio_input(audioFd, socket)) break;

  }

  return FALSE;
}

void client_usage(char *pgm){
  fprintf(stderr, 
	  "usage: %s url [-a audiodev] [-v volume] [-V] [-s pktsize]\n", pgm);
  exit(1);
}

main(int argc, char **argv){
  char *audiofile = AUDIO_DEV;
  int port, i, volume = AUDIO_VOLUME, verbose = FALSE, pksize = AUDIO_BLKSZ;
  char host [128], name [64], url[64];
  if(argc < 2) client_usage(argv[0]);

  for(i = 2; i < argc; i++){
    if(argv[i][0] != '-' || argv[i][2] != '\0') client_usage(argv[0]);
    switch(argv[i][1]){
    case 'a':
      if(argv[i+1] == 0) client_usage(argv[0]);
      audiofile = argv[i+1];
      i++;
      break;
    case 'v':
      if(argv[i+1] == 0) client_usage(argv[0]);
      volume = atoi(argv[i+1]);
      i++;
      break;
    case 'V':
      verbose = TRUE;
      break;
    case 's':
      if(argv[i+1] == 0) client_usage(argv[0]);
      pksize = atoi(argv[i+1]);
      if(pksize < AUDIO_MIN_BLKSZ || pksize > AUDIO_MAX_BLKSZ){
	fprintf(stderr, "%s: pktsize must be >= %d and <= %d\n", 
		argv[0], AUDIO_MIN_BLKSZ, AUDIO_MAX_BLKSZ);
	client_usage(argv[0]);
      }
      i++;
      break;
    default:
      client_usage(argv[0]);
    }
  }

  signal(SIGPIPE, sigpipe_hdlr);
  
  util_fill_global(argv[0], volume, verbose, pksize);
  strcpy(url, argv[1]);
  util_load_rcfile(url);
  if(!url_check(url)) client_usage(argv[0]);
  if(!url_split(url, name, host, &port)) client_usage(argv[0]);
  exit(client_run(host, port, audiofile));
}
SHAR_EOF
fi # end of overwriting check
if test -f 'tcp.c'
then
	echo shar: will not over-write existing file "'tcp.c'"
else
cat << \SHAR_EOF > 'tcp.c'

/* $Id: tcp.c,v 1.7 1995/11/17 21:46:23 maffeis Exp maffeis $
 *
 * Author: Silvano Maffeis, maffeis@acm.org.
 * Copyright (c) 1995 by Silvano Maffeis, maffeis@acm.org.
 * All rights reserved.
 * 
 * THIS IS FREE SOFTWARE.
 * Permission to use, copy, modify, and distribute this software and 
 * documentation in all settings is hereby granted, provided that no fee is 
 * charged for this software and provided that this copyright notice appears 
 * in all copies of any software which is or includes a copy or modification 
 * of this software.
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>

#include <netdb.h>

#include "config.h"
#include "util.h"

#define TCP_SETBUFSZ 0
#define TCP_NOLINGER
#define TCP_KEEPALIVE

/* create socket and listen:
 */
int tcp_listen(int *fd, int port){
  int length;
  struct sockaddr_in server;
  memset(&server, 0, sizeof(server));

  if((*fd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
    perror("tcp_listen: socket"); return FALSE;
  }

  server.sin_family      = AF_INET;
  server.sin_addr.s_addr = INADDR_ANY;
  server.sin_port        = port;

  if(bind(*fd, (struct sockaddr*)&server, sizeof(server)) == -1) {
    perror("tcp_listen: bind"); return FALSE;
  }

  if(listen(*fd, 5) < 0){
    perror("tcp_listen: listen"); return FALSE;
  }

  return TRUE;
}

/* accept syscall and set socket options:
 */
int tcp_accept(int socket, int *newSocket){
  int len;
  struct linger linger;

  if((*newSocket = accept(socket, 0, 0)) == -1){
    perror("tcp_accept: accept"); return FALSE;
  }

#if TCP_SETBUFSZ > 0
  len = TCP_SETBUFSZ;
  if(setsockopt(*newSocket, SOL_SOCKET, SO_SNDBUF, &len, sizeof(len)) == -1){
     perror("tcp_listen: setsockopt(SO_SNDBUF)"); return FALSE;
  }

  if(setsockopt(*newSocket, SOL_SOCKET, SO_RCVBUF, &len, sizeof(len)) == -1){
     perror("tcp_listen: setsockopt(SO_RCVBUF)"); return FALSE;
  }
#endif /* TCP_SETBUFSZ */

#ifdef TCP_KEEPALIVE
  len = 0;
  if(setsockopt(*newSocket, SOL_SOCKET, SO_KEEPALIVE, (char*)&len, 
		sizeof(len)) == -1){
     perror("tcp_listen: setsockopt(SO_KEEPALIVE)"); return FALSE;
  }
#endif

#ifdef TCP_NOLINGER
  linger.l_onoff  = 1;
  linger.l_linger = 0;
  if(setsockopt(*newSocket, SOL_SOCKET, SO_LINGER, (char*)&linger, 
		sizeof(linger)) == -1){
     perror("tcp_listen: setsockopt(SO_LINGER)"); return FALSE;
  }
#endif

  return TRUE;
}

/* connect syscall and set socket options:
 */
int tcp_connect(int *fd, char *host, int port){
  struct hostent *hp;
  struct sockaddr_in server;
  int len;
  memset(&server, 0, sizeof(server));

  if((*fd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
    perror("tcp_connect: socket"); return FALSE;
  }

  if(isdigit(host[0])){
    server.sin_addr.s_addr = inet_addr(host);
  } else {
    if((hp = gethostbyname(host)) == 0){
      perror("tcp_connect: gethostbyname"); 
      close(*fd);
      return FALSE;
    }
    memcpy((char*)&server.sin_addr, (char*)hp->h_addr, hp->h_length);
  }

  server.sin_family = AF_INET;
  server.sin_port = htons(port);

  if(connect(*fd, (struct sockaddr*)&server, sizeof(server)) == -1){
    perror("tcp_connect: connect"); 
    close(*fd);
    return FALSE;
  }

#if TCP_SETBUFSZ > 0
  len = TCP_SETBUFSZ;
  if(setsockopt(*fd, SOL_SOCKET, SO_SNDBUF, &len, sizeof(len)) == -1){
     perror("tcp_connect: setsockopt(SO_SNDBUF)"); return FALSE;
  }

  if(setsockopt(*fd, SOL_SOCKET, SO_RCVBUF, &len, sizeof(len)) == -1){
     perror("tcp_connect: setsockopt(SO_RCVBUF)"); return FALSE;
  }
#endif /* TCP_SETBUFSZ */

#ifdef TCP_KEEPALIVE
  len = 0;
  if(setsockopt(*fd, SOL_SOCKET, SO_KEEPALIVE, (char*)&len, 
		sizeof(len)) == -1){
     perror("tcp_connect: setsockopt(SO_KEEPALIVE)"); return FALSE;
  }
#endif

  return TRUE;
}

/* send buf over TCP connection:
 */
int tcp_write(int fd, char *buf, int size){
  if(send(fd, (char*)&size, sizeof(size), 0) != sizeof(size)){
    perror("tcp_write: write1"); return FALSE;
  }

  if(send(fd, buf, size, 0) != size){
    perror("tcp_write: write2"); return FALSE;
  }

  return TRUE;
}

/* read at most max_bytes from TCP connection:
 */
int tcp_read(int fd, char *buf, int max_bytes, int *bytes_read){
  int n;
  char *brp = (char*)bytes_read;

  n = 0;
  while(n != sizeof(*bytes_read)){
    if((n += recv(fd, brp + n, sizeof(*bytes_read) - n, 0)) <= 0){
      perror("tcp_read: read1"); return FALSE;
    }
  }

  n = 0;
  while(n != *bytes_read){
    if((n += recv(fd, buf + n, *bytes_read - n, 0)) <= 0){
      perror("tcp_read: read2"); return FALSE;
    }
  }

  return TRUE;
}

/* send an ack byte:
 */
int tcp_send_ack(int fd){
  char ack = 'y';
  if(send(fd, &ack, sizeof(ack), 0) != sizeof(ack)){
    perror("tcp_send_ack: send"); return FALSE;
  }
  return TRUE;
}

/* receive an ack byte:
 */
int tcp_read_ack(int fd){
  char ack;
  if(recv(fd, &ack, sizeof(ack), 0) != sizeof(ack)){
    perror("tcp_read_ack: read"); return FALSE;
  }
  if(ack != 'y'){
    fprintf(stderr, "%s: tcp_read_ack: got bad ack\n", g.pgm); return FALSE;
  }
  return TRUE;
}
SHAR_EOF
fi # end of overwriting check
if test -f 'util.c'
then
	echo shar: will not over-write existing file "'util.c'"
else
cat << \SHAR_EOF > 'util.c'

/* $Id: util.c,v 1.7 1995/11/22 13:00:43 maffeis Exp maffeis $
 *
 * Author: Silvano Maffeis, maffeis@acm.org.
 * Copyright (c) 1995 by Silvano Maffeis, maffeis@acm.org.
 * All rights reserved.
 * 
 * THIS IS FREE SOFTWARE.
 * Permission to use, copy, modify, and distribute this software and 
 * documentation in all settings is hereby granted, provided that no fee is 
 * charged for this software and provided that this copyright notice appears 
 * in all copies of any software which is or includes a copy or modification 
 * of this software.
 */

#include <stdio.h>
#include <stdlib.h>
#include <sys/param.h>
#include <sys/errno.h>
#include <pwd.h>

#include "config.h"
#include "util.h"

extern int errno;

/* fill out the util_global structure:
 */
void util_fill_global(char *pgm, int volume, int verbose, int pktsize){
  g.myurl[0] = '\0';
  g.pgm      = pgm;
  g.quality  = 0;
  g.volume   = volume;
  g.verbose  = verbose;
  g.pktsize  = pktsize;
  g.version  = VERSION;
}
  
/* the first packet contains configuration data, e.g., the sender's URL,
 * packet size, etc.
 */
int util_send_configdata(int fd){
  char cdata [128];
  struct passwd *pwe;
  char host [64];
  char domain [64];

  /* g.myurl is set in the ~/.filerc file. If not set, guess the
   * client's URL:
   */
  if(g.myurl[0] == '\0'){
    if((pwe = getpwuid(geteuid())) == 0){
      perror("util_send_configdata: getpwuid");
      return FALSE;
    }
    
    if(gethostname(host, sizeof(host)) == -1){
      perror("util_send_configdata: gethostname");
      return FALSE;
    }
    
    if(getdomainname(domain, 64) == -1){
      perror("util_send_configdata: getdomainname");
      return FALSE;
    }
    
    sprintf(cdata, "iphone://%s@%s.%s %d %s", 
	    pwe->pw_name, host, domain, g.pktsize, g.version);
  } else {
    sprintf(cdata, "%s %d %s", g.myurl, g.pktsize, g.version);
  }
  
  return tcp_write(fd, cdata, strlen(cdata)+1);
}

/* receive and set the client's config data:
 */
int util_recv_configdata(int fd){
  char cdata [128];
  char url[64];
  char version [8];
  int bytes_read;
  
  if(!tcp_read(fd, cdata, sizeof(cdata), &bytes_read)) return FALSE;
  if(sscanf(cdata, "%s %d %s", url, &g.pktsize, version) != 3){
    fprintf(stderr, "%s: got a malformed config line from: %s\n", 
	    g.pgm, cdata);
    return FALSE;
  }

  if(strcmp(version, g.version)){
    fprintf(stderr, "%s: WARNING: client runs version %s, but I'm version %s\n",
	    g.pgm, version, g.version);
  }
  
  fprintf(stderr, "%s: call from %s\n", g.pgm, url);

  return TRUE;
}

/* parse ~/.iphonerc file:
 */
int util_load_rcfile(char *url){
  FILE *f;
  char *h, cmd[64], val[64], val2[64];
  int n;

  if((h = getenv("HOME")) == 0){
    perror("util_load_rcfile: getenv(\"HOME\")");
    return FALSE;
  }

  if(chdir(h) == -1){
    perror("util_load_rcfile: chdir");
    return FALSE;
  }

  if((f = fopen(UTIL_RCFILE, "r")) == 0){
    if(errno == ENOENT) return TRUE;
    else perror("util_load_rcfile: fopen");
    return FALSE;
  }

  while((n = fscanf(f, "%s%s", cmd, val)) != EOF){
    if(n != 2){
      fprintf(stderr, "%s: malformed %s entry \"%s\"\n", 
	      g.pgm, UTIL_RCFILE, cmd);
      continue;
    }
    if(!strcmp(cmd, "myurl")){
      strcpy(g.myurl, val);
    } else if(!strcmp(cmd, "alias")){
      if(fscanf(f, "%s", val2) != 1){
	fprintf(stderr, "%s: malformed url in %s alias: %s\n", 
		g.pgm, UTIL_RCFILE, val);
	continue;
      }
      if(!strcmp(val, url)){
	if(!url_check(val2)){
	  fprintf(stderr, "%s: malformed url in %s alias: %s\n", 
		  g.pgm, UTIL_RCFILE, val2);
	  continue;
	}
	strcpy(url, val2);
      }
    } else {
      fprintf(stderr, "%s: skipping unknown %s entry \"%s\"\n", 
	      g.pgm, UTIL_RCFILE, cmd);
      continue;
    }
  }

  fclose(f);
  return TRUE;
}

/* swap work buffers. Needed by filter module:
 */
void swapbuf(){
  char *tmp = inbuf;
  inbuf = outbuf;
  outbuf = tmp;
}

struct util_global g;

char buf1[2*AUDIO_MAX_BLKSZ];
char buf2[2*AUDIO_MAX_BLKSZ];

char *inbuf  = buf1;
char *outbuf = buf2;
SHAR_EOF
fi # end of overwriting check
#	End of shell archive
exit 0


