#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <ctype.h>
#include <ssdef.h>
#include <descrip.h>
#include <signal.h>
#include <lib$routines.h>
#include "global.h"
#include "command.h"
#include "constants.h"
#include "window.h"
#include "qio.h"
#include "notify.h"
#include "chan.h"
#include "alias.h"
#include "binding.h"
#include "queue.h"
#include "level.h"
#include "set.h"
#include "on.h"
#include "exec.h"
#include "split.h"
#include "window.h"
#include "sock.h"
#include "winfunc.h"
#include "dcc.h"
#include "inp.h"
#include "ff.h"
#include "irchelp.h"
#include "ignore.h"
#include "load.h"
#include "server.h"
#include "mode.h"

extern char userhost[], *g_body, *g_sent, *g_invite, *g_from;
extern int quit;
extern win_ptr gwin, awins;
extern struct list_structs list_rpl, names_rpl, who_rpl;
extern server_ptr gsrv;

int old_display, flushing = FALSE;
char *flush_nick;

/* ------------------------------------------------------------------ */
CmdList *exact_find_command (CmdList *clist, int omax, char *com)
{
  int j, min=0, max, pos, old_pos = -1;
 
  max = omax;
  while (1) {
    pos = (max + min) / 2;
    if (pos == old_pos) return (CmdList *)NULL;
    old_pos = pos;
    j = strcasecmp(com, clist[pos].command);
    if (j == 0) return &clist[pos];
    if (j < 0) max = pos-1;
    else min = pos+1;
  }
  return (CmdList *)0;
}

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_node *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;
}
 
/* ------------------------------------------------------------------ */
static char *next_expr(char **str, char tt)
{
  char *temp, *ptr2, *ptr3, *ptr;
  if (!*str) return NULL;
  ptr = *str;
  if (!*ptr) return NULL;
  if (*ptr != tt) {
    say("Expression syntax");
    return NULL;
  }
  temp = matching_end(ptr+1, tt, (tt=='(')?')':'}');
  if (!temp) {
    say("Unmatched %c", tt);
    return NULL;
  }
  for (ptr++; *ptr == ' '; ptr++);
  for (ptr2 = temp + 1; *ptr2 == ' '; ptr2++);
  for (ptr3 = temp - 1; *ptr3 == ' '; ptr3--);
  ptr3[1] = 0;
  *str = ptr2;
  return ptr;
}

/* ------------------------------------------------------------------ */
static void user_foreach(char *str, int varpar, char *subargs)
{
  char ass[MAXLEN], exec[MAXLEN], *rest, ret[MAXLEN*5], *var;
  char one[MAXLEN], *line, *sline;
  int dummy, disp;

  sline = line = m_strcpy(str);
  grab_word(&line, ' ', ass);
  strcat(ass, ".");
  assign_matches(ass, ret, sizeof(ret));
  var = m_grab_word(&line, ' ');
  
  while (*line==' ') line++;
  if (!(rest = next_expr(&line, '{')))
     say("*** Missing commands in FOREACH");
  else {
    line = ret;
    while (grab_word(&line, ' ', one)) {
      sprintf(exec, "%s %s", var, one);
      disp = getivar(VAR_DISPLAY);
      setivar(VAR_DISPLAY, -1);
      user_assign(exec, TRUE, NULL);
      setivar(VAR_DISPLAY, disp);
      parse_line(rest, subargs, FALSE, FALSE);
    }
  }
  myfree(var);
  myfree(sline);
}

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

/* ------------------------------------------------------------------ */
#define is_true(_xx) ((_xx ) && (*(_xx)) && (*(_xx) != '0'))

static void loop_raw(char *pre, char *cond, char *post, char *body,
                     int precond, char *args)
{
  char *ret;
  int added = 1;
  if (pre) ret = parse_inline(pre, args, &added);
  if (precond) {
    ret = parse_inline(cond, args, &added);
    if (!is_true(ret)) return;
  }
  while (1) {
    parse_inline(body, args, &added);
    if (post) parse_inline(post, args, &added);
    ret = parse_inline(cond, args, &added);
    if (!is_true(ret)) break;
  }
}

