untested. Will probably not build out-of-the-box on other BSDs (we need an autoconf script to use the right dbm headers). DOS and Windows builds are still broken pending adding Berkeley DB 1.x sources to the Crawl tree, which I'll probably do sometime tomorrow.
Renamed descriptions.txt -> descript.txt to fit into DOS filename limits.
The descript.txt -> descript.db conversion is now performed automatically by Crawl on startup if descript.txt is newer than descript.db (or descript.db does not exist). descript.db is saved in the save directory which is a convenient directory that is guaranteed writable by Crawl.
The makedb.pl script is no longer necessary (and is unreliable anyway, because it's easy to have multiple dbm versions with incompatible db formats and have the installed Perl link with one while Crawl uses the other, leading to nasty segfaults).
Some .des file changes (Erik).
Deep elf sorcerers are M_EVIL.
Ghosts get mephitic cloud.
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@1337 c06c8d41-db1a-0410-9941-cceddc491573
KR655YT3I3U5DMN5NS3FPUGMDNT4BI56K3SFF2FNJ77C45NFKL5AC
3A537UMVZSYIFVKVO3SVK5JRSRLDN3YJKK3RKELHR2D4U6CMKM4QC
Y56C5OMUQ5XF2G6DKDV4R5MED44UOIUPTBBQVWQBUHYIXYA5MOZAC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC
7AMQN7MITMXBNVDAK5VOXTQ4TZIAOD6ZLOFJG7GQMBTY23Y2BKSAC
2NCKGJDDPPGP2NXYYPEPVRJIIWEP6M7HE6WYMQN3UNNN3C2JIRFQC
SDLKLUNFGVKDS55DDJZCBAVIB7NL3RRYPTACAY65SCUQKV6APFSAC
OJH7P7REIZ3ED25ELK2W55VW56H3ZSORZB63BTU6IUVUPHH7GNTQC
RIRJ746W5ESARX4HUEA4JRVAKXXF3WYVXUCFFONPJMMKWHQAGI2AC
442VGKMARB6LTQUEBIB5P447EI34BRJL6JALZKXLWPDHWCM6KKCQC
6HQB2N6N75R2RGKJFWRUN7WAC2PNGWQFXTII5DTRLTHZ2BOTMTVAC
34C4U6EQWERY75GZJKUCM5KVGU2OUICETS5LGZF6RMKMZT4R5SQAC
BUUPPUTWFL2NTMFJWP7LKDXON74CX47E37WDAZ3QI4N55UOUQTXAC
22RFWMSJGG26Z2MQEEXGKVTFSTLREHQIG46WYOTMDRKI5YVMRNVAC
TR4NPGNO5QNNRJNVMNSUEO5QLT37HCXXDOBKXCB5XWXRQNAJ5SHAC
class file_lock
{
public:
file_lock(const std::string &filename, const char *mode,
bool die_on_fail = true);
~file_lock();
private:
FILE *handle;
const char *mode;
std::string filename;
};
#endif
#endif
}
/////////////////////////////////////////////////////////////////////////////
// file_lock
//
// Locks a named file (usually an empty lock file), creating it if necessary.
file_lock::file_lock(const std::string &s, const char *_mode, bool die_on_fail)
: handle(NULL), mode(_mode), filename(s)
{
#ifdef USE_FILE_LOCKING
if (!(handle = lk_open(mode, filename)) && die_on_fail)
end(1, true, "Unable to open lock file \"%s\"", filename.c_str());
#endif
}
file_lock::~file_lock()
{
#ifdef USE_FILE_LOCKING
if (handle)
lk_close(handle, mode, filename);
void databaseSystemInit() {
if (!databaseIsInitted) {
databaseIsInitted = true;
openDBList = nil;
std::string descriptionPath = datafile_path("descriptions.db");
descriptionPath.erase(descriptionPath.length() - 3, descriptionPath.length());
descriptionDB = openDB(descriptionPath.c_str());
return (filestat.st_mtime);
}
// Returns true if file a is newer than file b.
bool is_newer(const std::string &a, const std::string &b)
{
return (file_modtime(a) > file_modtime(b));
}
void check_newer(const std::string &target,
const std::string &dependency,
void (*action)())
{
if (is_newer(dependency, target))
action();
}
void databaseSystemInit()
{
if (!descriptionDB)
{
std::string descriptionPath = get_savedir_path(DESC_DB);
check_newer(descriptionPath,
datafile_path(DESC_TXT),
generate_description_db);
descriptionPath.erase(descriptionPath.length() - 3);
if (!(descriptionDB = openDB(descriptionPath.c_str())))
end(1, true, "Failed to open DB: %s", descriptionPath.c_str());
if (dbToReturn) {
if (openDBList) {
dbList_t *endOfDBList = openDBList;
while (openDBList->next) {
endOfDBList = openDBList->next;
}
endOfDBList->next = (dbList_t *)malloc(sizeof(dbList_t));
endOfDBList = endOfDBList->next;
endOfDBList->next = nil;
endOfDBList->dbPtr = dbToReturn;
} else {
openDBList = (dbList_t *)malloc(sizeof(dbList_t));
openDBList->next = nil;
openDBList->dbPtr = dbToReturn;
}
} else {
dbToReturn = nil;
}
if (dbToReturn)
openDBList.push_front(dbToReturn);
std::string stringToReturn((const char *)result.dptr, result.dsize);
// free the result...
free(lowercaseKey);
return stringToReturn;
return std::string((const char *)result.dptr, result.dsize);
}
static void store_descriptions(const std::string &in, const std::string &out);
static void generate_description_db()
{
std::string db_path = get_savedir_path(DESC_BASE_NAME);
std::string txt_path = datafile_path(DESC_TXT);
file_lock lock(get_savedir_path(DESC_BASE_NAME ".lk"), "wb");
store_descriptions(txt_path, db_path);
DO_CHMOD_PRIVATE(get_savedir_path(DESC_DB).c_str());
}
static void trim_right(std::string &s)
{
s.erase(s.find_last_not_of(" \r\t\n") + 1);
}
static void trim_leading_newlines(std::string &s)
{
s.erase(0, s.find_first_not_of("\n"));
}
static void add_entry(DBM *db, const std::string &k, std::string &v)
{
trim_leading_newlines(v);
datum key, value;
key.dptr = (char *) k.c_str();
key.dsize = k.length();
value.dptr = (char *) v.c_str();
value.dsize = v.length();
if (dbm_store(db, key, value, DBM_REPLACE))
end(1, true, "Error storing %s", k.c_str());
}
static void parse_descriptions(std::ifstream &inf, DBM *db)
{
char buf[1000];
std::string key;
std::string value;
bool in_entry = false;
while (!inf.eof())
{
inf.getline(buf, sizeof buf);
if (*buf == '#')
continue;
if (!strncmp(buf, "%%%%", 4))
{
if (!key.empty())
add_entry(db, key, value);
key.clear();
value.clear();
in_entry = true;
continue;
}
if (!in_entry)
continue;
if (key.empty())
{
key = buf;
trim_string(key);
lowercase(key);
}
else
{
std::string line = buf;
trim_right(line);
value += line + "\n";
}
}
if (!key.empty())
add_entry(db, key, value);
}
static void store_descriptions(const std::string &in, const std::string &out)
{
std::ifstream inf(in.c_str());
if (!inf)
end(1, true, "Unable to open input file: %s", in.c_str());
if (DBM *db = dbm_open(out.c_str(), O_RDWR | O_CREAT, 0660))
{
parse_descriptions(inf, db);
dbm_close(db);
}
else
end(1, true, "Unable to open DB: %s", out.c_str());
inf.close();