chiark / gitweb /
Integrated `select' handling bits from the background resolver project.
authormdw <mdw>
Fri, 14 May 1999 21:01:28 +0000 (21:01 +0000)
committermdw <mdw>
Fri, 14 May 1999 21:01:28 +0000 (21:01 +0000)
Makefile.am
conn.c [new file with mode: 0644]
conn.h [new file with mode: 0644]
lbuf.c [new file with mode: 0644]
lbuf.h [new file with mode: 0644]
sel.c [new file with mode: 0644]
sel.h [new file with mode: 0644]
selbuf.c [new file with mode: 0644]
selbuf.h [new file with mode: 0644]

index 4759694..d143566 100644 (file)
@@ -1,7 +1,7 @@
 ## Process this file with Automake to generate `Makefile.in'
 ## -*-Makefile-*-
 ##
-## $Id: Makefile.am,v 1.4 1999/05/06 19:51:35 mdw Exp $
+## $Id: Makefile.am,v 1.5 1999/05/14 21:01:28 mdw Exp $
 ##
 ## Building the distribution
 ##
@@ -30,6 +30,9 @@
 ##----- Revision history ----------------------------------------------------
 ##
 ## $Log: Makefile.am,v $
+## Revision 1.5  1999/05/14 21:01:28  mdw
+## Integrated `select' handling bits from the background resolver project.
+##
 ## Revision 1.4  1999/05/06 19:51:35  mdw
 ## Reformatted the LGPL notice a little bit.
 ##
@@ -56,7 +59,8 @@ lib_LIBRARIES = libmLib.a
 
 pkginclude_HEADERS = \
        alloc.h crc32.h dstr.h dynarray.h exc.h mdwopt.h \
-       quis.h report.h sub.h sym.h testrig.h trace.h track.h tv.h
+       quis.h report.h sub.h sym.h testrig.h trace.h track.h \
+       conn.h lbuf.h sel.h selbuf.h tv.h
 
 ## --- Things to put in the library ---
 
@@ -64,4 +68,5 @@ pkginclude_HEADERS = \
 
 libmLib_a_SOURCES = \
        alloc.c crc32.c dstr.c exc.c mdwopt.c quis.c \
