From 4ba4a43a195f509d9021563e40de5184e013b8f3 Mon Sep 17 00:00:00 2001
From: Dan Egnor <egnor@ofb.net>
Date: Thu, 12 Oct 2000 17:27:19 +0000
Subject: [PATCH] v0.6 + Ian's EXCEPT patch

---
 Makefile.am  | 13 +++++---
 adns.c       |  8 ++---
 configure.in | 11 ++++--
 glib.c       | 17 +++++++---
 oop-rl.h     | 20 +++++++++++
 oop.h        |  5 +--
 select.c     | 27 ++++++++++++---
 test-oop.c   | 94 ++++++++++++++++++++++++++++++++++++++++------------
 8 files changed, 151 insertions(+), 44 deletions(-)
 create mode 100644 oop-rl.h

diff --git a/Makefile.am b/Makefile.am
index 0a53de1..b611eac 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -7,11 +7,11 @@
 # See the file COPYING for details.
 
 AUTOMAKE_OPTIONS = foreign 1.2
-lib_LTLIBRARIES = liboop.la liboop-adns.la liboop-glib.la liboop-www.la
+lib_LTLIBRARIES = liboop.la liboop-adns.la liboop-glib.la liboop-www.la liboop-rl.la
 INCLUDES = $(GLIB_INCLUDES) $(WWW_INCLUDES)
-EXTRA_DIST = liboop.spec
 
-# versions updated as of 0.4; 0.5 and 0.6 only change build stuff.
+# versions updated as of 0.4; 0.5 only changes build stuff;
+# 0.6 only adds the readline interface.
 
 liboop_la_LDFLAGS = -version-info 2:0:0 # version:revision:age
 liboop_la_SOURCES = sys.c select.c signal.c alloc.c
@@ -25,12 +25,15 @@ liboop_glib_la_SOURCES = glib.c
 liboop_www_la_LDFLAGS = -version-info 0:0:0
 liboop_www_la_SOURCES = www.c
 
-include_HEADERS = oop.h oop-adns.h oop-glib.h oop-www.h
+liboop_rl_la_LDFLAGS = -version-info 0:0:0
+liboop_rl_la_SOURCES = readline.c
+
+include_HEADERS = oop.h oop-adns.h oop-glib.h oop-www.h oop-rl.h
 
 noinst_PROGRAMS = test-oop
 
 test_oop_SOURCES = test-oop.c
-test_oop_LDADD = $(ADNS_LIBS) $(GLIB_LIBS) $(WWW_LIBS) liboop.la
+test_oop_LDADD = $(ADNS_LIBS) $(GLIB_LIBS) $(WWW_LIBS) $(READLINE_LIBS) liboop.la
 
 release: dist
 	gzip -dc $(PACKAGE)-$(VERSION).tar.gz | bzip2 -9 \
