/
2.0.5/doc/
2.0.5/gnu/
2.0.5/sha/
/* convertdb.c */

#include "config.h"

/*
 *		       This file is part of TeenyMUD II.
 *		 Copyright(C) 1993, 1994, 1995 by Jason Downs.
 *                           All rights reserved.
 * 
 * TeenyMUD II is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * TeenyMUD II is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program (see the file 'COPYING'); if not, write to
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA.
 *
 */

#include <stdio.h>
#include <sys/types.h>
#ifdef HAVE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif			/* HAVE_STRING_H */
#include <time.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif			/* HAVE_STDLIB_H */
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif			/* HAVE_UNISTD_H */

#include "conf.h"
#include "teeny.h"
#include "teenydb.h"
#include "textdb.h"
#include "ptable.h"
#include "sha/sha_wrap.h"
#include "gnu/getopt.h"
#include "externs.h"

extern int errno;

static void print_version _ANSI_ARGS_((void));
static void print_help _ANSI_ARGS_((void));
static void upgrade_database _ANSI_ARGS_((int, int, int));
static void create_startup _ANSI_ARGS_((void));

enum text_state { TEXT_NONE, TEXT_READ, TEXT_WRITE };

static char *progname;

static struct option const getopt_options[] =
{
  { "configuration", required_argument, 0, 'c' },
  { "filter-garbage", no_argument, 0, 'G' },
  { "input-file", required_argument, 0, 'i' },
  { "initialize-db", no_argument, 0, 's' },
  { "output-file", required_argument, 0, 'o' },
  { "nonstandard", no_argument, 0, 'C' },
  { "xibo-compat", no_argument, 0, 'X' },
  { "use-existing-root", no_argument, 0, 'e' },
  { "help", no_argument, 0, 'h' },
  { "version", no_argument, 0, 'v' },
  { NULL, 0, 0, '\0' }
};
 
extern const char teenymud_version[];

static void print_version()
{
  fprintf(stderr,
  	  "This is the TeenyMUD Database Conversion System, version %s\n",
	  teenymud_version);
}

static void print_help()
{
  print_version();

  fputs("Choose from the following:\n", stderr);
  fputs("-c, --configuration\tspecify configuration file\n", stderr);
  fputs("-e, --use-existing-root\tforce use of existing root room\n", stderr);
  fputs("-G, --filter-garbage\tdo not write garbage objects\n", stderr);
  fputs("-i, --input-file\tspecify input file\n", stderr);
  fputs("-o, --output-file\tspecify output file\n", stderr);
  fputs("-s, --initialze-db\tcreate a new database\n", stderr);
  fputs("-C, --nonstandard\tenable nonstandard compatibility\n", stderr);
  fputs("-X, --xibo-compat\tenable 1.3 compatibility\n", stderr);
  fputs("-h, --help\t\tprint this help message\n", stderr);
  fputs("-v, --version\t\tprint the version number\n", stderr);
}

