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 string line;
00120 unsigned int line_no = 0;
00121 while (getline(stub, line)) {
00122 ++line_no;
00123 if (line.empty() || line[0] == '#')
00124 continue;
00125 string::size_type space = line.find(' ');
00126 if (space == string::npos) space = line.size();
00127
00128 string type(line, 0, space);
00129 line.erase(0, space + 1);
00130
00131 if (type == "auto") {
00132 resolve_relative_path(line, file);
00133 db.add_database(Database(line));
00134 continue;
00135 }
00136
00137 #ifdef XAPIAN_HAS_CHERT_BACKEND
00138 if (type == "chert") {
00139 resolve_relative_path(line, file);
00140 db.add_database(Chert::open(line));
00141 continue;
00142 }
00143 #endif
00144
00145 #ifdef XAPIAN_HAS_FLINT_BACKEND
00146 if (type == "flint") {
00147 resolve_relative_path(line, file);
00148 db.add_database(Flint::open(line));
00149 continue;
00150 }
00151 #endif
00152
00153 #ifdef XAPIAN_HAS_BRASS_BACKEND
00154 if (type == "brass") {
00155 resolve_relative_path(line, file);
00156 db.add_database(Brass::open(line));
00157 continue;
00158 }
00159 #endif
00160
00161 #ifdef XAPIAN_HAS_REMOTE_BACKEND
00162 if (type == "remote") {
00163 string::size_type colon = line.find(':');
00164 if (colon == 0) {
00165
00166
00167
00168 space = line.find(' ');
00169 string args;
00170 if (space != string::npos) {
00171 args.assign(line, space + 1, string::npos);
00172 line.assign(line, 1, space - 1);
00173 } else {
00174 line.erase(0, 1);
00175 }
00176 db.add_database(Remote::open(line, args));
00177 } else if (colon != string::npos) {
00178
00179
00180 unsigned int port = atoi(line.c_str() + colon + 1);
00181 line.erase(colon);
00182 db.add_database(Remote::open(line, port));
00183 }
00184 continue;
00185 }
00186 #endif
00187
00188 #ifdef XAPIAN_HAS_INMEMORY_BACKEND
00189 if (type == "inmemory" && line.empty()) {
00190 db.add_database(InMemory::open());
00191 continue;
00192 }
00193 #endif
00194
00195
00196
00197
00198
00199
00200 throw DatabaseOpeningError(file + ':' + str(line_no) + ": Bad line");
00201 }
00202
00203
00204
00205
00206
00207
00208
00209
00210 }
00211
00212 static void
00213 open_stub(WritableDatabase &db, const string &file, int action)
00214 {
00215
00216
00217
00218
00219
00220
00221
00222
00223 ifstream stub(file.c_str());
00224 string line;
00225 unsigned int line_no = 0;
00226 while (true) {
00227 if (db.internal.size() > 1) {
00228 throw DatabaseOpeningError(file + ": Can't open a stub database listing multiple databases as a WritableDatabase");
00229 }
00230
00231 if (!getline(stub, line)) break;
00232
00233 ++line_no;
00234 if (line.empty() || line[0] == '#')
00235 continue;
00236 string::size_type space = line.find(' ');
00237 if (space == string::npos) space = line.size();
00238
00239 string type(line, 0, space);
00240 line.erase(0, space + 1);
00241
00242 if (type == "auto") {
00243 resolve_relative_path(line, file);
00244 db.add_database(WritableDatabase(line, action));
00245 continue;
00246 }
00247
00248 #ifdef XAPIAN_HAS_CHERT_BACKEND
00249 if (type == "chert") {
00250 resolve_relative_path(line, file);
00251 db.add_database(Chert::open(line, action));
00252 continue;
00253 }
00254 #endif
00255
00256 #ifdef XAPIAN_HAS_FLINT_BACKEND
00257 if (type == "flint") {
00258 resolve_relative_path(line, file);
00259 db.add_database(Flint::open(line, action));
00260 continue;
00261 }
00262 #endif
00263
00264 #ifdef XAPIAN_HAS_BRASS_BACKEND
00265 if (type == "brass") {
00266 resolve_relative_path(line, file);
00267 db.add_database(Brass::open(line, action));
00268 continue;
00269 }
00270 #endif
00271
00272 #ifdef XAPIAN_HAS_REMOTE_BACKEND
00273 if (type == "remote") {
00274 string::size_type colon = line.find(':');
00275 if (colon == 0) {
00276
00277
00278
00279 space = line.find(' ');
00280 string args;
00281 if (space != string::npos) {
00282 args.assign(line, space + 1, string::npos);
00283 line.assign(line, 1, space - 1);
00284 } else {
00285 line.erase(0, 1);
00286 }
00287 db.add_database(Remote::open_writable(line, args));
00288 } else if (colon != string::npos) {
00289
00290
00291 unsigned int port = atoi(line.c_str() + colon + 1);
00292 line.erase(colon);
00293 db.add_database(Remote::open_writable(line, port));
00294 }
00295 continue;
00296 }
00297 #endif
00298
00299 #ifdef XAPIAN_HAS_INMEMORY_BACKEND
00300 if (type == "inmemory" && line.empty()) {
00301 db.add_database(InMemory::open());
00302 continue;
00303 }
00304 #endif
00305
00306
00307
00308
00309
00310
00311 throw DatabaseOpeningError(file + ':' + str(line_no) + ": Bad line");
00312 }
00313
00314 if (db.internal.empty()) {
00315 throw DatabaseOpeningError(file + ": No databases listed");
00316 }
00317 }
00318
00319 Database
00320 Auto::open_stub(const string &file)
00321 {
00322 LOGCALL_STATIC(API, Database, "Auto::open_stub", file);
00323 Database db;
00324 open_stub(db, file);
00325 RETURN(db);
00326 }
00327
00328 WritableDatabase
00329 Auto::open_stub(const string &file, int action)
00330 {
00331 LOGCALL_STATIC(API, WritableDatabase, "Auto::open_stub", file | action);
00332 WritableDatabase db;
00333 open_stub(db, file, action);
00334 RETURN(db);
00335 }
00336
00337 Database::Database(const string &path)
00338 {
00339 LOGCALL_CTOR(API, "Database", path);
00340
00341 struct stat statbuf;
00342 if (stat(path, &statbuf) == -1) {
00343 throw DatabaseOpeningError("Couldn't stat '" + path + "'", errno);
00344 }
00345
00346 if (S_ISREG(statbuf.st_mode)) {
00347
00348 open_stub(*this, path);
00349 return;
00350 }
00351
00352 if (rare(!S_ISDIR(statbuf.st_mode))) {
00353 throw DatabaseOpeningError("Not a regular file or directory: '" + path + "'");
00354 }
00355
00356 #ifdef XAPIAN_HAS_CHERT_BACKEND
00357 if (file_exists(path + "/iamchert")) {
00358 internal.push_back(new ChertDatabase(path));
00359 return;
00360 }
00361 #endif
00362
00363 #ifdef XAPIAN_HAS_FLINT_BACKEND
00364 if (file_exists(path + "/iamflint")) {
00365 internal.push_back(new FlintDatabase(path));
00366 return;
00367 }
00368 #endif
00369
00370 #ifdef XAPIAN_HAS_BRASS_BACKEND
00371 if (file_exists(path + "/iambrass")) {
00372 internal.push_back(new BrassDatabase(path));
00373 return;
00374 }
00375 #endif
00376
00377
00378 string stub_file = path;
00379 stub_file += "/XAPIANDB";
00380 if (rare(!file_exists(stub_file))) {
00381 throw DatabaseOpeningError("Couldn't detect type of database");
00382 }
00383
00384 open_stub(*this, stub_file);
00385 }
00386
00387 #if defined XAPIAN_HAS_FLINT_BACKEND || \
00388 defined XAPIAN_HAS_CHERT_BACKEND || \
00389 defined XAPIAN_HAS_BRASS_BACKEND
00390 #define HAVE_DISK_BACKEND
00391 #endif
00392
00393 WritableDatabase::WritableDatabase(const std::string &path, int action)
00394 : Database()
00395 {
00396 LOGCALL_CTOR(API, "WritableDatabase", path | action);
00397 #ifdef HAVE_DISK_BACKEND
00398 enum {
00399 #ifdef XAPIAN_HAS_CHERT_BACKEND
00400 CHERT,
00401 #endif
00402 #ifdef XAPIAN_HAS_FLINT_BACKEND
00403 FLINT,
00404 #endif
00405 #ifdef XAPIAN_HAS_BRASS_BACKEND
00406 BRASS,
00407 #endif
00408 UNSET
00409 } type = UNSET;
00410 #endif
00411 struct stat statbuf;
00412 if (stat(path, &statbuf) == -1) {
00413
00414 if (errno != ENOENT)
00415 throw DatabaseOpeningError("Couldn't stat '" + path + "'", errno);
00416 } else {
00417
00418
00419 if (S_ISREG(statbuf.st_mode)) {
00420
00421 open_stub(*this, path, action);
00422 return;
00423 }
00424
00425 if (rare(!S_ISDIR(statbuf.st_mode))) {
00426 throw DatabaseOpeningError("Not a regular file or directory: '" + path + "'");
00427 }
00428
00429 if (file_exists(path + "/iamchert")) {
00430
00431 #ifdef XAPIAN_HAS_CHERT_BACKEND
00432 type = CHERT;
00433 #else
00434 throw FeatureUnavailableError("Chert backend disabled");
00435 #endif
00436 } else if (file_exists(path + "/iamflint")) {
00437
00438 #ifdef XAPIAN_HAS_FLINT_BACKEND
00439 type = FLINT;
00440 #else
00441 throw FeatureUnavailableError("Flint backend disabled");
00442 #endif
00443 } else if (file_exists(path + "/iambrass")) {
00444
00445 #ifdef XAPIAN_HAS_BRASS_BACKEND
00446 type = BRASS;
00447 #else
00448 throw FeatureUnavailableError("Brass backend disabled");
00449 #endif
00450 } else {
00451
00452 string stub_file = path;
00453 stub_file += "/XAPIANDB";
00454 if (usual(file_exists(stub_file))) {
00455 open_stub(*this, stub_file, action);
00456 return;
00457 }
00458 }
00459 }
00460
00461 #ifdef HAVE_DISK_BACKEND
00462 switch (type) {
00463 case UNSET: {
00464 #ifdef XAPIAN_HAS_BRASS_BACKEND
00465
00466
00467 # if defined XAPIAN_HAS_CHERT_BACKEND || defined XAPIAN_HAS_FLINT_BACKEND
00468
00469
00470 const char *p = getenv("XAPIAN_PREFER_BRASS");
00471 if (p && *p)
00472 goto brass;
00473 #endif
00474 #endif
00475 }
00476
00477
00478 #ifdef XAPIAN_HAS_CHERT_BACKEND
00479 case CHERT:
00480 internal.push_back(new ChertWritableDatabase(path, action, 8192));
00481 break;
00482 #endif
00483 #ifdef XAPIAN_HAS_FLINT_BACKEND
00484 case FLINT:
00485 internal.push_back(new FlintWritableDatabase(path, action, 8192));
00486 break;
00487 #endif
00488 #ifdef XAPIAN_HAS_BRASS_BACKEND
00489 case BRASS:
00490 brass:
00491 internal.push_back(new BrassWritableDatabase(path, action, 8192));
00492 break;
00493 #endif
00494 }
00495 #else
00496 throw FeatureUnavailableError("No disk-based writable backend is enabled");
00497 #endif
00498 }
00499
00500 }