/*
 * Command parser
 */

#include <global.h>
#include <commands.h>
#include <sproto.h>
#include <ctype.h>
#include <X11/keysym.h>

/* Added times to all the commands.  However, this was quickly done,
 * and probably needs more refinements.  All socket and DM commands
 * take 0 time.
 */


/*
 * Normal game commands
 */
CommArray_s Commands[] = {
  {"save", command_save,	0.0},
#ifdef SAVE_WINDOW_POSITIONS
  {"savewinpos", command_savewinpos,	0.0},
#endif /* SAVE_WINDOW_POSITIONS */

#ifdef SOUND_EFFECTS
  {"sound", command_sound,	0.0},
#endif
#ifdef SIMPLE_PARTY_SYSTEM
  {"party", command_party,	0.0},
  {"gsay", command_gsay,	1.0},
#endif

#ifdef DEBUG
  {"sstable", command_sstable,	0.0},
#endif
#ifdef DEBUG_MALLOC_LEVEL
  {"verify", command_malloc_verify,0.0},
#endif
  {"add", command_add,		0.0},
  {"apply", command_apply,	1.0},	/* should be variable */
  {"archs", command_archs,	0.0},
  {"bell", command_bell,	0.0},
  {"berzerk", command_berzerk,	0.0},
  {"bind", command_bind,	0.0},
  {"brace", command_brace,	0.0},
  {"cast", command_cast,	0.0},	/* Is this right? */
  {"clearinfo", command_clearinfo,0.0},
  {"disarm", command_disarm,	1.0},
  {"dm", command_dm,		0.0},
  {"drop", command_drop,	1.0},
  {"dropall", command_dropall,	1.0},
  {"examine", command_examine,	0.5},
  {"fix_me", command_fix_me,	0.0},
  {"get", command_take,		1.0},
  {"help", command_help,	0.0},
  {"hiscore", command_hiscore,	0.0},
  {"inv-lock", command_lock,	0.0},
  {"inv-unlock", command_unlock,0.0},
  {"inventory", command_inventory,0.0},
  {"invoke", command_invoke,	1.0},
  {"keyboard", command_keyboard_flush,0.0},
  {"last", command_last,	0.0},
  {"listen", command_listen,	0.0},
  {"malloc", command_malloc,	0.0},
  {"maps", command_maps,	0.0},
  {"mapinfo", command_mapinfo,	0.0},
  {"mark", command_mark,	0.0},
  {"motd", command_motd,	0.0},
  {"output-sync", command_output_sync,	0.0},
  {"output-count", command_output_count,0.0},
  {"peaceful", command_peaceful,0.0},
  {"pickup", command_pickup,	1.0},
  {"prepare", command_prepare,	1.0},
  {"quit", command_quit,	0.0},
  {"refresh", command_refresh,	0.0},
  {"rotateinventory", command_rotateinventory,	0.0},
  {"rotateshoottype", command_rotateshoottype,	0.0},
  {"rotatespells", command_rotatespells,	0.0},
  {"say", command_say,		0.0},
  {"scroll", command_scroll,	0.0},
  {"shout", command_shout,	0.0},
  {"show", command_show,	0.0},
  {"showinvicon", command_show_inv_icon, 0.0},
#ifdef ALLOW_SKILLS
  {"skills", command_skills,	0.0},	/* shows player list of skills */
  {"use_skill", command_uskill, 0.0},
  {"ready_skill", command_rskill, 0.0},
#endif
  {"search",command_search,	1.0},
#ifdef SEARCH_ITEMS
  {"search-items", command_search_items,	0.0},
#endif
  {"sort_inventory", command_sort_inventory, 0.0},
  {"statistics", command_statistics,	0.0},
  {"strength", command_strength,	0.0},
  {"strings", command_strings,	0.0},
  {"sync", command_sync,	0.0},
  {"take", command_take,	1.0},
  {"tell", command_tell,	0.0},
  {"throw", command_throw,	0.0},
  {"time", command_time,	0.0},
#ifdef SET_TITLE
  {"title", command_title,	0.0},
#endif
  {"unbind", command_unbind,	0.0},
  {"use-buttons", command_use_buttons,	0.0},
  {"version", command_version,	0.0},
  {"wimpy", command_wimpy,	0.0},
  {"who", command_who,		0.0},

  {"stay", command_stay,	0.0},
  {"north", command_north,	1.0},
  {"east", command_east,	1.0},
  {"south", command_south,	1.0},
  {"west", command_west,	1.0},
  {"northeast", command_northeast,	1.0},
  {"southeast", command_southeast,	1.0},
  {"southwest", command_southwest,	1.0},
  {"northwest", command_northwest,	1.0},
};

