billr@saab.tek.com (Bill Randle) (03/09/91)
Submitted-by: John Temples <jwt!john@uunet.UU.NET> Posting-number: Volume 12, Issue 47 Archive-name: tmc/Part01 Environment: System V [This goes along with the System V patches previously posted to provide tinymud operation on non-networked System V systems. (Untested by me.) -br] #!/bin/sh # This is a shell archive (shar 3.32) # made 03/08/1991 15:39 UTC by john@john # # existing files WILL be overwritten # # This shar contains: # length mode name # ------ ---------- ------------------------------------------ # 2443 -rw-r--r-- tmc.doc # 139 -rw-r--r-- Makefile # 1925 -rw-r--r-- tmc.h # 11599 -rw-rw-rw- tmc.c # if touch 2>&1 | fgrep 'amc' > /dev/null then TOUCH=touch else TOUCH=true fi # ============= tmc.doc ============== echo "x - extracting tmc.doc (Text)" sed 's/^X//' << 'SHAR_EOF' > tmc.doc && X Client for Non-Networked System V TinyMUD X Xtmc is a curses-based client for the non-network System V version of XTinyMUD. It replaces tinymud.sh which is provided with the Xdistributed package. X X XInstallation: X XLook at tmc.h. The only thing that you're likely to need to change Xis the FIFO #define which points to the named pipes used by the Xserver. This is the directory in which you started the server. The Xexecutable can be put in any public place. 'make' will build the Xexecutable. X XStarting tmc: X XType 'tmc' to start the program. The TinyMUD server (fifonetmud) Xshould already be running. tmc provides a split-screen display, with Xthe user's commands being echoed in the lower window, and responses Xfrom the server appearing in the upper window. Word wrapping is Xprovided in both windows. X XOn startup, tmc reads a startup file (default: ~/.tmcrc) and Xtransmits its contents to the server. This is useful for automatic Xlogin. X X XCommand line options: X X-a filename X Use 'filename' as an alternate startup file rather than the X compiled-in default. Tilde expansion is supported. X X-c frequency X Use 'frequency' as the clock update frequency (in seconds) rather X than the compiled-in default. A frequency of zero disables the X clock display. X X-e X Echo commands typed by the player into the remote window. X X-h X Disable hardware screen scrolling. Hardware screen scrolling can X be visually annoying, but runs much faster. X X-l lines X Use 'lines' as the size of the local (lower) window rather than X the compiled-in default. X X-r X Ignore the startup file, if any. X X XDefault command line options may be assigned to the environment Xvariable TMCINIT. Any options explicitly stated on the command line Xwill override any found in TMCINIT. X X XKeyboard commands: X XText entered by the user is echoed in the local window, and Xtransmitted to the server when the enter key is pressed. Word Xwrapping is provided on text as it is typed. Certain keys have a Xspecial meaning: X XBackspace or DEL: X The previous character is deleted. Backspace wrapping to the X previous line is not handled correctly. X XControl U: X The current line is discarded. X XControl L: X The screen is redrawn. X XInterrupt: X Disconnect from the server and exit the program. Also, typing X QUIT (all capitals) will tell the server to disconnect you. When X tmc detects a server disconnect, it exits. X X-- XDirect questions or comments to John Temples (john@jwt.UUCP). SHAR_EOF $TOUCH -am 0308102491 tmc.doc && chmod 0644 tmc.doc || echo "restore of tmc.doc failed" set `wc -c tmc.doc`;Wc_c=$1 if test "$Wc_c" != "2443"; then echo original size 2443, current size $Wc_c fi # ============= Makefile ============== echo "x - extracting Makefile (Text)" sed 's/^X//' << 'SHAR_EOF' > Makefile && XCC = gcc XCFLAGS = -O XLDFLAGS = -s XLIBS = -lcurses -lc_s X Xtmc: tmc.o X $(CC) tmc.o $(LDFLAGS) -o tmc $(LIBS) X mcs -d tmc X Xtmc.o: tmc.c tmc.h SHAR_EOF $TOUCH -am 0304123191 Makefile && chmod 0644 Makefile || echo "restore of Makefile failed" set `wc -c Makefile`;Wc_c=$1 if test "$Wc_c" != "139"; then echo original size 139, current size $Wc_c fi # ============= tmc.h ============== echo "x - extracting tmc.h (Text)" sed 's/^X//' << 'SHAR_EOF' > tmc.h && X/* Configuration items for tmc X */ X X/* tab size: 4 */ X X#define FIFO "/usr2/local/lib/mud/" /* directory containing server FIFOs */ X X#define DEF_LOCAL_SIZE 5 /* default size of local window */ X#define MAXTRIES 10 /* how many connect attempts */ X#define TINYPORT 4201 /* Pseudo-port number of server */ X#define DEF_CLOCK_FREQ 5 /* clock update freq. (secs) */ X#define DEF_RCFILE "~/.tmcrc" /* startup file */ X X/* Global variables X */ X Xextern char *optarg; X Xtypedef struct { X long mtype; X char msg[BUFSIZ+1]; X} msg_t; X Xint inport; /* file descriptor for reading from server */ Xint outport; /* file descriptor for writing to server */ Xint msqid; /* id of message queue */ Xint readpid; /* pid of child process reading server */ Xint writepid; /* pid of child process reading keyboard */ XWINDOW *local; /* local (bottom) window */ XWINDOW *remote; /* remote (top) window */ XWINDOW *status; /* center dividing window */ X Xint hw_scroll = TRUE; /* use hardware screen scrolling? */ Xint echo_cmds = FALSE; /* echo commands to remote window */ Xint wants_rc = TRUE; /* should we read the startup file? */ X Xint local_size = DEF_LOCAL_SIZE; /* default size of local window */ Xint server_connected = FALSE; /* have we connected with the server? */ Xint clock_ticked = FALSE; /* has the timer alarm gone off? */ Xtime_t start_time; /* time our session started */ Xint clock_freq = DEF_CLOCK_FREQ; /* clock tick frequency */ X X/* Function declarations X */ X Xchar *getenv(); X Xvoid make_queue(); Xvoid init_windows(); Xvoid connect_server(); Xvoid error(); Xvoid exit_prog(); Xvoid spawn_children(); Xvoid add_queue(char *, int, int); Xvoid read_queue(); Xvoid message(char *); Xvoid process_input(char); Xvoid process_output(char *, int, int); Xchar *reformat(char *); Xvoid clock_tick(); Xvoid update_clock(); Xint special(char *); Xchar *strstr(char *, char *); X X#define CTL(c) (c & 0x1F) SHAR_EOF $TOUCH -am 0308100391 tmc.h && chmod 0644 tmc.h || echo "restore of tmc.h failed" set `wc -c tmc.h`;Wc_c=$1 if test "$Wc_c" != "1925"; then echo original size 1925, current size $Wc_c fi # ============= tmc.c ============== echo "x - extracting tmc.c (Text)" sed 's/^X//' << 'SHAR_EOF' > tmc.c && X/* Copyright (c) 1991, John W. Temples, III. All rights reserved. */ X/* This program may be freely redistributed and modified. */ X X/* Client for non-network System V version of TinyMUD. X*/ X X/* tab size: 4 */ X X#include <stdio.h> X#include <sys/types.h> X#include <curses.h> X#include <sys/fcntl.h> X#include <errno.h> X#include <string.h> X#include <sys/ipc.h> X#include <signal.h> X#include <time.h> X X#include "tmc.h" X Xvoid main(int argc, char *argv[]) X{ Xint c, i, nargc; Xchar buf[BUFSIZ]; XFILE *rcfile; Xchar rcpath[128]; Xchar *cptr; Xchar *nargv[32]; X Xstrcpy(rcpath, DEF_RCFILE); X X/* process command line arguments */ X X/* first pick up default switches */ X Xnargc = 1; /* leave 0 empty for program name */ X Xif (cptr = getenv("TMCINIT")) X if (nargv[nargc] = strtok(cptr, " ")) X while (nargv[++nargc] = strtok(NULL, " ")) X ; X X/* tack on command line switches */ X Xnargv[0] = argv[0]; /* program name */ X Xfor (i = 1; i < argc; i++) X nargv[nargc++] = argv[i]; X Xwhile ((c = getopt(nargc, nargv, "a:c:ehl:r")) != -1) { X switch (c) { X case 'a': /* alternate rc file */ X strcpy(rcpath, optarg); X break; X X case 'c': /* clock tick frequency */ X clock_freq = atoi(optarg); X break; X X case 'e': /* command echo */ X echo_cmds = !echo_cmds; X break; X X case 'h': /* no hardware screen scrolling */ X hw_scroll = !hw_scroll; X break; X X case 'l': /* local window size */ X local_size = atoi(optarg); X break; X X case 'r': /* ignore startup file */ X wants_rc = !wants_rc; X break; X X default: X fprintf(stderr, "usage: %s [-a rcfile] [-c freq] [-l lines] [-ehr]\n", argv[0]); X exit(1); X } X} X X/* set up the screen windows */ X Xinit_windows(); X X/* handle signals */ X Xsignal(SIGINT, exit_prog); Xsignal(SIGQUIT, exit_prog); Xsignal(SIGALRM, clock_tick); Xsignal(SIGSEGV, exit_prog); Xsignal(SIGILL, exit_prog); Xsignal(SIGBUS, exit_prog); X X/* connect to the TinyMUD server */ X Xconnect_server(); X X/* create the message queue the children will use to talk to us */ X Xmake_queue(); X X/* create the child processes */ X Xspawn_children(); X X/* read the startup file */ X Xif (wants_rc) { X if (rcpath[0] == '~') { X if ((cptr = getenv("HOME")) != NULL) { X strcpy(buf, &rcpath[1]); X strcpy(rcpath, cptr); X strcat(rcpath, buf); X } X } X if ((rcfile = fopen(rcpath, "r")) != NULL) { X while (fgets(buf, sizeof(buf), rcfile)) X write(outport, buf, strlen(buf)); X fclose(rcfile); X } X} X Xalarm(clock_freq); X Xstart_time = time(NULL); X X/* read the message queue and process. never returns */ X Xread_queue(); X X} X X X Xvoid make_queue() X{ Xchar *buf[128]; Xkey_t key; X Xsprintf(buf, "%sfifor%d", FIFO, getpid()); X Xif ((key = ftok(buf, 'M')) == (key_t)-1) X error("ftok() failed"); X Xif ((msqid = msgget(key, 0)) >= 0 && msgctl(msqid, IPC_RMID, 0) == -1) X error("Can't remove old message queue id"); X Xif ((msqid = msgget(key, 0600 | IPC_CREAT)) == -1) X error("Can't create new message queue"); X X} X X X Xvoid init_windows() X{ Xint i; X X Xinitscr(); Xcbreak(); Xnoecho(); X Xstatus = newwin(1, COLS, LINES - local_size - 1, 0); Xlocal = newwin(local_size, COLS, LINES - local_size, 0); Xremote = newwin(LINES - local_size - 1, COLS, 0, 0); X Xscrollok(local, TRUE); Xscrollok(remote, TRUE); X Xif (hw_scroll) { X idlok(local, TRUE); X idlok(remote, TRUE); X} X Xfor (i = 0; i < COLS/2-5; i++) X waddch(status, ACS_HLINE); X Xwaddstr(status, "[TinyMUD!]"); X Xfor (i = 0; i < COLS/2-5; i++) X waddch(status, ACS_HLINE); X Xwrefresh(status); X} X X X Xvoid connect_server() X{ Xchar buf[128]; Xint mypid = getpid(); Xint tries = 0; Xint server; X X Xmessage("[Connecting to the TinyMUD server -- please wait]"); X Xsprintf(buf, "%sfifor%d", FIFO, TINYPORT); X Xif ((server = open(buf, O_WRONLY)) == -1) X error("Couldn't find server"); X Xsprintf(buf, "%d\n", mypid); X Xwrite(server, buf, strlen(buf)); X Xclose(server); X Xsprintf(buf, "%sfifor%d", FIFO, mypid); X Xdo { X sleep(1); X if ((outport = open(buf, O_WRONLY)) == -1) X if (errno != ENOENT) X error("Couldn't open port to server"); X else X continue; X else X break; X X} while (++tries < MAXTRIES); X Xif (tries >= MAXTRIES) X error("Timed out waiting for server"); X Xtries = 0; Xsprintf(buf, "%sfifow%d", FIFO, mypid); X Xdo { X if ((inport = open(buf, O_RDONLY)) == -1) X if (errno != ENOENT) X error("Couldn't open port from server"); X else { X sleep(1); X continue; X } X else X break; X X} while (++tries < MAXTRIES); X Xif (tries >= MAXTRIES) X error("Timed out waiting for server"); X Xserver_connected = TRUE; Xwerase(remote); Xwrefresh(remote); X} X X X Xvoid error(char *msg) X{ Xwaddstr(remote, msg); Xwaddch(remote, '\n'); Xwrefresh(remote); Xsleep(3); Xexit_prog(); X} X X X Xvoid exit_prog() X{ Xif (server_connected) { X message("\n[Disconnecting from server]"); X write(outport, "QUIT\n", 5); X sleep(2); X} Xclear(); Xrefresh(); Xendwin(); Xif (readpid) { X kill(readpid, 9); X wait((int *)0); X} Xif (writepid) { X kill(writepid, 9); X wait((int *)0); X} X Xmsgctl(msqid, IPC_RMID, 0); X Xexit(0); X} X X X Xvoid spawn_children() X{ Xint mypid; X X/* first child reads from the server, writes it into the message queue */ X Xif ((readpid = fork()) == 0) { X char buf[BUFSIZ]; X int cnt; X X setpgrp(); X mypid = getpid(); X while (1) { X cnt = read(inport, buf, BUFSIZ); X add_queue(buf, mypid, cnt); X } X} X X/* second child reads from the keyboard, writes it into the message queue */ X Xif ((writepid = fork()) == 0) { X char c; X X setpgrp(); X mypid = getpid(); X while (1) { X read(0, &c, 1); X if (c == '\r') X c = '\n'; X add_queue(&c, mypid, 1); X } X} X} X X X/* place a buffer into the message queue. pid is the message type */ X Xvoid add_queue(char *buf, int pid, int cnt) X{ Xmsg_t msg; X Xmsg.mtype = pid; Xmemcpy(msg.msg, buf, cnt); X Xif (msgsnd(msqid, &msg, cnt, 0) == -1) X error("msgsnd failed"); X} X X X/* read the message queue and process the data. parent process main loop */ X Xvoid read_queue() X{ Xmsg_t msg; Xint cnt; X Xwhile (1) { X if (clock_ticked) X update_clock(); X X if ((cnt = msgrcv(msqid, &msg, sizeof(msg.msg), 0, 0)) == -1) X if (errno == EINTR) X continue; X else X error("msgrcv failed"); X X /* messages from the read process go to the remote window, messages X * from the keyboard process go to the local window X */ X X if (msg.mtype == readpid || msg.mtype == 1) { X if (cnt < 1) { X server_connected = FALSE; X error("\n*** Lost contact with server -- Bye! ***"); X } X process_output(msg.msg, cnt, msg.mtype); X } else if (msg.mtype == writepid) { X process_input(msg.msg[0]); X } else { X error("Unknown message type received"); X } X} X X} X X X Xvoid message(char *msg) X{ Xwaddstr(remote, msg); Xwaddch(remote, '\n'); Xwrefresh(remote); X} X X X/* format keyboard input. handle backspace/kill keys. do word wrapping. X */ X Xvoid process_input(char c) X{ Xstatic char buf[BUFSIZ], *cptr = buf, *thisline = buf; Xstatic int lastspace = -1, col = 0; Xint cur_row, cur_col; Xchar *ptr; Xstatic char valid[] = { '\b', '\n', CTL('L'), CTL('U') }; X Xif (c > 127) X return; X Xif (c < 32 && (strchr(valid, c) == NULL)) X return; X Xswitch (c) { X case '\b': /* backspace/delete */ X case 127: X if (cptr > buf) { X cptr--; X getyx(local, cur_row, cur_col); X if (--col < 0) X col = 0; X mvwdelch(local, cur_row, --cur_col); X X if (col <= lastspace) { X *cptr = 0; X if ((ptr = strrchr(thisline, ' ')) != NULL) X lastspace = ptr - thisline; X else X lastspace = -1; X } X } X break; X X case CTL('U'): /* cancel */ X if (cptr > buf) { X cptr = buf; X waddstr(local, "\n[Cancelled]\n"); X lastspace = -1; X thisline = buf; X col = 0; X } X break; X X case '\n': /* line complete */ X *cptr++ = c; X *cptr = 0; X X /* special message number '1' is an echoed command */ X X if (echo_cmds) X add_queue(buf, 1, strlen(buf)); X else /* add blank line between server responses */ X add_queue(&c, readpid, 1); X X waddch(local, c); X write(outport, buf, strlen(buf)); X cptr = buf; X thisline = buf; X lastspace = -1; X col = 0; X break; X X case CTL('L'): /* redraw screen */ X endwin(); X doupdate(); X break; X X case ' ': /* mark spaces for word wrap */ X lastspace = col; X /* no break -- INSERT NOTHING HERE */ X X default: /* all other chars */ X *cptr++ = c; X col++; X if ((col >= COLS) && (lastspace > 0)) { X getyx(local, cur_row, cur_col); X wmove(local, cur_row, lastspace); X wclrtoeol(local); X waddch(local, '\n'); X *cptr = 0; X thisline += lastspace + 1; X waddstr(local, thisline); X col = cptr - thisline; X lastspace = -1; X } else X waddch(local, c); X X break; X} X Xwrefresh(local); X} X X X/* process output from server. do word wrapping. highlight 'special' X * responses. X */ X Xvoid process_output(char *buf, int cnt, int hilite) X{ Xchar *ptr; Xint unbold = FALSE; X Xbuf[cnt] = 0; X Xreformat(buf); Xwhile (ptr = reformat(NULL)) { X if (special(ptr)) { /* pages, etc. */ X unbold = TRUE; X wattron(remote, A_REVERSE); X } X X if (hilite == 1) { /* echoed commands */ X unbold = TRUE; X wattron(remote, A_UNDERLINE); X } X X waddstr(remote, ptr); X X if (unbold) X wstandend(remote); X} X Xwnoutrefresh(remote); Xwnoutrefresh(local); /* put cursor back on local window */ Xdoupdate(); X} X X X/* reformat a null-terminated string containing possibly several newlines. X * return a series of newline/null terminated strings which fit on the X * screen. X */ X Xchar *reformat(char *buf) X{ Xstatic char retbuf[BUFSIZ+1], holdbuf[2*BUFSIZ+1], *baseptr; Xchar *cptr; Xstatic int leftovers = FALSE; X X/* initial call. save the user's string for further processing and return. X */ X Xif (buf) { X if (leftovers) /* unfished line from last call? */ X strcat(holdbuf, buf); X else X strcpy(holdbuf, buf); X baseptr = holdbuf; X leftovers = FALSE; X return NULL; X} else if (baseptr[0] == '\0') X return NULL; X Xif ((cptr = strchr(baseptr, '\n')) == NULL) { /* no newline */ X strcpy(holdbuf, baseptr); X leftovers = TRUE; X return NULL; X} X Xif (cptr - baseptr < COLS) { /* this line fits */ X memcpy(retbuf, baseptr, cptr - baseptr + 1); X retbuf[cptr - baseptr + 1] = 0; X baseptr = cptr + 1; X return retbuf; X} /* this line doesn't fit */ X Xcptr = &baseptr[COLS-1]; X Xwhile ((*cptr != ' ') && (cptr > baseptr)) /* find last space */ X cptr--; X Xif (cptr == baseptr) { /* no space found */ X memcpy(retbuf, baseptr, COLS-1); X retbuf[COLS-1] = '\n'; X retbuf[COLS] = 0; X baseptr += COLS - 1; X return retbuf; X} X X/* space found */ X Xmemcpy(retbuf, baseptr, cptr - baseptr); Xretbuf[cptr - baseptr] = '\n'; Xretbuf[cptr - baseptr + 1] = 0; Xbaseptr = cptr + 1; Xreturn retbuf; X X} X X X X/* alarm interrupt handler */ X Xvoid clock_tick() X{ Xsignal(SIGALRM, clock_tick); Xclock_ticked = TRUE; Xalarm(clock_freq); X} X X X X/* update the status window clock display */ X Xvoid update_clock() X{ Xtime_t current = time(NULL); Xtime_t elapsed = current - start_time; Xtime_t hours = elapsed / 3600; Xtime_t mins = (elapsed - (hours * 3600)) / 60; Xtime_t secs = elapsed % 60; Xchar *cptr = ctime(¤t); X Xcptr[19] = 0; X Xmvwprintw(status, 0, COLS/4-5, "[%02d:%02d:%02d]", hours, mins, secs); Xmvwprintw(status, 0, 3*COLS/4-5, "[%s]", &cptr[11]); X Xwnoutrefresh(status); Xwnoutrefresh(local); Xdoupdate(); X Xclock_ticked = FALSE; X} X X X/* returns true if we want to highlight this line */ X X Xint special(char *buf) X{ Xreturn strstr(buf, "pages:") == NULL ? 0 : 1; X} X X X X/* Find first occurence of str2 in str1 and return a pointer to it */ X Xchar *strstr(str1, str2) Xchar *str1, *str2; X{ X register char *Sptr, *Tptr; X int len = strlen(str1) -strlen(str2) + 1; X X if (*str2) X for (; len > 0; len--, str1++) { X if (*str1 != *str2) X continue; X X for (Sptr = str1, Tptr = str2; *Tptr != '\0'; Sptr++, Tptr++) X if (*Sptr != *Tptr) X break; X X if (*Tptr == '\0') X return str1; X } X X return NULL; X} SHAR_EOF $TOUCH -am 0308102291 tmc.c && chmod 0666 tmc.c || echo "restore of tmc.c failed" set `wc -c tmc.c`;Wc_c=$1 if test "$Wc_c" != "11599"; then echo original size 11599, current size $Wc_c fi exit 0