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

extern char userhost[MAXLEN];
extern int debug, sys$setast(), display;
extern server_ptr gsrv;
int did_expand = 0;
int lookup_routine();

/* --------------------- start string routines -------------------------- */

int ff_strncpy(char *s1, char *s2, int len, int maxlen)
{
  maxlen--;
  if (len>maxlen) len = maxlen;
  (void)strncpy(s1, s2, len);
  s1[len] = 0;
  return len;
}

char *m_strcpy(char *start, int len)
{
  char *xx;
  xx = malloc(len+1);
  if (!xx) return NULL;
  (void)strncpy(xx, start, len);
  xx[len] = 0;
  return xx;
}

char *ms_strcpy(char *str)
{
  char *xx;
  xx = malloc(strlen(str)+1);
  if (!xx) return NULL;
  (void)strcpy(xx, str);
  return xx;
}

char *md_strcpy(char *start, char *end)
{
  char *new = NULL, *walk = NULL;
  int len;
  len = (int)end - (int)start + 1;
  if (len<=0) return NULL;
  walk = new = (char *)malloc(len);
  if (!walk) return NULL;
  while (start<end) {
    if (*start=='\\') start++;
    if (start!=end) *walk++ = *start++;
  }
  *walk = 0;
  return new;
}

#define FIRST   1
#define SECOND  2

char *m_strcat(char *s1, char *s2, int extra, int deallocate)
{
  char *new;
  int len;
  len = s1?strlen(s1):0 + s2?strlen(s2):0;
  new = (char *)malloc(len + 1 + extra);
  if (!new) return new;
  if (extra) 
    (void)sprintf(new, "%s %s\000", s1?s1:"", s2?s2:"");
  else (void)sprintf(new, "%s%s\000", s1?s1:"", s2?s2:"");
  if ((deallocate&FIRST)&&s1) free(s1);
  if ((deallocate&SECOND)&&s2) free(s2);
  return new;
}

/* ---------------------- end string routines -------------------------- */

/*
 * given a pair of characters - such as [ and ] or ( and ), match them
 * up with one another, and return the closing characters posisition in
 * the string.
 */

char *strip_ends(char *str, char x, char y, char *ret, int len)
{
  int count = 1;
  len--;
  if (*str != x) {
    *ret = 0;
    return NULL;
  } else str++;
  while (*str && count && len) {
    if (*str == '\\') *ret++ = *str++;
    else if (*str == x) count++;
    else if (*str == y) count--;
    if (*str && count) *ret++ = *str++;
    else if (*str) str++;
    len--;
  }
  *ret = 0;
  if (!count) return str;
  return NULL;
}

char *matching_end(str, x, y)
  char *str;
  int x, y;
{
  int count = 1;
  while (*str && count) {
    if (*str == '\\') str++;
    else if (*str == x) count++;
    else if (*str == y) count--;
    if (*str) str++;
  }
  if (count==0) return --str;
  return NULL;
}

/*
 * given a string, return the starting location of the 'num'th word of
 * the string. if the word doesn't exist, a pointer to the end of the
 * string is returned
 */

char *find_word_num(char *str, int num)
{
  if (!str) return NULL;
  while (*str == ' ') str++;
  while (*str && (num > 0))
    if (*str++ == ' ')
      if (*str != ' ') num--;
  return str;
}

extern assign_ptr lookup_assign();
extern int check_builtins();

int word_count(char *str)
{
  int num = 0;
  while (*str)
    if (!isspace(*str++) && isspace(*str)) num++;
  return num;
}

void grab_variable(char **str, char *ret, int maxlen)
{
  char *walk;
  int count = 0;
  maxlen--;
  walk = *str;
  while ( (count<maxlen) &&
          (isalpha(*walk) || (*walk=='_') ||
          ((*walk=='.') && isalpha(*(walk+1)) ))) {
    *ret++ = *walk++;
    count++;
  }
  *str = walk;
  *ret = 0;
}

/*
 * given a string (str) check to see if it matches a builtin alias,
 * or if the value of str is an assigned string. If it is either,
 * copy the equivalent string into new
 */

