options.c

Go to the documentation of this file.
00001 /*
00002     Copyright (C) 2007 Krzysztof Kosciuszkiewicz
00003 
00004     This program is free software; you can redistribute it and/or modify
00005     it under the terms of the GNU General Public License as published by
00006     the Free Software Foundation; either version 2 of the License, or
00007     (at your option) any later version.
00008 
00009     This program is distributed in the hope that it will be useful,
00010     but WITHOUT ANY WARRANTY; without even the implied warranty of
00011     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012     GNU General Public License for more details.
00013 
00014     You should have received a copy of the GNU General Public License
00015     along with this program; if not, write to the Free Software
00016     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00017 */
00018 #include "race.h"
00019 #include "options.h"
00020 #include "macros.h"
00021 #include "pace.h"
00022 #include "fs.h"
00023 #include "utils.h"
00024 #include "logging.h"
00025 #include <assert.h>
00026 #include <ctype.h>
00027 #include <errno.h>
00028 #include <stdio.h>
00029 #include <stdlib.h>
00030 #include <string.h>
00031 #include <SDL.h>
00032 
00033 #define ENVIRON_DATADIR ("BARIS_DATA")
00034 #define ENVIRON_SAVEDIR ("BARIS_SAVE")
00035 
00036 #if CONFIG_WIN32
00037 #  define DEFAULT_DATADIR ("c:/" PACKAGE_TARNAME )
00038 #  define DEFAULT_SAVEDIR ("c:/" PACKAGE_TARNAME "/savedat")
00039 #elif CONFIG_LINUX
00040 #  define DEFAULT_DATADIR CONFIG_DATADIR
00041 #  define DEFAULT_SAVEDIR (".")
00042 #elif CONFIG_MACOSX
00043 #  define DEFAULT_DATADIR CONFIG_DATADIR
00044 #  define DEFAULT_SAVEDIR (".")
00045 #endif
00046 
00047 /* and provide defaults for unspecified OS */
00048 #ifndef DEFAULT_DATADIR
00049 #  define DEFAULT_DATADIR (".")
00050 #endif
00051 
00052 #ifndef DEFAULT_SAVEDIR
00053 #  define DEFAULT_SAVEDIR (".")
00054 #endif
00055 
00056 #if !HAVE_GETENV
00057 #  if HAVE_SDL_GETENV
00058 #    define getenv SDL_getenv
00059 #  else
00060 #    warn I don't know a way to read environment on this system
00061 #    define getenv(a) (NULL)
00062 #  endif
00063 #endif
00064 
00065 game_options options;
00066 
00067 LOG_DEFAULT_CATEGORY(config);
00068 
00069 /*set-up array for environment vars */
00070 static struct {
00071     char *name;
00072     char **dest;
00073     char *def_val;
00074 } env_vars[] = {
00075     {ENVIRON_DATADIR, &options.dir_gamedata, DEFAULT_DATADIR},
00076     {ENVIRON_SAVEDIR, &options.dir_savegame, DEFAULT_SAVEDIR},
00077 };
00078 
00079 static struct {
00080     char *name; /**< name of option */
00081     void *dest; /**< pointer to the variable to hold the content  */
00082     char *type; /**< type of the data we get */
00083     int need_alloc; /**< max size of the data in byte ??? */
00084     char *comment; /**< a note to the user */
00085 } config_strings[] = {
00086     {"datadir", &options.dir_gamedata, "%1024[^\n\r]", 1025,
00087         "Path to directory with game data files." },
00088     {"audio", &options.want_audio, "%u", 0,
00089         "Set to 0 if you don't want audio in game." },
00090     {"nofail",  &options.want_cheats, "%u", 0,
00091         "Set to 1 if you want every mission step check to succeeed." },
00092     {"intro", &options.want_intro, "%u", 0,
00093         "Set to 0 if do not want intro displayed at startup." },
00094     {"fullscreen", &options.want_fullscreen, "%u", 0,
00095         "Set to 1 if you want (ugly) full screen game." },
00096     {"debuglevel", &options.want_debug, "%u", 0,
00097         "Set to positive values to increase debugging verbosity" },
00098 };
00099 
00100 /** prints the minimal usage information to stderr
00101  * 
00102  * \param fail sets the exit code
00103  */
00104 static void
00105 usage (int fail)
00106 {
00107     fprintf(stderr, "usage:   raceintospace [options...]\n"
00108                     "options: -a -i -f -v -n\n"
00109                     "\t-v verbose mode\n\t\tadd this several times to get to DEBUG level\n"
00110                     "\t-f fullscreen mode\n\t\tugly\n"
00111            );
00112     exit((fail) ? EXIT_FAILURE : EXIT_SUCCESS);
00113 }
00114 
00115 static void
00116 shift_argv(char ** argv, int len, int shift)
00117 {
00118     int i = 0;
00119 
00120     assert(shift >= 0);
00121     assert(len >= 0);
00122 
00123     for (i = shift; i < len; ++i)
00124     {
00125         argv[i - shift] = argv[i];
00126         argv[i] = NULL;
00127     }
00128 }
00129 
00130 static int
00131 skip_past_newline(FILE * f)
00132 {
00133     assert(f);
00134     return fscanf(f, "%*[^\r\n] ");
00135 }
00136 
00137 static int
00138 parse_var_value(FILE * f, int index)
00139 {
00140     char format[128];
00141     int need_alloc;
00142     int res = 0, chars = 0, i = index;
00143     void **target;
00144 
00145     assert(f);
00146     assert(i >= 0 && i < (int) ARRAY_LENGTH(config_strings));
00147 
00148     target = config_strings[i].dest;
00149     need_alloc = config_strings[i].need_alloc;
00150     if (need_alloc)
00151         *target = xrealloc(*target, need_alloc);
00152 
00153     sprintf(format, " %s%%n", config_strings[i].type);
00154     res = fscanf(f, format, *target, &chars);
00155 
00156     if (res < 1)
00157         return -1;
00158 
00159     if (need_alloc && chars < need_alloc)
00160         *target = xrealloc(*target, chars + 1);
00161 
00162     return 0;
00163 }
00164 
00165 /** read the config file 
00166  * 
00167  * 
00168  * 
00169  * \return -1 if the config file is unavailable
00170  */
00171 static int
00172 read_config_file(void)
00173 {
00174     FILE *f = open_savedat("config", "rt");
00175     char config_word[32 + 1];
00176     int err = 0, res = 0, i = 0;
00177     char c[2];
00178 
00179     if (!f)
00180     {
00181         WARNING1("could not open config file");
00182         return -1;
00183     }
00184 
00185     while (1)
00186     {
00187         /* skip comments */
00188         if ((res = fscanf(f, " %1[#]", c)) == 1)
00189             goto skip_newline;
00190 
00191         if (res == EOF)
00192             break;
00193 
00194         /* get configuration variable name */
00195         /** \note config variables may be 32 characters of alphas plus dash and underscore */
00196         res = fscanf(f, "%32[a-zA-Z_-]", config_word);
00197 
00198         /* do we have a match? */
00199         if (res == 1)
00200         {
00201             for (i = 0; i < (int) ARRAY_LENGTH(config_strings); ++i)
00202             {
00203                 if (strcmp(config_word, config_strings[i].name) == 0)
00204                 {
00205                     res = parse_var_value(f, i);
00206                     if (res != 0 && feof(f))
00207                         goto skip_newline;
00208                     else if (res != 0)
00209                     {
00210                         NOTICE2("wrong value type for variable `%s'",
00211                                 config_word);
00212                         goto skip_newline;
00213                     }
00214                     else
00215                         break;
00216                 }
00217             }
00218 
00219             /* none matched */
00220             if (i == (int) ARRAY_LENGTH(config_strings))
00221             {
00222                 NOTICE2("unknown variable in file `%s'",
00223                         config_word);
00224                 goto skip_newline;
00225             }
00226         }
00227         else if (res == EOF)
00228             break;
00229         else
00230         {
00231             NOTICE1("expected variable name");
00232             goto skip_newline;
00233         }
00234 
00235       skip_newline:
00236         if (EOF == skip_past_newline(f))
00237             break;
00238     }
00239 
00240     err = !feof(f);
00241 
00242     fclose(f);
00243 
00244     return -err;
00245 }
00246 
00247 static int
00248 write_default_config(void)
00249 {
00250     int i = 0;
00251     int err = 0;
00252     FILE *f = NULL;
00253 
00254     create_save_dir();
00255     f = open_savedat("config", "wt");
00256     if (!f)
00257     {
00258         WARNING4("can't write defaults to file `%s/%s': %s\n",
00259                 options.dir_savegame, "config", strerror(errno));
00260         return -1;
00261     }
00262     else
00263         NOTICE3("written defaults to file `%s/%s'",
00264                 options.dir_savegame, "config");
00265 
00266     fprintf(f, "# This is template configuration file for %s\n",
00267         PACKAGE_STRING);
00268     fprintf(f, "# Comments start with '#' sign and span whole line.\n");
00269     fprintf(f, "# Non comment lines should look like:\n");
00270     fprintf(f, "# variable_name variable_value\n\n");
00271     for (i = 0; i < (int) ARRAY_LENGTH(config_strings); ++i)
00272         fprintf(f, "# %s\n# %s\n\n",
00273             config_strings[i].comment, config_strings[i].name);
00274     err = ferror(f);
00275     if (err)
00276         WARNING2("read error: %s", strerror(errno));
00277     fclose(f);
00278     return err;
00279 }
00280 
00281 /* return a location of user's home directory, or NULL if unknown.
00282  * returned string is malloc-ed */
00283 static char *
00284 get_homedir(void)
00285 {
00286     char *s = NULL;
00287 
00288     if ((s = getenv("HOME")))
00289     {
00290         return xstrdup(s);
00291     }
00292 #if CONFIG_WIN32
00293     if ((s = getenv("HOMEPATH")))
00294     {
00295         char *s2 = NULL;
00296 
00297         if ((s2 = getenv("HOMEDRIVE")) || (s2 = getenv("HOMESHARE")))
00298         {
00299             return xstrcat2(s2, s);
00300         }
00301     }
00302     if ((s = getenv("USERPROFILE")))
00303     {
00304         return xstrdup(s);
00305     }
00306 #endif
00307     return NULL;
00308 }
00309 
00310 static void
00311 fixpath_options(void)
00312 {
00313     fix_pathsep(options.dir_savegame);
00314     fix_pathsep(options.dir_gamedata);
00315 }
00316 
00317 /** read the commandline options
00318  * 
00319  * \return length of modified argv 
00320  *
00321  * \todo possibly maintain a list of dirs to search?? 
00322  */
00323 int
00324 setup_options(int argc, char *argv[])
00325 {
00326     char *str = NULL;
00327     int pos, i;
00328 
00329     /* first set up defaults */
00330     for (i = 0; i < (int) ARRAY_LENGTH(env_vars); ++i)
00331     {
00332         if ((str = getenv(env_vars[i].name)))
00333             *env_vars[i].dest = xstrdup(str);
00334         else if (strcmp(env_vars[i].name, ENVIRON_SAVEDIR) == 0
00335             && (str = get_homedir()))
00336         {
00337             size_t len = strlen(str) + strlen(PACKAGE_TARNAME) + 3;
00338 
00339             *env_vars[i].dest = xmalloc(len);
00340             sprintf(*env_vars[i].dest, "%s/.%s", str, PACKAGE_TARNAME);
00341             free(str);
00342         }
00343         else
00344             *env_vars[i].dest = xstrdup(env_vars[i].def_val);
00345     }
00346 
00347     options.want_audio = 1;
00348     options.want_intro = 1;
00349     options.want_cheats = 0;
00350     options.want_fullscreen = 0;
00351 
00352     fixpath_options();
00353 
00354     /* now try to read config file, if it exists */
00355     if (read_config_file() < 0)
00356         /* if not, then write default config template */
00357         write_default_config();
00358 
00359     /* first pass: command line options */
00360     for (pos = 1; pos < argc; ++pos)
00361     {
00362         str = argv[pos];
00363 
00364         if (str[0] != '-')
00365             continue;
00366 
00367         if (!str[1])
00368             continue;
00369 
00370         if (strcmp(str, "--") == 0)
00371         {
00372             shift_argv(argv + pos, argc - pos, 1);
00373             argc--;
00374             break;
00375         }
00376 
00377         /* check what option matches */
00378         if (strcmp(str, "-h") == 0)
00379             usage(0);
00380         else if (strcmp(str, "-i") == 0)
00381             options.want_intro = 0;
00382         else if (strcmp(str, "-n") == 0)
00383             options.want_cheats = 1;
00384         else if (strcmp(str, "-a") == 0)
00385             options.want_audio = 0;
00386         else if (strcmp(str, "-f") == 0)
00387             options.want_fullscreen = 1;
00388         else if (strcmp(str, "-v") == 0)
00389             options.want_debug++;
00390         else
00391         {
00392             ERROR2("unknown option %s", str);
00393             usage(1);
00394         }
00395 
00396         shift_argv(argv + pos, argc - pos, 1);
00397         argc--;
00398         pos--; /* for loop will advance it again */
00399     }
00400 
00401     /* second pass: variable assignments */
00402     for (pos = 1; pos < argc; ++pos)
00403     {
00404         /** \todo should use PATH_MAX or something similar here */
00405         char name[32 + 1], *value;
00406         int offset = 0;
00407         int fields = 0;
00408 
00409         fields = sscanf(argv[pos], "%32[A-Z_]=%n", name, &offset);
00410 
00411         /* it is unclear whether %n increments return value */
00412         if (fields < 1)
00413             continue;
00414 
00415         value = argv[pos] + offset;
00416 
00417         for (i = 0; i < (int) ARRAY_LENGTH(env_vars); ++i)
00418         {
00419             if (strcmp(name, env_vars[i].name) == 0)
00420             {
00421                 free(*env_vars[i].dest);
00422                 *env_vars[i].dest = xstrdup(value);
00423                 break;
00424             }
00425         }
00426 
00427         if (i == (int) ARRAY_LENGTH(env_vars))
00428             WARNING2("unsupported command line variable `%s'", name);
00429 
00430         /* remove matched string from argv */
00431         shift_argv(argv + pos, argc - pos, 1);
00432 
00433         /*
00434          * now we have one less arg, pos points to the next arg, 
00435          * keep it this way after incrementing it on top of the loop
00436          */
00437         pos--;
00438         argc--;
00439     }
00440 
00441     fixpath_options();
00442 
00443     return argc;
00444 }
00445 
00446 /* vim: set noet ts=4 sw=4 tw=77: */

Generated on Fri Sep 28 00:35:27 2007 for raceintospace by  doxygen 1.5.3