+ return (_("BOGUS EVENT TYPE"));
+}
+
+/*
+ * FDSource -- a source for a file descriptor
+ *
+ * We could use Glib's GIOChannel for this, but it adds some buffering
+ * and Unicode functionality that we really don't want. The custom GSource
+ * is simple enough anyway, and the Glib documentation describes it in prose.
+ */
+
+typedef struct FDSource {
+ GSource source; /* must be the first element in the struct */
+ GPollFD pollfd; /* Our file descriptor */
+} FDSource;
+
+static gboolean
+fdsource_prepare(
+ GSource *source G_GNUC_UNUSED,
+ gint *timeout_)
+{
+ *timeout_ = -1; /* block forever, as far as we're concerned */
+ return FALSE;
+}
+
+static gboolean
+fdsource_check(
+ GSource *source)
+{
+ FDSource *fds = (FDSource *)source;
+
+ /* we need to be dispatched if any interesting events have been received by the FD */
+ return fds->pollfd.events & fds->pollfd.revents;
+}
+
+static gboolean
+fdsource_dispatch(
+ GSource *source G_GNUC_UNUSED,
+ GSourceFunc callback,
+ gpointer user_data)
+{
+ if (callback)
+ return callback(user_data);
+
+ /* Don't automatically detach the event source if there's no callback. */
+ return TRUE;
+}
+
+GSource *
+new_fdsource(gint fd, GIOCondition events)
+{
+ static GSourceFuncs *fdsource_funcs = NULL;
+ GSource *src;
+ FDSource *fds;
+
+ /* initialize these here to avoid a compiler warning */
+ if (!fdsource_funcs) {
+ fdsource_funcs = g_new0(GSourceFuncs, 1);
+ fdsource_funcs->prepare = fdsource_prepare;
+ fdsource_funcs->check = fdsource_check;
+ fdsource_funcs->dispatch = fdsource_dispatch;
+ }
+
+ src = g_source_new(fdsource_funcs, sizeof(FDSource));
+ fds = (FDSource *)src;
+
+ fds->pollfd.fd = fd;
+ fds->pollfd.events = events;
+ g_source_add_poll(src, &fds->pollfd);
+
+ return src;
+}
+
+/*
+ * ChildWatchSource -- a source for a file descriptor
+ *
+ * Newer versions of glib provide equivalent functionality; consider
+ * optionally using that, protected by a GLIB_CHECK_VERSION condition.
+ */
+
+/* Versions before glib-2.4.0 didn't include a child watch source, and versions
+ * before 2.6.0 used unreliable signals. On these versions, we implement
+ * a "dumb" version of our own invention. This is dumb in the sense that it
+ * doesn't use SIGCHLD to detect a dead child, preferring to just poll at
+ * exponentially increasing interals. Writing a smarter implementation runs into
+ * some tricky race conditions and extra machinery. Since there are few, if any,
+ * users of a glib version this old, such machinery wouldn't get much testing.
+ *
+ * FreeBSD users have also reported problems with the glib child watch source,
+ * so we use the dumb version on FreeBSD, too.
+ */
+
+#if (defined(__FreeBSD__) || GLIB_MAJOR_VERSION < 2 || (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION < 6))
+typedef struct ChildWatchSource {
+ GSource source; /* must be the first element in the struct */
+
+ pid_t pid;
+
+ gint dead;
+ gint status;
+
+ gint timeout;
+} ChildWatchSource;
+
+/* this corresponds to rapid checks for about 10 seconds, after which the
+ * waitpid() check occurs every 2 seconds. */
+#define CWS_BASE_TIMEOUT 20
+#define CWS_MULT_TIMEOUT 1.1
+#define CWS_MAX_TIMEOUT 2000
+
+static gboolean
+child_watch_source_prepare(
+ GSource *source,
+ gint *timeout_)
+{
+ ChildWatchSource *cws = (ChildWatchSource *)source;
+
+ *timeout_ = cws->timeout;
+
+ cws->timeout *= CWS_MULT_TIMEOUT;
+ if (cws->timeout > CWS_MAX_TIMEOUT) cws->timeout = CWS_MAX_TIMEOUT;
+
+ return FALSE;
+}
+
+static gboolean
+child_watch_source_check(
+ GSource *source)
+{
+ ChildWatchSource *cws = (ChildWatchSource *)source;
+
+ /* is it dead? */
+ if (!cws->dead && waitpid(cws->pid, &cws->status, WNOHANG) > 0) {
+ cws->dead = TRUE;
+ }
+
+ return cws->dead;
+}
+
+static gboolean
+child_watch_source_dispatch(
+ GSource *source G_GNUC_UNUSED,
+ GSourceFunc callback,
+ gpointer user_data)
+{
+ ChildWatchSource *cws = (ChildWatchSource *)source;
+
+ /* this shouldn't happen, but just in case */
+ if (cws->dead) {
+ if (!callback) {
+ g_warning("child %jd died before callback was registered", (uintmax_t)cws->pid);
+ return FALSE;
+ }
+
+ ((ChildWatchFunc)callback)(cws->pid, cws->status, user_data);
+
+ /* Un-queue this source unconditionally -- the child can't die twice */
+ return FALSE;
+ }
+
+ return TRUE;