untermud/DOC/
untermud/DOC/U/
untermud/DOC/U/U-examples/
untermud/DOC/internals/
untermud/DOC/wizard/
untermud/MISC/
untermud/MISC/dbchk/
untermud/RWHO/
untermud/RWHO/rwhod/
#include "copyright.h"

#include <stdio.h>
#include <ctype.h>
#include <sys/param.h>

#include "db.h"
#include "config.h"

#ifndef MUDNAME
#define MUDNAME "Islandia"
#endif

static int include_all = 0;     /* include everything unless specified */
static int keep_players = 0;    /* keep all players */
static int safe_below = 1;      /* Keep everything <= safe_below */
static int safe_above = 2e9;    /* Keep everything >= safe_above */
static int reachable = 0;       /* Only keep rooms reachable from #0 */
static int norecycle = 0;       /* Exclude things in recycling center */
static int inbuild = 0;         /* True when in main nuild_trans loop */
static int recycling_center = 0;        /* Room number home("Recycler") */

# define REACH_FLAG 0x40000000
# define REACHABLE(X) (db[X].flags & REACH_FLAG)
# define SET_REACHABLE(X) (db[X].flags |= REACH_FLAG)

static dbref included[NCARGS + 1];
static dbref excluded[NCARGS + 1];

static dbref *trans;            /* translation vector */

#define DEFAULT_LOCATION (0)
#define DEFAULT_OWNER (1)

static int isok (dbref);
char *OIF_ID (int);
void OIFboolexp (FILE *, struct boolexp *);
char *ofix (const char *);

/* returns 1 if object is specifically excluded */
static int is_excluded (dbref x)
{
  int i;

  if (x == NOTHING)
    return 0;                   /* Don't exclude nothing */

  /* check that it isn't excluded */
  for (i = 0; excluded[i] != NOTHING; i++) {
    if (excluded[i] == x)
      return 1;                 /* always exclude specifics */
    if (excluded[i] == db[x].owner)
      return 1;
  }

  return (0);
}

/* returns 1 if it is not excluded */
static int not_excluded (dbref x)
{
  int i;

  if (x == NOTHING)
    return 1;                   /* Don't exclude nothing */

  /* check that it isn't excluded */
  for (i = 0; excluded[i] != NOTHING; i++) {
    if (excluded[i] == x)
      return 0;                 /* always exclude specifics */
    if (excluded[i] == db[x].owner)
      return 0;
  }

  /* if it's an exit, check that its destination is ok */
  if (Typeof (x) == TYPE_EXIT && db[x].location >= 0) {
    return isok (db[x].location);
  } else {
    return 1;
  }
}

/* returns 1 if it should be included in translation vector */
static int isok (dbref x)
{
  int i;

  if (x == DEFAULT_OWNER || x == DEFAULT_LOCATION)
    return 1;
  if (x == NOTHING)
    return 1;

  for (i = 0; included[i] != NOTHING; i++) {
    if (included[i] == x)
      return 1;                 /* always get specific ones */

    if (reachable && Typeof (x) == TYPE_ROOM && !REACHABLE (x)) {
# ifdef DEBUG
      if (inbuild)
        fprintf (stderr, "Excluding %s(%dR), not reachable\n", db[x].name, x);
# endif
      return 0;
    }
#ifdef	RECYCLER
    if (norecycle && db[x].location == recycling_center)
      return 0;
#endif

    if (included[i] == db[x].owner || (x <= safe_below || x >= safe_above)
      || keep_players && Typeof (x) == TYPE_PLAYER) {
      return not_excluded (x);
    }
  }

#if	0
  /* This is an UnterMUD converter. UnterMUDs don't have carried
     exits, so DROP such. Notez Bien: relies on the new source field. */
  if (Typeof (i) == TYPE_EXIT && db[i].source == NOTHING)
    return 0;
#endif

  /* not in the list, can only get it if include_all is on */
  /* or it's owned by DEFAULT_OWNER */
  return (include_all && not_excluded (x));
}

static void build_trans (void)
{
  dbref i;
  dbref val;

  if ((trans = (dbref *) malloc (sizeof (dbref) * db_top)) == 0) {
    abort ();
  }

  inbuild++;

  val = 0;
  for (i = 0; i < db_top; i++) {
    if (isok (i)) {
      trans[i] = val++;
    } else {
      trans[i] = NOTHING;
    }
  }

  inbuild--;
}

static dbref translate (dbref x)
{
  if (x == NOTHING || x == HOME) {
    return (x);
  } else {
    return (trans[x]);
  }
}

/* TRUE_BOOLEXP means throw this argument out */
/* even on OR; it's effectively a null boolexp */
/* NOTE: this doesn't free anything, it just munges it up */
static struct boolexp *translate_boolexp (struct boolexp *exp)
{
  struct boolexp *s1;
  struct boolexp *s2;

  if (exp == TRUE_BOOLEXP) {
    return TRUE_BOOLEXP;
  } else {
    switch (exp->type) {
    case BOOLEXP_NOT:
      s1 = translate_boolexp (exp->sub1);
      if (s1 == TRUE_BOOLEXP) {
        return TRUE_BOOLEXP;
      } else {
        exp->sub1 = s1;
        return exp;
      }
      /* break; */
    case BOOLEXP_AND:
    case BOOLEXP_OR:
      s1 = translate_boolexp (exp->sub1);
      s2 = translate_boolexp (exp->sub2);
      if (s1 == TRUE_BOOLEXP && s2 == TRUE_BOOLEXP) {
        /* nothing left */
        return TRUE_BOOLEXP;
      } else if (s1 == TRUE_BOOLEXP && s2 != TRUE_BOOLEXP) {
        /* s2 is all that is left */
        return s2;
      } else if (s1 != TRUE_BOOLEXP && s2 == TRUE_BOOLEXP) {
        /* s1 is all that is left */
        return s1;
      } else {
        exp->sub1 = s1;
        exp->sub2 = s2;
        return exp;
      }
      /* break; */
    case BOOLEXP_CONST:
      exp->thing = translate (exp->thing);
      if (exp->thing == NOTHING) {
        return TRUE_BOOLEXP;
      } else {
        return exp;
      }
      /* break; */
    default:
      abort ();                 /* bad boolexp type, we lose */
      return TRUE_BOOLEXP;
    }
  }
}

static int ok (dbref x)
{
  if (x == NOTHING || x == HOME) {
    return 1;
  }
  /* This is an UnterMUD converter. UnterMUDs don't have carried
     exits, so DROP such. Notez Bien: relies on the new source field. */
  if (Typeof (x) == TYPE_EXIT && db[x].source == NOTHING)
    return 0;                   /* Put here to avoid things getting renumbered. */
#ifdef	TYPE_GARBAGE
  if (Typeof (x) == TYPE_GARBAGE)
    return 0;
#endif
  return trans[x] != NOTHING;
}

static void check_bad_exits (dbref x)
{
  dbref e;

  if (Typeof (x) == TYPE_ROOM && !isok (x)) {
    /* mark all exits as excluded */
    DOLIST (e, db[x].exits) {
      trans[e] = NOTHING;
    }
  }
}

static void check_owner (dbref x)
{
  if (ok (x) && !ok (db[x].owner)) {
    db[x].owner = DEFAULT_OWNER;
  }
}

static void check_location (dbref x)
{
  dbref loc;
  dbref newloc;

  if (ok (x) && (Typeof (x) == TYPE_THING || Typeof (x) == TYPE_PLAYER)
    && !ok (loc = db[x].location)) {
    /* move it to home or DEFAULT_LOCATION */
    if (ok (db[x].exits)) {
      newloc = db[x].exits;     /* home */
    } else {
      newloc = DEFAULT_LOCATION;
    }
    db[loc].contents = remove_first (db[loc].contents, x);
    PUSH (x, db[newloc].contents);
    db[x].location = newloc;
  }
}

static void check_next (dbref x)
{
  dbref next;

  if (ok (x)) {
    while (!ok (next = db[x].next))
      db[x].next = db[next].next;
  }
}

static void check_contents (dbref x)
{
  dbref c;

  if (ok (x)) {
    while (!ok (c = db[x].contents))
      db[x].contents = db[c].next;
  }
}

/* also updates home */
/* MUST BE CALLED AFTER check_owner! */
static void check_exits (dbref x)
{
  dbref e;

  if (ok (x) && !ok (e = db[x].exits)) {
    switch (Typeof (x)) {
    case TYPE_ROOM:
      while (!ok (e = db[x].exits))
        db[x].exits = db[e].next;
      break;
    case TYPE_PLAYER:
    case TYPE_THING:
      if (ok (db[db[x].owner].exits)) {
        /* set it to owner's home */
        db[x].exits = db[db[x].owner].exits;    /* home */
      } else {
        /* set it to DEFAULT_LOCATION */
        db[x].exits = DEFAULT_LOCATION; /* home */
      }
      break;
    }
  }
}

void exitsources ()
{
  dbref i;
  dbref e;

  /* Make sure all sources have something recognizable. */
  for (i = 0; i < db_top; i++)
    db[i].source = NOTHING;
  for (i = 0; i < db_top; i++) {
    if (Typeof (i) == TYPE_ROOM) {
      DOLIST (e, db[i].exits) {
        db[e].source = i;
      }
    }
  }
}


char buf[BUFSIZ];

static void do_OIF_write (void)
{
  dbref i;
  dbref kludge;

  for (i = 0, kludge = 0; i < db_top; i++) {
    if (trans[i] != NOTHING)
      trans[i] = kludge++;
  }

  printf
    ("object sysobj\nint _objcnt=%d\nint newsarticle=0\nstr nam=The System Object (all mighty data point)\nobj _syslimbo=0\nlst own=wizard\nlst _wizards=wizard\nendobj\n",
    db_top - 1);
  strcpy (buf, "wizard");
  rot_init (buf);
  rot_encode ("potrzebie", buf);
  printf
    ("object wizard\nflg _wz=\nflg _pl=\nstr nam=UnterWiz\nobj home=0@%s\nstr pass=%s\nobj loc=0\nlst own=wizard\nendobj\n",
    MUDNAME, buf);

  for (i = 0; i < db_top; i++)
    if (ok (i)) {
      printf ("object %s\n", OIF_ID (i));
      printf ("str nam=%s\n", db[i].name);
      if (db[i].description)
        printf ("str desc=%s\n", db[i].description);
      if (db[i].contents) {
        printf ("lst con=");
        for (kludge = db[i].contents; kludge != NOTHING;
          kludge = db[kludge].next)
          if (Typeof (kludge) != TYPE_PLAYER)
            printf ("%s;", OIF_ID (kludge));
        printf ("\n");
      }
      if (db[i].key && !(Typeof (i) == TYPE_EXIT &&
          db[i].location == NOTHING)) {
        printf ("boo lok=");
        OIFboolexp (stdout, translate_boolexp (db[i].key));
      } else if (Typeof (i) == TYPE_EXIT && db[i].location == NOTHING)
        printf ("boo lok=F\n"); /* People can't walk through unlinked exits. */
      else
        printf ("boo lok=T\n");
      if (db[i].fail_message)
        printf ("str fail=%s\n", db[i].fail_message);
      if (db[i].ofail)
        printf ("str ofail=%s\n", ofix (db[i].ofail));
      if (db[i].succ_message)
        printf ("str succ=%s\n", db[i].succ_message);
      if (db[i].osuccess)
        printf ("str osucc=%s\n", ofix (db[i].osuccess));
      printf ("lst own=%s\n", OIF_ID (db[i].owner));
      if (db[i].flags & LINK_OK)
        printf ("boo lnk=T\n");
      if (db[i].flags & DARK)
        printf ("flg dark=\n");
      switch (Typeof (i)) {
      case TYPE_PLAYER:
        printf ("flg _pl=\n");
        if (Wizard (i))
          printf ("flg _wz=\n");
        printf ("obj loc=%s\n", OIF_ID (db[i].location));
        printf ("obj home=%d@%s\n", translate (db[i].exits), MUDNAME);
        if (Genderof (i) == GENDER_MALE) {
          printf ("str subv=he\n");
          printf ("str objv=him\n");
          printf ("str posv=his\n");
        } else if (Genderof (i) == GENDER_FEMALE) {
          printf ("str subv=she\n");
          printf ("str objv=her\n");
          printf ("str posv=her\n");
        } else if (Genderof (i) == GENDER_NEUTER) {
          printf ("str subv=it\n");
          printf ("str objv=it\n");
          printf ("str posv=its\n");
        } else {
          printf ("str subv=%s\n", db[i].name);
          printf ("str objv=%s\n", db[i].name);
          printf ("str posv=%s's\n", db[i].name);
        }
        OIF_ID (i);
        if (db[i].password) {
          rot_init (buf);
          rot_encode (db[i].password, buf);
        } else {
          buf[0] = 0;
        }
        printf ("str pass=%s\n", buf);
        break;
      case TYPE_THING:
        printf ("obj loc=%s\n", OIF_ID (db[i].location));
        printf ("obj home=%d@%s\n", translate (db[i].exits), MUDNAME);
        if (db[i].flags & STICKY)
          printf ("cmd drop=tel $me home;_echo Dropped.\n");
        break;
      case TYPE_ROOM:
        printf ("flg _rm=\n");
        if (translate (db[i].location) != NOTHING)
          printf ("obj drpto=%s\n", OIF_ID (db[i].location));
        printf ("lst xit=");
        for (kludge = db[i].exits; kludge != NOTHING;
          kludge = db[kludge].next)
          printf ("%s;", OIF_ID (kludge));
        printf ("\n");
        printf ("lst ply=");
        for (kludge = db[i].contents; kludge != NOTHING;
          kludge = db[kludge].next)
          if (Typeof (kludge) == TYPE_PLAYER)
            printf ("%s;", OIF_ID (kludge));
        printf ("\n");
        break;
      case TYPE_EXIT:
        /* Unlinked exit? Make it actually linked to home. */
        if (db[i].location == NOTHING)
          printf ("obj dst=home\n");
        else
          printf ("obj dst=%s\n", OIF_ID (db[i].location));
        /* Yes, this is NEW. */
        printf ("obj loc=%s\n", OIF_ID (db[i].source));
        break;
      default:                 /* Do something sensible for stuff we don't
                                   understand. */
        fprintf (stderr, "Can't handle type %d\n", Typeof (i));
        abort ();
       /*NOTREACHED*/}
      printf ("endobj\n");
    }
}

char *OIF_ID (dbref i)
{
  if (i == HOME)
    sprintf (buf, "home");
  else if (Typeof (i) == TYPE_PLAYER || Typeof (i) == TYPE_THING)
    sprintf (buf, "%d@%s", translate (i), MUDNAME);
  else
    sprintf (buf, "%d", translate (i));
  return (buf);
}


int reach_lvl = 0;

void make_reachable (dbref x)
{
  dbref e, r;
#ifdef DEBUG
  int i;
#endif

  if (Typeof (x) != TYPE_ROOM || is_excluded (x))
    return;

  reach_lvl++;

  SET_REACHABLE (x);

#ifdef DEBUG
  for (i = 0; i < reach_lvl; i++)
    fputc (' ', stderr);
  fprintf (stderr, "Set %s(%dR) reachable.\n", db[x].name, x);
#endif

  DOLIST (e, db[x].exits) {
    r = db[e].location;

    if (r < 0)
      continue;
    if (is_excluded (r))
      continue;
    if (is_excluded (e))
      continue;
    if (!REACHABLE (r))
      make_reachable (r);
  }

  reach_lvl--;
}

void main (int argc, char **argv)
{
  dbref i;
  int top_in;
  int top_ex;
  char *arg0;

  top_in = 0;
  top_ex = 0;

  /* Load database */
  if (db_read (stdin) < 0) {
    fputs ("Database load failed!\n", stderr);
    exit (1);
  }

  fputs ("Done loading database...\n", stderr);


  /* now parse args */
  arg0 = *argv;
  for (argv++, argc--; argc > 0; argv++, argc--) {
    if (isdigit (**argv) || **argv == '-' && isdigit ((*argv)[1])) {
      i = atol (*argv);
    } else if (**argv == '+' && isdigit ((*argv)[1])) {
      i = atol (*argv + 1);
    } else if (**argv == 'b' && isdigit ((*argv)[1])) {
      safe_below = atol (*argv + 1);
      fprintf (stderr, "Including all objects %d and below\n", safe_below);
    } else if (**argv == 'a' && isdigit ((*argv)[1])) {
      safe_above = atol (*argv + 1);
      fprintf (stderr, "Including all objects %d and above\n", safe_above);
    } else if (!strcmp (*argv, "all")) {
      include_all = 1;
    } else if (!strcmp (*argv, "reachable")) {
      reachable = 1;
    } else if (!strcmp (*argv, "players")) {
      keep_players = 1;
    } else if (!strcmp (*argv, "norecycle")) {
      norecycle = 1;
    } else if (**argv == '-' && (i = lookup_player (*argv + 1)) != 0) {
      fprintf (stderr, "Excluding player %s(%d)\n", db[i].name, i);
      i = -i;
    } else if (**argv != '-' && (i = lookup_player (*argv)) != NOTHING) {
      fprintf (stderr, "Including player %s(%d)\n", db[i].name, i);
    } else {
      fprintf (stderr, "%s: bogus argument %s\n", arg0, *argv);
      continue;
    }

    if (i < 0) {
      excluded[top_ex++] = -i;
    } else {
      included[top_in++] = i;
    }
  }

  /* Terminate */
  included[top_in++] = NOTHING;
  excluded[top_ex++] = NOTHING;

  /* Check for reachability from DEFAULT_LOCATION */
  if (reachable) {
    make_reachable (DEFAULT_LOCATION);
    fputs ("Done marking reachability...\n", stderr);
  }

  /* Gives all exits sources -- this becomes important later on. */
  exitsources ();

#ifdef	RECYCLER
  /* Find recycler */
  if (norecycle && ((i = lookup_player (RECYCLER)) != NOTHING)) {
    recycling_center = db[i].exits;
/*      if (recycling_center == DEFAULT_LOCATION) norecycle = 0;
      else */
    {
      fprintf (stderr, "Excluding all players in %s(%d)\n",
        db[recycling_center].name, recycling_center);
    }
  }
#endif

  /* Build translation table */
  build_trans ();
  fputs ("Done building translation table...\n", stderr);

  /* Scan everything */
  for (i = 0; i < db_top; i++)
    check_bad_exits (i);
  fputs ("Done checking bad exits...\n", stderr);

  for (i = 0; i < db_top; i++)
    check_owner (i);
  fputs ("Done checking owners...\n", stderr);

  for (i = 0; i < db_top; i++)
    check_location (i);
  fputs ("Done checking locations...\n", stderr);

  for (i = 0; i < db_top; i++)
    check_next (i);
  fputs ("Done checking next pointers...\n", stderr);

  for (i = 0; i < db_top; i++)
    check_contents (i);
  fputs ("Done checking contents...\n", stderr);

  for (i = 0; i < db_top; i++)
    check_exits (i);
  fputs ("Done checking homes and exits...\n", stderr);

/*    do_write();   */
  do_OIF_write ();
  fputs ("Done.\n", stderr);

  exit (0);
}

static void OIFbool_subexp (FILE * f, struct boolexp *b)
{
  switch (b->type) {
  case BOOLEXP_AND:
    putc ('(', f);
    OIFbool_subexp (f, b->sub1);
    putc (AND_TOKEN, f);
    OIFbool_subexp (f, b->sub2);
    putc (')', f);
    break;
  case BOOLEXP_OR:
    putc ('(', f);
    OIFbool_subexp (f, b->sub1);
    putc (OR_TOKEN, f);
    OIFbool_subexp (f, b->sub2);
    putc (')', f);
    break;
  case BOOLEXP_NOT:
    putc ('(', f);
    putc (NOT_TOKEN, f);
    OIFbool_subexp (f, b->sub1);
    putc (')', f);
    break;
  case BOOLEXP_CONST:
    fprintf (f, "%s", OIF_ID (b->thing));
    break;
  default:
    break;
  }
}

void OIFboolexp (FILE * f, struct boolexp *b)
{
  if (b != TRUE_BOOLEXP) {
    OIFbool_subexp (f, b);
  }
  putc ('\n', f);
}

char *ofix (const char *ins)
{
  char foo[BUFSIZ];
  char *bar, *baz;

  strcpy (foo, ins);
  strcpy (buf, "");
  bar = foo;
  while ((baz = (char *) index (bar, '%')) != NULL) {
    *baz++ = '\0';
    strcat (buf, bar);
    switch (*baz) {
    case 's':
      strcat (buf, "$subv");
      break;
    case 'o':
      strcat (buf, "$objv");
      break;
    case 'p':
      strcat (buf, "$posv");
      break;
    case 'n':
      strcat (buf, "$nam");
    }
    bar = baz + 1;
  }
  strcat (buf, bar);
  return (buf);
}