]> git.pond.sub.org Git - empserver/commitdiff
Rewrite much of client's playing phase code:
authorMarkus Armbruster <armbru@pond.sub.org>
Sat, 17 Nov 2007 14:17:38 +0000 (14:17 +0000)
committerMarkus Armbruster <armbru@pond.sub.org>
Sat, 17 Nov 2007 14:17:38 +0000 (14:17 +0000)
(EOF_COOKIE, INTR_COOKIE, input_fd, send_intr, recv_output)
(recv_input, intr, play): New playing phase code.  No native Windows
support yet.  Sends just one EOF cookie on EOF on standard input
instead of up to three.  Old servers (before recvclient.c rev. 1.16)
fail to terminate the session when they receive an EOF cookie at an
argument prompt.  The session then hangs; use SIGINT to get out.  No
longer blocks on sending input, which could deadlock the session.
Closes #827090.  Fixes error handling for select().  Fixes race
condition that could cause server output to be discarded on EOF on
standard input.
(main): Replace old playing phase code by a call to play().
(intr, sock, interrupt, handleintr): Replaced by play(), remove.
[_WIN32] (hStdIn): Ditto.
(auxfp): New.
(servercmd, prompt, doexecute): Remove parameter auxfi, use auxfp.
(eight_bit_clean): Move to servcmd.c.
(servercmd): Work on a single non-C_DATA line instead of getting lines
from an ioqueue.
(servercmd, output, screen, outch): Deal with all ids in servercmd()
rather than some there and some in output().  Don't treat C_NOECHO,
C_ABORT, C_CMDERR, C_BADCMD specially.  Fix C_FLASH and C_EXIT to
ignore redirections; they used to ignore them only for some parts.
Replace output() by outch(), fold screen into outch().
(servercmd): Truncate long prompts and telegram infos to prevent
buffer overflow.
(prompt): Use new parameters code, prompt, teles instead of global
variables mode, the_prompt, num_teles.
(num_teles, the_prompt, mode, nbtu, nmin): Remove.
(prompt): Don't write an empty line before argument prompts to auxfp.
(servercmd): Don't strip newline from redirections and execute,
doredir(), dopipe() and doexecute() need it now.
(doredir, dopipe, doexecute): Use new seen_input() instead of gettag().
(doexecute): Set input_fd and leave reading the script file to play().
(serverio, termio, sendeof): Replaced by play(), remove.
(LBUF_LEN_MAX, lbuf, lbuf_init, lbuf_len, lbuf_full, lbuf_line)
(lbuf_putc): New.
(RING_SIZE, ring, ring_init, ring_len, ring_space, ring_peek)
(ring_getc, ring_putc, ring_putm, ring_discard, ring_search)
(ring_from_file, ring_to_file): New.
(clear_recent_input, save_input, seen_input): New.
(MAX): New.
(ioqueue, io, ioq_init, ioq_dequeue, ioq_read, ioq_write, ioq_qsize)
(ioq_drain, ioq_gets, ioqtobuf, enqueuecc, dequeuecc): Unused, remove.
(QEMPTY, qelem, insque, remque, initque): Unused, remove.
(tagstruct, taglist, io_init, gettag): Unused, remove.

19 files changed:
src/client/Makefile.in
src/client/ioqueue.c [deleted file]
src/client/ioqueue.h [deleted file]
src/client/linebuf.c [new file with mode: 0644]
src/client/linebuf.h [new file with mode: 0644]
src/client/main.c
src/client/misc.h
src/client/play.c [new file with mode: 0644]
src/client/queue.c [deleted file]
src/client/queue.h [deleted file]
src/client/ringbuf.c [new file with mode: 0644]
src/client/ringbuf.h [new file with mode: 0644]
src/client/secure.c [new file with mode: 0644]
src/client/secure.h [new file with mode: 0644]
src/client/servcmd.c
src/client/serverio.c [deleted file]
src/client/tags.c [deleted file]
src/client/tags.h [deleted file]
src/client/termio.c [deleted file]

index ce45e2557540ce4686d3a3e8c00fc669497d9bb7..6c8b0a0533cad04290a8e3f8fbdb7b6ff7a818b3 100644 (file)
@@ -53,8 +53,8 @@ srcdir = @srcdir@
 VPATH = @srcdir@
 
 prog = empire$E
-obj = expect.$O host.$O ioqueue.$O ipglob.$O login.$O main.$O queue.$O \
-servcmd.$O serverio.$O tags.$O termio.$O termlib.$O version.$O
+obj = expect.$O host.$O ipglob.$O linebuf.$O login.$O main.$O play.$O  \
+ringbuf.$O secure.$O servcmd.$O termlib.$O version.$O
 
 all: $(prog)
 
@@ -81,14 +81,13 @@ uninstall:
 # FIXME generate from .d
 expect.$O: misc.h
 host.$O: misc.h
-ioqueue.$O: misc.h queue.h ioqueue.h
+linebuf.$O: linebuf.h
 login.$O: misc.h proto.h
-main.$O: misc.h proto.h queue.h ioqueue.h tags.h version.h
-queue.$O: misc.h queue.h
-servcmd.$O: misc.h proto.h queue.h ioqueue.h tags.h
-serverio.$O: misc.h queue.h ioqueue.h
-tags.$O: misc.h tags.h
-termio.$O: misc.h tags.h
+main.$O: misc.h version.h
+play.$O: linebuf.h misc.h proto.h ringbuf.h secure.h
+ringbuf.$O: ringbuf.h
+secure.$O: ringbuf.h secure.h
+servcmd.$O: misc.h proto.h secure.h
 termlib.$O: misc.h
 version.$O: version.h
 $(obj): config.h
