00001
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include <config.h>
00024
00025 #include "xapian/dbfactory.h"
00026
00027 #include "xapian/database.h"
00028 #include "xapian/error.h"
00029 #include "xapian/version.h"
00030
00031 #include "debuglog.h"
00032 #include "fileutils.h"
00033 #include "str.h"
00034 #include "utils.h"
00035
00036 #include "safeerrno.h"
00037
00038 #ifdef XAPIAN_HAS_BRASS_BACKEND
00039 # include "brass/brass_database.h"
00040 #endif
00041 #ifdef XAPIAN_HAS_CHERT_BACKEND
00042 # include "chert/chert_database.h"
00043 #endif
00044 #ifdef XAPIAN_HAS_FLINT_BACKEND
00045 # include "flint/flint_database.h"
00046 #endif
00047 #ifdef XAPIAN_HAS_INMEMORY_BACKEND
00048 # include "inmemory/inmemory_database.h"
00049 #endif
00050
00051 #include <fstream>
00052 #include <string>
00053
00054 using namespace std;
00055
00056 namespace Xapian {
00057
00058 #ifdef XAPIAN_HAS_BRASS_BACKEND
00059 Database
00060 Brass::open(const string &dir) {
00061 LOGCALL_STATIC(API, Database, "Brass::open", dir);
00062 RETURN(Database(new BrassDatabase(dir)));
00063 }
00064
00065 WritableDatabase
00066 Brass::open(const string &dir, int action, int block_size) {
00067 LOGCALL_STATIC(API, WritableDatabase, "Brass::open", dir | action | block_size);
00068 RETURN(WritableDatabase(new BrassWritableDatabase(dir, action, block_size)));
00069 }
00070 #endif
00071
00072 #ifdef XAPIAN_HAS_CHERT_BACKEND
00073 Database
00074 Chert::open(const string &dir) {
00075 LOGCALL_STATIC(API, Database, "Chert::open", dir);
00076 return Database(new ChertDatabase(dir));
00077 }
00078
00079 WritableDatabase
00080 Chert::open(const string &dir, int action, int block_size) {
00081 LOGCALL_STATIC(API, WritableDatabase, "Chert::open", dir | action | block_size);
00082 return WritableDatabase(new ChertWritableDatabase(dir, action, block_size));
00083 }
00084 #endif
00085
00086 #ifdef XAPIAN_HAS_FLINT_BACKEND
00087 Database
00088 Flint::open(const string &dir) {
00089 LOGCALL_STATIC(API, Database, "Flint::open", dir);
00090 return Database(new FlintDatabase(dir));
00091 }
00092
00093 WritableDatabase
00094 Flint::open(const string &dir, int action, int block_size) {
00095 LOGCALL_STATIC(API, WritableDatabase, "Flint::open", dir | action | block_size);
00096 return WritableDatabase(new FlintWritableDatabase(dir, action, block_size));
00097 }
00098 #endif
00099
00100 #ifdef XAPIAN_HAS_INMEMORY_BACKEND
00101 WritableDatabase
00102 InMemory::open() {
00103 LOGCALL_STATIC(API, Database, "InMemory::open", NO_ARGS);
00104 return WritableDatabase(new InMemoryDatabase);
00105 }
00106 #endif
00107
00108 static void
00109 open_stub(Database &db, const string &file)
00110 {
00111
00112
00113
00114
00115
00116
00117
00118 ifstream stub(file.c_str());
00119 if (!stub) {
00120 string msg = "Couldn't open stub database file: ";
00121 msg += file;
00122 throw Xapian::DatabaseOpeningError(msg, errno);
00123 }
00124 string line;
00125 unsigned int line_no = 0;
00126 while (getline(stub, line)) {
00127 ++line_no;
00128 if (line.empty() || line[0] == '#')
00129 continue;
00130 string::size_type space = line.find(' ');
00131 if (space == string::npos) space = line.size();
00132
00133 string type(line, 0, space);
00134 line.erase(0, space + 1);
00135
00136 if (type == "auto") {
00137 resolve_relative_path(line, file);
00138 db.add_database(Database(line));
00139 continue;
00140 }
00141
00142 #ifdef XAPIAN_HAS_CHERT_BACKEND
00143 if (type == "chert") {
00144 resolve_relative_path(line, file);
00145 db.add_database(Chert::open(line));
00146 continue;
00147 }
00148 #endif
00149
00150 #ifdef XAPIAN_HAS_FLINT_BACKEND
00151 if (type == "flint") {
00152 resolve_relative_path(line, file);
00153 db.add_database(Flint::open(line));
00154 continue;
00155 }
00156 #endif
00157
00158 #ifdef XAPIAN_HAS_BRASS_BACKEND
00159 if (type == "brass") {
00160 resolve_relative_path(line, file);
00161 db.add_database(Brass::open(line));
00162 continue;
00163 }
00164 #endif
00165
00166 #ifdef XAPIAN_HAS_REMOTE_BACKEND
00167 if (type == "remote") {
00168 string::size_type colon = line.find(':');
00169 if (colon == 0) {
00170
00171
00172
00173 space = line.find(' ');
00174 string args;
00175 if (space != string::npos) {
00176 args.assign(line, space + 1, string::npos);
00177 line.assign(line, 1, space - 1);
00178 } else {
00179 line.erase(0, 1);
00180 }
00181 db.add_database(Remote::open(line, args));
00182 } else if (colon != string::npos) {
00183
00184
00185 unsigned int port = atoi(line.c_str() + colon + 1);
00186 line.erase(colon);
00187 db.add_database(Remote::open(line, port));
00188 }
00189 continue;
00190 }
00191 #endif
00192
00193 #ifdef XAPIAN_HAS_INMEMORY_BACKEND
00194 if (type == "inmemory" && line.empty()) {
00195 db.add_database(InMemory::open());
00196 continue;
00197 }
00198 #endif
00199
00200
00201
00202
00203
00204
00205 throw DatabaseOpeningError(file + ':' + str(line_no) + ": Bad line");
00206 }
00207
00208
00209
00210
00211
00212
00213
00214
00215 }
00216
00217 static void
00218 open_stub(WritableDatabase &db, const string &file, int action)
00219 {
00220
00221
00222
00223
00224
00225
00226
00227
00228 ifstream stub(file.c_str());
00229 if (!stub) {
00230 string msg = "Couldn't open stub database file: ";
00231 msg += file;
00232 throw Xapian::DatabaseOpeningError(msg, errno);
00233 }
00234 string line;
00235 unsigned int line_no = 0;
00236 while (true) {
00237 if (db.internal.size() > 1) {
00238 throw DatabaseOpeningError(file + ": Can't open a stub database listing multiple databases as a WritableDatabase");
00239 }
00240
00241 if (!getline(stub, line)) break;
00242
00243 ++line_no;
00244 if (line.empty() || line[0] == '#')
00245 continue;
00246 string::size_type space = line.find(' ');
00247 if (space == string::npos) space = line.size();
00248
00249 string type(line, 0, space);
00250 line.erase(0, space + 1);
00251
00252 if (type == "auto") {
00253 resolve_relative_path(line, file);
00254 db.add_database(WritableDatabase(line, action));
00255 continue;
00256 }
00257
00258 #ifdef XAPIAN_HAS_CHERT_BACKEND
00259 if (type == "chert") {
00260 resolve_relative_path(line, file);
00261 db.add_database(Chert::open(line, action));
00262 continue;
00263 }
00264 #endif
00265
00266 #ifdef XAPIAN_HAS_FLINT_BACKEND
00267 if (type == "flint") {
00268 resolve_relative_path(line, file);
00269 db.add_database(Flint::open(line, action));
00270 continue;
00271 }
00272 #endif
00273
00274 #ifdef XAPIAN_HAS_BRASS_BACKEND
00275 if (type == "brass") {
00276 resolve_relative_path(line, file);
00277 db.add_database(Brass::open(line, action));
00278 continue;
00279 }
00280 #endif
00281
00282 #ifdef XAPIAN_HAS_REMOTE_BACKEND
00283 if (type == "remote") {
00284 string::size_type colon = line.find(':');
00285 if (colon == 0) {
00286
00287
00288
00289 space = line.find(' ');
00290 string args;
00291 if (space != string::npos) {
00292 args.assign(line, space + 1, string::npos);
00293 line.assign(line, 1, space - 1);
00294 } else {
00295 line.erase(0, 1);
00296 }
00297 db.add_database(Remote::open_writable(line, args));
00298 } else if (colon != string::npos) {
00299
00300
00301 unsigned int port = atoi(line.c_str() + colon + 1);
00302 line.erase(colon);
00303 db.add_database(Remote::open_writable(line, port));
00304 }
00305 continue;
00306 }
00307 #endif
00308
00309 #ifdef XAPIAN_HAS_INMEMORY_BACKEND
00310 if (type == "inmemory" && line.empty()) {
00311 db.add_database(InMemory::open());
00312 continue;
00313 }
00314 #endif
00315
00316
00317
00318
00319
00320
00321 throw DatabaseOpeningError(file + ':' + str(line_no) + ": Bad line");
00322 }
00323
00324 if (db.internal.empty()) {
00325 throw DatabaseOpeningError(file + ": No databases listed");
00326 }
00327 }
00328
00329 Database
00330 Auto::open_stub(const string &file)
00331 {
00332 LOGCALL_STATIC(API, Database, "Auto::open_stub", file);
00333 Database db;
00334 open_stub(db, file);
00335 RETURN(db);
00336 }
00337
00338 WritableDatabase
00339 Auto::open_stub(const string &file, int action)
00340 {
00341 LOGCALL_STATIC(API, WritableDatabase, "Auto::open_stub", file | action);
00342 WritableDatabase db;
00343 open_stub(db, file, action);
00344 RETURN(db);
00345 }
00346
00347 Database::Database(const string &path)
00348 {
00349 LOGCALL_CTOR(API, "Database", path);
00350
00351 struct stat statbuf;
00352 if (stat(path, &statbuf) == -1) {
00353 throw DatabaseOpeningError("Couldn't stat '" + path + "'", errno);
00354 }
00355
00356 if (S_ISREG(statbuf.st_mode)) {
00357
00358 open_stub(*this, path);
00359 return;
00360 }
00361
00362 if (rare(!S_ISDIR(statbuf.st_mode))) {
00363 throw DatabaseOpeningError("Not a regular file or directory: '" + path + "'");
00364 }
00365
00366 #ifdef XAPIAN_HAS_CHERT_BACKEND
00367 if (file_exists(path + "/iamchert")) {
00368 internal.push_back(new ChertDatabase(path));
00369 return;
00370 }
00371 #endif
00372
00373 #ifdef XAPIAN_HAS_FLINT_BACKEND
00374 if (file_exists(path + "/iamflint")) {
00375 internal.push_back(new FlintDatabase(path));
00376 return;
00377 }
00378 #endif
00379
00380 #ifdef XAPIAN_HAS_BRASS_BACKEND
00381 if (file_exists(path + "/iambrass")) {
00382 internal.push_back(new BrassDatabase(path));
00383 return;
00384 }
00385 #endif
00386
00387
00388 string stub_file = path;
00389 stub_file += "/XAPIANDB";
00390 if (rare(!file_exists(stub_file))) {
00391 throw DatabaseOpeningError("Couldn't detect type of database");
00392 }
00393
00394 open_stub(*this, stub_file);
00395 }
00396
00397 #if defined XAPIAN_HAS_FLINT_BACKEND || \
00398 defined XAPIAN_HAS_CHERT_BACKEND || \
00399 defined XAPIAN_HAS_BRASS_BACKEND
00400 #define HAVE_DISK_BACKEND
00401 #endif
00402
00403 WritableDatabase::WritableDatabase(const std::string &path, int action)
00404 : Database()
00405 {
00406 LOGCALL_CTOR(API, "WritableDatabase", path | action);
00407 #ifdef HAVE_DISK_BACKEND
00408 enum {
00409 #ifdef XAPIAN_HAS_CHERT_BACKEND
00410 CHERT,
00411 #endif
00412 #ifdef XAPIAN_HAS_FLINT_BACKEND
00413 FLINT,
00414 #endif
00415 #ifdef XAPIAN_HAS_BRASS_BACKEND
00416 BRASS,
00417 #endif
00418 UNSET
00419 } type = UNSET;
00420 #endif
00421 struct stat statbuf;
00422 if (stat(path, &statbuf) == -1) {
00423
00424 if (errno != ENOENT)
00425 throw DatabaseOpeningError("Couldn't stat '" + path + "'", errno);
00426 } else {
00427
00428
00429 if (S_ISREG(statbuf.st_mode)) {
00430
00431 open_stub(*this, path, action);
00432 return;
00433 }
00434
00435 if (rare(!S_ISDIR(statbuf.st_mode))) {
00436 throw DatabaseOpeningError("Not a regular file or directory: '" + path + "'");
00437 }
00438
00439 if (file_exists(path + "/iamchert")) {
00440
00441 #ifdef XAPIAN_HAS_CHERT_BACKEND
00442 type = CHERT;
00443 #else
00444 throw FeatureUnavailableError("Chert backend disabled");
00445 #endif
00446 } else if (file_exists(path + "/iamflint")) {
00447
00448 #ifdef XAPIAN_HAS_FLINT_BACKEND
00449 type = FLINT;
00450 #else
00451 throw FeatureUnavailableError("Flint backend disabled");
00452 #endif
00453 } else if (file_exists(path + "/iambrass")) {
00454
00455 #ifdef XAPIAN_HAS_BRASS_BACKEND
00456 type = BRASS;
00457 #else
00458 throw FeatureUnavailableError("Brass backend disabled");
00459 #endif
00460 } else {
00461
00462 string stub_file = path;
00463 stub_file += "/XAPIANDB";
00464 if (usual(file_exists(stub_file))) {
00465 open_stub(*this, stub_file, action);
00466 return;
00467 }
00468 }
00469 }
00470
00471 #ifdef HAVE_DISK_BACKEND
00472 switch (type) {
00473 case UNSET: {
00474 #ifdef XAPIAN_HAS_BRASS_BACKEND
00475
00476
00477 # if defined XAPIAN_HAS_CHERT_BACKEND || defined XAPIAN_HAS_FLINT_BACKEND
00478
00479
00480 const char *p = getenv("XAPIAN_PREFER_BRASS");
00481 if (p && *p)
00482 goto brass;
00483 #endif
00484 #endif
00485 }
00486
00487
00488 #ifdef XAPIAN_HAS_CHERT_BACKEND
00489 case CHERT:
00490 internal.push_back(new ChertWritableDatabase(path, action, 8192));
00491 break;
00492 #endif
00493 #ifdef XAPIAN_HAS_FLINT_BACKEND
00494 case FLINT:
00495 internal.push_back(new FlintWritableDatabase(path, action, 8192));
00496 break;
00497 #endif
00498 #ifdef XAPIAN_HAS_BRASS_BACKEND
00499 case BRASS:
00500 brass:
00501 internal.push_back(new BrassWritableDatabase(path, action, 8192));
00502 break;
00503 #endif
00504 }
00505 #else
00506 throw FeatureUnavailableError("No disk-based writable backend is enabled");
00507 #endif
00508 }
00509
00510 }