const int CommandsSize =sizeof(Commands) / sizeof(CommArray_s);

CommArray_s NewServerCommands [] = {
  {"run", command_run, 1.0},
  {"run_stop", command_run_stop, 0.0},
  {"fire", command_fire, 1.0},
  {"fire_stop", command_fire_stop, 0.0}
};

const int NewServerCommandSize = sizeof(NewServerCommands)/ sizeof(CommArray_s);

/*
 * And alternative socket commands
 */
CommArray_s SocketCommands [] = {
  {"add", command_add,0.0},
  {"archs", command_archs,0.0},
  {"bell", command_bell,0.0},
  {"dm", command_dm,0.0},
  {"help", command_help,0.0},
  {"hiscore", command_hiscore,0.0},
  {"listen", command_listen,0.0},
  {"malloc", command_malloc,0.0},
  {"maps", command_maps,0.0},
  {"name", command_name,0.0},
  {"protocol", command_protocol,0.0},
  {"quit", command_quit,0.0},
  {"set", command_set,0.0},
  {"shout", command_shout,0.0},
  {"strings", command_strings,0.0},
  {"sync", command_sync,0.0},
  {"tell", command_tell,0.0},
  {"time", command_time,0.0},
  {"unset", command_unset,0.0},
  {"version", command_version,0.0},
  {"who", command_who,0.0},
};

const int SocketCommandsSize =sizeof(SocketCommands) / sizeof(CommArray_s);

/*
 * Wizard commands (for both)
 */
CommArray_s WizCommands [] = {
  {"abil", command_abil,0.0},
  {"addexp", command_addexp,0.0},
  {"create", command_create,0.0},
  {"debug", command_debug,0.0},
  {"dump", command_dump,0.0},
  {"dumpbelow", command_dumpbelow,0.0},
  {"dumpfriendlyobjects", command_dumpfriendlyobjects,0.0},
  {"dumplights", command_dumplights,0.0},
  {"dumpallarchetypes", command_dumpallarchetypes,0.0},
  {"dumpallmaps", command_dumpallmaps,0.0},
  {"dumpallobjects", command_dumpallobjects,0.0},
  {"dumpmap", command_dumpmap,0.0},
  {"free", command_free,0.0},
  {"goto", command_goto,0.0},
  {"invisible", command_invisible,0.0},
  {"nodm", command_nowiz,0.0},
  {"nowiz", command_nowiz,0.0},
  {"patch", command_patch,0.0},
  {"printlos", command_printlos,0.0},
  {"remove", command_remove,0.0},
  {"reset", command_reset,0.0},
  {"set_god", command_setgod, 0.0},
  {"speed", command_speed,0.0},
  {"spellreset", command_spell_reset,0.0},
  {"ssdumptable", command_ssdumptable,0.0},
  {"stats", command_stats,0.0},
  {"summon", command_summon,0.0},
  {"wizpass", command_wizpass,0.0},
};

const int WizCommandsSize =sizeof(WizCommands) / sizeof(CommArray_s);



static int compare_A(const void *a, const void *b)
{
  return strcmp(((CommArray_s *)a)->name, ((CommArray_s *)b)->name);
}

void init_commands()
{
  qsort((char *)Commands, CommandsSize, sizeof(CommArray_s), compare_A);
  qsort((char *)WizCommands, WizCommandsSize, sizeof(CommArray_s), compare_A);
  qsort((char *)SocketCommands, SocketCommandsSize, sizeof(CommArray_s), compare_A);
  qsort((char *)NewServerCommands, NewServerCommandSize, sizeof(CommArray_s), compare_A);
}

#ifndef tolower
#define tolower(C)	(((C) >= 'A' && (C) <= 'Z')? (C) - 'A' + 'a': (C))
#endif