/* ------------------------------------------------------------------ */
static void user_while(char *string, int param, char *args)
{
  int added = 0, done = FALSE;
  char *end = NULL, *condition, *ret, *line, *cond, *m_end;

  line = condition = m_strcpy(string);
  if (!(cond = next_expr(&line, '(')))
     say("*** Missing condition in WHILE");
  else if (!(end = next_expr(&line, '{')))
     say("*** Missing command in WHILE");
  else {
    done = FALSE;
    m_end = m_strcpy(end);
    while (!done)
    {
      ret = parse_inline(cond, args, &added);
      if (ret && *ret && (*ret!='0'))
      {
        parse_line(m_end, args, FALSE, FALSE);
        strcpy(m_end, end);
      } else done = TRUE;
      if (ret) myfree(ret);
    }
    myfree(m_end);
  }
  myfree(condition);
}

/* ------------------------------------------------------------------ */
static void user_assign2(char *string, int param, char *args)
{
  int added = 1, old;
  char *res;

  old = getivar(VAR_DISPLAY);
  setivar(VAR_DISPLAY, -1);
  res = parse_inline(string, args, &added);
  myfree(res);
  if (getivar(VAR_DISPLAY) == -1) setivar(VAR_DISPLAY, old);
}
/* ------------------------------------------------------------------ */
static void user_if(char *string, int param, char *args)
{
  int old, added = 1;
  char *walk, *ans, *res;
  char *saved, *line, *cond;

  line = saved = m_strcpy(string);
  if (!(cond = next_expr(&line, '(')))
    yell("*** Missing condition in IF");
  else if (!(walk = next_expr(&line, '{')))
    yell("*** THEN condition missing from IF");
  else {
    res = parse_inline(cond, args, &added);
    if (getivar(VAR_DEBUG) & DEBUG$USER_IF)
      yell("If expression expands to: (%s)", res?res:"null");
    if (!res || (*res==0) || (*res=='0'))
      walk = next_expr(&line, '{');
    if (walk) parse_line(walk, args, FALSE, FALSE);
  }
  myfree(saved);
}
/* ------------------------------------------------------------------ */
static void user_cmt(char *string, int varpar, char *subparams)
{
}
/* ------------------------------------------------------------------ */
static struct command_node names_flags[] = {
  {{"ALL"},     (char *)0},
  {{"MAX"},     (char *)1},
  {{"MIN"},     (char *)2},
  {{"PRIVATE"}, (char *)3},
  {{"PUBLIC"},  (char *)4},
  {{"CHOPS"},   (char *)5},
  {{"OPS"},     (char *)6},
  {{NULL},     (char *)NULL}
};
 
static void user_names(char *string, int comm, char *subparams)
{
  int dummy, num;
  char word[80], msg[80], *target;
  struct list_structs *ptr;

  num = grab_word(&string, ' ', word);
  if (comm==1) ptr = &list_rpl;
  else if (comm==2) ptr = &who_rpl;
  else ptr = &names_rpl;
  ptr->chop_only = ptr->ircop_only = FALSE;
  do {
    if (word[0] == '-') {
      num = lookup_command(names_flags, &word[1], &dummy);
      if (num < 0) {
        say("*** Invalid flag: %s", word);
        return;
      }
      if (dummy == 0) {
        ptr->public = ptr->private = TRUE;
      }
      else if (dummy == 1) {
        (void)grab_word(&string, ' ', word);
        ptr->max = atoi(word);
      }
      else if (dummy == 2) {
        (void)grab_word(&string, ' ', word);
        ptr->min = atoi(word);
      }
      else if (dummy == 3) {
        ptr->public = FALSE;
        ptr->private = TRUE;
      }
      else if (dummy == 4) {
        ptr->public = TRUE;
        ptr->private = FALSE;
      }
      else if (dummy == 5)
        ptr->chop_only = TRUE;
      else if (dummy == 6)
        ptr->ircop_only = TRUE;
      num = grab_word(&string, ' ', word);
    }
  } while (word[0] == '-');
  target = expand_target(word);
  if (!target && strstr(word, "*")) {
    if (ptr->str) {
      say("*** There is already a %s active. Please wait.",
          (comm==1)?"list":((comm==0)?"names":"who"));
      return;
    } else ptr->str = m_strcpy(word);
    *word = 0;
  }
  new_send(gsrv, "%s %s\n", (comm==1)?"LIST":((comm==0)?"NAMES":"WHO"),
           target?target:word);
}
 
