#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <ctype.h>
#include <ssdef.h>
#include <descrip.h>
#include "global.h"
#include "command.h"
#include "constants.h"
#include "window.h"
#include "defines.h"
#include "debug.h"

extern char userhost[], g_body[MAXLEN], g_sent[MAXNICKLEN], g_invite[MAXLEN];
extern char g_from[MAXNICKLEN];
extern int quit, display, debug, sys$exit(), sys$setast();
extern win_ptr gwin, awins;
extern int list_min, list_max, list_public, list_private;
extern int names_min, names_max, names_public, names_private;
extern void bind_typetext(char *string), user_exec(char *string), irc_receive();
extern server_ptr gsrv;
extern int clean_word(char **str, char *word), set_echo(int);
extern void ff_strncpy(char *str, char *start, int len, int max);
extern void say();

int old_display, flushing = FALSE;
char flush_nick[MAXNICKLEN];

/* ------------------------------------------------------------------ */
extern void set_redirect(char *str, server_ptr srv), master_list();
extern char *next_arg(char **str);
extern win_ptr get_win_by_name(char *str);
/* ------------------------------------------------------------------ */
   void      user_window   (char *str);
/* ------------------------------------------------------------------ */

CmdList *find_command (CmdList *clist, int omax, char *com, int *cnt)
{
  int j, min=0, max, pos, len, old_pos = -1;
 
  len = com?strlen(com):0;
  max = omax;

  while (1) {
    pos = (max + min) / 2;
    if (pos == old_pos) {
      *cnt = 0;
      return ((CmdList *) 0);
    }
    old_pos = pos;
    j = strncasecmp(com, clist[pos].command, len);
    if (j == 0) break;
    else if (j < 0) max = pos-1;
    else min = pos+1;
  }
  *cnt = 1;
  min = pos - 1;
  while ((min >= 0) && (strncasecmp(com, clist[min].command, len) == 0)) {
    (*cnt)++;
    min--;
  }
  min++;
  max = pos + 1;
  while ((max < omax) && (strncasecmp(com, clist[max].command, len) == 0)) {
    (*cnt)++;
    max++;
  }
  if (strlen(clist[min].command) == len)
     *cnt *= -1;
  return (&(clist[min]));
}
 
/* ------------------------------------------------------------------ */
 
int lookup_command(command_ptr command_list, char *string, int *indx)
{
  int ret = c_none;
  int loop;
 
  *indx = 0;
  for (loop=0; command_list[loop].command; loop++) {
    if (strcasecmp(string, command_list[loop].command) == 0) {
      *indx = (int)command_list[loop].constant;
      return loop;
    }
    if (strncasecmp(string, command_list[loop].command, strlen(string)) == 0)
    {
      if (ret == c_none) {
        *indx = (int)command_list[loop].constant;
        ret = loop;
      } else {
        ret = c_amb;
        *indx = 0;
      }
    }
  }
  return ret;
}
 
/* ------------------------------------------------------------------ */
extern int assign_matches(char *str, char *ret, int let);
void user_foreach(char *str)
{
  char ass[MAXLEN], var[MAXLEN], rest[MAXLEN], exec[MAXLEN], ret[MAXLEN];
  char one[MAXLEN], *line;
  int dummy, disp;
  grab_word(&str, ' ', ass);
  grab_word(&str, ' ', var);
  while (isspace(*str)) str++;
  if ((*str!='{') || !strip_ends(str, '{', '}', rest, sizeof(rest))) {
    say("*** Invalid foreach - no matching {} found");
    return;
  }
  dummy = 0;
  strcat(ass, ".");
  assign_matches(ass, ret, sizeof(ret));
  line = ret;
  while (grab_word(&line, ' ', one)) {
    sprintf(exec, "%s %s", var, one + strlen(ass));
    disp = display;
    display = -1;
    user_assign(exec, TRUE);
    display = disp;
    parse_line(rest, "", TRUE, FALSE);
  }
}

/* ------------------------------------------------------------------ */
void expand_target(char *target)
{
  if (strcasecmp(target, "*") == 0) (void)get_channel(target);
}

/* ------------------------------------------------------------------ */
extern int sys$setddir();

void user_cd(char *string)
{
  char device[MAXLEN], newdir[MAXLEN], olddir[MAXLEN];
  struct dsc$descriptor_s old_d, new_d;
  unsigned short maxlen;
  int num, status;

  old_d.dsc$w_length = new_d.dsc$w_length = MAXLEN;
  old_d.dsc$b_dtype = new_d.dsc$b_dtype = DSC$K_DTYPE_T;
  old_d.dsc$b_class = new_d.dsc$b_class = DSC$K_CLASS_S;
  old_d.dsc$a_pointer = olddir;
  new_d.dsc$a_pointer = newdir;
  num = grab_word(&string, ':', device);
  if (num>0) {
  } else string = device;
  strcpy(newdir, string);
  new_d.dsc$w_length = strlen(string);
  status = sys$setddir(&new_d, &maxlen, &old_d);
  olddir[maxlen] = 0;
  if (status&1) {
    status = sys$setddir(0, &maxlen, &old_d);
    olddir[maxlen] = 0;
  }
  say("*** Current directory is now: %s", olddir);
}

