summaryrefslogtreecommitdiffstats
path: root/crawl-ref/source/initfile.cc
diff options
context:
space:
mode:
authordshaligram <dshaligram@c06c8d41-db1a-0410-9941-cceddc491573>2008-05-26 15:30:07 +0000
committerdshaligram <dshaligram@c06c8d41-db1a-0410-9941-cceddc491573>2008-05-26 15:30:07 +0000
commita844f8e10213c19b6e48d6ff98e97bc9f026a42a (patch)
tree9cada2344ca78a2c2de555dc8b7fa943ee7382fe /crawl-ref/source/initfile.cc
parent1f1939b11a6ed95f769644c21e53589a1e7abeb9 (diff)
downloadcrawl-ref-a844f8e10213c19b6e48d6ff98e97bc9f026a42a.tar.gz
crawl-ref-a844f8e10213c19b6e48d6ff98e97bc9f026a42a.zip
Better support for settings files that include other files:
- Files can be included as "include foo" in .crawlrc instead of using the Lua call: : crawl.read_options('foo'). include foo and the Lua crawl.read_options('foo') are not equivalent - Lua only runs after the start of a new game, which is too late for some option settings. - Crawl searches for included files in this sequence: - Absolute paths: use the path directly (but not if DATA_DIR_PATH is set, since we don't want Crawl to read arbitrary files on multiuser systems). - Search relative to the including file. - Search relative to any -rcdir(s) provided. - Search in the data file search path. - The data file search path now includes settings/ for when we move rc stuff to settings/ .gitignore: ignore saves and morgue dirs correctly. git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@5256 c06c8d41-db1a-0410-9941-cceddc491573
Diffstat (limited to 'crawl-ref/source/initfile.cc')
-rw-r--r--crawl-ref/source/initfile.cc255
1 files changed, 232 insertions, 23 deletions
diff --git a/crawl-ref/source/initfile.cc b/crawl-ref/source/initfile.cc
index 5b8ecad4f2..02e74c0a30 100644
--- a/crawl-ref/source/initfile.cc
+++ b/crawl-ref/source/initfile.cc
@@ -892,6 +892,9 @@ void game_options::reset_options()
// Setup travel information. What's a better place to do this?
initialise_travel();
+
+ // Forget any files we remembered as included.
+ included.clear();
}
void game_options::clear_cset_overrides()
@@ -1080,53 +1083,85 @@ void game_options::add_cset_override(char_set_type set, dungeon_char_type dc,
cset_override[set][dc] = symbol;
}
-// returns where the init file was read from
-std::string read_init_file(bool runscript)
+static std::string _find_crawlrc()
{
const char* locations_data[][2] = {
- { SysEnv.crawl_rc.c_str(), "" },
{ SysEnv.crawl_dir.c_str(), "init.txt" },
#ifdef MULTIUSER
- { SysEnv.home.c_str(), "/.crawlrc" },
+ { SysEnv.home.c_str(), ".crawlrc" },
{ SysEnv.home.c_str(), "init.txt" },
#endif
+#ifndef DATA_DIR_PATH
{ "", "init.txt" },
-#ifdef WIN32CONSOLE
- { "", ".crawlrc" },
- { "", "_crawlrc" },
+ { "..", "init.txt" },
#endif
{ NULL, NULL } // placeholder to mark end
};
- Options.reset_options();
+ // We'll look for these files in any supplied -rcdirs.
+ static const char *rc_dir_filenames[] = {
+ ".crawlrc", "init.txt"
+ };
+
+ // -rc option always wins.
+ if (!SysEnv.crawl_rc.empty())
+ return (SysEnv.crawl_rc);
+
+ // If we have any rcdirs, look in them for files from the
+ // rc_dir_names list.
+ for (int i = 0, size = SysEnv.rcdirs.size(); i < size; ++i)
+ {
+ for (unsigned n = 0; n < ARRAYSZ(rc_dir_filenames); ++n)
+ {
+ const std::string rc(
+ catpath(SysEnv.rcdirs[i], rc_dir_filenames[n]));
+ if (file_exists(rc))
+ return (rc);
+ }
+ }
- FILE* f = NULL;
- char name_buff[kPathLen];
// Check all possibilities for init.txt
- for ( int i = 0; f == NULL && locations_data[i][1] != NULL; ++i )
+ for ( int i = 0; locations_data[i][1] != NULL; ++i )
{
// Don't look at unset options
if ( locations_data[i][0] != NULL )
{
- snprintf( name_buff, sizeof name_buff, "%s%s",
- locations_data[i][0], locations_data[i][1] );
- f = fopen( name_buff, "r" );
+ const std::string rc =
+ catpath(locations_data[i][0], locations_data[i][1]);
+ if (file_exists(rc))
+ return (rc);
}
}
+ return ("");
+}
+
+// Returns an error message if the init.txt was not found.
+std::string read_init_file(bool runscript)
+{
+ Options.reset_options();
+
+ const std::string init_file_name( _find_crawlrc() );
+
+ FILE* f = fopen(init_file_name.c_str(), "r");
if ( f == NULL )
{
+ if (!init_file_name.empty())
+ return make_stringf("(\"%s\" is not readable)",
+ init_file_name.c_str());
+
#ifdef MULTIUSER
- return "not found (~/.crawlrc missing)";
+ return "(~/.crawlrc missing)";
#else
- return "not found (init.txt missing from current directory)";
+ return "(no init.txt in current directory)";
#endif
}
+ Options.filename = init_file_name;
read_options(f, runscript);
fclose(f);
- return std::string(name_buff);
-} // end read_init_file()
+ return ("");
+}
void read_startup_prefs()
{
@@ -1580,6 +1615,23 @@ void game_options::set_option_fragment(const std::string &s)
}
}
+bool game_options::include_file_directive(const std::string &line,
+ bool runscript)
+{
+ const std::string prefix = "include ";
+ if (line.find(prefix) == std::string::npos)
+ return (false);
+
+ std::string include_line = trimmed_string(line);
+ if (include_line.find(prefix) == 0)
+ {
+ include(trimmed_string(include_line.substr(prefix.length())),
+ true, runscript);
+ return (true);
+ }
+ return (false);
+}
+
void game_options::read_option_line(const std::string &str, bool runscript)
{
#define BOOL_OPTION_NAMED(_opt_str, _opt_var) \
@@ -1634,6 +1686,11 @@ void game_options::read_option_line(const std::string &str, bool runscript)
bool plus_equal = false;
bool minus_equal = false;
+ // If the user asked us to include a file, don't try to process the line
+ // as an option.
+ if (include_file_directive(str, runscript))
+ return;
+
const int first_equals = str.find('=');
// all lines with no equal-signs we ignore
@@ -2033,12 +2090,15 @@ void game_options::read_option_line(const std::string &str, bool runscript)
field.c_str() );
}
}
+ // If DATA_DIR_PATH is set, don't set crawl_dir from .crawlrc.
+#ifndef DATA_DIR_PATH
else if (key == "crawl_dir")
{
// We shouldn't bother to allocate this a second time
// if the user puts two crawl_dir lines in the init file.
SysEnv.crawl_dir = field;
}
+#endif
else if (key == "race")
{
race = _str_to_race( field );
@@ -2592,7 +2652,9 @@ void game_options::read_option_line(const std::string &str, bool runscript)
}
else if (key == "additional_macro_file")
{
- additional_macro_files.push_back(orig_field);
+ const std::string resolved = resolve_include(orig_field, "macro ");
+ if (!resolved.empty())
+ additional_macro_files.push_back(resolved);
}
#ifdef USE_TILE
else if (key == "tile_show_items")
@@ -2718,6 +2780,128 @@ void game_options::read_option_line(const std::string &str, bool runscript)
}
}
+// Checks an include file name for safety and resolves it to a readable path.
+// If safety check fails, throws a string with the reason for failure.
+// If file cannot be resolved, returns the empty string (this does not throw!)
+// If file can be resolved, returns the resolved path.
+std::string game_options::resolve_include(
+ std::string parent_file,
+ std::string included_file,
+ const std::vector<std::string> *rcdirs)
+
+ throw (std::string)
+{
+ // Before we start, make sure we convert forward slashes to the platform's
+ // favoured file separator.
+ parent_file = canonicalise_file_separator(parent_file);
+ included_file = canonicalise_file_separator(included_file);
+
+ // How we resolve include paths:
+ // 1. If it's an absolute path, use it directly.
+ // 2. Try the name relative to the parent filename, if supplied.
+ // 3. Try the name relative to any of the provided rcdirs.
+ // 4. Try locating the name as a regular data file (also checks for the
+ // file name relative to the current directory).
+ // 5. Fail, and return empty string.
+
+ assert_read_safe_path(included_file);
+
+ // There's only so much you can do with an absolute path.
+ // Note: absolute paths can only get here if we're not on a
+ // multiuser system. On multiuser systems assert_read_safe_path()
+ // will throw an exception if it sees absolute paths.
+ if (is_absolute_path(included_file))
+ return (file_exists(included_file)? included_file : "");
+
+ if (!parent_file.empty())
+ {
+ const std::string candidate =
+ get_path_relative_to(parent_file, included_file);
+ if (file_exists(candidate))
+ return (candidate);
+ }
+
+ if (rcdirs)
+ {
+ const std::vector<std::string> &dirs(*rcdirs);
+ for (int i = 0, size = dirs.size(); i < size; ++i)
+ {
+ const std::string candidate( catpath(dirs[i], included_file) );
+ if (file_exists(candidate))
+ return (candidate);
+ }
+ }
+
+ return datafile_path(included_file, false, true);
+}
+
+std::string game_options::resolve_include(
+ const std::string &file,
+ const char *type)
+{
+ try
+ {
+ const std::string resolved =
+ resolve_include(this->filename, file, &SysEnv.rcdirs);
+
+ if (resolved.empty())
+ report_error(
+ make_stringf("Cannot find %sfile \"%s\".",
+ type, file.c_str()));
+ return (resolved);
+ }
+ catch (const std::string &err)
+ {
+ report_error(
+ make_stringf("Cannot include %sfile: %s", type, err.c_str()));
+ return "";
+ }
+}
+
+bool game_options::was_included(const std::string &file) const
+{
+ return (included.find(file) != included.end());
+}
+
+void game_options::include(const std::string &rawfilename,
+ bool resolve,
+ bool runscript)
+{
+ const std::string include_file =
+ resolve ? resolve_include(rawfilename) : rawfilename;
+
+ if (was_included(include_file))
+ {
+ // Report error with rawfilename, not the resolved file name - we
+ // don't want to leak file paths in dgamelaunch installs.
+ report_error(make_stringf("Skipping previously included file: \"%s\".",
+ rawfilename.c_str()));
+ return;
+ }
+
+ included.insert(include_file);
+
+ // Change this->filename to the included filename while we're reading it.
+ unwind_var<std::string> optfile(this->filename, include_file);
+ FILE* f = fopen( include_file.c_str(), "r" );
+ if (f)
+ {
+ FileLineInput fl(f);
+ read_options(fl, runscript);
+ fclose(f);
+ }
+}
+
+void game_options::report_error(const std::string &error)
+{
+ // If called before game starts, log a startup error,
+ // otherwise spam the warning channel.
+ if (crawl_state.need_save)
+ mprf(MSGCH_WARN, "Warning: %s", error.c_str());
+ else
+ crawl_state.add_startup_error(error);
+}
+
static std::string check_string(const char *s)
{
return (s? s : "");
@@ -2778,6 +2962,7 @@ enum commandline_option_type {
CLO_PLAIN,
CLO_DIR,
CLO_RC,
+ CLO_RCDIR,
CLO_TSCORES,
CLO_VSCORES,
CLO_SCOREFILE,
@@ -2788,10 +2973,11 @@ enum commandline_option_type {
CLO_NOPS
};
-static const char *cmd_ops[] = { "scores", "name", "race", "class",
- "pizza", "plain", "dir", "rc", "tscores",
- "vscores", "scorefile", "morgue",
- "macro", "mapstat" };
+static const char *cmd_ops[] = {
+ "scores", "name", "race", "class", "pizza", "plain", "dir", "rc",
+ "rcdir", "tscores", "vscores", "scorefile", "morgue", "macro",
+ "mapstat"
+};
const int num_cmd_ops = CLO_NOPS;
bool arg_seen[num_cmd_ops];
@@ -2800,6 +2986,8 @@ bool parse_args( int argc, char **argv, bool rc_only )
{
set_crawl_base_dir(argv[0]);
+ SysEnv.rcdirs.clear();
+
if (argc < 2) // no args!
return (true);
@@ -2991,6 +3179,15 @@ bool parse_args( int argc, char **argv, bool rc_only )
}
break;
+ case CLO_RCDIR:
+ // Always parse
+ if (!next_is_param)
+ return (false);
+
+ SysEnv.add_rcdir(next_arg);
+ nextUsed = true;
+ break;
+
case CLO_DIR:
// ALWAYS PARSE
if (!next_is_param)
@@ -3078,6 +3275,18 @@ int game_options::o_colour(const char *name, int def) const
}
///////////////////////////////////////////////////////////////////////
+// system_environment
+
+void system_environment::add_rcdir(const std::string &dir)
+{
+ std::string cdir = canonicalise_file_separator(dir);
+ if (dir_exists(cdir))
+ rcdirs.push_back(cdir);
+ else
+ end(1, false, "Cannot find -rcdir \"%s\"", cdir.c_str());
+}
+
+///////////////////////////////////////////////////////////////////////
// menu_sort_condition
menu_sort_condition::menu_sort_condition(menu_type _mt, int _sort)