int lookup_alias(char *str, char *args, char *new, int size)
{
  assign_ptr ll;
  char dummy[MAXLEN], *temp, orig = 0;
  int len = 0, added;

  *new = 0;
  size--;
  if (debug&DEBUG$ROUTINE) yell("Expanding %s in lookup_alias", str);
  if ((*str=='#') || (*str=='@')) orig = *str++;
  if ((str[1] == 0) && (check_builtins(str[0], dummy))) {
    len = strlen(dummy);
    if (len>size) len = size;
    (void)strncpy(new, dummy, len);
  } else if (ll = lookup_assign(str)) {
    strcpy(new, ll->value);
    if (debug&DEBUG$EXPAND) yell("Expanded [%s] to [%s]", ll->value, new);
    len = strlen(new);
  } else if (get_variable(str, NULL, dummy)) {
    len = strlen(dummy);
    if (len>size) len = size;
    (void)strncpy(new, dummy, len);
  } else len = 0;
  if (orig) {
    if (orig=='#') len = word_count(new);
    len = sprintf(new, "%d\000", len);
  }
  new[len] = 0;
  if (debug&16) yell("[%s] expanded to [%s] +lookup_alias+", str, new);
  return len;
}

/*
 * we use max_param when a user requests a list of words such as $4-
 * since we don't know how many words are in the string, we pick an
 * arbitrarily long string
 */

#define MAX_PARAM 80

/*
 * given a starting and ending word number, copy the correct words from
 * str into 'rest'
 */

char *add_numbs(b, e, rest)
  char *rest;
  int b, e;
{
  char *p, *start, *s1;
  int c = 0, on = 1;
  did_expand = 1;
  s1 = start = find_word_num(rest, b);
  while (start && *start && (b<=e)) {
    if (*start++ != ' ')
      if (*start == ' ') b++;
  }
  return md_strcpy(s1, start);
}

int isnum(char *str, int *num)
{
  *num = 0;
  if (!str) return FALSE;
  while (*str) {
    if (!isdigit(*str)) return FALSE;
    *num = *num * 10 + *str - 48;
    str++;
  }
  return TRUE;
}

char *expand_braces(char *str, char *rest, char *ret, int len)
{
  char *end, *d1, gl1[MAXLEN];
  int added = 1;

  while (*str == '[') {
    if (!(end = strip_ends(str, '[', ']', gl1, sizeof(gl1)) )) {
      if (debug&128) yell("expand_braces no ]");
      return NULL;
    }
    (void)expand_alias(gl1, rest, &added, ret, len);
    len -= strlen(ret);
    ret += strlen(ret);
    if ((*end=='[') && (len>1)) {
      sprintf(ret, ".");
      ret++;
      len--;
    }
    str = end;
  } /* while loop */
  *ret = 0;
  return str;
}

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

typedef struct hack_node_type hack_node;
struct hack_node_type {
  char oper[5], *param;
  int inuse;
};

void print_what_list(list, num)
  hack_node *list;
  int num;
{
  int loop;
  yell("What list");
  for(loop=0;loop<=num;loop++)
    if (list[loop].inuse)
      yell("OPER [%s] Param [%s]", list[loop].oper,
                list[loop].param?list[loop].param:"(null)");
}

char *operators = "#+-*/&!<>()=[]|&";

extern void lookup_assign_value(char *str, char *ret, int len);