int main(argc, argv)
    int argc;
    char **argv;
{
  int ch, read_version, read_flags, read_total;
  int dash_c = 0, one_three = 0, use_existing = 0;
  int startup = 0;
  int output_flags = OUTPUT_FLAGS;
  enum text_state state = TEXT_NONE;
  char *conf_file = (char *) NULL;
  char *text_in = (char *) NULL;
  char *text_out = (char *) NULL;
  char buffer[BUFFSIZ];
  FILE *text;

  progname = ty_basename(argv[0]); 

  opterr = 0;
  while ((ch = getopt_long(argc, argv, "c:i:o:esCXGhv",
  			   getopt_options, (int *)0)) != -1) {
    switch (ch) {
    case 'h':
      print_help();
      exit(0);
    case 'v':
      print_version();
      exit(0);
    case 's':
      startup++;
      break;
    case 'e':
      use_existing++;
      break;
    case 'c':			/* config file */
      conf_file = optarg;
      break;
    case 'i':
      if (text_out != (char *) NULL) {
	fprintf(stderr, "%s: You may only specify one of -i or -o.\n",
		progname);
	fprintf(stderr, "%s: Use '%s --help' for a complete list of options.\n",
		progname, progname);
	exit(0);
      }
      text_in = optarg;
      state = TEXT_READ;
      break;
    case 'o':
      if (text_in != (char *) NULL) {
	fprintf(stderr, "%s: You may only specify one of -i or -o.\n",
		progname);
	fprintf(stderr, "%s: Use '%s --help' for a complete list of options.\n",
		progname, progname);
	exit(0);
      }
      text_out = optarg;
      state = TEXT_WRITE;
      break;
    case 'C':			/* ancient -C database format */
      dash_c++;
      break;
    case 'X':			/* Xibo database format(tm) */
      one_three++;
      break;
    case 'G':
      output_flags |= DB_GFILTER;
      break;
    default:
      fprintf(stderr, "%s: bad option -- %c\n", progname, ch);
      fprintf(stderr, "%s: Use '%s --help' for a complete list of options.\n",
	      progname, progname);
      exit(0);
    }
  }
  if (startup && (state != TEXT_NONE)) {
    fprintf(stderr, "%s: You cannot initialize a text database!\n", progname);
    exit(0);
  }
  if (conf_file == (char *) NULL) {
    fprintf(stderr, "%s: You must specify a configuration file.\n", progname);
    exit(0);
  }
  if (dash_c && (state != TEXT_READ)) {
    fprintf(stderr,
	    "%s: You may only specify -C while reading a database.\n",
	    progname);
    fprintf(stderr, "%s: Use '%s --help' for a complete list of options.\n",
	    progname, progname);
    exit(0);
  }
  if (one_three && (state != TEXT_READ)) {
    fprintf(stderr,
	    "%s: You may only specify -X while reading a database.\n",
	    progname);
    fprintf(stderr, "%s: Use '%s --help' for a complete list of options.\n",
	    progname, progname);
    exit(0);
  }
  if ((output_flags & DB_GFILTER) && (state != TEXT_WRITE)) {
    fprintf(stderr,
	    "%s: You may only specify -G while writing a database.\n",
	    progname);
    fprintf(stderr, "%s: Use '%s --help' for a complete list of options.\n",
	    progname, progname);
    exit(0);
  }

  /* works just like the MUD, now. */
  mudstat.status = MUD_COLD;
  time(&mudstat.now);

  if(conf_init(conf_file) == -1)
    exit(-1);

  /* first things first. */
  if(chdir(mudconf.chdir_path) != 0) {
    fprintf(stderr, "%s: chdir [%s] failed: %s, continuing...\n", progname,
	    mudconf.chdir_path, strerror(errno));
  }

  cache_init();

  fprintf(stderr, "TeenyMUD %s database conversion system.\n",
  	  teenymud_version);
  fprintf(stdout, "TeenyMUD %s is Copyright(C) 1993, 1994, 1995 Jason Downs.\n",
          teenymud_version);
  fputs("TeenyMUD is free software and comes with absolutely no warranty.\n",
        stdout);

  fprintf(stderr, "Read configuration for %s.\n", mudconf.name);

  if(startup)
    create_startup();	/* This is all self-contained. */

  switch (state) {
  case TEXT_READ:
    if ((text = fopen(text_in, "r")) == NULL) {
      fprintf(stderr, "Couldn't open %s for read, %s\n", text_in,
	      strerror(errno));
      exit(-1);
    }
    if (dbmfile_init(mudconf.dbm_file, TEENY_DBMFAST) != 0) {
      fprintf(stderr, "Couldn't open dbm file %s.\n", mudconf.dbm_file);
      exit(-1);
    }

    fputs("Looking at your database...\n", stderr);

    if(text_version(text, &read_version, &read_flags, &read_total) == -1){
      fputs("Aborting.\n", stderr);
      exit(-1);
    }
    if(read_version >= 0) {	/* TeenyMUD */
      if(read_version == 0) {	/* might really be version 5 */
        if (!dash_c) {
          fputs("Hmmm. Your database appears to be a very old format.\n",
	  	stderr);
          fputs("Was it created by the old TeenyMUD 1.1-C server? [n] ",
	  	stderr);
          fflush(stderr);
          if(fgets(buffer, BUFFSIZ, stdin) == (char *)NULL) {
	    fputs("Aborting.\n", stderr);
	    exit(-1);
          }
          if((buffer[0] == 'y') || (buffer[0] == 'Y'))
	    read_version = 5;
        } else
          read_version = 5;
      } else if((read_version < 5) && (read_version > 0)){/* might be 1.3 */
        if (!one_three) {
          fputs("Hmmm. Your database appears to be an old format,\n", stderr);
          fputs("and I can't tell if it was created by TeenyMUD 1.2,\n",
	        stderr);
          fputs("or TeenyMUD 1.3.  Which one is it? [1.3] ", stderr);
          fflush(stderr);
          if(fgets(buffer, BUFFSIZ, stdin) == (char *)NULL) {
	    fputs("Aborting.\n", stderr);
	    exit(-1);
          }
          if(!strchr(buffer, '2')){
	    read_flags |= DB_SITE;
	    read_flags |= DB_ARRIVE;
          }
        } else {
          read_flags |= DB_SITE;
	  read_flags |= DB_ARRIVE;
        }
      }
      if((read_version > 9) && (read_version < 20)) {	/* 1.3 */
        read_version -= 10;
        read_flags |= DB_SITE;
        read_flags |= DB_ARRIVE;
      }

      if((read_version < 5) && !(read_flags & DB_SITE)) {
        fputs("Hmm, looks like a pre-1.3 TeenyMUD database.\n", stderr);
      } else if((read_version < 5) && (read_flags & DB_SITE)) {
        fputs("Hmm, looks like a TeenyMUD 1.3 database.\n", stderr);
      } else if(read_version == 5) {
        fputs("Hmm, looks like a TeenyMUD 1.1-C database.\n", stderr);
      } else if(read_version < 200) {
        fputs("Hmm, looks like a TeenyMUD 1.4 or TeenyMUSK database.\n",
	      stderr);
      } else if(read_version < 300) {
        fputs("Hmm, looks like a TeenyMUD 2.0a database.\n", stderr);
      } else {
        fputs("Hmm, looks like a TeenyMUD 2.0 database.\n", stderr);
      }
    } else {		/* Something else. */
      if(read_version == -1)
        fputs("Hmm, looks like a TinyMUD database.  Buh.\n", stderr);
    }

    fputs("Loading text database", stderr);
    fflush(stderr);
    if(text_load(text, read_version, read_flags, read_total) == -1) {
      fputs("Load of text database failed. Aborting.\n", stderr);
      exit(-1);
    }
    fclose(text);
    fprintf(stderr, "Loaded text database version %d.", read_version);
    if (read_flags & DB_SITE)
      fputs("  [SITE]", stderr);
    if (read_flags & DB_ARRIVE)
      fputs("  [ARRIVE]", stderr);
    if (read_flags & DB_DESTARRAY)
      fputs("  [DESTARRAY]", stderr);
    if (read_flags & DB_PARENTS)
      fputs("  [PARENTS]", stderr);
    if (read_flags & DB_IMMUTATTRS)
      fputs("  [IMMUTATTRS]", stderr);
    fputc('\n', stderr);

    if (read_version < 300)
      upgrade_database(read_version, read_flags, use_existing);
    break;
  case TEXT_WRITE:
    if ((text = fopen(text_out, "w")) == NULL) {
      fprintf(stderr, "Couldn't open %s for write.\n", text_out);
      exit(-1);
    }
    fputs("Writing text database", stderr);
    fflush(stderr);
    if (dbmfile_open(mudconf.dbm_file, TEENY_DBMFAST|TEENY_DBMREAD) != 0) {
      fprintf(stderr, "Couldn't open dbm file %s\n", mudconf.dbm_file);
      exit(-1);
    }
    if (database_read(mudconf.db_file) != 0) {
      fprintf(stderr, "Couldn't read database file %s\n", mudconf.db_file);
      exit(-1);
    }
    ptable_init();
    text_dump(text, OUTPUT_VERSION, output_flags);
    fclose(text);
    break;
  default:
    fprintf(stderr, "%s: You must specify one of either -i or -o.\n", progname);
    fprintf(stderr, "%s: Use '%s --help' for a complete list of options.\n",
    	    progname, progname);
    exit(0);
  }
  cache_flush();
  dbmfile_close();
  if (database_write(mudconf.db_file) == -1)
    fputs("Error writing database file.\n", stderr);

  return (0);
}

static void upgrade_database(read_version, read_flags, use_existing)
    int read_version, read_flags, use_existing;
{
  register int i;
  int default_loc;
  int newloc = 0;

  default_loc = mudconf.root_location;
  if ((read_version < 100) && !use_existing) {
    char ibuff[16];

    fputs("Create a new room to be the root? [Ny] ", stderr);
    fflush(stderr);
    
    if(fgets(ibuff, sizeof(ibuff), stdin) != (char *)NULL) {
      if((ibuff[0] == 'y') || (ibuff[0] == 'Y')) {
        default_loc = create_obj(TYP_ROOM);
	if(default_loc == -1) {
	  fprintf(stderr, "Couldn't create root room.  Aborting!\n");
	  return;
	}
	if((set_str_elt(default_loc, NAME, "Root Room") == -1)
	   || (set_int_elt(default_loc, OWNER, mudconf.player_god) == -1)) {
	  fprintf(stderr, "Couldn't set root room name or owner.  Aborting!\n");
	  return;
	}
	stamp(default_loc, STAMP_MODIFIED);
	newloc++;

	fprintf(stderr, "Created a root room with object #%d.\n",
		default_loc);
	fputs("(Be sure to put this object number in your configuration.)\n",
	      stderr);
      }
    }

    if((read_version < 100) && !newloc) {
      if (!exists_object(default_loc) || !isROOM(default_loc)) {
        fprintf(stderr,
		"Illegal default room location (#%d), reseting to #0.\n",
	        default_loc);
        default_loc = 0;
      }
      if (!exists_object(default_loc) || !isROOM(default_loc)) {
        fprintf(stderr, "Illegal default room location (#%d). Fatal error.\n",
	        default_loc);
        exit(-1);
      }
      fprintf(stderr, "Using object #%d as the root room.\n", default_loc);
    }

    fputs("Upgrading the database...", stderr);
    fflush(stderr);

    if(read_version < 0) {		/* TinyMUD. */
      /* Figure out exit locations...   Augh. */
      int list, next;

      for (i = 0; i < mudstat.total_objects; i++) {
        if ((main_index[i] == (struct dsc *)NULL)
	    || (DSC_TYPE(main_index[i]) == TYP_EXIT))
	  continue;
	
	/* Check the contents list. */
	if(get_int_elt(i, CONTENTS, &list) == -1) {
	  fprintf(stderr, "Database error on #%d.\n", i);
	  return;
	}
	while(list != -1) {
	  if(_exists_object(list)) {
	    next = DSC_NEXT(main_index[list]);
	    if(Typeof(list) == TYP_EXIT) {
	      list_drop(list, i, CONTENTS);
	      list_add(list, i, EXITS);
	    }
	    list = next;
	  } else {
	    fprintf(stderr, "Database error on #%d.\n", i);
	    return;
	  }
	}

	/* Check the exits list. */
	if(get_int_elt(i, EXITS, &list) == -1) {
	  fprintf(stderr, "Database error on #%d.\n", i);
	  return;
	}
	while(list != -1) {
	  if(_exists_object(list)) {
	    if(set_int_elt(list, LOC, i) == -1) {
	      fprintf(stderr, "Database error on #%d.\n", list);
	      return;
	    }
	    list = DSC_NEXT(main_index[list]);
	  } else {
	    fprintf(stderr, "Database error on #%d.\n", i);
	    return;
	  }
	}
      }
    }

    if (read_version < 100) {
      for (i = 0; i < mudstat.total_objects; i++) {
        if ((main_index[i] == (struct dsc *)NULL)
	    || (DSC_TYPE(main_index[i]) != TYP_ROOM))
	  continue;

        DSC_NEXT(main_index[i]) = -1;
	list_add(i, default_loc, ROOMS);
	if(set_int_elt(i, LOC, default_loc) == -1) {
	  fprintf(stderr, "Database error on #%d.\n", i);
	  return;
	}
      }
    }

    fputs("Done.\n", stderr);
    fflush(stderr);
  } else if((read_version == 200) || (read_version < 0)) {
    int loc;

    fprintf(stderr, "Recycling %s...",
    	    ((read_version == 200) ? "programs" : "garbage"));
    fflush(stderr);

    for (i = 0; i < mudstat.total_objects; i++) {
      if (main_index[i] == NULL)
	continue;
      switch(DSC_TYPE(main_index[i])){
      case TYP_PLAYER:
      case TYP_EXIT:
      case TYP_THING:
      case TYP_ROOM:
        break;
      default:
        if(get_int_elt(i, LOC, &loc) != -1) {
	  if(_exists_object(loc))
	    list_drop(i, loc, CONTENTS);
	  destroy_obj(i);
	}
      }
    }

    fputs("Done.\n", stderr);
  }
}