/* ------------------------------------------------------------------ */

struct command_node log_levels[] =
{
  {{"PUBLIC"},    (char *)PUBLIC},
  {{"MSGS"},      (char *)MSGS},
  {{"NOTICE"},    (char *)NOTICES},
  {{"WALL"},      (char *)WALL},
  {{"WALLOP"},    (char *)WALLOP},
  {{"ACTIONS"},   (char *)ACTIONS},
  {{"CRAP"},      (char *)CRAP},
  {{NULL},        (char *)NULL}
};

/* ------------------------------------------------------------------ */

void user_while(char *string, int param, char *args)
{
  int bad = FALSE, added, done = FALSE, oldc;
  char *end = NULL, condition[MAXLEN], *ret, *line;

  while (isspace(*string)) string++;
  if (*string!='(') bad = TRUE;
  if (!bad) end = matching_end(++string, '(', ')');
  if (!end) {
    say("*** Matching () not found in while");
    return;
  }
  ff_strncpy(condition, string, end-string, sizeof(condition));
  end++;
  while (isspace(*end)) end++;
  string = end;
  if (*string!='{') bad = TRUE;
  if (!(end = matching_end(++string, '{', '}'))) {
    say("*** Matching {} not found in while");
    return;
  }
  oldc = *end;
  *end = 0;
  done = FALSE;
  do {
    ret = whatawho(condition, args, &added);
    if (ret && (*ret!=0) && (*ret!='0'))
      parse_line(string, args, TRUE, FALSE);
    else done = TRUE;
    if (ret) free(ret);
  } while (!done);
  *end = oldc;
}

/* ------------------------------------------------------------------ */

void user_lastlog(char *string)
{
  char word[MAXLEN], match[MAXLEN], re_match[MAXLEN];
  int flags = 0, num = 0, comm, dummy, oldlevel;
  win_ptr oldwin;

  while (*string=='-') {
    string++;
    (void)grab_word(&string, ' ', word);
    comm = lookup_command(log_levels, word, &dummy);
    if (comm<0) {
      say("*** Flag \"%s\" unknown or ambiguous.", word);
      return;
    } else
      flags |= dummy;
  }
  if (!flags) flags = ALL;
/*  (void)grab_word(&string, ' ', match); */
  strcpy(match, string);
  oldwin = set_outwin(gwin);
  oldlevel = set_loglevel(LASTLOG);
  say("*** Lastlog:");
  if ((num = atoi(match)) != 0) {
    int count = 1;
    list_ptr walk;
    walk = gwin->lastlog.top;
    while (walk && (walk->older) && (count<num)) {
      if ((walk->type & flags) != 0) count++;
      walk = walk->older;
    }
    while (walk) {
      if (walk->type & flags)
        say("%s", walk->text);
      walk = walk->newer;
    }
  } else {
    list_ptr walk;
    walk = gwin->lastlog.tail;
    if (!strchr(match, '*') && !strchr(match, '%') && !strchr(match, '?'))
      sprintf(re_match, "*%s*", match);
    else
      strcpy(re_match, match);
    say("Looking for [%s]", re_match);
    while (walk) {
      if (((walk->type & flags) != 0) && (wild_match(re_match, walk->text)>=0))
        say("%s", walk->text);
      walk = walk->newer;
    }
  }
  say("*** End of lastlog");
  set_outwin(oldwin);
  set_loglevel(oldlevel);
}

/* ------------------------------------------------------------------ */

void user_assign2(char *string, int param, char *args)
{
  int old, disp, added;
  char assign[MAXLEN], dummy[MAXLEN], *res;
  (void)grab_word(&string, ' ', assign);
  (void)grab_word(&string, ' ', dummy);
  if (strcmp(dummy, "=") != 0) {
    say("*** Bad assignment!");
  } else {
    old = sys$setast(0);
    if (debug & DEBUG$EXPAND) yell("..%s.. to whatawho", string);
    res = whatawho(string, args, &added);
    if (old==SS$_WASSET) (void)sys$setast(1);
    (void)sprintf(dummy, "%s %s\0", assign, res?res:"");
    free(res);
    disp = display;
    display = -1;
    user_assign(dummy, FALSE);
    display = disp;
  }
}

/* ------------------------------------------------------------------ */