/* --------------------------------------------------------------------- */
char *process_list(hack_node *ll, int curr)
{
  int level, loop, prev, olddisp, num1, num2, res;
  char *d1, gl1[MAXLEN*10];

/* now it is time to rip apart this stupid structure */
/* level 0 means parse *, /                  */
/* level 1 means parse +, -, ##              */
/* level 2 means parse ==, >, >=, <, <= !=   */
/* level 3 means parse =                     */
/* level 4 means parse &&, ||, ^^            */

  for (level=0;level<5;level++) {
    for (loop=1; loop<=curr; loop++) {  /* start at 1, 0 has no operator */
      while ((loop<=curr) && (!ll[loop].inuse)) loop++;
      if (loop<=curr) {
        prev = loop-1;
        while (!ll[prev].inuse) prev--;
        switch (ll[loop].oper[0]) {
        case '|':
        case '&':
          if ((ll[loop].oper[0] != ll[loop].oper[1]) || (level != 4)) break;
          num1 = (ll[prev].param && *ll[prev].param && (*ll[prev].param!='0'));
          num2 = (ll[loop].param && *ll[loop].param && (*ll[loop].param!='0'));
          if (((ll[loop].oper[0]=='|') && (num1 || num2)) ||
              ((ll[loop].oper[0]=='&') && (num1 && num2)))
            sprintf(ll[prev].param, "1");
          else
            sprintf(ll[prev].param, "0");
          ll[loop].inuse = FALSE;
          free(ll[loop].param);
          break;
        case '#': 
          if ((ll[loop].oper[1]=='#') && (level==1)) {
            ll[loop].inuse = FALSE;
            d1 = m_strcat(ll[prev].param, ll[loop].param, FALSE, 3);
            ll[prev].param = d1;
          }
          break;
        case '!':
        case '=':
          if (! ll[loop].oper[1] && (level==3)) {
            ll[loop].inuse = FALSE;
            sprintf(gl1, "%s %s", ll[prev].param, ll[loop].param);
            olddisp = display;
            display = -1;
            say("assign [%s]", gl1);
            user_assign(gl1, FALSE);
            if (display==-1) display = olddisp;
            free(ll[prev].param);
            ll[prev].param = ll[loop].param;
          } else if ((ll[loop].oper[1] == '=') && (level==2)) {
            ll[loop].inuse = FALSE;
            d1 = malloc(2);
            d1[1] = 0;
            if (!isnum(ll[prev].param, &num1) ||
                !isnum(ll[loop].param, &num2)) {
              if (strcasecmp(ll[prev].param, ll[loop].param) == 0) *d1 = '1';
              else *d1 = '0';
            } else {
              if (num1==num2) *d1 = '1';
              else *d1 = '0';
            }
            if (ll[loop].oper[0] == '!')
              if (*d1 == '1') *d1 = '0';
              else *d1 = '1';
            free(ll[loop].param);
            free(ll[prev].param);
            ll[prev].param = d1;
          }
          break;
        case '*':
        case '/':
          if ((level != 0) || (ll[loop].oper[1]) != 0) break;
          ll[loop].inuse = FALSE;
          if (ll[loop].oper[0] == '*')
            (void)sprintf(gl1, "%d\0", atoi(ll[prev].param) * atoi(ll[loop].param));
          else
            (void)sprintf(gl1, "%d\0", atoi(ll[prev].param) / atoi(ll[loop].param));
          free(ll[loop].param);
          free(ll[prev].param);
          ll[prev].param = ms_strcpy(gl1);
          break;         
        case '-':
        case '+':
          if ((level != 1) || (ll[loop].oper[1]) != 0) break;
          ll[loop].inuse = FALSE;
          if (ll[loop].oper[0] == '+')
            (void)sprintf(gl1, "%d\0", atoi(ll[prev].param) + atoi(ll[loop].param));
          else
            (void)sprintf(gl1, "%d\0", atoi(ll[prev].param) - atoi(ll[loop].param));
          free(ll[loop].param);
          free(ll[prev].param);
          ll[prev].param = ms_strcpy(gl1);
          break;
        case '>':
        case '<':
          if (level != 2) break;
          ll[loop].inuse = FALSE;
          if (!isnum(ll[prev].param, &num1) || !isnum(ll[loop].param, &num2))
            res = strcasecmp(ll[prev].param, ll[loop].param);
          else {
            if (num1==num2) res = 0;
            else if (num1<num2) res = -1;
            else res = 1;
          }
          d1 = malloc(2);
          d1[1] = 0;
          if (((ll[loop].oper[1] == '=') && (res == 0)) ||
              ((ll[loop].oper[0] == '>') && (res > 0)) ||
              ((ll[loop].oper[0] == '<') && (res < 0)))
            *d1 = '1';
          else *d1 = '0';
          free(ll[loop].param);
          free(ll[prev].param);
          ll[prev].param = d1;
        }
      }
    }
  }
  if (debug&DEBUG$WHATAWHO)  /**/
    yell("Whatawho return[%s]", ll[0].param?ll[0].param:"NULL");
  return ll[0].param;
}

/* --------------------------------------------------------------------- */
int get_number(char **str)
{
  int neg = FALSE, num = 0;
  char *walk = *str;
  if (*walk=='-') {
    walk++;
    neg = TRUE;
  }
  while (isdigit(*walk)) num = num * 10 + *walk++ - 48;
  *str = walk;
  return (neg)?-num:num;
}