static void create_startup()
{
  int room, wiz;

  if (dbmfile_init(mudconf.dbm_file, 0) != 0) {
    fprintf(stderr, "%s: Couldn't upen dbm file %s\n", progname,
    	    mudconf.dbm_file);
    exit(0);
  }
  initialize_db(16);
  mudstat.slack = 16;

  room = create_obj(TYP_ROOM);
  wiz = create_obj(TYP_PLAYER);

  if ((set_str_elt(room, NAME, "Limbo") == -1)
      || (set_str_elt(wiz, NAME, "Wizard") == -1)
      || (attr_add(wiz, PASSWORD, cryptpwd("potrzebie"),
		   PASSWORD_FLGS) == -1)
      || (set_int_elt(wiz, NEXT, -1) == -1)
      || (set_int_elt(room, NEXT, -1) == -1)
      || (set_int_elt(wiz, LOC, room) == -1)
      || (set_int_elt(room, LOC, room) == -1)
      || (set_int_elt(room, CONTENTS, wiz) == -1)
      || (set_int_elt(wiz, HOME, room) == -1)
      || (set_int_elt(room, OWNER, wiz) == -1)
      || (set_int_elt(wiz, OWNER, wiz) == -1)) {
    fprintf(stderr, "%s: Something bad happened.\n", progname);
    exit(0);
  }
  stamp(room, STAMP_MODIFIED);
  stamp(wiz, STAMP_MODIFIED);

  cache_flush();
  dbmfile_close();
  if (database_write(mudconf.db_file) == -1)
    fprintf(stderr, "%s: Error writing the database.\n", progname);
  else
    fprintf(stderr, "Database initialized.\n");
  exit(0);
}