## 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
##
##----- 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.
##
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 ---
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
--- /dev/null
+/* -*-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 -------------------------------------------------*/
--- /dev/null
+/* -*-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
--- /dev/null
+/* -*-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 -------------------------------------------------*/
--- /dev/null
+/* -*-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
--- /dev/null
+/* -*-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 -------------------------------------------------*/
--- /dev/null
+/* -*-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
--- /dev/null
+/* -*-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 -------------------------------------------------*/
--- /dev/null
+/* -*-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