void user_if(char *string, int param, char *args)
{
  int old, added;
  char *end, cond[MAXLEN], *walk, *ans, line[MAXLEN], *res, *parsed;

  if (!(end = strip_ends(string, '(', ')', cond, sizeof(cond)))) return;
  while (isspace(*end)) end++;
  if ((*end != '{') || !(walk = matching_end(++end, '{', '}')) ) {
    say("*** THEN condition missing from if");
    return;
  }

  old = sys$setast(0);
  res = whatawho(cond, args, &added);
/*  res = whatawho(cond, args?args:end, &added); /**/
  if (old==SS$_WASSET) (void)sys$setast(1);
  if (debug & DEBUG$USER_IF)
    yell("If expression expands to: (%s)", res?res:"null");
  if (res && (*res!=0) && (*res!='0')) {
    ans = cond;
    while (end!=walk) *ans++ = *end++;
  } else {
    ans = cond;
    for (++walk; *walk == ' '; walk++);
    if (*walk!='{') return;
    if (!(end = matching_end(++walk, '{', '}')))
    {
      say("*** ELSE condition incomplete from if");
      return;
    }
    while (walk!=end) *ans++ = *walk++;
  }
  *ans = 0;
  ans = cond;
  while (isspace(*ans)) ans++;
  parse_line(ans, args, TRUE, FALSE);
}

/* ------------------------------------------------------------------ */

void user_cmt(char *string)
{
}
 
/* ------------------------------------------------------------------ */
 
void user_ping(char *string)
{
  (void)new_send(gsrv, "PRIVMSG %s :\001PING %d\001\n", string, time(NULL));
}
 
/* ------------------------------------------------------------------ */

struct command_node names_flags[] = {
  {{"ALL"},     (char *)0},
  {{"MAX"},     (char *)1},
  {{"MIN"},     (char *)2},
  {{"PRIVATE"}, (char *)3},
  {{"PUBLIC"},  (char *)4},
  {{NULL},     (char *)NULL}
};
 
void user_names(char *string, int comm)
{
  int dummy, num;
  char word[80], msg[80];
 
  num = grab_word(&string, ' ', word);
  do {
    if (word[0] == '-') {
      num = lookup_command(names_flags, &word[1], &dummy);
      if (num < 0) {
        say("*** Invalid flag: %s", word);
        return;
      }
      if (dummy == 0) {
        if (comm) { list_public = TRUE; list_private = TRUE;}
        else { names_public = TRUE; names_private = TRUE;}
      }
      else if (dummy == 1) {
        (void)grab_word(&string, ' ', word);
        if (comm) list_max = atoi(word);
        else names_max = atoi(word);
      }
      else if (dummy == 2) {
        (void)grab_word(&string, ' ', word);
        if (comm) list_min = atoi(word);
        else names_min = atoi(word);
      }
      else if (dummy == 3) {
        if (comm) { list_public = FALSE; list_private = TRUE;}
        else { names_public = FALSE; names_private = TRUE;}
      }
      else if (dummy == 4) {
        if (comm) { list_public = TRUE; list_private = FALSE;}
        else { names_public = TRUE; names_private = FALSE;}
      }
      num = grab_word(&string, ' ', word);
    }
  } while (word[0] == '-');
  expand_target(word);
  if (comm) (void)new_send(gsrv, "LIST %s\n", word);
  else (void)new_send(gsrv, "NAMES %s\n", word);
}
 
/* ------------------------------------------------------------------ */
 
void user_expand(char *string, int flag)
{
  char new[MAXLEN];
  int added;
  while (*string == ' ') string++;
  if (flag)
    parse_line(string, "", TRUE, FALSE);
  else {
    expand_alias(string, "", &added, new, sizeof(new));
    say("old [%s] new [%s]", string, new);
  }
}
 
/* ------------------------------------------------------------------ */
 
void user_invite(char *string)
{
  char chan[MAXLEN], user[MAXLEN];
 
  (void)grab_word(&string, ' ', user);
  while (*string == ' ') string++;
  if (*string==0) (void)get_channel(chan);
  else (void)strcpy(chan, string);
  (void)new_send(gsrv, "INVITE %s :%s\n", user, chan);
}
 
/* ------------------------------------------------------------------ */
 
void user_desc(char *string)
{
  char s[MAXLEN];

/*  s = next_arg(&string); */
  (void)grab_word(&string, ' ', s);
  say("* -> %s: %s %s", s, gsrv->nick?gsrv->nick:"(null)", string);
  (void)new_send(gsrv, "PRIVMSG %s :\001ACTION %s\001\n", s, string);
}
 
/* ------------------------------------------------------------------ */
 
void user_kick(char *string)
{
  char chan[MAXLEN], msg[MAXLEN], comment[MAXLEN];
  char user[MAXLEN];
 
  (void)grab_word(&string, ' ', chan);
  while (*string == ' ') string++;
  if (*string==0) {
    (void)strcpy(user,chan);
    (void)get_channel(chan);
    (void)new_send(gsrv, "KICK %s %s\n",chan,user);
  } else {
    (void)grab_word(&string, ' ', user);
    while (*string == ' ') string++;
    if (*string==0)
      (void)new_send(gsrv, "KICK %s %s\n",chan,user);
    else
      (void)new_send(gsrv, "KICK %s %s :%s\n",chan,user,string);
  }
}
 
/* ------------------------------------------------------------------ */
 
