/*
 * static char *rcsid_init_c =
 *   "$Id: init.c,v 1.49 1997/06/22 05:14:27 master Exp $";
 */

/*
    CrossFire, A Multiplayer game for X-windows

    Copyright (C) 1992 Mark Wedel
    Copyright (C) 1992 Frank Tore Johansen

    This program 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.

    This program 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; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

    The author can be reached via e-mail to master@rahul.net
*/

#include <global.h>
#include <loader.h>
#include <main.h>
#ifndef __CEXTRACT__
#include <sproto.h>
#endif
#include <version.h>
#include <X11/keysym.h>

void set_chrfont(char *val) {    settings.chrfont=val; }
void set_logfile(char *val) { settings.logfilename=val; }
void set_servermode() { settings.servermode=SERVER_ENABLED; }
void set_fix_fontpath() { settings.fixfontpath=0; }
void set_pixmaps() { settings.displaymode=Dm_Bitmap; }
void set_synchronize() { settings.synchronize=0; }
void call_version() { version(NULL); exit(0); }
void showscores() { display_high_score(NULL,9999,NULL); exit(0); }
void set_blackwhite() { settings.blackwhite=1; }
void set_initflag() { settings.noadd=1; }
void set_debug() { settings.debug=llevDebug; }
void unset_debug() { settings.debug=llevError; }
void set_mondebug() { settings.debug=llevMonster; }
void set_splitwindow() { settings.splitwindows=1; }
void set_dumpmon1() {settings.dumpvalues=1; }
void set_dumpmon2() {settings.dumpvalues=2; }
void set_dumpmon3() {settings.dumpvalues=3; }
void set_dumpmon4() {settings.dumpvalues=4; }
void set_dumpmon5() {settings.dumpvalues=5; }
void set_dumpmon6() {settings.dumpvalues=6; }
void set_dumpmon7() {settings.dumpvalues=7; }
void set_dumpmon8() {settings.dumpvalues=8; }
void set_daemon() {settings.daemonmode=1; }
void set_xpm() { settings.displaymode=Dm_Pixmap; }
void set_libdir(char *path) { settings.libdir=path; }
void set_fontdir(char *path) { settings.fontdir=path; }
void set_mapdir(char *path) { settings.mapdir=path; }
void set_archetypes(char *path) { settings.archetypes=path; }
void set_treasures(char *path) { settings.treasures=path; }
void set_uniquedir(char *path) { settings.uniquedir=path; }
void set_playerdir(char *path) { settings.playerdir=path; }
void set_tmpdir(char *path) { settings.tmpdir=path; }

void showscoresparm(char *data) { 
    display_high_score(NULL,9999,data); 
    exit(0); 
}

void set_port(char *val)
{ 
    settings.listen_port=atoi(val);
    if (settings.listen_port<=0 || settings.listen_port>32765 ||
	(settings.listen_port<1024 && getuid()!=0)) {
	fprintf(stderr,"%d is an invalid port number.\n",settings.listen_port);
	exit(1);
    }
}

void set_csport(char *val)
{ 
    settings.csport=atoi(val);
    if (settings.csport<=0 || settings.csport>32765 ||
	(settings.csport<1024 && getuid()!=0)) {
	fprintf(stderr,"%d is an invalid csport number.\n",settings.csport);
	exit(1);
    }
}

void newplayer(char *val)
{
    char *di = (char *) getenv("DISPLAY");

    /* If the default display matches one passed via the -display, then
     * we don't want to add to the default display.
     */
    if (di && !strcmp(di, val))
	settings.noadd=1;
    if (add_player(val, NULL, first_map, 0))
        LOG(llevDebug,"%s\n", errmsg);
}


/* Most of this is shamelessly stolen from XSysStats.  But since that is
 * also my program, no problem.
 */
