+static void a_destroy(admin */*a*/);
+static void a_lock(admin */*a*/);
+static void a_unlock(admin */*a*/);
+
+/*----- Output functions --------------------------------------------------*/
+
+/* --- @trywrite@ --- *
+ *
+ * Arguments: @admin *a@ = pointer to an admin block
+ * @const char *p@ = pointer to buffer to write
+ * @size_t sz@ = size of data to write
+ *
+ * Returns: The number of bytes written, or less than zero on error.
+ *
+ * Use: Attempts to write data to a client.
+ */
+
+static ssize_t trywrite(admin *a, const char *p, size_t sz)
+{
+ ssize_t n, done = 0;
+
+again:
+ if (!sz)
+ return (done);
+ n = write(a->w.fd, p, sz);
+ if (n > 0) {
+ done += n;
+ p += n;
+ sz -= n;
+ goto again;
+ }
+ if (n < 0) {
+ if (errno == EINTR)
+ goto again;
+ if (errno != EAGAIN && errno != EWOULDBLOCK) {
+ a_destroy(a);
+ a_warn("disconnecting admin client due to write errors: %s",
+ strerror(errno));
+ return (-1);
+ }
+ }
+ return (done);
+}
+
+/* --- @dosend@ --- *
+ *
+ * Arguemnts: @admin *a@ = pointer to an admin block
+ * @const char *p@ = pointer to buffer to write
+ * @size_t sz@ = size of data to write
+ *
+ * Returns: ---
+ *
+ * Use: Sends data to an admin client.
+ */
+
+static void dosend(admin *a, const char *p, size_t sz)
+{
+ ssize_t n;
+ obuf *o;
+
+ if (a->f & AF_DEAD)
+ return;
+
+ /* --- Try to send the data immediately --- */
+
+ if (!a->o_head) {
+ if ((n = trywrite(a, p, sz)) < 0)
+ return;
+ p += n;
+ sz -= n;
+ if (!sz)
+ return;
+ }
+
+ /* --- Fill buffers with the data until it's all gone --- */
+
+ o = a->o_tail;
+ if (!o)
+ sel_addfile(&a->w);
+ else if (o->p_in < o->buf + OBUFSZ)
+ goto noalloc;
+
+ do {
+ o = xmalloc(sizeof(obuf));
+ o->next = 0;
+ o->p_in = o->p_out = o->buf;
+ if (a->o_tail)
+ a->o_tail->next = o;
+ else
+ a->o_head = o;
+ a->o_tail = o;
+
+ noalloc:
+ n = o->buf + OBUFSZ - o->p_in;
+ if (n > sz)
+ n = sz;
+ memcpy(o->p_in, p, n);
+ o->p_in += n;
+ p += n;
+ sz -= n;
+ } while (sz);
+}
+
+/* --- @a_flush@ --- *
+ *
+ * Arguments: @int fd@ = file descriptor
+ * @unsigned mode@ = what's happening
+ * @void *v@ = pointer to my admin block
+ *
+ * Returns: ---
+ *
+ * Use: Flushes buffers when a client is ready to read again.
+ */
+
+static void a_flush(int fd, unsigned mode, void *v)
+{
+ admin *a = v;
+ obuf *o, *oo;
+ ssize_t n;
+
+ o = a->o_head;
+ while (o) {
+ if ((n = trywrite(a, o->p_out, o->p_in - o->p_out)) < 0)
+ return;
+ o->p_out += n;
+ if (o->p_in < o->p_out)
+ break;
+ oo = o;
+ o = o->next;
+ xfree(oo);
+ }
+ a->o_head = o;
+ if (!o) {
+ a->o_tail = 0;
+ sel_rmfile(&a->w);
+ }
+}
+