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:
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
C55G5JGGSVWMU7XVEJL6YZZLDXQZGRN7JQOAALS6WIKFPX3L2U6QC
6WG4FW67ZXR3BLWHCIGJGHWJO4AKVG3HT5JQL7FVSYFE76PLFVOQC
SRQJVKQVUY7QGCEBA2VQTWEJ7ADIUSY7L46HJQSQNM5DXYRRH5KAC
QEEJFAETO6B2J4IWDIDCJ5UNIFNNHHG22IWF2CUJRTJJBNE47CWQC
AM7QPHDAWNXHLUEVUHVRHG2RO2DOIEFFU4GV3DCIROW6O5HW7H4AC
GVDKSZGU5NDWQAADOPCIURSBQG2T7VMFDKAF75Z5T3KVVP6744VQC
KI2WFER7ZCET4FVNEJ2UTK2AEYLTJ6R5OSRJG3Q3EHOEPLZC2BTQC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC
4O3VTUJT5T7NBNF3Q45XO2WHS6TCJXVLH6CKX4K36WUBDRT5F6KAC
VXSORUQOM2VZA4CAZDC6KPAY373NQIN3UT7CXQXTRCYXO2WM62DAC
GQL5SIGBHLU3FMCE54XVGLRY5AZHRM6DUEB722REA2DPLGJSN6EQC
SVY2PTCLXR3KNPQAWXVXTTGCC5DR334HOAKHYO3VDDRWM2BWMALAC
SDLKLUNFGVKDS55DDJZCBAVIB7NL3RRYPTACAY65SCUQKV6APFSAC
V4WGXVERZ34B7CEINV4D3ZYEKKT2TUIUYTOX5FSOX6B26U3DPVLQC
QXQE7C5XDYNF4JSHGDAVMS7HYLOC6YEZFOVFJ3RD7RB6U4AJSFIQC
O6ZMFKDI3XO2SWPNEYHIPYFDWJR4TVDP5BAATK6LVCVETQID6E7AC
O3QBT2ISABMEWTNSYEJKAJG6LZMON7U6RQZDSLY2KQHSMILUYQFQC
547JREUJXTZNYVGHNNAET5F5O5JYYGNTDQB6ABZNT7YX5EY64OHAC
T3V75V2CMAABSOXKSGCC6ZTI2WBKSVSGUEF2QAGM2BWLVRIL6SQQC
LOAHVKQFG57THOYHK6ONP5YUUO2WATATFTW3IETBPJZ5NYO3KZ5AC
Z3RI4XAN7J2GUABAQ5G6YIOQYMBOAEQFFGRBWHMUEB2A2QYXO5MQC
GSQ72ULBSL6WBJZUB3GJKAPQDXZIQV7B2TDBA5OP2WVGHVJMCQFQC
WLX2RQMMOMP2PYPAGJRM4VFD2WTLJTOAZZPPY3MV76FU2EGEJ54QC
ASLW3Z5PAVZSWJEMMMVZT226P44EKSAD47QS72JIFJESAI3RPN3AC
BWAQ3FHBBM6G3K3KYP75CRTR343RDQZJRYX5ZGYUEXYBAC3APDLAC
5UVDIVD4NSXA52U4QMQIVST3GSZJ2A2YZK3RUEXKPM43YVQ7LI5AC
NCDWWDJQLAU5ORSAQGZKJJ5E22VTDGGPJMVVBWQFHQ2B3U3UFHDQC
3ZNI2YMHYXRVEONY5CGWXSRMFSLOATZMKU7H6HRY3CC2W6OZAM7QC
HNXKX6ZDQJV33E7UKZOLBYWJMRZ4QLEMXVXJZNRCTOIG2KVRTIEAC
5B5DP5S6A6LQMKZYVLQAEMHQZWFWYDHPCKQGRNSCNNYIBQYZ6BIQC
SH6NU4H5TG4O7CRXRHZ7MDHABZLWWXQ77NJDBHI7KCVHXHQYK46QC
QYDFLJPRJ3GKULPAMHDSTR4JFASE5T45FPQYXJ25PWVN77KFKPGAC
NXVPOFYKJFWQWKVPQUMWH2Y2KJEZX44BUOBFJ4JD4KFGPEGYHG4QC
OB4OY6HAYGB5LZ2VQSD2S7EG5M3YDDXIIMXM4EGIZ4BVO5IICWWQC
K2MLPJIAXXZRWEWZCNSGICCBNIU2WAAPT7SPIMOH7FLLTOB4QFRAC
TP5EDQXPVPTKQYTAMN3VQYHM4WRT2RNIR4EDLWLDAV2OQGKSB6KAC
CH7JECYYH35H4TRHRNRTRJCQTQLZ2WRH62TKV72SUIU2RTK5OH7AC
2VDLCWQOJPOXPJQ7XYGOWZG5P2AE44DGFHC76WI4GBOOK4B7FQTQC
72GIZBEMQLEF3TITOHABWHRLL5TE7KOUSVWOUEHFMO2OZQ4EIB7AC
THEWZBBFONK266AMYIIFQ4SFGZMTMU62ZO2Y43UMCDD6DDBSDYKAC
YL67KHG3TAZXJCWGRZPVASD6RS2SQ3V5KMIUK4E6PV43V2NBOLEAC
EHSY6DVGUMI6C67WKET3GDJVLWJWGYBYQONNDK5JVT7BCTHBEZVAC
D77K7ISUWRLGNSQGYH5P2KEJZCNYQHDZC5AMLSKTXVBIRNG6F5KQC
4VK7VHWRVRO66BRSGTBPSYJGGXLRF6AG6G74UDTPHTPXR7ZRNINQC
msg += init_file_location;
if (init_found)
{
#ifdef DGAMELAUNCH
// For dgl installs, show only the last segment of the .crawlrc
// file name so that we don't leak details of the directory
// structure to (untrusted) users.
msg += get_base_filename(Options.filename);
#else
msg += Options.filename;
#endif
msg += ".)";
}
else
{
msg += init_file_error;
msg += ", using defaults.";
}
FILE* f = NULL;
char name_buff[kPathLen];
// -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);
}
}
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);
}
}
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);
// 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 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"
};
// 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());
}
///////////////////////////////////////////////////////////////////////
}
bool is_absolute_path(const std::string &path)
{
return (!path.empty()
&& (path[0] == FILE_SEPARATOR
#if defined(WIN32CONSOLE) || defined(WIN32TILES)
|| path.find(':') != std::string::npos
#endif
));
}
// Concatenates two paths, separating them with FILE_SEPARATOR if necessary.
// Assumes that the second path is not absolute.
//
// If the first path is empty, returns the second unchanged. The second path
// may be absolute in this case.
std::string catpath(const std::string &first, const std::string &second)
{
if (first.empty())
return (second);
std::string directory = first;
if (directory[directory.length() - 1] != FILE_SEPARATOR)
directory += FILE_SEPARATOR;
directory += second;
return (directory);
}
// Given a relative path and a reference file name, returns the relative path
// suffixed to the directory containing the reference file name. Assumes that
// the second path is not absolute.
std::string get_path_relative_to(const std::string &referencefile,
const std::string &relativepath)
{
return catpath(get_parent_directory(referencefile),
relativepath);
}
return (true);
}
// Checks whether the given path is safe to read from. A path is safe if:
// 1. If Unix: It contains no shell metacharacters.
// 2. If DATA_DIR_PATH is set: the path is not an absolute path.
// 3. If DATA_DIR_PATH is set: the path contains no ".." sequence.
void assert_read_safe_path(const std::string &path) throw (std::string)
{
// Check for rank tomfoolery first:
if (path.empty())
throw "Empty file name.";
#ifdef UNIX
if (!shell_safe(path.c_str()))
throw make_stringf("\"%s\" contains bad characters.",
path.c_str());
#endif
#ifdef DATA_DIR_PATH
if (is_absolute_path(path))
throw make_stringf("\"%s\" is an absolute path.", path.c_str());
if (path.find("..") != std::string::npos)
throw make_stringf("\"%s\" contains \"..\" sequences.",
path.c_str());
#endif
// Path is okay.
}
bool is_read_safe_path(const std::string &path)
{
try
{
assert_read_safe_path(path);
void include(const std::string &file, bool resolve, bool runscript);
void report_error(const std::string &error);
std::string resolve_include(const std::string &file,
const char *type = "");
bool was_included(const std::string &file) const;
static std::string resolve_include(
std::string including_file,
std::string included_file,
const std::vector<std::string> *rcdirs = NULL)
throw (std::string);
FILE* f = fopen( filename, "r" );
if (f)
{
FileLineInput fl(f);
Options.read_options(fl, true);
fclose(f);
}
else
{
mprf(MSGCH_WARN, "Warning: could not read options file '%s'", filename);
}
Options.include(filename, true, true);
# You can use the travel_stop_message for making autotravel better behaved.
# The following file contains a list of such options, with explanations.
: crawl.read_options('docs/travel_stoppers.txt')
# You can use travel_stop_message to make autotravel stop for situations that
# warrant your attention. This file (travel_stoppers.txt) contains a list of
# travel_stop_message settings, with brief descriptions of what they do.
include docs/travel_stoppers.txt