aboutsummaryrefslogtreecommitdiffstats
path: root/src/beam.c
diff options
context:
space:
mode:
authorSteve Slaven <bpk@hoopajoo.net>2019-11-05 06:26:14 (GMT)
committerSteve Slaven <bpk@hoopajoo.net>2019-11-05 06:26:14 (GMT)
commit9c4c0a1e366b9d932e4ab2ce03a0e80126d93d9b (patch)
tree928e4c6f49ac50f7e69777b00073df37d7d11e3f /src/beam.c
parenteb9898c7fcc017a35c240c1bd83c8a8ff451431a (diff)
downloadpowwow-9c4c0a1e366b9d932e4ab2ce03a0e80126d93d9b.zip
powwow-9c4c0a1e366b9d932e4ab2ce03a0e80126d93d9b.tar.gz
powwow-9c4c0a1e366b9d932e4ab2ce03a0e80126d93d9b.tar.bz2
reorganizing files
Diffstat (limited to 'src/beam.c')
-rw-r--r--src/beam.c480
1 files changed, 480 insertions, 0 deletions
diff --git a/src/beam.c b/src/beam.c
new file mode 100644
index 0000000..cff503d
--- /dev/null
+++ b/src/beam.c
@@ -0,0 +1,480 @@
+/*
+ * beam.c -- code to beam texts across the TCP connection following a
+ * special protocol
+ *
+ * Copyright (C) 1998 by Massimiliano Ghilardi
+ *
+ * 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.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <arpa/telnet.h>
+
+#include "defines.h"
+#include "main.h"
+#include "utils.h"
+#include "beam.h"
+#include "map.h"
+#include "tcp.h"
+#include "tty.h"
+#include "edit.h"
+#include "eval.h"
+
+editsess *edit_sess; /* head of session list */
+
+char edit_start[BUFSIZE]; /* messages to send to host when starting */
+char edit_end[BUFSIZE]; /* or leaving editing sessions */
+
+static void write_message(char *s)
+{
+ clear_input_line(opt_compact);
+ if (!opt_compact) {
+ tty_putc('\n');
+ status(1);
+ }
+ tty_puts(s);
+}
+
+/*
+ * Process editing protocol message from buf with len remaining chars.
+ * Return number of characters used in the message.
+ */
+int process_message(char *buf, int len)
+{
+ int msglen, i, l, used;
+ char *text, *from, *to;
+ char msg[BUFSIZE];
+ int got_iac;
+
+ status(1);
+
+ msglen = atoi(buf + 1);
+ for (i = 1; i < len && isdigit(buf[i]); i++)
+ ;
+
+ if (i < len && buf[i] == '\r') i++;
+ if (i >= len || buf[i] != '\n') {
+ write_message ("#warning: MPI protocol error\n");
+ return 0;
+ }
+
+ l = len - ++i;
+
+ text = (char *)malloc(msglen);
+ if (!text) {
+ errmsg("malloc");
+ return i;
+ }
+
+ /* only doing trivial (IAC IAC) processing; this should all be
+ * rewritten */
+ got_iac = 0;
+ from = buf + i;
+ to = text;
+ for (i = 0; i < l && (to - text) < msglen; ++i) {
+ if (got_iac) {
+ if (*from != (char)IAC)
+ errmsg("got IAC in MPI message; treating as IAC IAC");
+ else
+ ++from;
+ got_iac = 0;
+ } else {
+ got_iac = (*to++ = *from++) == (char)IAC;
+ }
+ }
+ i = to - text;
+ used = from - buf;
+
+ while (i < msglen) {
+ /* read() might block here, but it should't be long */
+ while ((l = read(tcp_fd, text + i, msglen - i)) == -1 && errno == EINTR)
+ ;
+ if (l == -1) {
+ errmsg("read message from socket");
+ return used > len ? len : used;
+ }
+ from = to = text + i;
+ for (i = 0; i < l; ++i) {
+ if (got_iac) {
+ if (*from != (char)IAC)
+ errmsg("got IAC in MPI message; treating as IAC IAC");
+ else
+ ++from;
+ got_iac = 0;
+ } else {
+ got_iac = (*to++ = *from++) == (char)IAC;
+ }
+ }
+ i = to - text;
+ tty_printf("\rgot %d chars out of %d", i, msglen);
+ tty_flush();
+ }
+ tty_printf("\rread all %d chars.%s\n", msglen, tty_clreoln);
+
+ switch(*buf) {
+ case 'E':
+ message_edit(text, msglen, 0, 0);
+ break;
+ case 'V':
+ message_edit(text, msglen, 1, 0);
+ break;
+ default:
+ sprintf(msg, "#warning: received a funny message (0x%x)\n", *buf);
+ write_message(msg);
+ free(text);
+ break;
+ }
+
+ return used;
+}
+
+/*
+ * abort an editing session when
+ * the MUD socket it comes from gets closed
+ */
+void abort_edit_fd(int fd)
+{
+ editsess **sp, *p;
+
+ if (fd < 0)
+ return;
+
+ for (sp = &edit_sess; *sp; sp = &(*sp)->next) {
+ p = *sp;
+ if (p->fd != fd)
+ continue;
+
+ if (kill(p->pid, SIGKILL) < 0) { /* Editicide */
+ errmsg("kill editor child");
+ continue;
+ }
+
+ PRINTF("#aborted \"%s\" (%u)\n", p->descr, p->key);
+ p->cancel = 1;
+ }
+}
+
+/*
+ * cancel an editing session; does not free anything
+ * (the child death signal handler will remove the session from the list)
+ */
+void cancel_edit(editsess *sp)
+{
+ char buf[BUFSIZE];
+ char keystr[16];
+
+ if (kill(sp->pid, SIGKILL) < 0) { /* Editicide */
+ errmsg("kill editor child");
+ return;
+ }
+ PRINTF("#killed \"%s\" (%u)\n", sp->descr, sp->key);
+ sprintf(keystr, "C%u\n", sp->key);
+ sprintf(buf, "%sE%d\n%s", MPI, (int)strlen(keystr), keystr);
+ tcp_write(sp->fd, buf);
+ sp->cancel = 1;
+}
+
+static ssize_t read_file(int fd, void *buf, size_t count)
+{
+ size_t result = 0;
+ while (count > 0) {
+ int r;
+ do {
+ r = read(fd, buf, count);
+ } while (r < 0 && errno == EINTR);
+ if (r < 0)
+ return -1;
+ if (r == 0)
+ break;
+ result += r;
+ buf += r;
+ count -= r;
+ }
+ return result;
+}
+
+/*
+ * send back edited text to server, or cancel the editing session if the
+ * file was not changed.
+ */
+static void finish_edit(editsess *sp)
+{
+ char *realtext = NULL, *text;
+ int fd, txtlen, hdrlen;
+ struct stat sbuf;
+ char keystr[16], buf[256], hdr[65];
+
+ if (sp->fd == -1)
+ goto cleanup_file;
+
+ fd = open(sp->file, O_RDONLY);
+ if (fd == -1) {
+ errmsg("open edit file");
+ goto cleanup_file;
+ }
+ if (fstat(fd, &sbuf) == -1) {
+ errmsg("fstat edit file");
+ goto cleanup_fd;
+ }
+
+ txtlen = sbuf.st_size;
+
+ if (!sp->cancel && (sbuf.st_mtime > sp->ctime || txtlen != sp->oldsize)) {
+ /* file was changed by editor: send back result to server */
+
+ realtext = (char *)malloc(txtlen + 65);
+ /* +1 is for possible LF, +64 for header */
+ if (!realtext) {
+ errmsg("malloc");
+ goto cleanup_fd;
+ }
+
+ text = realtext + 64;
+ txtlen = read_file(fd, text, txtlen);
+ if (txtlen < 0)
+ goto cleanup_text;
+
+ if (txtlen && text[txtlen - 1] != '\n') {
+ /* If the last line isn't LF-terminated, add an LF;
+ * however, empty files must remain empty */
+ text[txtlen] = '\n';
+ txtlen++;
+ }
+
+ sprintf(keystr, "E%u\n", sp->key);
+
+ sprintf(hdr, "%sE%d\n%s", MPI, (int)(txtlen + strlen(keystr)), keystr);
+
+ text -= (hdrlen = strlen(hdr));
+ memcpy(text, hdr, hdrlen);
+
+ /* text[hdrlen + txtlen] = '\0'; */
+ tcp_write_escape_iac(sp->fd, text, hdrlen + txtlen);
+
+ sprintf(buf, "#completed session %s (%u)\n", sp->descr, sp->key);
+ write_message(buf);
+ } else {
+ /* file wasn't changed, cancel editing session */
+ sprintf(keystr, "C%u\n", sp->key);
+ sprintf(hdr, "%sE%d\n%s", MPI, (int) strlen(keystr), keystr);
+
+ tcp_raw_write(sp->fd, hdr, strlen(hdr));
+
+ sprintf(buf, "#cancelled session %s (%u)\n", sp->descr, sp->key);
+ write_message(buf);
+ }
+
+cleanup_text: if (realtext) free(realtext);
+cleanup_fd: close(fd);
+cleanup_file: if (unlink(sp->file) < 0)
+ errmsg("unlink edit file");
+}
+
+/*
+ * start an editing session: process the EDIT/VIEW message
+ * if view == 1, text will be viewed, else edited
+ */
+void message_edit(char *text, int msglen, char view, char builtin)
+{
+ char tmpname[BUFSIZE], command_str[BUFSIZE], buf[BUFSIZE];
+ char *errdesc = "#warning: protocol error (message_edit, no %s)\n";
+ int tmpfd, i, childpid;
+ unsigned int key;
+ editsess *s;
+ char *editor, *descr;
+ char *args[4];
+ int waitforeditor;
+
+ status(1);
+
+ args[0] = "/bin/sh"; args[1] = "-c";
+ args[2] = command_str; args[3] = 0;
+
+ if (view) {
+ key = (unsigned int)-1;
+ i = 0;
+ } else {
+ if (msglen < 1 || text[0] != 'M') {
+ tty_printf(errdesc, "M");
+ free(text);
+ return;
+ }
+ for (i = 1; i < msglen && isdigit(text[i]); i++)
+ ;
+ if (text[i++] != '\n' || i >= msglen) {
+ tty_printf(errdesc, "\\n");
+ free(text);
+ return;
+ }
+ key = strtoul(text + 1, NULL, 10);
+ }
+ descr = text + i;
+ while (i < msglen && text[i] != '\n') i++;
+ if (i >= msglen) {
+ tty_printf(errdesc, "desc");
+ free(text);
+ return;
+ }
+ text[i++] = '\0';
+
+ sprintf(tmpname, "/tmp/powwow.%u.%d%d", key, getpid(), abs(rand()) >> 8);
+ if ((tmpfd = open(tmpname, O_WRONLY | O_CREAT, 0600)) < 0) {
+ errmsg("create temp edit file");
+ free(text);
+ return;
+ }
+ if (write(tmpfd, text + i, msglen - i) < msglen - i) {
+ errmsg("write to temp edit file");
+ free(text);
+ close(tmpfd);
+ return;
+ }
+ close(tmpfd);
+
+ s = (editsess*)malloc(sizeof(editsess));
+ if (!s) {
+ errmsg("malloc");
+ return;
+ }
+ s->ctime = time((time_t*)NULL);
+ s->oldsize = msglen - i;
+ s->key = key;
+ s->fd = (view || builtin) ? -1 : tcp_fd; /* MUME doesn't expect a reply. */
+ s->cancel = 0;
+ s->descr = my_strdup(descr);
+ s->file = my_strdup(tmpname);
+ free(text);
+
+ /* send a edit_start message (if wanted) */
+ if ((!edit_sess) && (*edit_start)) {
+ error = 0;
+ parse_instruction(edit_start, 0, 0, 1);
+ history_done = 0;
+ }
+
+ if (view) {
+ if (!(editor = getenv("POWWOWPAGER")) && !(editor = getenv("PAGER")))
+ editor = "more";
+ } else {
+ if (!(editor = getenv("POWWOWEDITOR")) && !(editor = getenv("EDITOR")))
+ editor = "emacs";
+ }
+
+ if (editor[0] == '&') {
+ waitforeditor = 0;
+ editor++;
+ } else
+ waitforeditor = 1;
+
+ if (waitforeditor) {
+ tty_quit();
+ /* ignore SIGINT since interrupting the child would interrupt us too,
+ if we are in the same tty group */
+ sig_permanent(SIGINT, SIG_IGN);
+ sig_permanent(SIGCHLD, SIG_DFL);
+ }
+
+ switch(childpid = fork()) { /* let's get schizophrenic */
+ case 0:
+ sprintf(command_str, "%s %s", editor, s->file);
+ sprintf(buf, "TITLE=%s", s->descr);
+ putenv(buf);
+ /* setenv("TITLE", s->descr, 1);*/
+ execvp((char *)args[0], (char **)args);
+ syserr("execve");
+ break;
+ case -1:
+ errmsg("fork");
+ free(s->descr);
+ free(s->file);
+ free(s);
+ return;
+ }
+ s->pid = childpid;
+ if (waitforeditor) {
+ while ((i = waitpid(childpid, (int*)NULL, 0)) == -1 && errno == EINTR)
+ ;
+
+ signal_start(); /* reset SIGINT and SIGCHLD handlers */
+ tty_start();
+
+ if (s->fd != -1) {
+ tty_gotoxy(0, lines - 1);
+ tty_putc('\n');
+ }
+
+ if (i == -1)
+ errmsg("waitpid");
+ else
+ finish_edit(s);
+
+ free(s->descr);
+ free(s->file);
+ if (i != -1 && !edit_sess && *edit_end) {
+ error = 0;
+ parse_instruction(edit_end, 0, 0, 1);
+ history_done = 0;
+ }
+
+ free(s);
+
+ } else {
+ s->next = edit_sess;
+ edit_sess = s;
+ }
+}
+
+/*
+ * Our child has snuffed it. check if it was an editor, and update the
+ * session list if that is the case.
+ */
+void sig_chld_bottomhalf(void)
+{
+ int fd, pid, ret;
+ editsess **sp, *p;
+
+ /* GH: while() instead of just one check */
+ while ((pid = waitpid(-1, &ret, WNOHANG)) > 0) {
+
+ /* GH: check for WIFSTOPPED unnecessary since no */
+ /* WUNTRACED to waitpid() */
+ for (sp = &edit_sess; *sp && (*sp)->pid != pid; sp = &(*sp)->next)
+ ;
+ if (*sp) {
+ finish_edit(*sp);
+ p = *sp; *sp = p->next;
+ fd = p->fd;
+ free(p->descr);
+ free(p->file);
+ free(p);
+
+ /* GH: only send message if found matching session */
+
+ /* send the edit_end message if this is the last editor... */
+ if ((!edit_sess) && (*edit_end)) {
+ int otcp_fd = tcp_fd; /* backup current socket fd */
+ tcp_fd = fd;
+ error = 0;
+ parse_instruction(edit_end, 0, 0, 1);
+ history_done = 0;
+ tcp_fd = otcp_fd;
+ }
+ }
+ }
+}
+