/* --------------------------------------------------------------------- */
char *whatawho(char *string, char *args, int *added)
{
  hack_node ll[100];
  int curr = 0, num;
  char *start, *end, gl1[MAXLEN*10], *ret, old;
  ll[curr].oper[0] = 0;
  ll[curr].inuse = FALSE;
  start = string;
  if (debug&DEBUG$WHATAWHO)
    yell("TOP of whatawho [%s][%s]", string, args?args:"null");
  while (start && *start) {
    ll[curr].inuse = TRUE;
    while (isspace(*start)) start++;
    if (*start=='(') {
      end = matching_end(++start, '(', ')');
      if (!end) return NULL;
      old = *end;
      *end = 0;
      ll[curr].param = whatawho(start, args, added);
      *end = old;
      start = ++end;
    } else if (isdigit(*start) || (*start=='-')) {
      num = get_number(&start);
      sprintf(gl1, "%d", num);
      ll[curr].param = ms_strcpy(gl1);
    } else {
      start = alias_special(start, args, added, gl1, sizeof(gl1), TRUE);
      if (!start) return NULL;
      ll[curr].param = ms_strcpy(gl1);
    }
    while (isspace(*start)) start++;
    if (*start) {
      end = start;
      ll[++curr].inuse = FALSE;
      if (strchr(operators, *end)) { /* at most 2 character per operator */
        ll[curr].oper[0] = *end++;
        if (strchr(operators, *end)) ll[curr].oper[1] = *end++;
        else ll[curr].oper[1] = 0;
      } else ll[curr].oper[0] = 0;
      start = end;
    }
  }
  return process_list(ll, curr);
}

/* --------------------------------------------------------------------- */
char *old_whatawho(char *string, char *rest, int *added)
{
  hack_node ll[100];
  char *start, *end, *end2, *d1, gl1[MAXLEN], gl2[MAXLEN], gl3[MAXLEN];
  int level, num1, num2, res, loop, prev, curr = 0, olddisp;

  if (debug&DEBUG$WHATAWHO) yell("TOP of whatawho [%s][%s]", string, rest);
  ll[curr].oper[0] = 0;
  ll[curr].inuse = FALSE;
  start = string;
  while (isspace(*start)) start++; /* no leading spaces */
  end = start;

/* while there is something left in the string... keep going! */

  while (*start) {
    ll[curr].inuse = TRUE;
    ll[curr].param = NULL;
    while (isspace(*start)) start++; /* no leading spaces */
    if ((*end == '#') || (*end=='@')) end++;
    while (!strchr(operators, *end) && *end && !isspace(*end)) end++;

/* was the first non alpha-numeric character a ( ?? if so, find the 
 * matching ) and parse the text inbetween. If there were some
 * alpha-numeric characters before the first (, assume that it is
 * a function call
 */

    if (*end == '(') {
      (void)ff_strncpy(gl1, start, end-start, sizeof(gl1));
      if (!(end2 = strip_ends(end, '(', ')', gl2, sizeof(gl2)) )) {
        if (debug&DEBUG$WHATAWHO) yell("Whatawho return NULL (");
        return NULL;
      }
      if (start!=end) {
        (void)lookup_routine(gl1, gl2, gl3, sizeof(gl3), rest, added);
        ll[curr].param = ms_strcpy(gl3);
      } else
       ll[curr].param = whatawho(gl2, rest, added);
      end = end2;
    }

/* if the first non alphanumeric character was a [, then we are dealing 
 * with a string (unless there were characters before the first [
 */

    else if (*end == '[') {
      if (start != end) {         /* the stuff before the [ */
        (void)ff_strncpy(gl1, start, end-start, sizeof(gl1));
        (void)expand_alias(gl1, rest, added, gl3, sizeof(gl3));
      } else gl3[0] = 0;
      num1 = strlen(gl3);
      end2 = end;
      end = expand_braces(end2, rest, &gl3[num1], sizeof(gl3) - num1);
      if (!end) return;
      if (start == end2)
        ll[curr].param = ms_strcpy(gl3);
      else {
        lookup_assign_value(gl3, gl1, sizeof(gl1));
        ll[curr].param = ms_strcpy(gl1);
      }
    } else {

/* there was no ( or [, so we assume that an operator terminated the
 * sequence. If the valid alpha-numeric is not a number, then we have
 * to lookup the string as if it were an assign, or a builtin
 */
      int neg = FALSE;
      if (end==start) {
        if (*start=='-') {
          start = ++end;
          while (isdigit(*end)) end++;
          neg = TRUE;
        } else {
          if (debug&DEBUG$WHATAWHO) yell("Whatawho return NULL NOT");
          return NULL;
        }
      }
      (void)ff_strncpy(gl1, start, end-start, sizeof(gl1));
      if (!isnum(gl1, &num1)) (void)lookup_alias(gl1, rest, gl2, sizeof(gl2));
      else (void)sprintf(gl2, "%d\0", neg?-num1:num1);
      ll[curr].param = ms_strcpy(gl2);
    }
    while (isspace(*end)) end++;
    start = end;

/* if we are not at the end of the string, we assume that an operator
 * is going to be following */

    if (*start) {
      curr++;
      ll[curr].inuse = FALSE;
      if (strchr(operators, *end)) { /* at most 2 character per operator */
        end++;
        if (strchr(operators, *end)) end++;
      }
      (void)ff_strncpy(ll[curr].oper, start, end-start, sizeof(ll[curr].oper));
      while (isspace(*end)) end++;
      start = end;
    }
  }
  if (debug&DEBUG$WHATAWHO)  /**/
    print_what_list(ll, curr);
  return process_list(ll, curr);
}