void send_chan(char *string)
{
  char ack[MAXLEN];
  int oldlevel;
  win_ptr oldwin;

  oldwin = set_outwin(gwin);
  oldlevel = set_loglevel(PUBLIC);
  if (get_channel(ack)) {
    (void)new_send(gsrv, "PRIVMSG %s :%s\n", ack, string);
    say("> %s ", string);
  }
  else say("*** You aren't on a channel!");
  set_outwin(oldwin);
  set_loglevel(oldlevel);
}
 
/* ------------------------------------------------------------------ */
#define IS_DCC_CHAT(x) (*x=='=')
extern short dcc_lookup_chat_chan(char *);
/* ------------------------------------------------------------------ */
 
void send_msg(outuser, string)
  char *outuser;
  char *string;
{
  int ret_send, oldlevel, print = TRUE, chan;
  char msg[MAXLEN], on_line[MAXLEN], format[MAXLEN];
  win_ptr win, index, ret, oldwin;

  if (is_chan(outuser)) {
    (void)new_send(gsrv, "PRIVMSG %s :%s\n", outuser, string);
    if (get_chan_num(gsrv->chan_list, outuser, &win) && win) {
      (void)sprintf(on_line, "%s %s", outuser, string);
      oldwin = set_outwin(win);
      oldlevel = set_loglevel(PUBLIC);
      if (handle_on(o_send_public, on_line, 0)) print = FALSE;
      strcpy(format, "%0.0s> %s");
    } else  {
      win = type_winget(gsrv, PUBLIC);
      oldwin = set_outwin(win);
      strcpy(format, ">%s> %s");
    }
  } else if (IS_DCC_CHAT(outuser)) {
    chan = dcc_lookup_chat_chan(&outuser[1]);
    if (chan != -1) {
      (void)sprintf(msg, "%s\n", string);
      ret_send = tcp_send(chan, msg, strlen(msg));
      (void)sprintf(on_line, "%s %s", outuser, string);
      win = type_winget(gsrv, MSGS);
      oldwin = set_outwin(win);
      oldlevel = set_loglevel(MSGS);
      if (handle_on(o_send_msg, on_line, 0)) print = FALSE;
      outuser++;
      strcpy(format, "=> =%s= %s");
    }
    else say("*** There is no such active dcc connection.");
  } else {
    (void)strcpy(g_sent, outuser);
    (void)strcpy(g_body, string);
    win = NULL;
    for (index=awins; index; index = index->next)
      if ((index->server == gsrv) &&
          (strcasecmp(index->query, outuser) == 0)) win = index;
    if (!win) win = type_winget(gsrv, MSGS);
    oldwin = set_outwin(win);
    oldlevel = set_loglevel(MSGS);
    strcpy(format, "-> *%s* %s");
    (void)sprintf(on_line, "%s %s", outuser, string);
    if (handle_on(o_send_msg, on_line, 0)) print = FALSE;
    (void)new_send(gsrv, "PRIVMSG %s :%s\n", outuser, string);
  }
  if (print) say(format, outuser, string);
  set_loglevel(oldlevel);
  set_outwin(oldwin);
}
 
/* ------------------------------------------------------------------ */
/* Dough's flush - thanks to Bill Brennessel                          */
/* ------------------------------------------------------------------ */
extern void kill_hold();
/* ------------------------------------------------------------------ */
void user_flush(char *string)
{
  old_display = display;
  flushing = TRUE;
  (void)strcpy(flush_nick, gsrv->nick);
  (void)new_send(gsrv, "USERHOST %s\n", gsrv->nick);
  display = FALSE;
  kill_hold();
}
 
/* ------------------------------------------------------------------ */

struct userhost_struct userhost_list[MAX_USERHOST] =
{"","", "","", "","", "","", "","", "","", "","", "","", "","", "",""};

/* ------------------------------------------------------------------ */

void user_userhost(char *string)
{
  int loop;
  char nick[MAXLEN], cmd[MAXLEN];
  (void)grab_word(&string, ' ', nick);
  (void)grab_word(&string, ' ', cmd);
  if (strcasecmp(cmd, "-cmd")==0) {
    for (loop=0;loop<MAX_USERHOST;loop++) {
      if (!*userhost_list[loop].nick) {
        strcpy(userhost_list[loop].nick, nick);
        strcpy(userhost_list[loop].command, string);
        break;
      }
    }
    if (loop==8) 
      yell("*** Unable to add userhost to list.");
  }
  new_send(gsrv, "USERHOST %s\n", nick);
}
 
/* ------------------------------------------------------------------ */
 
void user_clear(char *string)
{
  clear_window();
}
 
/* ------------------------------------------------------------------ */
 
void user_msg(char *string)
{
  char loca[MAXLEN];
 
  (void)grab_word(&string, ' ', loca);
  if (strcmp(loca, ",") == 0) strcpy(loca, g_from);
  else if (strcmp(loca, ".") == 0) strcpy(loca, g_sent);
  send_msg(loca, string);
}
 
/* ------------------------------------------------------------------ */
 