diff --git a/src/client/ioqueue.c b/src/client/ioqueue.c
deleted file mode 100644 (file)
index 872e6ad..0000000
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- *  Empire - A multi-player, client/server Internet based war game.
- *  Copyright (C) 1986-2007, Dave Pare, Jeff Bailey, Thomas Ruschak,
- *                           Ken Stevens, Steve McClure
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program 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 General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- *  ---
- *
- *  See files README, COPYING and CREDITS in the root of the source
- *  tree for related information and legal notices.  It is expected
- *  that future projects/authors will amend these files as needed.
- *
- *  ---
- *
- *  ioqueue.c: Manage an i/o queue
- * 
- *  Known contributors to this file:
- *     Steve McClure, 1998
- */
-
-#include <config.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include "ioqueue.h"
-#include "misc.h"
-#include "queue.h"
-
-static int ioqtobuf(struct ioqueue *ioq, char *buf, int cc);
-static void enqueuecc(struct ioqueue *ioq, char *buf, int cc);
-static int dequeuecc(struct ioqueue *ioq, int cc);
-
-
-void
-ioq_init(struct ioqueue *ioq, int bsize)
-{
-    initque(&ioq->queue);
-    ioq->cc = 0;
-    ioq->bsize = bsize;
-}
-
-/*
- * Copy the specified number of characters into the buffer
- * provided, without actually dequeueing the data.  Return
- * number of bytes actually found.
- */
-int
-ioq_peek(struct ioqueue *ioq, char *buf, int cc)
-{
-    return ioqtobuf(ioq, buf, cc);
-}
-
-int
-ioq_dequeue(struct ioqueue *ioq, int cc)
-{
-    if (dequeuecc(ioq, cc) != cc)
-       return 0;
-    return cc;
-}
-
-int
-ioq_read(struct ioqueue *ioq, char *buf, int cc)
-{
-    int n;
-
-    n = ioqtobuf(ioq, buf, cc);
-    if (n > 0)
-       dequeuecc(ioq, n);
-    return n;
-}
-
-void
-ioq_write(struct ioqueue *ioq, char *buf, int cc)
-{
-    enqueuecc(ioq, buf, cc);
-}
-
-int
-ioq_qsize(struct ioqueue *ioq)
-{
-    return ioq->cc;
-}
-
-void
-ioq_drain(struct ioqueue *ioq)
-{
-    struct io *io;
-    struct qelem *qp;
-
-    while ((qp = ioq->queue.q_forw) != &ioq->queue) {
-       io = (struct io *)qp;
-       free(io->data);
-       (void)remque(&io->queue);
-       (void)free(io);
-    }
-    ioq->cc = 0;
-}
-
-char *
-ioq_gets(struct ioqueue *ioq, char *buf, int cc, int *eol)
-{
-    char *p;
-    char *end;
-    int nbytes;
-
-    *eol = 0;
-
-    cc--;
-
-    nbytes = ioqtobuf(ioq, buf, cc);
-    end = &buf[nbytes];
-    for (p = buf; p < end && *p; p++) {
-       if (*p == '\n') {
-           *++p = '\0';
-           *eol = 1;
-           dequeuecc(ioq, p - buf);
-           return buf;
-       }
-    }
-    if (cc && (p - buf) == cc) {
-       dequeuecc(ioq, cc);
-       buf[cc] = '\0';
-       return buf;
-    }
-    return NULL;
-}
-
-/*
- * all the rest are local to this module
- */
-
-
-/*
- * copy cc bytes from ioq to buf.
- * this routine doesn't free memory; this is
- * left for a higher level.
- */
-static int
-ioqtobuf(struct ioqueue *ioq, char *buf, int cc)
-{
-    struct io *io;
-    struct qelem *qp;
-    char *offset;
-    int nbytes;
-    int nleft;
-
-    nleft = cc;
-    offset = buf;
-    for (qp = ioq->queue.q_forw; qp != &ioq->queue; qp = qp->q_forw) {
-       io = (struct io *)qp;
-       if ((nbytes = io->nbytes - io->offset) < 0) {
-           fprintf(stderr, "ioqtobuf: offset %d nbytes %d\n",
-                   io->offset, io->nbytes);
-           continue;
-       }
-       if (nbytes > 0) {
-           if (nleft < nbytes)
-               nbytes = nleft;
-           memcpy(offset, io->data + io->offset, nbytes);
-           offset += nbytes;
-           nleft -= nbytes;
-       }
-    }
-    return offset - buf;
-}
-
-/*
- * append a buffer to the end of the ioq.
- */
-static void
-enqueuecc(struct ioqueue *ioq, char *buf, int cc)
-{
-    struct io *io;
-
-    io = malloc(sizeof(*io));
-    io->nbytes = cc;
-    io->offset = 0;
-    io->data = buf;
-    insque(&io->queue, ioq->queue.q_back);
-    ioq->cc += cc;
-}
-
-/*
- * remove cc bytes from ioqueue ioq
- * free memory, dequeue io elements
- * which are no longer used.
- */
-static int
-dequeuecc(struct ioqueue *ioq, int cc)
-{
-    struct io *io;
-    struct qelem *qp;
-    int nbytes;
-    int there;
-
-    nbytes = 0;
-    while ((qp = ioq->queue.q_forw) != &ioq->queue) {
-       io = (struct io *)qp;
-       there = io->nbytes - io->offset;
-       if (there < 0) {
-           fprintf(stderr, "dequeuecc: nbytes %d, offset %d\n",
-                   io->nbytes, io->offset);
-           continue;
-       }
-       if (cc > there) {
-           cc -= there;
-           nbytes += there;
-           (void)remque(&io->queue);
-           free(io->data);
-       } else {
-           io->offset += cc;
-           nbytes += cc;
-           break;
-       }
-    }
-    ioq->cc -= nbytes;
-    return nbytes;
-}
diff --git a/src/client/ioqueue.h b/src/client/ioqueue.h
deleted file mode 100644 (file)
index 9a88c38..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- *  Empire - A multi-player, client/server Internet based war game.
- *  Copyright (C) 1986-2007, Dave Pare, Jeff Bailey, Thomas Ruschak,
- *                           Ken Stevens, Steve McClure
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program 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 General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- *  ---
- *
- *  See files README, COPYING and CREDITS in the root of the source
- *  tree for related information and legal notices.  It is expected
- *  that future projects/authors will amend these files as needed.
- *
- *  ---
- *
- *  ioqueue.h: stores and frees data associated with a file descriptor.
- *             uses writev to write, and read to read.
- * 
- *  Known contributors to this file:
- *  
- */
-
-#ifndef IOQUEUE_H
-#define IOQUEUE_H
-
-#include "queue.h"
-
-struct ioqueue {
-    struct qelem queue;                /* queue fwd/back */
-    int bsize;                 /* basic block size */
-    int cc;                    /* character count */
-};
-
-struct io {
-    struct qelem queue;                /* list of ioqueue elements */
-    int nbytes;                        /* number of data bytes present */
-    int offset;                        /* offset into current entry */
-    char *data;                        /* pointer to start */
-};
-
-void ioq_init(struct ioqueue *ioq, int bsize);
-int ioq_peek(struct ioqueue *ioq, char *buf, int cc);
-int ioq_dequeue(struct ioqueue *ioq, int cc);
-int ioq_read(struct ioqueue *ioq, char *buf, int cc);
-void ioq_write(struct ioqueue *ioq, char *buf, int cc);
-int ioq_qsize(struct ioqueue *ioq);
-void ioq_drain(struct ioqueue *ioq);
-char *ioq_gets(struct ioqueue *ioq, char *buf, int cc, int *eol);
-
-#endif
diff --git a/src/client/linebuf.c b/src/client/linebuf.c
new file mode 100644 (file)
index 0000000..4187bd5
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ *  Empire - A multi-player, client/server Internet based war game.
+ *  Copyright (C) 1986-2007, Dave Pare, Jeff Bailey, Thomas Ruschak,
+ *                           Ken Stevens, Steve McClure
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  ---
+ *
+ *  See files README, COPYING and CREDITS in the root of the source
+ *  tree for related information and legal notices.  It is expected
+ *  that future projects/authors will amend these files as needed.
+ *
+ *  ---
+ *
+ *  linebuf.c: Simple line buffer
+ * 
+ *  Known contributors to this file:
+ *     Markus Armbruster, 2007
+ */
+
+#include <config.h>
+
+#include <assert.h>
+#include <stdlib.h>
+#include "linebuf.h"
+
+/*
+ * Initialize empty line buffer.
+ * Not necessary if *LBUF is already zeroed.
+ */
+void
+lbuf_init(struct lbuf *lbuf)
+{
+    lbuf->len = lbuf->full = 0;
+}
+
+/*
+ * Return length of currently buffered line.
+ * This includes the newline if present.
+ */
+int
+lbuf_len(struct lbuf *lbuf)
+{
+    return lbuf->len;
+}
+
+/*
+ * Is LBUF full (i.e. we got the newline)?
+ */
+int
+lbuf_full(struct lbuf *lbuf)
+{
+    return lbuf->full;
+}
+
+/*
+ * Return a pointer to the currently buffered line.
+ * If you mess with the line in a way that changes the line length,
+ * better call lbuf_init() next.
+ */
+char *
+lbuf_line(struct lbuf *lbuf)
+{
+    assert(lbuf->len < sizeof(lbuf->line));
+    lbuf->line[lbuf->len] = 0;
+    return lbuf->line;
+}
+
+/*
+ * Append CH to the line buffered in LBUF.
+ * LBUF must not be full.
+ * If CH is a newline, the buffer is now full.  Return the line
+ * length, including the newline.
+ * Else return 0 if there was space, and -1 if not.
+ */
+int
+lbuf_putc(struct lbuf *lbuf, char ch)
+{
+    assert(!lbuf->full);
+
+    if (ch == '\n') {
+       assert(lbuf->len + 1 < sizeof(lbuf->line));
+       lbuf->line[lbuf->len++] = ch;
+       lbuf->line[lbuf->len] = 0;
+       lbuf->full = 1;
+       return lbuf->len;
+    }
+
+    if (lbuf->len + 2 >= sizeof(lbuf->line))
+       return -1;              /* truncating long line */
+
+    lbuf->line[lbuf->len++] = ch;
+    return 0;
+}
diff --git a/src/client/linebuf.h b/src/client/linebuf.h
new file mode 100644 (file)
index 0000000..4149adf
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ *  Empire - A multi-player, client/server Internet based war game.
+ *  Copyright (C) 1986-2007, Dave Pare, Jeff Bailey, Thomas Ruschak,
+ *                           Ken Stevens, Steve McClure
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  ---
+ *
+ *  See files README, COPYING and CREDITS in the root of the source
+ *  tree for related information and legal notices.  It is expected
+ *  that future projects/authors will amend these files as needed.
+ *
+ *  ---
+ *
+ *  linebuf.h: Simple line buffer
+ * 
+ *  Known contributors to this file:
+ *     Markus Armbruster, 2007
+ */
+
+#ifndef LINEBUF_H
+#define LINEBUF_H
+
+#define LBUF_LEN_MAX 4096
+
+struct lbuf {
+    /* All members are private! */
+    unsigned len;              /* strlen(line) */
+    int full;                  /* got a complete line, with newline? */
+    char line[LBUF_LEN_MAX];   /* buffered line, zero-terminated */
+};
+
+extern void lbuf_init(struct lbuf *);
+extern int lbuf_len(struct lbuf *);
+extern int lbuf_full(struct lbuf *);
+extern char *lbuf_line(struct lbuf *);
+extern int lbuf_putc(struct lbuf *, char);
+
+#endif
index 754f6b0f8f8df7630581208e0bded8661ff662ac..cd0f6fc25da333f2faac3908a65ba45ea9fcf46a 100644 (file)
 
 #include <config.h>
 
-#include <errno.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#if defined(_WIN32)
-#include <io.h>
-#else
 #include <pwd.h>
-#include <sys/time.h>
-#include <sys/types.h>
+#include <stdlib.h>
 #include <unistd.h>
