Crossfire Server, Branch 1.12  R12190
init.c
Go to the documentation of this file.
00001 /*
00002  * static char *rcsid_init_c =
00003  *   "$Id: init.c 11578 2009-02-23 22:02:27Z lalo $";
00004  */
00005 
00006 /*
00007     CrossFire, A Multiplayer game for X-windows
00008 
00009     Copyright (C) 2002 Mark Wedel & Crossfire Development Team
00010     Copyright (C) 1992 Frank Tore Johansen
00011 
00012     This program is free software; you can redistribute it and/or modify
00013     it under the terms of the GNU General Public License as published by
00014     the Free Software Foundation; either version 2 of the License, or
00015     (at your option) any later version.
00016 
00017     This program is distributed in the hope that it will be useful,
00018     but WITHOUT ANY WARRANTY; without even the implied warranty of
00019     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00020     GNU General Public License for more details.
00021 
00022     You should have received a copy of the GNU General Public License
00023     along with this program; if not, write to the Free Software
00024     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00025 
00026     The authors can be reached via e-mail at crossfire-devel@real-time.com
00027 */
00028 
00034 #include <global.h>
00035 #include <loader.h>
00036 #include <version.h>
00037 #ifndef __CEXTRACT__
00038 #include <sproto.h>
00039 #endif
00040 
00041 /* Needed for strcasecmp(). */
00042 #include <strings.h>
00043 
00044 static void help(void);
00045 static void usage(void);
00046 static void init_beforeplay(void);
00047 static void init_startup(void);
00048 static void compile_info(void);
00049 static void init_signals(void);
00050 static void init_races(void);
00051 static void dump_races(void);
00052 static void add_to_racelist(const char *race_name, object *op);
00053 static racelink *get_racelist(void);
00054 static void fatal_signal(int make_core);
00055 
00057 static char default_daemon_log[] = "logfile";
00058 
00059 static void set_logfile(char *val) {
00060     settings.logfilename = val;
00061 }
00062 
00063 static void call_version(void) {
00064     version(NULL);
00065     exit(0);
00066 }
00067 
00068 static void showscores(void) {
00069     display_high_score(NULL, 9999, NULL);
00070     exit(0);
00071 }
00072 
00073 static void set_debug(void) {
00074     settings.debug = llevDebug;
00075 }
00076 
00077 static void unset_debug(void) {
00078     settings.debug = llevInfo;
00079 }
00080 
00081 static void set_mondebug(void) {
00082     settings.debug = llevMonster;
00083 }
00084 
00085 static void set_dumpmon1(void) {
00086     settings.dumpvalues = 1;
00087 }
00088 
00089 static void set_dumpmon2(void) {
00090     settings.dumpvalues = 2;
00091 }
00092 
00093 static void set_dumpmon3(void) {
00094     settings.dumpvalues = 3;
00095 }
00096 
00097 static void set_dumpmon4(void) {
00098     settings.dumpvalues = 4;
00099 }
00100 
00101 static void set_dumpmon5(void) {
00102     settings.dumpvalues = 5;
00103 }
00104 
00105 static void set_dumpmon6(void) {
00106     settings.dumpvalues = 6;
00107 }
00108 
00109 static void set_dumpmon7(void) {
00110     settings.dumpvalues = 7;
00111 }
00112 
00113 static void set_dumpmon8(void) {
00114     settings.dumpvalues = 8;
00115 }
00116 
00117 static void set_dumpmon9(void) {
00118     settings.dumpvalues = 9;
00119 }
00120 
00121 static void set_dumpmont(const char *name) {
00122     settings.dumpvalues = 10;
00123     settings.dumparg = name;
00124 }
00125 
00126 static void set_daemon(void) {
00127     settings.daemonmode = 1;
00128     if (settings.logfilename[0] == '\0') {
00129         settings.logfilename = default_daemon_log;
00130     }
00131 }
00132 
00133 static void set_datadir(const char *path) {
00134     settings.datadir = path;
00135 }
00136 
00137 static void set_confdir(const char *path) {
00138     settings.confdir = path;
00139 }
00140 
00141 static void set_localdir(const char *path) {
00142     settings.localdir = path;
00143 }
00144 
00145 static void set_mapdir(const char *path) {
00146     settings.mapdir = path;
00147 }
00148 
00149 static void set_archetypes(const char *path) {
00150     settings.archetypes = path;
00151 }
00152 
00153 static void set_regions(const char *path) {
00154     settings.regions = path;
00155 }
00156 
00157 static void set_treasures(const char *path) {
00158     settings.treasures = path;
00159 }
00160 
00161 static void set_uniquedir(const char *path) {
00162     settings.uniquedir = path;
00163 }
00164 
00165 static void set_templatedir(const char *path) {
00166     settings.templatedir = path;
00167 }
00168 
00169 static void set_playerdir(const char *path) {
00170     settings.playerdir = path;
00171 }
00172 
00173 static void set_tmpdir(const char *path) {
00174     settings.tmpdir = path;
00175 }
00176 
00177 static void free_races(void);
00178 
00179 static void free_materials(void);
00180 
00181 static void showscoresparm(const char *data) {
00182     display_high_score(NULL, 9999, data);
00183     exit(0);
00184 }
00185 
00192 static void set_csport(const char *val) {
00193     settings.csport = atoi(val);
00194 #ifndef WIN32 /* ***win32: set_csport: we remove csport error secure check here, do this later */
00195     if (settings.csport <= 0
00196     || settings.csport > 32765
00197     || (settings.csport < 1024 && getuid() != 0)) {
00198         LOG(llevError, "%d is an invalid csport number.\n", settings.csport);
00199         exit(1);
00200     }
00201 #endif /* win32 */
00202 }
00203 
00206 typedef void (*cmdlinefunc_args0)(void);
00207 typedef void (*cmdlinefunc_args1)(const char* arg1);
00208 typedef void (*cmdlinefunc_args2)(const char* arg1, const char* arg2);
00216 struct Command_Line_Options {
00217     const char *cmd_option; 
00218     uint8 num_args;         
00219     uint8 pass;             
00220     void (*func)();         
00224 };
00225 
00234 static struct Command_Line_Options options[] = {
00238     { "-h", 0, 1, help },
00239     /* Honor -help also, since it is somewhat common */
00240     { "-help", 0, 1, help },
00241     { "-v", 0, 1, call_version },
00242     { "-d", 0, 1, set_debug },
00243     { "+d", 0, 1, unset_debug },
00244     { "-mon", 0, 1, set_mondebug },
00245     { "-data", 1, 1, set_datadir },
00246     { "-conf", 1, 1, set_confdir },
00247     { "-local", 1, 1, set_localdir },
00248     { "-maps", 1, 1, set_mapdir },
00249     { "-arch", 1, 1, set_archetypes },
00250     { "-regions", 1, 1, set_regions },
00251     { "-playerdir", 1, 1, set_playerdir },
00252     { "-treasures", 1, 1, set_treasures },
00253     { "-uniquedir", 1, 1, set_uniquedir },
00254     { "-templatedir", 1, 1, set_templatedir },
00255     { "-tmpdir", 1, 1, set_tmpdir },
00256     { "-log", 1, 1, set_logfile },
00257     { "-detach", 0, 1, set_daemon },
00258 
00259 #ifdef WIN32
00260     /* Windows service stuff */
00261     { "-regsrv", 0, 1, service_register },
00262     { "-unregsrv", 0, 1, service_unregister },
00263     { "-srv", 0, 1, service_handle },
00264 #endif
00265 
00269     { "-csport", 1, 2, set_csport },
00270 
00274     { "-o", 0, 3, compile_info },
00275     { "-m", 0, 3, set_dumpmon1 },
00276     { "-m2", 0, 3, set_dumpmon2 },
00277     { "-m3", 0, 3, set_dumpmon3 },
00278     { "-m4", 0, 3, set_dumpmon4 },
00279     { "-m5", 0, 3, set_dumpmon5 },
00280     { "-m6", 0, 3, set_dumpmon6 },
00281     { "-m7", 0, 3, set_dumpmon7 },
00282     { "-m8", 0, 3, set_dumpmon8 },
00283     { "-m9", 0, 3, set_dumpmon9 },
00284     { "-mt", 1, 3, set_dumpmont },
00285     { "-mexp", 0, 3, dump_experience },
00286     { "-s", 0, 3, showscores },
00287     { "-score", 1, 3, showscoresparm }
00288 };
00289 
00304 static void parse_args(int argc, char *argv[], int pass) {
00305     size_t i;
00306     int on_arg = 1;
00307 
00308     while (on_arg < argc) {
00309         for (i = 0; i < sizeof(options)/sizeof(struct Command_Line_Options); i++) {
00310             if (!strcmp(options[i].cmd_option, argv[on_arg])) {
00311                 /* Found a matching option, but should not be processed on
00312                  * this pass.  Just skip over it
00313                  */
00314                 if (options[i].pass != pass) {
00315                     on_arg += options[i].num_args+1;
00316                     break;
00317                 }
00318                 if (options[i].num_args) {
00319                     if ((on_arg+options[i].num_args) >= argc) {
00320                         fprintf(stderr, "%s requires an argument.\n", options[i].cmd_option);
00321                         exit(1);
00322                     } else {
00323                         if (options[i].num_args == 1)
00324                             ((cmdlinefunc_args1)options[i].func)(argv[on_arg+1]);
00325                         if (options[i].num_args == 2)
00326                             ((cmdlinefunc_args2)options[i].func)(argv[on_arg+1], argv[on_arg+2]);
00327                         on_arg += options[i].num_args+1;
00328                     }
00329                 } else { /* takes no args */
00330                     ((cmdlinefunc_args0)options[i].func)();
00331                     on_arg++;
00332                 }
00333                 break;
00334             }
00335         }
00336         if (i == sizeof(options)/sizeof(struct Command_Line_Options)) {
00337             fprintf(stderr, "Unknown option: %s\n", argv[on_arg]);
00338             usage();
00339             exit(1);
00340         }
00341     }
00342 }
00343 
00345 materialtype_t *materialt;
00346 
00355 static materialtype_t *get_empty_mat(void) {
00356     materialtype_t *mt;
00357     int i;
00358 
00359     mt = (materialtype_t *)malloc(sizeof(materialtype_t));
00360     if (mt == NULL)
00361         fatal(OUT_OF_MEMORY);
00362     mt->name = NULL;
00363     mt->description = NULL;
00364     for (i = 0; i < NROFATTACKS; i++) {
00365         mt->save[i] = 0;
00366         mt->mod[i] = 0;
00367     }
00368     mt->chance = 0;
00369     mt->difficulty = 0;
00370     mt->magic = 0;
00371     mt->damage = 0;
00372     mt->wc = 0;
00373     mt->ac = 0;
00374     mt->sp = 0;
00375     mt->weight = 100;
00376     mt->value = 100;
00377     mt->next = NULL;
00378     return mt;
00379 }
00380 
00385 static void load_materials(void) {
00386     char buf[MAX_BUF], filename[MAX_BUF], *cp, *next;
00387     FILE *fp;
00388     materialtype_t *mt;
00389     int i, value;
00390 
00391     snprintf(filename, sizeof(filename), "%s/materials", settings.datadir);
00392     LOG(llevDebug, "Reading material type data from %s...\n", filename);
00393     if ((fp = fopen(filename, "r")) == NULL) {
00394         LOG(llevError, "Cannot open %s for reading\n", filename);
00395         mt = get_empty_mat();
00396         mt->next = NULL;
00397         materialt = mt;
00398         return;
00399     }
00400     mt = get_empty_mat();
00401     materialt = mt;
00402     while (fgets(buf, MAX_BUF, fp) != NULL) {
00403         if (*buf == '#')
00404             continue;
00405         if ((cp = strchr(buf, '\n')) != NULL)
00406             *cp = '\0';
00407         cp = buf;
00408         while (*cp == ' ') /* Skip blanks */
00409             cp++;
00410         if (!strncmp(cp, "name", 4)) {
00411             /* clean up the previous entry */
00412             if (mt->next != NULL) {
00413                 if (mt->description == NULL)
00414                     mt->description = add_string(mt->name);
00415                 mt = mt->next;
00416             }
00417             mt->next = get_empty_mat();
00418             mt->name = add_string(strchr(cp, ' ')+1);
00419         } else if (!strncmp(cp, "description", 11)) {
00420             mt->description = add_string(strchr(cp, ' ')+1);
00421         } else if (sscanf(cp, "material %d", &value)) {
00422             mt->material = value;
00423         } else if (!strncmp(cp, "saves", 5)) {
00424             cp = strchr(cp, ' ')+1;
00425             for (i = 0; i < NROFATTACKS; i++) {
00426                 if (cp == NULL) {
00427                     mt->save[i] = 0;
00428                     continue;
00429                 }
00430                 if ((next = strchr(cp, ',')) != NULL)
00431                     *(next++) = '\0';
00432                 sscanf(cp, "%d", &value);
00433                 mt->save[i] = (sint8)value;
00434                 cp = next;
00435             }
00436         } else if (!strncmp(cp, "mods", 4)) {
00437             cp = strchr(cp, ' ')+1;
00438             for (i = 0; i < NROFATTACKS; i++) {
00439                 if (cp == NULL) {
00440                     mt->save[i] = 0;
00441                     continue;
00442                 }
00443                 if ((next = strchr(cp, ',')) != NULL)
00444                     *(next++) = '\0';
00445                 sscanf(cp, "%d", &value);
00446                 mt->mod[i] = (sint8)value;
00447                 cp = next;
00448             }
00449         } else if (sscanf(cp, "chance %d\n", &value)) {
00450             mt->chance = (sint8)value;
00451         } else if (sscanf(cp, "diff %d\n", &value)) {
00452             mt->difficulty = (sint8)value;
00453         } else if (sscanf(cp, "magic %d\n", &value)) {
00454             mt->magic = (sint8)value;
00455         } else if (sscanf(cp, "damage %d\n", &value)) {
00456             mt->damage = (sint8)value;
00457         } else if (sscanf(cp, "wc %d\n", &value)) {
00458             mt->wc = (sint8)value;
00459         } else if (sscanf(cp, "ac %d\n", &value)) {
00460             mt->ac = (sint8)value;
00461         } else if (sscanf(cp, "sp %d\n", &value)) {
00462             mt->sp = (sint8)value;
00463         } else if (sscanf(cp, "weight %d\n", &value)) {
00464             mt->weight = value;
00465         } else if (sscanf(cp, "value %d\n", &value)) {
00466             mt->value = value;
00467         }
00468     }
00469     if (mt->next) {
00470         free(mt->next);
00471         mt->next = NULL;
00472     }
00473     LOG(llevDebug, "Done.\n");
00474     fclose(fp);
00475 
00476 }
00477 
00481 static void free_materials(void) {
00482     materialtype_t *next;
00483 
00484     while (materialt) {
00485         next = materialt->next;
00486         free(materialt);
00487         materialt = next;
00488     }
00489     materialt = NULL;
00490 }
00491 
00497 static void load_settings(void) {
00498     char buf[MAX_BUF], *cp;
00499     int has_val, comp;
00500     FILE *fp;
00501 
00502     snprintf(buf, sizeof(buf), "%s/settings", settings.confdir);
00503 
00504     /* We don't require a settings file at current time, but down the road,
00505      * there will probably be so many values that not having a settings file
00506      * will not be a good thing.
00507      */
00508     if ((fp = open_and_uncompress(buf, 0, &comp)) == NULL) {
00509         LOG(llevError, "Warning: No settings file found\n");
00510         return;
00511     }
00512     while (fgets(buf, MAX_BUF-1, fp) != NULL) {
00513         if (buf[0] == '#')
00514             continue;
00515         /* eliminate newline */
00516         if ((cp = strrchr(buf, '\n')) != NULL)
00517             *cp = '\0';
00518 
00519         /* Skip over empty lines */
00520         if (buf[0] == 0)
00521             continue;
00522 
00523         /* Skip all the spaces and set them to nulls.  If not space,
00524          * set cp to "" to make strcpy's and the like easier down below.
00525          */
00526         if ((cp = strchr(buf, ' ')) != NULL) {
00527             while (*cp == ' ')
00528                 *cp++ = 0;
00529             has_val = 1;
00530         } else {
00531             cp = "";
00532             has_val = 0;
00533         }
00534 
00535         if (!strcasecmp(buf, "metaserver_notification")) {
00536             if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
00537                 settings.meta_on = TRUE;
00538             } else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
00539                 settings.meta_on = FALSE;
00540             } else {
00541                 LOG(llevError, "load_settings: Unknown value for metaserver_notification: %s\n", cp);
00542             }
00543         } else if (!strcasecmp(buf, "metaserver_server")) {
00544             if (has_val)
00545                 strcpy(settings.meta_server, cp);
00546             else
00547                 LOG(llevError, "load_settings: metaserver_server must have a value.\n");
00548         } else if (!strcasecmp(buf, "motd")) {
00549             if (has_val)
00550                 strcpy(settings.motd, cp);
00551             else
00552                 LOG(llevError, "load_settings: motd must have a value.\n");
00553         } else if (!strcasecmp(buf, "dm_mail")) {
00554             if (has_val)
00555                 strcpy(settings.dm_mail, cp);
00556             else
00557                 LOG(llevError, "load_settings: dm_mail must have a value.\n");
00558         } else if (!strcasecmp(buf, "metaserver_host")) {
00559             if (has_val)
00560                 strcpy(settings.meta_host, cp);
00561             else
00562                 LOG(llevError, "load_settings: metaserver_host must have a value.\n");
00563         } else if (!strcasecmp(buf, "port")) {
00564             set_csport(cp);
00565         } else if (!strcasecmp(buf, "metaserver_port")) {
00566             int port = atoi(cp);
00567 
00568             if (port < 1 || port > 65535)
00569                 LOG(llevError, "load_settings: metaserver_port must be between 1 and 65535, %d is invalid\n", port);
00570             else
00571                 settings.meta_port = port;
00572         } else if (!strcasecmp(buf, "metaserver_comment")) {
00573             strcpy(settings.meta_comment, cp);
00574         } else if (!strcasecmp(buf, "worldmapstartx")) {
00575             int size = atoi(cp);
00576 
00577             if (size < 0)
00578                 LOG(llevError, "load_settings: worldmapstartx must be at least 0, %d is invalid\n", size);
00579             else
00580                 settings.worldmapstartx = size;
00581         } else if (!strcasecmp(buf, "worldmapstarty")) {
00582             int size = atoi(cp);
00583 
00584             if (size < 0)
00585                 LOG(llevError, "load_settings: worldmapstarty must be at least 0, %d is invalid\n", size);
00586             else
00587                 settings.worldmapstarty = size;
00588         } else if (!strcasecmp(buf, "worldmaptilesx")) {
00589             int size = atoi(cp);
00590 
00591             if (size < 1)
00592                 LOG(llevError, "load_settings: worldmaptilesx must be greater than 1, %d is invalid\n", size);
00593             else
00594                 settings.worldmaptilesx = size;
00595         } else if (!strcasecmp(buf, "worldmaptilesy")) {
00596             int size = atoi(cp);
00597 
00598             if (size < 1)
00599                 LOG(llevError, "load_settings: worldmaptilesy must be greater than 1, %d is invalid\n", size);
00600             else
00601                 settings.worldmaptilesy = size;
00602         } else if (!strcasecmp(buf, "worldmaptilesizex")) {
00603             int size = atoi(cp);
00604 
00605             if (size < 1)
00606                 LOG(llevError, "load_settings: worldmaptilesizex must be greater than 1, %d is invalid\n", size);
00607             else
00608                 settings.worldmaptilesizex = size;
00609         } else if (!strcasecmp(buf, "worldmaptilesizey")) {
00610             int size = atoi(cp);
00611 
00612             if (size < 1)
00613                 LOG(llevError, "load_settings: worldmaptilesizey must be greater than 1, %d is invalid\n", size);
00614             else
00615                 settings.worldmaptilesizey = size;
00616         } else if (!strcasecmp(buf, "fastclock")) {
00617             int lev = atoi(cp);
00618 
00619             if (lev < 0)
00620                 LOG(llevError, "load_settings: fastclock must be at least 0, %d is invalid\n", lev);
00621             else
00622                 settings.fastclock = lev;
00623         } else if (!strcasecmp(buf, "not_permadeth")) {
00624             if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
00625                 settings.not_permadeth = TRUE;
00626             } else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
00627                 settings.not_permadeth = FALSE;
00628             } else {
00629                 LOG(llevError, "load_settings: Unknown value for not_permadeth: %s\n", cp);
00630             }
00631         } else if (!strcasecmp(buf, "resurrection")) {
00632             if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
00633                 settings.resurrection = TRUE;
00634             } else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
00635                 settings.resurrection = FALSE;
00636             } else {
00637                 LOG(llevError, "load_settings: Unknown value for resurrection: %s\n", cp);
00638             }
00639         } else if (!strcasecmp(buf, "set_title")) {
00640             if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
00641                 settings.set_title = TRUE;
00642             } else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
00643                 settings.set_title = FALSE;
00644             } else {
00645                 LOG(llevError, "load_settings: Unknown value for set_title: %s\n", cp);
00646             }
00647         } else if (!strcasecmp(buf, "search_items")) {
00648             if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
00649                 settings.search_items = TRUE;
00650             } else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
00651                 settings.search_items = FALSE;
00652             } else {
00653                 LOG(llevError, "load_settings: Unknown value for search_items: %s\n", cp);
00654             }
00655         } else if (!strcasecmp(buf, "spell_encumbrance")) {
00656             if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
00657                 settings.spell_encumbrance = TRUE;
00658             } else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
00659                 settings.spell_encumbrance = FALSE;
00660             } else {
00661                 LOG(llevError, "load_settings: Unknown value for spell_encumbrance: %s\n", cp);
00662             }
00663         } else if (!strcasecmp(buf, "spell_failure_effects")) {
00664             if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
00665                 settings.spell_failure_effects = TRUE;
00666             } else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
00667                 settings.spell_failure_effects = FALSE;
00668             } else {
00669                 LOG(llevError, "load_settings: Unknown value for spell_failure_effects: %s\n", cp);
00670             }
00671         } else if (!strcasecmp(buf, "casting_time")) {
00672             if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
00673                 settings.casting_time = TRUE;
00674             } else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
00675                 settings.casting_time = FALSE;
00676             } else {
00677                 LOG(llevError, "load_settings: Unknown value for casting_time: %s\n", cp);
00678             }
00679         } else if (!strcasecmp(buf, "real_wiz")) {
00680             if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
00681                 settings.real_wiz = TRUE;
00682             } else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
00683                 settings.real_wiz = FALSE;
00684             } else {
00685                 LOG(llevError, "load_settings: Unknown value for real_wiz: %s\n", cp);
00686             }
00687         } else if (!strcasecmp(buf, "recycle_tmp_maps")) {
00688             if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
00689                 settings.recycle_tmp_maps = TRUE;
00690             } else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
00691                 settings.recycle_tmp_maps = FALSE;
00692             } else {
00693                 LOG(llevError, "load_settings: Unknown value for recycle_tmp_maps: %s\n", cp);
00694             }
00695         } else if (!strcasecmp(buf, "explore_mode")) {
00696             if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
00697                 settings.explore_mode = TRUE;
00698             } else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
00699                 settings.explore_mode = FALSE;
00700             } else {
00701                 LOG(llevError, "load_settings: Unknown value for explore_mode: %s\n", cp);
00702             }
00703         } else if (!strcasecmp(buf, "who_format")) {
00704             if (has_val)
00705                 strcpy(settings.who_format, cp);
00706         } else if (!strcasecmp(buf, "who_wiz_format")) {
00707             if (has_val)
00708                 strcpy(settings.who_wiz_format, cp);
00709         } else if (!strcasecmp(buf, "spellpoint_level_depend")) {
00710             if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
00711                 settings.spellpoint_level_depend = TRUE;
00712             } else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
00713                 settings.spellpoint_level_depend = FALSE;
00714             } else {
00715                 LOG(llevError, "load_settings: Unknown value for spellpoint_level_depend: %s\n", cp);
00716             }
00717         } else if (!strcasecmp(buf, "stat_loss_on_death")) {
00718             if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
00719                 settings.stat_loss_on_death = TRUE;
00720             } else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
00721                 settings.stat_loss_on_death = FALSE;
00722             } else {
00723                 LOG(llevError, "load_settings: Unknown value for stat_loss_on_death: %s\n", cp);
00724             }
00725         } else if (!strcasecmp(buf, "use_permanent_experience")) {
00726             LOG(llevError, "use_permanent_experience is deprecated, usepermenent_experience_percentage instead\n");
00727         } else if (!strcasecmp(buf, "permanent_experience_percentage")) {
00728             int val = atoi(cp);
00729             if (val < 0 || val > 100)
00730                 LOG(llevError, "load_settings: permenent_experience_percentage must be between 0 and 100, %d is invalid\n", val);
00731             else
00732                 settings.permanent_exp_ratio = val;
00733         } else if (!strcasecmp(buf, "death_penalty_percentage")) {
00734             int val = atoi(cp);
00735             if (val < 0 || val > 100)
00736                 LOG(llevError, "load_settings: death_penalty_percentage must be between 0 and 100, %d is invalid\n", val);
00737             else
00738                 settings.death_penalty_ratio = val;
00739         } else if (!strcasecmp(buf, "death_penalty_levels")) {
00740             int val = atoi(cp);
00741             if (val < 0 || val > 255)
00742                 LOG(llevError, "load_settings: death_penalty_levels can not be negative, %d is invalid\n", val);
00743             else
00744                 settings.death_penalty_level = val;
00745         } else if (!strcasecmp(buf, "balanced_stat_loss")) {
00746             if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
00747                 settings.balanced_stat_loss = TRUE;
00748             } else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
00749                 settings.balanced_stat_loss = FALSE;
00750             } else {
00751                 LOG(llevError, "load_settings: Unknown value for balanced_stat_loss: %s\n", cp);
00752             }
00753         } else if (!strcasecmp(buf, "simple_exp")) {
00754             if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
00755                 settings.simple_exp = TRUE;
00756             } else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
00757                 settings.simple_exp = FALSE;
00758             } else {
00759                 LOG(llevError, "load_settings: Unknown value for simple_exp: %s\n", cp);
00760             }
00761         } else if (!strcasecmp(buf, "item_power_factor")) {
00762             float tmp = atof(cp);
00763             if (tmp < 0)
00764                 LOG(llevError, "load_settings: item_power_factor must be a positive number (%f < 0)\n", tmp);
00765             else
00766                 settings.item_power_factor = tmp;
00767         } else if (!strcasecmp(buf, "pk_luck_penalty")) {
00768             sint16 val = atoi(cp);
00769 
00770             if (val < -100 || val > 100)
00771                 LOG(llevError, "load_settings: pk_luck_penalty must be between -100 and 100, %d is invalid\n", val);
00772             else
00773                 settings.pk_luck_penalty = val;
00774         } else if (!strcasecmp(buf, "set_friendly_fire")) {
00775             int val = atoi(cp);
00776 
00777             if (val < 1 || val > 100)
00778                 LOG(llevError, "load_settings: set_friendly_fire must be between 1 an 100, %d is invalid\n", val);
00779             else
00780                 settings.set_friendly_fire = val;
00781         } else if (!strcasecmp(buf, "armor_max_enchant")) {
00782             int max_e = atoi(cp);
00783             if (max_e <= 0)
00784                 LOG(llevError, "load_settings: armor_max_enchant is %d\n", max_e);
00785             else
00786                 settings.armor_max_enchant = max_e;
00787         } else if (!strcasecmp(buf, "armor_weight_reduction")) {
00788             int wr = atoi(cp);
00789             if (wr < 0)
00790                 LOG(llevError, "load_settings: armor_weight_reduction is %d\n", wr);
00791             else
00792                 settings.armor_weight_reduction = wr;
00793         } else if (!strcasecmp(buf, "armor_weight_linear")) {
00794             if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
00795                 settings.armor_weight_linear = TRUE;
00796             } else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
00797                 settings.armor_weight_linear = FALSE;
00798             } else {
00799                 LOG(llevError, "load_settings: unknown value for armor_weight_linear: %s\n", cp);
00800             }
00801 
00802         } else if (!strcasecmp(buf, "armor_speed_improvement")) {
00803             int wr = atoi(cp);
00804             if (wr < 0)
00805                 LOG(llevError, "load_settings: armor_speed_improvement is %d\n", wr);
00806             else
00807                 settings.armor_speed_improvement = wr;
00808         } else if (!strcasecmp(buf, "armor_speed_linear")) {
00809             if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
00810                 settings.armor_speed_linear = TRUE;
00811             } else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
00812                 settings.armor_speed_linear = FALSE;
00813             } else {
00814                 LOG(llevError, "load_settings: unknown value for armor_speed_linear: %s\n", cp);
00815             }
00816 
00817         } else if (!strcasecmp(buf, "no_player_stealing")) {
00818             if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
00819                 settings.no_player_stealing = TRUE;
00820             } else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
00821                 settings.no_player_stealing = FALSE;
00822             } else {
00823                 LOG(llevError, "load_settings: unknown value for no_player_stealing: %s\n", cp);
00824             }
00825 
00826         } else if (!strcasecmp(buf, "create_home_portals")) {
00827             if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
00828                 settings.create_home_portals = TRUE;
00829             } else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
00830                 settings.create_home_portals = FALSE;
00831             } else {
00832                 LOG(llevError, "load_settings: unknown value for create_home_portals: %s\n", cp);
00833             }
00834         } else if (!strcasecmp(buf, "personalized_blessings")) {
00835             if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
00836                 settings.personalized_blessings = TRUE;
00837             } else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
00838                 settings.personalized_blessings = FALSE;
00839             } else {
00840                 LOG(llevError, "load_settings: unknown value for personalized_blessings: %s\n", cp);
00841             }
00842         } else if (!strcasecmp(buf, "pk_max_experience")) {
00843             sint64 pkme = atoll(cp);
00844             if (pkme < 0)
00845                 pkme = -1;
00846             settings.pk_max_experience = pkme;
00847         } else if (!strcasecmp(buf, "pk_max_experience_percent")) {
00848             int pkmep = atoi(cp);
00849             if (pkmep < 0) {
00850                 LOG(llevError, "load_settings: pk_max_experience_percent should be positive or zero\n", cp);
00851             } else
00852                 settings.pk_max_experience_percent = pkmep;
00853         } else if (!strcasecmp(buf, "allow_denied_spells_writing")) {
00854             if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
00855                 settings.allow_denied_spells_writing = TRUE;
00856             } else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
00857                 settings.allow_denied_spells_writing = FALSE;
00858             } else {
00859                 LOG(llevError, "load_settings: unknown value for allow_denied_spells_writing: %s\n", cp);
00860             }
00861         } else if (!strcasecmp(buf, "allow_broken_converters")) {
00862             if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
00863                 settings.allow_broken_converters = TRUE;
00864             } else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
00865                 settings.allow_broken_converters = FALSE;
00866             } else {
00867                 LOG(llevError, "load_settings: unknown value for allow_broken_converters: %s\n", cp);
00868             }
00869         } else if (!strcasecmp(buf, "log_timestamp")) {
00870             if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
00871                 settings.log_timestamp = TRUE;
00872             } else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
00873                 settings.log_timestamp = FALSE;
00874             } else {
00875                 LOG(llevError, "load_settings: unknown value for log_timestamp: %s\n", cp);
00876             }
00877         } else if (!strcasecmp(buf, "log_timestamp_format")) {
00878             free(settings.log_timestamp_format);
00879             settings.log_timestamp_format = strdup_local(cp);
00880         } else {
00881             LOG(llevError, "Unknown value in settings file: %s\n", buf);
00882         }
00883     }
00884     close_and_delete(fp, comp);
00885     if (settings.log_timestamp_format == NULL)
00886         settings.log_timestamp_format = strdup_local("%y/%m/%d %H:%M:%S");
00887 
00888     /*
00889     * The who formats are defined in config to be blank. They should have been
00890     * overridden by the settings file, if there are no entries however, it will
00891     * have stayed blank. Since this probably isn't what is wanted, we will check if
00892     * new formats have been specified, and if not we will use the old defaults.
00893     */
00894     if (!strcmp(settings.who_format, ""))
00895         strcpy(settings.who_format, "%N_%T%t%h%d%b%n<%m>");
00896     if (!strcmp(settings.who_wiz_format, ""))
00897         strcpy(settings.who_wiz_format, "%N_%T%t%h%d%b%nLevel %l <%m>(@%i)(%c)");
00898 }
00899 
00905 void init(int argc, char **argv) {
00906 
00907     init_done = 0;  /* Must be done before init_signal() */
00908     logfile = stderr;
00909     parse_args(argc, argv, 1); /* First arg pass - right now it does
00910      * nothing, but in future specifying the
00911      * LibDir in this pass would be reasonable*/
00912 
00913     init_library(); /* Must be called early */
00914     load_settings(); /* Load the settings file */
00915     load_materials();
00916     parse_args(argc, argv, 2);
00917     fprintf(logfile, "Welcome to CrossFire, v%s\n", FULL_VERSION);
00918     fprintf(logfile, "Copyright (C) 1994 Mark Wedel.\n");
00919     fprintf(logfile, "Copyright (C) 1992 Frank Tore Johansen.\n");
00920 
00921     if (strcmp(settings.dm_mail, "") != 0) {
00922         fprintf(logfile, "Maintained locally by: %s\n", settings.dm_mail);
00923         fprintf(logfile, "Questions and bugs should be mailed to above address.\n");
00924     }
00925     SRANDOM(time(NULL));
00926 
00927     init_startup(); /* Write (C), check shutdown/forbid files */
00928     init_signals(); /* Sets up signal interceptions */
00929     init_commands(); /* Sort command tables */
00930     read_map_log(); /* Load up the old temp map files */
00931     init_skills();
00932     init_ob_methods();
00933     cftimer_init();
00934 
00935     parse_args(argc, argv, 3);
00936 
00937 #ifndef WIN32 /* ***win32: no become_daemon in windows */
00938     if (settings.daemonmode)
00939         become_daemon();
00940 #endif
00941 
00942     init_beforeplay();
00943     init_server();
00944     metaserver_init();
00945     metaserver2_init();
00946     reset_sleep();
00947     init_done = 1;
00948 }
00949 
00955 void free_server(void) {
00956     free_materials();
00957     free_races();
00958 }
00959 
00960 static void usage(void) {
00961     (void)fprintf(logfile, "Usage: crossfire [-h] [-<flags>]...\n");
00962 }
00963 
00964 static void help(void) {
00965     /* The information in usage is redundant with what is given below, so why call it? */
00966     /*    usage();*/
00967     printf("Flags:\n");
00968     printf(" -csport <port> Specifies the port to use for the new client/server code.\n");
00969     printf(" -d           Turns on some debugging.\n");
00970     printf(" +d           Turns off debugging (useful if server compiled with debugging\n");
00971     printf("              as default).\n");
00972     printf(" -detach      The server will go in the background, closing all\n");
00973     printf("              connections to the tty.\n");
00974     printf(" -h           Display this information.\n");
00975     printf(" -log <file>  Specifies which file to send output to.\n");
00976     printf("              Only has meaning if -detach is specified.\n");
00977     printf(" -mon         Turns on monster debugging.\n");
00978     printf(" -o           Prints out info on what was defined at compile time.\n");
00979     printf(" -s           Display the high-score list.\n");
00980     printf(" -score <name or class> Displays all high scores with matching name/class.\n");
00981     printf(" -v           Print version and contributors.\n");
00982     printf(" -conf        Sets the configuration dir (settings, motd, etc.)\n");
00983     printf(" -data        Sets the lib dir (archetypes, treasures, etc.)\n");
00984     printf(" -local       Read/write local data (hiscore, unique items, etc.)\n");
00985     printf(" -maps        Sets the directory for maps.\n");
00986     printf(" -arch        Sets the archetype file to use.\n");
00987     printf(" -regions     Sets the regions file to use.\n");
00988     printf(" -playerdir   Sets the directory for the player files.\n");
00989     printf(" -templatedir Sets the directory for template generate maps.\n");
00990     printf(" -treasures   Sets the treasures file to use.\n");
00991     printf(" -uniquedir   Sets the unique items/maps directory.\n");
00992     printf(" -tmpdir      Sets the directory for temporary files (mostly maps.)\n");
00993     printf(" -m           Lists out suggested experience for all monsters.\n");
00994     printf(" -m2          Dumps out abilities.\n");
00995     printf(" -m3          Dumps out artifact information.\n");
00996     printf(" -m4          Dumps out spell information.\n");
00997     printf(" -m5          Dumps out skill information.\n");
00998     printf(" -m6          Dumps out race information.\n");
00999     printf(" -m7          Dumps out alchemy information.\n");
01000     printf(" -m8          Dumps out gods information.\n");
01001     printf(" -m9          Dumps out more alchemy information (formula checking).\n");
01002     printf(" -mt <name>   Dumps out list of treasures for a monster.\n");
01003     exit(0);
01004 }
01005 
01006 static void init_beforeplay(void) {
01007     init_archetypes(); /* If not called before, reads all archetypes from file */
01008     init_artifacts();  /* If not called before, reads all artifacts from file */
01009     check_spells();     /* If not called before, links archtypes used by spells */
01010     init_regions();    /* If not called before, reads all regions from file */
01011     init_archetype_pointers(); /* Setup global pointers to archetypes */
01012     init_races();    /* overwrite race designations using entries in lib/races file */
01013     init_gods(); /* init linked list of gods from archs*/
01014     init_readable(); /* inits useful arrays for readable texts */
01015     init_formulae();  /* If not called before, reads formulae from file */
01016 
01017     switch (settings.dumpvalues) {
01018     case 1:
01019         print_monsters();
01020         cleanup();
01021 
01022     case 2:
01023         dump_abilities();
01024         cleanup();
01025 
01026     case 3:
01027         dump_artifacts();
01028         cleanup();
01029 
01030     case 4:
01031         dump_spells();
01032         cleanup();
01033 
01034     case 5:
01035         cleanup();
01036 
01037     case 6:
01038         dump_races();
01039         cleanup();
01040 
01041     case 7:
01042         dump_alchemy();
01043         cleanup();
01044 
01045     case 8:
01046         dump_gods();
01047         cleanup();
01048 
01049     case 9:
01050         dump_alchemy_costs();
01051         cleanup();
01052 
01053     case 10:
01054         dump_monster_treasure(settings.dumparg);
01055         cleanup();
01056     }
01057 }
01058 
01064 static void init_startup(void) {
01065     char buf[MAX_BUF];
01066     FILE *fp;
01067     int comp;
01068 
01069 #ifdef SHUTDOWN_FILE
01070     snprintf(buf, sizeof(buf), "%s/%s", settings.confdir, SHUTDOWN_FILE);
01071     if ((fp = open_and_uncompress(buf, 0, &comp)) != NULL) {
01072         while (fgets(buf, MAX_BUF-1, fp) != NULL)
01073             printf("%s", buf);
01074         close_and_delete(fp, comp);
01075         exit(1);
01076     }
01077 #endif
01078 
01079     if (forbid_play()) { /* Maybe showing highscore should be allowed? */
01080         LOG(llevError, "CrossFire: Playing not allowed.\n");
01081         exit(-1);
01082     }
01083 }
01084 
01091 static void compile_info(void) {
01092     int i = 0;
01093     char err[MAX_BUF];
01094 
01095     printf("Non-standard include files:\n");
01096 #if !defined(__STRICT_ANSI__) || defined(__sun__)
01097 #if !defined(Mips)
01098     printf("<stdlib.h>\n");
01099     i = 1;
01100 #endif
01101 #if !defined(MACH) && !defined(sony)
01102     printf("<malloc.h>\n");
01103     i = 1;
01104 #endif
01105 #endif
01106 #ifndef __STRICT_ANSI__
01107 #ifndef MACH
01108     printf("<memory.h\n");
01109     i = 1;
01110 #endif
01111 #endif
01112 #ifndef sgi
01113     printf("<sys/timeb.h>\n");
01114     i = 1;
01115 #endif
01116     if (!i)
01117         printf("(none)\n");
01118     printf("Datadir:\t\t%s\n", settings.datadir);
01119     printf("Localdir:\t\t%s\n", settings.localdir);
01120 #ifdef PERM_FILE
01121     printf("Perm file:\t<ETC>/%s\n", PERM_FILE);
01122 #endif
01123 #ifdef SHUTDOWN_FILE
01124     printf("Shutdown file:\t<ETC>/%s\n", SHUTDOWN_FILE);
01125 #endif
01126     printf("Save player:\t<true>\n");
01127     printf("Save mode:\t%4.4o\n", SAVE_MODE);
01128     printf("Playerdir:\t<VAR>/%s\n", settings.playerdir);
01129     printf("Itemsdir:\t<VAR>/%s\n", settings.uniquedir);
01130     printf("Tmpdir:\t\t%s\n", settings.tmpdir);
01131     printf("Map max timeout:\t%d\n", MAP_MAXTIMEOUT);
01132     printf("Max objects:\t%d\n", MAX_OBJECTS);
01133 #ifdef USE_CALLOC
01134     printf("Use_calloc:\t<true>\n");
01135 #else
01136     printf("Use_calloc:\t<false>\n");
01137 #endif
01138 
01139     printf("Max_time:\t%d\n", MAX_TIME);
01140 
01141 #ifdef WIN32 /* ***win32 compile_info(): remove execl... */
01142     printf("Logfilename:\t%s\n", settings.logfilename);
01143     exit(0);
01144 #else
01145     execl("/bin/uname", "uname", "-a", NULL);
01146     LOG(llevError, "Oops, shouldn't have gotten here: execl(/bin/uname) failed: %s\n", strerror_local(errno, err, sizeof(err)));
01147     exit(-1);
01148 #endif
01149 }
01150 
01151 /* Signal handlers: */
01152 
01158 static void rec_sigsegv(int i) {
01159     LOG(llevError, "\nSIGSEGV received.\n");
01160     fatal_signal(1);
01161 }
01162 
01168 static void rec_sigint(int i) {
01169     LOG(llevInfo, "\nSIGINT received.\n");
01170     fatal_signal(0);
01171 }
01172 
01185 static void rec_sighup(int i) {
01186     /* Don't call LOG().  It calls non-reentrant functions.  The other
01187      * signal handlers shouldn't really call LOG() either. */
01188     if (logfile != stderr) {
01189         reopen_logfile = 1;
01190     }
01191 }
01192 
01199 static void rec_sigquit(int i) {
01200     LOG(llevInfo, "\nSIGQUIT received\n");
01201     fatal_signal(1);
01202 }
01203 
01217 static void rec_sigpipe(int i) {
01218     LOG(llevError, "\nSIGPIPE--------------\n------------\n--------\n---\n");
01219 #if 1 && !defined(WIN32) /* ***win32: we don't want send SIGPIPE */
01220     LOG(llevInfo, "\nReceived SIGPIPE, ignoring...\n");
01221     signal(SIGPIPE, rec_sigpipe);/* hocky-pux clears signal handlers */
01222 #else
01223     LOG(llevError, "\nSIGPIPE received, not ignoring...\n");
01224     fatal_signal(1); /*Might consider to uncomment this line */
01225 #endif
01226 }
01227 
01234 static void rec_sigbus(int i) {
01235 #ifdef SIGBUS
01236     LOG(llevError, "\nSIGBUS received\n");
01237     fatal_signal(1);
01238 #endif
01239 }
01240 
01247 static void rec_sigterm(int i) {
01248     LOG(llevInfo, "\nSIGTERM received\n");
01249     fatal_signal(0);
01250 }
01251 
01258 static void fatal_signal(int make_core) {
01259     if (init_done) {
01260         emergency_save(0);
01261         clean_tmp_files();
01262     }
01263     if (make_core)
01264         abort();
01265     exit(0);
01266 }
01267 
01271 static void init_signals(void) {
01272 #ifndef WIN32 /* init_signals() remove signals */
01273     struct sigaction sa;
01274 
01275     sa.sa_sigaction = NULL;
01276     sigemptyset(&sa.sa_mask);
01277     sa.sa_flags = 0;
01278     sa.sa_handler = rec_sighup;
01279     sigaction(SIGHUP, &sa, NULL);
01280     signal(SIGINT, rec_sigint);
01281 #ifndef DEBUG
01282     signal(SIGQUIT, rec_sigquit);
01283     signal(SIGSEGV, rec_sigsegv);
01284     LOG(llevInfo, "\n---------registering SIGPIPE\n");
01285     signal(SIGPIPE, rec_sigpipe);
01286 #ifdef SIGBUS
01287     signal(SIGBUS, rec_sigbus);
01288 #endif
01289     signal(SIGTERM, rec_sigterm);
01290 #endif
01291 #endif /* win32 */
01292 }
01293 
01300 static void init_races(void) {
01301     FILE *file;
01302     char race[MAX_BUF], fname[MAX_BUF], buf[MAX_BUF], *cp, variable[MAX_BUF];
01303     archetype *mon = NULL;
01304     static int init_done = 0;
01305 
01306     if (init_done)
01307         return;
01308     init_done = 1;
01309     first_race = NULL;
01310 
01311     snprintf(fname, sizeof(fname), "%s/races", settings.datadir);
01312     LOG(llevDebug, "Reading races from %s...\n", fname);
01313     if (!(file = fopen(fname, "r"))) {
01314         LOG(llevError, "Cannot open races file %s: %s\n", fname, strerror_local(errno, buf, sizeof(buf)));
01315         return;
01316     }
01317 
01318     while (fgets(buf, MAX_BUF, file) != NULL) {
01319         int set_race = 1, set_list = 1;
01320         if (*buf == '#')
01321             continue;
01322         if ((cp = strchr(buf, '\n')) != NULL)
01323             *cp = '\0';
01324         cp = buf;
01325         while (*cp == ' ' || *cp == '!' || *cp == '@') {
01326             if (*cp == '!')
01327                 set_race = 0;
01328             if (*cp == '@')
01329                 set_list = 0;
01330             cp++;
01331         }
01332         if (sscanf(cp, "RACE %s", variable)) { /* set new race value */
01333             strcpy(race, variable);
01334         } else {
01335             char *cp1;
01336 
01337             /* Take out beginning spaces */
01338             for (cp1 = cp; *cp1 == ' '; cp1++)
01339                 ;
01340             /* Remove newline and trailing spaces */
01341             for (cp1 = cp+strlen(cp)-1; *cp1 == '\n' || *cp1 == ' '; cp1--) {
01342                 *cp1 = '\0';
01343                 if (cp == cp1)
01344                     break;
01345             }
01346 
01347             if (cp[strlen(cp)-1] == '\n')
01348                 cp[strlen(cp)-1] = '\0';
01349             /* set creature race to race value */
01350             if ((mon = find_archetype(cp)) == NULL)
01351                 LOG(llevError, "Creature %s in race file lacks archetype\n", cp);
01352             else {
01353                 if (set_race && (!mon->clone.race || strcmp(mon->clone.race, race))) {
01354                     if (mon->clone.race) {
01355                         LOG(llevDebug, " Resetting race to %s from %s for archetype %s\n", race, mon->clone.race, mon->name);
01356                         free_string(mon->clone.race);
01357                     }
01358                     mon->clone.race = add_string(race);
01359                 }
01360                 /* if the arch is a monster, add it to the race list */
01361                 if (set_list && QUERY_FLAG(&mon->clone, FLAG_MONSTER))
01362                     add_to_racelist(race, &mon->clone);
01363             }
01364         }
01365     }
01366     fclose(file);
01367     LOG(llevDebug, "done races.\n");
01368 }
01369 
01373 static void dump_races(void) {
01374     racelink *list;
01375     objectlink *tmp;
01376 
01377     for (list = first_race; list; list = list->next) {
01378         fprintf(stderr, "\nRACE %s:\t", list->name);
01379         for (tmp = list->member; tmp; tmp = tmp->next)
01380             fprintf(stderr, "%s(%d), ", tmp->ob->arch->name, tmp->ob->level);
01381     }
01382     fprintf(stderr, "\n");
01383 }
01384 
01388 static void free_races(void) {
01389     racelink *race;
01390     objectlink *link;
01391 
01392     LOG(llevDebug, "Freeing race information.\n");
01393     while (first_race) {
01394         race = first_race->next;
01395         while (first_race->member) {
01396             link = first_race->member->next;
01397             free(first_race->member);
01398             first_race->member = link;
01399         }
01400         free_string(first_race->name);
01401         free(first_race);
01402         first_race = race;
01403     }
01404 }
01405 
01414 static void add_to_racelist(const char *race_name, object *op) {
01415     racelink *race;
01416 
01417     if (!op || !race_name)
01418         return;
01419     race = find_racelink(race_name);
01420 
01421     if (!race) { /* add in a new race list */
01422         race = get_racelist();
01423         race->next = first_race;
01424         first_race = race;
01425         race->name = add_string(race_name);
01426     }
01427 
01428     if (race->member->ob) {
01429         objectlink *tmp = get_objectlink();
01430 
01431         tmp->next = race->member;
01432         race->member = tmp;
01433     }
01434     race->nrof++;
01435     race->member->ob = op;
01436 }
01437 
01446 static racelink *get_racelist(void) {
01447     racelink *list;
01448 
01449     list = (racelink *)malloc(sizeof(racelink));
01450     if (!list)
01451         fatal(OUT_OF_MEMORY);
01452     list->name = NULL;
01453     list->nrof = 0;
01454     list->member = get_objectlink();
01455     list->next = NULL;
01456 
01457     return list;
01458 }
01459 
01468 racelink *find_racelink(const char *name) {
01469     racelink *test = NULL;
01470 
01471     if (name && first_race)
01472         for (test = first_race; test && test != test->next; test = test->next)
01473             if (!test->name || !strcmp(name, test->name))
01474                 break;
01475 
01476     return test;
01477 }