diff -uNr quake3/cgame/cg_consolecmds.c fi/cgame/cg_consolecmds.c
--- quake3/cgame/cg_consolecmds.c	Sun Jan 14 16:45:10 2001
+++ fi/cgame/cg_consolecmds.c	Tue Aug  7 22:32:48 2001
@@ -478,6 +478,10 @@
 	const char	*cmd;
 	int		i;
 
+  switch (QSH_trap()) {
+    case 0: return qfalse; break;
+    case 1: return qtrue; break;
+  }
 	cmd = CG_Argv(0);
 
 	for ( i = 0 ; i < sizeof( commands ) / sizeof( commands[0] ) ; i++ ) {
diff -uNr quake3/cgame/cg_hash.c fi/cgame/cg_hash.c
--- quake3/cgame/cg_hash.c	Wed Dec 31 16:00:00 1969
+++ fi/cgame/cg_hash.c	Tue Aug  7 21:24:34 2001
@@ -0,0 +1,55 @@
+/*
+  The following hash function is a modified version of the one found in
+  kazlib-1.19, and qualifies as a `derivative work'.
+  Copyright notice for kazlib-1.19 follows, which applies *only* to the hash
+  function `hash_func'.
+*/
+/*
+ * Hash Table Data Type
+ * Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
+ *
+ * Free Software License:
+ *
+ * All rights are reserved by the author, with the following exceptions:
+ * Permission is granted to freely reproduce and distribute this software,
+ * possibly in exchange for a fee, provided that this copyright notice appears
+ * intact. Permission is also granted to adapt this software to produce
+ * derivative works, as long as the modified versions carry this copyright
+ * notice and additional notices stating that the work has been modified.
+ * This source code may be translated into executable form and incorporated
+ * into proprietary software; there is no requirement for such software to
+ * contain a copyright notice related to this source.
+ *
+ */
+unsigned int
+hash_func (const char *key)
+{
+  static unsigned long randbox[] = {
+    0x49848f1bU, 0xe6255dbaU, 0x36da5bdcU, 0x47bf94e9U,
+    0x8cbcce22U, 0x559fc06aU, 0xd268f536U, 0xe10af79aU,
+    0xc1af4d69U, 0x1d2917b5U, 0xec4c304dU, 0x9ee5016cU,
+    0x69232f74U, 0xfead7bb3U, 0xe9089ab6U, 0xf012f6aeU,
+  };
+
+  const char *str;
+  unsigned int acc = 0;
+
+  str = key;
+  while (*str) {
+    acc ^= randbox[(*str + acc) & 0xf];
+    acc = (acc << 1) | (acc >> 31);
+    acc &= 0xffffffffU;
+    acc ^= randbox[((*str++ >> 4) + acc) & 0xf];
+    acc = (acc << 2) | (acc >> 30);
+    acc &= 0xffffffffU;
+  }
+  /* Normalize along -INF to +INF */
+  return (int)(acc - 0x80000000);
+}
+
+
+unsigned int
+QSH_hash (const char *key)
+{
+  return hash_func(key);
+}
diff -uNr quake3/cgame/cg_qsh2.c fi/cgame/cg_qsh2.c
--- quake3/cgame/cg_qsh2.c	Wed Dec 31 16:00:00 1969
+++ fi/cgame/cg_qsh2.c	Tue Aug  7 23:57:13 2001
@@ -0,0 +1,543 @@
+/*
+  Quake III Shell Interpreter Take 2
+
+  by PhaethonH <phaethon@linux.ucla.edu>
+*/
+
+#define QSH_VERSION "0.01"
+#define MAGIC_QSH 0x27610286
+
+#define debug CG_Printf
+
+
+
+#include "cg_local.h"
+
+
+#define SUBST_MARKER '$'
+
+
+
+enum {
+  QSH_OUTPUT_SIZE = 1024,
+};
+
+struct qsh_s {
+  int magic;
+  int shift;      /* if any shift. */
+  int numintern;  /* number of internal commands. */
+  char qerr[QSH_OUTPUT_SIZE];
+  char qout[QSH_OUTPUT_SIZE];
+};
+
+typedef struct qsh_s qsh_t;
+
+
+
+
+struct qshcmd_s {
+  unsigned int hash;
+  int (*func)(char*,char*,char*);
+  char *name;
+};
+
+typedef struct qshcmd_s qshcmd_t;
+
+
+
+
+
+
+qsh_t qsh;
+
+
+
+
+
+
+
+extern unsigned int QSH_hash(const char*);
+
+#define PROTOTYPE_SUBST(p) int QSH_##p (char *, char*, char, int)
+/* Then PROTOTYPE_SUBST(foo) => int QSH_foo (char *, char *, char, int) */
+
+PROTOTYPE_SUBST(substitution);
+PROTOTYPE_SUBST(param_subst);
+PROTOTYPE_SUBST(pos_subst);
+PROTOTYPE_SUBST(cmd_subst);
+PROTOTYPE_SUBST(var_subst);
+PROTOTYPE_SUBST(arith_subst);
+
+
+
+
+
+
+int
+QSH_argc ()
+{
+  int retval;
+  retval = trap_Argc() - qsh.shift;
+  return retval;
+}
+
+int
+QSH_argv (char *dest, int n, int destsize)
+{
+  int retval;
+  n -= qsh.shift;
+  trap_Argv(n, dest, destsize);
+  retval = strlen(dest);
+  return retval;
+}
+
+void
+QSH_print (char *format, ...)
+{
+  va_list argptr;
+
+  va_start(argptr, format);
+  Q_strcat(qsh.qout, sizeof(qsh.qout), va(format, argptr));
+  va_end(argptr);
+}
+
+void
+QSH_error (char *format, ...)
+{
+  va_list argptr;
+
+  va_start(argptr, format);
+  Q_strcat(qsh.qerr, sizeof(qsh.qerr), va(format, argptr));
+  va_end(argptr);
+}
+
+
+int
+QSH_isdigit (char c)
+{
+  return (('0' <= c) && (c <= '9'));
+}
+
+
+int
+QSH_isid (char c)
+{
+  return ( (('a' <= c) && (c <= 'z'))
+          || (('A' <= c) && (c <= 'Z'))
+          || (c == '_')
+          );
+}
+
+
+
+
+
+
+
+
+/*
+  Special variables that are always in-scope.
+*/
+int
+QSH_try_var_special (char *dest, char *name, int destsize)
+{
+  if (0 == Q_stricmp(name, "health"))
+    {
+      Q_strncpyz(dest, va("%d", cg.snap->ps.stats[STAT_HEALTH]), destsize);
+    }
+  else if (0 == Q_stricmp(name, "armor"))
+    {
+      Q_strncpyz(dest, va("%d", cg.snap->ps.stats[STAT_ARMOR]), destsize);
+    }
+  else if (0 == Q_stricmp(name, "location"))
+    {
+      Q_strncpyz(dest, CG_ConfigString(CS_LOCATIONS + cgs.clientinfo[cg.clientNum].location), destsize);
+    }
+  else if (0 == Q_stricmp(name, "weapon"))
+    {
+      Q_strncpyz(dest, cg_weapons[cg.snap->ps.weapon].item->pickup_name, destsize);
+    }
+  else if (0 == Q_stricmp(name, "ammo"))
+    {
+      Q_strncpyz(dest, va("%d", cg.snap->ps.ammo[cg.snap->ps.weapon]), destsize);
+    }
+
+  return strlen(dest);
+}
+
+
+/*
+  Variables in the local stack frame.
+*/
+int
+QSH_try_var_local (char *dest, char *name, int destsize)
+{
+  return 0;
+}
+
+
+/*
+  Cvar variables.
+  Cvars in Q3SH shadow the role of environment variables in bash.
+*/
+int
+QSH_try_var_console (char *dest, char *name, int destsize)
+{
+  trap_Cvar_VariableStringBuffer(name, dest, destsize);
+  return strlen(dest);
+}
+
+
+
+/*
+  Given a variable name `name', copy the value of the variable to `dest', which has size constraint `destsize' bytes.
+
+  Returns length of value.  -1 if existence of variable could not be verified.
+*/
+int
+QSH_copyvarbyname (char *dest, char *name, int destsize)
+{
+  int i;
+
+  /* Obtuse abuse of shortcuts.  But it works, dammit. */
+  /* Change the order of functions to change precedence of scopes. */
+  /*  e.g. put QSH_try_var_console to put cvars as top precedence. */
+  i =
+   QSH_try_var_special (dest, name, destsize)
+   ||
+   QSH_try_var_local (dest, name, destsize)
+   ||
+   QSH_try_var_console (dest, name, destsize)
+   ;
+
+  return i;
+}
+
+
+
+
+
+/*
+  Scan the string `src' for any required substitution.
+  The evaluated result is placed into `dest', with size limitation `destsize'.
+
+  Returns number of characters scanned in `src' before returning.
+*/
+int
+QSH_substitution (char *dest, char *src, char terminator, int destsize)
+{
+  int i, len;
+
+  len = 0;
+  i = 0;
+  while ((src[i]) && (src[i] != terminator))
+    {
+      if (src[i] == SUBST_MARKER)
+        {
+//debug("qsh_subst: hit subst marker\n");
+          i++;
+          i += QSH_param_subst (dest + len, src + i, 0, destsize - len);
+          len += strlen(dest + len);   /* dangerous? */
+        }
+      else
+        {
+          if (len < destsize - 1)
+            {
+              dest[len++] = src[i];
+              dest[len] = 0;
+            }
+        }
+      i++;  /* Next character. */
+    }
+  return i;
+}
+
+
+/*
+  We hit a substitution marker, now we're here.
+  We can go to positional, variable, or command expansion from here.
+  We reach arithmetic substitution by jumping to command first.
+*/
+int
+QSH_param_subst (char *dest, char *src, char terminator, int destsize)
+{
+  int i, len, b;
+  char buf[256];
+
+  i = 0;
+  len = 0;
+
+//debug("qsh_param_subst: start, term = [%d]\n", terminator);
+  if (terminator)
+    {
+      b = 0;
+      while ((src[i] != terminator) && (b < sizeof(buf)))
+        {
+          buf[b++] = src[i++];
+        }
+      buf[b] = 0;
+//debug("qsh_param_subst: evaling [%s]\n", buf);
+      QSH_copyvarbyname(dest + len, buf, destsize - len);
+      len += strlen(dest + len);
+      return i;
+    }
+
+  if (src[i] == '{')
+    {
+//debug("qsh_param_subst: going param subst on {\n");
+      i++;
+      i += QSH_param_subst (dest + len, src + i, '}', destsize - len);
+      len += strlen(dest + len);
+    }
+  else if (src[i] == '(')
+    {
+//debug("qsh_param_subst: going cmd subst on (\n");
+      i++;
+      i += QSH_cmd_subst (dest + len, src + i, ')', destsize - len);
+      len += strlen(dest + len);
+    }
+  else if (QSH_isdigit(src[i]) || (src[i] == '*') || (src[i] == '@'))
+    /* A digit, a '*', or a '@' */
+    {
+//debug("qsh_param_subst: going pos subst on %c\n", src[i]);
+      i += QSH_pos_subst (dest + len, src + i, 0, destsize - len);
+      len += strlen(dest + len);
+    }
+  else //if (QSH_isid(src[i]))
+    {
+//debug("qsh_param_subst: going var subst on %c\n", src[i]);
+      i += QSH_var_subst (dest + len, src + i, 0, destsize - len);
+      len += strlen(dest + len);
+    }
+
+  return i;
+}
+
+
+int
+QSH_pos_subst (char *dest, char *src, char terminator, int destsize)
+{
+  int i, len, n;
+
+  i = 0;
+  len = 0;
+  n = 0;
+  if (src[i] == '@')
+    {
+      /* All positional parameters attached. */
+      for (n = 1; n < QSH_argc(); n++)
+        {
+          Q_strcat(dest, destsize, " ");
+          len = strlen(dest);
+          len += QSH_argv(dest + len, n, destsize - len);
+        }
+    }
+  else if (src[i] == '*')
+    {
+      /* All positional parameters as one word. */
+      Q_strcat(dest, destsize, "\"");
+      for (n = 1; n < QSH_argc(); n++)
+        {
+          QSH_argv(dest + len, n, destsize - len);
+          Q_strcat(dest, destsize, " ");
+          len = strlen(dest);
+        }
+      dest[len - 1] = '"';
+    }
+  else
+    {
+      n = (*src - '0');
+      trap_Argv(n, dest + len, destsize - len);
+      len += strlen(dest + len);
+    }
+
+  i = 1;
+  return i;
+}
+
+
+int
+QSH_var_subst (char *dest, char *src, char terminator, int destsize)
+{
+  int i, len, b;
+  char buf[256];
+
+  i = 0;
+  len = 0;
+  b = 0;
+
+  while (src[i] && (src[i] != terminator) && (QSH_isid(src[i])))
+    {
+      buf[b++] = src[i++];
+    }
+  buf[b] = 0;
+  i--;  /* since `i' is now on a delimiter, and we don't actually consume that. */
+//debug("var name = [%s]\n", buf);
+
+  len = QSH_copyvarbyname(dest, buf, destsize);
+//debug("[%s] = [%s]\n", buf, dest);
+  return i;
+}
+
+
+int
+QSH_cmd_subst (char *dest, char *src, char terminator, int destsize)
+{
+  int i;
+  int len;
+
+  i = 0;
+  len = 0;
+  if (src[i] == '(')
+    {
+      i++;
+      i += QSH_arith_subst(dest + len, src + i, ')', destsize - len);
+      len += strlen(dest + len);
+    }
+  else
+    {
+      0;
+    }
+  return i;
+}
+
+
+int
+QSH_arith_subst (char *dest, char *src, char terminator, int destsize)
+{
+  return 0;
+}
+
+
+
+
+int
+QSH_eval (char *qout, char *qerr, char *qin)
+{
+  char args[1024];
+  char buf[1024];
+
+//debug("qsh_eval: start\n");
+  trap_Args(args, sizeof(args));
+//debug("qsh_eval: subbing on [%s]\n", args);
+  QSH_substitution(buf, args, 0, sizeof(buf));
+//debug("qsh_eval: subbed = [%s]\n", buf);
+  trap_SendConsoleCommand(buf);
+//debug("qsh_eval: end\n");
+  return 1;
+}
+
+
+int
+QSH_ver (char *quot, char *qerr, char *qin)
+{
+  QSH_print ("Q3SH v" QSH_VERSION "\n");
+  return 1;
+}
+
+
+
+
+
+
+
+
+
+
+qshcmd_t qshcmds[] = {
+  { 0, QSH_eval, "eval" },
+  { 0, QSH_ver, "ver" },
+  { 0, 0, 0 },
+ };
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/*
+  Entry point into Q3SH.
+  Intercept console command to interpret as scripting constructs.
+
+  Returns:
+    -1 - if nothing to intercept (i.e. run regular command check).
+    0 - if intercepted with unknown construct (== unknown or bad command).
+    1 - if intercepted with known construct (== known command).
+
+
+  The default declaration for a function is no-parameter with return type int.
+  Prototype for this function does not appear anywhere else, nor need to, as the default declaration holds true.
+  Horrid abuse of C-compiler customs.
+*/
+
+int
+QSH_trap ()
+{
+  int i;
+  int h;
+  int retval;
+  char cmd[256];
+
+//debug("qsh_trap: start\n");
+  if (qsh.magic != MAGIC_QSH) QSH_init();
+  retval = -1;
+  QSH_argv(cmd, 0, sizeof(cmd));
+//debug("qsh_trap: cmd = [%s]\n", cmd);
+  h = QSH_hash(cmd);
+//debug("qsh_trap: check hash = %08X\n", h);
+  for(i = 0; qshcmds[i].name; i++)
+    {
+//debug("qsh_trap: check on [%s]\n", qshcmds[i].name);
+      if ((qshcmds[i].hash == h) && (0 == Q_stricmp(qshcmds[i].name, cmd)))
+        {
+//debug("qsh_trap: match on [%s]\n", qshcmds[i].name);
+          retval = qshcmds[i].func(qsh.qout, qsh.qerr, "");
+          Com_Printf("%s", qsh.qerr);
+          Com_Printf("%s", qsh.qout);
+          qsh.qerr[0] = 0;
+          qsh.qout[0] = 0;
+          break;
+        }
+    }
+//debug("qsh_trap: done\n");
+  return retval;
+}
+
+
+
+
+
+
+int
+QSH_init ()
+{
+  int i;
+
+  memset(&qsh, 0, sizeof(qsh));
+  qsh.magic = MAGIC_QSH;
+  for(i = 0; qshcmds[i].name; i++)
+    {
+      qshcmds[i].hash = QSH_hash(qshcmds[i].name);
+    }
+  qsh.numintern = i;
+
+  return 1;
+}
+
+void
+QSH_destroy()
+{
+  memset(&qsh, 0, sizeof(qsh));
+}
+
+
+
+