/* --------------------------------------------------------------------- */
/* This is where we handle all of the builtin functions.                 */
/* --------------------------------------------------------------------- */

struct command_node2 {
  char *name;
  void (*routine)();
};

void built_index(char *str, char *ret)
{
  char word[MAXLEN];
  int ind, l0, l1, good = -1;
  (void)grab_word(&str, ' ', word);
  for (l0=0; (l0<strlen(str)) && (good == -1) ;l0++)
    for (l1=0;l1<strlen(word);l1++)
      if (tolower(word[l1]) == tolower(str[l0])) good = l0;
  (void)sprintf(ret, "%d\0", good);
}

void built_rindex(char *str, char *ret)
{
  int loop1, loop2, good = -1;
  char word[MAXLEN];
  (void)grab_word(&str, ' ', word);
  for (loop1 = strlen(str)-1; (loop1>=0) && (good==-1); loop1--)
     for (loop2 = 0; loop2<strlen(word); loop2++)
       if (tolower(word[loop2]) == tolower(str[loop1])) good = loop1;
  (void)sprintf(ret, "%d\0", good);
}

void built_ischannel(char *str, char *ret)
{
  if ((*str == '&') || (*str == '#')) (void)strcpy(ret, "1");
  else (void)strcpy(ret, "0");
}

void built_ischanop(char *str, char *ret)
{
  (void)strcpy(ret, "0");
}

void built_userhost(char *str, char *ret)
{
  (void)strcpy(ret, userhost);
}

void built_left(char *str, char *ret)
{
  char word[80];
  (void)grab_word(&str, ' ', word);
  (void)strncpy(ret, str, atoi(word));
  ret[atoi(word)] = 0;
}

void built_right(char *str, char *ret)
{
  char word[80];
  int len;
  (void)grab_word(&str, ' ', word);
  len = strlen(str);
  if ((len-atoi(word))<0) len = atoi(word);
  (void)strcpy(ret, &str[len-atoi(word)]);
}

void built_rand(char *str, char *ret)
{
  int xx;
  xx = atoi(str);
  (void)sprintf(ret, "%d\0", rand()%xx);
}

void built_tolower(char *str, char *ret)
{
  while (*str) *ret++ = tolower(*str++);
  *ret = 0;
}

void built_toupper(char *str, char *ret)
{
  while (*str) *ret++ = toupper(*str++);
  *ret = 0;
}

void built_mid(char *str, char *ret)
{
  char w1[80], w2[80];
  int left, count, len;
  (void)grab_word(&str, ' ', w1);
  (void)grab_word(&str, ' ', w2);
  left = atoi(w1);
  count = atoi(w2);
  len = strlen(str);
  if ((left>=len) || (left<0) || (count<0)) *ret = 0;
  else {
    if ((left+count) > len) count = len-left;
    (void)strncpy(ret, &str[left], count);
    ret[count] = 0;
  }
}

