diff options
Diffstat (limited to 'beam.c')
-rw-r--r-- | beam.c | 480 |
1 files changed, 0 insertions, 480 deletions
@@ -1,480 +0,0 @@ -/* - * 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; - } - } - } -} - |