void user_query(char *string)
{
  char mess[MAXLEN];
  char user[MAXLEN];
  win_ptr oldwin;

  (void)grab_word(&string, ' ', user);
  if (strlen(user) == 0) {
    if (clearquery(user))
      (void)sprintf(mess, "You stop querying %s.", user);
    else
      (void)sprintf(mess, "You weren't querying anyone.");
  } else {
    setquery(user);
    (void)sprintf(mess, "You are now querying %s.", user);
  }
  oldwin = set_outwin(gwin);
  say("%s", mess);
  set_outwin(oldwin);
}
 
/* ------------------------------------------------------------------ */
 
void user_nick(char *string)
{
  char nick[MAXLEN];
 
  (void)grab_word(&string, ' ', nick);
  if (strlen(nick) > 9)
    say("Nick names must be 9 characters or less.");
  else
    (void)new_send(gsrv, "NICK %s\n", nick);
}
 
/* ------------------------------------------------------------------ */
 
void user_join(char *string)
{
  int indx;
  char chan[MAXLEN];
  win_ptr win;
  (void)grab_word(&string, ' ', chan);
  if ((*chan=='-') &&
      (strncasecmp("-INVITE", chan, (strlen(chan)>7)?7:strlen(chan))==0) )
    strcpy(chan, g_invite);
  if (gsrv) indx = get_chan_num(gsrv->chan_list, chan, &win);
  else indx = 0;
  if (indx) set_channel(indx);
  (void)new_send(gsrv, "JOIN %s\n", chan);
}
 
/* ------------------------------------------------------------------ */
 
void user_away(char *string)
{
  if (strlen(string) == 0)  gsrv->away = FALSE;
  else                      gsrv->away = TRUE;
  (void)new_send(gsrv, "AWAY :%s\n", string);
}
 
/* ------------------------------------------------------------------ */
 
void user_topic(char *string)
{
  char chan[TOLEN];
 
  (void)grab_word(&string, ' ', chan);
  expand_target(chan);
  if (*string) (void)new_send(gsrv, "TOPIC %s :%s\n", chan, string);
  else (void)new_send(gsrv, "TOPIC %s\n", chan);
}
 
/* ------------------------------------------------------------------ */

void user_notice(char *string)
{
  char target[MAXLEN];
  win_ptr win, oldwin;

  (void)grab_word(&string, ' ', target);
  (void)get_chan_num(gsrv->chan_list, target, &win);
  if (win==NW) win = type_winget(gsrv, CRAP);
  oldwin = set_outwin(win);
  say("-> -%s- %s", target, string);
  set_outwin(oldwin);
  (void)new_send(gsrv, "NOTICE %s :%s\n", target, string);
}

/* ------------------------------------------------------------------ */
void user_oper(char *str)
{
  int old;
  char user[MAXLEN], pass[MAXLEN];
  grab_word(&str, ' ', user);
  grab_word(&str, ' ', pass);
  if (*pass == 0)
  {
    old = set_echo(FALSE);
    read_input(pass, sizeof(pass)-1, "Password: ");
    set_echo(old);
  }
  new_send(gsrv, "OPER %s %s\n", user, pass);
}

/* ------------------------------------------------------------------ */
char basicarray[30][20] = {
  {"WHO "},
  {"PART "},
  {"WHOIS "},
  {"LIST "},
  {"NOTICE "},
  {"MODE "},
  {"MOTD "},
  {""},
  {"ADMIN "},
  {"LUSERS "},
  {"TIME "},
  {"INFO "},
  {"OPER "},
  {"CONNECT "},
  {"TRACE "},
  {"DIE "},
  {"REHASH "},
  {"RESTART "},
  {"LINKS "},
  {"STATS "},
  {"SUMMON "},
  {"WALLOPS "},
  {"WHOWAS "},
  {"NAMES "},
};
 
void usr_bsc(char *string, int comm)
{
  char target[MAXLEN];
  if (comm<0) {
    comm = -comm;
    (void)grab_word(&string, ' ', target);
    expand_target(target);
    (void)new_send(gsrv, "%s%s %s\n", basicarray[comm-1], target, string);
    if (comm==4) (void)handle_on(o_send_notice, string, 0);
  } else {
    (void)new_send(gsrv, "%s%s\n", basicarray[comm], string);
    if (comm==4) (void)handle_on(o_send_notice, string, 0);
  }
}
 
/* ------------------------------------------------------------------ */
 
void usr_bsc2(char *string, int comm)
{
  char first[MAXLEN];
 
  (void)grab_word(&string, ' ', first);
  (void)new_send(gsrv, "%s %s :%s\n", basicarray[comm], first, string);
}
 
/* ------------------------------------------------------------------ */
 
void user_quit(char *string)
{
  user_basic3("QUIT", *string?string:"Leaving");
  quit = TRUE;
  delete_windows();
  (void)cleanup();
  (void)sys$exit(1);
}
 
/* ------------------------------------------------------------------ */
 
