1 ///###////////////////////////////////////////////////////////////////////////
2 //
3 // Burton Computer Corporation
4 // http://www.burton-computer.com
5 // http://www.cooldevtools.com
6 // $Id: DatabaseConfig.cc 272 2007-01-06 19:37:27Z brian $
7 //
8 // Copyright (C) 2007 Burton Computer Corporation
9 // ALL RIGHTS RESERVED
10 //
11 // This program is open source software; you can redistribute it
12 // and/or modify it under the terms of the Q Public License (QPL)
13 // version 1.0. Use of this software in whole or in part, including
14 // linking it (modified or unmodified) into other programs is
15 // subject to the terms of the QPL.
16 //
17 // This program is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 // Q Public License for more details.
21 //
22 // You should have received a copy of the Q Public License
23 // along with this program; see the file LICENSE.txt.  If not, visit
24 // the Burton Computer Corporation or CoolDevTools web site
25 // QPL pages at:
26 //
27 //    http://www.burton-computer.com/qpl.html
28 //    http://www.cooldevtools.com/qpl.html
29 //
30 
31 #include <stdexcept>
32 #include <cstdlib>
33 #include "File.h"
34 #include "WordData.h"
35 #include "FrequencyDBImpl.h"
36 #include "FrequencyDBImpl_split.h"
37 #include "FrequencyDBImpl_hash.h"
38 #include "FrequencyDBImpl_cache.h"
39 #include "FrequencyDBImpl_dual.h"
40 #include "FrequencyDBImpl_dbm.h"
41 #include "FrequencyDBImpl_bdb.h"
42 #include "FrequencyDBImpl_pbl.h"
43 #include "FrequencyDBImpl_null.h"
44 #include "DatabaseConfig.h"
45 
46 struct DBType {
47   const string prefix;
48   const char *suffix;
49   FrequencyDBImplFactory factory;
50 };
51 
52 static DBType DBTYPES[] = {
53 #ifdef USE_MMAP
54   { "split", 0, FrequencyDBImpl_split::factory },
55   { "hash", FrequencyDBImpl_hash::SEARCH_SUFFIX, FrequencyDBImpl_hash::factory },
56 #endif
57 #ifdef USE_PBL
58   { "pbl", FrequencyDBImpl_pbl::SEARCH_SUFFIX, FrequencyDBImpl_pbl::factory },
59 #endif
60 #ifdef USE_DB
61   { "bdb", 0, FrequencyDBImpl_bdb::factory },
62 #endif
63 #ifdef USE_DBM
64   { "gdbm", 0, FrequencyDBImpl_dbm::factory },
65 #endif
66 #ifdef USE_PBL
67   { "", FrequencyDBImpl_pbl::SEARCH_SUFFIX, FrequencyDBImpl_pbl::factory },
68 #endif
69 #ifdef USE_DB
70   { "", 0, FrequencyDBImpl_bdb::factory },
71 #endif
72 #ifdef USE_DBM
73   { "", 0, FrequencyDBImpl_dbm::factory },
74 #endif
75 #ifdef USE_MMAP
76   { "", FrequencyDBImpl_hash::SEARCH_SUFFIX, FrequencyDBImpl_hash::factory },
77 #endif
78   { "", 0 }
79 };
80 
DatabaseConfig()81 DatabaseConfig::DatabaseConfig()
82   : m_targetSizeMB(32),
83     m_maxCacheTerms(15000)
84 {
85   File basedir(File::getHomeDir(), ".spamprobe");
86   File sp_dir(basedir, "sp_words");
87   m_privateFilename = sp_dir.getPath();
88 }
89 
~DatabaseConfig()90 DatabaseConfig::~DatabaseConfig()
91 {
92 }
93 
parseCommandLineArg(string & arg,string & database_type,int & target_size_mb)94 void DatabaseConfig::parseCommandLineArg(string &arg,
95                                          string &database_type,
96                                          int &target_size_mb)
97 {
98   database_type = "";
99   target_size_mb = m_targetSizeMB;
100 
101   string::size_type first_colon = arg.find(":");
102   if (first_colon == string::npos) {
103     return;
104   }
105 
106   database_type = arg.substr(0, first_colon);
107 
108   string::size_type last_colon = arg.find(":", first_colon + 1);
109   if (last_colon == string::npos) {
110     arg.erase(0, first_colon + 1);
111   } else {
112     target_size_mb = atoi(arg.substr(first_colon + 1, last_colon - first_colon - 1).c_str());
113     arg.erase(0, last_colon + 1);
114   }
115 }
116 
createDatabaseImpl(bool read_only) const117 OWNED FrequencyDBImpl *DatabaseConfig::createDatabaseImpl(bool read_only) const
118 {
119   string private_filename(m_privateFilename);
120   Ptr<FrequencyDBImpl> private_db(createDatabaseImplUsingBestFit(m_databaseType, private_filename));
121 
122   Ptr<FrequencyDBImpl> db;
123   if (m_sharedFilename.length() == 0) {
124     db.set(private_db.release());
125   } else {
126     string shared_filename(m_sharedFilename);
127     Ptr<FrequencyDBImpl> shared_db(createDatabaseImplUsingBestFit("", shared_filename));
128     db.set(new FrequencyDBImpl_dual(shared_db.release(), private_db.release(), shared_filename));
129   }
130   if (m_maxCacheTerms > 0) {
131     db.set(new FrequencyDBImpl_cache(db.release(), m_maxCacheTerms));
132   }
133   return db->open(private_filename, read_only, FrequencyDBImpl::PRIVATE_DB_MODE) ? db.release() : 0;
134 }
135 
createDatabaseImplUsingBestFit(const string & type,string & filename) const136 OWNED FrequencyDBImpl *DatabaseConfig::createDatabaseImplUsingBestFit(const string &type,
137                                                                       string &filename) const
138 {
139   Ptr<FrequencyDBImpl> db;
140 
141   if (db.isNull() && type.length() > 0) {
142     db.set(createDatabaseImplForType(type, filename));
143   }
144 
145   if (db.isNull()) {
146     db.set(createDatabaseImplUsingExistingFile(filename));
147   }
148 
149   if (db.isNull()) {
150     db.set(createDatabaseImplUsingDefaultType(filename));
151   }
152 
153   if (db.isNull()) {
154     throw runtime_error(string("no database type known for filename: ") + filename);
155   }
156 
157   return db.release();
158 }
159 
createDatabaseImplForType(const string & type,string & filename) const160 OWNED FrequencyDBImpl *DatabaseConfig::createDatabaseImplForType(const string &type,
161                                                                  string &filename) const
162 {
163   File search_file(filename);
164 
165   for (DBType *dbt = DBTYPES; dbt->factory; ++dbt) {
166     if (dbt->prefix == type) {
167       if (dbt->suffix) {
168         search_file.setSuffix(dbt->suffix);
169       }
170       if (is_debug) {
171         cerr << "USING REQUESTED DATABASE TYPE '" << dbt->prefix << "' PATH " << search_file.getPath() << endl;
172       }
173       filename = search_file.getPath();
174       return (dbt->factory)(this);
175     }
176   }
177 
178   return 0;
179 }
180 
createDatabaseImplUsingDefaultType(string & filename) const181 OWNED FrequencyDBImpl *DatabaseConfig::createDatabaseImplUsingDefaultType(string &filename) const
182 {
183   return createDatabaseImplForType("", filename);
184 }
185 
createDatabaseImplUsingExistingFile(string & filename) const186 OWNED FrequencyDBImpl *DatabaseConfig::createDatabaseImplUsingExistingFile(string &filename) const
187 {
188   File search_file(filename);
189 
190   for (DBType *dbt = DBTYPES; dbt->factory; ++dbt) {
191     if (dbt->suffix) {
192       search_file.setSuffix(dbt->suffix);
193       if (search_file.isFile()) {
194         if (is_debug) {
195           cerr << "USING DISCOVERED DATABASE TYPE '" << dbt->prefix << "' PATH " << search_file.getPath() << endl;
196         }
197         filename = search_file.getPath();
198         return (dbt->factory)(this);
199       }
200     }
201   }
202 
203   return 0;
204 }
205