/* ------------------------------------------------------------------ */
static void user_expand(char *string, int flag, char *subparams)
{
  char new[MAXLEN*10];
  int added;
  while (*string == ' ') string++;
  if (flag)
    parse_line(string, "", FALSE, FALSE);
  else {
    expand_alias(string, "", &added, new, sizeof(new));
    say("old [%s] new [%s]", string, new);
  }
}
/* ------------------------------------------------------------------ */
static void send_chan(char *string, int varpar, char *subparams)
{
  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=='=')
/* ------------------------------------------------------------------ */
void send_msg(char *outuser, char *string)
{
  int ret_send, oldlevel, print = TRUE, chan;
  char msg[MAXLEN], on_line[MAXLEN], format[MAXLEN];
  win_ptr win, ind, ret, oldwin;

  win = isquery(gsrv, outuser);
  if (win) oldwin = set_outwin(win);
  if (is_chan(outuser)) {
    oldlevel = set_loglevel(PUBLIC);
    (void)new_send(gsrv, "PRIVMSG %s :%s\n", outuser, string);
    if (get_chan_num(get_server_chanlist(gsrv), outuser, &ind) && ind) {
      (void)sprintf(on_line, "%s %s", outuser, string);
      if (!win) oldwin = set_outwin(ind);
      if (handle_on(o_send_public, on_line, 0)) print = FALSE;
      strcpy(format, "%0.0s> %s");
    } else  {
      if (!win) {
        win = type_winget(gsrv, PUBLIC);
        oldwin = set_outwin(win);
      }
      strcpy(format, ">%s> %s");
    }
  } else if (IS_DCC_CHAT(outuser)) {
    if (!win) {
      win = type_winget(gsrv, DCC);
      oldwin = set_outwin(win);
    }
    oldlevel = set_loglevel(DCC);
    if (dcc_send_chat_msg(&outuser[1], string) != -1)
    {
      (void)sprintf(on_line, "%s %s", outuser, string);
      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.");
      print = FALSE;
    }
  } else {
    myfree(g_sent); myfree(g_body);
    g_sent = m_strcpy(outuser);
    g_body = m_strcpy(string);
    if (!win) {
      win = type_winget(gsrv, MSGS);
      oldwin = set_outwin(win);
    }
    oldlevel = set_loglevel(MSGS);
    strcpy(format, "-> *%s* %s");
    sprintf(on_line, "%s %s", outuser, string);
    if (handle_on(o_send_msg, on_line, 0)) print = FALSE;
    else new_send(gsrv, "PRIVMSG %s :%s\n", outuser, string);
  }
  if (print) say(format, outuser, string);
  set_loglevel(oldlevel);
  set_outwin(oldwin);
}
/* ------------------------------------------------------------------ */
static void user_flush(char *string, int varpar, char *subparams)
{
  old_display = getivar(VAR_DISPLAY);
  flushing = TRUE;
  myfree(flush_nick);
  flush_nick = m_strcpy(get_server_nick(gsrv));
  (void)new_send(gsrv, "USERHOST %s\n", flush_nick);
  setivar(VAR_DISPLAY, 0);
  kill_hold();
}
/* ------------------------------------------------------------------ */
stackptr userhost_queue = NULL;
/* ------------------------------------------------------------------ */
static void user_userhost(char *string, int varpar, char *subparams)
{
  int loop;
  char nick[MAXLEN], cmd[MAXLEN];
  if (!userhost_queue) userhost_queue = make_new_stack(FALSE, 0);
  (void)grab_word(&string, ' ', nick);
  (void)grab_word(&string, ' ', cmd);
  if (strcasecmp(cmd, "-cmd")==0)
    push_element(userhost_queue, nick, string, 0, TRUE);
  else
    push_element(userhost_queue, nick, NULL, 0, TRUE);
  new_send(gsrv, "USERHOST %s\n", nick);
}
/* ------------------------------------------------------------------ */
static void user_clear(char *string, int varpar, char *subparams)
{
  clear_window();
}
/* ------------------------------------------------------------------ */
static void user_msg(char *string, int varpar, char *subparams)
{
  char loca[MAXLEN];
 
  (void)grab_word(&string, ' ', loca);
  if (strcmp(loca, ",") == 0)
    if (g_from) strcpy(loca, g_from);
    else *loca = 0;
  else if (strcmp(loca, ".") == 0)
    if (g_sent) strcpy(loca, g_sent);
    else *loca = 0;
  else if (*loca == '%')
  {
    exec_msg(&loca[1], string);
    return;
  }
  send_msg(loca, string);
}
/* ------------------------------------------------------------------ */
static void user_query(char *string, int varpar, char *subparams)
{
  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);
}
/* ------------------------------------------------------------------ */
static void user_join(char *string, int varpar, char *subparams)
{
  int indx;
  char chan[MAXLEN], *on_chan;
  win_ptr win;
  (void)grab_word(&string, ' ', chan);
  if (*chan=='-')
  {
    if (strncasecmp("-INVITE", chan, (strlen(chan)>7)?7:strlen(chan))==0)
      if (g_invite) strcpy(chan, g_invite);
      else *chan = 0;
  }
  if (gsrv) indx = get_chan_num(get_server_chanlist(gsrv), chan, &win);
  else indx = 0;
  if (indx) set_channel(indx);
  else {
    new_send(gsrv, "JOIN %s %s\n", chan, string);
  }
/* These 2 lines are needed in case they join a channel that they are
   already on .. the server returns no notice */
  find_input_prompt(FALSE);
  updatestatwin();
}
/* ------------------------------------------------------------------ */
static void user_notice(char *string, int varpar, char *subparams)
{
  char target[MAXLEN];
  win_ptr win, oldwin;

  (void)grab_word(&string, ' ', target);
  (void)get_chan_num(get_server_chanlist(gsrv), 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);
}