void user_basic3(comm, string)
  char *comm;
  char *string;
{
  (void)new_send(gsrv, "%s :%s\n", comm, string);
}
 
/* ------------------------------------------------------------------ */
 
void user_me(char *string)
{
  char msg[MAXLEN], to[MAXLEN];
  char loca[MAXLEN], on_line[MAXLEN];
  win_ptr oldwin, win;
  int oldlevel;
  if (!getquery(to))
    if (!get_channel(to)) {
      say("*** No target specified.");
      return;
    }
  (void)new_send(gsrv, "PRIVMSG %s :\001ACTION %s\001\n", to, string);
  (void)sprintf(on_line, "%s %s", gsrv->nick, string);
  win = gwin;
/*  win = type_winget(gsrv, ACTIONS); */
  oldwin = set_outwin(win);
  oldlevel = set_loglevel(ACTIONS);
  if (!handle_on(o_send_action, on_line, 0))
    say("*> %s %s", gsrv->nick, string);
  set_outwin(oldwin);
  set_loglevel(oldlevel);
}
 
/* ------------------------------------------------------------------ */

server_ptr change_server(server, port, nick, oldser)
  char *server, *nick;
  int port;
  server_ptr oldser;
{
  short chan;
  unsigned status;
  char old[MAXLEN];
  server_ptr temp = NULL;
  if ((chan = open_connection(server, port, &status, oldser)) == -1) {
    say("*** could not open connection");
  } else {
    if (oldser) {
      (void)strcpy(old, oldser->server_name);
      (void)handle_on(o_disconnect, old, 0);
    }
    if (temp = (server_ptr)add_server(server, port, NULL, nick, chan, oldser))
    {
      (void)handle_on(o_connect, server, 0);
      irc_receive(temp);
    }
  }
  return temp;
}

/* ------------------------------------------------------------------ */

char *win_newserver(char *str, server_ptr serv);

void user_server(char *string)
{
  string = win_newserver(string, gsrv);
}
 
/* ------------------------------------------------------------------ */
 
void user_deop(char *string)
{
  (void)new_send(gsrv, "MODE %s -o\n", gsrv->nick);
}
 
/* ------------------------------------------------------------------ */
 
void user_squit(char *string)
{
  char srver[MAXLEN];
 
  (void)grab_word(&string, ' ', srver);
  while (*string == ' ') string++;
  if (*string) {
    (void)new_send(gsrv, "SQUIT %s :%s\n",srver,string);
  } else {
    say("Server Quit comments required for /squit.");
  }
}
 
/* ------------------------------------------------------------------ */
 
void user_echo(char *string)
{
  yell("%s", string?string:"(null)");
}
 
/* ------------------------------------------------------------------ */
 
void user_kill(char *string)
{
  char user[MAXLEN];
 
  (void)grab_word(&string, ' ', user);
  while (*string == ' ') string++;
  if (*string)
    (void)new_send(gsrv, "KILL %s :%s\n",user, string);
  else
    say("Kill comments required for /kill.");
}
 
/* ------------------------------------------------------------------ */
void user_ctcp(char *string)
{
  char to[MAXLEN], *s;
 
  (void)grab_word(&string, ' ', to);
  while (*string==32) string++;
  if (to[0] == 0)
    say("*** Request from whom?");
  else if (string[0] == 0)
         (void)new_send(gsrv, "PRIVMSG %s :\001VERSION\001\n", to);
       else {
         for (s = string; *s && (*s != ' '); s++) *s = toupper(*s);
         (void)new_send(gsrv, "PRIVMSG %s :\001%s\001\n", to, string);
       }
}
 
/* ------------------------------------------------------------------ */
void admin_disabled(char *word, int param)
{
  char comm[20];
  strcpy(comm, "<unknown>");
  if (param==0)
    strcpy(comm, "ON");
  else if (param==1)
    strcpy(comm, "EXEC");
  else if (param==2)
    strcpy(comm, "SPAWN");
  yell("The %s command has been disabled by your admin.", comm);
}
 
/* ------------------------------------------------------------------ */
void user_version(char *str)
{
  if (!*str)
  {
    say("Client version: \002%s\002", VERSION);
    say("Thanks to the following people:");
    say("masdough   masdough@ubvms.cc.buffalo.edu (Author)");
    say("tychy      Z_HERRONLM@CCSVAX.SFASU.EDU");
    say("toofache   iscnwl@mars.cc.nus.sg");
    say("bighorn    ARSEBESTYEN@CSUPomona.Edu");
    say("fordy      fordenem@scobva.cobleskill.edu");
    say("squiz      s_arnold@hns.com");
    say("matt       matt@sluaxa.slu.edu");
    say("uros       brvar@rcum.uni-mb.si");
    say("tboard     TEST@KUPHSX.PHSX.UKANS.EDU");
    say("friar      7169::sparkss");
  }
  new_send(gsrv, "VERSION %s\n", str); 
}