-       report.c sub.c sym.c testrig.c trace.c track.c tv.c
+       report.c sub.c sym.c testrig.c trace.c track.c \
+       conn.c lbuf.c sel.c selbuf.c tv.c
diff --git a/conn.c b/conn.c
new file mode 100644 (file)
index 0000000..6ce7769
--- /dev/null
+++ b/conn.c
@@ -0,0 +1,193 @@
+/* -*-c-*-
+ *
+ * $Id: conn.c,v 1.1 1999/05/14 21:01:14 mdw Exp $
+ *
+ * Nonblocking connect handling
+ *
+ * (c) 1999 Mark Wooding
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of the mLib utilities library.
+ *
+ * mLib is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * mLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with mLib; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: conn.c,v $
+ * Revision 1.1  1999/05/14 21:01:14  mdw
+ * Integrated `select' handling bits from the background resolver project.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/types.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "conn.h"
+#include "sel.h"
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @conn_connect@ --- *
+ *
+ * Arguments:  @int fd@ = file descriptor to try to connect
+ *             @unsigned mode@ = what we can do to the file
+ *             @void *p@ = pointer to connection context
+ *
+ * Returns:    ---
+ *
+ * Use:                Handles select results for pending connections.
+ */
+
+static void conn_connect(int fd, unsigned mode, void *p)
+{
+  conn *c = p;
+  struct sockaddr_in sin;
+  int sinsz;
+
+  sinsz = sizeof(sin);
+  if (getpeername(fd, (struct sockaddr *)&sin, &sinsz) < 0) {
+    int err;
+    int errsz = sizeof(err);
+    if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &errsz) == 0)
+      errno = err;
+    c->func(-1, c->p);
+    close(fd);
+  } else
+    c->func(fd, c->p);
+  sel_rmfile(&c->writer);
+}
+
+/* --- @conn_init@ --- *
+ *
+ * Arguments:  @conn *c@ = pointer to connection block
+ *             @sel_state *s@ = pointer to select state to attach to
+ *             @unsigned long saddr@ = source IP address
+ *             @unsigned short sport@ = source port
+ *             @unsigned long daddr@ = destination IP address
+ *             @unsigned short dport@ = destination port
+ *             @void (*func)(int fd, void *p) = handler function
+ *             @void *p@ = argument for the handler function
+ *
+ * Returns:    ---
+ *
+ * Use:                Sets up a nonblocking connect job.
+ */
+
+void conn_init(conn *c, sel_state *s,
+              unsigned long saddr,
+              unsigned short sport,
+              unsigned long daddr,
+              unsigned long dport,
+              void (*func)(int /*fd*/, void */*p*/),
+              void *p)
+{
+  int fd;
+
+  /* --- Make a socket to do the connecting with --- */
+
+  c->writer.fd = -1;
+  if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
+    goto fail;
+
+  /* --- Make the socket nonblocking --- */
+
+  {
+    int f;
+
+    if ((f = fcntl(fd, F_GETFL)) < 0 ||
+       fcntl(fd, F_SETFL, f | O_NONBLOCK))
+      goto fail_close;
+  }
+
+  /* --- Set up the source address and bind it to the socket --- */
+
+  {
+    struct sockaddr_in sin;
+
+    memset(&sin, 0, sizeof(sin));
+    sin.sin_family = AF_INET;
+    sin.sin_addr.s_addr = saddr;
+    sin.sin_port = sport;
+    if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
+      goto fail_close;
+  }
+
+  /* --- Finally, set up the destination and try the connect --- */
+
+  {
+    struct sockaddr_in sin;
+
+    memset(&sin, 0, sizeof(sin));
+    sin.sin_family = AF_INET;
+    sin.sin_addr.s_addr = daddr;
+    sin.sin_port = dport;
+    if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
+      if (errno != EINPROGRESS)
+       goto fail_close;
+      c->func = func;
+      c->p = p;
+      sel_initfile(s, &c->writer, fd, SEL_WRITE, conn_connect, c);
+      sel_addfile(&c->writer);
+    } else
+      func(fd, p);
+  }
+
+  /* --- Everything is set up now --- */
+
+  return;
+
+  /* --- Something went pear-shaped --- */
+
+fail_close:
+  close(fd);
+fail:
+  func(-1, p);
+}
+
+/* --- @conn_kill@ --- *
+ *
+ * Arguments:  @conn *c@ = pointer to connection to dispose of
+ *
+ * Returns:    ---
+ *
+ * Use:                Disposes of a connection when it's not wanted any more.
+ */
+
+void conn_kill(conn *c)
+{
+  if (c->writer.fd != -1) {
+    close(c->writer.fd);
+    sel_rmfile(&c->writer);
+    c->writer.fd = -1;
+  }
+}
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/conn.h b/conn.h
new file mode 100644 (file)
index 0000000..7362b65
--- /dev/null
+++ b/conn.h
@@ -0,0 +1,111 @@
+/* -*-c-*-
+ *
+ * $Id: conn.h,v 1.1 1999/05/14 21:01:14 mdw Exp $
+ *
+ * Nonblocking connect handling
+ *
+ * (c) 1999 Mark Wooding
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of the mLib utilities library.
+ *
+ * mLib is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * mLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with mLib; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: conn.h,v $
+ * Revision 1.1  1999/05/14 21:01:14  mdw
+ * Integrated `select' handling bits from the background resolver project.
+ *
+ */
+
+#ifndef CONN_H
+#define CONN_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#ifndef SEL_H
+#  include "sel.h"
+#endif
+
+/*----- Data structures ---------------------------------------------------*/
+
+/* --- The nonblocking connect structure --- */
+
+typedef struct conn {
+  sel_file writer;                     /* Select listener */
+  void (*func)(int /*fd*/, void */*p*/); /* Handler function */
+  void *p;                             /* Argument for handler function */
+} conn;
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @conn_init@ --- *
+ *
+ * Arguments:  @conn *c@ = pointer to connection block
+ *             @sel_state *s@ = pointer to select state to attach to
+ *             @unsigned long saddr@ = source IP address
+ *             @unsigned short sport@ = source port
+ *             @unsigned long daddr@ = destination IP address
+ *             @unsigned short dport@ = destination port
+ *             @void (*func)(int fd, void *p) = handler function
+ *             @void *p@ = argument for the handler function
+ *
+ * Returns:    ---
+ *
+ * Use:                Sets up a nonblocking connect job.  The source address and
+ *             port can be zero if you don't care.  When the connection
+ *             completes, the handler function is called with the connected
+ *             socket as an argument.  If the connect fails rather than
+ *             completes, the handler is informed of this by being passed a
+ *             negative file descriptor.  In either case, the select job is
+ *             then removed.
+ */
+
+extern void conn_init(conn */*c*/, sel_state */*s*/,
+                     unsigned long /*saddr*/,
+                     unsigned short /*sport*/,
+                     unsigned long /*daddr*/,
+                     unsigned long /*dport*/,
+                     void (*/*func*/)(int /*fd*/, void */*p*/),
+                     void */*p*/);
+
+/* --- @conn_kill@ --- *
+ *
+ * Arguments:  @conn *c@ = pointer to connection to dispose of
+ *
+ * Returns:    ---
+ *
+ * Use:                Disposes of a connection when it's not wanted any more.  The
+ *             connect handler function is not called.
+ */
+
+extern void conn_kill(conn */*c*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/lbuf.c b/lbuf.c
new file mode 100644 (file)
index 0000000..b81136f
--- /dev/null
+++ b/lbuf.c
@@ -0,0 +1,269 @@
+/* -*-c-*-
+ *
+ * $Id: lbuf.c,v 1.1 1999/05/14 21:01:14 mdw Exp $
+ *
+ * Block-to-line buffering
+ *
+ * (c) 1999 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of the mLib utilities library.
+ *
+ * mLib is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * mLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with mLib; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: lbuf.c,v $
+ * Revision 1.1  1999/05/14 21:01:14  mdw
+ * Integrated `select' handling bits from the background resolver project.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lbuf.h"
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @lbuf_flush@ --- *
+ *
+ * Arguments:  @lbuf *b@ = pointer to buffer block
+ *             @char *p@ = pointer to where to start searching
+ *             @size_t len@ = length of new material added
+ *
+ * Returns:    ---
+ *
+ * Use:                Flushes any complete lines in a line buffer.  New material
+ *             is assumed to have been added starting at @p@.  If @p@ is
+ *             null, then the scan starts at the beginning of the buffer,
+ *             and the size of data already in the buffer is used in place
+ *             of @len@.
+ *
+ *             It is assumed that the buffer is initially enabled.  You
+ *             shouldn't be contributing data to a disabled buffer anyway.
+ *             However, the buffer handler may at some point disable itself,
+ *             and @lbuf_flush@ can cope with this eventuality.  Any pending
+ *             data is left at the start of the buffer and can be flushed
+ *             out by calling @lbuf_flush(b, 0, 0)@ if the buffer is ever
+ *             re-enabled.
+ */
+
+void lbuf_flush(lbuf *b, char *p, size_t len)
+{
+  char *l;                             /* Limit of data in buffer */
+  char *q;                             /* Roving pointer through string */
+  char *base;                          /* Base address of current line */
+  int cr;                              /* Carriage return state */
+
+  /* --- Initialize variables as necessary --- */
+
+  if (!p) {
+    p = b->buf;
+    cr = 0;
+    len = b->len;
+  } else
+    cr = b->f & lbuf_cr;
+
+  l = p + len;
+
+  /* --- Clear @base@ if I'm discarding an overlong line --- */
+
+  if (b->len == sizeof(b->buf))
+    base = 0;
+  else
+    base = b->buf;
+
+  /* --- Now I march through the string --- */
+
+  for (q = p; q < l; q++) {
+
+    /* --- Quickly discard uninteresting characters --- */
+
+    if (*q != '\r' && *q != '\n') {
+      cr = 0;
+      continue;
+    }
+    if (*q == '\r') {
+      cr = 1;
+      continue;
+    }
+
+    /* --- Two choices here --- *
+     *
+     * I can either be strict about CRLF line ends, or I can be shoddy
+     * and allow bare LFs.  I'll do the latter, although I oughtn't,
+     * because it makes testing interactively and with Unix text files
+     * easier.
+     */
+
+#ifdef STRICT_CRLF
+    if (!cr)
+      continue;
+#endif 
+
+    /* --- I have a positive ID on a linefeed --- *
+     *
+     * If I'm interested in this string, report it to my owner.
+     */
+
+    if (base) {
+      if (cr)
+       q[-1] = 0;                      /* Exercise: why is this safe? */
+      else
+       *q = 0;
+      b->func(base, b->p);
+      if (!(b->f & lbuf_enable)) {
+       base = q + 1;
+       break;
+      }
+    }
+    base = q + 1;
+    cr = 0;
+  }
+
+  /* --- Sift through the aftermath --- */
+
+  if (base) {
+    size_t len = l - base;
+    if (len == sizeof(b->buf)) {
+      b->buf[len - 1] = 0;
+      b->func(base, b->p);
+    } else if (base != b->buf)
+      memmove(b->buf, base, len);
+    b->len = len;
+    if (cr)
+      b->f |= lbuf_cr;
+    else
+      b->f &= ~lbuf_cr;
+  }
+}
+
+/* --- @lbuf_close@ --- *
+ *
+ * Arguments:  @lbuf *b@ = pointer to buffer block
+ *
+ * Returns:    ---
+ *
+ * Use:                Empties the buffer of any data currently lurking in it, and
+ *             informs the client that this has happened.  It's assumed that
+ *             the buffer is enabled: you shouldn't be reading close events
+ *             on disabled buffers.
+ */
+
+void lbuf_close(lbuf *b)
+{
+  if (b->len && b->len != sizeof(b->buf)) {
+    b->buf[b->len] = 0;
+    b->func(b->buf, b->p);
+  }
+  if (b->f & lbuf_enable)
+    b->func(0, b->p);
+}
+
+/* --- @lbuf_free@ --- *
+ *
+ * Arguments:  @lbuf *b@ = pointer to buffer block
+ *             @char **p@ = output pointer to free space
+ *
+ * Returns:    Free buffer size.
+ *
+ * Use:                Returns the free portion of a line buffer.  Data can then be
+ *             written to this portion, and split out into lines by calling
+ *             @lbuf_flush@.
+ */
+
+size_t lbuf_free(lbuf *b, char **p)
+{
+  /* --- There's a special case to consider --- *
+   *
+   * If a line from the file wouldn't fit in the buffer, I truncate it and
+   * return what would fit.  The rest of the line ought to be discarded.
+   * This condition is signalled by @len = sizeof(buf)@, and means that the
+   * entire buffer is OK to be trashed.  In other cases, @len@ is the amount
+   * of space currently occupied in the buffer.  This special case is the
+   * reason this routine exists.
+   */
+
+  if (b->len == 0 || b->len == sizeof(b->buf)) {
+    *p = b->buf + b->len;
+    return (sizeof(b->buf) - b->len);
+  } else {
+    *p = b->buf;
+    return (sizeof(b->buf));
+  }
+}
+
+/* --- @lbuf_snarf@ --- *
+ *
+ * Arguments:  @lbuf *b@ = pointer to buffer block
+ *             @const void *p@ = pointer to input data buffer
+ *             @size_t sz@ = size of data in input buffer
+ *
+ * Returns:    ---
+ *
+ * Use:                Snarfs the data from the input buffer and spits it out as
+ *             lines.  This interface ignores the complexities of dealing
+ *             with disablement: you should be using @lbuf_free@ to
+ *             contribute data if you want to cope with that.
+ */
+
+void lbuf_snarf(lbuf *b, const void *p, size_t sz)
+{
+  const char *pp = p;
+  while (sz) {
+    size_t bsz;
+    char *bp;
+
+    bsz = lbuf_free(b, &bp);
+    if (bsz > sz)
+      bsz = sz;
+    memcpy(bp, pp, bsz);
+    lbuf_flush(b, bp, bsz);
+    pp += bsz;
+    sz -= bsz;
+  }
+}
+
+/* --- @lbuf_init@ --- *
+ *
+ * Arguments:  @lbuf *b@ = pointer to buffer block
+ *             @void (*func)(char *s, void *p)@ = handler function
+ *             @void *p@ = argument pointer for @func@
+ *
+ * Returns:    ---
+ *
+ * Use:                Initializes a line buffer block.  Any recognized lines are
+ *             passed to @func@ for processing.
+ */
+
+void lbuf_init(lbuf *b,
+              void (*func)(char */*s*/, void */*p*/),
+              void *p)
+{
+  b->func = func;
+  b->p = p;
+  b->len = 0;
+  b->f = lbuf_enable;
+}
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/lbuf.h b/lbuf.h
new file mode 100644 (file)
index 0000000..7772885
--- /dev/null
+++ b/lbuf.h
@@ -0,0 +1,205 @@
+/* -*-c-*-
+ *
+ * $Id: lbuf.h,v 1.1 1999/05/14 21:01:14 mdw Exp $
+ *
+ * Block-to-line buffering
+ *
+ * (c) 1999 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of the mLib utilities library.
+ *
+ * mLib is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * mLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with mLib; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: lbuf.h,v $
+ * Revision 1.1  1999/05/14 21:01:14  mdw
+ * Integrated `select' handling bits from the background resolver project.
+ *
+ */
+
+#ifndef LBUF_H
+#define LBUF_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Line buffering ----------------------------------------------------*
+ *
+ * The line buffer accepts as input arbitrary-sized lumps of data and
+ * converts them, by passing them to a client-supplied function, into a
+ * sequence of lines.  It's particularly useful when performing multiplexed
+ * network I/O.  It's not normally acceptable to block while waiting for the
+ * rest of a text line to arrive, for example.  The line buffer stores the
+ * start of the line until the rest of it arrives later.
+ *
+ * A line is a piece of text terminated by either a linefeed or a carriage-
+ * return/linefeed pair.  (The former is there to cope with Unix; the latter
+ * copes with Internet-format line ends.)
+ *
+ * There's a limit to the size of lines that the buffer can cope with.  It's
+ * not hard to remove this limit, but it's probably a bad idea in a lot of
+ * cases, because it'd allow a remote user to gobble arbitrary amounts of
+ * your memory.  If a line exceeds the limit, it is truncated: the initial
+ * portion of the line is processed normally, and the remaining portion is
+ * simply discarded.
+ *
+ * Lines extracted from the input data are passed, one at a time, to a
+ * `handler function', along with a caller-supplied pointer argument to
+ * provide the handler with some context.  The line read is null-terminated
+ * and does not include the trailing newline characters.  It is legal for a
+ * handler function to modify the string it is passed.  However, writing
+ * beyond the terminating null byte is not allowed.  An end-of-file condition
+ * is signalled to the handler by passing it a null pointer rather than the
+ * address of a string.
+ *
+ * A complexity arises because of the concept of a `disabled' buffer.
+ * Disablement is really a higher-level concept, but it turns out to be
+ * important to implement it here.  It's useful for a line handler function
+ * to `disable' itself, so that it doesn't get called any more.  For example,
+ * this might happen if it encouters an error, or when it finishes reading
+ * everything it wanted to read.  The line buffer needs to be `in the loop'
+ * so that it stops attempting to flush any further lines stored in its
+ * buffer towards a handler function which isn't ready to accept them.
+ * Buffers are initially enabled, although higher- level buffering systems
+ * might well disable them immediately for their own purposes.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <stddef.h>
+
+/*----- Data structures ---------------------------------------------------*/
+
+/* --- The buffer structure --- *
+ *
+ * The only thing that's safe to fiddle with in here is the @lbuf_enable@
+ * flag.  Only higher-level buffering systems should be playing with even
+ * that.
+ */
+
+typedef struct lbuf {
+  void (*func)(char */*s*/, void */*p*/); /* Handler function */
+  void *p;                             /* Argument for handler */
+  size_t len;                          /* Length of data in buffer */
+  unsigned f;                          /* Various useful state flags */
+  char buf[256];                       /* The actual buffer */
+} lbuf;
+
+enum {
+  lbuf_cr = 1,                         /* Read a carriage return */
+  lbuf_enable = 2                      /* Buffer is currently enabled */
+};
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @lbuf_flush@ --- *
+ *
+ * Arguments:  @lbuf *b@ = pointer to buffer block
+ *             @char *p@ = pointer to where to start searching
+ *             @size_t len@ = length of new material added
+ *
+ * Returns:    ---
+ *
+ * Use:                Flushes any complete lines in a line buffer.  New material
+ *             is assumed to have been added starting at @p@.  If @p@ is
+ *             null, then the scan starts at the beginning of the buffer,
+ *             and the size of data already in the buffer is used in place
+ *             of @len@.
+ *
+ *             It is assumed that the buffer is initially enabled.  You
+ *             shouldn't be contributing data to a disabled buffer anyway.
+ *             However, the buffer handler may at some point disable itself,
+ *             and @lbuf_flush@ can cope with this eventuality.  Any pending
+ *             data is left at the start of the buffer and can be flushed
+ *             out by calling @lbuf_flush(b, 0, 0)@ if the buffer is ever
+ *             re-enabled.
+ */
+
+extern void lbuf_flush(lbuf */*b*/, char */*p*/, size_t /*len*/);
+
+/* --- @lbuf_close@ --- *
+ *
+ * Arguments:  @lbuf *b@ = pointer to buffer block
+ *
+ * Returns:    ---
+ *
+ * Use:                Empties the buffer of any data currently lurking in it, and
+ *             informs the client that this has happened.  It's assumed that
+ *             the buffer is enabled: you shouldn't be reading close events
+ *             on disabled buffers.
+ */
+
+extern void lbuf_close(lbuf */*b*/);
+
+/* --- @lbuf_free@ --- *
+ *
+ * Arguments:  @lbuf *b@ = pointer to buffer block
+ *             @char **p@ = output pointer to free space
+ *
+ * Returns:    Free buffer size.
+ *
+ * Use:                Returns the free portion of a line buffer.  Data can then be
+ *             written to this portion, and split out into lines by calling
+ *             @lbuf_flush@.
+ */
+
+extern size_t lbuf_free(lbuf */*b*/, char **/*p*/);
+
+/* --- @lbuf_snarf@ --- *
+ *
+ * Arguments:  @lbuf *b@ = pointer to buffer block
+ *             @const void *p@ = pointer to input data buffer
+ *             @size_t sz@ = size of data in input buffer
+ *
+ * Returns:    ---
+ *
+ * Use:                Snarfs the data from the input buffer and spits it out as
+ *             lines.  This interface ignores the complexities of dealing
+ *             with disablement: you should be using @lbuf_free@ to
+ *             contribute data if you want to cope with that.
+ */
+
+extern void lbuf_snarf(lbuf */*b*/, const void */*p*/, size_t /*sz*/);
+
+/* --- @lbuf_init@ --- *
+ *
+ * Arguments:  @lbuf *b@ = pointer to buffer block
+ *             @void (*func)(char *s, void *p)@ = handler function
+ *             @void *p@ = argument pointer for @func@
+ *
+ * Returns:    ---
+ *
+ * Use:                Initializes a line buffer block.  Any recognized lines are
+ *             passed to @func@ for processing.
+ */
+
+extern void lbuf_init(lbuf */*b*/,
+                     void (*/*func*/)(char */*s*/, void */*p*/),
+                     void */*p*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/sel.c b/sel.c
new file mode 100644 (file)
index 0000000..3f5493b
--- /dev/null
+++ b/sel.c
@@ -0,0 +1,250 @@
+/* -*-c-*-
+ *
+ * $Id: sel.c,v 1.1 1999/05/14 21:01:14 mdw Exp $
+ *
+ * I/O multiplexing support
+ *
+ * (c) 1999 Mark Wooding
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of the mLib utilities library.
+ *
+ * mLib is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * mLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with mLib; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: sel.c,v $
+ * Revision 1.1  1999/05/14 21:01:14  mdw
+ * Integrated `select' handling bits from the background resolver project.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#include "sel.h"
+#include "tv.h"
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @sel_init@ --- *
+ *
+ * Arguments:  @sel_state *s@ = pointer to a state block to initialize
+ *
+ * Returns:    ---
+ *
+ * Use:                Initializes a select state block.
+ */
+
+void sel_init(sel_state *s)
+{
+  s->files = 0;
+  s->timers = 0;
+  FD_ZERO(&s->fd[SEL_READ]);
+  FD_ZERO(&s->fd[SEL_WRITE]);
+  FD_ZERO(&s->fd[SEL_EXC]);
+}
+
+/* --- @sel_initfile@ --- *
+ *
+ * Arguments:  @sel_state *s@ = select state to attach to
+ *             @sel_file *f@ = pointer to a file block to initialize
+ *             @int fd@ = the file descriptor to listen to
+ *             @unsigned mode@ = what to listen for
+ *             @void (*func)(int fd, unsigned mode, void *p)@ = handler
+ *             @void *p@ = argument to pass to handler
+ *
+ * Returns:    ---
+ *
+ * Use:                Initializes a file block ready for use.  The file block
+ *             isn't added to the list of things to do until a call to
+ *             @sel_addfile@.
+ */
+
+void sel_initfile(sel_state *s, sel_file *f,
+                 int fd, unsigned mode,
+                 void (*func)(int /*fd*/, unsigned /*mode*/, void */*p*/),
+                 void *p)
+{
+  f->s = s;
+  f->fd = fd;
+  f->mode = mode;
+  f->func = func;
+  f->p = p;
+}
+
+/* --- @sel_addfile@ --- *
+ *
+ * Arguments:  @sel_file *f@ = pointer to a file block
+ *
+ * Returns:    ---
+ *
+ * Use:                Adds a file block into the list of things to listen to.
+ */
+
+void sel_addfile(sel_file *f)
+{
+  sel_file **ff = &f->s->files;
+
+  /* --- This little dance looks like line-noise, but it does the job --- */
+
+  while (*ff && (*ff)->fd > f->fd)
+    ff = &(*ff)->next;
+  f->next = *ff;
+  f->prev = (sel_file *)ff;
+  if (*ff)
+    (*ff)->prev = f;
+  *ff = f;
+  FD_SET(f->fd, f->s->fd + f->mode);
+}
+
+/* --- @sel_rmfile@ --- *
+ *
+ * Arguments:  @sel_file *f@ = pointer to a file block
+ *
+ * Returns:    ---
+ *
+ * Use:                Removes a file block from the list of things to listen to.
+ */
+
+void sel_rmfile(sel_file *f)
+{
+  f->prev->next = f->next;
+  if (f->next)
+    f->next->prev = f->prev;
+  FD_CLR(f->fd, f->s->fd + f->mode);
+}
+
+/* --- @sel_addtimer@ --- *
+ *
+ * Arguments:  @sel_state *s@ = pointer to a state block
+ *             @sel_timer *t@ = pointer to a timer block
+ *             @struct timeval *tv@ = pointer to time to activate
+ *             @void (*func)(struct timeval *tv, void *p)@ = handler
+ *             @void *p@ = argument for handler function
+ *
+ * Returns:    ---
+ *
+ * Use:                Registers and sets up a timer.
+ */
+
+void sel_addtimer(sel_state *s, sel_timer *t,
+                 struct timeval *tv,
+                 void (*func)(struct timeval */*tv*/, void */*p*/),
+                 void *p)
+{
+  sel_timer **tt = &s->timers;
+
+  /* --- Set up the timer block --- */
+
+  t->tv = *tv;
+  t->func = func;
+  t->p = p;
+
+  /* --- More line noise --- */
+  
+  while (*tt && tv_cmp(&(*tt)->tv, tv) > 0)
+    tt = &(*tt)->next;
+  t->next = *tt;
+  t->prev = (sel_timer *)tt;
+  if (*tt)
+    (*tt)->prev = t;
+  *tt = t;
+}
+
+/* --- @sel_rmtimer@ --- *
+ *
+ * Arguments:  @sel_timer *t@ = pointer to timer block
+ *
+ * Returns:    ---
+ *
+ * Use:                Removes a timer from the list of timers.
+ */
+
+void sel_rmtimer(sel_timer *t)
+{
+  t->prev->next = t->next;
+  if (t->next)
+    t->next->prev = t->prev;
+}
+
+/* --- @sel_select@ --- *
+ *
+ * Arguments:  @sel_state *s@ = pointer to state block
+ *
+ * Returns:    Zero if all OK, -1 on error.
+ *
+ * Use:                Does a @select@ call (or equivalent @poll@).
+ */
+
+int sel_select(sel_state *s)
+{
+  fd_set fd[SEL_MODES];
+  struct timeval tv;
+  int err;
+
+  memcpy(fd, s->fd, sizeof(s->fd));
+  if (s->timers) {
+    struct timeval now;
+    gettimeofday(&now, 0);
+    tv_sub(&tv, &now, &s->timers->tv);
+    err = select(s->files ? s->files->fd + 1 : 0,
+                fd + SEL_READ, fd + SEL_WRITE, fd + SEL_EXC,
+                &tv);
+    gettimeofday(&tv, 0);
+  } else
+    err = select(s->files ? s->files->fd + 1 : 0,
+                fd + SEL_READ, fd + SEL_WRITE, fd + SEL_EXC,
+                0);
+
+  if (err < 0)
+    return (err);
+
+  {
+    sel_timer *t, *tt;
+    for (t = s->timers; t && tv_cmp(&t->tv, &tv) <= 0; t = tt) {
+      tt = t->next;
+      t->next = t->prev = t;
+      t->func(&tv, t->p);
+    }
+    s->timers = t;
+    if (t)
+      t->prev = (sel_timer *)&s->timers;
+  }
+
+  {
+    sel_file *f, *ff;
+    for (f = s->files; f; f = ff) {
+      ff = f->next;
+      if (FD_ISSET(f->fd, fd + f->mode))
+       f->func(f->fd, f->mode, f->p);
+    }
+  }
+
+  return (0);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/sel.h b/sel.h
new file mode 100644 (file)
index 0000000..3777eed
--- /dev/null
+++ b/sel.h
@@ -0,0 +1,234 @@
+/* -*-c-*-
+ *
+ * $Id: sel.h,v 1.1 1999/05/14 21:01:15 mdw Exp $
+ *
+ * I/O multiplexing support
+ *
+ * (c) 1999 Mark Wooding
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of the mLib utilities library.
+ *
+ * mLib is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * mLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with mLib; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: sel.h,v $
+ * Revision 1.1  1999/05/14 21:01:15  mdw
+ * Integrated `select' handling bits from the background resolver project.
+ *
+ */
+
+#ifndef SEL_H
+#define SEL_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Theory lesson -----------------------------------------------------*
+ *
+ * Things which are expecting to do I/O or go off at a certain time are
+ * called `selectors'.  There are two types of selectors: `file selectors'
+ * wait patiently for a file to become readable or writable; `timeout
+ * selectors' wait for a certain amount of time to elapse.  There is also a
+ * `multiplexor' which copes with managing all of this stuff.
+ *
+ * Multiplexors aren't actually very interesting.  You initialize them with
+ * @sel_init@, and then add and remove selectors as you go.  When you want to
+ * wait for something to happen, call @sel_select@.
+ *
+ * A file selector can *either* read *or* write.  It can't do both.  This is
+ * because you quite often want to read a socket but not write it; during
+ * those times you don't want to write, you just don't install a write
+ * selector.
+ *
+ * File selectors are called when their files are available for reading or
+ * writing as appropriate, and given their file descriptor, the state of the
+ * file, and a pointer that was registered along with the selector.
+ *
+ * File selectors are set up in two phases.  First, they're `initialized'
+ * with @sel_initfile@.  An initialized file selector doesn't do anything.
+ * It needs to be added to a multiplexor using `sel_addfile'.  It can later
+ * be removed using `sel_rmfile'.  You can carry on adding and removing as
+ * you wish.  Just don't try adding it twice in a row.
+ *
+ * Timeout selectors are called at a certain time.  (Actually, they're called
+ * *after* a certain time.)  There's no separate initialization step with
+ * timouts: you just add them and they work.  If you decide you don't want a
+ * timeout to go off, then you just remove it.  (Adding and removing the
+ * *same* timeout isn't something you do very often.  You usually use a
+ * different expiry time each time.)
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+/*----- Data structures ---------------------------------------------------*/
+
+/* --- Listening for a file --- */
+
+typedef struct sel_file {
+  struct sel_file *next;
+  struct sel_file *prev;
+  struct sel_state *s;
+  int fd;
+  unsigned mode;
+  void (*func)(int /*fd*/, unsigned /*mode*/, void */*p*/);
+  void *p;
+} sel_file;
+
+#define SEL_READ 0
+#define SEL_WRITE 1
+#define SEL_EXC 2
+#define SEL_MODES 3
+
+/* --- Waiting for a timeout --- */
+
+typedef struct sel_timer {
+  struct sel_timer *next;
+  struct sel_timer *prev;
+  struct timeval tv;
+  void (*func)(struct timeval *tv, void *p);
+  void *p;
+} sel_timer;
+
+/* --- A multiplexer --- *
+ *
+ * The files are sorted in reverse order of file descriptor number; the
+ * timers are in normal order of occurrence.  Thus, the interesting one
+ * is always at the front of the list.
+ */
+
+typedef struct sel_state {
+  struct sel_file *files;
+  struct sel_timer *timers;
+  fd_set fd[SEL_MODES];
+  struct timeval tv;
+} sel_state;
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @sel_init@ --- *
+ *
+ * Arguments:  @sel_state *s@ = pointer to a state block to initialize
+ *
+ * Returns:    ---
+ *
+ * Use:                Initializes a select state block.
+ */
+
+extern void sel_init(sel_state */*s*/);
+
+/* --- @sel_initfile@ --- *
+ *
+ * Arguments:  @sel_state *s@ = select state to attach to
+ *             @sel_file *f@ = pointer to a file block to initialize
+ *             @int fd@ = the file descriptor to listen to
+ *             @unsigned mode@ = what to listen for
+ *             @void (*func)(int fd, unsigned mode, void *p)@ = handler
+ *             @void *p@ = argument to pass to handler
+ *
+ * Returns:    ---
+ *
+ * Use:                Initializes a file block ready for use.  The file block
+ *             isn't added to the list of things to do until a call to
+ *             @sel_addfile@.
+ */
+
+extern void sel_initfile(sel_state */*s*/, sel_file */*f*/,
+                        int /*fd*/, unsigned /*mode*/,
+                        void (*/*func*/)(int /*fd*/,
+                                         unsigned /*mode*/,
+                                         void */*p*/),
+                        void */*p*/);
+
+/* --- @sel_addfile@ --- *
+ *
+ * Arguments:  @sel_file *f@ = pointer to a file block
+ *
+ * Returns:    ---
+ *
+ * Use:                Adds a file block into the list of things to listen to.
+ */
+
+extern void sel_addfile(sel_file */*f*/);
+
+/* --- @sel_rmfile@ --- *
+ *
+ * Arguments:  @sel_file *f@ = pointer to a file block
+ *
+ * Returns:    ---
+ *
+ * Use:                Removes a file block from the list of things to listen to.
+ */
+
+extern void sel_rmfile(sel_file */*f*/);
+
+/* --- @sel_addtimer@ --- *
+ *
+ * Arguments:  @sel_state *s@ = pointer to a state block
+ *             @sel_timer *t@ = pointer to a timer block
+ *             @struct timeval *tv@ = pointer to time to activate
+ *             @void (*func)(struct timeval *tv, void *p)@ = handler
+ *             @void *p@ = argument for handler function
+ *
+ * Returns:    ---
+ *
+ * Use:                Registers and sets up a timer.
+ */
+
+extern void sel_addtimer(sel_state */*s*/, sel_timer */*t*/,
+                        struct timeval */*tv*/,
+                        void (*/*func*/)(struct timeval */*tv*/,
+                                         void */*p*/),
+                        void */*p*/);
+
+/* --- @sel_rmtimer@ --- *
+ *
+ * Arguments:  @sel_timer *t@ = pointer to timer block
+ *
+ * Returns:    ---
+ *
+ * Use:                Removes a timer from the list of timers.
+ */
+
+extern void sel_rmtimer(sel_timer */*t*/);
+
+/* --- @sel_select@ --- *
+ *
+ * Arguments:  @sel_state *s@ = pointer to state block
+ *
+ * Returns:    Zero if all OK, -1 on error.
+ *
+ * Use:                Does a @select@ call (or equivalent @poll@).
+ */
+
+extern int sel_select(sel_state */*s*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/selbuf.c b/selbuf.c
new file mode 100644 (file)
index 0000000..0f07dfc
--- /dev/null
+++ b/selbuf.c
@@ -0,0 +1,155 @@
+/* -*-c-*-
+ *
+ * $Id: selbuf.c,v 1.1 1999/05/14 21:01:15 mdw Exp $
+ *
+ * Line-buffering select handler
+ *
+ * (c) 1999 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of the mLib utilities library.
+ *
+ * mLib is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * mLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with mLib; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: selbuf.c,v $
+ * Revision 1.1  1999/05/14 21:01:15  mdw
+ * Integrated `select' handling bits from the background resolver project.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#include "lbuf.h"
+#include "sel.h"
+#include "selbuf.h"
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @selbuf_enable@ --- *
+ *
+ * Arguments:  @selbuf *b@ = pointer to buffer block
+ *
+ * Returns:    ---
+ *
+ * Use:                Enables a buffer for reading, and emits any queued lines
+ *             to the buffer's owner.
+ */
+
+void selbuf_enable(selbuf *b)
+{
+  if (!(b->b.f & lbuf_enable)) {
+    b->b.f |= lbuf_enable;
+    sel_addfile(&b->reader);
+    lbuf_flush(&b->b, 0, 0);
+  }
+}
+
+/* --- @selbuf_disable@ --- *
+ *
+ * Arguments:  @selbuf *b@ = pointer to a buffer block
+ *
+ * Returns:    ---
+ *
+ * Use:                Disables a buffer.  It won't be read from until it's
+ *             enabled again.
+ */
+
+void selbuf_disable(selbuf *b)
+{
+  if (b->b.f & lbuf_enable) {
+    b->b.f &= ~lbuf_enable;
+    sel_rmfile(&b->reader);
+  }
+}
+
+/* --- @selbuf_read@ --- *
+ *
+ * Arguments:  @int fd@ = file descriptor to read from
+ *             @int mode@ = what we can do to the file
+ *             @void *vp@ = pointer to buffer context
+ *
+ * Returns:    ---
+ *
+ * Use:                Acts on the result of a @select@ call.
+ */
+
+static void selbuf_read(int fd, unsigned mode, void *vp)
+{
+  selbuf *b = vp;
+  char *p;
+  size_t sz;
+  int n;
+
+  sz = lbuf_free(&b->b, &p);
+again:
+  n = read(fd, p, sz);
+  if (n <= 0) {
+    switch (errno) {
+      case EINTR:
+       goto again;
+      case EAGAIN:
+#if EAGAIN != EWOULDBLOCK
+      case EWOULDBLOCK:
+#endif
+       return;
+      default:
+       lbuf_close(&b->b);
+    }
+  }
+  else
+    lbuf_flush(&b->b, p, n);
+}
+
+/* --- @selbuf_init@ --- *
+ *
+ * Arguments:  @selbuf *b@ = pointer to buffer block
+ *             @sel_state *s@ = pointer to select state to attach to
+ *             @int fd@ = file descriptor to listen to
+ *             @void (*func)(char *s, void *p)@ = function to call
+ *             @void *p@ = argument for function
+ *
+ * Returns:    ---
+ *
+ * Use:                Initializes a buffer block.
+ */
+
+void selbuf_init(selbuf *b,
+                sel_state *s,
+                int fd,
+                void (*func)(char */*s*/, void */*p*/),
+                void *p)
+{
+  lbuf_init(&b->b, func, p);
+  b->b.f &= ~lbuf_enable;
+  sel_initfile(s, &b->reader, fd, SEL_READ, selbuf_read, b);
+  selbuf_enable(b);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/selbuf.h b/selbuf.h
new file mode 100644 (file)
index 0000000..689d3f7
--- /dev/null
+++ b/selbuf.h
@@ -0,0 +1,113 @@
+/* -*-c-*-
+ *
+ * $Id: selbuf.h,v 1.1 1999/05/14 21:01:15 mdw Exp $
+ *
+ * Line-buffering select handler
+ *
+ * (c) 1999 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of the mLib utilities library.
+ *
+ * mLib is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * mLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with mLib; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: selbuf.h,v $
+ * Revision 1.1  1999/05/14 21:01:15  mdw
+ * Integrated `select' handling bits from the background resolver project.
+ *
+ */
+
+#ifndef SELBUF_H
+#define SELBUF_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#ifndef LBUF_H
+#  include "lbuf.h"
+#endif
+
+#ifndef SEL_H
+#  include "sel.h"
+#endif
+
+/*----- Data structures ---------------------------------------------------*/
+
+typedef struct selbuf {
+  sel_file reader;                     /* File selection object */
+  lbuf b;                              /* Line buffering object */
+} selbuf;
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @selbuf_enable@ --- *
+ *
+ * Arguments:  @selbuf *b@ = pointer to buffer block
+ *
+ * Returns:    ---
+ *
+ * Use:                Enables a buffer for reading, and emits any queued lines
+ *             to the buffer's owner.
+ */
+
+extern void selbuf_enable(selbuf */*b*/);
+
+/* --- @selbuf_disable@ --- *
+ *
+ * Arguments:  @selbuf *b@ = pointer to a buffer block
+ *
+ * Returns:    ---
+ *
+ * Use:                Disables a buffer.  It won't be read from until it's
+ *             enabled again.
+ */
+
+extern void selbuf_disable(selbuf */*b*/);
+
+/* --- @selbuf_init@ --- *
+ *
+ * Arguments:  @selbuf *b@ = pointer to buffer block
+ *             @sel_state *s@ = pointer to select state to attach to
+ *             @int fd@ = file descriptor to listen to
+ *             @void (*func)(char *s, void *p)@ = function to call
+ *             @void *p@ = argument for function
+ *
+ * Returns:    ---
+ *
+ * Use:                Initializes a buffer block.
+ */
+
+extern void selbuf_init(selbuf */*b*/,
+                       sel_state */*s*/,
+                       int /*fd*/,
+                       void (*/*func*/)(char */*s*/, void */*p*/),
+                       void */*p*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif