TinyMAZE/
TinyMAZE/config/
TinyMAZE/doc/
TinyMAZE/run/msgs/
TinyMAZE/src/
TinyMAZE/src/db/
TinyMAZE/src/ident/
TinyMAZE/src/io/
TinyMAZE/src/prog/
TinyMAZE/src/softcode/
TinyMAZE/src/util/
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "externs.h"
#include "function.h"

SCFUNC *sc_function_list = NULL;

extern int quiet_reboot;

static SCFUNC *find_sc_func(OBJ *player, char *fname)
{
  SCFUNC *f;

  for(f = sc_function_list;f;f = f->next)
  {
    if(f->flags & SCFUNC_GLOBAL || !player ||
      f->owner == player->owner)
    {
      if(!strcmp(f->name, fname))
        break;
    }
  }

  return(f);
}

static SCFUNC *new_sc_func()
{
  SCFUNC *f;

  f = (SCFUNC *)stack_alloc(sizeof(SCFUNC), 1, 0);

  f->next = sc_function_list;
  sc_function_list = f;

  return(f);
}

static unsigned long parse_sc_func_flags(char *flaglist)
{
  char *p, *q;
  unsigned long flags = 0;
  char buf[1024];

  strcpy(buf, flaglist);
  p = buf;

  while((q = strchr(p, ',')))
  {
    *q++ = '\0';

    if(!string_compare(p, "global"))
      flags |= SCFUNC_GLOBAL;
    else
      return(-1);

    p = q;
  }

  if(!string_compare(p, "global"))
    flags |= SCFUNC_GLOBAL;
  else
    return(-1);

  return(flags);
}

static int ok_func_name(char *name)
{
  char *p;

  for(p = name;*p;p++)
    if(isspace(*p) || !isalnum(*p))
      return(0);

  if(!*name)
    return(0);

  return(1);
}

static void list_funcs(OBJ *player, int all)
{
  SCFUNC *f;
  int ctr = 0;
  char flags[128];

  for(f = sc_function_list;f;f = f->next)
    if(all || f->flags & SCFUNC_GLOBAL || f->owner == player->owner)
    {
      if(f->owner == player->owner)
        strcpy(flags, "       ");
      else
        strcpy(flags, my_ljust(tprintf("#%d", f->owner->dbref), 7));
      if(f->flags & SCFUNC_GLOBAL)
        strcpy(flags, "GLOBAL ");

      if(!ctr)
        notify(player, " FLAGS         NAME         ARGS");
      notify(player, tprintf("%s %s %d",
        flags, my_ljust(f->name, 20), f->nargs));
      ctr++;
    }

  if(ctr)
    notify(player, "");
  notify(player,
    tprintf("|+B|Total functions available|+W|: |+C|%d", ctr));
}