static CommFunc find_command(char *cmd)
{
  CommArray_s *asp, dummy;
  char *cp;

  for (cp=cmd; *cp; cp++)
    *cp =tolower(*cp);

  dummy.name =cmd;
  asp =(CommArray_s *)bsearch((void *)&dummy,
			      (void *)Commands, CommandsSize,
			      sizeof(CommArray_s), compare_A);
  if (asp)
    return asp->func;
  return NULL;
}

static CommFunc find_wizcommand(char *cmd)
{
  CommArray_s *asp, dummy;
  char *cp;

  for (cp=cmd; *cp; cp++)
    *cp =tolower(*cp);

  dummy.name =cmd;
  asp =(CommArray_s *)bsearch((void *)&dummy,
			      (void *)WizCommands, WizCommandsSize,
			      sizeof(CommArray_s), compare_A);
  if (asp)
    return asp->func;
  return NULL;
}

static CommFunc find_socketcommand(char *cmd)
{
  CommArray_s *asp, dummy;
  char *cp;

  for (cp=cmd; *cp; cp++)
    *cp =tolower(*cp);

  dummy.name =cmd;
  asp =(CommArray_s *)bsearch((void *)&dummy,
			      (void *)SocketCommands, SocketCommandsSize,
			      sizeof(CommArray_s), compare_A);
  if (asp)
    return asp->func;
  return NULL;
}

long xfire_kc2ks(player *p,KeyCode kc,long foozit)
{
  if (p->eric_server == 0) {
    return XKeycodeToKeysym(p->gdisp,kc,foozit);
  } else {
    return NoSymbol; /* esrv stuff doesn't deal with foozit -- I don't care*/
  }
}

long xfire_ks2kc(player *p,KeySym ks)
{
  if (p->eric_server>0) {
    LOG(llevDebug,"xfire_ks2kc called in ericserver mode.\n");
    return 0;
  } else {
    return XKeysymToKeycode(p->gdisp,ks);
  }
}

void insert_key_complex(player *p, KeySym ks, KeyCode keycode, int flags, char* line)
{
  int ix, len;
  char *cp;
  Key_s *newkey =(Key_s *)malloc(sizeof(Key_s));
  

#ifdef INPUT_DEBUG
  if (line)
    fprintf(stderr, "%i: %s (%i) 0x%x '%s'\n",
      (int)p, ((cp=XKeysymToString(ks))? cp: "(null)"), (int)keycode, flags, line);
  else
    fprintf(stderr, "%i: %s (%i) 0x%x (null)\n",
	(int)p, ((cp=XKeysymToString(ks))? cp: "(null)"), (int)keycode, flags);
#endif

  if (!(newkey))
    fatal(OUT_OF_MEMORY);

  if (ks != NoSymbol &&
      ks != xfire_kc2ks(p, keycode, 0) &&
      ks != xfire_kc2ks(p, keycode, ShiftMask))
    keycode =xfire_ks2kc(p, ks);

  ix = (int)(keycode) % COMMAND_HASH_SIZE;
  newkey->keysym  =ks;
  newkey->keycode =keycode;
  newkey->flags   =flags;
  newkey->next    =p->keys[ix];
  newkey->params  =NULL;

  if (flags & KEYF_EDIT) {
    newkey->func =NULL;
    if (!line) {
      free(newkey);
      return;
    }
    cp =line;
  } else {			/* KEYF_EDIT */
    if (!line) {
      newkey->func =NULL;
      p->keys[ix] =newkey;
      return;
    }
    if ((cp=strchr(line, ' '))&&!strchr(line,';')) {
      *(cp++) ='\0';
      if (!(newkey->func=find_command(line))) {
		  cp[-1] =' ';
		  cp =line;
      }
    } else {			/* ' ' */
      if ((newkey->func=find_command(line))) {
		  p->keys[ix] =newkey;
		  return;
      }	
      cp =line;
    }				/* ' ' */
  }				/* KEYF_EDIT */
  len =strlen(cp);
  if (len && cp[len-1] == '\n')
    cp[--len] ='\0';
  newkey->params =(char *)malloc(len+1);
  if (!newkey->params)
    fatal(OUT_OF_MEMORY);
  memcpy(newkey->params, cp, len+1);
  p->keys[ix] =newkey;
}