/* ------------------------------------------------------------------ */
static void user_input(char *str, int varpar, char *subparams)
{
  char prompt[MAXLEN], input[MAXLEN];
  while (*str==' ') str++;
  grab_word(&str, (*str=='"')?'"':' ', prompt);
  while (*str==' ') str++;
  read_input(input, sizeof(input)-1, prompt);
  parse_line(str, input, TRUE, FALSE);
}

/* ------------------------------------------------------------------ */
static void user_save(char *str, int varpar, char *subparams)
{
  FILE *fp = NULL;
  say("This command is not implemented yet");
  if (!*str) {
    yell("*** A file must be specified for saving");
    return;
  }
  fp = fopen(str, "w");
  save_notify(fp);
  save_aliases(fp);
  save_assigns(fp);
  fclose(fp);
}

/* ------------------------------------------------------------------ */
static void user_oper(char *str, int varpar, char *subparams)
{
  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);
}

/* ------------------------------------------------------------------ */
static char basicarray[30][20] = {
  {"WHO "},
  {"PART "},
  {"WHOIS "},
  {"LIST "},                                       
  {"NOTICE "},
  {"MODE "},
  {"MOTD "},
  {""},     /* intentionally left blank for QUOTE */
  {"ADMIN "},
  {"LUSERS "},
  {"TIME "},    /* 10 */
  {"INFO "},
  {"OPER "},
  {"CONNECT "},
  {"TRACE "},
  {"DIE "},
  {"REHASH "},
  {"RESTART "},
  {"LINKS "},
  {"STATS "},
  {"SUMMON "},   /* 20 */
  {"WALLOPS "},
  {"WHOWAS "},
  {"NAMES "},
  {"NICK "},
};

static void usr_bsc(char *string, int comm, char *suboptions)
{
  char target[MAXLEN], *targ;
  if (comm<0) {
    comm = -comm;
    (void)grab_word(&string, ' ', target);
    targ = expand_target(target);
    (void)new_send(gsrv, "%s%s %s\n", basicarray[comm-1], targ?targ: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);
  }
}
 