void do_function(OBJ *player, char *arg1, char *arg2)
{
  SCFUNC *func, *pfunc;
  char buf[4096];
  char *p;
  int nargs;
  unsigned long flags;
  int is_global;

  if(!*arg1)
  {
    list_funcs(player, 0);
    return;
  }

  if(!*arg2)
  {
    if(!strcmp(arg1, "ALL") && power(player, POW_FUNCTIONS))
    {
      list_funcs(player, 1);
      return;
    }

    if(power(player, POW_FUNCTIONS))
      func = find_sc_func(NULL, arg1);
    else
      func = find_sc_func(player, arg1);

    if(!func)
    {
      notify(player, tprintf("Unknown function '%s'.", arg1));
      return;
    }

    is_global = func->flags & SCFUNC_GLOBAL;

    notify(player, tprintf("|+B|Function |+G|%s()|+W|%s|+R|%s",
      func->name, (is_global)?" (GLOBAL)":"",
      (func->owner != player->owner && !is_global)?" (UNAVAILABLE)":""));
    notify(player, tprintf("|+B|Owner|+W|: %s",
      unparse_object(player, func->owner)));
    notify(player, tprintf("|+B|Number of Arguments|+W|: |+C|%d",
      func->nargs));
    notify(player, tprintf("|+B|Value|+W|: |+C|%s", func->value));
    return;
  }

  if(!string_compare(arg1, "new"))
  {
    strcpy(buf, arg2);
    if(!(p = strchr(buf, ',')))
    {
      notify(player, "Type 'help +function' for help.");
      return;
    }
    *p++ = '\0';
    if((func = find_sc_func(NULL, buf)))
    {
      notify(player, tprintf("Function %s already exists.", buf));
      return;
    }

    nargs = atoi(p);

    if(nargs < 0 || nargs > 9 || !*p)
    {
      notify(player, "You must specify a valid number of arguments.");
      return;
    }

    if(!(p = strchr(p, ',')))
      flags = 0;
    else
    {
      if((flags = parse_sc_func_flags(p+1)) == -1)
      {
        notify(player, "Unknown flag in list.");
        return;
      }
    }

    if(!ok_func_name(buf))
    {
      notify(player, "Illegal function name.");
      return;
    }

    func = new_sc_func();
    func->name = stack_string_alloc(buf, 1);
    func->owner = player->owner;
    func->flags = flags;
    func->nargs = nargs;
    func->value = stack_string_alloc("", 1);

    notify(player, tprintf("Function '%s' defined.", buf));
    return;
  }

  if(!string_compare(arg1, "set"))
  {
    strcpy(buf, arg2);
    if(!(p = strchr(buf, ',')))
    {
      notify(player, "Type 'help +function' for help.");
      return;
    }
    *p++ = '\0';
    if(power(player, POW_FUNCTIONS))
      func = find_sc_func(NULL, buf);
    else
      func = find_sc_func(player, buf);
    if(!func)
    {
      notify(player, tprintf("Unknown function '%s'.", buf));
      return;
    }
    if(func->owner != player->owner && !power(player, POW_FUNCTIONS))
    {
      notify(player, perm_denied());
      return;
    }

    func->value = stack_string_realloc(func->value, p);

    notify(player, tprintf("Function '%s' set.", buf));
    return;
  }

  if(!string_compare(arg1, "del"))
  {
    if(!(func = find_sc_func(player, arg2)))
    {
      notify(player, tprintf("Unknown function '%s'.", arg2));
      return;
    }

    if(func->owner != player->owner && !power(player, POW_FUNCTIONS))
    {
      notify(player, perm_denied());
      return;
    }

    if(func == sc_function_list)
      sc_function_list = func->next;
    else
    {
      for(pfunc = sc_function_list;pfunc;pfunc = pfunc->next)
      {
        if(pfunc->next)
          if(pfunc->next == func)
            break;
      }

      pfunc->next = func->next;
    }

    stack_free(func->name);
    stack_free(func->value);
    stack_free(func);

    notify(player, tprintf("Function '%s' deleted.", buf));
    return;
  }

  if(!strcmp(arg1, "args"))
  {
    strcpy(buf, arg2);
    if(!(p = strchr(buf, ',')))
    {
      notify(player, "You must specify a valid number of arguments.");
      return;
    }
    *p++ = '\0';
    if(power(player, POW_FUNCTIONS))
      func = find_sc_func(NULL, buf);
    else
      func = find_sc_func(player, buf);
    if(!func)
    {
      notify(player, tprintf("Unknown function '%s'.", buf));
      return;
    }

    nargs = atoi(p);

    if(nargs < 0 || nargs > 9 || !*p)
    {
      notify(player, "You must specify a valid number of arguments.");
      return;
    }

    if(func->owner != player->owner && !power(player, POW_FUNCTIONS))
    {
      notify(player, perm_denied());
      return;
    }

    func->nargs = nargs;

    notify(player,
      tprintf("Number of args for function '%s' is now %d.",
      func->name, nargs));
    return;
  }

  if(!strcmp(arg1, "flags"))
  {
    strcpy(buf, arg2);
    if(!(p = strchr(buf, ',')))
      flags = 0;
    else
    {
      *p++ = '\0';
      if((flags = parse_sc_func_flags(p)) == -1)
      {
        notify(player, "Unknown flag in list.");
        return;
      }
    }
    if(power(player, POW_FUNCTIONS))
      func = find_sc_func(NULL, buf);
    else
      func = find_sc_func(player, buf);
    if(!func)
    {
      notify(player, tprintf("Unknown function '%s'.", buf));
      return;
    }
    if(func->owner != player->owner && !power(player, POW_FUNCTIONS))
    {
      notify(player, perm_denied());
      return;
    }

    func->flags = flags;

    notify(player, "Flags set.");
    return;
  }

  notify(player, "Type 'help +function' for help.");
  return;
}

void save_sc_funcs()
{
  const char *filename = "db/scfuncs.db";
  SCFUNC *f;
  FILE *fp;
  int num_funcs = 0;

  if(!(fp = fopen(filename, "w")))
  {
    log_error(tprintf("Couldn't open \"%s\" for writing!", filename));
    return;
  }

  for(f = sc_function_list;f;f = f->next)
    num_funcs++;

  fprintf(fp, "%d\n", num_funcs);

  for(f = sc_function_list;f;f = f->next)
  {
    fprintf(fp, "%s\n", f->name);
    fprintf(fp, "%d\n", f->owner->dbref);
    fprintf(fp, "%ld\n", f->flags);
    fprintf(fp, "%d\n", f->nargs);
    fprintf(fp, "%s\n", f->value);
  }

  fclose(fp);
}