void insert_key(player *p, int baseflags, char *text)
{
  char *tmp;
  int nro, flags, len;

  if (!(tmp=strchr(text, ' '))) {
    LOG(llevError, "Corrupted keysym in %s\n", text);
    return;
  }
  *(tmp++) ='\0';
  if (!(nro=strtol(tmp, NULL, 10))) {
    nro= xfire_ks2kc(p, XStringToKeysym(text));
    if (nro==0 && p->eric_server<1) {
	LOG(llevError, "Corrupted keycode in %s\n", tmp);
	return;
    }
  }
  if (!(tmp=strchr(tmp, ' '))) {
    LOG(llevError, "Corrupted flags in %s\n", text);
    return;
  }
  flags =baseflags;
  tmp++;
  while (*tmp != ' ' && *tmp != '\n' && *tmp != '\0') {
    switch (*tmp) {
    case 'A':
      flags |= KEYF_NORMAL | KEYF_FIRE | KEYF_RUN;
      break;
    case 'N':
      flags |= KEYF_NORMAL;
      break;
    case 'F':
      flags |= KEYF_FIRE;
      break;
    case 'R':
      flags |= KEYF_RUN;
      break;
    case 'E':
      flags |= KEYF_EDIT;
      break;
    default:
      LOG(llevError, "Corrupted flags in %s\n", text);
      return;
    }
    tmp++;
  }

  len =strlen(tmp);
  if (len && tmp[--len] == '\n')
    tmp[len] ='\0';

  if (*tmp != ' ')
    insert_key_complex(p, XStringToKeysym(text), (KeyCode)nro, flags, NULL);
  else
    insert_key_complex(p, XStringToKeysym(text), (KeyCode)nro, flags, tmp+1);
}


void load_default_keys(player *p)
{
  FILE *fp;
  char filename[MAX_BUF], line[MAX_BUF];
  int i;

  p->commandkeysym =XK_apostrophe;
  p->commandkey =xfire_ks2kc(p, XK_apostrophe);
  if (!p->commandkey) {
    p->commandkeysym =XK_acute;
    p->commandkey =xfire_ks2kc(p, XK_acute);
  }
  p->firekeysym[0] =XK_Shift_L;
  p->firekey[0] =xfire_ks2kc(p, XK_Shift_L);
  p->firekeysym[1] =XK_Shift_R;
  p->firekey[1] =xfire_ks2kc(p, XK_Shift_R);
  p->runkeysym[0]  =XK_Control_L;
  p->runkey[0]  =xfire_ks2kc(p, XK_Control_L);
  p->runkeysym[1]  =XK_Control_R;
  p->runkey[1]  =xfire_ks2kc(p, XK_Control_R);

  for(i=0;i<COMMAND_HASH_SIZE;i++)
    while (p->keys[i]) {
      Key_s *kp =p->keys[i];
      if (kp->params)
	free(kp->params);
      p->keys[i] =kp->next;
      free(kp);
    }
  
  sprintf(filename, "%s/def_keys", settings.libdir);
  
  if ((fp=fopen(filename, "r")) == NULL) {
    LOG(llevError, "Can't open %s\n", filename);
    perror("Can't read default keys");
    return;
  }
  
  while (fgets(line, MAX_BUF, fp))
    if (line[0] != '#' && line[0] != '\n') {
      line[MAX_BUF-1] ='\0';
      insert_key(p, KEYF_DEFAULT, line);
    }
  
  fclose(fp);
}


char *find_func_name(CommFunc func)
{
  int i;

  for (i=0; i<CommandsSize; i++)
    if (Commands[i].func == func)
      return Commands[i].name;
  return "(null)";
}