struct Command_Line_Options {
    char    *cmd_option;    /* how it is called on the command line */
    uint8   num_args;	    /* Number or args it takes */
    uint8   pass;           /* What pass this should be processed on. */
    void    (*func)();      /* function to call when we match this.
			     * if num_args is true, than that gets passed
			     * to the function, otherwise nothing is passed
			     */
};

/* The way this system works is pretty simple - parse_args takes
 * the options passed to the program and a pass number.  If an option
 * matches both in name and in pass (and we have enough options), 
 * we call the associated function.  This makes writing a multi
 * pass system very easy, and it is very easy to add in new options.
 */
struct Command_Line_Options options[] = {

/* Pass 1 functions - STuff that can/should be called before we actually
 * initialize any data.
 */
{"-h", 0, 1, help},
{"-v", 0, 1, call_version},
{"-d", 0, 1, set_debug},
{"+d", 0, 1, unset_debug},
{"-mon", 0, 1, set_mondebug},
#ifndef SECURE
{"-lib",1,1, set_libdir},
{"-fontdir", 1, 1, set_fontdir},
{"-maps", 1, 1, set_mapdir},
{"-arch", 1, 1, set_archetypes},
{"-playerdir", 1, 1, set_playerdir},
{"-treasures", 1, 1, set_treasures},
{"-uniquedir", 1, 1, set_uniquedir},
{"-tmpdir", 1, 1, set_tmpdir},
#endif

/* Pass 2 functions.  Most of these could probably be in pass 1, 
 * as they don't require much of anything to bet set up.
 */
#ifdef CHRFONT
{"-chrfont", 1, 2, set_chrfont},
#endif
{"-log", 1, 2, set_logfile},
#ifdef SERVER
{"-listen_port", 1, 2, set_port},
#ifdef ERIC_SERVER
{"-csport", 1, 2, set_csport},
#endif
{"-server", 0, 2, set_servermode},
#endif
{"-p", 0, 2, set_fix_fontpath},
{"-pix", 0, 2, set_pixmaps},
{"-f", 0, 2, set_synchronize},
{"-l", 0, 2, set_initflag},
{"-b", 0, 2, set_blackwhite},
{"-w", 0, 2, set_splitwindow},
{"-split", 0, 2, set_splitwindow},
{"-detach", 0, 2, set_daemon},
#if Xpm_Pix
{"-xpm", 0, 2, set_xpm},
#endif
/* Start of pass 3 information. In theory, by pass 3, all data paths
 * and defaults should have been set up. 
 */
{"-o", 0, 3, compile_info},
#ifdef DUMP_SWITCHES
{"-m", 0, 3, set_dumpmon1},
{"-m2", 0, 3, set_dumpmon2},
{"-m3", 0, 3, set_dumpmon3},
{"-m4", 0, 3, set_dumpmon4},
{"-m5", 0, 3, set_dumpmon5},
{"-m6", 0, 3, set_dumpmon6},
{"-m7", 0, 3, set_dumpmon7},
{"-m8", 0, 3, set_dumpmon8},
#endif
{"-display", 1, 3, newplayer},
{"-s", 0, 3, showscores},
{"-score", 1, 3, showscoresparm},
};


/* Note since this may be called before the library has been set up,
 * we don't use any of crossfires built in logging functions.
 */
static void parse_args(int argc, char *argv[], int pass)
{
    int i, on_arg=1;

    while (on_arg<argc) {
	for (i=0; i<sizeof(options)/sizeof(struct Command_Line_Options); i++) {
	    if (!strcmp(options[i].cmd_option, argv[on_arg])) {
		/* Found a matching option, but should not be processed on
		 * this pass.  Just skip over it
		 */
		if (options[i].pass != pass) {
		    on_arg += options[i].num_args+1;
		    break;
		}
		if (options[i].num_args) {
		    if ((on_arg+options[i].num_args)>=argc) {
			fprintf(stderr,"%s requires an argument.\n", options[i].cmd_option);
			exit(1);
		    }
		    else {
			if (options[i].num_args==1)
				options[i].func(argv[on_arg+1]);
			if (options[i].num_args==2)
				options[i].func(argv[on_arg+1],argv[on_arg+2]);
			on_arg +=options[i].num_args+1;
		    }
		}
		else { /* takes no args */
			options[i].func();
			on_arg++;
		}
		break;
	    }
	}
	if (i==sizeof(options)/sizeof(struct Command_Line_Options)) {
		fprintf(stderr,"Unknown option: %s\n", argv[on_arg]);
		usage();
		exit(1);
	}
    }
}