-#endif
-
-#include "ioqueue.h"
 #include "misc.h"
-#include "proto.h"
-#include "tags.h"
 #include "version.h"
 
-#ifdef _WIN32
-HANDLE hStdIn;
-#endif
-
-#define        RETRY   3
-
-int eight_bit_clean;
-int sock;
-
-static volatile sig_atomic_t interrupt;
-static void intr(int sig);
-static int handleintr(int);
-
 static void
 print_usage(char *program_name)
 {
@@ -94,25 +67,10 @@ main(int argc, char **argv)
     char *uname;
     char *host;
     char *port;
+    int sock;
 #ifdef _WIN32
-    WORD wVersionRequested;
-    WSADATA WsaData;
-    int err;
-    fd_set readfds;
-    struct timeval tm;
-    DWORD stdinmode;
-    SECURITY_ATTRIBUTES security;
-    int bRedirected = 0;
     char unamebuf[128];
-#else
-    struct sigaction sa;
-    fd_set mask;
-    fd_set savemask;
-    int retry = 0;
 #endif
-    struct ioqueue server;
-    FILE *auxout_fp = NULL;
-    int n;
 
 #ifdef _WIN32
     /*
@@ -124,9 +82,6 @@ main(int argc, char **argv)
      * after each prompt is required.
      */
     setvbuf(stdout, NULL, _IOLBF, 4096);
-#else
-    FD_ZERO(&mask);
-    FD_ZERO(&savemask);
 #endif
 
     while ((opt = getopt(argc, argv, "2:kuhv")) != EOF) {
@@ -192,14 +147,13 @@ main(int argc, char **argv)
     }
 
     getsose();
-    if (auxfname && (auxout_fp = fopen(auxfname, "a")) == NULL) {
+    if (auxfname && (auxfp = fopen(auxfname, "a")) == NULL) {
        fprintf(stderr, "Unable to open %s for append\n", auxfname);
        exit(1);
     }
 
 #ifdef _WIN32
-    wVersionRequested = MAKEWORD(2, 0);
-    err = WSAStartup(wVersionRequested, &WsaData);
+    err = WSAStartup(MAKEWORD(2, 0), &WsaData);
     if (err != 0) {
        printf("WSAStartup Failed, error code %d\n", err);
        exit(1);
@@ -211,156 +165,8 @@ main(int argc, char **argv)
     if (!login(sock, uname, country, passwd, send_kill, utf8))
        exit(1);
 
-    ioq_init(&server, 2048);
-    io_init();
-#ifndef _WIN32
-    FD_ZERO(&mask);
-    FD_SET(0, &savemask);
-    FD_SET(sock, &savemask);
-    sigemptyset(&sa.sa_mask);
-    sa.sa_flags = 0;
-    sa.sa_handler = intr;
-    sigaction(SIGINT, &sa, NULL);
-    sa.sa_handler = SIG_IGN;
-    sigaction(SIGPIPE, &sa, NULL);
-    while (FD_ISSET(sock, &savemask)) {
-       mask = savemask;
-       n = select(sock + 1, &mask, NULL, NULL, NULL);
-       if (interrupt) {
-           if (!handleintr(sock))
-               break;
-           errno = 0;
-       }
-       if (n <= 0) {
-           if (errno == EINTR) {
-               perror("select");
-               (void)close(sock);
-               FD_CLR(sock, &savemask);
-           }
-       } else {
-           if (FD_ISSET(0, &mask)) {
-               if (!termio(0, sock, auxout_fp)) {
-                   if (retry++ >= RETRY) {
-                       FD_CLR(0, &savemask);
-                   }
-               } else {
-                   retry = 0;
-               }
-           }
-           if (FD_ISSET(sock, &mask)) {
-               if (!serverio(sock, &server))
-                   FD_CLR(sock, &savemask);
-               else
-                   servercmd(&server, auxout_fp);
-           }
-       }
-    }
-#else  /* _WIN32 */
-    signal(SIGINT, intr);
-
-    bRedirected = 0;
-    tm.tv_sec = 0;
-    tm.tv_usec = 1000;
-
-    if (!isatty(fileno(stdin)))
-       bRedirected = 1;
-    else {
-       security.nLength = sizeof(SECURITY_ATTRIBUTES);
-       security.lpSecurityDescriptor = NULL;
-       security.bInheritHandle = TRUE;
-       hStdIn = CreateFile("CONIN$",
-                           GENERIC_READ | GENERIC_WRITE,
-                           FILE_SHARE_READ | FILE_SHARE_WRITE,
-                           &security, OPEN_EXISTING, (DWORD) NULL, NULL);
-       
-       if (hStdIn == INVALID_HANDLE_VALUE) {
-           printf("Error getting hStdIn.\n");
-           fflush(stdout);
-           exit(-3);
-       }
-       
-       err = GetConsoleMode(hStdIn, &stdinmode);
-       if (!err) {
-           printf("Error getting console mode.\n");
-           fflush(stdout);
-           exit(-4);
-       } else {
-           stdinmode |= ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT;
-           err = SetConsoleMode(hStdIn, stdinmode);
-           if (!err) {
-               printf("Error setting console mode.\n");
-               fflush(stdout);
-               exit(-5);
-           }
-       }
-    }
-    while (1) {
-       FD_ZERO(&readfds);
-       FD_SET(sock, &readfds);
-       n = select(sock + 1, &readfds, NULL, NULL, &tm);
-       if (interrupt) {
-           if (!handleintr(sock))
-               break;
-           errno = 0;
-       }
-       if (n < 0) {
-           if (errno == EINTR) {
-               errno = WSAGetLastError();
-               perror("select");
-               (void)closesocket(sock);
-               break;
-           }
-       } else {
-           if (bRedirected == 1) {
-               if (!termio(0, sock, auxout_fp))
-                   bRedirected = -1;
-           } else if (bRedirected == 0) {
-               if (WaitForSingleObject(hStdIn, 10) != WAIT_TIMEOUT) {
-                   termio(-1, sock, auxout_fp);
-                   FlushConsoleInputBuffer(hStdIn);
-               }
-           }
-           if (FD_ISSET(sock, &readfds)) {
-               if (!serverio(sock, &server))
-                   break;
-               else
-                   servercmd(&server, auxout_fp);
-           }
-       }
-    }
-    if (bRedirected == 0)
-       CloseHandle(hStdIn);
-#endif /* _WIN32 */
-    ioq_drain(&server);
-#ifdef _WIN32
-    (void)closesocket(sock);
-#else
-    (void)close(sock);
-#endif
-    return 0;                  /* Shut the compiler up */
-}
-
-static void
-intr(int sig)
-{
-    interrupt = 1;
-#ifdef _WIN32
-    signal(SIGINT, intr);
-#endif
-}
+    if (play(sock) < 0)
+       exit(1);
 
-static int
-handleintr(int s)
-{
-    if (interrupt) {
-       /* tacky, but it works */
-#if !defined(_WIN32)
-       if (write(s, "\naborted\n", 1 + 7 + 1) <= 0)
-#else
-       if (send(s, "\naborted\n", 1 + 7 + 1, 0) <= 0)
-#endif
-           return 0;
-       interrupt = 0;
-    }
-    return 1;
+    return 0;
 }
index 2dc3bbd3c4b8d2b88e0967167f08b4770ea6051a..ee9c9e064851cf6cc2891a7bd3f0dd6bc88e7648 100644 (file)
 #include <windows.h>
 #endif
 
-struct ioqueue;
+#define MAX(a, b) ((a) >= (b) ? (a) : (b))
 
 extern char empirehost[];
 extern char empireport[];
 extern int eight_bit_clean;
-extern int sock;
+extern int input_fd;
+extern FILE *auxfp;
 extern char *SO;
 extern char *SE;
 
-#ifdef _WIN32
-extern HANDLE hStdIn;
-#endif
-
 #ifdef _WIN32
 #define getsose() ((void)0)
 #define putso() ((void)0)
@@ -67,9 +64,9 @@ int recvline(int s, char *buf);
 int expect(int s, int match, char *buf);
 int tcp_connect(char *, char *);
 int login(int s, char *uname, char *cname, char *cpass, int kill_proc, int);
+int play(int);
 void sendcmd(int s, char *cmd, char *arg);
-void servercmd(struct ioqueue *ioq, FILE *auxfi);
-int serverio(int s, struct ioqueue *ioq);
-int termio(int fd, int sock, FILE *auxfi);
+void servercmd(int, char *, int);
+void outch(char);
 
 #endif
diff --git a/src/client/play.c b/src/client/play.c
new file mode 100644 (file)
index 0000000..c3fe98e
--- /dev/null
@@ -0,0 +1,316 @@
+/*
+ *  Empire - A multi-player, client/server Internet based war game.
+ *  Copyright (C) 1986-2007, Dave Pare, Jeff Bailey, Thomas Ruschak,
+ *                           Ken Stevens, Steve McClure
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  ---
+ *
+ *  See files README, COPYING and CREDITS in the root of the source
+ *  tree for related information and legal notices.  It is expected
+ *  that future projects/authors will amend these files as needed.
+ *
+ *  ---
+ *
+ *  play.c: Playing the game
+ * 
+ *  Known contributors to this file:
+ *     Markus Armbruster, 2007
+ */
+
+#include <config.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include "linebuf.h"
+#include "misc.h"
+#include "proto.h"
+#include "ringbuf.h"
+#include "secure.h"
+
+#define EOF_COOKIE "ctld\n"
+#define INTR_COOKIE "\naborted\n"
+
+int input_fd;
+static volatile sig_atomic_t send_intr; /* need to send INTR_COOKIE */
+
+/*
+ * Receive and process server output from SOCK.
+ * Return number of characters received on success, -1 on error.
+ */
+static int
+recv_output(int sock)
+{
+    /*
+     * Read a chunk of server output and feed its characters into a
+     * simple state machine.
+     * Initial state is SCANNING_ID.
+     * In state SCANNING_ID, buffer the character.  If it's a space,
+     * decode the id that has been buffered, and enter state BUFFERING
+     * or COPYING depending on its value.
+     * In state BUFFERING, buffer the character.  If it's newline,
+     * pass id and buffered text to servercmd(), then enter state
+     * SCANNING_ID.
+     * In state COPYING, pass the character to outch().  If it's
+     * newline, enter state SCANNING_ID.
+     */
+    static enum {
+       SCANNING_ID, BUFFERING, COPYING
+    } state = SCANNING_ID;
+    static int id;
+    static struct lbuf lbuf;
+    char buf[4096];
+    ssize_t n;
+    int i, ch, len;
+    char *line, *end;
+
+    n = read(sock, buf, sizeof(buf));
+    if (n < 0)
+       return -1;
+
+    for (i = 0; i < n; i++) {
+       ch = buf[i];
+       switch (state) {
+       case SCANNING_ID:
+           if (ch == '\n') {
+               /* FIXME gripe unexpected! */
+               lbuf_init(&lbuf);
+               break;
+           }
+           if (ch != ' ') {
+               lbuf_putc(&lbuf, ch);
+               break;
+           }
+           line = lbuf_line(&lbuf);
+           id = strtol(line, &end, 16);
+           if (end == line || *end) {
+               /* FIXME gripe bad id */
+               id = -1;
+           }
+           lbuf_init(&lbuf);
+
+           switch (id) {
+           case C_PROMPT:
+           case C_FLUSH:
+           case C_EXECUTE:
+           case C_EXIT:
+           case C_FLASH:
+           case C_INFORM:
+           case C_PIPE:
+           case C_REDIR:
+               state = BUFFERING;
+               break;
+           default:
+               /* unknown or unexpected id, treat like C_DATA */
+           case C_DATA:
+               state = COPYING;
+               break;
+           }
+           break;
+
+       case BUFFERING:
+           len = lbuf_putc(&lbuf, ch);
+           if (len) {
+               line = lbuf_line(&lbuf);
+               servercmd(id, line, len);
+               lbuf_init(&lbuf);
+               state = SCANNING_ID;
+           }
+           break;
+
+       case COPYING:
+           outch(ch);
+           if (ch == '\n')
+               state = SCANNING_ID;
+       }
+    }
+
+    return n;
+}
+
+/*
+ * Receive command input from FD into INBUF.
+ * Return 1 on receipt of input, zero on EOF, -1 on error.
+ */
+static int
+recv_input(int fd, struct ring *inbuf)
+{
+    static struct lbuf cmdbuf;
+    int n, i, ch;
+    char *line;
+    int res = 1;
+
+    n = ring_from_file(inbuf, fd);
+    if (n < 0)
+       return -1;
+    if (n == 0) {
+       /* EOF on input */
+       if (lbuf_len(&cmdbuf)) {
+           /* incomplete line */
+           ring_putc(inbuf, '\n');
+           n++;
+       }
+       /*
+        * Can't put EOF cookie into INBUF here, it may not fit.
+        * Leave it to caller.
+        */
+       res = 0;
+    }
+
+    /* copy input to AUXFP etc. */
+    for (i = -n; i < 0; i++) {
+       ch = ring_peek(inbuf, i);
+       assert(ch != EOF);
+       if (lbuf_putc(&cmdbuf, ch)) {
+           line = lbuf_line(&cmdbuf);
+           if (auxfp)
+               fputs(line, auxfp);
+           save_input(line);
+           lbuf_init(&cmdbuf);
+       }
+    }
+
+    return res;
+}
+
+static void
+intr(int sig)
+{
+    send_intr = 1;
+#ifdef _WIN32
+    signal(SIGINT, intr);
+#endif
+}
+
+/*
+ * Play on SOCK.
+ * The session must be in the playing phase.
+ * Return 0 when the session ended, -1 on error.
+ */
+int
+play(int sock)
+{
+    /*
+     * Player input flows from INPUT_FD through recv_input() into ring
+     * buffer INBUF, which drains into SOCK.  This must not block.
+     * Server output flows from SOCK into recv_output().  Reading SOCK
+     * must not block.
+     */
+    struct sigaction sa;
+    struct ring inbuf;         /* input buffer, draining to SOCK */
+    int eof_fd0;               /* read fd 0 hit EOF? */
+    int send_eof;              /* need to send EOF_COOKIE */
+    fd_set rdfd, wrfd;
+    int n;
+
+    sa.sa_flags = 0;
+    sa.sa_handler = intr;
+    sigaction(SIGINT, &sa, NULL);
+    sa.sa_handler = SIG_IGN;
+    sigaction(SIGPIPE, &sa, NULL);
+
+    ring_init(&inbuf);
+    eof_fd0 = send_eof = 0;
+
+    for (;;) {
+       FD_ZERO(&rdfd);
+       FD_ZERO(&wrfd);
+
+       /*
+        * Want to read player input only when we don't need to send
+        * cookies, and we haven't hit EOF on fd 0, and INBUF can
+        * accept some.
+        */
+       if (!send_intr && !send_eof && !eof_fd0 && ring_space(&inbuf))
+           FD_SET(input_fd, &rdfd);
+       /* Want to send player input only when we have something */
+       if (send_intr || send_eof || ring_len(&inbuf))
+           FD_SET(sock, &wrfd);
+       /* Always want to read server output */
+       FD_SET(sock, &rdfd);
+
+       n = select(MAX(input_fd, sock) + 1, &rdfd, &wrfd, NULL, NULL);
+       if (n < 0) {
+           if (errno != EINTR) {
+               perror("select");
+               return -1;
+           }
+       }
+
+       if (send_eof
+           && ring_putm(&inbuf, EOF_COOKIE, sizeof(EOF_COOKIE) - 1) >= 0)
+           send_eof = 0;
+       if (send_intr
+           && ring_putm(&inbuf, INTR_COOKIE, sizeof(INTR_COOKIE) - 1) >= 0)
+           send_intr = 0;
+
+       if (n < 0)
+           continue;
+
+       /* read player input */
+       if (FD_ISSET(input_fd, &rdfd)) {
+           n = recv_input(input_fd, &inbuf);
+           if (n < 0) {
+               perror("read stdin"); /* FIXME stdin misleading, could be execing */
+               return -1;
+           }
+           if (n == 0) {
+               /* EOF on input */
+               send_eof = 1;
+               if (input_fd) {
+                   /* execute done, switch back to fd 0 */
+                   input_fd = 0;
+               } else {
+                   /* stop reading input, drain socket ring buffers */
+                   eof_fd0 = 1;
+                   sa.sa_handler = SIG_DFL;
+                   sigaction(SIGINT, &sa, NULL);
+               }
+           }
+       }
+
+       /* send it to the server */
+       if (FD_ISSET(sock, &wrfd)) {
+           n = ring_to_file(&inbuf, sock);
+           if (n < 0) {
+               perror("write socket");
+               return -1;
+           }
+       }
+
+       /* read server output and print it */
+       if (FD_ISSET(sock, &rdfd)) {
+           n = recv_output(sock);
+           if (n < 0) {
+               perror("read socket");
+               return -1;
+           }
+           if (n == 0)
+               return 0;
+           if (input_fd < 0) {
+               /* execute failed */
+               input_fd = 0;
+               send_eof = 1;
+           }
+       }
+    }
+}
diff --git a/src/client/queue.c b/src/client/queue.c
deleted file mode 100644 (file)
index 557ff68..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- *  Empire - A multi-player, client/server Internet based war game.
- *  Copyright (C) 1986-2007, Dave Pare, Jeff Bailey, Thomas Ruschak,
- *                           Ken Stevens, Steve McClure
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program 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 General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- *  ---
- *
- *  See files README, COPYING and CREDITS in the root of the source
- *  tree for related information and legal notices.  It is expected
- *  that future projects/authors will amend these files as needed.
- *
- *  ---
- *
- *  queue.c: implementation of various queuing routines
- * 
- *  Known contributors to this file:
- *     Steve McClure, 1998
- */
-
-#include <config.h>
-
-#include "queue.h"
-
-void
-insque(struct qelem *p, struct qelem *q)
-{
-    p->q_forw = q->q_forw;
-    p->q_back = q;
-    q->q_forw->q_back = p;
-    q->q_forw = p;
-}
-
-void
-remque(struct qelem *p)
-{
-    p->q_back->q_forw = p->q_forw;
-    p->q_forw->q_back = p->q_back;
-}
-
-void
-initque(struct qelem *p)
-{
-    p->q_forw = p;
-    p->q_back = p;
-}
diff --git a/src/client/queue.h b/src/client/queue.h
deleted file mode 100644 (file)
index 9db972b..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- *  Empire - A multi-player, client/server Internet based war game.
- *  Copyright (C) 1986-2007, Dave Pare, Jeff Bailey, Thomas Ruschak,
- *                           Ken Stevens, Steve McClure
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program 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 General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- *  ---
- *
- *  See files README, COPYING and CREDITS in the root of the source
- *  tree for related information and legal notices.  It is expected
- *  that future projects/authors will amend these files as needed.
- *
- *  ---
- *
- *  queue.h: generic vax-like doubly linked list queues.
- * 
- *  Known contributors to this file:
- *  
- */
-
-#ifndef QUEUE_H
-#define QUEUE_H
-
-#define        QEMPTY(p)       ((p)->q_forw == (p))
-
-struct qelem {
-    struct qelem *q_forw;
-    struct qelem *q_back;
-};
-
-void insque(struct qelem *p, struct qelem *q);
-void remque(struct qelem *p);
-void initque(struct qelem *p);
-
-#endif
diff --git a/src/client/ringbuf.c b/src/client/ringbuf.c
new file mode 100644 (file)
index 0000000..b893bc1
--- /dev/null
@@ -0,0 +1,245 @@
+/*
+ *  Empire - A multi-player, client/server Internet based war game.
+ *  Copyright (C) 1986-2007, Dave Pare, Jeff Bailey, Thomas Ruschak,
+ *                           Ken Stevens, Steve McClure
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  ---
+ *
+ *  See files README, COPYING and CREDITS in the root of the source
+ *  tree for related information and legal notices.  It is expected
+ *  that future projects/authors will amend these files as needed.
+ *
+ *  ---
+ *
+ *  ringbuf.c: Simple ring buffer
+ * 
+ *  Known contributors to this file:
+ *     Markus Armbruster, 2007
+ */
+
+#include <config.h>
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include "ringbuf.h"
+
+/*
+ * Initialize empty ring buffer.
+ * Not necessary if *R is already zeroed.
+ */
+void
+ring_init(struct ring *r)
+{
+    r->cons = r->prod = 0;
+}
+
+/*
+ * Return number of bytes held in ring buffer.
+ */
+int
+ring_len(struct ring *r)
+{
+    assert(r->prod - r->cons <= RING_SIZE);
+    return r->prod - r->cons;
+}
+
+/*
+ * Return how much space is left in ring buffer.
+ */
+int
+ring_space(struct ring *r)
+{
+    return RING_SIZE - (r->prod - r->cons);
+}
+
+/*
+ * Peek at ring buffer contents.
+ * N must be between -RING_SIZE-1 and RING_SIZE.
+ * If N>=0, peek at the (N+1)-th byte to be gotten.
+ * If N<0, peek at the -N-th byte that has been put in.
+ * Return the byte as unsigned char coverted to int, or EOF if there
+ * aren't that many bytes in the ring buffer.
+ */
+int
+ring_peek(struct ring *r, int n)
+{
+    unsigned idx;
+
+    assert(-RING_SIZE - 1 <= n && n <= RING_SIZE);
+
+    idx = n >= 0 ? r->cons + n : r->prod - -n;
+    if (idx < r->cons && idx >= r->prod)
+       return EOF;
+    return r->buf[idx % RING_SIZE];
+}
+
+/*
+ * Get and remove the oldest byte from the ring buffer.
+ * Return it as unsigned char coverted to int, or EOF if the buffer was
+ * empty.
+ */
+int
+ring_getc(struct ring *r)
+{
+    if (r->cons == r->prod)
+       return EOF;
+    return r->buf[r->cons++ % RING_SIZE];
+}
+
+/*
+ * Attempt to put byte C into the ring buffer.
+ * Return EOF when the buffer is full, else C.
+ */
+int
+ring_putc(struct ring *r, unsigned char c)
+{
+    if (r->prod - r->cons == RING_SIZE)
+       return EOF;
+    return r->buf[r->prod++ % RING_SIZE] = c;
+}
+
+/*
+ * Attempt to put SZ bytes from BUF into the ring buffer.
+ * Return space left in ring buffer when it fits, else don't change
+ * the ring buffer and return how much space is missing.
+ */
+int
+ring_putm(struct ring *r, void *buf, size_t sz)
+{
+    char *p = buf;
+    int left = ring_space(r) - sz;
+    int res;
+    size_t i;
+
+    if (left >= 0) {
+       res = 0;
+       for (i = 0; i < sz; i++)
+           res = ring_putc(r, p[i]);
+       assert(res != EOF);
+    }
+
+    return left;
+}
+
+/*
+ * Discard the N oldest bytes from the ring buffer.
+ * It must hold at least that many.
+ */
+void
+ring_discard(struct ring *r, int n)
+{
+    assert(n <= ring_len(r));
+    r->cons += n;
+}
+
+/*
+ * Search the ring buffer for zero-terminated string S.
+ * If found, return a non-negative value referring to the beginning of
+ * S in the buffer when passed to ring_peek().  Else return -1.
+ */
+int
+ring_search(struct ring *r, char *s)
+{
+    size_t len = strlen(s);
+    size_t i, j;
+
+    for (i = r->cons; i + len <= r->prod; i++) {
+       for (j = 0; j < len && s[j] == r->buf[(i + j) % RING_SIZE]; j++) ;
+       if (!s[j])
+           return i - r->cons;
+    }
+    return -1;
+}
+
+/*
+ * Fill ring buffer from file referred by file descriptor FD.
+ * If ring buffer is already full, do nothing and return 0.
+ * Else attempt to read as many bytes as space permits, with readv(),
+ * and return its value.
+ */
+int
+ring_from_file(struct ring *r, int fd)
+{
+    unsigned cons = r->cons % RING_SIZE;
+    unsigned prod = r->prod % RING_SIZE;
+    struct iovec iov[2];
+    int cnt;
+    ssize_t res;
+
+    if (r->prod == r->cons + RING_SIZE)
+       return 0;
+
+    iov[0].iov_base = r->buf + prod;
+    if (cons <= prod) {
+       /* r->buf[prod..] */
+       iov[0].iov_len = RING_SIZE - prod;
+       /* r->buf[..cons-1] */
+       iov[1].iov_base = r->buf;
+       iov[1].iov_len = cons;
+       cnt = 2;
+    } else {
+       /* r->buf[prod..cons-1] */
+       iov[0].iov_len = cons - prod;
+       cnt = 1;
+    }
+    res = readv(fd, iov, cnt);
+    if (res < 0)
+       return res;
+    r->prod += res;
+    return res;
+}
+
+/*
+ * Drain ring buffer to file referred by file descriptor FD.
+ * If ring buffer is already empty, do nothing and return 0.
+ * Else attempt to write complete contents with writev(), and return
+ * its value.
+ */
+int
+ring_to_file(struct ring *r, int fd)
+{
+    unsigned cons = r->cons % RING_SIZE;
+    unsigned prod = r->prod % RING_SIZE;
+    struct iovec iov[2];
+    int cnt;
+    ssize_t res;
+
+    if (r->cons == r->prod)
+       return 0;
+
+    iov[0].iov_base = r->buf + cons;
+    if (prod <= cons) {
+       /* r->buf[cons..] */
+       iov[0].iov_len = RING_SIZE - cons;
+       /* r->buf[..prod-1] */
+       iov[1].iov_base = r->buf;
+       iov[1].iov_len = prod;
+       cnt = 2;
+    } else {
+       /* r->buf[cons..prod-1] */
+       iov[0].iov_len = prod - cons;
+       cnt = 1;
+    }
+    res = writev(fd, iov, cnt);
+    if (res < 0)
+       return res;
+    r->cons += res;
+    return res;
+}
diff --git a/src/client/ringbuf.h b/src/client/ringbuf.h
new file mode 100644 (file)
index 0000000..0dc0866
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ *  Empire - A multi-player, client/server Internet based war game.
+ *  Copyright (C) 1986-2007, Dave Pare, Jeff Bailey, Thomas Ruschak,
+ *                           Ken Stevens, Steve McClure
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  ---
+ *
+ *  See files README, COPYING and CREDITS in the root of the source
+ *  tree for related information and legal notices.  It is expected
+ *  that future projects/authors will amend these files as needed.
+ *
+ *  ---
+ *
+ *  ringbuf.h: Simple ring buffer
+ * 
+ *  Known contributors to this file:
+ *     Markus Armbruster, 2007
+ */
+
+#ifndef RINGBUF_H
+#define RINGBUF_H
+
+#include <stddef.h>
+
+#define RING_SIZE 4096
+
+/* Ring buffer, consumer reads, producer writes */
+struct ring {
+    /* All members are private! */
+    /*
+     * Consumer reads from buf[cons % RING_SIZE], incrementing cons
+     * Produces writes to buf[prod % RING_SIZE], incrementing prod
+     * prod == cons: empty
+     * prod == cons + RING_SIZE: full
+     * invariant: prod - cons <= RING_SIZE
+     */
+    unsigned cons, prod;
+    unsigned char buf[RING_SIZE];
+};
+
+extern void ring_init(struct ring *);
+extern int ring_len(struct ring *);
+extern int ring_space(struct ring *);
+extern int ring_peek(struct ring *, int);
+extern int ring_getc(struct ring *);
+extern int ring_putc(struct ring *, unsigned char);
+extern int ring_putm(struct ring *, void *, size_t);
+extern void ring_discard(struct ring *, int);
+extern int ring_search(struct ring *, char *);
+extern int ring_from_file(struct ring *, int fd);
+extern int ring_to_file(struct ring *, int fd);
+
+#endif
diff --git a/src/client/secure.c b/src/client/secure.c
new file mode 100644 (file)
index 0000000..258ee31
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ *  Empire - A multi-player, client/server Internet based war game.
+ *  Copyright (C) 1986-2007, Dave Pare, Jeff Bailey, Thomas Ruschak,
+ *                           Ken Stevens, Steve McClure
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  ---
+ *
+ *  See files README, COPYING and CREDITS in the root of the source
+ *  tree for related information and legal notices.  It is expected
+ *  that future projects/authors will amend these files as needed.
+ *
+ *  ---
+ *
+ *  secure.c: Check redir etc. to protect against tampering deity
+ * 
+ *  Known contributors to this file:
+ *     Markus Armbruster, 2007
+ */
+
+#include <config.h>
+
+#include <string.h>
+#include "ringbuf.h"
+#include "secure.h"
+
+struct ring recent_input;
+
+void
+save_input(char *inp)
+{
+    size_t len = strlen(inp);
+    int left;
+
+    left = ring_putm(&recent_input, inp, len);
+    if (left < 0) {
+       ring_discard(&recent_input, ring_search(&recent_input, "\n"));
+       ring_putm(&recent_input, inp, len);
+    }
+}
+
+int
+seen_input(char *tail)
+{
+    int dist = ring_search(&recent_input, tail);
+
+    if (dist < 0)
+       return 0;
+
+    ring_discard(&recent_input, dist + strlen(tail));
+    return 1;
+}
diff --git a/src/client/secure.h b/src/client/secure.h
new file mode 100644 (file)
index 0000000..a92026e
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ *  Empire - A multi-player, client/server Internet based war game.
+ *  Copyright (C) 1986-2007, Dave Pare, Jeff Bailey, Thomas Ruschak,
+ *                           Ken Stevens, Steve McClure
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  ---
+ *
+ *  See files README, COPYING and CREDITS in the root of the source
+ *  tree for related information and legal notices.  It is expected
+ *  that future projects/authors will amend these files as needed.
+ *
+ *  ---
+ *
+ *  secure.h: Check redir etc. to protect against tampering deity
+ * 
+ *  Known contributors to this file:
+ *     Markus Armbruster, 2007
+ */
+
+#ifndef SECURE_H
+#define SECURE_H
+
+extern void clear_recent_input(void);
+extern void save_input(char *);
+extern int seen_input(char *);
+
+#endif
index 24bae60844c9e544e249c268d191df91048f8b9c..f4129b138a6be605eee05ce3ab4282587f00c74b 100644 (file)
 
 #include <config.h>
 
+#include <assert.h>
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <stdio.h>
-#include <stdlib.h>
 #include <string.h>
-#ifdef _WIN32
-#include <io.h>
-#else
-#include <unistd.h>
-#endif
-
-#include "ioqueue.h"
 #include "misc.h"
 #include "proto.h"
-#include "tags.h"
+#include "secure.h"
+
+int eight_bit_clean;
+FILE *auxfp;
 
-static char num_teles[64];
-static char the_prompt[1024];
-static int mode;
-static int nbtu;
-static int nmin;
 static FILE *redir_fp;
 static FILE *pipe_fp;
 
-static void prompt(FILE *auxfi);
+static void prompt(int, char *, char *);
 static void doredir(char *p);
 static void dopipe(char *p);
-static void doexecute(char *p, FILE *auxfi);
-static void output(int code, char *buf, FILE *auxfi);
-static void screen(char *buf);
+static void doexecute(char *p);
 
 void
-servercmd(struct ioqueue *ioq, FILE *auxfi)
+servercmd(int code, char *arg, int len)
 {
-    char buf[1024];
-    char *p;
-    static int code = -1;
-    int eol;
+    static int nmin, nbtu;
+    static char the_prompt[1024];
+    static char teles[64];
 
-    while (ioq_gets(ioq, buf, sizeof(buf), &eol)) {
-       p = buf;
-       if (code == -1) {
-           if (isalpha(*buf))
-               code = 10 + (*buf - 'a');
-           else
-               code = *buf - '0';
-           while (*p && !isspace(*p))
-               p++;
-           *p++ = 0;
+    switch (code) {
+    case C_PROMPT:
+       if (sscanf(arg, "%d %d", &nmin, &nbtu) != 2) {
+           fprintf(stderr, "prompt: bad server prompt %s\n", arg);
        }
-       /*
-        * FIXME
-        * C_REDIR, C_PIPE, and C_EXECUTE will not
-        * work with filename longer than one buffer
-        */
-       switch (code) {
-       case C_PROMPT:
-           if (sscanf(p, "%d %d", &nmin, &nbtu) != 2) {
-               fprintf(stderr, "prompt: bad server prompt %s\n", p);
+       snprintf(the_prompt, sizeof(the_prompt), "[%d:%d] Command : ",
+                nmin, nbtu);
+       prompt(code, the_prompt, teles);
+       break;
+    case C_FLUSH:
+       snprintf(the_prompt, sizeof(the_prompt), "%.*s", len - 1, arg);
+       prompt(code, the_prompt, teles);
+       break;
+    case C_EXECUTE:
+       doexecute(arg);
+       break;
+    case C_EXIT:
+       printf("Exit: %s", arg);
+       if (auxfp)
+           fprintf(auxfp, "Exit: %s", arg);
+       break;
+    case C_FLASH:
+       printf("\n%s", arg);
+       if (auxfp)
+           fprintf(auxfp, "\n%s", arg);
+       break;
+    case C_INFORM:
+       if (*arg) {
+           snprintf(teles, sizeof(teles), "(%.*s )", len -1, arg);
+           if (!redir_fp && !pipe_fp) {
+               putchar('\07');
+               prompt(code, the_prompt, teles);
            }
-           mode = code;
-           sprintf(the_prompt, "[%d:%d] Command : ", nmin, nbtu);
-           prompt(auxfi);
-           break;
-       case C_REDIR:
-           if (eol)
-               p[strlen(p) - 1] = '\0';
-           doredir(p);
-           break;
-       case C_PIPE:
-           if (eol)
-               p[strlen(p) - 1] = '\0';
-           dopipe(p);
-           break;
-       case C_FLUSH:
-           mode = code;
-           if (eol)
-               p[strlen(p) - 1] = '\0';
-           sprintf(the_prompt, "%s", p);
-           prompt(auxfi);
-           break;
-       case C_EXECUTE:
-           if (eol)
-               p[strlen(p) - 1] = '\0';
-           doexecute(p, auxfi);
-           break;
-       case C_INFORM:
-           if (eol)
-               p[strlen(p) - 1] = '\0';
-           if (*p) {
-               p[strlen(p) - 1] = '\0';
-               sprintf(num_teles, "(%s) ", p + 1);
-               if (!redir_fp && !pipe_fp) {
-                   putchar('\07');
-                   prompt(NULL);
-               }
-           } else
-               *num_teles = '\0';
-           break;
-       default:
-           output(code, p, auxfi);
-           break;
-       }
-       if (eol)
-           code = -1;
+       } else
+           teles[0] = 0;
+       break;
+    case C_PIPE:
+       dopipe(arg);
+       break;
+    case C_REDIR:
+       doredir(arg);
+       break;
+    default:
+       assert(0);
+       break;
     }
 }
 
 static void
-prompt(FILE *auxfi)
+prompt(int code, char *prompt, char *teles)
 {
-    if (mode == C_PROMPT) {
+    char *nl;
+
+    if (code == C_PROMPT) {
        if (redir_fp) {
            (void)fclose(redir_fp);
            redir_fp = NULL;
@@ -156,13 +125,12 @@ prompt(FILE *auxfi)
            pipe_fp = NULL;
        }
     }
-    if (mode == C_PROMPT)
-       printf("\n");
-    printf("%s%s", num_teles, the_prompt);
-    (void)fflush(stdout);
-    if (auxfi) {
-       fprintf(auxfi, "\n%s%s", num_teles, the_prompt);
-       (void)fflush(auxfi);
+    nl = code == C_PROMPT || code == C_INFORM ? "\n" : "";
+    printf("%s%s%s", nl, teles, prompt);
+    fflush(stdout);
+    if (auxfp) {
+       fprintf(auxfp, "%s%s%s", nl, teles, prompt);
+       fflush(auxfp);
     }
 }
 
@@ -183,7 +151,6 @@ fname(char *s)
 static void
 doredir(char *p)
 {
-    char *tag;
     int mode;
     int fd;
 
@@ -192,6 +159,12 @@ doredir(char *p)
        redir_fp = NULL;
     }
 
+    if (!seen_input(p)) {
+       fprintf(stderr, "WARNING!  Server attempted to redirect %s\n",
+               p);
+       return;
+    }
+
     if (*p++ != '>') {
        fprintf(stderr, "WARNING!  Weird redirection %s", p);
        return;
@@ -207,19 +180,12 @@ doredir(char *p)
     } else
        mode |= O_EXCL;
 
-    tag = gettag(p);
     p = fname(p);
-    if (tag == NULL) {
-       fprintf(stderr, "WARNING!  Server redirected output to file %s\n",
-               p);
-       return;
-    }
-    free(tag);
-
     if (*p == 0) {
        fprintf(stderr, "Redirection lacks a file name\n");
        return;
     }
+
     fd = open(p, mode, 0600);
     redir_fp = fd < 0 ? NULL : fdopen(fd, "w");
     if (!redir_fp) {
@@ -234,25 +200,22 @@ doredir(char *p)
 static void
 dopipe(char *p)
 {
-    char *tag;
-
-    if (*p++ != '|') {
-       fprintf(stderr, "WARNING!  Weird pipe %s", p);
+    if (!seen_input(p)) {
+       fprintf(stderr, "WARNING!  Server attempted to pipe %s", p);
        return;
     }
 
-    tag = gettag(p);
-    if (tag == NULL) {
-       fprintf(stderr, "WARNING!  Server attempted to run: %s\n", p);
+    if (*p++ != '|') {
+       fprintf(stderr, "WARNING!  Weird pipe %s", p);
        return;
     }
-    free(tag);
 
     for (; *p && isspace(*p); p++) ;
     if (*p == 0) {
        fprintf(stderr, "Redirection lacks a command\n");
        return;
     }
+
     if ((pipe_fp = popen(p, "w")) == NULL) {
        fprintf(stderr, "Can't redirect to pipe %s: %s\n",
                p, strerror(errno));
@@ -260,103 +223,48 @@ dopipe(char *p)
 }
 
 static void
-doexecute(char *p, FILE *auxfi)
+doexecute(char *p)
 {
-    int fd;
-    char *tag;
-
-    tag = gettag(p);
-    if (tag == NULL) {
+    if (!seen_input(p)) {
        fprintf(stderr,
-               "WARNING!  Server attempted unauthorized read of file %s\n",
+               "WARNING!  Server attempted to read file %s",
                p);
        return;
     }
-    free(tag);
 
     p = fname(p);
     if (*p == 0) {
        fprintf(stderr, "Need a file to execute\n");
        return;
     }
-    if ((fd = open(p, O_RDONLY, 0)) < 0) {
+
+    if ((input_fd = open(p, O_RDONLY)) < 0) {
        fprintf(stderr, "Can't open execute file %s: %s\n",
                p, strerror(errno));
        return;
     }
-    /* copies 4k at a time to the socket */
-    while (termio(fd, sock, auxfi))    /*do copy */
-       ;
-    /*
-     * Some platforms don't send the eof (cntl-D) at the end of
-     * copying a file.  If emp_client hangs at the end of an
-     * execute, include the following line and notify wolfpack
-     * of the platform you are using.
-     * sendeof(sock);
-     */
-    close(fd);
 }
 
-static void
-output(int code, char *buf, FILE *auxfi)
+void
+outch(char c)
 {
-    switch (code) {
-    case C_NOECHO:
-       /* not implemented; server doesn't send it */
-       break;
-    case C_ABORT:
-       printf("Aborted\n");
-       if (auxfi)
-           fprintf(auxfi, "Aborted\n");
-       break;
-    case C_CMDERR:
-    case C_BADCMD:
-       printf("Error; ");
-       if (auxfi)
-           fprintf(auxfi, "Error; ");
-       break;
-    case C_EXIT:
-       printf("Exit: ");
-       if (auxfi)
-           fprintf(auxfi, "Exit: ");
-       break;
-    case C_FLASH:
-       printf("\n");
-       break;
-    default:
-       break;
-    }
-    if (auxfi) {
-       fprintf(auxfi, "%s", buf);
-    }
-
+    if (auxfp)
+       putc(c, auxfp);
     if (redir_fp)
-       fprintf(redir_fp, "%s", buf);
+       putc(c, redir_fp);
     else if (pipe_fp)
-       fprintf(pipe_fp, "%s", buf);
-    else {
-       screen(buf);
-    }
-}
-
-static void
-screen(char *buf)
-{
-    char c;
-
-    while ((c = *buf++)) {
-       if (eight_bit_clean) {
-           if (c == 14)
-               putso();
-           else if (c == 15)
-               putse();
-           else
-               putchar(c);
-       } else if (c & 0x80) {
+       putc(c, pipe_fp);
+    else if (eight_bit_clean) {
+       if (c == 14)
            putso();
-           putchar(c & 0x7f);
+       else if (c == 15)
            putse();
-       else
+       else
            putchar(c);
-    }
+    } else if (c & 0x80) {
+       putso();
+       putchar(c & 0x7f);
+       putse();
+    } else
+       putchar(c);
 }
diff --git a/src/client/serverio.c b/src/client/serverio.c
deleted file mode 100644 (file)
index 7df0ae1..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- *  Empire - A multi-player, client/server Internet based war game.
- *  Copyright (C) 1986-2007, Dave Pare, Jeff Bailey, Thomas Ruschak,
- *                           Ken Stevens, Steve McClure
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program 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 General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- *  ---
- *
- *  See files README, COPYING and CREDITS in the root of the source
- *  tree for related information and legal notices.  It is expected
- *  that future projects/authors will amend these files as needed.
- *
- *  ---
- *
- *  serverio.c: Handle input from server
- * 
- *  Known contributors to this file:
- *     Steve McClure, 1998
- */
-
-#include <config.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#if !defined(_WIN32)
-#include <unistd.h>
-#endif
-#include "misc.h"
-#include "ioqueue.h"
-
-int
-serverio(int s, struct ioqueue *ioq)
-{
-    char *buf;
-    int n;
-
-    if ((buf = malloc(ioq->bsize)) == NULL) {
-       fprintf(stderr, "malloc server i/o failed\n");
-       return 0;
-    }
-#ifdef _WIN32
-    n = recv(s, buf, ioq->bsize, 0);
-#else
-    n = read(s, buf, ioq->bsize);
-#endif
-    if (n < 0) {
-#ifdef _WIN32
-       errno = WSAGetLastError();
-#endif
-       perror("server i/o read");
-       free(buf);
-       return 0;
-    }
-    if (n == 0) {
-       fprintf(stderr, "Server EOF\n");
-#ifdef WIN32
-       (void)closesocket(s);
-#else
-       (void)close(s);
-#endif
-       return 0;
-    }
-    if (n != ioq->bsize)
-       buf = realloc(buf, n);
-    ioq_write(ioq, buf, n);
-    return 1;
-}
diff --git a/src/client/tags.c b/src/client/tags.c
deleted file mode 100644 (file)
index 9713910..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- *  Empire - A multi-player, client/server Internet based war game.
- *  Copyright (C) 1986-2007, Dave Pare, Jeff Bailey, Thomas Ruschak,
- *                           Ken Stevens, Steve McClure
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program 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 General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- *  ---
- *
- *  See files README, COPYING and CREDITS in the root of the source
- *  tree for related information and legal notices.  It is expected
- *  that future projects/authors will amend these files as needed.
- *
- *  ---
- *
- *  tags.c: save redir, execute and pipe info to protect against tampering
- *          by the deity.
- * 
- *  Known contributors to this file:
- *     John Yockey, 1998
- */
-
-#include <config.h>
-
-#include <stdlib.h>
-#include <string.h>
-#include "misc.h"
-#include "tags.h"
-
-struct tagstruct *taglist;
-
-void
-io_init(void)
-{
-    taglist = NULL;
-}
-
-char *
-gettag(char *p)
-{
-    struct tagstruct *tag1, *tag2;
-
-    if (taglist == NULL)
-       return NULL;
-    tag1 = taglist;
-    if (!strncmp(tag1->item, p, strlen(tag1->item))) {
-       p = tag1->item;
-       taglist = taglist->next;
-       free(tag1);
-       return p;
-    }
-    while (tag1->next != NULL) {
-       tag2 = tag1->next;
-       if (!strncmp(tag2->item, p, strlen(tag2->item))) {
-           p = tag2->item;
-           tag1->next = tag2->next;
-           free(tag2);
-           return p;
-       }
-       tag1 = tag1->next;
-    }
-    return NULL;
-}
diff --git a/src/client/tags.h b/src/client/tags.h
deleted file mode 100644 (file)
index 9da5e34..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- *  Empire - A multi-player, client/server Internet based war game.
- *  Copyright (C) 1986-2007, Dave Pare, Jeff Bailey, Thomas Ruschak,
- *                           Ken Stevens, Steve McClure
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program 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 General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- *  ---
- *
- *  See files README, COPYING and CREDITS in the root of the source
- *  tree for related information and legal notices.  It is expected
- *  that future projects/authors will amend these files as needed.
- *
- *  ---
- *
- *  tags.h: Tag structure definition
- * 
- *  Known contributors to this file:
- *     John Yockey, 1998
- */
-
-#ifndef TAGS_H
-#define TAGS_H
-
-struct tagstruct {
-    char *item;
-    struct tagstruct *next;
-};
-
-extern struct tagstruct *taglist;
-
-void io_init(void);
-char *gettag(char *p);
-
-#endif
diff --git a/src/client/termio.c b/src/client/termio.c
deleted file mode 100644 (file)
index 9819bde..0000000
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
- *  Empire - A multi-player, client/server Internet based war game.
- *  Copyright (C) 1986-2007, Dave Pare, Jeff Bailey, Thomas Ruschak,
- *                           Ken Stevens, Steve McClure
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program 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 General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- *  ---
- *
- *  See files README, COPYING and CREDITS in the root of the source
- *  tree for related information and legal notices.  It is expected
- *  that future projects/authors will amend these files as needed.
- *
- *  ---
- *
- *  termio.c: Various io functions
- * 
- *  Known contributors to this file:
- *     Steve McClure, 1998
- */
-
-#include <config.h>
-
-#include <ctype.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#ifdef _WIN32
-#include <io.h>
-#else
-#include <unistd.h>
-#endif
-#include "misc.h"
-#include "tags.h"
-
-static int sendeof(int sock);
-
-int
-termio(int fd, int sock, FILE *auxfi)
-{
-    static char exec[] = "execute";
-    static char buf[4096];
-    char out[4096];
-    int i, n;
-    char *ptr;
-    char *p, *q, *r, *s, *t;
-    int nbytes;
-    int prespace, exec_com, inarg, quoted, tagging;
-    struct tagstruct *tag;
-#ifdef _WIN32
-    char c;
-    INPUT_RECORD InpBuffer[2];
-    int ret;
-    DWORD records;
-#endif
-
-    i = strlen(buf);
-    p = buf + i;
-#ifndef _WIN32
-    n = read(fd, p, sizeof(buf) - i);
-#else
-/* The keyboard is sometimes generating both keydown and keyup
- * events for the same key.  Thus, we only want to grab keydown
- * events. */
-    if (fd == -1) {
-       ret = PeekConsoleInput(hStdIn, InpBuffer, 1, &records);
-       if (!ret) {
-           fprintf(stderr, "Error peeking the console input (%lu)\n",
-               GetLastError());
-           return 0;
-       }
-       if (records == 0)
-           return 0;
-       if (InpBuffer[0].EventType != KEY_EVENT) {
-           ret = ReadConsoleInput(hStdIn, InpBuffer, 1, &records);
-           if (!ret) {
-               fprintf(stderr, "Error reading the console input (%lu)\n",
-                   GetLastError());
-               return 0;
-           }
-           if (records == 0)
-               return 0;
-           return 1;
-       }
-       if (!InpBuffer[0].Event.KeyEvent.bKeyDown) {
-           ret = ReadConsoleInput(hStdIn, InpBuffer, 1, &records);
-           if (!ret) {
-               fprintf(stderr, "Error reading the console input (%lu)\n",
-                   GetLastError());
-               return 0;
-           }
-           if (records == 0)
-               return 0;
-           return 1;
-       }
-       c = InpBuffer[0].Event.KeyEvent.uChar.AsciiChar;
-
-       if (c == 13)
-           c = 10;
-       records = 1;
-       p[0] = c;
-       p[1] = '\0';
-       if (c != 10) {
-           ret = ReadConsole(hStdIn, p, sizeof(buf) - i, &records, NULL);
-           if (!ret) {
-               fprintf(stderr, "Error reading the console (%lu)\n",
-                   GetLastError());
-               return 0;
-           }
-       } else
-           putchar(c);
-/* Strip off the CRLF to just LF */
-       if (records > 1) {
-           if (p[records - 2] == 13 && p[records - 1] == 10) {
-               p[records - 2] = 10;
-               p[records - 1] = 0;
-               records--;
-           }
-       }
-       FlushConsoleInputBuffer(hStdIn);
-       if (records == 0)
-           return 0;
-       n = records;
-    } else if (fd == 0) {
-       if (feof(stdin)) {
-           sendeof(sock);
-           return 0;
-       }
-       fgets(p, sizeof(buf) - i, stdin);
-       n = strlen(p);
-    } else {
-       n = read(fd, p, sizeof(buf) - i);
-    }
-#endif
-    if (n == 0) {
-       sendeof(sock);
-       return 0;
-    }
-    if (n < 0) {
-       perror("read standard input");
-       return 0;
-    }
-    n += i;
-    ptr = buf;
-    p = buf;
-    q = out;
-    r = out;
-    tagging = 0;
-    inarg = 0;
-    prespace = 1;
-    quoted = 0;
-    exec_com = 0;
-    while (p < buf + n && q < out + 4000) {
-       if (*p == '\n') {
-           if (tagging) {
-               tag = malloc(sizeof(struct tagstruct));
-               tag->item = malloc(1 + p - s);
-               tag->next = taglist;
-               taglist = tag;
-               t = tag->item;
-               while (s < p)
-                   *t++ = *s++;
-               *t = 0;
-           }
-           *q++ = *p++;
-           tagging = 0;
-           inarg = 0;
-           prespace = 1;
-           quoted = 0;
-           exec_com = 0;
-           ptr = p;
-           r = q;
-       } else if (tagging) {
-           *q++ = *p++;
-       } else if (!quoted && isspace(*p)) {
-           *q++ = *p++;
-           prespace = 1;
-           if (exec_com && s > exec + 2) {
-               tagging = 1;
-               s = p;
-           }
-       } else if (prespace && *p == '|') {
-           tagging = 1;
-           *q++ = *p++;
-           s = p;
-       } else if (prespace && *p == '>') {
-           tagging = 1;
-           *q++ = *p++;
-           if (*p != '\n' && (*p == '!' || *p == '>'))
-               *q++ = *p++;
-           s = p;
-       } else {
-           prespace = 0;
-           if (*p == '"') {
-               quoted = !quoted;
-           } else {
-               if (!inarg && *p != '?') {
-                   s = exec;
-                   exec_com = 1;
-                   inarg = 1;
-               }
-               if (exec_com && *s && *s++ != *p)
-                   exec_com = 0;
-           }
-           *q++ = *p++;
-       }
-    }
-    p = buf;
-    while (ptr < buf + n)
-       *p++ = *ptr++;
-    *p = 0;
-    ptr = out;
-    n = r - out;
-    if (auxfi) {
-       fwrite(out, n, 1, auxfi);
-    }
-    while (n > 0) {
-#ifndef _WIN32
-       nbytes = write(sock, ptr, n);
-#else
-       nbytes = send(sock, ptr, n, 0);
-#endif
-       if (nbytes <= 0) {
-#ifdef _WIN32
-           errno = WSAGetLastError();
-#endif
-           perror("write server socket");
-           return 0;
-       }
-       ptr += nbytes;
-       n -= nbytes;
-    }
-    return 1;
-}
-
-static int
-sendeof(int sock)
-{
-#ifndef _WIN32
-    if (write(sock, "ctld\n", 5) < 5) {
-#else
-    if (send(sock, "ctld\n", 5, 0) < 5) {
-#endif
-       fprintf(stderr, "sendeof: EOF send failed\n");
-#ifdef _WIN32
-       closesocket(sock);
-#else
-       close(sock);
-#endif
-       return 0;
-    }
-    return 1;
-}