void dump_keys(player *p, FILE *fp)
{
  int i, bi;
  Key_s *key, *prev, *prev2;
  char buff[4];

  for (i=0; i<COMMAND_HASH_SIZE; i++) {
    /* find bottom entry */
    for (prev=NULL,key=p->keys[i];
	 key && !(key->flags & KEYF_DEFAULT);
	 key =key->next)
      prev =key;

    /* from buttom -> up */
    while (prev) {
      if (prev->flags & KEYF_WIZ)
	continue;
      if(prev->keysym == NoSymbol)
	fprintf(fp, "key (null) %i ", prev->keycode);
      else
	fprintf(fp, "key %s %i ", XKeysymToString(prev->keysym),prev->keycode);
      buff[0] ='\0'; buff[1] ='\0'; buff[2] ='\0'; buff[3] ='\0';
      bi =0;
      if ((prev->flags & (KEYF_NORMAL|KEYF_FIRE|KEYF_RUN)) ==
	  (KEYF_NORMAL|KEYF_FIRE|KEYF_RUN))
	buff[bi++] ='A';
      else {
	if (prev->flags & KEYF_NORMAL)
	  buff[bi++] ='N';
	if (prev->flags & KEYF_FIRE)
	  buff[bi++] ='F';
	if (prev->flags & KEYF_RUN)
	  buff[bi++] ='R';
      }
      if (prev->flags & KEYF_EDIT)
	buff[bi++] ='E';
      if (prev->func)
	if (prev->params)
	  fprintf(fp, "%s %s %s\n", buff, find_func_name(prev->func), prev->params);
	else
	  fprintf(fp, "%s %s\n", buff, find_func_name(prev->func));
      else
	if (prev->params)
	  fprintf(fp, "%s %s\n", buff, prev->params);
	else
	  fprintf(fp, "%s\n", buff);

      /* step one up */
      for (prev2=NULL,key=p->keys[i]; key != prev; key =key->next)
	prev2 =key;
      prev =prev2;
    }				/* while prev */
  }				/* for i */
}

void configure_keys(object *op, KeyCode k, KeySym keysym)
{
  int flags;
  char *cp;

  if (op->contr->write_buf[0] == 'B' || 
      op->contr->write_buf[0] == 'U') {
    if(k == op->contr->firekey[0] || k == op->contr->firekey[1]) {
	op->contr->fire_on =1;
	draw_message_window(op);
	return;
    }
    if(k == op->contr->runkey[0] || k == op->contr->runkey[1]) {
	op->contr->run_on =1;
	draw_message_window(op);
	return;
    }
  }
  
  op->contr->state = ST_PLAYING;

  switch (op->contr->write_buf[0]) {
  case 'C':
    op->contr->commandkey =k;
    op->contr->commandkeysym =keysym;
    goto done;
  case 'F':
    op->contr->firekey[0] =k;
    op->contr->firekeysym[0] =keysym;
    goto done;
  case 'f':
    op->contr->firekey[1] =k;
    op->contr->firekeysym[1] =keysym;
    goto done;
  case 'R':
    op->contr->runkey[0] =k;
    op->contr->runkeysym[0] =keysym;
    goto done;
  case 'r':
    op->contr->runkey[1] =k;
    op->contr->runkeysym[1] =keysym;
    goto done;
  case 'B':
    flags =strtol(op->contr->write_buf+1, &cp, 10);
    if (!cp || !flags) {
      LOG(llevError, "Corrupted line to bind!\n");
      return;
    }
    if (*cp == ' ')
      cp++;
    if (flags & 0x1000)
      insert_key_complex(op->contr, NoSymbol, k, (flags&0xfff)|KEYF_USER, cp);
    else
      insert_key_complex(op->contr, keysym, k, flags|KEYF_USER, cp);
    goto done;
  }

  LOG(llevError, "Got to end of configure??\n");
  return;

  /*
   * Post config
   */
 done:
  new_draw_info_format(NDI_UNIQUE, 0, op,
    "Binded to key '%s' (%i)", XKeysymToString(keysym), (int)k);
  op->contr->fire_on=0;
  op->contr->run_on=0;
  draw_message_window(op);
  return;
}




/*
 * parse_string may be called from a player in the game or from a socket
 * (op is NULL if it's a socket).
 * It returnes 1 if it recognized the command, otherwise 0.
 * Actually return value is used as can-repeat -flag
 */