/*
 * init() is called only once, when starting the program.
 */

void init(int argc, char **argv) {
    char *di=NULL;

    init_done=0;		/* Must be done before init_signal() */
    logfile=stderr;
    parse_args(argc, argv, 1);	/* First arg pass - right now it does
				 * nothing, but in future specifying the
				 * LibDir in this pass would be reasonable*/

    init_library();	/* Must be called early */
    parse_args(argc, argv, 2);
    fprintf(logfile,"Welcome to CrossFire, v%s%s\n",VERSION,PATCH);
    fprintf(logfile,"Copyright (C) 1994 Mark Wedel.\n");
    fprintf(logfile,"Copyright (C) 1992 Frank Tore Johansen.\n");

#ifdef DM_MAIL
    fprintf(logfile,"Maintained locally by: %s\n",DM_MAIL);
    fprintf(logfile,"Questions and bugs should be mailed to above address.\n");
#endif
    (void) umask(0);	/* We don't want to be affected by players' umask */
    SRANDOM(time(NULL));

    init_startup();	/* Write (C), check shutdown/forbid files */
    init_signals();	/* Sets up signal interceptions */
    setup_library();	/* Set up callback function pointers */
    setuperrors();	/* Sets up X11 error-handlers */
    init_commands();	/* Sort command tables */
    read_map_log();	/* Load up the old temp map files */

#ifdef SOUND_EFFECTS
    setup_sounds();	/* Sets up sound effects */
#endif

    parse_args(argc, argv, 3);

    di = (char *) getenv("DISPLAY");
    if((!settings.noadd)&&di!=NULL&& settings.servermode != SERVER_ENABLED) {
	if(add_player(di,NULL,first_map,0))
	    LOG(llevError,"%s\n",errmsg);
    }
    if(first_player==NULL && settings.servermode != SERVER_ENABLED) {
	usage();
	exit(1);
    }
    if (settings.servermode == SERVER_ENABLED && settings.daemonmode)
	logfile = BecomeDaemon(settings.logfilename);
    init_ericserver();
    init_socket();
    reset_sleep();
    init_done=1;
}

void usage() {
  (void) fprintf(logfile,
	"Usage: crossfire [-h] [-<flags>]... -display [display1] -display [display2]...\n");
}