void built_match(char *str, char *ret)
{
  char pattern[MAXLEN], word[MAXLEN];
  int xx = 1, good = 0;

  (void)grab_word(&str, ' ', pattern);
  do {
    (void)grab_word(&str, ' ', word);
    if (wild_match(pattern, word)!=-1) good = xx;
    xx++;
  } while ((good==0) && (*str));
  (void)sprintf(ret, "%d\0", good);
}

void built_time(char *str, char *ret)
{
  (void)sprintf(ret, "%d\0", time(0));
}

void built_stime(char *str, char *ret)
{
  unsigned time;
  time = atoi(str);
  (void)sprintf(ret, "%s\0", ctime( (unsigned long *) (&time)) );
  ret[strlen(ret)-1] = 0;
}

void built_strip(char *str, char *ret)
{
  char word[MAXLEN];
  (void)grab_word(&str, ' ', word);
  while (*str) {
    if (!strchr(word, *str)) *ret++ = *str;
    str++;
  }
  *ret = 0;
}

void built_rmatch(char *str, char *ret)
{
  char word1[MAXLEN], word2[MAXLEN];
  int count = 0, best_num, this, best = 0;
  best_num = -1;
  (void)grab_word(&str, ' ', word1);
  while (grab_word(&str, ' ', word2)) {
    count++;
    this = wild_match(word2, word1);
    if (this>best_num) {
      best_num = this;
      best = count;
    }
  }
  (void)sprintf(ret, "%d\0", best);
}


void built_encode(char *str, char *ret)
{
  while (*str) {
    *ret++ = (*str >> 4) + 'A';
    *ret++ = (*str & 0x0f) + 'A';
    str++;
  }
  *ret = 0;
}

void built_decode(char *str, char *ret)
{
  while (*str && *(str + 1)) {
    *ret++ = ((*str - 'A')  << 4) | (*(str+1) - 'A');
    str = (char *)((int)str + 2);
  }
  *ret = 0;
}

void built_word(char *str, char *ret)
{
  char word[MAXLEN], *start;
  (void)grab_word(&str, ' ', word);
  start = find_word_num(str, atoi(word));
  while (!isspace(*start) && *start)
    *ret++ = *start++;
  *ret = 0;
}

void built_tdiff(char *str, char *ret)
{
  int num, days, hours, minutes, seconds, time;
  time = atoi(str);
  days = time / (60 * 60 * 24);
  hours = (time / (60 * 60)) % 24;
  minutes = (time / 60) % 60;
  seconds = (time) % 60;
  *ret = 0;
  if (days>0) sprintf(ret, "%d days ", days);
  ret += strlen(ret);
  if (hours>0) sprintf(ret, "%d hours ", hours);
  ret += strlen(ret);
  if (minutes>0) sprintf(ret, "%d minutes ", minutes);
  ret += strlen(ret);
  sprintf(ret, "%d seconds", seconds);
}

int read_raw_connect(connect_node *node)
{
  node->buffer[node->iosb.length] = 0;
  say("+ %s", node->buffer);
  return 1;
}

int lame_routine(short chan)
{
  short nchan;
  SIN sin;
  nchan = qio_accept_after_wait(chan, chan, &sin, sizeof(sin));
  start_read(chan, nchan, read_raw_connect, NULL);
  return 1;
}

extern char nameserver[];

void built_write(char *str, char *ret)
{
  char word[MAXLEN];
  short num;
  grab_word(&str, ' ', word);
  num = tcp_send(atoi(word), str, strlen(str));
  sprintf(ret, "%d", num);
}

void built_writecrlf(char *str, char *ret)
{
  char word[MAXLEN], line[MAXLEN];
  short num;
  grab_word(&str, ' ', word);
  sprintf(line, "%s\n", str);
  num = tcp_send(atoi(word), line, strlen(line));
  sprintf(ret, "%d", num);
}

void built_connect(char *str, char *ret)
{
  char serv[MAXLEN], errmsg[80];
  short chan;
  SIN sin, *new_sin;
  strcpy(ret, "-1 -1");
  grab_word(&str, ' ', serv);
  memset(&sin, 0, sizeof(sin));
  if (!good_host(&sin.sin_address, serv))
    if (!nslookup(nameserver, serv, &sin.sin_address))
    {
      say("*** could not resolve %s\n", serv);
      return;
    }
  sin.sin_port = tcp_htons(atoi(str));
  sin.sin_family = AF_INET;
  chan = qio_socket_and_connect(&sin, sizeof(sin));
  if (chan!=-1)
  {
    sprintf(ret, "%d %d", chan, tcp_htons(sin.sin_port));
    new_sin = (SIN *)malloc(sizeof(sin));
    memcpy(new_sin, &sin, sizeof(sin));
    add_connect_to_list(chan, new_sin, NULL, CONNECT_RAW);
    start_read(chan, chan, read_raw_connect, NULL);
  } else {
    get_socket_error(errmsg);
    say("qio_socket_and_connect: %s", errmsg);
  }
}