int parse_string(object *op, char *str)
{
    CommFunc f;
    char *cp;

#ifdef INPUT_DEBUG
    LOG(llevDebug, "Command: '%s'\n", str);
#endif

    /*
     * No arguments?
     */
    if (!(cp=strchr(str, ' '))) {
	if (!op) {
	    if(active_socket == (sockets *) NULL) {
		LOG(llevError,"parse_string without active socket: %s\n",str);
		return 0;
	    }
	    if ((f=find_socketcommand(str)))
		return f(op, NULL);
	    if (active_socket->wiz && (f=find_wizcommand(str)))
		return f(op, NULL);
	    new_draw_info_format(NDI_UNIQUE, 0, op, "Unknown command: %s", str);
	    return 0;
	}
	op->contr->writing =0;
	if(!op->contr->no_echo) {
	    new_draw_info_format(NDI_UNIQUE, 0, op, ">%s", str);
	}
	if ((f=find_command(str)))
	    return f(op, NULL);
	if (QUERY_FLAG(op,FLAG_WIZ) && (f=find_wizcommand(str)))
	    return f(op, NULL);

	if(op) {
	    op->contr->no_echo = 0;
	    new_draw_info(NDI_UNIQUE, 0,op, "Unknown command.  Try help.");
	}
	return 0;
    }

    /*
     * Command with some arguments
     */

    *(cp++) ='\0';

    if (!op) {
	if(active_socket == (sockets *) NULL) {
	    LOG(llevError,"parse_string without active socket: %s %s\n",str, cp);
	    return 0;
	}
	if ((f=find_socketcommand(str)))
	    return f(op, cp);
	if (active_socket->wiz && (f=find_wizcommand(str)))
	    return f(op, cp);
	return 0;
    }

    op->contr->writing =0;
    if(!op->contr->no_echo) {
	new_draw_info_format(NDI_UNIQUE, 0, op, ">%s %s", str, cp);
    }
    if ((f=find_command(str)))
	return f(op, cp);
    if (QUERY_FLAG(op, FLAG_WIZ) && (f=find_wizcommand(str)))
	return f(op, cp);

    if(op) {
	op->contr->no_echo = 0;
	new_draw_info(NDI_UNIQUE, 0,op, "Unknown command.  Try help.");
    }
    return 0;
}

/* This function is used the player hit a character in extended input
 * mode (ie, after the ' has been pressed.)
 */

int parse_writing(object *op, char k)
{
  player *pl=op->contr;

  if (k != 13) {
    write_ch(op, k);
    return 1;
  }

  if(pl->write_buf[0] != '>')
    LOG(llevError, "Corrupted commandline (%s)\n", pl->write_buf);

  pl->write_buf[pl->writing] ='\0';
  return parse_command(op, pl->write_buf+1);
}


int parse_key(object *op, char k, KeyCode kc, KeySym keysym)
{
  int i, ix;
  Key_s *key;
  player *pl=op->contr;
  static char p_buff[MAX_BUF];
  char *charkey;

#ifdef INPUT_DEBUG

  if (k >= ' ')
    fprintf(stderr, "Key '%c' Keycode %d Keysym '%s'\n",
	k, (int)kc, XKeysymToString(keysym));
  else
    fprintf(stderr, "Keycode %d Keysym '%s'\n",
	(int)kc, XKeysymToString(keysym));
#endif

  if(pl->viewmap) {
    pl->viewmap=0;
    refresh(op);
  }

  if (kc == pl->commandkey && keysym == pl->commandkeysym && 
      !pl->writing) {
    write_ch(op,'>');
    pl->count=0;
    return 1;
  }
  if ((kc == pl->firekey[0] && keysym == pl->firekeysym[0]) ||
      (kc == pl->firekey[1] && keysym == pl->firekeysym[1])) {
	pl->fire_on=1;
	draw_message_window(op);
	return 1;
  }
  if ((kc == pl->runkey[0] && keysym == pl->runkeysym[0]) ||
      (kc == pl->runkey[1] && keysym == pl->runkeysym[1])) {
	pl->run_on=1;
	draw_message_window(op);
	return 1;
  }
  if(pl->writing) {
    if (keysym != NoSymbol &&
	!(IsKeypadKey(keysym) || IsCursorKey(keysym) || IsPFKey(keysym) ||
	  IsFunctionKey(keysym) || IsMiscFunctionKey(keysym) ||
	  IsModifierKey(keysym)))
      return parse_writing(op, k);
  }

  pl->prev_keycode =kc,
  pl->prev_keysym =keysym,
  pl->prev_cmd =k;
  pl->prev_fire_on =pl->fire_on;

  ix =(int)(kc) % COMMAND_HASH_SIZE;
  for (key=pl->keys[ix]; key ; key =key->next) {
    if ((key->keycode != kc) ||
	(key->keysym != NoSymbol && key->keysym != keysym) ||
	(key->flags & KEYF_WIZ && !QUERY_FLAG(op, FLAG_WIZ)) ||
	(pl->fire_on && !(key->flags & KEYF_FIRE)) ||
	(pl->run_on && !(key->flags & KEYF_RUN)) ||
	(!pl->fire_on && !pl->run_on &&
	 !(key->flags & KEYF_NORMAL)))
      continue;
    if (key->func) {
      if (key->params) {
	strcpy(p_buff, key->params);
	return (key->func)(op, p_buff);
      }
      return (key->func)(op, NULL);
    }
    if (key->params) {
      if (!(key->flags & KEYF_EDIT))
	return parse_command(op, key->params);
      if (pl->writing)
	return 1;
      write_ch(op, '>');
      for (i=0; key->params[i]; i++)
	write_ch(op, key->params[i]);
      return 1;
    }
    return 1;
  }

  if(k >= '0' && k <= '9') {
    pl->count =((k-'0')+(int)pl->count*10) % 100000;
    new_draw_info_format(NDI_UNIQUE, 0, op,
	"Count: %d.", pl->count);
    return 1;
  }

  charkey=XKeysymToString(keysym);
  new_draw_info_format(NDI_UNIQUE, 0, op,
	"Key unused (%s%s%s)",
	  (pl->fire_on? "Fire&": ""),
	  (pl->run_on ? "Run&" : ""),
	  (charkey ? charkey:"(null)"));
  pl->count_left=0;
  pl->count=0;
  return 1;
}