void help() {
    usage();
    printf("Flags:\n");
    printf(" -b          Black/white instead of colors.\n");
#ifdef CHRFONT
/* Eneq(@csd.uu.se): Added -chrfont <fontname> */
    printf(" -chrfont    Followed by fontname; will replace character\n");
    printf("             rep. by that font.\n");
#endif
    printf(" -d          Turns on some debugging.\n");
    printf(" -display <display> Open crossfire on the specified display.  Multiple\n");
    printf("             displays can be given.\n");

    printf(" -f          Only Flush, don't Synchronize.\n");
    printf(" -h          Display this information.\n");
    printf(" -l          Removes local player (Won't try to add player $DISPLAY).\n");
#ifdef DUMP_SWITCHES
    printf(" -m          Lists out suggested experience for all monsters.\n");
    printf(" -m2         Dumps out abilities.\n");
    printf(" -m3         Dumps out artificat information.\n");
    printf(" -m4         Dumps out spell information.\n");
    printf(" -m5         Dumps out skill information.\n");
    printf(" -m6         Dumps out race information.\n");
    printf(" -m7         Dumps out alchemy information.\n");
    printf(" -m8         Dumps out more alchemy information.\n");
#endif
    printf(" -mon        Turns on monster debugging.\n");
    printf(" -o          Prints out info on what was defined at compile time.\n");
    printf(" -p          Don't try to fix the fontpath.\n");
    printf(" -pix        Uses pixmaps instead of fonts.\n");
    printf(" -s          Display the high-score list.\n");
    printf(" -score <name or class> Displays all high scores with matching name/class.\n");
    printf(" -split      Makes split-windows default.\n");
    printf(" -v          Print version and contributors.\n");
#ifdef Xpm_Pix
    printf(" -xpm        Use color pixmaps (XPM) instead of bitmaps or fonts.\n");
#endif
#ifdef SERVER
#ifdef ERIC_SERVER
    printf(" -csport <port> Specifies the port to use for the new client/server code.\n");
#endif
    printf(" -detach     The server will go in the background, closing all\n");
    printf("             connections to the tty.\n");
    printf(" -listen_port <port>  Specifies server listen port number\n");
    printf(" -log <file> Specifies which file to send output to.\n");
    printf("             Only has meaning if -detatch is specified.\n");
    printf(" -server     Puts crossfire into server mode.\n");
#endif
    exit(0);
}

void init_beforeplay() {
  init_archetypes(); /* If not called before, reads all archetypes from file */
  init_artifacts();  /* If not called before, reads all artifacts from file */
  init_spells();     /* If not called before, links archtypes used by spells */
  init_archetype_pointers(); /* Setup global pointers to archetypes */
  init_races();	   /* overwrite race designations using entries in lib/races file */ 
  init_gods();	/* init linked list of gods from archs*/ 
  init_readable();	/* inits useful arrays for readable texts */
#ifdef ALCHEMY
  init_formulae();  /* If not called before, reads formulae from file */
#endif
#ifdef ALLOW_SKILLS
  init_new_exp_system();    /* If not called before, inits experience system */
#endif
#ifdef DUMP_SWITCHES
  switch(settings.dumpvalues) {
  case 1:
    print_monsters();
    exit(0);
  case 2:
    dump_abilities();
    exit(0);
  case 3:
    dump_artifacts();
    exit(0);
  case 4:
    dump_spells();
    exit(0);
  case 5:
    dump_skills();
    exit(0);
  case 6:
    dump_races();
    exit(0);
  case 7:
    dump_alchemy();
    exit(0);
  case 8:
    dump_gods();
    exit(0);
  case 9:
    dump_prod_val_vs_cost();
    exit(0);
  }
#endif
}

void init_startup() {
  char buf[MAX_BUF];
  FILE *fp;
  int comp;

#ifdef SHUTDOWN_FILE
  sprintf(buf,"%s/%s",settings.libdir,SHUTDOWN_FILE);
  if ((fp = open_and_uncompress(buf, 0, &comp)) != NULL) {
    while (fgets(buf, MAX_BUF-1, fp) != NULL)
      printf("%s", buf);
    close_and_delete(fp, comp);
    exit(1);
  }
#endif

  if (forbid_play()) { /* Maybe showing highscore should be allowed? */
      LOG(llevError, "CrossFire: Playing not allowed.\n");
      exit(-1);
  }
}

/*
 * compile_info(): activated with the -o flag.
 * It writes out information on how Imakefile and config.h was configured
 * at compile time.
 */

