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 "db.h"
#include "externs.h"
#include "softcode.h"

static void show_obj_prog_summary(OBJ *player, OBJ *thing)
{
  PROG *p;
  PROGLINE *pline;
  int num_lines;

  if(!thing->program)
  {
    notify(player, tprintf("%s contains no programs.",
      unparse_object(player, thing)));
    return;
  }

  notify(player, tprintf("|+B|.%s.", my_string("-", 60)));
  notify(player, tprintf("|+B|| |+W|%s |+B||",
    my_center(tprintf("Program Summary for %s",
    unparse_object(player, thing)), 58)));
  notify(player, tprintf("|+B||%s|", my_string("-", 60)));
  notify(player,
    tprintf("|+B|||+W|%s|+B|||+W|%s|+B|||+W| Lines  |+B|||+W| Running |+B||",
    my_center("Name", 20), my_center("Trigger", 20)));
  notify(player, tprintf("|+B||%s|", my_string("-", 60)));

  for(p = thing->program;p;p = p->next)
  {
    num_lines = 0;

    for(pline = p->program;pline;pline = pline->next)
      num_lines++;

    notify(player,
      tprintf("|+B|| |+G|%s|+B|| |+G|%s|+B|| |+G|%6d |+B|||+G|%s|+B||",
      my_ljust(p->name, 19),
      my_ljust((*p->trigger)?p->trigger:"NONE", 19), num_lines,
      my_center((is_running(p))?"YES":"NO", 9)));
  }

  notify(player, tprintf("|+B|`%s'", my_string("-", 60)));
}

PROG *find_prog(OBJ *thing, char *progname)
{
  PROG *p;

  for(p = thing->program;p;p = p->next)
    if(!string_compare(p->name, progname))
      break;

  return(p);
}

int next_line_num(PROG *program)
{
  PROGLINE *pline;

  if(!program->program)
    return(10);

  for(pline = program->program;pline->next;pline = pline->next);

  if(pline->line >= 65526)
    return(0);

  return((pline->line)+10);
}

void del_prog_line(PROG *program, int line_num)
{
  PROGLINE *pline, *plineprev = NULL;

  for(pline = program->program;pline;pline = pline->next)
  {
    if(pline->line == line_num)
      break;
    plineprev = pline;
  }

  if(!pline)
    return;

  stack_free(pline->code);

  if(!plineprev)
    program->program = pline->next;
  else
    plineprev->next = pline->next;

  stack_free(pline);
}

void add_new_prog_line(OBJ *player, OBJ *thing, char *progname)
{
  PROG *program;
  char *p, *q;
  int line_num;
  char *code;

  if(!controls(player, thing, POW_MODIFY))
  {
    notify(player, perm_denied());
    return;
  }

  if(!(p = strchr(progname, ',')))
  {
    notify(player, "Type 'help +code' for help.");
    return;
  }

  *p++ = '\0';

  if(!(program = find_prog(thing, progname)))
  {
    notify(player, tprintf("Program %s doesn't exist on %s.",
      progname, unparse_object(player, thing)));
    return;
  }

  if(is_running(program))
  {
    notify(player, "You may not modify a program while it is running.");
    return;
  }

  for(q = p;isdigit(*q);++q);

  if(*q == ',' || !*q)
  {
    if(*q)
      *q++ = '\0';

    line_num = atoi(p);
    if(line_num < 1 || line_num > 65535)
    {
      notify(player, "Valid line numbers are 1 through 65535.");
      return;
    }
    code = q;
  }
  else
  {
    if(!(line_num = next_line_num(program)))
    {
      notify(player, "Error! Next line number would exceed 65535.");
      return;
    }
    code = p;
  }

  if(!*code)
    del_prog_line(program, line_num);
  else
    add_prog_line(program, line_num, code);

  notify(player, "Program modified.");
}

static int ok_prog_name(char *progname)
{
  char *p;

  if(!*progname || strlen(progname) > 50)
    return(0);

  for(p = progname;*p;++p)
    if(*p == ' ' || *p == ',')
      return(0);

  return(1);
}