/* ------------------------------------------------------------------ */
int waiting = FALSE;
void user_wait(char *word)
{
  int loop = 0;
  waiting = TRUE;
  do {
    if (loop++==0) (void)new_send(gsrv, "WAIT\n");
  } while (waiting);
}

/* ------------------------------------------------------------------ */
void user_redirect(char *str)
{
  char *whom;
  whom = next_arg(&str);
  set_redirect(whom, gsrv);
  parse_line(str, NULL, TRUE, TRUE);
  new_send(gsrv, "REDIRECT\n"); 
}

/* ------------------------------------------------------------------ */
void user_notify(char *str)
{
  notify(gsrv, str);
}

/* ------------------------------------------------------------------ */

void user_show_chan(char *str)
{

}

/* ------------------------------------------------------------------ */
void user_xecho(char *str)
{
  win_ptr win = NW, oldwin;
  int level = CRAP, oldlevel, dummy=0;
  char *word;
  while (*str=='-') {
    word = next_arg(&str);
    word++;
    if (strcasecmp(word, "level") == 0) {
      word = next_arg(&str);
      level = dummy = 0;
      lookup_level(word, &level, &dummy);
    }
    else if (strcasecmp(word, "window") == 0) {
      word = next_arg(&str);
      win = get_win_by_name(word);
    } else {
      say("*** Unknown flag: %s", word);
      return;
    }
  }
  if (win==NW) win = type_winget(gsrv, level);
  oldwin = set_outwin(win);
  oldlevel = set_loglevel(level);
  yell("%s", str);
  set_loglevel(oldlevel);
  set_outwin(oldwin);
}

/* ------------------------------------------------------------------ */

static CmdList command_list[] =
{
  { "",          send_chan,      0 },
  { "#",         user_cmt,       0 },
  { ":",         user_cmt,       0 },
  { "@",         user_assign2,   0 },
  { "aaa",       master_list,    0 },
  { "admin",     usr_bsc,        8 },
  { "alias",     user_alias,     0 },
  { "assign",    user_assign,    1 },
  { "away",      user_away,      0 },
  { "bind",      dobind,         0 },
  { "bye",       user_quit,      0 },
  { "cd",        user_cd,        0 },
  { "channel",   user_join,      0 },
  { "clear",     user_clear,     0 },
  { "comment",   user_cmt,       0 },
  { "connect",   usr_bsc,       13 },
  { "ctcp",      user_ctcp,      0 },
  { "date",      usr_bsc,       10 },
  { "dcc",       user_dcc,       0 },
  { "deop",      user_deop,      0 },
  { "describe",  user_desc,      1 },
  { "die",       usr_bsc,       15 },
  { "echo",      user_echo,      0 },
  { "eval",      user_expand,    1 },
#ifdef ENABLE_EXEC
  { "exec",      user_exec,      0 },
#else
  { "exec",      admin_disabled, 1 },
#endif
  { "exit",      user_quit,      0 },
  { "expand",    user_expand,    0 },
  { "flush",     user_flush,     0 },
  { "foreach",   user_foreach,   0 },
  { "help",      help,           0 },
  { "if",        user_if,        0 },
  { "ignore",    user_ignore,    0 },
  { "info",      usr_bsc,       11 },
  { "invite",    user_invite,    0 },
  { "join",      user_join,      0 },
  { "kick",      user_kick,      0 },
  { "kill",      user_kill,      0 },
  { "lastlog",   user_lastlog,   0 },
  { "leave",     usr_bsc,       -2 },
  { "links",     usr_bsc,       18 },
  { "list",      user_names,     1 },
  { "load",      load_file,      0 },
  { "lusers",    usr_bsc,        9 },
  { "me",        user_me,        0 },
  { "mode",      usr_bsc,       -6 },
  { "motd",      usr_bsc,        6 },
  { "msg",       user_msg,       0 },
  { "names",     user_names,     0 },
  { "nick",      user_nick,      0 },
  { "notice",    user_notice,    0 },
  { "notify",    user_notify,    0 },
#ifdef ENABLE_ON
  { "on",        on,             0 },
#else
  { "on",        admin_disabled, 0 },
#endif
  { "oper",      user_oper,      0 },
  { "part",      usr_bsc,       -2 },
  { "ping",      user_ping,      0 },
  { "query",     user_query,     0 },
  { "quit",      user_quit,      0 },
  { "quote",     usr_bsc,        7 },
  { "redirect",  user_redirect,  0 },
  { "rehash",    usr_bsc,       16 },
  { "remap",     remap,          0 },
  { "restart",   usr_bsc,       17 },
  { "say",       send_chan,      0 },
  { "server",    user_server,    0 },
  { "set",       user_set,       0 },
  { "show_chan", user_show_chan, 0 },
#ifdef ENABLE_SPAWN
  { "spawn",     user_spawn,     0 },
#else
  { "spawn",     admin_disabled, 2 },
#endif
  { "squit",     user_squit,     0 },
  { "stats",     usr_bsc,       19 },
  { "summon",    usr_bsc,       20 },
  { "time",      usr_bsc,       10 },
  { "topic",     user_topic,     0 },
  { "trace",     usr_bsc,       14 },
  { "type",      bind_typetext,  0 },
  { "userhost",  user_userhost,  0 },
  { "version",   user_version,   0 },
  { "wait",      user_wait,      0 },
  { "wallop",    usr_bsc2,      21 },
  { "while",     user_while,     0 },
  { "who",       usr_bsc,       -1 },
  { "whois",     usr_bsc,        2 },
  { "whowas",    usr_bsc,       22 },
  { "window",    user_window,    0 },
  { "xecho",     user_xecho,     0 },
};
 