void handle_keyrelease(object *op,KeyCode kc,KeySym ks)
{
  player *pl = op->contr;
  pl->key_down=0;

/* If in ST_MENU_MORE mode, we need to catch the release of shift
 * key, from the user typing shift-a-  A)pply (the menu sign).  Otherwise,
 * the player is frozen in place until shift is pressed again.
 * Mark Wedel (master@cats.ucsc.edu)
 */

  if ((kc == pl->firekey[0] && ks == pl->firekeysym[0]) ||
      (kc == pl->firekey[1] && ks == pl->firekeysym[1])) {
	pl->fire_on=0;
	draw_message_window(op);
	return;
  }
  if ((kc == pl->runkey[0] && ks == pl->runkeysym[0]) ||
      (kc == pl->runkey[1] && ks == pl->runkeysym[1])) {
	pl->run_on=0;
	draw_message_window(op);
	return;
  }
}

void parse_key_release(object *op)
{
  KeyCode kc=op->contr->gevent.xkey.keycode;
  KeySym ks=op->contr->gkey;

  handle_keyrelease(op,kc,ks);
}

/*  this function handles splitting up a ; separated
 *  compound command into sub-commands:  it is recursive. 
 */
int parse_command(object *op, char *str) {
  char *tmp,*tmp2;
  int i;
  /* if it's a keybinding command, ignore semicolons */
  if(strstr(str,"bind")) return parse_string(op,str);

  /* If on a socket, you can not do complex commands. */
  if(op && (tmp=strchr(str,';'))!=NULL)  /* we've found a ';' do the 1st and recurse */
	 {
	    char buf[MAX_BUF];
		/* copy 1st command into buf */
		/* Even if tmp2 points the the input_buf, this should still
		 * be safe operation.
		 */
		for(i=0,tmp2=str;tmp2!=tmp;i++,tmp2++) 
		    buf[i]= (*tmp2);
		buf[i]='\0'; /* null terminate the copy*/
		strncpy(op->contr->input_buf,tmp2+1, MAX_BUF);
		op->contr->input_buf[MAX_BUF-1]=0;
		parse_string(op,buf);
	 }
  else {
	/* We need to set the input_buf to 0 so clear any complex keybinding
	 * there might be.  However, str can be a pointer to input_buf in
	 * the case of a complex keybinding.  So first we process the command,
	 * clear the buffer, and then return the value.
	 */
	i=parse_string(op,str);
	if (op) op->contr->input_buf[0]=0;
	return i;
  }
  return 0;
}

		
  
  