void compile_info() {
  int i=0;
  printf("Non-standard include files:\n");
#if !defined (__STRICT_ANSI__) || defined (__sun__)
#if !defined (Mips)
  printf("<stdlib.h>\n");
  i=1;
#endif
#if !defined (MACH) && !defined (sony)
  printf("<malloc.h>\n");
  i=1;
#endif
#endif
#ifndef __STRICT_ANSI__
#ifndef MACH
  printf("<memory.h\n");
  i=1;
#endif
#endif
#ifndef sgi
  printf("<sys/timeb.h>\n");
  i=1;
#endif
  if(!i)
    printf("(none)\n");
#ifdef SECURE
  printf("Secure:\t\t<true>\n");
#else
  printf("Secure:\t\t<false>\n");
#endif
#ifdef NO_LOG
  printf("Logging:\t<false>\n");
#else
  printf("Logging:\t<true>\n");
#endif
  printf("Libdir:\t\t%s\n",settings.libdir);
#ifdef PERM_FILE
  printf("Perm file:\t<LIB>/%s\n",PERM_FILE);
#endif
#ifdef SHUTDOWN_FILE
  printf("Shutdown file:\t<LIB>/%s\n",SHUTDOWN_FILE);
#endif
  printf("Save player:\t<true>\n");
  printf("Save mode:\t%4.4o\n",SAVE_MODE);
#ifdef SAVE_HOMEDIR
  printf("Playerdir:\t%s/%s\n",(char *) getenv("HOME"),settings.playerdir);
  printf("Save homedir:\t<true>\n");
#else
  printf("Playerdir:\t<LIB>/%s\n",settings.playerdir);
  printf("Save homedir:\t<false>\n");
#endif
#ifdef LOCK_PLAYER
  printf("Lock player:\t<true>\n");
#else
  printf("Lock player:\t<false>\n");
#endif
#ifdef UNIQUE_ITEMS
  printf("Unique items:\t<true>\n");
  printf("Itemsdir:\t<LIB>/%s\n", settings.uniquedir);
#ifdef LOCK_ITEMS
  printf("Lock items:\t<true>\n");
#else
  printf("Lock items:\t<false>\n");
#endif
#else
  printf("Unique items:\t<false>\n");
#endif
#ifdef USE_CHECKSUM
  printf("Use checksum:\t<true>\n");
#else
  printf("Use checksum:\t<false>\n");
#endif
  printf("Tmpdir:\t\t%s\n",settings.tmpdir);
  printf("Fontdir:\t%s\n",settings.fontdir);
  printf("Compress:\t%s\n",COMPRESS);
  printf("Uncompress:\t%s\n",UNCOMPRESS);
  printf("Map max timeout:\t%d\n",MAP_MAXTIMEOUT);
#ifdef MAP_RESET
  printf("Map reset:\t<true>\n");
#else
  printf("Map reset:\t<false>\n");
#endif
  printf("Max objects:\t%d\n",MAX_OBJECTS);
#ifdef USE_CALLOC
  printf("Use_calloc:\t<true>\n");
#else
  printf("Use_calloc:\t<false>\n");
#endif
#ifdef CHRFONT
  printf("CHRFONT:\t<true>\n");
#else
  printf("CHRFONT:\t<false>\n");
#endif

#ifdef USE_SWAP_STATS
  printf("Use_swap_stats:\t<true>\n");
#else
  printf("Use_swap_stats:\t<false>\n");
#endif
#ifdef SOUND_EFFECTS
  printf("Sound_effects:\t<true>\n");
#else
  printf("Sound_effects:\t<false>\n");
#endif
#ifdef DM_MAIL
  printf("DM mail:\t%s\n",DM_MAIL);
#endif
#ifdef X_EDITOR
  printf("Editor:\t\t%s\n",X_EDITOR);
#endif
#ifdef SERVER
  printf("Server:\t\t<true>\n");
  printf("Port:\t\t%d\n",PORT);
#else
  printf("Server:\t\t<false>\n");
#endif
#ifdef EXPLORE_MODE
  printf("Explore mode:\t<true>\n");
#else
  printf("Explore mode:\t<false>\n");
#endif
#ifdef SHOP_LISTINGS
  printf("Shop listings:\t<true>\n");
#else
  printf("Shop listings:\t<false>\n");
#endif
#ifdef RANDOM_ENCOUNTERS
  printf("Random encounter:\t<true>\n");
#else
  printf("Random encounter:\t<false>\n");
#endif
#ifdef NEW_IMPROVE_WEAPON
  printf("New improve weapon:\t<true>\n");
#else
  printf("New improve weapon:\t<false>\n");
#endif
  printf("Max_time:\t%d\n",MAX_TIME);
  execl("/bin/uname", "uname", "-a", NULL);
  LOG(llevError, "Opps, should't have gotten here.");
  perror("execl");
  exit(-1);
}