void add_new_prog(OBJ *player, OBJ *thing, char *progname)
{
  PROG *program;

  if(!controls(player, thing, POW_MODIFY))
  {
    notify(player, perm_denied());
    return;
  }

  if(!ok_prog_name(progname))
  {
    notify(player, "Invalid program name.");
    return;
  }

  if((program = find_prog(thing, progname)))
  {
    notify(player, tprintf("%s already has a program called %s.",
      unparse_object(player, thing), progname));
    return;
  }

  program = new_program(thing);

  program->name = stack_string_alloc(progname, 1);

  notify(player, tprintf("Program %s added to %s.",
    progname, unparse_object(player, thing)));
}

static void del_prog(OBJ *player, OBJ *thing, char *progname)
{
  PROG *program, *programprev = NULL;
  PROGLINE *pline, *plinenext;

  if(!controls(player, thing, POW_MODIFY))
  {
    notify(player, perm_denied());
    return;
  }

  for(program = thing->program;program;program = program->next)
  {
    if(!string_compare(program->name, progname))
      break;
    programprev = program;
  }

  if(!program)
  {
    notify(player, tprintf("Program %s doesn't exist on %s.",
      progname, unparse_object(player, thing)));
    return;
  }

  if(is_running(program))
  {
    notify(player, "You may not modify a program while it is running.");
    return;
  }

  for(pline = program->program;pline;pline = plinenext)
  {
    plinenext = pline->next;

    stack_free(pline->code);
    stack_free(pline);
  }

  if(!programprev)
    thing->program = program->next;
  else
    programprev->next = program->next;

  stack_free(program->trigger);
  stack_free(program->name);
  stack_free(program);

  notify(player, tprintf("Program %s removed from %s.",
    progname, unparse_object(player, thing)));
}

static void list_prog(OBJ *player, OBJ *thing, char *progname)
{
  PROG *program;
  PROGLINE *pline;
  int high = 65535, low = 1;
  char *p;
  char buf[4096];
  char *b;

  if(!controls(player, thing, POW_MODIFY) && !(thing->flags & VISIBLE))
  {
    notify(player, perm_denied());
    return;
  }

  if((p = strchr(progname, ',')))
  {
    *p++ = '\0';
    b = buf;
    while(isdigit(*p))
      *b++ = *p++;
    *b = '\0';
    if(!*buf)
      low = 1;
    else
    {
      low = atoi(buf);
      if(low < 1 || low > 65535)
      {
        notify(player, "Valid line numbers are 1 through 65535.");
        return;
      }
    }
    if(*p == '-')
    {
      p++;
      b = buf;
      while(isdigit(*p))
        *b++ = *p++;
      *b = '\0';
      if(!*buf)
        high = 65535;
      else
      {
        high = atoi(buf);
        if(high < 1 || high > 65535)
        {
          notify(player, "Valid line numbers are 1 through 65535.");
          return;
        }
        if(high < low)
        {
          notify(player,
            "The lowest number cannot be higher than the highest.");
          return;
        }
      }
    }
  }

  if(!(program = find_prog(thing, progname)))
  {
    notify(player, tprintf("Program %s doesn't exist on %s.",
      progname, unparse_object(player, thing)));
    return;
  }

  notify(player, tprintf("|+B|Listing of program |+Y|%s|+B| on %s|+W|:",
    program->name, unparse_object(player, thing)));

  for(pline = program->program;pline;pline = pline->next)
    if(pline->line >= low && pline->line <= high)
      notify(player, tprintf("%d %s", pline->line, pline->code));

  notify(player, "|+W|-- |+B|End of list|+W|--");
}

static void renum_prog(OBJ *player, OBJ *thing, char *progname)
{
  PROG *program;
  PROGLINE *pline, *pline2;
  int step, num_lines;
  char *p, *q;
  char command[4096];

  if(!*progname)
  {
    notify(player, "Usage: +code renum=<object>,<program>[,<step>]");
    return;
  }

  if(!(p = strchr(progname, ',')))
    step = 10;
  else
  {
    *p++ = '\0';

    for(q = p;*q;q++)
      if(!isdigit(*q))
      {
        notify(player, "Invalid step!");
        return;
      }
    step = atoi(p);
  }

  if(step < 1 || step > 65535)
  {
    notify(player, "Invalid step!");
    return;
  }

  if(!(program = find_prog(thing, progname)))
  {
    notify(player, tprintf("Program %s doesn't exist on %s.",
      progname, unparse_object(player, thing)));
    return;
  }

  num_lines = step;

  for(pline = program->program;pline;pline = pline->next)
  {
    num_lines += step;

    if(num_lines > 65535)
    {
      notify(player, 
        tprintf("With a step of %d, line numbers would exceed 65535.",
        step));
      return;
    }
  }

  num_lines = step;

  for(pline = program->program;pline;pline = pline->next)
  {
    for(pline2 = program->program;pline2;pline2 = pline2->next)
    {
      if((p = strchr(pline2->code, ' ')))
      {
        strncpy(command, pline2->code, p-pline2->code);
        *(command+(p-pline2->code)) = '\0';
      }
      else
        continue;

      if(!string_compare(command, "goto") ||
        !string_compare(command, "gosub"))
      {
        if(atoi(++p) == pline->line)
          add_prog_line(program, pline2->line,
            tprintf("%s %d", command, num_lines));
      }
    }
    pline->line = num_lines;
    num_lines += step;
  }

  notify(player, tprintf("Program %s modified.", program->name));
}