diff --git a/adns.c b/adns.c
index 4b6abfc..f290e6c 100644
--- a/adns.c
+++ b/adns.c
@@ -97,7 +97,7 @@ static void set_select(oop_adapter_adns *a) {
 	FD_ZERO(&xfd);
 	gettimeofday(&now,NULL);
 	adns_beforeselect(a->state,&maxfd,&rfd,&wfd,&xfd,&out,&buf,&now);
-	oop_select_set(a->select,maxfd,&rfd,&wfd,out);
+	oop_select_set(a->select,maxfd,&rfd,&wfd,&xfd,out);
 }
 
 static void *on_process(oop_source *source,struct timeval when,void *data) {
@@ -133,14 +133,12 @@ static void *on_process(oop_source *source,struct timeval when,void *data) {
 
 static void *on_select(
 	oop_adapter_select *select,
-	int num,fd_set *rfd,fd_set *wfd,
+	int num,fd_set *rfd,fd_set *wfd,fd_set *xfd,
 	struct timeval now,void *data)
 {
 	oop_adapter_adns *a = (oop_adapter_adns *) data;
-	fd_set xfd;
 
-	FD_ZERO(&xfd);
-	adns_afterselect(a->state,num,rfd,wfd,&xfd,&now);
+	adns_afterselect(a->state,num,rfd,wfd,xfd,&now);
 	return on_process(a->source,OOP_TIME_NOW,a);
 }
 
diff --git a/configure.in b/configure.in
index 8aaa68c..80f751f 100644
--- a/configure.in
+++ b/configure.in
@@ -1,6 +1,6 @@
 dnl Process this file with autoconf to produce a configure script.
 AC_INIT(INSTALL)
-AM_INIT_AUTOMAKE(liboop,0.5)
+AM_INIT_AUTOMAKE(liboop,0.6)
 AC_CANONICAL_HOST
 
 dnl Use libtool for shared libraries
@@ -27,10 +27,16 @@ esac
 AC_CHECK_HEADERS(poll.h sys/select.h)
 
 AC_CHECK_LIB(adns,adns_init,[
-  AC_DEFINE(HAVE_ADNS)
   ADNS_LIBS="liboop-adns.la -ladns"
+  AC_DEFINE(HAVE_ADNS)
 ])
 
+AC_CHECK_LIB(readline,rl_callback_handler_install,[
+AC_CHECK_HEADER(readline/readline.h,[
+  READLINE_LIBS="liboop-rl.la -lreadline"
+  AC_DEFINE(HAVE_READLINE)
+])])
+
 AC_CHECK_PROG(PROG_GLIB_CONFIG,glib-config,glib-config)
 if test -n "$PROG_GLIB_CONFIG" ; then
   GLIB_INCLUDES="`glib-config --cflags`"
@@ -60,4 +66,5 @@ AC_SUBST(GLIB_LIBS)
 AC_SUBST(ADNS_LIBS)
 AC_SUBST(WWW_INCLUDES)
 AC_SUBST(WWW_LIBS)
+AC_SUBST(READLINE_LIBS)
 AC_OUTPUT(Makefile)
diff --git a/glib.c b/glib.c
index d96d7df..942a24a 100644
--- a/glib.c
+++ b/glib.c
@@ -24,16 +24,17 @@ static int use_count = 0;
 static oop_source_sys *sys;
 static oop_adapter_select *sel;
 
-static fd_set read_set,write_set;
+static fd_set read_set,write_set,except_set;
 static int count;
 static void *ret = NULL;
 
 static void *on_select(
-	oop_adapter_select *s,int num,fd_set *r,fd_set *w,
-	struct timeval now,void *x) 
+	oop_adapter_select *s,int num,fd_set *r,fd_set *w,fd_set *x,
+	struct timeval now,void *unused) 
 {
 	read_set = *r;
 	write_set = *w;
+	except_set = *x;
 	count = num;
 	return &use_count;
 }
@@ -44,12 +45,17 @@ static gint on_poll(GPollFD *array,guint num,gint timeout) {
 
 	FD_ZERO(&read_set);
 	FD_ZERO(&write_set);
+	FD_ZERO(&except_set);
 	count = 0;
 	for (i = 0; i < num; ++i) {
 		if (array[i].events & G_IO_IN)
 			FD_SET(array[i].fd,&read_set);
 		if (array[i].events & G_IO_OUT)
 			FD_SET(array[i].fd,&write_set);
+		if (array[i].events & G_IO_PRI)
+			FD_SET(array[i].fd,&except_set);
+		/* {G_IO_,POLL}{ERR,HUP,INVAL} don't correspond to anything
+		   in select(2), and aren't `normal' events anyway. */
 		if (array[i].fd >= count)
 			count = 1 + array[i].fd;
 	}
@@ -57,7 +63,8 @@ static gint on_poll(GPollFD *array,guint num,gint timeout) {
 	tv.tv_sec = timeout / 1000;
 	tv.tv_usec = timeout % 1000;
 
-	oop_select_set(sel,count,&read_set,&write_set,timeout < 0 ? NULL : &tv);
+	oop_select_set(sel,count,&read_set,&write_set,&except_set,
+		       timeout < 0 ? NULL : &tv);
 	ret = oop_sys_run(sys);
 
 	if (&use_count != ret) {
@@ -71,6 +78,8 @@ static gint on_poll(GPollFD *array,guint num,gint timeout) {
 			array[i].revents |= G_IO_IN;
 		if (FD_ISSET(array[i].fd,&write_set))
 			array[i].revents |= G_IO_OUT;
+		if (FD_ISSET(array[i].fd,&except_set))
+			array[i].revents |= G_IO_PRI;
 	}
 
 	return count;
diff --git a/oop-rl.h b/oop-rl.h
new file mode 100644
index 0000000..174719c
--- /dev/null
+++ b/oop-rl.h
@@ -0,0 +1,20 @@
+/* oop-rl.h, liboop, copyright 2000 Dan Egnor
+
+   This is free software; you can redistribute it and/or modify it under the
+   terms of the GNU Lesser General Public License, version 2.1 or later.
+   See the file COPYING for details. */
+
+#ifndef OOP_READLINE_H
+#define OOP_READLINE_H
+
+#include "oop.h"
+
+/* Use a liboop event source to call rl_callback_read_char().
+   It is up to you to call rl_callback_handler_install().
+   Note well that readline uses malloc(), not oop_malloc(). */
+void oop_readline_register(oop_source *);
+
+/* Stop notifying readline of input characters. */
+void oop_readline_cancel(oop_source *);
+
+#endif
diff --git a/oop.h b/oop.h
index ebc8923..0e68618 100644
--- a/oop.h
+++ b/oop.h
@@ -19,6 +19,7 @@ typedef struct oop_source oop_source;
 typedef enum {
 	OOP_READ,
 	OOP_WRITE,
+	OOP_EXCEPT,
 
 	OOP_NUM_EVENTS
 } oop_event;
@@ -81,7 +82,7 @@ oop_source *oop_sys_source(oop_source_sys *);
 typedef struct oop_adapter_select oop_adapter_select;
 typedef void *oop_call_select(
 	oop_adapter_select *,
-	int num,fd_set *r,fd_set *w,
+	int num,fd_set *r,fd_set *w,fd_set *x,
 	struct timeval now,void *);
 
 oop_adapter_select *oop_select_new(
@@ -91,7 +92,7 @@ oop_adapter_select *oop_select_new(
 
 void oop_select_set(
 	oop_adapter_select *,int num_fd,
-	fd_set *rfd,fd_set *wfd,struct timeval *timeout);
+	fd_set *rfd,fd_set *wfd,fd_set *xfd,struct timeval *timeout);
 
 void oop_select_delete(oop_adapter_select *);
 
diff --git a/select.c b/select.c
index 9972d65..1f4f969 100644
--- a/select.c
+++ b/select.c
@@ -9,7 +9,7 @@
 #include <assert.h>
 
 struct select_set {
-	fd_set rfd,wfd;
+	fd_set rfd,wfd,xfd;
 };
 
 struct oop_adapter_select {
@@ -33,8 +33,10 @@ oop_adapter_select *oop_select_new(
 	s->source = source;
 	FD_ZERO(&s->watch.rfd);
 	FD_ZERO(&s->watch.wfd);
+	FD_ZERO(&s->watch.xfd);
 	FD_ZERO(&s->active.rfd);
 	FD_ZERO(&s->active.wfd);
+	FD_ZERO(&s->active.xfd);
 	s->num_fd = 0;
 	s->do_timeout = 0;
 	s->is_active = 0;
@@ -58,19 +60,22 @@ static void deactivate(oop_adapter_select *s) {
 		s->num_fd_active = 0;
 		FD_ZERO(&s->active.rfd);
 		FD_ZERO(&s->active.wfd);
+		FD_ZERO(&s->active.xfd);
 	}
 }
 
 void oop_select_set(
 	oop_adapter_select *s,int num_fd,
-	fd_set *rfd,fd_set *wfd,struct timeval *timeout)
+	fd_set *rfd,fd_set *wfd,fd_set *xfd,struct timeval *timeout)
 {
 	int fd;
 	for (fd = 0; fd < num_fd || fd < s->num_fd; ++fd) {
 		int rfd_set = fd < num_fd && FD_ISSET(fd,rfd);
 		int wfd_set = fd < num_fd && FD_ISSET(fd,wfd);
+		int xfd_set = fd < num_fd && FD_ISSET(fd,xfd);
 		int w_rfd_set = fd < s->num_fd && FD_ISSET(fd,&s->watch.rfd);
 		int w_wfd_set = fd < s->num_fd && FD_ISSET(fd,&s->watch.wfd);
+		int w_xfd_set = fd < s->num_fd && FD_ISSET(fd,&s->watch.xfd);
 
 		if (rfd_set && !w_rfd_set) {
 			s->source->on_fd(s->source,fd,OOP_READ,on_fd,s);
@@ -91,6 +96,16 @@ void oop_select_set(
 			s->source->cancel_fd(s->source,fd,OOP_WRITE);
 			FD_CLR(fd,&s->watch.wfd);
 		}
+
+		if (xfd_set && !w_xfd_set) {
+			s->source->on_fd(s->source,fd,OOP_EXCEPT,on_fd,s);
+			FD_SET(fd,&s->watch.xfd);
+		}
+
+		if (!xfd_set && w_xfd_set) {
+			s->source->cancel_fd(s->source,fd,OOP_EXCEPT);
+			FD_CLR(fd,&s->watch.xfd);
+		}
 	}
 
 	s->num_fd = num_fd;
@@ -118,7 +133,7 @@ void oop_select_set(
 void oop_select_delete(oop_adapter_select *s) {
 	fd_set fd;
 	FD_ZERO(&fd);
-	oop_select_set(s,0,&fd,&fd,NULL);
+	oop_select_set(s,0,&fd,&fd,&fd,NULL);
 	oop_free(s);
 }
 
@@ -140,6 +155,10 @@ static void *on_fd(oop_source *source,int fd,oop_event event,void *data) {
 		assert(FD_ISSET(fd,&s->watch.wfd));
 		set_fd(fd,&s->active.wfd,&s->num_fd_active);
 		break;
+	case OOP_EXCEPT:
+		assert(FD_ISSET(fd,&s->watch.xfd));
+		set_fd(fd,&s->active.xfd,&s->num_fd_active);
+		break;
 	default:
 		assert(0);
 		break;
@@ -160,5 +179,5 @@ static void *on_collect(oop_source *source,struct timeval when,void *data) {
 	struct timeval now;
 	gettimeofday(&now,NULL);
 	deactivate(s);
-	return s->call(s,num,&set.rfd,&set.wfd,now,s->data);
+	return s->call(s,num,&set.rfd,&set.wfd,&set.xfd,now,s->data);
 }
diff --git a/test-oop.c b/test-oop.c
index 13d2baa..605f659 100644
--- a/test-oop.c
+++ b/test-oop.c
@@ -21,6 +21,10 @@
 GMainLoop *glib_loop;
 #endif
 
+#ifdef HAVE_READLINE
+#include <readline/readline.h>
+#endif
+
 struct timer {
 	struct timeval tv;
 	int delay;
@@ -38,8 +42,11 @@ static void usage(void) {
 "         glib     GLib source adapter\n"
 #endif
 "sinks:   timer    some timers\n"
-"         echo     a stdin->stdout copy\n"
 "         signal   some signal handlers\n"
+"         echo     a stdin->stdout copy\n"
+#ifdef HAVE_READLINE
+"         readline like echo but with line editing\n"
+#endif
 #ifdef HAVE_ADNS
 "         adns     some asynchronous DNS lookups\n"
 #endif
@@ -80,6 +87,26 @@ void add_timer(oop_source *source,int interval) {
 	on_timer(source,timer->tv,timer);
 }
 
+/* -- signal --------------------------------------------------------------- */
+
+static oop_call_signal on_signal;
+static void *on_signal(oop_source *source,int sig,void *data) {
+	switch (sig) {
+	case SIGINT:
+		puts("signal: SIGINT (control-C) caught.  "
+		     "(Use SIGQUIT, control-\\, to terminate.)");
+		break;
+	case SIGQUIT:
+		puts("signal: SIGQUIT (control-\\) caught, terminating.");
+		source->cancel_signal(source,SIGINT,on_signal,NULL);
+		source->cancel_signal(source,SIGQUIT,on_signal,NULL);
+		break;
+	default:
+		assert(0 && "unknown signal?");
+	}
+	return OOP_CONTINUE;
+}
+
 /* -- echo ----------------------------------------------------------------- */
 
 static oop_call_fd on_data;
@@ -97,26 +124,42 @@ static void *stop_data(oop_source *source,int sig,void *data) {
 	return OOP_CONTINUE;
 }
 
-/* -- signal --------------------------------------------------------------- */
+/* -- readline ------------------------------------------------------------- */
 
-static oop_call_signal on_signal;
-static void *on_signal(oop_source *source,int sig,void *data) {
-	switch (sig) {
-	case SIGINT:
-		puts("signal: SIGINT (control-C) caught.  "
-		     "(Use SIGQUIT, control-\\, to terminate.)");
-		break;
-	case SIGQUIT:
-		puts("signal: SIGQUIT (control-\\) caught, terminating.");
-		source->cancel_signal(source,SIGINT,on_signal,NULL);
-		source->cancel_signal(source,SIGQUIT,on_signal,NULL);
-		break;
-	default:
-		assert(0 && "unknown signal?");
+#ifdef HAVE_READLINE
+
+static void on_readline(const char *input) {
+	if (NULL == input)
+		puts("\rreadline: EOF");
+	else {
+		fputs("readline: \"",stdout);
+		fputs(input,stdout);
+		puts("\"");
 	}
+}
+
+static void *stop_readline(oop_source *src,int sig,void *data) {
+	oop_readline_cancel(src);
+	src->cancel_signal(src,SIGQUIT,stop_readline,NULL);
+	rl_callback_handler_remove();
 	return OOP_CONTINUE;
 }
 
+static void add_readline(oop_source *src) {
+	rl_callback_handler_install("> ",(VFunction *) on_readline);
+	oop_readline_register(src);
+	src->on_signal(src,SIGQUIT,stop_readline,NULL);
+}
+
+#else
+
+static void add_readline(oop_source *src) {
+	fputs("sorry, readline not available\n",stderr);
+	usage();
+}
+
+#endif
+
 /* -- adns ----------------------------------------------------------------- */
 
 #ifdef HAVE_ADNS
@@ -129,7 +172,7 @@ static void *on_signal(oop_source *source,int sig,void *data) {
 oop_adns_query *q[NUM_Q];
 oop_adapter_adns *adns;
 
-void cancel_adns(void) {
+static void cancel_adns(void) {
 	int i;
 
 	for (i = 0; i < NUM_Q; ++i)
@@ -144,13 +187,13 @@ void cancel_adns(void) {
 	}
 }
 
-void *stop_lookup(oop_source *src,int sig,void *data) {
+static void *stop_lookup(oop_source *src,int sig,void *data) {
 	cancel_adns();
 	src->cancel_signal(src,SIGQUIT,stop_lookup,NULL);
 	return OOP_CONTINUE;
 }
 
-void *on_lookup(oop_adapter_adns *adns,adns_answer *reply,void *data) {
+static void *on_lookup(oop_adapter_adns *adns,adns_answer *reply,void *data) {
 	int i;
 	for (i = 0; i < NUM_Q; ++i) if (data == &q[i]) q[i] = NULL;
 
@@ -358,17 +401,24 @@ static void add_sink(oop_source *src,const char *name) {
 		return;
 	}
 
+	if (!strcmp(name,"signal")) {
+		src->on_signal(src,SIGINT,on_signal,NULL);
+		src->on_signal(src,SIGQUIT,on_signal,NULL);
+		return;
+	}
+
 	if (!strcmp(name,"echo")) {
 		src->on_fd(src,0,OOP_READ,on_data,NULL);
 		src->on_signal(src,SIGQUIT,stop_data,NULL);
 		return;
 	}
 
-	if (!strcmp(name,"signal")) {
-		src->on_signal(src,SIGINT,on_signal,NULL);
-		src->on_signal(src,SIGQUIT,on_signal,NULL);
+#ifdef HAVE_READLINE
+	if (!strcmp(name,"readline")) {
+		add_readline(src);
 		return;
 	}
+#endif
 
 #ifdef HAVE_ADNS
 	if (!strcmp(name,"adns")) {
-- 
GitLab