? Makefile ? xtush Index: Imakefile =================================================================== RCS file: /usr/local/cvsroot/xtush/Imakefile,v retrieving revision 1.1.1.1 retrieving revision 1.3 diff -c -r1.1.1.1 -r1.3 *** Imakefile 1998/10/24 21:00:10 1.1.1.1 --- Imakefile 1998/12/03 11:59:13 1.3 *************** *** 25,30 **** --- 25,36 ---- #GRAPHICS = -DGRAPHICS #GRAPHLIBS = -lm $(XLIB) + # Support compression? + COMPRESSION = -DCOMPRESSION + COMPLIBS = -lz + COMPSRC = mccpDecompress.c + COMPOBJ = mccpDecompress.o + # Swap the LOCAL_LIBRARIES definitions around if you're trying to compile this # on Solaris 2.2 *************** *** 37,48 **** #LOCAL_LIBRARIES = -ltermcap $(GRAPHLIBS) #And another line for linux systems ! LOCAL_LIBRARIES = -ltermcap -lbsd $(GRAPHLIBS) ! SRCS = alias.c command.c main.c socket.c vscreen.c graphics.c bsxgraphics.c ! OBJS = alias.o command.o main.o socket.o vscreen.o graphics.o bsxgraphics.o ! DEFINES = $(SYSTEM) $(MALL) $(GRAPHICS) -DHELPPATH='"$(HELPFILEDIR)/tush.doc"' ComplexProgramTarget(xtush) --- 43,54 ---- #LOCAL_LIBRARIES = -ltermcap $(GRAPHLIBS) #And another line for linux systems ! LOCAL_LIBRARIES = -ltermcap -lbsd $(GRAPHLIBS) $(COMPLIBS) ! SRCS = alias.c command.c main.c socket.c vscreen.c graphics.c bsxgraphics.c $(COMPSRC) ! OBJS = alias.o command.o main.o socket.o vscreen.o graphics.o bsxgraphics.o $(COMPOBJ) ! DEFINES = $(SYSTEM) $(MALL) $(GRAPHICS) $(COMPRESSION) -DHELPPATH='"$(HELPFILEDIR)/tush.doc"' ComplexProgramTarget(xtush) Index: Makefile.std =================================================================== RCS file: /usr/local/cvsroot/xtush/Makefile.std,v retrieving revision 1.1.1.1 retrieving revision 1.3 diff -c -r1.1.1.1 -r1.3 *** Makefile.std 1998/10/24 21:00:10 1.1.1.1 --- Makefile.std 1998/12/03 11:59:14 1.3 *************** *** 36,41 **** --- 36,47 ---- #GRAPHICS = -DGRAPHICS #EXTRALIBS = -lm -lX11 + # Support compression? + COMPRESSION = -DCOMPRESSION + EXTRALIBS = $(EXTRALIBS) -lz + COMPSRC = mccpDecompress.c + COMPOBJ = mccpDecompress.o + # Swap the LIBS definitions around if you're trying to compile this on # Solaris 2.2 # The -L and -R should point to where your ucb libs are. *************** *** 51,57 **** ####################################################################### # You shouldn't need to alter anything below here. ! DEFS = $(SYSTEM) $(MALL) $(GRAPHICS) -DHELPPATH='"$(HELPFILEDIR)/tush.doc"' HDRS = clist.h \ --- 57,63 ---- ####################################################################### # You shouldn't need to alter anything below here. ! DEFS = $(SYSTEM) $(MALL) $(GRAPHICS) $(COMPRESSION) -DHELPPATH='"$(HELPFILEDIR)/tush.doc"' HDRS = clist.h \ *************** *** 63,69 **** socket.o \ vscreen.o \ graphics.o \ ! bsxgraphics.o SRCS = alias.c \ command.c \ --- 69,76 ---- socket.o \ vscreen.o \ graphics.o \ ! bsxgraphics.o \ ! $(COMPOBJ) SRCS = alias.c \ command.c \ *************** *** 71,77 **** socket.c \ vscreen.c \ graphics.c \ ! bsxgraphics.c all: $(PROGRAM) --- 78,85 ---- socket.c \ vscreen.c \ graphics.c \ ! bsxgraphics.c \ ! $(COMPSRC) all: $(PROGRAM) Index: config.h =================================================================== RCS file: /usr/local/cvsroot/xtush/config.h,v retrieving revision 1.1.1.1 retrieving revision 1.3 diff -c -r1.1.1.1 -r1.3 *** config.h 1998/10/24 21:00:10 1.1.1.1 --- config.h 1998/12/03 11:59:14 1.3 *************** *** 108,113 **** --- 108,117 ---- #include <stdio.h> + #ifdef COMPRESSION + #include "mccpDecompress.h" + #endif + /* the shell structure definition */ struct shs { *************** *** 148,153 **** --- 152,161 ---- int bsxgraphics; char inputgraphics[MAXGRAPHICSLEN]; int graphicslen; + #endif + + #ifdef COMPRESSION + mc_state *compress; #endif }; Index: mccpDecompress.c =================================================================== RCS file: mccpDecompress.c diff -N mccpDecompress.c *** /dev/null Wed May 6 08:32:27 1998 --- /tmp/04108aaa Fri Dec 4 01:02:01 1998 *************** *** 0 **** --- 1,374 ---- + /* + * Client decompression module for the mud client compression protocol. + * See http://homepages.ihug.co.nz/~icecube/compress/ for more details. + * + * mccpDecompress.c - module code. Link this with your client code. + * + * Oliver Jowett <icecube$ihug.co.nz>. Demangle address as needed. + * + * This code is placed in the public domain. + * + */ + + /* Modified: 981203 */ + + /* See mccpDecompress.h for API information */ + + #include <stdlib.h> + #include <stdio.h> + #include <string.h> + + #include <zlib.h> + + #include "mccpDecompress.h" + + /* Telnet values we're interested in */ + + #define IAC 255 + #define DONT 254 + #define DO 253 + #define WONT 252 + #define WILL 251 + #define SB 250 + #define SE 240 + + #define TELOPT_COMPRESS 85 + + /* Server sends IAC WILL COMPRESS + * We reply with IAC DO COMPRESS + * + * Later the server sends IAC SB COMPRESS WILL SE, and immediately following + * that, begins compressing + * + * Compression ends on a Z_STREAM_END, no other marker is used + */ + + static char will_sig[] = { IAC, WILL, TELOPT_COMPRESS, 0 }; + static char do_sig[] = { IAC, DO, TELOPT_COMPRESS, 0 }; + static char on_sig[] = { IAC, SB, TELOPT_COMPRESS, WILL, SE, 0 }; + + /* this is used on every call to mudcompress_receive */ + #define will_sig_len 3 + #define on_sig_len 5 + + /* "Opaque" state object */ + + struct mc_state_s { + z_stream *stream; /* stream we're using */ + + unsigned char *inbuf; /* input buffer (data from mud) */ + unsigned int insize; /* .. and how much is used */ + unsigned int inalloc; /* .. and how much is allocated */ + + unsigned char *outbuf; /* output buffer (data to user) */ + unsigned int outsize; /* .. and how much is used */ + unsigned int outalloc; /* .. and how much is allocated */ + + int error; + int response; + + unsigned long comp; + unsigned long uncomp; + }; + + /* Initialise a new state object */ + mc_state *mudcompress_new(void) + { + mc_state *state; + + state = malloc(sizeof(*state)); + state->stream = NULL; /* Not decompressing */ + state->inalloc = 2048; + state->outalloc = 2048; + state->inbuf = malloc(state->inalloc); + state->outbuf = malloc(state->outalloc); + state->insize = 0; + state->outsize = 0; + state->error = 0; + state->comp = 0; + state->uncomp = 0; + state->response = 0; + + return state; + } + + /* Clean up a state object */ + void mudcompress_delete(mc_state *state) + { + if (state->stream) { + inflateEnd(state->stream); + free(state->stream); + } + + free(state->inbuf); + free(state->outbuf); + free(state); + } + + /* zlib helpers */ + + static void *zlib_alloc(void *opaque, unsigned int items, unsigned int size) + { + return calloc(items, size); + } + + static void zlib_free(void *opaque, void *address) + { + free(address); + } + + static void grow_inbuf(mc_state *state, int needed) + { + int old = state->inalloc; + + while (state->inalloc < state->insize + needed) + state->inalloc *= 2; + + if (old != state->inalloc) + state->inbuf = realloc(state->inbuf, state->inalloc); + } + + static void grow_outbuf(mc_state *state, int needed) + { + int old = state->outalloc; + + while (state->outalloc < state->outsize + needed) + state->outalloc *= 2; + + if (old != state->outalloc) + state->outbuf = realloc(state->outbuf, state->outalloc); + } + + static void decompress_inbuf(mc_state *state) + { + int status; + + /* We are now decompressing from inbuf to outbuf */ + + if (!state->insize) + return; /* nothing to decompress? */ + + state->stream->next_in = state->inbuf; + state->stream->next_out = state->outbuf + state->outsize; + state->stream->avail_in = state->insize; + state->stream->avail_out = state->outalloc - state->outsize; + + status = inflate(state->stream, Z_PARTIAL_FLUSH); + + if (status == Z_OK || status == Z_STREAM_END) { + /* Successful decompression */ + + /* Remove used data from inbuf */ + state->comp += state->insize - state->stream->avail_in; + state->uncomp += state->stream->next_out - state->outbuf; + + memmove(state->inbuf, state->stream->next_in, state->stream->avail_in); + state->insize = state->stream->avail_in; + + /* Update outbuf pointers */ + state->outsize = state->stream->next_out - state->outbuf; + + /* Done */ + + if (status == Z_STREAM_END) { + /* Turn off compression too */ + + grow_outbuf(state, state->insize); + + memcpy(state->outbuf + state->outsize, state->inbuf, state->insize); + state->outsize += state->insize; + state->insize = 0; + + inflateEnd(state->stream); + free(state->stream); + state->stream = NULL; + } + + return; + } + + if (status == Z_BUF_ERROR) { + /* Full buffers? Maybe we need more output space.. */ + + if (state->outsize * 2 > state->outalloc) { + grow_outbuf(state, state->outalloc); + decompress_inbuf(state); + } + + return; + } + + /* Error */ + state->error = 1; + } + + /* We received some data */ + void mudcompress_receive(mc_state *state, const char *data, unsigned len) + { + int i; + + if (state->error) + return; + + if (!state->stream) { + int residual = -1; + int clen; + + /* Just copy to outbuf. Also copy any residual inbuf */ + + grow_outbuf(state, len + state->insize); + memcpy(state->outbuf + state->outsize, data, len); + state->outsize += len; + memcpy(state->outbuf + state->outsize, state->inbuf, state->insize); + state->outsize += state->insize; + state->insize = 0; + + /* Check for Magic Marker. ugh this is messy */ + for (i=0; i < state->outsize; i++) { + if (state->outbuf[i] == IAC) { + if (i + 1 < state->outsize && state->outbuf[i+1] == IAC) { + /* IAC IAC - ignore */ + i++; + continue; + } + + clen = (i + will_sig_len < state->outsize) ? will_sig_len : state->outsize - i; + + if (!memcmp(&state->outbuf[i], will_sig, clen)) { + if (clen != will_sig_len) { + /* Partial match. Save it. */ + residual = i; + break; + } + + /* Say we'll do compression. remove sig from inbuf */ + + state->response = 1; + + memmove(&state->outbuf[i], + &state->outbuf[i + will_sig_len], + state->outsize - will_sig_len); + state->outsize -= strlen(will_sig); + i--; + continue; + } + + clen = (i + on_sig_len < state->outsize) ? on_sig_len : state->outsize - i; + + if (!memcmp(&state->outbuf[i], on_sig, clen)) { + if (clen != on_sig_len) { + /* Partial match. Save it. */ + residual = i; + break; + } + + /* Switch to compression */ + /* copy any compressible bits to our inbuf */ + + grow_inbuf(state, state->outsize - i - strlen(on_sig)); + + memcpy(state->inbuf, + state->outbuf + i + strlen(on_sig), + state->outsize - i - strlen(on_sig)); + + state->insize = state->outsize - i - strlen(on_sig); + + /* clean up our output buffer */ + state->outsize = i; + + /* init stream */ + state->stream = malloc(sizeof(z_stream)); + state->stream->zalloc = zlib_alloc; + state->stream->zfree = zlib_free; + state->stream->opaque = NULL; + + if (inflateInit(state->stream) != Z_OK) { + state->error = 1; + free(state->stream); + state->stream = NULL; + return; + } + + /* Continue with decompression */ + break; + } + } + } + + if (!state->stream) { /* didn't start decompressing? */ + /* We might have some residual, copy to inbuf for later checking */ + + if (residual != -1) { + grow_inbuf(state, state->outsize - residual); + memcpy(state->inbuf + state->insize, state->outbuf + residual, state->outsize - residual); + state->outsize = residual; + } + + return; + } + } else { + /* New data to decompress. Copy to inbuf */ + grow_inbuf(state, len); + memcpy(state->inbuf + state->insize, data, len); + state->insize += len; + } + + decompress_inbuf(state); + } + + /* How much data is available? */ + int mudcompress_pending(mc_state *state) + { + return state->error ? 0 : state->outsize; + } + + /* Was there an error? */ + int mudcompress_error(mc_state *state) + { + return state->error; + } + + /* Get some data */ + int mudcompress_get(mc_state *state, char *buf, int size) + { + int copied; + + if (state->error || !state->outsize) + return 0; + + if (size > state->outsize) + copied = state->outsize; + else + copied = size; + + memcpy(buf, state->outbuf, copied); + state->outsize -= copied; + if (state->outsize) + memmove(state->outbuf, state->outbuf + copied, state->outsize); + + /* Do some more decompression */ + decompress_inbuf(state); + + return copied; + } + + void mudcompress_stats(mc_state *state, unsigned long *comp, unsigned long *uncomp) + { + *comp = state->comp; + *uncomp = state->uncomp; + } + + const char *mudcompress_response(mc_state *state) + { + if (state->response) { + state->response = 0; + return do_sig; + } + + return NULL; + } + + int mudcompress_compressing(mc_state *state) + { + return (state->stream != NULL); + } Index: mccpDecompress.h =================================================================== RCS file: mccpDecompress.h diff -N mccpDecompress.h *** /dev/null Wed May 6 08:32:27 1998 --- /tmp/04108baa Fri Dec 4 01:02:01 1998 *************** *** 0 **** --- 1,141 ---- + /* + * Client decompression module for the mud client compression protocol. + * See http://homepages.ihug.co.nz/~icecube/compress/ for more details. + * + * mccpDecompress.h - header definitions. #include this in your client code. + * + * Oliver Jowett <icecube$ihug.co.nz>. Demangle address as needed. + * + * This code is placed in the public domain. + * + */ + + /* Modified: 981203 */ + + /* + * + * mc_state is an opaque type representing the current compression state of + * a connection. You should include a (mc_state *) in the information you + * store for a server connection. + * + * Initialization / cleanup: + * + * When a connection is initiated, call mudcompress_new, and store the + * resulting pointer in your server connection information. This pointer is + * used as a handle for all other functions. This does NOT begin compression + * - it just initialises various internal structures. + * + * When a connection is terminated, call mudcompress_delete with the handle + * to delete all memory allocated by the decompressor for the connection. + * + * Reading / writing: + * + * Reading from the server connection must go through the decompressor at + * all times. The decompressor handles both negotiation and decompression + * transparently - it receives input directly from the server, then provides + * the main client code with decompressed data, hiding all protocol details. + * + * When data is received from the mud server, call mudcompress_receive, + * passing it the handle for the connection, a pointer to the data read, + * and the length of data read. It is VITAL that ALL data read is passed + * to the decompressor - including data with embedded NULs! + * + * After mudcompress_receive has been called, call mudcompress_pending() to + * see if any decompressed data is available. It returns the number of + * bytes pending. + * + * If there is pending data waiting, call mudcompress_get to retrieve it. + * This fills up to "size" bytes in the provided buffer "buf", and returns + * the number of bytes copied. Your client can now process this data as if + * it had been directly read from the server. + * + * Be sure to check mudcompress_pending again after calling mudcompress_get! + * Removing some data from the decompress buffer may have allowed the + * decompressor to decompress some more data - in which case, you want to + * process it immediately, rather than waiting for another read from the + * mud server. + * + * Regularly call mudcompress_response. If non-NULL, you need to write the + * returned string to the mud server. This is needed when the decompressor + * is negotiating compression with the server. When called, + * mudcompress_response clears any pending string, so be sure to save its + * return value! + * + * Status information: + * + * mudcompress_error returns non-0 if there has been a (fatal) decompression + * error. In this case, all you can do is tell the user that something went + * wrong and close the connection. + * + * mudcompress_stats fills in the two (unsigned long *) values passed, with + * the number of compressed bytes read, and the number of bytes that they + * decompressed to. + * + * mudcompress_compressing returns non-0 if the connection is currently + * using compression. + * + */ + + #ifndef MUDCOMPRESS_H + #define MUDCOMPRESS_H + + #ifdef __cplusplus + extern "C" { + #endif + + /* Opaque handle for a decompressor. Details defined in Compress.c - you + * should never need to see them externally. + */ + struct mc_state_s; + typedef struct mc_state_s mc_state; + + /* Create a new decompressor. Return a handle to it. + */ + mc_state *mudcompress_new(void); + + /* Deallocate a decompressor and associated data. 'state' is invalid + * afterthis call. + */ + void mudcompress_delete(mc_state *state); + + /* Perform decompression and negotiation on some received data. + * 'data' is a pointer to the received data, 'len' is its length. + */ + void mudcompress_receive(mc_state *state, const char *data, unsigned len); + + /* Return the number of pending decompressed bytes that can currently + * be read by mudcompress_get + */ + int mudcompress_pending(mc_state *state); + + /* Return true (non-0) if this decompressor encountered a fatal error. + */ + int mudcompress_error(mc_state *state); + + /* Read decompressed data from the decompressor into 'buf', up to a + * maximum of 'size' bytes. Returns the number of bytes actually copied. + */ + int mudcompress_get(mc_state *state, char *buf, int size); + + /* Set *comp to the number of compressed bytes read, and *uncomp to the + * number of bytes they expanded to, for this decompressor. + */ + void mudcompress_stats(mc_state *state, unsigned long *comp, unsigned long *uncomp); + + /* Check for a negotiation response. If this returns NULL, no output is + * needed. If it returns non-NULL, it points to a NUL-terminated string + * that should be sent to the mud server. Calling this function clears + * the pending string (so be sure to save the result). + */ + const char *mudcompress_response(mc_state *state); + + /* Return true (non-0) if this decompressor has successfully negotiated + * compression and is currently performing decompression. + */ + int mudcompress_compressing(mc_state *state); + + #ifdef __cplusplus + } + #endif + + #endif Index: socket.c =================================================================== RCS file: /usr/local/cvsroot/xtush/socket.c,v retrieving revision 1.1.1.1 diff -c -r1.1.1.1 socket.c *** socket.c 1998/10/24 21:00:10 1.1.1.1 --- socket.c 1998/12/03 12:02:00 *************** *** 33,39 **** --- 33,41 ---- extern void print(); extern void *scratch; + #ifndef LINUX extern char *sys_errlist[]; + #endif extern shell *current_shell, *first_shell; #ifdef GRAPHICS *************** *** 64,69 **** --- 66,76 ---- sh->grmode = GRNONE; redisplay(); #endif + + #ifdef COMPRESSION + mudcompress_delete(sh->compress); + sh->compress = NULL; + #endif if (sh->flags&AUTODIE) kill_shell(sh,str); } } *************** *** 541,550 **** --- 548,590 ---- m = malloc(chars_ready+1); if (read(sd, m, chars_ready) == -1) handle_error("Reading from socket."); + + #ifdef COMPRESSION + /* Filter through mudcompress */ + mudcompress_receive(sh->compress, m, chars_ready); + free(m); + + /* maybe send out a response */ + m = mudcompress_response(sh->compress); + if (m) + write(sd, m, strlen(m)); + + while (mudcompress_pending(sh->compress)) { + int count; + + m = malloc(mudcompress_pending(sh->compress)); + count = mudcompress_get(sh->compress, m, mudcompress_pending(sh->compress)); + + for (i = 0; i < count; i++) + parse_input(sh, m[i], 0); + + free(m); + } + + parse_input(sh, '\0', 1); + + if (mudcompress_error(sh->compress)) { + shell_print(sh,"Compression error"); + close_com(sh,0); + update(sh); + return; + } + #else for (i = 0; i < chars_ready; i++) parse_input(sh, m[i], 0); parse_input(sh, '\0', 1); free(m); + #endif if (sh != current_shell) { if ((sh->flags&MONITOR) && !(sh->flags&MONITOR_GONEOFF)) { *************** *** 617,622 **** --- 657,666 ---- shell_print(sh,"Connected to server ..."); sh->flags |= CONNECTED; sh->flags &= ~COMMAND; + + #ifdef COMPRESSION + sh->compress = mudcompress_new(); + #endif } /* the open command */