/* ------------------------------------------------------------------ */
static void usr_bsc3(char *string, int comm, char *subparams)
{
  (void)new_send(gsrv, "%s :%s\n", basicarray[comm], string);
}
/* ------------------------------------------------------------------ */
void user_quit(char *string, int varpar, char *subparams)
{
  new_send(gsrv, "QUIT :%s\n", *string?string:"Leaving");
  quit = TRUE;
  stop_input();
  delete_windows();
  cleanup_input();
  exec_close_all();
  (void)sys$exit(1);
}
/* ------------------------------------------------------------------ */
void user_server(char *string, int varpar, char *subpararms)
{
  if (!*string)
    print_server_list();
  else  {
    int did = 0, num = 0;
    char *w;
    for (w= string; (*w >= '0') && (*w <= '9'); w++) {
      num = num * 10 + *w - '0';
      did = 1;
    }
    if (did && ((*w == ' ') || (*w == 0))) {
      connect_server_by_number(num);
      return;
    }
    /* Arguments need to be switched .. */
    /* Replace ' ' with ':' for partial compatibility with unix ircII */
    for (w = string; *w; w++) if (*w==' ') *w = ':';
    string = win_newserver(string, gsrv);
  }
}
/* ------------------------------------------------------------------ */
static void user_echo(char *string, int varpar, char *subparams)
{
  yell("%s", string?string:"(null)");
}
/* ------------------------------------------------------------------ */
static void admin_disabled(char *word, int param, char *subparams)
{
  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");
  else if (param==3)
    strcpy(comm, "SERVER");
  yell("*** The %s command has been disabled by your admin.", comm);
}
/* ------------------------------------------------------------------ */
static void user_version(char *str, int varpar, char *subparams)
{
  if (!*str)
  {
    say("Client version: \002%s\002", VERSION);
    say("Thanks to the following people:");
    say("masdough   masdough@ubvms.cc.buffalo.edu (Author)");
    say("toofache   iscnwl@mars.cc.nus.sg");
    say("bighorn    ARSEBESTYEN@CSUPomona.Edu");
    say("remainz    chedean@acs.eku.edu");
    say("quasar     stdrjn@niord.shsu.edu");
    say("melvin     MELVIN@ntutl1.ntu.ac.sg");
    say("tboard     uros     squiz     fordy");
    say("tychy      matt");
  }
  new_send(gsrv, "VERSION %s\n", str); 
}
/* ------------------------------------------------------------------ */
int waiting = FALSE;

static void user_wait(char *word, int varpar, char *subparams)
{
  int loop = 0, old = 7;
  waiting = TRUE;
  new_send(gsrv, "WAIT\n");
  if (!lib$ast_in_prog()) {
/* must process receive */
    while (waiting && old--) {
      process_receive(gsrv);
      if (waiting) {
        sleep(waiting); 
/*        yell("Breaking out of wait because we were waiting too long"); */
      }
    }
/*    yell("done waiting - %d seconds left", old); */
  }
  else process_wait(gsrv);
}

