summaryrefslogtreecommitdiffstats
path: root/crawl-ref/source/database.cc
diff options
context:
space:
mode:
Diffstat (limited to 'crawl-ref/source/database.cc')
-rw-r--r--crawl-ref/source/database.cc246
1 files changed, 167 insertions, 79 deletions
diff --git a/crawl-ref/source/database.cc b/crawl-ref/source/database.cc
index 71927f6b8e..d8d954e912 100644
--- a/crawl-ref/source/database.cc
+++ b/crawl-ref/source/database.cc
@@ -14,48 +14,71 @@
#include <unistd.h>
#include <stdlib.h>
#endif
-#include <stdlib.h>
+#include <cstdlib>
+#include <fstream>
#include "database.h"
#include "files.h"
+#include "libutil.h"
+#include "stuff.h"
// Convenience functions for (read-only) access to generic
// berkeley DB databases.
-#define nil 0
+db_list openDBList;
+DBM *descriptionDB;
-static bool databaseIsInitted = false;
-dbList_t *openDBList;
-DBM *descriptionDB;
+#define DESC_BASE_NAME "descript"
+#define DESC_TXT (DESC_BASE_NAME ".txt")
+#define DESC_DB (DESC_BASE_NAME ".db")
-struct dbList_s {
- DBM *dbPtr;
- dbList_s *next;
-} dbList_s;
+static void generate_description_db();
+time_t file_modtime(const std::string &file)
+{
+ struct stat filestat;
+ if (stat(file.c_str(), &filestat))
+ return (0);
-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 databaseSystemShutdown() {
- if (!databaseIsInitted) {
- // All done.
- return;
+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());
}
- dbList_t *current = openDBList;
- dbList_t *prev = nil;
- while (current) {
- dbm_close(current->dbPtr);
- prev = current;
- current = current->next;
- free(prev);
+}
+
+void databaseSystemShutdown()
+{
+ for (db_list::iterator i = openDBList.begin();
+ i != openDBList.end(); ++i)
+ {
+ dbm_close(*i);
}
+ openDBList.clear();
+ descriptionDB = NULL;
}
// This is here, and is external, just for future expansion -- if we
@@ -64,76 +87,141 @@ void databaseSystemShutdown() {
// morally wrong to have the database module manage the memory here.
// But hey, life is hard and you can write your own berkeley DB
// calls if you like.
-DBM *openDB(const char *dbFilename) {
- if (!databaseIsInitted) {
- return nil;
- }
+DBM *openDB(const char *dbFilename)
+{
DBM *dbToReturn = dbm_open(dbFilename, O_RDONLY, 0660);
- 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);
+
return dbToReturn;
}
-datum database_fetch(DBM *database, const char *key) {
+datum database_fetch(DBM *database, const std::string &key)
+{
datum result;
- result.dptr = nil;
+ result.dptr = NULL;
result.dsize = 0;
- if (!databaseIsInitted) {
- return result;
- }
datum dbKey;
- dbKey.dptr = (void *)key;
- dbKey.dsize = strlen(key);
+ dbKey.dptr = (DPTR_COERCE) key.c_str();
+ dbKey.dsize = key.length();
result = dbm_fetch(database, dbKey);
return result;
}
-char *private_strlwr(char *string) {
- int i;
- for (i = 0; string[i]; i++) {
- string[i] = tolower(string[i]);
- }
- return string;
-}
+std::string getLongDescription(const char *key)
+{
+ if (!descriptionDB)
+ return ("");
-std::string getLongDescription(const char *key) {
- if (!databaseIsInitted) {
- return nil;
- }
// We have to canonicalize the key (in case the user typed it
// in and got the case wrong.
- int len = strlen(key);
- char *lowercaseKey = (char *)malloc(len+1);
- (void) strncpy(lowercaseKey, key, len+1);
- lowercaseKey = private_strlwr(lowercaseKey);
+ std::string canonical_key = key;
+ lowercase(canonical_key);
// Query the DB.
- datum result = database_fetch(descriptionDB, (const char *)lowercaseKey);
+ datum result = database_fetch(descriptionDB, canonical_key);
// Cons up a (C++) string to return. The caller must release it.
- 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();
}