static void set_prog_trigger(OBJ *player, OBJ *thing, char *progname)
{
  PROG *program;
  char *p;

  if(!(p = strchr(progname, ',')))
  {
    notify(player, "Type 'help +code' for help.");
    return;
  }

  *p++ = '\0';

  if(!(program = find_prog(thing, progname)))
  {
    notify(player, tprintf("Program |+Y|%s|n| doesn't exist on %s.",
      progname, unparse_object(player, thing)));
    return;
  }

  program->trigger = stack_string_realloc(program->trigger, p);

  notify(player, tprintf("Program |+Y|%s|n| on %s set to '%s'.",
    program->name, unparse_object(player, thing), p));
}

void do_code(OBJ *player, char *option, char *arg)
{
  OBJ *thing;
  char *arg2;
  SUBCOMMAND *sc, commands[] =
  {
    { "add",     add_new_prog_line,     0 },
    { "create",  add_new_prog,          0 },
    { "delete",  del_prog,              0 },
    { "list",    list_prog,             0 },
    { "renum",   renum_prog,            0 },
    { "summary", show_obj_prog_summary, 0 },
    { "trigger", set_prog_trigger,      0 },
    { NULL }
  };

  if(!*option || !*arg)
  {
    notify(player, "Type 'help +code' for help.");
    return;
  }

  if((arg2 = strchr(arg, ',')))
    *arg2++ = '\0';
  else
    arg2 = "";

  if(!(thing = match_object(player, arg, NOTYPE)))
  {
    notify(player, no_match(arg));
    return;
  }

  if(!(sc = subcommand_match(player, option, commands, NULL)))
    subcommand_print(player, "+code", commands);
  else
    sc->func(player, thing, arg2);
}

PROG *new_program(OBJ *thing)
{
  PROG *p, *newp;

  newp = (PROG *)stack_alloc(sizeof(PROG), 1, 0);

  if(!thing->program)
    thing->program = newp;
  else
  {
    for(p = thing->program;p->next;p = p->next);
    p->next = newp;
  }

  newp->trigger = stack_string_alloc("", 1);
  newp->program = NULL;
  newp->next = NULL;

  return(newp);
}

static PROGLINE *new_prog_line(PROG *program, int line_num)
{
  PROGLINE *p, *newpline, *pprev = NULL;

  newpline = (PROGLINE *)stack_alloc(sizeof(PROGLINE), 1, 0);

  newpline->next = NULL;

  if(!program->program)
    program->program = newpline;
  else
  {
    for(p = program->program;p->next;p = p->next)
    {
      if(p->line >= line_num)
        break;
      pprev = p;
    }

    if(p->line == line_num)
    {
      stack_free(newpline);
      stack_free(p->code);
      return(p);
    }

    if(p->line > line_num)      /* We're going to insert */
    {
      newpline->next = p;

      if(!pprev)
        program->program = newpline;
      else
        pprev->next = newpline;
    }
    else
      p->next = newpline;
  }

  newpline->line = line_num;

  return(newpline);
}

void add_prog_line(PROG *program, int line, char *codestr)
{
  PROGLINE *pline;

  pline = new_prog_line(program, line);

  pline->code = stack_string_alloc(codestr, 1);
}

void free_progs(OBJ *player)
{
  PROG *p, *pnext;

  for(p = player->program;p;p = pnext)
  {
    pnext = p->next;
    del_prog(player, player, p->name);
  }
}