/* ------------------------------------------------------------------ */
static void user_redirect(char *str, int varpar, char *subparams)
{
  char *whom;
  whom = next_arg(&str);
  set_redirect(whom, gsrv);
  parse_line(str, NULL, FALSE, TRUE);
  new_send(gsrv, "REDIRECT\n"); 
}
/* ------------------------------------------------------------------ */
static void user_notify(char *str, int varpar, char *subparams)
{
  notify(gsrv, str);
}
/* ------------------------------------------------------------------ */
static void user_show(char *str, int varpar, char *subparams)
{
  char *word;
  win_mode tmp;
  win_mode_ptr walk;
  word = next_arg(&str);
  if (strcasecmp(word, "mode") == 0)
  {
    if (*str) tmp = lookup_win_mode(str);
    else tmp = win_getmode(gwin);
    if (!tmp)
    {
      say("*** Could not find window mode %s", str);
      return;
    }
    say("*** Window mode %s:", tmp->overall_name);
    for (walk=tmp->modes; walk; walk=walk->next)
      if (walk->this_mode)
        say("***   %s", walk->this_mode->major_mode_name);
    say("*** Window mode end.");
  }
  else
    say("*** Unknown show option: %s", str);
}
/* ------------------------------------------------------------------ */
static void user_xecho(char *str, int varpar, char *subparams)
{
  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);
  if (win != NW) oldwin = set_outwin(win);
  oldlevel = set_loglevel(level);
  yell("%s", str);
  set_loglevel(oldlevel);
  if (win != NW) set_outwin(oldwin);
}
/* ------------------------------------------------------------------ */
static void user_dump(char *str, int comm, char *subparams)
{
  switch(comm) {
    case 0: if (gsrv) raw_dump_channel(get_server_chanlist(gsrv));
      break;
    default:
      say("Unknown dump paramater");
      break;
  }
}
/* ------------------------------------------------------------------ */
void user_scroll(char *rest, int param, char *subparams)
{
  int lines;
  lines = atoi(rest);
  if (lines == 0) lines = 1;
  scroll_with_recall(lines);
/*  window_do_scroll(gwin, lines); */
}
/* ------------------------------------------------------------------ */
void user_write(char *rest, int param, char *subparams)
{
/* row and column to print at. flag to clear to end of line */
  char *row, *col, *ceol;
  row = next_arg(&rest);
  col = next_arg(&rest);
  ceol = next_arg(&rest);
  write_raw_to_screen(atoi(row), atoi(col), (atoi(ceol))?1:0, rest);
}
/* ------------------------------------------------------------------ */
void user_formode(char *rest, int param, char *subparams)
{
  char *first;
  major_mode tmp = mode_set, lookup;
  first = next_arg(&rest);
  if (!first || !*first)
  {
    mode_set = NULL;
    return;
  }
  if (!(lookup = lookup_mode(first)))
  {
    say("*** No such mode %s", first);
    return;
  }
  mode_set = lookup;
  if (rest && *rest)
  {
    parse_line(rest, NULL, FALSE, TRUE);
    mode_set = tmp;
  }
}
/* ------------------------------------------------------------------ */
void user_new(char *rest, int param, char *subparams)
{
  char *first;
  int ll;
  first = next_arg(&rest);
  if (!first || !*first) return;
  ll = strlen(first);
  if (strncasecmp(first, "mode", ll) == 0)
  {
    first = next_arg(&rest);
    if (first && *first) create_mode(first);
  }
  else if (strncasecmp(first, "binding", ll) == 0)
  {
    first = next_arg(&rest);
    if (first && *first) create_binding(first);
  }
  else
    say("*** Unknown new command: %s", first);
}
/* ------------------------------------------------------------------ */
void user_delmode(char *rest, int param, char *subparams)
{
  char *first, *second;
  first = next_arg(&rest);
  if (!first || !*first) {
    yell("Usage: delmode mode_name");
    return;
  }
  del_mode_to_mode(first);
}

/* ------------------------------------------------------------------ */
void user_addmode(char *rest, int param, char *subparams)
{
  int is_def;
  char *first, *second;
  first = next_arg(&rest);
  if (!first || !*first) {
    yell("Usage: addmode mode_name [default]");
    return;
  }
  is_def = (strcasecmp(rest, "default") == 0);
  add_mode_to_mode(first, is_def);
}

/* ------------------------------------------------------------------ */
void user_test1(char *rest, int param, char *subparams)
{
  char *blah[15] = { "a", "bvdefg", "ddddddddd", "eeeeeee", "fffffffffffffff",
    "6", "7", "8", "9", "10", "11", "12", "13", "14", "15" };
   yell("%d", get_menu_choice(15, blah));
}
/* ------------------------------------------------------------------ */
void user_copy(char *rest, int param, char *subparams)
{
  char type[512], first[512], second[512];
  grab_word(&rest, ' ', type);
  grab_word(&rest, ' ', first);
  grab_word(&rest, ' ', second);
  say("*** Copying %s is not implemented", type);
}

/* ------------------------------------------------------------------ */
typedef struct {
  char *command;
  void (*cmdfcn)(char *rest, int varpar, char *subparams);
  int varpar;
} NewCmdList;