void load_sc_funcs()
{
  FILE *fp;
  const char *filename = "db/scfuncs.db";
  int num_funcs;
  SCFUNC *f;
  char buf[4096];

  if(!(fp = fopen(filename, "r")))
  {
    log_error(tprintf("Couldn't open \"%s\" for reading!", filename));
    return;
  }

  fgets(buf, sizeof(buf), fp);
  num_funcs = atoi(buf);

  if(!quiet_reboot && num_funcs)
  {
    notify_all2(tprintf("Loading %d softcode function%s...",
      num_funcs, check_plural(num_funcs == 1, "", "s")), NULL);
    flush_all_output();
  }

  while(num_funcs--)
  {
    f = new_sc_func();
    fgets(buf, sizeof(buf), fp);
    *(buf+strlen(buf)-1) = '\0';
    f->name = stack_string_alloc(buf, 1);
    fgets(buf, sizeof(buf), fp);
    f->owner = find_object(atoi(buf));
    fgets(buf, sizeof(buf), fp);
    f->flags = atol(buf);
    fgets(buf, sizeof(buf), fp);
    f->nargs = atoi(buf);
    fgets(buf, sizeof(buf), fp);
    *(buf+strlen(buf)-1) = '\0';
    f->value = stack_string_alloc(buf, 1);
  }

  fclose(fp);
}

void free_sc_funcs()
{
  SCFUNC *f, *fnext;

  for(f = sc_function_list;f;f = fnext)
  {
    fnext = f->next;

    stack_free(f->name);
    stack_free(f->value);
    stack_free(f);
  }

  sc_function_list = NULL;
}

static void expand_user_func_args(char *str, char *retbuf)
{
  char *p, *s, *q;
  int ch;         /* 0 if variable is an integer, 1 if char or string */
  char *cval = 0;
  int ival = 0;
  char varname[4096];
  char *v;
  char buf[4096];

  strcpy(retbuf, str);

  for(p = retbuf;*p;p++)
  {
    ch = 0;

    if(*p == '"')
    {
      p++;
      while(*p && *p != '"')
        p++;
      if(!*p)
        break;
      continue;
    }

    if(*p != '~')
      continue;

    s = p++;

    for(v = varname;isalnum(*p);v++,p++)
      *v = *p;
    *v = '\0';

    if(*p == '$')
    {
      ch = 1;
      p++;
    }

    for(q = s;*q && q < p;p--)
      extract_char(q);

    if(ch)
      cval = cvar_val(varname);
    else
      ival = ivar_val(varname);

    strncpy(buf, retbuf, s-retbuf);
    *(buf+(s-retbuf)) = '\0';
    if(ch)
      strcat(buf, cval);
    else
      sprintf(buf+strlen(buf), "%d", ival);
    strcat(buf, p);

    strcpy(retbuf, buf);
    p = retbuf;

    if(*p == '"')
      p--;
  }
}

int chk_user_func(OBJ *player, char *buffer, char *start)
{
  char buf[4096], buf2[4096];
  char *p, *f, *a;
  char funcname[4096];
  SCFUNC *func;
  int deep;
  int num_args;
  SCVAR *var;

  strncpy(buf, buffer, start-buffer);
  *(buf+(start-buffer)) = '\0';

  for(p = start, f = funcname;*p && *p != '(';p++, f++)
    *f = *p;
  *f = '\0';

  if(!*p || !*funcname)
    return(0);

  if(!(func = find_sc_func(player, funcname)))
    return(0);

  num_args = 1;
  deep = 0;
  a = buf2;

/* Start at the first '(' and find the last one */
  while(*p)
  {
    switch(*p)
    {
      case ',':
        if(num_args < 9 && deep == 1)
        {
          if(!(var = find_var(tprintf("arg%d", num_args), VAR_STR)))
          {
            notify(code_ptr->executor,
              tprintf("|+bR|Fatal internal error in |+bY|%d|+bR|.",
              code_ptr->pline->line));
            return(0);   /* Parent function should end the program */
          }
          *a = '\0';
          var->cvalue = stack_string_realloc(var->cvalue, buf2);
          num_args++;
          a = buf2;
        }
        break;
      case '(':
        deep++;
        break;
      case '{':
        while(*p && *p != '}')
          p++;
        if(!*p)
          return(0);
        break;
      case '}':
        return(0);
      case ')':
        deep--;
        break;
      default:
        *a++ = *p;
    }

    if(!deep)
    {
      *a = '\0';
      if(num_args == 1 && !*buf2)
      {
        num_args = 0;
        break;
      }
      if(!(var = find_var(tprintf("arg%d", num_args), VAR_STR)))
      {
        notify(code_ptr->executor,
          tprintf("|+bR|Fatal internal error in |+bY|%d|+bR|.",
          code_ptr->pline->line));
        return(0);   /* Parent function should end the program */
      }
      var->cvalue = stack_string_realloc(var->cvalue, buf2);
      break;
    }

    p++;
  }

  if(!*p)
    return(0);

  p++;

  if(func->nargs != num_args)
  {
    notify(code_ptr->executor,
      tprintf("|+R|Expect %d args for function |+G|%s |+R|in |+Y|%d|+R|.",
      func->nargs, func->name, code_ptr->pline->line));
    return(0);
  }

  expand_user_func_args(func->value, buf2);

  sprintf(buf+strlen(buf), "%s%s", buf2, p);
  strcpy(buffer, buf);

  return(1);
}