/* ------------------------------------------------------------------ */
 
#define NUMBER_OF_COMMANDS (sizeof(command_list) / sizeof(CmdList)) - 1
 
/* ------------------------------------------------------------------ */
extern int input_aliases, inalias, load_depth;

void parse_line(char *str, char *args, int append, int interactive)
{
  int added = 0;
  char line[MAXLEN*10], *walk, *owalk, *add;

  walk = str;
  if (args) {
    do {
      owalk = walk;
      walk = expand_alias(walk, args, &added, line, sizeof(line));
      if (walk && !*walk && *line && append && !added && args && *args) {
        add = line + strlen(line);
        sprintf(add, " %s", args);
      }
      if (debug & DEBUG$EXPAND)
        yell("Expanded [%s] -to- [%s] (%s)", owalk?owalk:"null",
             line, args?args:"null");
      if (*line) handle_command_line(line, interactive, args);
    } while (walk && *walk);
  } else
    if (load_depth) {
      handle_command_line(str, interactive, args);
    } else
      while (*walk)  {
        grab_word(&walk, '\n', line);
        handle_command_line(line, interactive, args);
      }
}

/* ------------------------------------------------------------------
 * expand_alais:
 */

#define BRACE_L '{'
#define BRACE_R '}'
#define PAREN_L '('
#define PAREN_R ')'

char *expand_alias(char *string, char *args, int *added, char *ret, int len)
{
  char lin[MAXLEN], *walk, quote_str[MAXLEN], other, *saved, *new;
  int done = 0, quote_len = 0;

  walk = ret;
  *ret = 0;
  if (*string == '@') *added = TRUE;
  while (*string && !done) {
    switch (*string)
    {
    case '$':
      string++;
      while (*string == '^') {
        string++;
        quote_str[quote_len++] = *string++;
      }
      saved = string;
      string = alias_special(saved, args, added, walk, len, FALSE);
      walk += strlen(walk);
      break;
    case ';':
      done = TRUE;
      string++;
      break;
    case PAREN_L:
    case BRACE_L:
      *walk++ = *string;
      other = (*string==BRACE_L)?BRACE_R:PAREN_R;
      new = strip_ends(string, *string, other,walk, MAXLEN);
      if (!new)
      {
        string++;
        yell("*** Unmatched %c", (other==BRACE_R)?'{':'(');
      } else {
        walk += strlen(walk);
        *walk++ = other;
        string = new;
      }
      break;
    case '\\':
      string++;
      if (*string) *walk++ = *string++;
      break;
    default:
      *walk++ = *string++;
      break;
    }
  }
  *walk = 0;
  return string;
}

int exe_level = 0;

void handle_command_line(char *string, int interactive, char *param)
{
  CmdList *command;
  char word[MAXLEN], *temp = NULL, dummy[MAXLEN*10];
  char outuser[MAXLEN], *news;
  int cmd_cnt, expand = 1, olddisplay, did = FALSE;

  if (debug & DEBUG$EXECUTE)
    yell("Execute: [%d] \026%s\026", exe_level, string);
  olddisplay = display;
  if (!string || (!*string)) return;
  if ((*string == '/') || !interactive)
  {
    if (*string == '/') string++;
    if (*string == '/') {string++;expand=0;}  /* Two backslashes in a row */
    if (*string == '^') {
      string++;
      display = -1;
    }
    if (*string=='#') return;

    if (*string == '@') {
      word[0] = *string++;
      word[1] = 0;
    } else if ((*string == ' ') && (interactive)) {
      string++;
      word[0] = 0;
    } else (void)clean_word(&string, word);
   if (expand) {
     exe_level++;
     did = exec_alias(word, string, param);
     exe_level--;
     if (did) {
       if (display==-1) display = olddisplay;
       return;
     }
   }
    command = find_command(command_list, NUMBER_OF_COMMANDS, word, &cmd_cnt);

    if (cmd_cnt == 0)
      say("Command %s is not known.", word);
    else if (((cmd_cnt < 0) || (cmd_cnt == 1)) && command) {
      strcpy(dummy, string);
      command->cmdfcn(dummy, command->varpar, param); /* was news */
    } else
      say("Command %s is ambiguous.", word);
    if (display==-1) display = olddisplay;
    if (temp) free(temp);
    return;
  }
  if (getquery(outuser)) send_msg(outuser, string);
  else send_chan(string);
  if (display==-1) display = olddisplay;
}
