[mw-devel] MW3 r1050 - trunk/src
finnw at sucs.org
finnw at sucs.org
Mon Jul 21 15:46:06 BST 2008
Author: finnw
Date: 2008-07-21 15:46:06 +0100 (Mon, 21 Jul 2008)
New Revision: 1050
Added:
trunk/src/doing.c
Modified:
trunk/src/Makefile
trunk/src/edit.c
trunk/src/incoming.c
trunk/src/newmain.c
trunk/src/proto.h
trunk/src/who.c
Log:
Status (aka .doing) messages can now be compressed to fit in the 78-byte field in
the user database. Hard limit is 160 bytes. Otherwise the string is trimmed
until it fits in 78 bytes after compression. Compression is using zlib with a
hard-coded dictionary based on some real status messages from the logs (selected
so their owner's can't be easily identified from the text.) Messages that fit
without compression are left alone. Compression is identified by the first byte
being between '001' and '032' inclusive.
Modified: trunk/src/Makefile
===================================================================
--- trunk/src/Makefile 2008-07-19 14:39:17 UTC (rev 1049)
+++ trunk/src/Makefile 2008-07-21 14:46:06 UTC (rev 1050)
@@ -24,7 +24,7 @@
# cflags for standard 'cc' compiler
ALL_CFLAGS+= -Wall -pedantic -fpie -std=gnu99 -D_GNU_SOURCE
LDFLAGS+= -pie
-LDLIBS+= -lreadline -lhistory -ltermcap -lcrypt -l$(JSLIB) -lsqlite3 -lcurl
+LDLIBS+= -lreadline -lhistory -ltermcap -lcrypt -l$(JSLIB) -lsqlite3 -lcurl -lz
# info strings, do not edit.
DEFS:= -DBUILD_DATE=\"$(shell date +%Y%m%d)\"
@@ -95,7 +95,7 @@
topten.o sort.o tidyup.o gags.o script_inst.o script.o\
incoming.o command.o chattable.o alias.o frl.o hash.o vars.o expand.o\
mud.o mudtable.o files.o completion.o sentinel.o iconv.o gagtable.o \
-js.o sqlite.o ipc.o
+js.o sqlite.o ipc.o doing.o
$(CC) $(LDFLAGS) $(LDLIBS) -o $@ $^
del_user: del_user.o perms.o strings.o
Added: trunk/src/doing.c
===================================================================
--- trunk/src/doing.c (rev 0)
+++ trunk/src/doing.c 2008-07-21 14:46:06 UTC (rev 1050)
@@ -0,0 +1,284 @@
+#include <string.h>
+#include <time.h>
+#include <zlib.h>
+
+#include "bb.h"
+#include "ipc.h"
+#include "strings.h"
+
+#define INFLATED_DOINGSIZE 160
+#define DEFLATED_PREFIX_SIZE 12
+#define DICT_LENGTH 2906
+#define LEVEL 9
+#define WINDOWBITS 12
+#define MEMLEVEL 8
+
+extern struct person *user;
+
+static void doing_inflate(const char *src, char *dst);
+static int doing_deflate(unsigned char *dst, size_t capacity,
+ const char *src, size_t size,
+ size_t *actualsize, size_t *discard);
+
+static const unsigned char deflated_prefix[DEFLATED_PREFIX_SIZE] =
+ "\x48\xe6\xe5\x63\x18\xff\x03\x00\x00\x00\x00\x01";
+
+static const char dictionary[] =
+ "is trying again thinks that it all went okay is far too hot having carried "
+ "too much fluid up the hill is making coffee is imagining the rude doing mes"
+ "sages SUs are going to set for unwitting entiti has left the building is tr"
+ "ying to find out if the A36 has been reopened is upgrading the kernel of hi"
+ "s phone is clock-watching until he can go and move house is waiting for God"
+ "ot has drunk too much coffee is changing his status message is moving house"
+ " :) is plotting your death is trying to figure out how to disable Ctrl-S in"
+ " screen is doing very little isn't any more hmm hasn't got the right time t"
+ "ests this thing decides to head for home Back In Swansea till Saturday is w"
+ "elcoming you to the new day. is walking the dog is going to slap whoever s"
+ "et that message is figuring out how to use this new .doing command appearin"
+ "g after a haircut is seriously considering extra sleep is thinking about ea"
+ "ting soon is wondering when someone will get around to writing a link from "
+ "mw status to is (gasp!) doing work on his thesis is on a bus as the Central"
+ " Line is up the wall is now on the Jubilee Line for the second time today i"
+ "s on the Central Line home is failing to come up with anything to write her"
+ "e is sorting the photos he took today is listening to his Last.fm Favourite"
+ " Songs is asleep has gone to bed welcomes you to the new day. is no longer"
+ " asleep is thinking that people should wonder a little less verbosely is co"
+ "ntemplating breakfast is contemplating a short nap. is contemplating a new"
+ " status message is waking up after spending most of the night awake debatin"
+ "g the meaning of li is not awake, despite appearances to the contrarary eva"
+ "luating pi has gone for a bath :) is seeking motivation is stewing the rhub"
+ "arb he just pulled from the garden is now getting lunch - brb. is failing "
+ "to remember what he set his status message to is back from lunch yawns is l"
+ "ying in his status message is playing Scramble is drinking tea is listening"
+ " to kerrang TV channel is going home. is in Swansea hates meetings... is "
+ "not thinking about elephants. has had enough for today, hometime is thinki"
+ "ng about not thinking about elephants. is not thinking about 4th order Run"
+ "ge-Kutta integrators and rigid bodies. has gone to bed is sleeping is off "
+ "to <brandname removed> being strangled by a blue tie-shaped thing is not th"
+ "inking about thinking about elephants still isn't thinking about elephants."
+ " Welcomes you aboard the Milliways Service terminating at the restaurant a"
+ "t the is listening to his Last.fm radio station is asleep at his desk. hea"
+ "ds into town to see a man about a dog is verbosely wondering whether 78 cha"
+ "racters is in fact really enough for stat at work, meh is back from going t"
+ "o the JobCentre (etc) has finshed assembleing for another week... is retur"
+ "ning is outta here... finds it strange that he's here first this morning i"
+ "s entering is testing is eating leftover egg fried rice";
+
+/*
+// This may need to be re-run if zlib is upgraded, but is not required in a normal
+// build:
+
+void doing_generate_deflated_prefix(void) {
+ unsigned char out_buf[42];
+ size_t have, line, col;
+
+ z_stream strm;
+ strm.zalloc = Z_NULL;
+ strm.zfree = Z_NULL;
+ strm.opaque = Z_NULL;
+ strm.avail_in = 0;
+ strm.next_in = Z_NULL;
+ deflateInit2(&strm, LEVEL, Z_DEFLATED, WINDOWBITS, MEMLEVEL, Z_DEFAULT_STRATEGY);
+ deflateSetDictionary(&strm, (const Bytef *) dictionary, DICT_LENGTH);
+
+ strm.avail_out = sizeof (out_buf);
+ strm.next_out = out_buf;
+ deflate(&strm, Z_FINISH);
+ deflateEnd(&strm);
+
+ have = sizeof (out_buf) - strm.avail_out;
+ printf("%d\n", (int) have);
+
+ for (line = 0; line < have; line += 17)
+ {
+ printf("\t\"");
+ for (col = 0; col < 17; ++ col)
+ if (line + col < have)
+ printf("\\x%02x", out_buf[line+col]);
+ printf("\"\n");
+ }
+}
+
+*/
+
+static void doing_inflate(const char *src, char *dst) {
+ z_stream strm;
+ int r;
+ size_t discard_bytes;
+ size_t doing_bytes;
+ size_t suffix_offset;
+
+ // Stream setup
+ strm.zalloc = Z_NULL;
+ strm.zfree = Z_NULL;
+ strm.opaque = Z_NULL;
+ strm.avail_in = 0;
+ strm.next_in = Z_NULL;
+ inflateInit2(&strm, WINDOWBITS);
+
+ // Concatenate deflated prefix + deflated suffix
+ discard_bytes = (size_t) (0xff & (unsigned int) src[0]) - 1;
+ doing_bytes = (size_t) (0xff & (unsigned int) src[1]);
+ suffix_offset = DEFLATED_PREFIX_SIZE - discard_bytes;
+
+ // First inflate the prefix
+ strm.avail_in = suffix_offset;
+ strm.next_in = (Bytef *) deflated_prefix;
+ strm.avail_out = MAXTEXTLENGTH;
+ strm.next_out = (Bytef *) dst;
+ r = inflate(&strm, Z_NO_FLUSH);
+ inflateSetDictionary(&strm, (const Bytef *) dictionary, DICT_LENGTH);
+ r = inflate(&strm, Z_NO_FLUSH);
+
+ // Then inflate the status message
+ strm.avail_in = doing_bytes;
+ strm.next_in = (Bytef *) &src[2];
+ r = inflate(&strm, Z_FINISH);
+
+ inflateEnd(&strm);
+
+ dst[MAXTEXTLENGTH - strm.avail_out] = '\0';
+}
+
+void doing_show(const char *name, const char *doing, long dowhen) {
+ char doingtext[MAXTEXTLENGTH];
+
+ if (dowhen && doing_iscompressed(doing)) {
+ doing_inflate(doing, doingtext);
+ escprintf("%s %s (%s ago)\n", name, doingtext, itime(time(0)-dowhen));
+ } else if (dowhen && doing[0] != 0) {
+ escprintf("%s %s (%s ago)\n", name, doing, itime(time(0)-dowhen));
+ }
+}
+
+void doing_show_wholist(int wiz, pid_t pid, char chat, const struct person *usrp) {
+ char doingtext[MAXTEXTLENGTH];
+ const char *idle = itime(time(0) - usrp->idletime);
+
+ if (doing_iscompressed(usrp->doing))
+ doing_inflate(usrp->doing, doingtext);
+ else
+ strcpy(doingtext, usrp->doing);
+
+ if (wiz) {
+ printf("%-5d %c%-*s %-20.20s %6s %s\n", pid, chat, NAMESIZE, usrp->name, usrp->realname, idle, doingtext);
+ } else {
+ printf("%c%-*s %6s %s\n", chat, NAMESIZE, usrp->name, idle, doingtext);
+ }
+}
+
+static int doing_deflate(unsigned char *dst, size_t capacity,
+ const char *src, size_t size,
+ size_t *actualsize, size_t *discard) {
+ unsigned char out_buf[DEFLATED_PREFIX_SIZE + MAXTEXTLENGTH];
+ size_t have, match_count;
+ int result = 1;
+
+ // Stream setup
+ z_stream strm;
+ strm.zalloc = Z_NULL;
+ strm.zfree = Z_NULL;
+ strm.opaque = Z_NULL;
+ strm.avail_in = 0;
+ strm.next_in = Z_NULL;
+ deflateInit2(&strm, LEVEL, Z_DEFLATED, WINDOWBITS, MEMLEVEL, Z_DEFAULT_STRATEGY);
+ deflateSetDictionary(&strm, (const Bytef *) dictionary, DICT_LENGTH);
+
+ // Compress the status message
+ strm.avail_in = size;
+ strm.next_in = (unsigned char *) src;
+ strm.avail_out = sizeof (out_buf);
+ strm.next_out = out_buf;
+ deflate(&strm, Z_FINISH);
+ deflateEnd(&strm);
+
+ have = sizeof (out_buf) - strm.avail_out;
+ *actualsize = have;
+
+ // Find the range of bytes that differ between prefix and (prefix + status)
+ for (match_count = 0; match_count < DEFLATED_PREFIX_SIZE; ++ match_count) {
+ if (out_buf[match_count] != deflated_prefix[match_count])
+ break;
+ }
+
+ *discard = DEFLATED_PREFIX_SIZE - match_count;
+ if (have <= capacity + match_count)
+ memcpy(dst, &out_buf[match_count], have - match_count);
+
+ return result;
+}
+
+int doing_iscompressed(const char *doing) {
+ return doing[0] && (0xff & (int) doing[0]) < 033;
+}
+
+
+int doing_edit(char *doing, const char *name) {
+ int changed = 0;
+ char doingtext[MAXTEXTLENGTH];
+
+ if (doing_iscompressed(doing))
+ doing_inflate(doing, doingtext);
+ else
+ strcpy(doingtext, doing);
+
+ printf(_("Current Status: %s\n"), doingtext);
+ printf(_("New Status (%d chars): "), INFLATED_DOINGSIZE - 1);
+ get_str(doingtext, INFLATED_DOINGSIZE - 1);
+
+ if (*doingtext) {
+ doing_set(doing, doingtext);
+ ipc_send_to_username(name, IPC_DOING, doingtext);
+ broadcast(3, "%s has just changed %s's status to %s.", user->name, name, doingtext);
+ mwlog("CHANGE(STATUS) of %s to %s", name, doingtext);
+ changed = 1;
+ }
+
+ return changed;
+}
+
+void doing_set(char *dst, const char *src) {
+ size_t doinglen = strlen(src);
+ size_t actual_size, discard_bytes;
+ unsigned char deflate_buf[DOINGSIZE - 2];
+ int deflate_ok, overflowed;
+
+ // Hard limit of INFLATED_DOINGSIZE characters
+ if (doinglen > INFLATED_DOINGSIZE)
+ doinglen = INFLATED_DOINGSIZE;
+
+ // Fits in the buffer without compression?
+ if (doinglen < DOINGSIZE) {
+ // Just copy
+ sprintf(dst, "%.*s", (int) doinglen, src);
+ } else {
+ // Trim the string one character at a time until the deflated version
+ // fits in the buffer
+ do {
+ deflate_ok = doing_deflate(deflate_buf, sizeof(deflate_buf),
+ src, doinglen,
+ &actual_size, &discard_bytes);
+ if (! deflate_ok) {
+ break;
+ } else {
+ overflowed = (actual_size >= DOINGSIZE - 2);
+ if (overflowed)
+ -- doinglen;
+ }
+ } while (overflowed);
+
+ if (deflate_ok && discard_bytes < 032) {
+ dst[0] = (char) (discard_bytes + 1);
+ dst[1] = (char) (actual_size + 1);
+ memcpy(&dst[2], deflate_buf, actual_size);
+ } else {
+ // Deflation failed; Truncate and store plain text
+ if (doinglen >= DOINGSIZE)
+ doinglen = DOINGSIZE - 1;
+
+ // Just copy
+ sprintf(dst, "%.*s", (int) doinglen, src);
+ }
+ }
+}
+
Modified: trunk/src/edit.c
===================================================================
--- trunk/src/edit.c 2008-07-19 14:39:17 UTC (rev 1049)
+++ trunk/src/edit.c 2008-07-21 14:46:06 UTC (rev 1050)
@@ -337,17 +337,13 @@
if (stringcmp(args,"doing",2))
{
char doing[DOINGSIZE];
- printf(_("Current Status: %s\n"),usr.doing);
- printf(_("New Status (%d chars): "),DOINGSIZE-1);
- get_str(doing,DOINGSIZE-1);
- if (*doing)
+ memcpy(doing, usr.doing, DOINGSIZE);
+
+ if (doing_edit(doing, usr.name))
{
- strcpy(usr.doing,doing);
+ memcpy(usr.doing, doing, DOINGSIZE);
usr.dowhen = time(0);
- ipc_send_to_username(usr.name, IPC_DOING, usr.doing);
printf(_("New status set.\n"));
- broadcast(3, "%s has just changed %s's status to %s.", user->name, usr.name, doing);
- mwlog("CHANGE(STATUS) of %s to %s", usr.name, doing);
}
}else
if (stringcmp(args,"timeout",1))
Modified: trunk/src/incoming.c
===================================================================
--- trunk/src/incoming.c 2008-07-19 14:39:17 UTC (rev 1049)
+++ trunk/src/incoming.c 2008-07-21 14:46:06 UTC (rev 1050)
@@ -540,7 +540,7 @@
strcpy(user->contact,newbuff);
break;
case IPC_DOING:
- snprintf(user->doing,DOINGSIZE,"%s",newbuff);
+ doing_set(user->doing, newbuff);
user->dowhen=time(0);
break;
case IPC_SPECIAL:
Modified: trunk/src/newmain.c
===================================================================
--- trunk/src/newmain.c 2008-07-19 14:39:17 UTC (rev 1049)
+++ trunk/src/newmain.c 2008-07-21 14:46:06 UTC (rev 1050)
@@ -451,10 +451,10 @@
void c_doing(CommandList *cm, int argc, char **argv, char *args)
{
if (argc > 1) {
- snprintf(user->doing, DOINGSIZE, "%s", args);
+ doing_set(user->doing, args);
user->dowhen = time(0);
update_user(user,userposn);
- broadcast(5, "%s %s", user->name, user->doing);
+ broadcast(5, "%s %s", user->name, args);
} else {
user->doing[0] = 0;
user->dowhen = time(0);
Modified: trunk/src/proto.h
===================================================================
--- trunk/src/proto.h 2008-07-19 14:39:17 UTC (rev 1049)
+++ trunk/src/proto.h 2008-07-21 14:46:06 UTC (rev 1050)
@@ -256,5 +256,11 @@
/* alias.c */
char *list_bind(const char *text, int state);
char *list_alias(const char *text, int state);
+/* doing.c */
+void doing_show(const char *name, const char *doing, long dowhen);
+void doing_set(char *dst, const char *src);
+int doing_edit(char *doing, const char *name);
+int doing_iscompressed(const char *doing);
+void doing_show_wholist(int wiz, pid_t pid, char chat, const struct person *usrp);
#endif /* PROTO_H */
Modified: trunk/src/who.c
===================================================================
--- trunk/src/who.c 2008-07-19 14:39:17 UTC (rev 1049)
+++ trunk/src/who.c 2008-07-21 14:46:06 UTC (rev 1050)
@@ -4,6 +4,7 @@
* see licence for furthur information. *
*********************************************************/
+#include <assert.h>
#include <stdio.h>
#include <errno.h>
#include <signal.h>
@@ -111,7 +112,7 @@
show_user_stats(u.status,stats,TRUE);
/* can they hear wiz messages ? */
- if (s_wizchat(u.special) && !s_chatoff(u.special))
+ if (wizchat && (s_wizchat(u.special) && !s_chatoff(u.special)))
chat='*';
else
chat=' ';
@@ -120,8 +121,7 @@
if (! ipc_send_to_pid(w.pid, IPC_NOOP, NULL))
{
if (mode == 1) {
- if (u.dowhen && u.doing[0] != 0)
- printf("%s %s (%s ago)\n",u.name, u.doing ,itime(time(0)-u.dowhen));
+ doing_show(u.name, u.doing, u.dowhen);
} else {
if (cm_flags(u.chatmode,CM_ONCHAT,CM_MODE_ANY))
{
@@ -160,6 +160,10 @@
}
else
snprintf(u.doing,DOINGSIZE, "Room %d",u.room);
+ /* All branches above overwrite u.doing, so it should
+ * not now be compressed.
+ */
+ assert (! doing_iscompressed(u.doing));
off=strlen(u.doing);
snprintf(&u.doing[off],DOINGSIZE-off," (");
@@ -211,14 +215,8 @@
snprintf(&u.doing[off],DOINGSIZE-off,")");
}
- if (wiz)
- {
- printf("%-5d %c%-*s %-20.20s %6s %s\n",w.pid,chat,NAMESIZE,u.name,u.realname,itime(time(0)-u.idletime),u.doing);
- }else
- {
- printf("%c%-*s %6s %s\n",wizchat?chat:' ',NAMESIZE,u.name,itime(time(0)-u.idletime),u.doing);
+ doing_show_wholist(wiz, w.pid, chat, &u);
}
- }
}
}
printf("%s\n", divider);
More information about the mw-devel
mailing list