void built_listen(char *str, char *ret)
{
  SIN *sin;
  short chan;
  sin = qio_bind_and_listen(&chan, lame_routine);
  if (sin) {
    add_connect_to_list(chan, sin, NULL, CONNECT_RAW);
    sprintf(ret, "%d %d", chan, tcp_htons(sin->sin_port));
  }
  else strcpy(ret, "-1 -1");
}

struct command_node2 in_functions[] =
{
  {{"CONNECT"},   built_connect},
  {{"DECODE"},    built_decode},
  {{"ENCODE"},    built_encode},
  {{"INDEX"},     built_index},
  {{"ISCHANNEL"}, built_ischannel},
  {{"ISCHANOP"},  built_ischanop},
  {{"LEFT"},      built_left},
  {{"LISTEN"},    built_listen},
  {{"MATCH"},     built_match},
  {{"MID"},       built_mid},
  {{"RAND"},      built_rand},
  {{"RIGHT"},     built_right},
  {{"RINDEX"},    built_rindex},
  {{"RMATCH"},    built_rmatch},
/*{{  "SRAND"},     built_srand}, */
  {{"STIME"},     built_stime},
  {{"STRIP"},     built_strip},
  {{"TDIFF"},     built_tdiff},
  {{"TIME"},      built_time},
  {{"TOLOWER"},   built_tolower},
  {{"TOUPPER"},   built_toupper},
  {{"USERHOST"},  built_userhost},
/*
  {{"WINNAME"},   built_winname},
  {{"WINNUM"},    built_winnum},
*/
  {{"WORD"},      built_word},
  {{"WRITE"},     built_write},
  {{"WRITECRLF"}, built_writecrlf},
  {{NULL},        NULL}
};

/* ---------------------------------------------------------------------- */
int lookup_routine(char *s1, char *s2, char *ret, int retlen, char *args,
                   int *added)
{
  int ddd, indx, len;
  char dummy[MAXLEN], *ack, temp[MAXLEN];

  retlen--;
  *ret = 0;
  if (!*s1) {
    yell("*** Null routine lookup");
    return 0;
  }
  ddd = lookup_command((command_ptr)in_functions, s1, &indx);
  if (ddd>=0) {
    (void)expand_alias(s2, args?args:"", added, temp, sizeof(temp));
    (in_functions[ddd].routine)(temp, dummy);
    len = strlen(dummy);
  } else {
    (void)expand_alias(s2, args?args:"", added, temp, sizeof(temp));
    if (debug&DEBUG$EXPAND)
      yell("[%s] expanded to [%s] ", s2, temp);
    if (exec_alias(s1, temp, args)) { 
/*
    if (exec_alias(s1, s2, args)) { 
*/
      lookup_assign_value("FUNCTION_RETURN", dummy, sizeof(dummy));
      len = strlen(dummy);
    } else {
      yell("*** Unknown command: %s", s1);
      len = 0;
    }
  }
  if (len>retlen) len = retlen;
  (void)strncpy(ret, dummy, len);
  ret[len] = 0;
  if (debug & DEBUG$ROUTINE) yell("Function %s(%s) returned %s", s1, s2, ret);
  return len;
}

/* ------------------------------------------------------------------------ */
int last_word(char *str, char *ret, int max)
{
  int num = 0;
  char *start;
  if (!str) return 0;
  start = str;
  while (*str) {
    if (isspace(*str) && isalpha(*(str+1))) start = str + 1;
    str++;
  }
  num = ff_strncpy(ret, str, strlen(str), max);
  return num;
}

/* -----------------------------------------------------------------------
 * real = the original string
 * args = the argumnts to the alias
 * ret = where we right our return string
 * added = did we used on of the special characters
 * ----------------------------------------------------------------------- */