static NewCmdList command_list[] =
{
  { "",          send_chan,      0 },
  { "#",         user_cmt,       0 },
  { ":",         user_cmt,       0 },
  { "@",         user_assign2,   0 },
  { "addmode",   user_addmode,   0 },
  { "alias",     user_alias,     0 },
  { "assign",    user_assign,    1 },
  { "bind",      dobind,         0 },
  { "channel",   user_join,      0 },
  { "clear",     user_clear,     0 },
  { "comment",   user_cmt,       0 },
  { "connect",   usr_bsc,       13 },
  { "d_channel", user_dump,      0 },
  { "dcc",       user_dcc,       0 },
  { "delmode",   user_delmode,   0 },
  { "echo",      user_echo,      0 },
  { "eval",      user_expand,    1 },
#ifdef ENABLE_EXEC
  { "exec",      user_exec,      0 },
#else
  { "exec",      admin_disabled, 1 },
#endif
  { "expand",    user_expand,    0 },
  { "flush",     user_flush,     0 },
  { "foreach",   user_foreach,   0 },
  { "formode",   user_formode,   0 },
  { "help",      help,           0 },
  { "if",        user_if,        0 },
  { "ignore",    user_ignore,    0 },
  { "input",     user_input,     0 },
  { "join",      user_join,      0 },
  { "lastlog",   user_lastlog,   0 },
  { "list",      user_names,     1 },
  { "load",      load_file,      0 },
  { "msg",       user_msg,       0 },
  { "names",     user_names,     0 },
  { "new",       user_new,       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 },
  { "query",     user_query,     0 },
  { "quit",      user_quit,      0 },
  { "quote",     usr_bsc,        7 },
  { "redirect",  user_redirect,  0 },
  { "rehash",    usr_bsc,       16 },
  { "restart",   usr_bsc,       17 },
  { "save",      user_save,      0 },
  { "say",       send_chan,      0 },
  { "scroll",    user_scroll,    0 },
  { "server",    user_server,    0 },
  { "set",       user_set,       0 },
  { "show",      user_show,      0 },
#ifdef ENABLE_SPAWN
  { "spawn",     user_spawn,     0 },
#else
  { "spawn",     admin_disabled, 2 },
#endif
  { "test1",     user_test1,     0 },
  { "type",      bind_typetext,  0 },
  { "userhost",  user_userhost,  0 },
  { "version",   user_version,   0 },
  { "wait",      user_wait,      0 },
  { "wallop",    usr_bsc3,      21 },
  { "while",     user_while,     0 },
  { "who",       user_names,     2 },
  { "window",    user_window,    0 },
  { "write",     user_write,     0 },
  { "xecho",     user_xecho,     0 },
};
/* ------------------------------------------------------------------ */
#define NUMBER_OF_COMMANDS (sizeof(command_list) / sizeof(CmdList)) - 1
/* ------------------------------------------------------------------ */
extern int load_depth;
static int p_d = 0;

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

  p_d++;
  if (getivar(VAR_DEBUG)&2048) yell("[%d] Parse_line enter: [%s]", p_d, str);
  walk = str;
  if (args) {
    do {
      nwalk = expand_alias(walk, args, &added, line, sizeof(line));
      if (getivar(VAR_DEBUG) & 4096)
        yell("[%d] Parse_line [%s] -to- [%s][%s] (%s)", p_d, walk,
             line, nwalk?nwalk:"null", args?args:"null");
      walk = nwalk;
      if (getivar(VAR_DEBUG) & 4096)
        yell("walk: %sempty line %sempty append_flag %d added %d args %sempty",
             (walk&&!*walk)?"non":"", *line?"":"non", append_flag,
             added, *args?"non":"");
      if ((!walk || !*walk) && *line && append_flag && !added && *args)
      {
        strcat(line, " ");
        strcat(line, args);
      }
      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);
      }
  if (getivar(VAR_DEBUG) & 2048) yell("[%d] parse line ended!", p_d);
  p_d--;
}

/* ------------------------------------------------------------------
 * expand_alias:
 */

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