/* Signal handlers: */

void rec_sigsegv(int i) {
  LOG(llevError,"\nSIGSEGV received.\n");
  fatal_signal(1, 1);
}

void rec_sigint(int i) {
  LOG(llevError,"\nSIGINT received.\n");
  fatal_signal(0, 1);
}

void rec_sighup(int i) {
  LOG(llevError,"\nSIGHUP received\n");
  fatal_signal(0, 1);
}

void rec_sigquit(int i) {
  LOG(llevError,"\nSIGQUIT received\n");
  fatal_signal(1, 1);
}

void rec_sigpipe(int i) {

/* Keep running if we receive a sigpipe.  Crossfire should really be able
 * to handle this signal (at least at some point in the future if not
 * right now).  By causing a dump right when it is received, it is not
 * doing much good.  However, if it core dumps later on, at least it can
 * be looked at later on, and maybe fix the problem that caused it to
 * dump core.  There is no reason that SIGPIPES should be fatal
 */
#if 1
  LOG(llevError,"\nReceived SIGPIPE, ignoring...\n");
  signal(SIGPIPE,rec_sigpipe);/* hocky-pux clears signal handlers */
#else
  LOG(llevError,"\nSIGPIPE received, not ignoring...\n");
  fatal_signal(1, 1); /*Might consider to uncomment this line */
#endif
}

void rec_sigbus(int i) {
#ifdef SIGBUS
  LOG(llevError,"\nSIGBUS received\n");
  fatal_signal(1, 1);
#endif
}

void rec_sigterm(int i) {
  LOG(llevError,"\nSIGTERM received\n");
  fatal_signal(0, 1);
}

void fatal_signal(int make_core, int close_sockets) {
  if(init_done) {
    emergency_save(0);
    clean_tmp_files();
    if(close_sockets)
      close_all_sockets();
  }
  if(make_core)
    abort();
  exit(0);
}

void init_signals() {
  signal(SIGHUP,rec_sighup);
  signal(SIGINT,rec_sigint);
  signal(SIGQUIT,rec_sigquit);
  signal(SIGSEGV,rec_sigsegv);
  signal(SIGPIPE,rec_sigpipe);
#ifdef SIGBUS
  signal(SIGBUS,rec_sigbus);
#endif
  signal(SIGTERM,rec_sigterm);
}

/*
 * init_library: Set up the function pointers which will point
 * back from the library into the server.
 */
void setup_library() {
  set_emergency_save(emergency_save);
  set_clean_tmp_files(clean_tmp_files);
  set_fix_auto_apply(fix_auto_apply);
  set_remove_friendly_object(remove_friendly_object);
  set_process_active_maps(process_active_maps);
  set_update_buttons(update_buttons);
  set_draw_info(new_draw_info);
  set_draw_stats(draw_stats);
  set_draw_inventory(draw_inventory);
  set_draw_look(draw_look);
  set_apply(apply);
  set_draw(draw);
  set_monster_check_apply(monster_check_apply);
  set_draw_inventory_faces(draw_inventory_faces);
  set_draw_look_faces(draw_look_faces);
  set_move_teleporter(move_teleporter);
  set_move_creator(move_creator);
  set_trap_adjust(trap_adjust);
  set_esrv_send_item(esrv_send_item);
  set_esrv_del_item(esrv_del_item);
/*  set_init_blocksview_players(init_blocksview_players); */
  set_info_map(new_info_map);
}

