shithub: qk1

Download patch

ref: fe2a32889ef6968fb533876bf2492927a9406344
parent: b36001c95a510ec4f6c8e8613ec09283135dc9fd
author: Sigrid Solveig Haflínudóttir <sigrid@ftrv.se>
date: Tue Oct 15 00:08:54 EDT 2024

better console completion; unite console cmds with aliases

--- a/cd.c
+++ b/cd.c
@@ -6,11 +6,12 @@
 bool cdloop = false, cdenabled;
 
 void
-cdcmd(void)
+cdcmd(cmd_t *cmd)
 {
 	char *c;
 	bool loop;
 
+	USED(cmd);
 	if(!cdenabled){
 		Con_Printf("cd disabled\n");
 		return;
--- a/cl_demo.c
+++ b/cl_demo.c
@@ -27,8 +27,9 @@
 }
 
 void
-stopdemo(void)
+stopdemo(cmd_t *c)
 {
+	USED(c);
 	if(cmd_source != src_command)
 		return;
 	if(!cls.demorecording){
@@ -84,8 +85,9 @@
 }
 
 void
-timedemo(void)
+timedemo(cmd_t *cmd)
 {
+	USED(cmd);
 	if(cmd_source != src_command)
 		return;
 	if(Cmd_Argc() != 2){
@@ -92,7 +94,7 @@
 		Con_Printf("timedemo <demoname> : gets demo speeds\n");
 		return;
 	}
-	playdemo();
+	playdemo(nil);
 	if(cls.demoplayback != 1)
 		return;
 	/* cls.td_starttime will be grabbed at the second frame of the demo, so
@@ -103,11 +105,12 @@
 }
 
 void
-recdemo(void)
+recdemo(cmd_t *cmd)
 {
 	int c, trk;
 	char *s, *a;
 
+	USED(cmd);
 	if(cmd_source != src_command)
 		return;
 	c = Cmd_Argc();
@@ -141,10 +144,11 @@
  * NET_GetMessages are read from the demo file. whenever cl.time gets past
  * the last received message, another message is read from the demo lump. */
 void
-playdemo(void)
+playdemo(cmd_t *cmd)
 {
 	char *s, *a;
 
+	USED(cmd);
 	if(cmd_source != src_command)
 		return;
 	if(Cmd_Argc() != 2){
--- a/cl_input.c
+++ b/cl_input.c
@@ -90,51 +90,52 @@
 	b->state |= 4; 		// impulse up
 }
 
-static void IN_KLookDown (void) {KeyDown(&in_klook);}
-static void IN_KLookUp (void) {KeyUp(&in_klook);}
-static void IN_MLookDown (void) {KeyDown(&in_mlook);}
-static void IN_MLookUp (void) {
+static void IN_KLookDown (cmd_t *c) {USED(c); KeyDown(&in_klook);}
+static void IN_KLookUp (cmd_t *c) {USED(c); KeyUp(&in_klook);}
+static void IN_MLookDown (cmd_t *c) {USED(c); KeyDown(&in_mlook);}
+static void IN_MLookUp (cmd_t *c) {
 	KeyUp(&in_mlook);
 	if ( !(in_mlook.state&1) &&  lookspring.value)
-		V_StartPitchDrift();
+		V_StartPitchDrift(c);
 }
-static void IN_UpDown(void) {KeyDown(&in_up);}
-static void IN_UpUp(void) {KeyUp(&in_up);}
-static void IN_DownDown(void) {KeyDown(&in_down);}
-static void IN_DownUp(void) {KeyUp(&in_down);}
-static void IN_LeftDown(void) {KeyDown(&in_left);}
-static void IN_LeftUp(void) {KeyUp(&in_left);}
-static void IN_RightDown(void) {KeyDown(&in_right);}
-static void IN_RightUp(void) {KeyUp(&in_right);}
-static void IN_ForwardDown(void) {KeyDown(&in_forward);}
-static void IN_ForwardUp(void) {KeyUp(&in_forward);}
-static void IN_BackDown(void) {KeyDown(&in_back);}
-static void IN_BackUp(void) {KeyUp(&in_back);}
-static void IN_LookupDown(void) {KeyDown(&in_lookup);}
-static void IN_LookupUp(void) {KeyUp(&in_lookup);}
-static void IN_LookdownDown(void) {KeyDown(&in_lookdown);}
-static void IN_LookdownUp(void) {KeyUp(&in_lookdown);}
-static void IN_MoveleftDown(void) {KeyDown(&in_moveleft);}
-static void IN_MoveleftUp(void) {KeyUp(&in_moveleft);}
-static void IN_MoverightDown(void) {KeyDown(&in_moveright);}
-static void IN_MoverightUp(void) {KeyUp(&in_moveright);}
+static void IN_UpDown(cmd_t *c) {USED(c); KeyDown(&in_up);}
+static void IN_UpUp(cmd_t *c) {USED(c); KeyUp(&in_up);}
+static void IN_DownDown(cmd_t *c) {USED(c); KeyDown(&in_down);}
+static void IN_DownUp(cmd_t *c) {USED(c); KeyUp(&in_down);}
+static void IN_LeftDown(cmd_t *c) {USED(c); KeyDown(&in_left);}
+static void IN_LeftUp(cmd_t *c) {USED(c); KeyUp(&in_left);}
+static void IN_RightDown(cmd_t *c) {USED(c); KeyDown(&in_right);}
+static void IN_RightUp(cmd_t *c) {USED(c); KeyUp(&in_right);}
+static void IN_ForwardDown(cmd_t *c) {USED(c); KeyDown(&in_forward);}
+static void IN_ForwardUp(cmd_t *c) {USED(c); KeyUp(&in_forward);}
+static void IN_BackDown(cmd_t *c) {USED(c); KeyDown(&in_back);}
+static void IN_BackUp(cmd_t *c) {USED(c); KeyUp(&in_back);}
+static void IN_LookupDown(cmd_t *c) {USED(c); KeyDown(&in_lookup);}
+static void IN_LookupUp(cmd_t *c) {USED(c); KeyUp(&in_lookup);}
+static void IN_LookdownDown(cmd_t *c) {USED(c); KeyDown(&in_lookdown);}
+static void IN_LookdownUp(cmd_t *c) {USED(c); KeyUp(&in_lookdown);}
+static void IN_MoveleftDown(cmd_t *c) {USED(c); KeyDown(&in_moveleft);}
+static void IN_MoveleftUp(cmd_t *c) {USED(c); KeyUp(&in_moveleft);}
+static void IN_MoverightDown(cmd_t *c) {USED(c); KeyDown(&in_moveright);}
+static void IN_MoverightUp(cmd_t *c) {USED(c); KeyUp(&in_moveright);}
 
-static void IN_SpeedDown(void) {KeyDown(&in_speed);}
-static void IN_SpeedUp(void) {KeyUp(&in_speed);}
-static void IN_StrafeDown(void) {KeyDown(&in_strafe);}
-static void IN_StrafeUp(void) {KeyUp(&in_strafe);}
+static void IN_SpeedDown(cmd_t *c) {USED(c); KeyDown(&in_speed);}
+static void IN_SpeedUp(cmd_t *c) {USED(c); KeyUp(&in_speed);}
+static void IN_StrafeDown(cmd_t *c) {USED(c); KeyDown(&in_strafe);}
+static void IN_StrafeUp(cmd_t *c) {USED(c); KeyUp(&in_strafe);}
 
-static void IN_AttackDown(void) {KeyDown(&in_attack);}
-static void IN_AttackUp(void) {KeyUp(&in_attack);}
+static void IN_AttackDown(cmd_t *c) {USED(c); KeyDown(&in_attack);}
+static void IN_AttackUp(cmd_t *c) {USED(c); KeyUp(&in_attack);}
 
-static void IN_UseDown (void) {KeyDown(&in_use);}
-static void IN_UseUp (void) {KeyUp(&in_use);}
-static void IN_JumpDown (void) {KeyDown(&in_jump);}
-static void IN_JumpUp (void) {KeyUp(&in_jump);}
+static void IN_UseDown (cmd_t *c) {USED(c); KeyDown(&in_use);}
+static void IN_UseUp (cmd_t *c) {USED(c); KeyUp(&in_use);}
+static void IN_JumpDown (cmd_t *c) {USED(c); KeyDown(&in_jump);}
+static void IN_JumpUp (cmd_t *c) {USED(c); KeyUp(&in_jump);}
 
 static void
-IN_Impulse(void)
+IN_Impulse(cmd_t *c)
 {
+	USED(c);
 	in_impulse = atoi(Cmd_Argv(1));
 }
 
--- a/cl_main.c
+++ b/cl_main.c
@@ -78,7 +78,7 @@
 	memset(&cl, 0, sizeof cl);
 
 	if(cls.state == ca_connected)
-		stopallsfx();
+		stopallsfx(nil);
 
 	SZ_Clear (&cls.message);
 	CL_ResetTEnts();
@@ -106,7 +106,7 @@
 */
 void CL_Disconnect (void)
 {
-	stopallsfx();
+	stopallsfx(nil);
 
 	// bring the console down and fade the colors back to normal
 	SCR_BringDownConsole ();
@@ -116,7 +116,7 @@
 		abortdemo();
 	else if (cls.state == ca_connected){
 		if (cls.demorecording)
-			stopdemo();
+			stopdemo(nil);
 
 		Con_DPrintf("CL_Disconnect: sending clc_disconnect...\n");
 		SZ_Clear (&cls.message);
@@ -134,9 +134,10 @@
 	cls.signon = 0;
 }
 
-void CL_Disconnect_f (void)
+void CL_Disconnect_f(cmd_t *c)
 {
-	CL_Disconnect ();
+	USED(c);
+	CL_Disconnect();
 	if (sv.active)
 		Host_ShutdownServer (false);
 }
@@ -247,11 +248,12 @@
 CL_PrintEntities_f
 ==============
 */
-static void CL_PrintEntities_f (void)
+static void CL_PrintEntities_f(cmd_t *c)
 {
 	entity_t	*ent;
 	int			i;
 
+	USED(c);
 	for (i=0,ent=cl_entities ; i<cl.num_entities ; i++,ent++)
 	{
 		if (!ent->model)
--- a/client.h
+++ b/client.h
@@ -261,7 +261,7 @@
 void CL_Signon4 (void);
 
 void CL_Disconnect (void);
-void CL_Disconnect_f (void);
+void CL_Disconnect_f (cmd_t *c);
 bool CL_NextDemo (void);
 
 #define			MAX_VISEDICTS	65536
--- a/cmd.c
+++ b/cmd.c
@@ -1,18 +1,5 @@
 #include "quakedef.h"
 
-void Cmd_ForwardToServer (void);
-
-#define	MAX_ALIAS_NAME	32
-
-typedef struct cmdalias_s
-{
-	struct cmdalias_s	*next;
-	char	name[MAX_ALIAS_NAME];
-	char	*value;
-} cmdalias_t;
-
-static cmdalias_t	*cmd_alias;
-
 static bool	cmd_wait;
 
 //=============================================================================
@@ -26,8 +13,9 @@
 bind g "impulse 5 ; +attack ; wait ; -attack ; impulse 2"
 ============
 */
-static void Cmd_Wait_f (void)
+static void Cmd_Wait_f (cmd_t *c)
 {
+	USED(c);
 	cmd_wait = true;
 }
 
@@ -180,11 +168,12 @@
 ===============
 */
 static void
-Cmd_Exec_f(void)
+Cmd_Exec_f(cmd_t *c)
 {
 	char	*f;
 	void	*mark;
 
+	USED(c);
 	if (Cmd_Argc () != 2)
 	{
 		Con_Printf ("exec <filename> : execute a script file\n");
@@ -212,10 +201,11 @@
 ===============
 */
 static void
-Cmd_Echo_f(void)
+Cmd_Echo_f(cmd_t *c)
 {
 	int		i;
 
+	USED(c);
 	for (i=1 ; i<Cmd_Argc() ; i++)
 		Con_Printf ("%s ",Cmd_Argv(i));
 	Con_Printf ("\n");
@@ -231,6 +221,23 @@
 	return out;
 }
 
+static void
+Cmd_AliasList(char *name, void *v, void *aux)
+{
+	cmd_t *a;
+
+	USED(aux);
+	a = v;
+	if(iscmd(a) && a->alias != nil)
+		Con_Printf("  %s : %s\n", name, a->alias);
+}
+
+static void
+Cmd_ExecAlias_f(cmd_t *a)
+{
+	Cbuf_InsertText(a->alias);
+}
+
 /*
 ===============
 Cmd_Alias_f
@@ -239,58 +246,47 @@
 ===============
 */
 static void
-Cmd_Alias_f(void)
+Cmd_Alias_f(cmd_t *t)
 {
-	cmdalias_t	*a;
-	char		cmd[1024];
-	int			i, c;
-	char		*s;
+	char *s, cmd[1024];
+	cmd_t *a;
+	int i, c;
 
-	if (Cmd_Argc() == 1)
-	{
+	USED(t);
+	if(Cmd_Argc() == 1){
 		Con_Printf ("Current alias commands:\n");
-		for (a = cmd_alias ; a ; a=a->next)
-			Con_Printf ("%s : %s\n", a->name, a->value);
+		Con_SearchObject("", 0, Cmd_AliasList, nil);
 		return;
 	}
 
 	s = Cmd_Argv(1);
-	if (strlen(s) >= MAX_ALIAS_NAME)
-	{
-		Con_Printf ("Alias name is too long\n");
+	if(strlen(Cmd_Args()) - strlen(s) >= sizeof(cmd)){
+		Con_Printf("Alias commands are too long\n");
 		return;
 	}
 
-	// if the alias already exists, reuse it
-	for (a = cmd_alias ; a ; a=a->next)
-	{
-		if(strcmp(s, a->name) == 0)
-		{
-			Z_Free (a->value);
-			break;
+	if((a = Con_FindObject(s)) != nil){
+		if(!iscmd(a) || a->alias == nil){
+			Con_Printf("Can't register alias %s, already defined\n", s);
+			return;
 		}
+		Z_Free(a->alias);
 	}
 
-	if (a == nil)
-	{
-		a = Z_Malloc(sizeof *a);
-		a->next = cmd_alias;
-		cmd_alias = a;
-	}
-	strcpy (a->name, s);
+	if(a == nil)
+		a = Cmd_AddCommand(CopyString(s), Cmd_ExecAlias_f);
 
 	// copy the rest of the command line
 	cmd[0] = 0;		// start out with a null string
 	c = Cmd_Argc();
-	for (i=2 ; i< c ; i++)
-	{
-		strcat (cmd, Cmd_Argv(i));
-		if (i != c)
-			strcat (cmd, " ");
+	for(i = 2; i < c; i++){
+		strcat(cmd, Cmd_Argv(i));
+		if(i != c)
+			strcat(cmd, " ");
 	}
-	strcat (cmd, "\n");
+	strcat(cmd, "\n");
 
-	a->value = CopyString (cmd);
+	a->alias = CopyString(cmd);
 }
 
 /*
@@ -310,9 +306,6 @@
 
 cmd_source_t	cmd_source;
 
-
-static	cmd_function_t	*cmd_functions;		// possible commands to execute
-
 /*
 ============
 Cmd_Init
@@ -320,9 +313,9 @@
 */
 void Cmd_Init (void)
 {
-	Cmd_AddCommand ("exec",Cmd_Exec_f);
-	Cmd_AddCommand ("echo",Cmd_Echo_f);
-	Cmd_AddCommand ("alias",Cmd_Alias_f);
+	Cmd_AddCommand ("exec", Cmd_Exec_f);
+	Cmd_AddCommand ("echo", Cmd_Echo_f);
+	Cmd_AddCommand ("alias", Cmd_Alias_f);
 	Cmd_AddCommand ("cmd", Cmd_ForwardToServer);
 	Cmd_AddCommand ("wait", Cmd_Wait_f);
 }
@@ -373,8 +366,10 @@
 	int		i;
 
 	// clear the args from the last string
-	for (i=0 ; i<cmd_argc ; i++)
-		Z_Free (cmd_argv[i]);
+	for(i = 0; i < cmd_argc; i++){
+		Z_Free(cmd_argv[i]);
+		cmd_argv[i] = nil;
+	}
 
 	cmd_argc = 0;
 	cmd_args = nil;
@@ -415,84 +410,25 @@
 Cmd_AddCommand
 ============
 */
-cmd_function_t *
-Cmd_AddCommand (char *cmd_name, xcommand_t function)
+cmd_t *
+Cmd_AddCommand(char *name, cmdfun_t f)
 {
-	cmd_function_t	*cmd;
+	cmd_t	*cmd;
 
-	if (host_initialized)	// because hunk allocation would get stomped
-		fatal("Cmd_AddCommand after host_initialized");
-
-	// fail if the command is a variable name
-	if (Cvar_VariableString(cmd_name)[0])
-	{
-		Con_Printf ("Cmd_AddCommand: %s already defined as a var\n", cmd_name);
+	if(Con_FindObject(name) != nil){
+		Con_Printf("Cmd_AddCommand: %s already defined\n", name);
 		return nil;
 	}
 
-	// fail if the command already exists
-	for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
-	{
-		if(strcmp(cmd_name, cmd->name) == 0)
-		{
-			Con_Printf ("Cmd_AddCommand: %s already defined\n", cmd_name);
-			return cmd;
-		}
-	}
-
-	cmd = Hunk_Alloc(sizeof *cmd);
-	cmd->name = cmd_name;
-	cmd->function = function;
-	cmd->next = cmd_functions;
-	cmd_functions = cmd;
+	cmd = Z_Malloc(sizeof *cmd);
+	cmd->name = name;
+	cmd->f = f;
+	Con_AddObject(name, cmd);
 	return cmd;
 }
 
 /*
 ============
-Cmd_Exists
-============
-*/
-bool	Cmd_Exists (char *cmd_name)
-{
-	cmd_function_t	*cmd;
-
-	for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
-	{
-		if(strcmp(cmd_name, cmd->name) == 0)
-			return true;
-	}
-
-	return false;
-}
-
-
-
-/*
-============
-Cmd_CompleteCommand
-============
-*/
-char *Cmd_CompleteCommand (char *partial)
-{
-	cmd_function_t	*cmd;
-	int				len;
-
-	len = strlen(partial);
-
-	if (!len)
-		return nil;
-
-	// check functions
-	for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
-		if(strncmp(partial, cmd->name, len) == 0 && !cmd->hidden)
-			return cmd->name;
-
-	return nil;
-}
-
-/*
-============
 Cmd_ExecuteString
 
 A complete command line has been parsed, so try to execute it
@@ -499,40 +435,26 @@
 FIXME: lookupnoadd the token to speed search?
 ============
 */
-void	Cmd_ExecuteString (char *text, cmd_source_t src)
+void
+Cmd_ExecuteString (char *text, cmd_source_t src)
 {
-	cmd_function_t	*cmd;
-	cmdalias_t		*a;
+	cmd_t *cmd;
 
 	cmd_source = src;
 	Cmd_TokenizeString (text);
 
 	// execute the command line
-	if (!Cmd_Argc())
+	if(!Cmd_Argc())
 		return;		// no tokens
 
-	// check functions
-	for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
-	{
-		if(cistrcmp(cmd_argv[0],cmd->name) == 0)
-		{
-			cmd->function ();
-			return;
-		}
+	cmd = Con_FindObject(cmd_argv[0]);
+	if(cmd != nil && iscmd(cmd)){
+		cmd->f(cmd);
+		return;
 	}
 
-	// check alias
-	for (a=cmd_alias ; a ; a=a->next)
-	{
-		if(cistrcmp(cmd_argv[0], a->name) == 0)
-		{
-			Cbuf_InsertText (a->value);
-			return;
-		}
-	}
-
 	// check cvars
-	if (!Cvar_Command ())
+	if (!Cvar_Command())
 		Con_Printf ("Unknown command \"%s\": %s\n", Cmd_Argv(0), text);
 
 }
@@ -545,8 +467,9 @@
 Sends the entire command line over to the server
 ===================
 */
-void Cmd_ForwardToServer (void)
+void Cmd_ForwardToServer (cmd_t *c)
 {
+	USED(c);
 	if (cls.state != ca_connected)
 	{
 		Con_Printf ("Can't \"%s\", not connected\n", Cmd_Argv(0));
--- a/cmd.h
+++ b/cmd.h
@@ -46,16 +46,22 @@
 
 */
 
-typedef void (*xcommand_t) (void);
+typedef struct cmd_t cmd_t;
 
-typedef struct cmd_function_s
+typedef void (*cmdfun_t)(cmd_t *cmd);
+
+struct cmd_t
 {
-	struct cmd_function_s	*next;
-	char					*name;
-	xcommand_t				function;
+	char *name;
+	char *_dummy;
 	bool hidden;
-} cmd_function_t;
+	cmdfun_t f;
+	char *alias;
+};
 
+#define iscmd(p) (((cmd_t*)(p))->_dummy == nil)
+#define ishiddencmd(p) (iscmd(p) && ((cmd_t*)(p))->hidden)
+
 typedef enum
 {
 	src_client,		// came in over a net connection as a clc_stringcmd
@@ -67,7 +73,7 @@
 
 void	Cmd_Init (void);
 
-cmd_function_t *Cmd_AddCommand (char *cmd_name, xcommand_t function);
+cmd_t *Cmd_AddCommand(char *cmd_name, cmdfun_t f);
 // called by the init functions of other parts of the program to
 // register commands and functions to call for them.
 // The cmd_name is referenced later, so it should not be in temp memory
@@ -90,7 +96,7 @@
 // Parses a single line of text into arguments and tries to execute it.
 // The text can come from the command buffer, a remote client, or stdin.
 
-void	Cmd_ForwardToServer (void);
+void	Cmd_ForwardToServer (cmd_t *c);
 // adds the current command line as a clc_stringcmd to the client message.
 // things like godmode, noclip, etc, are commands directed to the server,
 // so when they are typed in at the console, they will need to be forwarded.
--- a/console.c
+++ b/console.c
@@ -1,4 +1,5 @@
 #include "quakedef.h"
+#include "qp.h"
 
 static int con_linewidth;
 
@@ -33,12 +34,57 @@
 
 int			con_notifylines;		// scan lines to clear for notify lines
 
-extern void M_Menu_Main_f (void);
+extern void M_Menu_Main_f (cmd_t *c);
 
+static Trie *conobj;
 
 void
-Con_ToggleConsole_f(void)
+Con_AddObject(char *name, void *obj)
 {
+	conobj = qpset(conobj, name, 0, obj);
+}
+
+void *
+Con_FindObject(char *name)
+{
+	char *k;
+	void *v;
+
+	if(qpget(conobj, name, 0, &k, &v) != 0)
+		v = nil;
+	return v;
+}
+
+int
+Con_SearchObject(char *prefix, int len0, void (*f)(char *name, void *obj, void *aux), void *aux)
+{
+	char *k;
+	void *v;
+	int n, len, c;
+
+	if(conobj == nil)
+		return 0;
+
+	k = nil;
+	v = nil;
+	len = 0;
+	for(n = 0;;){
+		if(qpnext(conobj, &k, &len, &v) < 0)
+			break;
+		if(len0 == 0 || (len >= len0 && (c = strncmp(k, prefix, len0)) == 0)){
+			f(k, v, aux);
+			n++;
+		}else if(c > 0)
+			break;
+	}
+
+	return n;
+}
+
+void
+Con_ToggleConsole_f(cmd_t *c)
+{
+	USED(c);
 	if(key_dest == key_console){
 		if(cls.state == ca_connected){
 			key_dest = key_game;
@@ -46,7 +92,7 @@
 			key_linepos = 1;
 			IN_Grabm(1);
 		}else
-			M_Menu_Main_f();
+			M_Menu_Main_f(c);
 	}else{
 		key_dest = key_console;
 		IN_Grabm(0);
@@ -57,8 +103,9 @@
 }
 
 void
-Con_Clear_f(void)
+Con_Clear_f(cmd_t *c)
 {
+	USED(c);
 	if(con_text)
 		memset(con_text, ' ', CON_TEXTSIZE);
 }
@@ -83,8 +130,9 @@
 extern bool team_message;
 
 static void
-Con_MessageMode_f(void)
+Con_MessageMode_f(cmd_t *c)
 {
+	USED(c);
 	key_dest = key_message;
 	team_message = false;
 }
@@ -96,8 +144,9 @@
 ================
 */
 static void
-Con_MessageMode2_f(void)
+Con_MessageMode2_f(cmd_t *c)
 {
+	USED(c);
 	key_dest = key_message;
 	team_message = true;
 }
--- a/console.h
+++ b/console.h
@@ -17,7 +17,11 @@
 void Con_Printf (char *fmt, ...);
 #pragma varargck	argpos	Con_DPrintf	1
 void Con_DPrintf (char *fmt, ...);
-void Con_Clear_f (void);
+void Con_Clear_f (cmd_t *c);
 void Con_DrawNotify (void);
 void Con_ClearNotify (void);
-void Con_ToggleConsole_f (void);
+void Con_ToggleConsole_f (cmd_t *c);
+
+void Con_AddObject(char *name, void *obj);
+void *Con_FindObject(char *name);
+int Con_SearchObject(char *prefix, int len, void (*f)(char *, void *, void *), void *aux);
--- a/cvar.c
+++ b/cvar.c
@@ -1,20 +1,18 @@
 #include "quakedef.h"
 
-cvar_t	*cvar_vars;
-
 /*
 ============
 Cvar_FindVar
 ============
 */
-cvar_t *Cvar_FindVar (char *var_name)
+cvar_t *Cvar_FindVar (char *name)
 {
-	cvar_t	*var;
+	cvar_t *v;
 
-	for (var=cvar_vars ; var ; var=var->next)
-		if(strcmp(var_name, var->name) == 0)
-			return var;
-	return nil;
+	v = Con_FindObject(name);
+	if(v != nil && iscmd(v))
+		v = nil;
+	return v;
 }
 
 /*
@@ -48,29 +46,8 @@
 	return var->string;
 }
 
-
 /*
 ============
-Cvar_CompleteVariable
-============
-*/
-char *Cvar_CompleteVariable (char *partial)
-{
-	cvar_t		*cvar;
-	int			len;
-
-	len = strlen(partial);
-	if (!len)
-		return nil;
-	// check functions
-	for (cvar=cvar_vars ; cvar ; cvar=cvar->next)
-		if(strncmp(partial, cvar->name, len) == 0)
-			return cvar->name;
-	return nil;
-}
-
-/*
-============
 Cvar_RegisterVariable
 
 Adds a freestanding variable to the variable list.
@@ -81,28 +58,17 @@
 	char	*oldstr;
 
 	// first check to see if it has already been defined
-	if (Cvar_FindVar (variable->name))
-	{
-		Con_Printf ("Can't register variable %s, already defined\n", variable->name);
+	if(Con_FindObject(variable->name) != nil){
+		Con_Printf("Can't register variable %s, already defined\n", variable->name);
 		return;
 	}
 
-	// check for overlap with a command
-	if (Cmd_Exists (variable->name))
-	{
-		Con_Printf ("Cvar_RegisterVariable: %s is a command\n", variable->name);
-		return;
-	}
-
 	// copy the value off, because future sets will Z_Free it
 	oldstr = variable->string;
 	variable->string = Z_Malloc(strlen(variable->string)+1);
 	strcpy(variable->string, oldstr);
 	variable->value = atof(variable->string);
-
-	// link the variable in
-	variable->next = cvar_vars;
-	cvar_vars = variable;
+	Con_AddObject(variable->name, variable);
 }
 
 /*
@@ -117,8 +83,8 @@
 	cvar_t			*v;
 
 	// check variables
-	v = Cvar_FindVar (Cmd_Argv(0));
-	if (!v)
+	v = Cvar_FindVar(Cmd_Argv(0));
+	if(v == nil)
 		return false;
 
 	// perform a variable print or set
--- a/cvar.h
+++ b/cvar.h
@@ -40,7 +40,6 @@
 	char *string;
 	bool archive; // set to true to cause it to be saved to vars.rc
 	float value;
-	struct cvar_s *next;
 	void (*cb) (cvar_t *);
 	bool server;		// notifies players when changed
 };
--- a/fns.h
+++ b/fns.h
@@ -10,9 +10,9 @@
 void	pausecd(void);
 void	resumecd(void);
 void	playcd(int, bool);
-void	cdcmd(void);
+void	cdcmd(cmd_t *);
 void	shutcd(void);
-void	stopallsfx(void);
+void	stopallsfx(cmd_t *);
 void	stopsfx(int, int);
 void	startsfx(int, int, Sfx *, vec3_t, float, float);
 void	localsfx(char *);
@@ -27,11 +27,11 @@
 void	setcvar(char*, char*);
 void	setcvarv(char*, float);
 void	abortdemo(void);
-void	stopdemo(void);
+void	stopdemo(cmd_t *);
 int	readcl(void);
-void	timedemo(void);
-void	recdemo(void);
-void	playdemo(void);
+void	timedemo(cmd_t *);
+void	recdemo(cmd_t *);
+void	playdemo(cmd_t *);
 void	crc(u8int);
 void	initcrc(void);
 char*	ext(char*, char*);
@@ -39,7 +39,7 @@
 void*	loadhunklmp(char *, int *);
 void*	loadcachelmp(char *, mem_user_t *);
 void*	loadstklmp(char *, void *, int, int *);
-void	loadpoints(void);
+void	loadpoints(cmd_t *);
 FILE*	openlmp(char *f, int *len);
 FILE*	createfile(char *path);
 void	removefile(char *path);
--- a/fs.c
+++ b/fs.c
@@ -143,10 +143,11 @@
 }
 
 static void
-path(void)
+path(cmd_t *c)
 {
 	Paklist *pl;
 
+	USED(c);
 	for(pl=pkl; pl!=nil; pl=pl->pl)
 		if(pl->p)
 			Con_Printf(va("%s (%zd files)\n", pl->p->f, pl->p->e - pl->p->l));
@@ -427,7 +428,7 @@
 }
 
 void
-loadpoints(void)
+loadpoints(cmd_t *c)
 {
 	int i, n, nv;
 	FILE *bf;
@@ -435,6 +436,7 @@
 	vec_t *v;
 	particle_t *p;
 
+	USED(c);
 	bf = openlmp(va("maps/%s.pts", sv.name), &n);
 	if(bf == nil){
 		Con_Printf(va("loadpoints: %s\n", lerr()));
@@ -469,17 +471,22 @@
 }
 
 static void
-dumpcvars(FILE *bf)
+dumpcvar(char *name, void *o, void *bf)
 {
 	cvar_t *c;
 
-	for(c=cvar_vars; c!=nil; c=c->next)
-		if(c->archive)
-			if(fprintf(bf, "%s \"%s\"\n", c->name, c->string) < 0)
-				fatal("dumpcvars: %s", lerr());
+	c = o;
+	if(!iscmd(o) && c->archive && fprintf(bf, "%s \"%s\"\n", name, c->string) < 0)
+		fatal("dumpcvar: %s", lerr());
 }
 
 static void
+dumpcvars(FILE *bf)
+{
+	Con_SearchObject("", 0, dumpcvar, bf);
+}
+
+static void
 dumpkeys(FILE *bf)
 {
 	char **k;
@@ -687,7 +694,7 @@
 	setcvarv("skill", (float)current_skill);
 	if(s = frdlinedup(bf), s == nil)
 		goto exit;
-	CL_Disconnect_f();
+	CL_Disconnect_f(nil);
 	SV_SpawnServer(s);
 	free(s);
 	if(!sv.active){
--- a/host_cmd.c
+++ b/host_cmd.c
@@ -8,13 +8,14 @@
 ==================
 */
 
-extern void M_Menu_Quit_f (void);
+extern void M_Menu_Quit_f (cmd_t *c);
 
-void Host_Quit_f (void)
+void Host_Quit_f (cmd_t *c)
 {
+	USED(c);
 	if (key_dest != key_console && cls.state != ca_dedicated)
 	{
-		M_Menu_Quit_f ();
+		M_Menu_Quit_f (c);
 		return;
 	}
 	CL_Disconnect ();
@@ -30,7 +31,7 @@
 ==================
 */
 static void
-Host_Status_f(void)
+Host_Status_f(cmd_t *c)
 {
 	client_t	*client;
 	int			seconds;
@@ -39,11 +40,12 @@
 	int			j;
 	void		(*print) (char *fmt, ...);
 
+	USED(c);
 	if (cmd_source == src_command)
 	{
 		if (!sv.active)
 		{
-			Cmd_ForwardToServer ();
+			Cmd_ForwardToServer (nil);
 			return;
 		}
 		print = Con_Printf;
@@ -84,11 +86,12 @@
 ==================
 */
 static void
-Host_God_f(void)
+Host_God_f(cmd_t *c)
 {
+	USED(c);
 	if (cmd_source == src_command)
 	{
-		Cmd_ForwardToServer ();
+		Cmd_ForwardToServer (nil);
 		return;
 	}
 
@@ -103,11 +106,12 @@
 }
 
 static void
-Host_Notarget_f(void)
+Host_Notarget_f(cmd_t *c)
 {
+	USED(c);
 	if (cmd_source == src_command)
 	{
-		Cmd_ForwardToServer ();
+		Cmd_ForwardToServer (nil);
 		return;
 	}
 
@@ -124,11 +128,12 @@
 bool noclip_anglehack;
 
 static void
-Host_Noclip_f(void)
+Host_Noclip_f(cmd_t *c)
 {
+	USED(c);
 	if (cmd_source == src_command)
 	{
-		Cmd_ForwardToServer ();
+		Cmd_ForwardToServer (nil);
 		return;
 	}
 
@@ -157,11 +162,12 @@
 ==================
 */
 static void
-Host_Fly_f(void)
+Host_Fly_f(cmd_t *c)
 {
+	USED(c);
 	if (cmd_source == src_command)
 	{
-		Cmd_ForwardToServer ();
+		Cmd_ForwardToServer (nil);
 		return;
 	}
 
@@ -188,15 +194,16 @@
 ==================
 */
 static void
-Host_Ping_f(void)
+Host_Ping_f(cmd_t *c)
 {
 	int		i, j;
 	float	total;
 	client_t	*client;
 
+	USED(c);
 	if (cmd_source == src_command)
 	{
-		Cmd_ForwardToServer ();
+		Cmd_ForwardToServer (nil);
 		return;
 	}
 
@@ -232,11 +239,12 @@
 ======================
 */
 static void
-Host_Map_f(void)
+Host_Map_f(cmd_t *c)
 {
 	int		i;
 	char	name[Npath];
 
+	USED(c);
 	if (cmd_source != src_command)
 		return;
 
@@ -284,10 +292,11 @@
 ==================
 */
 static void
-Host_Changelevel_f(void)
+Host_Changelevel_f(cmd_t *c)
 {
 	char	level[Npath];
 
+	USED(c);
 	if (Cmd_Argc() != 2)
 	{
 		Con_Printf ("changelevel <levelname> : continue game on a new level\n");
@@ -311,10 +320,11 @@
 ==================
 */
 static void
-Host_Restart_f(void)
+Host_Restart_f(cmd_t *c)
 {
 	char	mapname[Npath];
 
+	USED(c);
 	if (cls.demoplayback || !sv.active)
 		return;
 
@@ -327,8 +337,9 @@
 
 /* wait for signon messages; sent before server level change */
 static void
-reconnect(void)
+reconnect(cmd_t *c)
 {
+	USED(c);
 	SCR_BeginLoadingPlaque();
 	cls.signon = 0;
 }
@@ -341,10 +352,11 @@
 =====================
 */
 static void
-Host_Connect_f(void)
+Host_Connect_f(cmd_t *c)
 {
 	char	name[Npath];
 
+	USED(c);
 	cls.demonum = -1;		// stop demo loop in case this fails
 	if (cls.demoplayback)
 	{
@@ -353,7 +365,7 @@
 	}
 	strcpy (name, Cmd_Argv(1));
 	CL_EstablishConnection (name);
-	reconnect();
+	reconnect(c);
 }
 
 /*
@@ -362,10 +374,11 @@
 ======================
 */
 static void
-Host_Name_f(void)
+Host_Name_f(cmd_t *c)
 {
 	char	newName[16];
 
+	USED(c);
 	if (Cmd_Argc () == 1)
 	{
 		Con_Printf ("\"name\" is \"%s\"\n", cl_name.string);
@@ -383,7 +396,7 @@
 			return;
 		setcvar ("_cl_name", newName);
 		if (cls.state == ca_connected)
-			Cmd_ForwardToServer ();
+			Cmd_ForwardToServer (nil);
 		return;
 	}
 
@@ -401,8 +414,9 @@
 }
 
 static void
-Host_Version_f(void)
+Host_Version_f(cmd_t *c)
 {
+	USED(c);
 	Con_Printf("Version %4.2f\n", VERSION);
 	Con_Printf("Exe: <REDACTED>\n");
 }
@@ -426,7 +440,7 @@
 		}
 		else
 		{
-			Cmd_ForwardToServer ();
+			Cmd_ForwardToServer (nil);
 			return;
 		}
 	}
@@ -472,19 +486,21 @@
 }
 
 static void
-Host_Say_f(void)
+Host_Say_f(cmd_t *c)
 {
+	USED(c);
 	Host_Say(false);
 }
 
 static void
-Host_Say_Team_f(void)
+Host_Say_Team_f(cmd_t *c)
 {
+	USED(c);
 	Host_Say(true);
 }
 
 static void
-Host_Tell_f(void)
+Host_Tell_f(cmd_t *c)
 {
 	client_t *client;
 	client_t *save;
@@ -492,9 +508,10 @@
 	char	*p;
 	char	text[64];
 
+	USED(c);
 	if (cmd_source == src_command)
 	{
-		Cmd_ForwardToServer ();
+		Cmd_ForwardToServer (nil);
 		return;
 	}
 
@@ -542,11 +559,12 @@
 ==================
 */
 static void
-Host_Color_f(void)
+Host_Color_f(cmd_t *c)
 {
 	int		top, bottom;
 	int		playercolor;
 
+	USED(c);
 	if (Cmd_Argc() == 1)
 	{
 		Con_Printf ("\"color\" is \"%d %d\"\n", ((int)cl_color.value) >> 4, ((int)cl_color.value) & 0x0f);
@@ -575,7 +593,7 @@
 	{
 		setcvarv ("_cl_color", playercolor);
 		if (cls.state == ca_connected)
-			Cmd_ForwardToServer ();
+			Cmd_ForwardToServer (nil);
 		return;
 	}
 
@@ -594,11 +612,12 @@
 ==================
 */
 static void
-Host_Kill_f(void)
+Host_Kill_f(cmd_t *c)
 {
+	USED(c);
 	if (cmd_source == src_command)
 	{
-		Cmd_ForwardToServer ();
+		Cmd_ForwardToServer (nil);
 		return;
 	}
 
@@ -620,12 +639,12 @@
 ==================
 */
 static void
-Host_Pause_f(void)
+Host_Pause_f(cmd_t *c)
 {
-
+	USED(c);
 	if (cmd_source == src_command)
 	{
-		Cmd_ForwardToServer ();
+		Cmd_ForwardToServer (nil);
 		return;
 	}
 	if (!pausable.value)
@@ -658,8 +677,9 @@
 ==================
 */
 static void
-Host_PreSpawn_f(void)
+Host_PreSpawn_f(cmd_t *c)
 {
+	USED(c);
 	if(cmd_source == src_command){
 		Con_Printf("prespawn is not valid from the console\n");
 		return;
@@ -679,7 +699,7 @@
 ==================
 */
 static void
-Host_Spawn_f(void)
+Host_Spawn_f(cmd_t *c)
 {
 	int		i;
 	client_t	*client;
@@ -686,6 +706,7 @@
 	edict_t	*ent;
 	float *a;
 
+	USED(c);
 	if (cmd_source == src_command)
 	{
 		Con_Printf ("spawn is not valid from the console\n");
@@ -803,8 +824,9 @@
 ==================
 */
 static void
-Host_Begin_f(void)
+Host_Begin_f(cmd_t *c)
 {
+	USED(c);
 	if (cmd_source == src_command)
 	{
 		Con_Printf ("begin is not valid from the console\n");
@@ -837,11 +859,12 @@
 }
 
 static void
-savegame(void)
+savegame(cmd_t *cmd)
 {
 	char *s, *a, cm[Nsavcm];
 	client_t *c, *e;
 
+	USED(cmd);
 	if(cmd_source != src_command)
 		return;
 	else if(!sv.active){
@@ -878,7 +901,7 @@
 }
 
 static void
-loadgame(void)
+loadgame(cmd_t *c)
 {
 	char *s, *a;
 
@@ -901,7 +924,7 @@
 	}
 	if(cls.state != ca_dedicated){
 		CL_EstablishConnection("local");
-		reconnect();
+		reconnect(c);
 	}
 }
 
@@ -913,7 +936,7 @@
 ==================
 */
 static void
-Host_Kick_f(void)
+Host_Kick_f(cmd_t *c)
 {
 	char		*who;
 	char		*message = nil;
@@ -921,11 +944,12 @@
 	int			i;
 	bool	byNumber = false;
 
+	USED(c);
 	if (cmd_source == src_command)
 	{
 		if (!sv.active)
 		{
-			Cmd_ForwardToServer ();
+			Cmd_ForwardToServer (nil);
 			return;
 		}
 	}
@@ -1006,15 +1030,16 @@
 ==================
 */
 static void
-Host_Give_f(void)
+Host_Give_f(cmd_t *c)
 {
 	char	*t;
 	int		v;
 	eval_t	*val;
 
+	USED(c);
 	if (cmd_source == src_command)
 	{
-		Cmd_ForwardToServer ();
+		Cmd_ForwardToServer (nil);
 		return;
 	}
 
@@ -1182,11 +1207,12 @@
 ==================
 */
 static void
-Host_Viewmodel_f(void)
+Host_Viewmodel_f(cmd_t *c)
 {
 	edict_t	*e;
 	model_t	*m;
 
+	USED(c);
 	e = FindViewthing ();
 	if (!e)
 		return;
@@ -1208,12 +1234,13 @@
 ==================
 */
 static void
-Host_Viewframe_f(void)
+Host_Viewframe_f(cmd_t *c)
 {
 	edict_t	*e;
 	int		f;
 	model_t	*m;
 
+	USED(c);
 	e = FindViewthing ();
 	if (!e)
 		return;
@@ -1246,11 +1273,12 @@
 ==================
 */
 static void
-Host_Viewnext_f(void)
+Host_Viewnext_f(cmd_t *c)
 {
 	edict_t	*e;
 	model_t	*m;
 
+	USED(c);
 	e = FindViewthing ();
 	if (!e)
 		return;
@@ -1269,11 +1297,12 @@
 ==================
 */
 static void
-Host_Viewprev_f(void)
+Host_Viewprev_f(cmd_t *c)
 {
 	edict_t	*e;
 	model_t	*m;
 
+	USED(c);
 	e = FindViewthing ();
 	if (!e)
 		return;
@@ -1302,10 +1331,11 @@
 ==================
 */
 static void
-Host_Startdemos_f(void)
+Host_Startdemos_f(cmd_t *cmd)
 {
 	int		i, c;
 
+	USED(cmd);
 	if (cls.state == ca_dedicated)
 	{
 		if (!sv.active)
@@ -1342,13 +1372,14 @@
 ==================
 */
 static void
-Host_Demos_f(void)
+Host_Demos_f(cmd_t *c)
 {
+	USED(c);
 	if (cls.state == ca_dedicated)
 		return;
 	if (cls.demonum == -1)
 		cls.demonum = 1;
-	CL_Disconnect_f ();
+	CL_Disconnect_f (nil);
 	CL_NextDemo ();
 }
 
@@ -1360,8 +1391,9 @@
 ==================
 */
 static void
-Host_Stopdemo_f(void)
+Host_Stopdemo_f(cmd_t *c)
 {
+	USED(c);
 	if (cls.state == ca_dedicated)
 		return;
 	if (!cls.demoplayback)
@@ -1417,5 +1449,5 @@
 	Cmd_AddCommand ("viewnext", Host_Viewnext_f);
 	Cmd_AddCommand ("viewprev", Host_Viewprev_f);
 
-	Cmd_AddCommand ("mcache", Mod_Print);
+	Cmd_AddCommand ("mcache", Mod_Print_f);
 }
--- a/keys.c
+++ b/keys.c
@@ -129,7 +129,30 @@
 ==============================================================================
 */
 
+static void
+Key_Complete(char *name, void *o, void *aux)
+{
+	static int printed = 0;
+	int n;
+	char *longest;
 
+	if(ishiddencmd(o))
+		return;
+	longest = aux;
+	if(longest[0] == 0){
+		strcpy(longest, name);
+		printed = 0;
+		return;
+	}
+	if(!printed){
+		Con_Printf("\n  %s\n", longest);
+		printed = 1;
+	}
+	Con_Printf("  %s (%s)\n", name, iscmd(o) ? "cmd" : "cvar");
+	for(n = 0; longest[n] == name[n]; n++);
+	longest[n] = 0;
+}
+
 /*
 ====================
 Key_Console
@@ -140,7 +163,7 @@
 static void
 Key_Console(int key)
 {
-	char *cmd, *s;
+	char longest[128], *s;
 	int n;
 
 	s = key_lines[edit_line];
@@ -158,17 +181,18 @@
 		return;
 	}
 
-	if(key == K_TAB){	// command completion
-		if((cmd = Cmd_CompleteCommand(s+1)) == nil)
-			cmd = Cvar_CompleteVariable(s+1);
-		if(cmd){
-			strcpy(s+1, cmd);
-			key_linepos = strlen(cmd)+1;
-			s[key_linepos] = ' ';
-			key_linepos++;
-			s[key_linepos] = 0;
+	if(key == K_TAB){	// command/cvar completion
+		longest[0] = 0;
+		if(key_linepos < 2)
 			return;
+		if((n = Con_SearchObject(s+1, key_linepos-1, Key_Complete, longest)) >= 1 && longest[0] != 0){
+			key_linepos = strlen(longest);
+			memmove(s+1, longest, key_linepos);
+			if(n == 1)
+				s[++key_linepos] = ' ';
+			s[++key_linepos] = 0;
 		}
+		return;
 	}
 
 	if(key == K_LEFTARROW){
@@ -410,10 +434,11 @@
 ===================
 */
 static void
-Key_Unbind_f(void)
+Key_Unbind_f(cmd_t *c)
 {
 	int		b;
 
+	USED(c);
 	if (Cmd_Argc() != 2)
 	{
 		Con_Printf ("unbind <key> : remove commands from a key\n");
@@ -431,13 +456,14 @@
 }
 
 static void
-Key_Unbindall_f(void)
+Key_Unbindall_f(cmd_t *c)
 {
 	int		i;
 
-	for (i=0 ; i<256 ; i++)
-		if (keybindings[i])
-			Key_SetBinding (i, "");
+	USED(c);
+	for(i = 0; i < 256; i++)
+		if(keybindings[i])
+			Key_SetBinding(i, "");
 }
 
 
@@ -447,13 +473,13 @@
 ===================
 */
 static void
-Key_Bind_f(void)
+Key_Bind_f(cmd_t *t)
 {
 	int			i, c, b;
 	char		cmd[1024];
 
+	USED(t);
 	c = Cmd_Argc();
-
 	if (c != 2 && c != 3)
 	{
 		Con_Printf ("bind <key> [command] : attach a command to a key\n");
@@ -553,9 +579,9 @@
 		menubound[K_F1+i] = true;
 
 	// register our functions
-	Cmd_AddCommand ("bind",Key_Bind_f);
-	Cmd_AddCommand ("unbind",Key_Unbind_f);
-	Cmd_AddCommand ("unbindall",Key_Unbindall_f);
+	Cmd_AddCommand ("bind", Key_Bind_f);
+	Cmd_AddCommand ("unbind", Key_Unbind_f);
+	Cmd_AddCommand ("unbindall", Key_Unbindall_f);
 
 
 }
@@ -614,7 +640,7 @@
 			break;
 		case key_game:
 		case key_console:
-			M_ToggleMenu_f ();
+			M_ToggleMenu_f (nil);
 			break;
 		default:
 			fatal ("Bad key_dest");
@@ -650,7 +676,7 @@
 	// during demo playback, most keys bring up the main menu
 	if (cls.demoplayback && down && consolekeys[key] && key_dest == key_game)
 	{
-		M_ToggleMenu_f ();
+		M_ToggleMenu_f (nil);
 		return;
 	}
 
--- a/menu.c
+++ b/menu.c
@@ -6,19 +6,19 @@
 
 enum {m_none, m_main, m_singleplayer, m_load, m_save, m_multiplayer, m_setup, m_net, m_options, m_keys, m_help, m_quit, m_lanconfig, m_gameoptions} m_state;
 
-void M_Menu_Main_f (void);
-	static void M_Menu_SinglePlayer_f (void);
-		static void M_Menu_Load_f (void);
-		static void M_Menu_Save_f (void);
-	static void M_Menu_MultiPlayer_f (void);
-		static void M_Menu_Setup_f (void);
-		static void M_Menu_Net_f (void);
-	static void M_Menu_Options_f (void);
-		static void M_Menu_Keys_f (void);
-	static void M_Menu_Help_f (void);
-	void M_Menu_Quit_f (void);
-static void M_Menu_LanConfig_f (void);
-static void M_Menu_GameOptions_f (void);
+void M_Menu_Main_f (cmd_t *c);
+	static void M_Menu_SinglePlayer_f (cmd_t *c);
+		static void M_Menu_Load_f (cmd_t *c);
+		static void M_Menu_Save_f (cmd_t *c);
+	static void M_Menu_MultiPlayer_f (cmd_t *c);
+		static void M_Menu_Setup_f (cmd_t *c);
+		static void M_Menu_Net_f (cmd_t *c);
+	static void M_Menu_Options_f (cmd_t *c);
+		static void M_Menu_Keys_f (cmd_t *c);
+	static void M_Menu_Help_f (cmd_t *c);
+	void M_Menu_Quit_f (cmd_t *c);
+static void M_Menu_LanConfig_f (cmd_t *c);
+static void M_Menu_GameOptions_f (cmd_t *c);
 
 static void M_Main_Draw (void);
 	static void M_SinglePlayer_Draw (void);
@@ -194,13 +194,14 @@
 static int m_save_demonum;
 
 void
-M_ToggleMenu_f(void)
+M_ToggleMenu_f(cmd_t *c)
 {
+	USED(c);
 	m_entersound = true;
 
 	if(key_dest == key_menu){
 		if(m_state != m_main){
-			M_Menu_Main_f();
+			M_Menu_Main_f(nil);
 			return;
 		}
 		key_dest = key_game;
@@ -208,10 +209,10 @@
 		return;
 	}
 	if(key_dest == key_console)
-		Con_ToggleConsole_f();
+		Con_ToggleConsole_f(nil);
 	else{
 		IN_Grabm(0);
-		M_Menu_Main_f();
+		M_Menu_Main_f(nil);
 	}
 }
 
@@ -223,8 +224,9 @@
 #define	MAIN_ITEMS	5
 
 
-void M_Menu_Main_f (void)
+void M_Menu_Main_f (cmd_t *c)
 {
+	USED(c);
 	if (key_dest != key_menu)
 	{
 		m_save_demonum = cls.demonum;
@@ -284,23 +286,23 @@
 		switch (m_main_cursor)
 		{
 		case 0:
-			M_Menu_SinglePlayer_f ();
+			M_Menu_SinglePlayer_f(nil);
 			break;
 
 		case 1:
-			M_Menu_MultiPlayer_f ();
+			M_Menu_MultiPlayer_f(nil);
 			break;
 
 		case 2:
-			M_Menu_Options_f ();
+			M_Menu_Options_f(nil);
 			break;
 
 		case 3:
-			M_Menu_Help_f ();
+			M_Menu_Help_f(nil);
 			break;
 
 		case 4:
-			M_Menu_Quit_f ();
+			M_Menu_Quit_f(nil);
 			break;
 		}
 	}
@@ -312,8 +314,9 @@
 static int	m_singleplayer_cursor;
 #define	SINGLEPLAYER_ITEMS	3
 
-static void M_Menu_SinglePlayer_f (void)
+static void M_Menu_SinglePlayer_f (cmd_t *c)
 {
+	USED(c);
 	key_dest = key_menu;
 	m_state = m_singleplayer;
 	m_entersound = true;
@@ -339,7 +342,7 @@
 	switch (key)
 	{
 	case K_ESCAPE:
-		M_Menu_Main_f ();
+		M_Menu_Main_f (nil);
 		break;
 
 	case K_DOWNARROW:
@@ -371,11 +374,11 @@
 			break;
 
 		case 1:
-			M_Menu_Load_f ();
+			M_Menu_Load_f (nil);
 			break;
 
 		case 2:
-			M_Menu_Save_f ();
+			M_Menu_Save_f (nil);
 			break;
 		}
 	}
@@ -386,8 +389,9 @@
 
 static int		load_cursor;		// 0 < load_cursor < Nsav
 
-static void M_Menu_Load_f (void)
+static void M_Menu_Load_f (cmd_t *c)
 {
+	USED(c);
 	m_entersound = true;
 	m_state = m_load;
 	key_dest = key_menu;
@@ -394,8 +398,9 @@
 	savnames();
 }
 
-static void M_Menu_Save_f (void)
+static void M_Menu_Save_f (cmd_t *c)
 {
+	USED(c);
 	if (!sv.active)
 		return;
 	if (cl.intermission)
@@ -443,7 +448,7 @@
 	switch (k)
 	{
 	case K_ESCAPE:
-		M_Menu_SinglePlayer_f ();
+		M_Menu_SinglePlayer_f (nil);
 		break;
 
 	case K_ENTER:
@@ -484,7 +489,7 @@
 	switch (k)
 	{
 	case K_ESCAPE:
-		M_Menu_SinglePlayer_f ();
+		M_Menu_SinglePlayer_f (nil);
 		break;
 
 	case K_ENTER:
@@ -517,8 +522,9 @@
 static int	m_multiplayer_cursor;
 #define	MULTIPLAYER_ITEMS	3
 
-static void M_Menu_MultiPlayer_f (void)
+static void M_Menu_MultiPlayer_f (cmd_t *c)
 {
+	USED(c);
 	key_dest = key_menu;
 	m_state = m_multiplayer;
 	m_entersound = true;
@@ -544,7 +550,7 @@
 	switch (key)
 	{
 	case K_ESCAPE:
-		M_Menu_Main_f ();
+		M_Menu_Main_f (nil);
 		break;
 
 	case K_DOWNARROW:
@@ -564,15 +570,15 @@
 		switch (m_multiplayer_cursor)
 		{
 		case 0:
-			M_Menu_Net_f ();
+			M_Menu_Net_f (nil);
 			break;
 
 		case 1:
-			M_Menu_Net_f ();
+			M_Menu_Net_f (nil);
 			break;
 
 		case 2:
-			M_Menu_Setup_f ();
+			M_Menu_Setup_f (nil);
 			break;
 		}
 	}
@@ -593,8 +599,9 @@
 
 #define	NUM_SETUP_CMDS	5
 
-static void M_Menu_Setup_f (void)
+static void M_Menu_Setup_f (cmd_t *c)
 {
+	USED(c);
 	key_dest = key_menu;
 	m_state = m_setup;
 	m_entersound = true;
@@ -648,7 +655,7 @@
 	switch (k)
 	{
 	case K_ESCAPE:
-		M_Menu_MultiPlayer_f ();
+		M_Menu_MultiPlayer_f (nil);
 		break;
 
 	case K_UPARROW:
@@ -700,7 +707,7 @@
 		if(setup_top != setup_oldtop || setup_bottom != setup_oldbottom)
 			Cbuf_AddText(va("color %d %d\n", setup_top, setup_bottom));
 		m_entersound = true;
-		M_Menu_MultiPlayer_f ();
+		M_Menu_MultiPlayer_f (nil);
 		break;
 
 	case K_BACKSPACE:
@@ -775,8 +782,9 @@
   " Area Network.          "
 };
 
-static void M_Menu_Net_f (void)
+static void M_Menu_Net_f (cmd_t *c)
 {
+	USED(c);
 	key_dest = key_menu;
 	m_state = m_net;
 	m_entersound = true;
@@ -832,7 +840,7 @@
 	switch (k)
 	{
 	case K_ESCAPE:
-		M_Menu_MultiPlayer_f ();
+		M_Menu_MultiPlayer_f (nil);
 		break;
 
 	case K_DOWNARROW:
@@ -856,7 +864,7 @@
 		case 1:
 			break;
 		case 2:
-			M_Menu_LanConfig_f ();
+			M_Menu_LanConfig_f (nil);
 			break;
 
 		default:
@@ -876,8 +884,9 @@
 
 static int		options_cursor;
 
-static void M_Menu_Options_f (void)
+static void M_Menu_Options_f (cmd_t *c)
 {
+	USED(c);
 	key_dest = key_menu;
 	m_state = m_options;
 	m_entersound = true;
@@ -1046,7 +1055,7 @@
 	switch (k)
 	{
 	case K_ESCAPE:
-		M_Menu_Main_f ();
+		M_Menu_Main_f (nil);
 		break;
 
 	case K_ENTER:
@@ -1054,11 +1063,11 @@
 		switch (options_cursor)
 		{
 		case 0:
-			M_Menu_Keys_f ();
+			M_Menu_Keys_f (nil);
 			break;
 		case 1:
 			m_state = m_none;
-			Con_ToggleConsole_f ();
+			Con_ToggleConsole_f (nil);
 			break;
 		case 2:
 			Cbuf_AddText ("exec default.cfg\n");
@@ -1128,8 +1137,9 @@
 static int		keys_cursor;
 static int		bind_grab;
 
-static void M_Menu_Keys_f (void)
+static void M_Menu_Keys_f (cmd_t *c)
 {
+	USED(c);
 	key_dest = key_menu;
 	m_state = m_keys;
 	m_entersound = true;
@@ -1248,7 +1258,7 @@
 	switch (k)
 	{
 	case K_ESCAPE:
-		M_Menu_Options_f ();
+		M_Menu_Options_f (nil);
 		break;
 
 	case K_LEFTARROW:
@@ -1289,8 +1299,9 @@
 static int		help_page;
 #define	NUM_HELP_PAGES	6
 
-static void M_Menu_Help_f (void)
+static void M_Menu_Help_f (cmd_t *c)
 {
+	USED(c);
 	key_dest = key_menu;
 	m_state = m_help;
 	m_entersound = true;
@@ -1307,7 +1318,7 @@
 	switch (key)
 	{
 	case K_ESCAPE:
-		M_Menu_Main_f ();
+		M_Menu_Main_f (nil);
 		break;
 
 	case K_UPARROW:
@@ -1378,8 +1389,9 @@
   "                        "
 };
 
-void M_Menu_Quit_f (void)
+void M_Menu_Quit_f (cmd_t *c)
 {
+	USED(c);
 	if (m_state == m_quit)
 		return;
 	wasInMenus = (key_dest == key_menu);
@@ -1412,7 +1424,7 @@
 	case 'Y':
 	case 'y':
 		key_dest = key_console;
-		Host_Quit_f ();
+		Host_Quit_f (nil);
 		break;
 
 	default:
@@ -1448,8 +1460,9 @@
 static char	lanConfig_portname[8];
 static char	lanConfig_joinname[22];
 
-static void M_Menu_LanConfig_f (void)
+static void M_Menu_LanConfig_f (cmd_t *c)
 {
+	USED(c);
 	key_dest = key_menu;
 	m_state = m_lanconfig;
 	m_entersound = true;
@@ -1516,7 +1529,7 @@
 	switch (key)
 	{
 	case K_ESCAPE:
-		M_Menu_Net_f ();
+		M_Menu_Net_f (nil);
 		break;
 
 	case K_UPARROW:
@@ -1546,7 +1559,7 @@
 		if (lanConfig_cursor == 1)
 		{
 			if(StartingGame){
-				M_Menu_GameOptions_f ();
+				M_Menu_GameOptions_f (nil);
 				break;
 			}else{
 				m_return_state = m_state;
@@ -1758,8 +1771,9 @@
 static bool m_serverInfoMessage = false;
 static double m_serverInfoMessageTime;
 
-static void M_Menu_GameOptions_f (void)
+static void M_Menu_GameOptions_f (cmd_t *c)
 {
+	USED(c);
 	key_dest = key_menu;
 	m_state = m_gameoptions;
 	m_entersound = true;
@@ -2004,7 +2018,7 @@
 	switch (key)
 	{
 	case K_ESCAPE:
-		M_Menu_Net_f ();
+		M_Menu_Net_f (nil);
 		break;
 
 	case K_UPARROW:
@@ -2076,6 +2090,8 @@
 	Cmd_AddCommand ("menu_keys", M_Menu_Keys_f);
 	Cmd_AddCommand ("help", M_Menu_Help_f);
 	Cmd_AddCommand ("menu_quit", M_Menu_Quit_f);
+	Cmd_AddCommand ("menu_lan_config", M_Menu_LanConfig_f);
+	Cmd_AddCommand ("menu_game_options", M_Menu_GameOptions_f);
 }
 
 void M_Draw (void)
--- a/menu.h
+++ b/menu.h
@@ -4,5 +4,5 @@
 void M_Init (void);
 void M_Keydown (int key);
 void M_Draw (void);
-void M_ToggleMenu_f (void);
+void M_ToggleMenu_f (cmd_t *c);
 void M_DrawPic(int x, int y, qpic_t *pic);
--- a/meson.build
+++ b/meson.build
@@ -73,6 +73,7 @@
 	'pr_edict.c',
 	'pr_exec.c',
 	'protocol.c',
+	'qp.c',
 	'r_aclip.c',
 	'r_alias.c',
 	'r_bsp.c',
--- a/mkfile
+++ b/mkfile
@@ -62,6 +62,7 @@
 	pr_edict.$O\
 	pr_exec.$O\
 	protocol.$O\
+	qp.$O\
 	r_aclip.$O\
 	r_alias.$O\
 	r_bsp.$O\
@@ -121,6 +122,7 @@
 	progdefs.h\
 	progs.h\
 	protocol.h\
+	qp.h\
 	quakedef.h\
 	r_local.h\
 	r_shared.h\
--- a/model.c
+++ b/model.c
@@ -282,16 +282,13 @@
 	return Mod_LoadModel(Mod_FindName(name), crash);
 }
 
-/*
-================
-Mod_Print
-================
-*/
-void Mod_Print (void)
+void
+Mod_Print_f(cmd_t *c)
 {
 	int		i;
 	model_t	*mod;
 
+	USED(c);
 	Con_Printf ("Cached models:\n");
 	for (i=0, mod=mod_known ; i < mod_numknown ; i++, mod++)
 	{
--- a/model.h
+++ b/model.h
@@ -384,7 +384,7 @@
 
 mleaf_t *Mod_PointInLeaf (const vec3_t p, model_t *model);
 byte	*Mod_LeafPVS (mleaf_t *leaf, model_t *model, int *sz);
-void	Mod_Print(void);
+void	Mod_Print_f(cmd_t *c);
 
 texture_t *Load_ExternalTexture(char *map, char *name);
 
--- a/net_dgrm_plan9.c
+++ b/net_dgrm_plan9.c
@@ -286,10 +286,11 @@
 	Con_Printf("\n");
 }
 
-void NET_Stats_f (void)
+void NET_Stats_f (cmd_t *c)
 {
 	qsocket_t	*s;
 
+	USED(c);
 	if (Cmd_Argc () == 1)
 	{
 		Con_Printf("unreliable messages sent   = %d\n", unreliableMessagesSent);
@@ -448,6 +449,7 @@
 
 	if (command == CQRUINFO)
 	{
+/* FIXME(sigrid): wtf is this MEGASHIT
 		char	*prevCvarName;
 		cvar_t	*var;
 
@@ -485,7 +487,7 @@
 		*((int *)net_message.data) = BigLong(NFCTL | (net_message.cursize & NFMASK));
 		dfunc.Write(net_message.data, net_message.cursize, &clientaddr);
 		SZ_Clear(&net_message);
-
+*/
 		goto done;
 	}
 
--- a/net_main.c
+++ b/net_main.c
@@ -161,10 +161,11 @@
 	sock->disconnected = true;
 }
 
-static void MaxPlayers_f (void)
+static void MaxPlayers_f (cmd_t *c)
 {
 	int 	n;
 
+	USED(c);
 	if (Cmd_Argc () != 2)
 	{
 		Con_Printf ("\"maxplayers\" is \"%d\"\n", svs.maxclients);
@@ -194,10 +195,11 @@
 }
 
 
-static void NET_Port_f (void)
+static void NET_Port_f (cmd_t *c)
 {
 	int 	n;
 
+	USED(c);
 	if (Cmd_Argc () != 2)
 	{
 		Con_Printf ("\"port\" is \"%s\"\n", myip.srv);
--- a/pr_cmds.c
+++ b/pr_cmds.c
@@ -1081,7 +1081,7 @@
 {
 	// FIXME(sigrid): needs to be pr-specific
 	USED(pr);
-	ED_PrintEdicts();
+	ED_PrintEdicts(nil);
 }
 
 static void
--- a/pr_edict.c
+++ b/pr_edict.c
@@ -532,10 +532,11 @@
 =============
 */
 void
-ED_PrintEdicts(void)
+ED_PrintEdicts(cmd_t *c)
 {
 	int		i;
 
+	USED(c);
 	Con_Printf ("%d entities\n", sv.pr->num_edicts);
 	for (i=0 ; i<sv.pr->num_edicts ; i++)
 		ED_PrintNum(sv.pr, i);
@@ -549,10 +550,11 @@
 =============
 */
 static void
-ED_PrintEdict_f(void)
+ED_PrintEdict_f(cmd_t *c)
 {
 	int		i;
 
+	USED(c);
 	i = atoi(Cmd_Argv(1));
 	if (i >= sv.pr->num_edicts)
 	{
@@ -570,12 +572,13 @@
 =============
 */
 static void
-ED_Count(void)
+ED_Count(cmd_t *c)
 {
 	int		i;
 	edict_t	*ent;
 	int		active, models, solid, step;
 
+	USED(c);
 	active = models = solid = step = 0;
 	for (i=0 ; i<sv.pr->num_edicts ; i++)
 	{
@@ -1132,6 +1135,7 @@
 	pl = &lumps[PR_LUMP_FIELDDEFS];
 	PR_FieldDefs(pr, in0 + pl->off, pl->num);
 	pr->edict_size = pr->entityfields*4 + sizeof(edict_t) - sizeof(entvars_t);
+	// 
 	pr->edict_size = (pr->edict_size + 7) & ~7;
 
 	Con_DPrintf("Programs occupy %dK.\n", n/1024);
--- a/pr_exec.c
+++ b/pr_exec.c
@@ -172,7 +172,7 @@
 
 ============
 */
-void PR_Profile_f (void)
+void PR_Profile_f (cmd_t *c)
 {
 	dfunction_t	*f, *best;
 	int			max;
@@ -179,6 +179,7 @@
 	int			num;
 	int			i;
 
+	USED(c);
 	if (!sv.active)
 		return;
 
--- a/progs.h
+++ b/progs.h
@@ -100,7 +100,7 @@
 char *PR_Str(pr_t *pr, int ofs);
 char *PR_UglyValueString(pr_t *pr, etype_t, eval_t *);
 
-void PR_Profile_f (void);
+void PR_Profile_f (cmd_t *c);
 
 edict_t *ED_Alloc(pr_t *pr);
 void ED_Free(edict_t *ed);
@@ -145,7 +145,7 @@
 void PR_RunError (pr_t *pr, char *error, ...);
 
 ddef_t *ED_FieldAtOfs (pr_t *pr, int ofs);
-void ED_PrintEdicts(void);
+void ED_PrintEdicts(cmd_t *c);
 void ED_PrintNum(pr_t *pr, int ent);
 
 eval_t *GetEdictFieldValue(pr_t *pr, edict_t *ed, char *field);
--- /dev/null
+++ b/qp.c
@@ -1,0 +1,230 @@
+#include "quakedef.h"
+#include "qp.h"
+
+typedef u16int Tbitmap;
+typedef struct Tbranch Tbranch;
+typedef struct Tleaf Tleaf;
+
+struct Tleaf {
+	char *k;
+	void *v;
+};
+
+struct Tbranch {
+	Trie *twigs;
+	u64int x;
+};
+
+union Trie {
+	Tleaf leaf;
+	Tbranch branch;
+};
+
+#define x_flags(x) ((x)>>62)
+#define x_index(x) ((x)>>16 & ((1ULL<<46)-1ULL))
+#define x_bitmap(x) ((x) & 0xffff)
+
+#define isbranch(t) (x_flags((t)->branch.x) != 0)
+#define hastwig(t, b) (x_bitmap((t)->branch.x) & (b))
+#define twigoff(t, b) popcount(x_bitmap((t)->branch.x) & ((b)-1))
+#define twig(t, i) (&(t)->branch.twigs[(i)])
+#define twigoffmax(off, max, t, b) do{ off = twigoff(t, b); max = popcount(x_bitmap((t)->branch.x)); }while(0)
+#define nibbit(k, f) (1 << (((k) & (((f)-2) ^ 0x0f) & 0xff) >> ((2-(f))<<2)))
+#define twigbit(t, k, len) \
+	(x_index((t)->branch.x) >= (u64int)(len) ? \
+	1 : \
+	nibbit((u8int)(k)[x_index((t)->branch.x)], x_flags((t)->branch.x)))
+
+/*
+// need some speed? just say no.
+#define POPCNT	BYTE $0xf3; BYTE $0x0f; BYTE $0xb8
+
+TEXT popcount(SB),$0
+	MOVL b+0(FP), AX
+	POPCNT
+	RET
+*/
+static u16int
+popcount(u16int b)
+{
+	b -= b>>1 & 0x5555;
+	b = (b & 0x3333) + ((b>>2) & 0x3333);
+	b = (b + (b>>4)) & 0x0f0f;
+	b = (b + (b>>8)) & 0x00ff;
+	return b;
+}
+
+int
+qpget(Trie *t, char *k, int len, char **pk, void **pv)
+{
+	Tbitmap b;
+
+	assert(k != nil && pk != nil && pv != nil);
+
+	if(len < 1)
+		len = strlen(k);
+	if(t == nil)
+		return -1;
+	for(; isbranch(t); t = twig(t, twigoff(t, b))){
+		b = twigbit(t, k, len);
+		if(!hastwig(t, b))
+			return -1;
+	}
+	if(strncmp(k, t->leaf.k, len) != 0)
+		return -1;
+	*pk = t->leaf.k;
+	*pv = t->leaf.v;
+
+	return 0;
+}
+
+int
+qpnext(Trie *t, char **pk, int *plen, void **pv)
+{
+	Tbitmap b;
+	unsigned s, m;
+
+	assert(pk != nil && plen != nil && pv != nil);
+
+	if(isbranch(t)){
+		b = twigbit(t, *pk, *plen);
+		twigoffmax(s, m, t, b);
+		for(; s < m; s++){
+			if(qpnext(twig(t, s), pk, plen, pv) == 0)
+				return 0;
+		}
+		return -1;
+	}
+
+	if(*pk == nil){
+		*pk = t->leaf.k;
+		*plen = strlen(*pk);
+		*pv = t->leaf.v;
+		return 0;
+	}
+	if(*pk == t->leaf.k || strcmp(*pk, t->leaf.k) == 0){
+		*pk = nil;
+		*plen = 0;
+		*pv = nil;
+		return -1;
+	}
+
+	return -1;
+}
+
+Trie *
+qpdel(Trie *t, char *k, int len, char **pk, void **pv)
+{
+	Trie *p, *twigs;
+	Tbitmap b;
+	unsigned s, m;
+
+	if(t == nil)
+		return nil;
+	assert(k != nil && pk != nil && pv != nil);
+	if(len < 1)
+		len = strlen(k);
+	assert(len > 0);
+
+	for(b = 0, p = nil; isbranch(t); p = t, t = twig(t, twigoff(t, b))){
+		b = twigbit(t, k, len);
+		if(!hastwig(t, b))
+			return t;
+	}
+	
+	if(strncmp(k, t->leaf.k, len) != 0)
+		return t;
+	*pk = t->leaf.k;
+	*pv = t->leaf.v;
+
+	if(p == nil){
+		free(t);
+		return nil;
+	}
+	t = p;
+
+	twigoffmax(s, m, t, b);
+	if(m == 2){
+		twigs = t->branch.twigs;
+		*t = *twig(t, !s);
+		free(twigs);
+		return t;
+	}
+	memmove(t->branch.twigs+s, t->branch.twigs+s+1, sizeof(*t)*(m-s-1));
+	t->branch.x &= ~(u64int)b;
+
+	t->branch.twigs = realloc(t->branch.twigs, sizeof(*t)*(m-1));
+	return t;
+}
+
+Trie *
+qpset(Trie *t, char *k, int len, void *v)
+{
+	Trie *t0, t1, t2;
+	unsigned i, s, m;
+	u8int f;
+	Tbitmap b, b1, b2;
+	u8int k2;
+
+	assert(k != nil && v != nil);
+	if(len < 1)
+		len = strlen(k);
+	assert(len > 0);
+
+	if(t == nil){
+		t = malloc(sizeof(*t));
+		assert(t != nil);
+		t->leaf.k = k;
+		t->leaf.v = v;
+		return t;
+	}
+
+	t0 = t;
+	for(; isbranch(t); t = twig(t, i)){
+		b = twigbit(t, k, len);
+		i = hastwig(t, b) ? twigoff(t, b) : 0;
+	}
+	for(i = 0; i <= (unsigned)len && k[i] == t->leaf.k[i]; i++);
+	if(i == (unsigned)len+1){
+		t->leaf.v = v;
+		return t0;
+	}
+
+	k2 = (u8int)t->leaf.k[i];
+	f = (((u8int)k[i] ^ k2) & 0xf0) ? 1 : 2;
+	b1 = nibbit(k[i], f);
+	t1.leaf.k = k;
+	t1.leaf.v = v;
+	for(t = t0; isbranch(t); t = twig(t, twigoff(t, b))){
+		if(i == x_index(t->branch.x)){
+			if(f == x_flags(t->branch.x))
+				goto growbranch;
+			if(f < x_flags(t->branch.x))
+				break;
+		}else if(i < x_index(t->branch.x)){
+			break;
+		}
+		b = twigbit(t, k, len);
+		assert(hastwig(t, b));
+	}
+
+	t2 = *t;
+	b2 = nibbit(k2, f);
+	t->branch.twigs = malloc(sizeof(*t)*2);
+	assert(t->branch.twigs != nil);
+	t->branch.x = (u64int)f<<62 | (u64int)i<<16 | b1 | b2;
+	*twig(t, twigoff(t, b1)) = t1;
+	*twig(t, twigoff(t, b2)) = t2;
+
+	return t0;
+
+growbranch:
+	assert(!hastwig(t, b1));
+	twigoffmax(s, m, t, b1);
+	t->branch.twigs = realloc(t->branch.twigs, sizeof(*t)*(m+1));
+	memmove(t->branch.twigs+s+1, t->branch.twigs+s, sizeof(*t)*(m-s));
+	memmove(t->branch.twigs+s, &t1, sizeof(t1));
+	t->branch.x |= b1;
+
+	return t0;
+}
--- /dev/null
+++ b/qp.h
@@ -1,0 +1,7 @@
+typedef union Trie Trie;
+#pragma incomplete Trie
+
+int qpget(Trie *t, char *k, int len, char **pk, void **pv);
+int qpnext(Trie *t, char **pk, int *plen, void **pv);
+Trie *qpdel(Trie *t, char *k, int len, char **pk, void **pv);
+Trie *qpset(Trie *t, char *k, int len, void *v);
--- a/quakedef.h
+++ b/quakedef.h
@@ -109,6 +109,7 @@
 typedef u8int byte;
 
 #include "cvar.h"
+#include "cmd.h"
 #include "common.h"
 #include "zone.h"
 #include "dat.h"
@@ -145,7 +146,6 @@
 #include "screen.h"
 #include "net.h"
 #include "protocol.h"
-#include "cmd.h"
 #include "sbar.h"
 
 extern cvar_t bgmvolume;
@@ -168,7 +168,6 @@
 
 extern bool noclip_anglehack;
 
-
 //
 // host
 //
@@ -199,7 +198,7 @@
 _Noreturn void Host_Error (char *error, ...);
 void Host_EndGame (char *message, ...);
 void Host_Frame (float time);
-void Host_Quit_f (void);
+void Host_Quit_f (cmd_t *c);
 void Host_ClientCommands (char *fmt, ...);
 void Host_ShutdownServer (bool crash);
 
--- a/r_fog.c
+++ b/r_fog.c
@@ -22,12 +22,13 @@
 }
 
 static void
-fog(void)
+fog(cmd_t *c)
 {
 	int i, n;
 	float x;
 	char *s;
 
+	USED(c);
 	i = 1;
 	n = Cmd_Argc();
 	switch(n){
--- a/sbar.c
+++ b/sbar.c
@@ -44,30 +44,17 @@
 ===============
 Sbar_ShowScores
 
-Tab key down
+Tab key down/up
 ===============
 */
 static void
-Sbar_ShowScores(void)
+Sbar_ShowScores(cmd_t *c)
 {
-	sb_showscores = true;
+	sb_showscores = c->name[0] == '+';
 }
 
 /*
 ===============
-Sbar_DontShowScores
-
-Tab key up
-===============
-*/
-static void
-Sbar_DontShowScores(void)
-{
-	sb_showscores = false;
-}
-
-/*
-===============
 Sbar_Init
 ===============
 */
@@ -152,7 +139,7 @@
 	sb_face_quad = Draw_PicFromWad ("face_quad");
 
 	Cmd_AddCommand ("+showscores", Sbar_ShowScores);
-	Cmd_AddCommand ("-showscores", Sbar_DontShowScores);
+	Cmd_AddCommand ("-showscores", Sbar_ShowScores);
 
 	sb_sbar = Draw_PicFromWad ("sbar");
 	sb_ibar = Draw_PicFromWad ("ibar");
--- a/screen.c
+++ b/screen.c
@@ -238,8 +238,9 @@
 Keybinding command
 =================
 */
-static void SCR_SizeUp_f (void)
+static void SCR_SizeUp_f (cmd_t *c)
 {
+	USED(c);
 	setcvarv ("viewsize",scr_viewsize.value+10);
 	vid.recalc_refdef = true;
 }
@@ -252,8 +253,9 @@
 Keybinding command
 =================
 */
-static void SCR_SizeDown_f (void)
+static void SCR_SizeDown_f (cmd_t *c)
 {
+	USED(c);
 	setcvarv ("viewsize",scr_viewsize.value-10);
 	vid.recalc_refdef = true;
 }
@@ -269,8 +271,8 @@
 	Cvar_RegisterVariable (&scr_centertime);
 	Cvar_RegisterVariable (&scr_printspeed);
 	Cvar_RegisterVariable(&scr_showfps);
-	Cmd_AddCommand ("sizeup",SCR_SizeUp_f);
-	Cmd_AddCommand ("sizedown",SCR_SizeDown_f);
+	Cmd_AddCommand ("sizeup", SCR_SizeUp_f);
+	Cmd_AddCommand ("sizedown", SCR_SizeDown_f);
 
 	scr_net = Draw_PicFromWad ("net");
 	scr_turtle = Draw_PicFromWad ("turtle");
@@ -460,7 +462,7 @@
 */
 void SCR_BeginLoadingPlaque (void)
 {
-	stopallsfx();
+	stopallsfx(nil);
 
 	if (cls.state != ca_connected)
 		return;
--- a/snd_mix.c
+++ b/snd_mix.c
@@ -376,8 +376,9 @@
 }
 
 void
-stopallsfx(void)
+stopallsfx(cmd_t *c)
 {
+	USED(c);
 	if(!ainit)
 		return;
 	memset(chans, 0, sizeof(*chans)*Nchan);
@@ -558,7 +559,7 @@
 }
 
 static void
-playsfx(void)
+playsfx(cmd_t *c)
 {
 	static int hash = 345;
 	int i;
@@ -565,6 +566,7 @@
 	char *s;
 	Sfx *sfx;
 
+	USED(c);
 	if(Cmd_Argc() < 2){
 		Con_Printf("play wav [wav..]: play a wav lump\n");
 		return;
@@ -582,7 +584,7 @@
 }
 
 static void
-playvolsfx(void)
+playvolsfx(cmd_t *c)
 {
 	static int hash = 543;
 	int i;
@@ -590,6 +592,7 @@
 	char *s;
 	Sfx *sfx;
 
+	USED(c);
 	if(Cmd_Argc() < 3){
 		Con_Printf("play wav vol [wav vol]..: play an amplified wav lump\n");
 		return;
@@ -608,7 +611,7 @@
 }
 
 static void
-sfxlist(void)
+sfxlist(cmd_t *cmd)
 {
 	char c;
 	int sz, sum;
@@ -615,6 +618,7 @@
 	Sfx *sfx, *e;
 	sfxcache_t *sc;
 
+	USED(cmd);
 	sum = 0;
 	for(sfx=known_sfx, e=known_sfx+num_sfx; sfx<e; sfx++){
 		if(sc = Cache_Check(&sfx->cu), sc == nil)
@@ -678,7 +682,7 @@
 
 		ambsfx[Ambwater] = precachesfx("ambience/water1.wav");
 		ambsfx[Ambsky] = precachesfx("ambience/wind2.wav");
-		stopallsfx();
+		stopallsfx(nil);
 	}
 	return 0;
 }
--- a/snd_openal.c
+++ b/snd_openal.c
@@ -413,10 +413,11 @@
 }
 
 void
-stopallsfx(void)
+stopallsfx(cmd_t *cmd)
 {
 	alchan_t *c, *next;
 
+	USED(cmd);
 	if(dev == nil)
 		return;
 	alListenerf(AL_GAIN, 0); ALERR();
@@ -725,12 +726,13 @@
 }
 
 static void
-sfxlist(void)
+sfxlist(cmd_t *c)
 {
 	int sz, sum, w, ch;
 	Sfx *sfx, *e;
 	albuf_t *b;
 
+	USED(c);
 	sum = 0;
 	for(sfx = known_sfx, e = known_sfx+num_sfx; sfx < e; sfx++){
 		if((b = Cache_Check(&sfx->cu)) == nil)
--- a/sv_main.c
+++ b/sv_main.c
@@ -52,10 +52,11 @@
 //============================================================================
 
 static void
-SV_Protocol_f(void)
+SV_Protocol_f(cmd_t *c)
 {
 	int i, n;
 
+	USED(c);
 	i = Cmd_Argc();
 	if(i == 1)
 		Con_Printf("\"sv_protocol\" is \"%d\" (%s)\n", sv_protocol->version, sv_protocol->name);
--- a/vid_plan9.c
+++ b/vid_plan9.c
@@ -149,7 +149,7 @@
 		resetfb();
 		vid.recalc_refdef = true;	/* force a surface cache flush */
 		Con_CheckResize();
-		Con_Clear_f();
+		Con_Clear_f(nil);
 		return;
 	}
 	if(frame == nil){
--- a/vid_sdl.c
+++ b/vid_sdl.c
@@ -100,7 +100,7 @@
 		resetfb();
 		vid.recalc_refdef = true;	/* force a surface cache flush */
 		Con_CheckResize();
-		Con_Clear_f();
+		Con_Clear_f(nil);
 		return;
 	}
 
--- a/view.c
+++ b/view.c
@@ -124,14 +124,13 @@
 static cvar_t v_centerspeed = {"v_centerspeed","500"};
 
 
-void V_StartPitchDrift (void)
+void
+V_StartPitchDrift(cmd_t *c)
 {
-	if (cl.laststop == cl.time)
-	{
+	USED(c);
+	if(cl.laststop == cl.time)
 		return;		// something else is keeping it from drifting
-	}
-	if (cl.nodrift || !cl.pitchvel)
-	{
+	if(cl.nodrift || !cl.pitchvel){
 		cl.pitchvel = v_centerspeed.value;
 		cl.nodrift = false;
 		cl.driftmove = 0;
@@ -138,7 +137,8 @@
 	}
 }
 
-void V_StopPitchDrift (void)
+void
+V_StopPitchDrift(void)
 {
 	cl.laststop = cl.time;
 	cl.nodrift = true;
@@ -179,9 +179,7 @@
 			cl.driftmove += host_frametime;
 
 		if ( cl.driftmove > v_centermove.value)
-		{
-			V_StartPitchDrift ();
-		}
+			V_StartPitchDrift(nil);
 		return;
 	}
 
@@ -309,8 +307,9 @@
 ==================
 */
 static void
-V_cshift_f(void)
+V_cshift_f(cmd_t *c)
 {
+	USED(c);
 	cshift_empty.destcolor[0] = atoi(Cmd_Argv(1));
 	cshift_empty.destcolor[1] = atoi(Cmd_Argv(2));
 	cshift_empty.destcolor[2] = atoi(Cmd_Argv(3));
@@ -326,8 +325,9 @@
 ==================
 */
 static void
-V_BonusFlash_f(void)
+V_BonusFlash_f(cmd_t *c)
 {
+	USED(c);
 	cl.cshifts[CSHIFT_BONUS].destcolor[0] = 215;
 	cl.cshifts[CSHIFT_BONUS].destcolor[1] = 186;
 	cl.cshifts[CSHIFT_BONUS].destcolor[2] = 69;
@@ -818,7 +818,7 @@
 //============================================================================
 
 static void
-screenshot(void)
+screenshot(cmd_t *c)
 {
 	static char opath[48];
 	static int pathcnt = 2;
@@ -827,6 +827,7 @@
 	byte *b;
 	int n;
 
+	USED(c);
 	if((t = sys_timestamp()) == nil){
 err:
 		Con_Printf("screenshot: %s\n", lerr());
--- a/view.h
+++ b/view.h
@@ -4,7 +4,7 @@
 void V_RenderView(void);
 void V_ParseDamage (void);
 float V_CalcRoll(vec3_t angles, vec3_t velocity);
-void V_StartPitchDrift (void);
+void V_StartPitchDrift (cmd_t *c);
 void V_StopPitchDrift (void);
 void V_SetContentsColor (int contents);
 void V_ApplyShifts(void);
--- a/zone.c
+++ b/zone.c
@@ -6,6 +6,7 @@
 {
 	mem_t *prev, *next;
 	mem_user_t *user;
+	int tag;
 	int size; // NOT including this header
 };
 
@@ -73,6 +74,28 @@
 	return m+1;
 }
 
+void
+Hunk_SetTag(void *p, int tag)
+{
+	mem_t *m;
+
+	assert(p != nil);
+	m = p;
+	m--;
+	m->tag = tag;
+}
+
+int
+Hunk_GetTag(void *p)
+{
+	mem_t *m;
+
+	assert(p != nil);
+	m = p;
+	m--;
+	return m->tag;
+}
+
 void *
 Hunk_Double(void *p)
 {
@@ -160,10 +183,11 @@
 }
 
 static void
-Cache_Flush(void)
+Cache_Flush(cmd_t *c)
 {
 	mem_t *s;
 
+	USED(c);
 	while(cache_head != nil){
 		s = cache_head->next;
 		free(cache_head);
--- a/zone.h
+++ b/zone.h
@@ -15,6 +15,9 @@
 void Hunk_Free(void *p);
 void *Hunk_TempAlloc(int size);
 
+void Hunk_SetTag(void *p, int tag);
+int Hunk_GetTag(void *p);
+
 void *Cache_Alloc(mem_user_t *c, int size);
 void *Cache_Realloc(mem_user_t *c, int size);
 void *Cache_Check(mem_user_t *c);