static int e_l = 0;
char *expand_alias(char *string, char *args, int *added, char *ret, int len)
{
  char line[MAXLEN*10], quote_str[MAXLEN], other, *saved, *new, *ptr;
  char *free_it, *walk;
  int done = 0, quote_len = 0, ch, old;

  old = sys$setast(0);
  walk = line;
  *walk = 0;
  ptr = free_it = m_strcpy(string);
  if (*ptr == '@') *added = TRUE;
  while (ptr && *ptr && !done) {
    switch (*ptr)
    {
    case '$':
      if (*free_it == '@')
      {
        *walk++ = *ptr++;
        break;
      }
      *ptr++ = 0;
      while (*ptr == '^') {
        ptr++;
        quote_str[quote_len++] = *ptr++;
      }
      if (getivar(VAR_DEBUG) & 4096) {
        *walk = 0;
        yell("ret: [%s] before: [%s] args: [%s]", line, ptr?ptr:"null",
             args?args:"{NULL}");
        yell("Length left: [%d]", sizeof(line) - (int)walk + (int)line);
      }
      ptr = alias_special(ptr, args, added, walk,
                          sizeof(line) - (int)walk + (int)line);
      walk += strlen(walk);
      if (getivar(VAR_DEBUG)&4096)
        yell("ret: [%s] after: [%s]", line, ptr?ptr:"null");
      break;
    case ';':
      done = TRUE;
      ptr++;
      break;
    case PAREN_L:
    case BRACE_L:
      *added = TRUE;
      *walk++ = ch = *ptr;
      other = (*ptr==BRACE_L)?BRACE_R:PAREN_R;
      if (!(new = strip_ends(ptr, *ptr, other, walk,
                             sizeof(line) - (int)walk + (int)line)))
      {
        ptr++;
        if (getivar(VAR_DEBUG)&1024) yell("*** Unmatched %c", ch);
      } else {
        walk += strlen(walk);
        *walk++ = other;
        ptr = new;
      }
      break;
    case '\\':
      ptr++;
      if (*ptr) *walk++ = *ptr++;
      break;
    default:
      *walk++ = *ptr++;
      break;
    }
  }
  myfree(free_it);
  *walk = 0;
  if (getivar(VAR_DEBUG) & DEBUG$EXPAND)
    yell("[%d] Expanded [%s] to [%s]", p_d, string?string:"null", line);
  strcpy(ret, line);
  if (old==SS$_WASSET) sys$setast(1);
  if (!ptr) return NULL;
  return (char *)((int)string + (int) ptr - (int)free_it);
}

static int exe_level = 0;
/* ======================================================================= */
void handle_command_line(char *string, int interactive, char *param)
{
  NewCmdList *command;
  char word[MAXLEN], *outuser, *tmp;
  int cmd_cnt, expand = 1, olddisplay, did = FALSE;
  major_mode backup_mode;
  win_mode wmp;

  if (getivar(VAR_DEBUG) & DEBUG$EXECUTE)
    yell("Execute: [%d] \026%s\026", exe_level, string);
  olddisplay = getivar(VAR_DISPLAY);
  if (!string || (!*string)) return;
  if ((*string == '/') || !interactive || getbvar(VAR_COMMAND_MODE))
  {
    if (*string == '/') string++;
    if (*string == '/') {string++;expand=0;}  /* Two backslashes in a row */
    if (*string == '^') {
      string++;
      setivar(VAR_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);
    exe_level++;
    if (expand)
      did = exec_alias(word, string, param, TRUE);
    if (!did)
    {
      wmp = win_getmode(gwin);
      if (wmp)
      {
        win_mode_ptr tmp;
        backup_mode = mode_set;
        tmp = wmp->modes;
        while (!did && tmp && expand)
        {
          mode_set = tmp->this_mode;
          did = exec_alias(word, string, param, TRUE);
          tmp = tmp->next;
        }
        if (!did && wmp->default_mode)
        {
          mode_set = wmp->default_mode;
          did = exec_alias(word, string, param, FALSE);
        }
        mode_set = backup_mode;
      }
    }
    exe_level--;
    if (did)
    {
      if (getivar(VAR_DISPLAY)==-1) setivar(VAR_DISPLAY, olddisplay);
      if (getivar(VAR_DEBUG) & DEBUG$EXECUTE)
        yell("Execute: [%d] DONE", exe_level);
      return;
    }
    command = (NewCmdList *)find_command((CmdList *)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) {
      command->cmdfcn(string, command->varpar, param);
    } else
      say("*** Command %s is ambiguous.", word);
    if (getivar(VAR_DISPLAY)==-1) setivar(VAR_DISPLAY, olddisplay);
    if (getivar(VAR_DEBUG) & DEBUG$EXECUTE)
      yell("Execute: [%d] DONE", exe_level);
    return;
  }
  if ((outuser = getquery())) send_msg(outuser, string);
  else send_chan(string, 0, NULL);
  if (getivar(VAR_DISPLAY)==-1) setivar(VAR_DISPLAY, olddisplay);
}
