home *** CD-ROM | disk | FTP | other *** search
- Newsgroups: comp.unix.internals
- Path: sparky!uunet!haven.umd.edu!darwin.sura.net!udel!gvls1!jabber!candle!root
- From: root@candle.uucp (Bruce Momjian)
- Subject: select()/poll() source code posting
- Organization: a consultant's basement
- Date: Thu, 27 Aug 1992 21:22:35 GMT
- Message-ID: <1992Aug27.212235.1684@candle.uucp>
- X-Newsreader: Tin 1.1 PL5
- Lines: 753
-
- Attached is a shar file containing an implementation of poll() and
- select() for systems that lack them. It is implemented as a device
- driver. I have called it pol() and selec() so as not to confuse them
- with the originals. It only works on reads and writes.
-
- This package is not ready for prime-time. If there is enough interest,
- and if someone can help me with the intricacies of the tty structure
- under different modes, I would be glad to polish it up.
-
- The shar file is 17k. My thanks to those people in comp.unix.internals
- who helped me with some of the details. This code was written by
- looking at other PD device driver implementaions, like fas(async),
- pty(ptys), herc(hercules graphics), and mouse(mouse). The code was
- developed on AT&T 386 Unix SVr3.1, which has poll(), but only for
- streams.
-
- -------------------------------------------------------------------
- #!/bin/sh
- # This is a shell archive (shar 3.32)
- # made 08/27/1992 20:44 UTC by root@candle
- # Source directory /u/src/pol/tmp
- #
- # existing files WILL be overwritten
- #
- # This shar contains:
- # length mode name
- # ------ ---------- ------------------------------------------
- # 877 -rw-r--r-- INSTALL
- # 964 -rw-r--r-- README
- # 8399 -rw-r--r-- pol.c
- # 521 -rw-r--r-- pol.h
- # 2624 -rw-r--r-- pol.lib.c
- # 305 -rw-r--r-- poltest.c
- # 62 -rw-r--r-- selec.h
- #
- if touch 2>&1 | fgrep 'amc' > /dev/null
- then TOUCH=touch
- else TOUCH=true
- fi
- # ============= INSTALL ==============
- echo "x - extracting INSTALL (Text)"
- sed 's/^X//' << 'SHAR_EOF' > INSTALL &&
- XBasically, pol.c is installed as a device driver with this line in
- Xthe mdevice file:
- X
- Xpol oci iHc pol 0 12 1 1 -1
- X ^
- X | major number of your choice
- X
- XAdd an appropriate file in sdevice.d link this:
- X
- Xpol Y 1 0 0 0 0 0 0 0
- X
- Xand add a file in node.d so a character device called /dev/pol getsc
- Xcreated:
- X
- Xpol pol c 0
- X
- XOnly one entry is needed, even though many programs can pol at the same
- Xtime.
- X
- XAdd a subdirectory under /etc/conf/pack.d called pol and compile pol.c
- Xand copy it into /etc/conf/pack.d/pol/Driver.o.
- X
- XCompile pol.lib.c and install it in your personal C library. It will be
- Xthe link points for pol() and selec().
- X
- XInstall pol.h and selec.h in /usr/include/sys.
- X
- XREADME - this file
- Xpol.c - device driver
- Xpol.lib.c - C library module
- Xpol.h - /usr/include/sys/pol.h file
- Xselec.h - /usr/include/sys/selec.h file
- Xpoltest.c - used to test the device driver
- SHAR_EOF
- $TOUCH -am 0827161092 INSTALL &&
- chmod 0644 INSTALL ||
- echo "restore of INSTALL failed"
- set `wc -c INSTALL`;Wc_c=$1
- if test "$Wc_c" != "877"; then
- echo original size 877, current size $Wc_c
- fi
- # ============= README ==============
- echo "x - extracting README (Text)"
- sed 's/^X//' << 'SHAR_EOF' > README &&
- XBasically, this package implements poll() and select() as a device
- Xdriver. It is called pol() and selec() so as not to be confused with
- Xthe real thing.
- X
- XThe driver accepts bit masks of read and write file descriptors. It
- Xfollows the descriptors to the file, then inode tables and gets the
- Xmajor and minor device numbers. It then finds the tty structures
- Xassociated with those devices and stores their addresses.
- X
- XIt scans through the addresses once, looking for read or written
- Xcharacters, then either returns or goes to sleep. Upon invocation, it
- Xreplaces the standard tty input/output functions with its own functions,
- Xwhich wake up the driver and call the usual tty functions. In this way,
- Xthe driver is woken up when tty activity occurs.
- X
- XA C library library is provided to translate the normal poll() and
- Xselect() parameters into pol device driver parameters, and then call the
- Xdevice driver.
- X
- XBruce Momjian, root@candle.uucp (root%candle.uucp@bts.com)
- SHAR_EOF
- $TOUCH -am 0827153792 README &&
- chmod 0644 README ||
- echo "restore of README failed"
- set `wc -c README`;Wc_c=$1
- if test "$Wc_c" != "964"; then
- echo original size 964, current size $Wc_c
- fi
- # ============= pol.c ==============
- echo "x - extracting pol.c (Text)"
- sed 's/^X//' << 'SHAR_EOF' > pol.c &&
- X/*
- X pol.c - poll/select() implemented as a device driver
- X
- X Bruce Momjian (root%candle.uucp@bts.com
- X
- X*/
- X
- X/* tabs = 4 */
- X/* Include files */
- X#undef DEBUG
- X#undef USE_BUF /* use tty->t/rbuf */
- X
- X#ifdef DEBUG
- X#define DB(x) x
- X#else
- X#define DB(x)
- X#endif
- X
- X#include <errno.h>
- X#include <sys/pol.h>
- X#include <sys/types.h>
- X#include <sys/param.h>
- X#include <sys/ioctl.h>
- X#include <sys/dir.h>
- X#include <sys/signal.h>
- X#include <sys/user.h>
- X#include <sys/file.h>
- X#include <sys/inode.h>
- X#include <sys/conf.h>
- X#include <sys/tty.h>
- X#include <sys/sxt.h>
- X#include <sys/sysmacros.h>
- X
- X#define TTIN_FUNCT ttin
- X#define TTWRITE_FUNCT ttwrite
- X#define POL_READ_FLAG 0
- X#define POL_WRITE_FLAG 1
- X
- Xint pol_uses = 0;
- Xint pol_intr = 0;
- X
- Xextern struct tty sxt_tty[], pts_tty[];
- Xextern int sxtopen(), ptmopen();
- Xextern char sxt_buf[];
- Xextern int ttin(), ttwrite();
- X
- Xint ttinpol(), ttwrpol(), poltimeo();
- Xextern time_t lbolt;
- X
- Xstruct pol_table {
- X caddr_t addr
- X#ifdef USE_BUF
- X , addr2
- X#endif
- X ;
- X int fd;
- X};
- X
- X/*---------------------------------------------------------------------------
- X**
- X** polopen
- X**
- X**--------------------------------------------------------------------------*/
- Xpolopen(fdev, flags)
- Xint fdev, flags;
- X{
- X if (pol_uses == 0)
- X {
- X if (linesw[0].l_input != TTIN_FUNCT)
- X {
- X printf("pol error: linesw structure corrupted.\n");
- X u.u_error = ENOMSG;
- X u.u_rval1 = -1;
- X return -1;
- X }
- X if (linesw[0].l_write != TTWRITE_FUNCT)
- X {
- X printf("pol error: linesw structure corrupted.\n");
- X u.u_error = ENOMSG;
- X u.u_rval1 = -1;
- X return -1;
- X }
- X spltty();
- X linesw[0].l_input = ttinpol;
- X linesw[0].l_write = ttwrpol;
- X spl0();
- X pol_uses = 1;
- X }
- X return 0;
- X}
- X
- X/*---------------------------------------------------------------------------
- X**
- X** polclose
- X**
- X**--------------------------------------------------------------------------*/
- Xpolclose(fdev)
- Xint fdev;
- X{
- X pol_uses = 0;
- X if (linesw[0].l_input != ttinpol)
- X {
- X printf("pol error: linesw structure corrupted.\n");
- X u.u_error = ENOMSG;
- X u.u_rval1 = -1;
- X }
- X else if (linesw[0].l_write != ttwrpol)
- X {
- X printf("pol error: linesw structure corrupted.\n");
- X u.u_error = ENOMSG;
- X u.u_rval1 = -1;
- X }
- X else u.u_rval1 = 0;
- X spltty();
- X linesw[0].l_input = ttin;
- X linesw[0].l_write = ttwrite;
- X spl0();
- X return u.u_rval1;
- X}
- X
- X/*---------------------------------------------------------------------------
- X**
- X** polioctl
- X**
- X**--------------------------------------------------------------------------*/
- Xpolioctl(fdev, command, polfd_p, mode)
- Xint fdev, command, mode;
- Xstruct polfd *polfd_p;
- X{
- X int i,
- X rfds_num = 0,
- X wfds_num = 0,
- X hits = 0,
- X timeout_idx = 0;
- X time_t start_ticks = lbolt, timeout_ticks;
- X struct polfd pol_s;
- X struct pol_table rfds_pol[NPOLFILE],
- X wfds_pol[NPOLFILE];
- X
- X DB(printf("Start func\n"));
- X if (copyin(polfd_p, &pol_s, sizeof(struct polfd)) != 0)
- X {
- X u.u_error = EFAULT;
- X u.u_rval1 = -1;
- X return -1;
- X }
- X if (command != POL_FDS)
- X {
- X u.u_error = EINVAL;
- X u.u_rval1 = -1;
- X return -1;
- X }
- X
- X if (pol_s.timeout != -1 && pol_s.timeout != 0)
- X {
- X timeout_ticks = start_ticks + pol_s.timeout * (HZ)/1000;
- X timeout_idx = timeout( poltimeo,
- X (caddr_t)NULL,
- X pol_s.timeout * (HZ)/1000);
- X }
- X
- X DB(printf("Start for loop.\n"));
- X DB(printf("Start while loop.\n"));
- X if (poladdr( pol_s.rfds, rfds_pol, &rfds_num, POL_READ_FLAG) == -1)
- X {
- X if (timeout_idx != 0 && lbolt < timeout_ticks)
- X untimeout(timeout_idx);
- X return -1;
- X }
- X if (poladdr( pol_s.wfds, wfds_pol, &wfds_num, POL_WRITE_FLAG) == -1)
- X {
- X if (timeout_idx != 0 && lbolt < timeout_ticks)
- X untimeout(timeout_idx);
- X return -1;
- X }
- X
- X pol_s.rfds = pol_s.wfds = 0;
- X
- X while(1)
- X {
- X do
- X {
- X pol_intr = 0;
- X spl0();
- X for (i=0; i < rfds_num; i++)
- X if (*(rfds_pol[i].addr) != 0)
- X {
- X pol_s.rfds |= 1 << rfds_pol[i].fd;
- X hits++;
- X }
- X#ifdef USE_BUF
- X else if (*(rfds_pol[i].addr2) != 0)
- X {
- X pol_s.rfds |= 1 << rfds_pol[i].fd;
- X hits++;
- X }
- X#endif
- X for (i=0; i < wfds_num; i++)
- X if (*(wfds_pol[i].addr) != 0)
- X {
- X pol_s.wfds |= 1 << wfds_pol[i].fd;
- X hits++;
- X }
- X#ifdef USE_BUF
- X else if (*(wfds_pol[i].addr2) != 0)
- X {
- X pol_s.wfds |= 1 << wfds_pol[i].fd;
- X hits++;
- X }
- X#endif
- X if (hits != 0)
- X {
- X if (timeout_idx != 0 && lbolt < timeout_ticks)
- X untimeout(timeout_idx);
- X DB(printf("got result\n"));
- X if (copyout(&pol_s, polfd_p, sizeof(struct polfd)) != 0)
- X {
- X u.u_error = EFAULT;
- X u.u_rval1 = -1;
- X }
- X else u.u_rval1 = hits;
- X return u.u_rval1;
- X }
- X spltty();
- X } while (pol_intr != 0);
- X DB(printf("Going to sleep\n"));
- X if (pol_s.timeout == 0 || (
- X pol_s.timeout != -1 && lbolt >= timeout_ticks))
- X {
- X spl0();
- X if (timeout_idx != 0 && lbolt < timeout_ticks)
- X untimeout(timeout_idx);
- X u.u_rval1 = 0;
- X return 0;
- X }
- X sleep((caddr_t *)&pol_uses, PSLEP);
- X spl0();
- X }
- X}
- X
- X/*---------------------------------------------------------------------------
- X**
- X** wakepl
- X**
- X**--------------------------------------------------------------------------*/
- Xwakepl(caddr)
- Xcaddr_t caddr;
- X{
- X if (pol_uses > 0)
- X {
- X DB(printf("wakepl called\n"));
- X wakeup((caddr_t)&pol_uses);
- X }
- X return wakeup(caddr);
- X}
- X
- X/*---------------------------------------------------------------------------
- X**
- X** poltimeo
- X**
- X**--------------------------------------------------------------------------*/
- Xpoltimeo()
- X{
- X if (pol_uses > 0)
- X {
- X DB(printf("timedout wakeup\n"));
- X wakeup((caddr_t)&pol_uses);
- X }
- X}
- X
- X/*---------------------------------------------------------------------------
- X**
- X** ttinpol
- X**
- X**--------------------------------------------------------------------------*/
- Xttinpol(tty_p, flag)
- Xstruct tty *tty_p;
- Xint flag;
- X{
- X if (pol_uses > 0)
- X {
- X DB(printf("ttinpol called\n"));
- X wakeup((caddr_t)&pol_uses);
- X }
- X pol_intr = 1;
- X return ttin(tty_p, flag);
- X}
- X
- X/*---------------------------------------------------------------------------
- X**
- X** ttwrpol
- X**
- X**--------------------------------------------------------------------------*/
- Xttwrpol(tty_p)
- Xstruct tty *tty_p;
- X{
- X if (pol_uses > 0)
- X {
- X DB(printf("ttwrpol called\n"));
- X wakeup((caddr_t)&pol_uses);
- X }
- X pol_intr = 1;
- X return ttwrite(tty_p);
- X}
- X
- X
- X/*---------------------------------------------------------------------------
- X**
- X** poladdr
- X**
- X**--------------------------------------------------------------------------*/
- Xpoladdr(fmask, fds_pol, num_fds, flag)
- Xint fmask, *num_fds, flag;
- Xstruct pol_table *fds_pol;
- X{
- X int i, j;
- X struct inode *fd_inode;
- X struct tty *major_tty;
- X struct Link *fd_link;
- X
- X for (i = 0; i != -1 && i <= NPOLFILE; i++)
- X {
- X if ( (fmask & (1 << i)) == 0)
- X continue;
- X DB(printf("Found fd\n"));
- X fds_pol->fd = i;
- X fd_inode = (u.u_ofile[i])->f_up.f_uinode;
- X if ( (fd_inode->i_ftype & IFMT) == IFCHR)
- X {
- X DB(printf("char device\n"));
- X DB(printf("major %d\n",major(fd_inode->i_rdev)));
- X DB(printf("minor %d\n",minor(fd_inode->i_rdev)));
- X if ( (major_tty=cdevsw[major(fd_inode->i_rdev)].d_ttys) == 0)
- X if (cdevsw[major(fd_inode->i_rdev)].d_open == sxtopen)
- X { /* sxt */
- X fds_pol->addr = (caddr_t)sxt_buf +
- X (long)LINK(minor(fd_inode->i_rdev)) *
- X (long)(sizeof(struct Link) +
- X sizeof(struct Channel) * (MAXPCHAN-1)) +
- X (long)&((struct Link *)0)->chans[0] +
- X CHAN(minor(fd_inode->i_rdev)) *
- X sizeof(struct Channel) +
- X (long)&((struct Channel *)0)->tty;
- X }
- X else if (cdevsw[major(fd_inode->i_rdev)].d_open == ptmopen)
- X { /* master ptys */
- X fds_pol->addr = (caddr_t)pts_tty;
- X flag = !flag; /* swap reads and writes*/
- X }
- X else
- X {
- X u.u_error = ENXIO;
- X u.u_rval1 = -1;
- X return -1;
- X }
- X else
- X fds_pol->addr =
- X (caddr_t)major_tty +
- X minor(fd_inode->i_rdev) * sizeof(struct tty);
- X }
- X else if ( (fd_inode->i_ftype & IFMT) == IFIFO)
- X fds_pol->addr = (caddr_t)&fd_inode->i_size;
- X else
- X {
- X u.u_error = EBADF;
- X u.u_rval1 = -1;
- X return -1;
- X }
- X DB(printf("Got address\n"));
- X if ( flag == POL_READ_FLAG)
- X {
- X#ifdef USE_BUF
- X fds_pol->addr2 = fds_pol->addr +
- X (long)&((struct tty *)0)->t_rbuf.c_count;
- X#endif
- X fds_pol->addr += (long)&((struct tty *)0)->t_rawq.c_cc;
- X }
- X else
- X {
- X#ifdef USE_BUF
- X fds_pol->addr2 = fds_pol->addr +
- X (long)&((struct tty *)0)->t_tbuf.c_count;
- X#endif
- X fds_pol->addr += (long)&((struct tty *)0)->t_outq.c_cc;
- X }
- X (*num_fds)++;
- X fds_pol++;
- X if ( (fmask &= ~(1 <<i)) == 0)
- X break;
- X }
- X return 0;
- X}
- SHAR_EOF
- $TOUCH -am 0827154392 pol.c &&
- chmod 0644 pol.c ||
- echo "restore of pol.c failed"
- set `wc -c pol.c`;Wc_c=$1
- if test "$Wc_c" != "8399"; then
- echo original size 8399, current size $Wc_c
- fi
- # ============= pol.h ==============
- echo "x - extracting pol.h (Text)"
- sed 's/^X//' << 'SHAR_EOF' > pol.h &&
- X/* sys/pol.h */
- X
- X#define POL_FDS 0x1234dcba
- X
- Xstruct polfd {
- X int rfds, wfds; /* file desc to poll */
- X int timeout; /* events of interest on fd */
- X};
- X
- X#define NPOLFILE 20
- X
- X/*#define DONT_HAVE_POLL /* uncomment this out if you don't have poll.h */
- X
- X#ifdef DONT_HAVE_POLL
- X/*
- X * Structure of file descriptor/event pairs supplied in
- X * the poll arrays.
- X */
- Xstruct pollfd {
- X int fd; /* file desc to poll */
- X short events; /* events of interest on fd */
- X short revents; /* events that occurred on fd */
- X};
- X#endif
- X
- SHAR_EOF
- $TOUCH -am 0827163292 pol.h &&
- chmod 0644 pol.h ||
- echo "restore of pol.h failed"
- set `wc -c pol.h`;Wc_c=$1
- if test "$Wc_c" != "521"; then
- echo original size 521, current size $Wc_c
- fi
- # ============= pol.lib.c ==============
- echo "x - extracting pol.lib.c (Text)"
- sed 's/^X//' << 'SHAR_EOF' > pol.lib.c &&
- X/*
- X pol.c - poll/select() C interface
- X
- X Bruce Momjian (root%candle.uucp@bts.com
- X
- X*/
- X
- X/* tabs = 4 */
- X/* Include files */
- X#include <fcntl.h>
- X
- X#include <sys/pol.h>
- X#include <sys/selec.h>
- X#include <poll.h>
- X
- Xstatic int pol_fd = -1;
- X
- X
- X/*---------------------------------------------------------------------------
- X**
- X** pol()
- X**
- X**--------------------------------------------------------------------------*/
- Xpol(fds, nfds, timeout)
- Xstruct pollfd *fds;
- Xunsigned long nfds;
- Xint timeout;
- X{
- X struct polfd pl;
- X int i, ret;
- X
- X if ( pol_fd == -1 && (pol_fd=open("/dev/pol",O_RDONLY)) == -1)
- X return -1;
- X
- X pl.rfds = pl.wfds = 0;
- X pl.timeout = timeout;
- X
- X for (i = 0; i < nfds; i++)
- X {
- X if ((fds[i].events & POLLIN) != 0)
- X pl.rfds |= (1 << fds[i].fd);
- X if ((fds[i].events & POLLOUT) != 0)
- X pl.wfds |= (1 << fds[i].fd);
- X }
- X
- X if ((ret=ioctl(pol_fd, POL_FDS, &pl)) == -1)
- X return -1;
- X
- X if (ret == 0 || ret == -1)
- X pl.rfds = pl.wfds = 0;
- X
- X for (i=0; i < nfds; i++)
- X {
- X fds[i].revents = 0;
- X if ( (pl.rfds & (1 << fds[i].fd)) != 0)
- X fds[i].revents |= POLLIN;
- X if ( (pl.wfds & (1 << fds[i].fd)) != 0)
- X fds[i].revents |= POLLOUT;
- X }
- X return ret;
- X}
- X
- X/*---------------------------------------------------------------------------
- X**
- X** selec()
- X**
- X**--------------------------------------------------------------------------*/
- Xselec(nfds, rfds, wfds, expfds, timeout)
- Xunsigned long nfds, *rfds, *wfds, *expfds; /* exceptions not implemented */
- Xstruct timeval *timeout;
- X{
- X struct polfd pl;
- X int i, ret;
- X
- X if ( pol_fd == -1 && (pol_fd=open("/dev/pol",O_RDONLY)) == -1)
- X return -1;
- X
- X pl.rfds = pl.wfds = 0;
- X if (timeout == 0L)
- X pl.timeout = -1;
- X else
- X pl.timeout = timeout->tv_sec * 1000 + timeout->tv_usec / 1000;
- X
- X for (i = 0; i < nfds; i++)
- X {
- X if ( (*rfds & (1 << i)) != 0)
- X pl.rfds |= 1 << i;
- X if ( (*wfds & (1 << i)) != 0)
- X pl.wfds |= 1 << i;
- X }
- X
- X if ((ret=ioctl(pol_fd, POL_FDS, &pl)) == -1)
- X return -1;
- X
- X if (ret == 0 || ret == -1)
- X pl.rfds = pl.wfds = 0;
- X *rfds = *wfds = *expfds = 0;
- X
- X for (i=0; i < nfds; i++)
- X {
- X if ( (pl.rfds & (1 << i)) != 0)
- X (*rfds) |= (1 << i);
- X if ( (pl.wfds & (1 << i)) != 0)
- X (*wfds) |= (1 << i);
- X }
- X return ret;
- X}
- X
- X/*---------------------------------------------------------------------------
- X**
- X** polclose()
- X**
- X**--------------------------------------------------------------------------*/
- Xpolclose()
- X{
- X if (pol_fd != -1)
- X return close(pol_fd);
- X return 0;
- X}
- X/*---------------------------------------------------------------------------
- X**
- X** selecclose()
- X**
- X**--------------------------------------------------------------------------*/
- Xselecclose()
- X{
- X return polclose();
- X}
- SHAR_EOF
- $TOUCH -am 0827154392 pol.lib.c &&
- chmod 0644 pol.lib.c ||
- echo "restore of pol.lib.c failed"
- set `wc -c pol.lib.c`;Wc_c=$1
- if test "$Wc_c" != "2624"; then
- echo original size 2624, current size $Wc_c
- fi
- # ============= poltest.c ==============
- echo "x - extracting poltest.c (Text)"
- sed 's/^X//' << 'SHAR_EOF' > poltest.c &&
- X#include <stdio.h>
- X#include <fcntl.h>
- X#include <poll.h>
- X
- Xmain()
- X{
- X int fd;
- X struct pollfd pl;
- X int x;
- X
- X
- X printf("fd = %d\n",fd);
- X pl.fd = 0;
- X pl.events = POLLIN;
- X printf("Begin ioctl, return\n");
- X if ((x=pol(&pl, 1, 5000)) == -1)
- X halt("PERROR : ioctl fail\n");
- X printf("End ioctl\n");
- X close(fd);
- X}
- SHAR_EOF
- $TOUCH -am 0827160292 poltest.c &&
- chmod 0644 poltest.c ||
- echo "restore of poltest.c failed"
- set `wc -c poltest.c`;Wc_c=$1
- if test "$Wc_c" != "305"; then
- echo original size 305, current size $Wc_c
- fi
- # ============= selec.h ==============
- echo "x - extracting selec.h (Text)"
- sed 's/^X//' << 'SHAR_EOF' > selec.h &&
- X/* sys/selec.h */
- Xstruct timeval {
- X long tv_usec, tv_sec;
- X};
- SHAR_EOF
- $TOUCH -am 0827152292 selec.h &&
- chmod 0644 selec.h ||
- echo "restore of selec.h failed"
- set `wc -c selec.h`;Wc_c=$1
- if test "$Wc_c" != "62"; then
- echo original size 62, current size $Wc_c
- fi
- exit 0
-
-
-
-
-
- --
- Bruce Momjian | 830 Blythe Avenue
- root%candle.uucp@bts.com | Drexel Hill, Pennsylvania 19026
- | (215) 353-9879(w) 853-3000(h)
-
- --
- Bruce Momjian | 830 Blythe Avenue
- root%candle.uucp@bts.com | Drexel Hill, Pennsylvania 19026
- | (215) 353-9879(w) 853-3000(h)
-