/* init_races() - reads the races file in the lib/ directory, then
 * overwrites old 'race' entries. This routine allow us to quickly
 * re-configure the 'alignment' of monsters, objects. Useful for
 * putting together lists of creatures, etc that belong to gods.
 */
 
void init_races () {
  FILE *file;
  char race[MAX_BUF], fname[MAX_BUF], buf[MAX_BUF], *cp, variable[MAX_BUF];
  archetype *mon=NULL;
  static int init_done=0;

  if (init_done) return;
  init_done=1;
  first_race=NULL;

  sprintf(fname,"%s/races",settings.libdir);
  LOG(llevDebug, "Reading races from %s...",fname);
  if(! (file=fopen(fname,"r"))) {
        perror(fname); return;
  }

  while(fgets(buf,MAX_BUF,file)!=NULL) {
    int set_race=1,set_list=1;
    if(*buf=='#') continue;
    if((cp=strchr(buf,'\n'))!=NULL)
      *cp='\0';
    cp=buf;
    while(*cp==' '||*cp=='!'||*cp=='@') { 
      if(*cp=='!') set_race=0;
      if(*cp=='@') set_list=0;
      cp++;
    }
    if(sscanf(cp,"RACE %s",variable)) { /* set new race value */
        strcpy(race,variable);
    } else {
	char *cp1;
	/* Take out beginning spaces */
	for (cp1 = cp; *cp1==' '; cp1++);
	/* Remove newline and trailing spaces */
	for (cp1 = cp + strlen(cp) -1; *cp1 == '\n' || *cp1 == ' '; cp1 --) {
		*cp1='\0';
		if (cp==cp1) break;
	}
	
	if (cp[strlen(cp)-1]=='\n') cp[strlen(cp)-1]='\0';
        /* set creature race to race value */
        if((mon=find_archetype(cp))==NULL)
           LOG(llevError,"\nCreature %s in race file lacks archetype",cp);
        else {
           if(set_race&&(!mon->clone.race||strcmp(mon->clone.race,race))) {
                if(mon->clone.race) {
                   LOG(llevDebug,"\n Resetting race to %s from %s for archetype %s",
                        race,mon->clone.race,mon->name);
                  free_string(mon->clone.race);
                }
                mon->clone.race=add_string(race);
           }
           /* if the arch is a monster, add it to the race list */
           if(set_list&&QUERY_FLAG(&mon->clone,FLAG_MONSTER))
                add_to_racelist(race, &mon->clone);
        }
    }
  }
  fclose(file);
    LOG(llevDebug,"done.\n");
}

void dump_races()
{ 
    racelink *list;
    objectlink *tmp;
    for(list=first_race;list;list=list->next) {
      fprintf(stderr,"\nRACE %s:\t",list->name); 
      for(tmp=list->member;tmp;tmp=tmp->next)
        fprintf(stderr,"%s(%d), ",tmp->ob->arch->name,tmp->ob->level);
    }
    fprintf(stderr,"\n");
}

void add_to_racelist (char *race_name, object *op) {
  racelink *race;
 
  if(!op||!race_name) return;
  race=find_racelink(race_name);
 
  if(!race) { /* add in a new race list */
    race = get_racelist();
    race->next = first_race;
    first_race = race;
    race->name=add_string(race_name);
  }
 
  if(race->member->ob) {
    objectlink *tmp = get_objectlink();
    tmp->next=race->member;
    race->member = tmp;
  }
  race->nrof++;
  race->member->ob = op;
}

racelink * get_racelist ( ) {
  racelink *list;
 
  list = (racelink *) malloc(sizeof(racelink ));
  list->name=NULL;
  list->nrof=0;
  list->member=get_objectlink();
  list->next=NULL;
 
  return list;
}
 
racelink * find_racelink( char *name ) {
  racelink *test=NULL;
 
  if(name&&first_race)
    for(test=first_race;test&&test!=test->next;test=test->next)
       if(!test->name||!strcmp(name,test->name)) break;
 
  return test;
}