int depth = 0;

char *alias_special(char *str, char *args, int *added, char *ret, int len,
                    int from_what)
{
  int lower, upper, width, set_width = FALSE, real;
  char *t1, *temp, oldc, saved[MAXLEN], saved2[MAXLEN], saved3[MAXLEN], *s;
  char olds;

  *ret = 0;
  if (!*str) return str;
  if (!from_what && (*str == '['))  /* A width specification */
  {
    set_width = TRUE;
    if (temp = strip_ends(str, '[', ']', saved, sizeof(saved)) ) {
      expand_alias(saved, args, added, saved2, sizeof(saved2));
      width = atoi(saved2);
      str = temp;
    } else {
      yell("Unbalanced [] for width");
      return (++str);
    }
  }
  if ((*str == '@') || (*str == '#')) oldc = *str++;
  else oldc = 0;
  switch (*str)
  {
  case '[':
    if (str = strip_ends(str, '[', ']', saved2, sizeof(saved2)) )
      expand_alias(saved2, args, added, saved, sizeof(saved));
    break;
  case '(':
    *added = TRUE;
    str = strip_ends(str, '(', ')', saved2, sizeof(saved2));
    expand_alias(saved2, args, added, saved3, sizeof(saved3));
    if (debug & DEBUG$EXPAND) yell("First $() %s to..%s..",
        saved2?saved2:"NULL", saved3?saved3:"NULL");
    alias_special(saved3, args, added, saved, sizeof(saved), FALSE);
    if (debug & DEBUG$EXPAND)
      yell("Expanded2 [%s] to [%s] with params [%s]", saved3,
           saved?saved:"null", args?args:"null");
    break;
  case '{':
    str = strip_ends(str, '{', '}', saved2, sizeof(saved2));
    s = whatawho(saved2, args, added);
    if (debug & DEBUG$EXPAND) yell("Expanded %s to %s",
        saved2?saved2:"NULL", s?s:"NULL");
    if (s) strcpy(saved, s);
    else *saved = 0;
    free(s);
    break;
  case '*':
    *added = TRUE;
    strcpy(saved, args);
    str++;
    break;
  case '$':
    strcpy(saved, "$");
    str++;
    break;
  default:
    if (isdigit(*str) || (*str == '-') || (*str == '~')) {
      *added = TRUE;
      if (*str == '~') {
        lower = upper = word_count(args);
        str++;
      } else {
        if (*str == '-') lower = 0;
        else lower = get_number(&str);
        if (*str=='-') {
          str++;
          if (isdigit(*str)) upper = get_number(&str);
          else upper = MAX_PARAM;
        } else upper = lower;
      }
      if (t1 = add_numbs(lower, upper, args)) {
        (void)ff_strncpy(saved, t1, strlen(t1), sizeof(saved));
        free(t1);
      } else *saved = 0;
    } else {
      olds = 0;
      t1 = str;
      *saved = *t1++;
      grab_variable(&t1, saved+1, sizeof(saved)-1);

      switch (*t1)
      {
      case '(':
        t1 = strip_ends(t1, '(', ')', saved2, sizeof(saved2));
        lookup_routine(saved, saved2, saved3, sizeof(saved3), args, added);
        break;
      case '{':
        t1 = strip_ends(t1, '{', '}', saved2, sizeof(saved2));
        yell("WTF do we do with: [%s] [%s]", saved?saved:"NULL",
             saved2?saved2:"NULL");
        break;
      case '[':
        strcat(saved, ".");
        t1 = expand_braces(t1, args, saved+strlen(saved),
                           sizeof(saved)-strlen(saved));
      default:
        lookup_alias(saved, args, saved3, sizeof(saved3));
      }
      strcpy(saved, saved3);
      str = t1;
    }
    break;
  }
  if (oldc) {
    if (oldc=='@')
      sprintf(saved, "%d", strlen(saved));
    else
      sprintf(saved, "%d", word_count(saved));
  }
  if (!set_width) {
    width = strlen(saved);
    len = (len>width)?width:len-1;
    strncpy(ret, saved, len);
    ret[len] = 0;
  } else {
    real = sprintf(ret, "%*.*s", -width, (width>0)?width:-width, saved);
    if (width<0) width = -width;
    while (real<width) ret[real++] = 32;
    ret[width] = 0;
  }
  return str;
}
