You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1534 lines
30 KiB
1534 lines
30 KiB
/***************************************************************************
|
|
database.cpp -
|
|
-------------------
|
|
begin : Fri Sep 8 2000
|
|
copyright : (C) 2000 by Andrea Rizzi
|
|
email : rizzi@kde.org
|
|
***************************************************************************/
|
|
|
|
/*
|
|
Translation search engine
|
|
|
|
|
|
Copyright 2000
|
|
Andrea Rizzi rizzi@kde.org
|
|
Copyright 2003 Stanislav Visnovsky visnovsky@kde.org
|
|
|
|
License GPL v 2.0
|
|
|
|
* *
|
|
* In addition, as a special exception, the copyright holders give *
|
|
* permission to link the code of this program with any edition of *
|
|
* the TQt library by Trolltech AS, Norway (or with modified versions *
|
|
* of TQt that use the same license as TQt), and distribute linked *
|
|
* combinations including the two. You must obey the GNU General *
|
|
* Public License in all respects for all of the code used other than *
|
|
* TQt. If you modify this file, you may extend this exception to *
|
|
* your version of the file, but you are not obligated to do so. If *
|
|
* you do not wish to do so, delete this exception statement from *
|
|
* your version. *
|
|
|
|
*/
|
|
#include <stdlib.h>
|
|
#include <kdebug.h>
|
|
#include <string.h>
|
|
#include <resources.h>
|
|
#include "database.h"
|
|
#include <unistd.h>
|
|
|
|
#include <tqfile.h>
|
|
|
|
#include <ktempfile.h>
|
|
#include <kio/netaccess.h>
|
|
|
|
WordItem::WordItem (char *data, TQString w)
|
|
{
|
|
word = w;
|
|
|
|
count = *(uint32 *) data;
|
|
|
|
data += 4;
|
|
//score=*(int *)data;
|
|
data += 4;
|
|
|
|
|
|
locations = (uint32 *) malloc (4 * count);
|
|
memcpy (locations, data, 4 * count);
|
|
|
|
|
|
}
|
|
|
|
WordItem::WordItem (TQString w)
|
|
{
|
|
locations = NULL;
|
|
count = 0;
|
|
word = w;
|
|
score = -1; // it means no references found.
|
|
}
|
|
|
|
/*
|
|
|
|
WordItem::WordItem(const WordItem &wi)
|
|
{
|
|
count=wi.count;
|
|
score=wi.score;
|
|
word=wi.word;
|
|
locations.duplicate(wi.locations);
|
|
locations.detach();
|
|
}
|
|
|
|
WordItem& WordItem::operator=(const WordItem & wi )
|
|
{
|
|
WordItem *i=new WordItem(wi);
|
|
i->locations.detach();
|
|
return *i;
|
|
}
|
|
*/
|
|
|
|
bool
|
|
WordItem::notFound ()
|
|
{
|
|
return (score == -1);
|
|
}
|
|
|
|
InfoItem::InfoItem ()
|
|
{
|
|
catalogName = "No catalog";
|
|
lastTranslator = "No translator";
|
|
lastFullPath = "";
|
|
charset = "No charset";
|
|
language = "No language";
|
|
}
|
|
|
|
InfoItem::InfoItem (const char *rawData, TQString lang)
|
|
{
|
|
const char *rd;
|
|
rd = rawData;
|
|
int len;
|
|
unsigned int secs;
|
|
// I'll change the charset handling if needed
|
|
|
|
charset = "Utf8";
|
|
|
|
catalogName = TQString::fromUtf8 (rd);
|
|
len = strlen (rd) + 1;
|
|
rd += len;
|
|
lastTranslator = TQString::fromUtf8 (rd);
|
|
len = strlen (rd) + 1;
|
|
rd += len;
|
|
secs = *(unsigned int *) rd;
|
|
revisionDate.setTime_t (secs);
|
|
rd += 4;
|
|
lastFullPath = TQString::fromUtf8 (rd);
|
|
len = strlen (rd) + 1;
|
|
rd += len;
|
|
|
|
|
|
language = lang;
|
|
}
|
|
|
|
void
|
|
InfoItem::rawData (char *rawData)
|
|
{
|
|
char *rd;
|
|
rd = rawData;
|
|
|
|
strcpy (rd, catalogName.utf8 ());
|
|
rd += strlen (rd) + 1;
|
|
strcpy (rd, lastTranslator.utf8 ());
|
|
rd += strlen (rd) + 1;
|
|
|
|
|
|
//TQDate Time problem!!!!!!!!!!!
|
|
TQDateTime zeroDate;
|
|
zeroDate.setTime_t (0);
|
|
*(unsigned int *) rd = -revisionDate.secsTo (zeroDate);
|
|
rd += 4;
|
|
strcpy (rd, lastFullPath.utf8 ());
|
|
rd += strlen (rd) + 1;
|
|
|
|
*rd = 0; //Empty for further info
|
|
|
|
}
|
|
|
|
int
|
|
InfoItem::size ()
|
|
{
|
|
int _size;
|
|
_size = 0;
|
|
_size += 1; // 1 Empty field;
|
|
_size += 3; // Terminating \0 of next 3 strings
|
|
_size += 4; // Int size (date)
|
|
_size += strlen (catalogName.utf8 ());
|
|
_size += strlen (lastTranslator.utf8 ());
|
|
_size += strlen (lastFullPath.utf8 ());
|
|
|
|
return _size;
|
|
}
|
|
|
|
// this is a quick hack to copy a local file
|
|
int
|
|
copy_hack (TQFile & input, TQFile & output)
|
|
{
|
|
if (!input.isOpen ())
|
|
{
|
|
if (!input.open (IO_ReadOnly))
|
|
return -1;
|
|
}
|
|
|
|
if (!output.isOpen ())
|
|
{
|
|
if (!output.open (IO_WriteOnly))
|
|
return -1;
|
|
}
|
|
|
|
char buffer[10240];
|
|
int s = 0;
|
|
while (!input.atEnd ())
|
|
{
|
|
s = input.readBlock (buffer, 10240);
|
|
output.writeBlock (buffer, s);
|
|
}
|
|
output.close ();
|
|
input.close ();
|
|
return 0;
|
|
}
|
|
|
|
DataBaseItem::DataBaseItem ()
|
|
{
|
|
numTra = 0;
|
|
location = 0;
|
|
}
|
|
|
|
DataBaseItem::DataBaseItem (char *_key, char *_data)
|
|
{
|
|
|
|
char *data = _data;
|
|
key = TQString::fromUtf8 (_key);
|
|
|
|
unsigned int i, r;
|
|
numTra = *(uint32 *) data;
|
|
data += 4;
|
|
location = *(uint32 *) data;
|
|
data += 4;
|
|
|
|
for (i = 0; i < numTra; i++)
|
|
{
|
|
|
|
TranslationItem tr;
|
|
tr.numRef = *(uint32 *) data;
|
|
|
|
data += 4;
|
|
for (r = 0; r < tr.numRef; r++)
|
|
{
|
|
|
|
int ref;
|
|
ref = *(uint32 *) data;
|
|
data += 4;
|
|
|
|
tr.infoRef.append (ref);
|
|
}
|
|
tr.translation = TQString::fromUtf8 ((const char *) data);
|
|
translations.append (tr);
|
|
data += strlen (data) + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
uint32
|
|
DataBaseItem::sizeKey ()
|
|
{
|
|
return strlen (key.utf8 ()) + 1;
|
|
}
|
|
|
|
uint32
|
|
DataBaseItem::sizeData ()
|
|
{
|
|
unsigned int i, _size = 4;
|
|
_size += numTra * 4;
|
|
_size += 4; // location
|
|
for (i = 0; i < numTra; i++)
|
|
{
|
|
_size += strlen (translations[i].translation.utf8 ()) + 1; // +1 is for \0
|
|
_size += translations[i].numRef * 4;
|
|
}
|
|
return _size;
|
|
}
|
|
|
|
void
|
|
DataBaseItem::toRawKey (char *_key)
|
|
{
|
|
strcpy (_key, key.utf8 ());
|
|
}
|
|
|
|
void
|
|
DataBaseItem::toRawData (char *_data)
|
|
{
|
|
char *data = _data;
|
|
unsigned int i, r;
|
|
|
|
*(uint32 *) data = numTra;
|
|
|
|
data += 4;
|
|
|
|
*(uint32 *) data = location;
|
|
data += 4;
|
|
|
|
for (i = 0; i < numTra; i++)
|
|
{
|
|
TranslationItem tr (translations[i]);
|
|
*(uint32 *) data = tr.numRef;
|
|
data += 4;
|
|
for (r = 0; r < tr.numRef; r++)
|
|
{
|
|
*(uint32 *) data = tr.infoRef[r]; //sub i with r
|
|
|
|
data += 4;
|
|
}
|
|
strcpy ((char *) data, tr.translation.utf8 ());
|
|
data += strlen (tr.translation.utf8 ()) + 1;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
DataBaseManager::DataBaseManager (TQString directory, TQString lang,
|
|
TQObject * parent, const char *name):
|
|
TQObject (parent, name)
|
|
{
|
|
TQString filename;
|
|
|
|
language = lang;
|
|
iAmOk = true;
|
|
basedir = directory;
|
|
indexDb = wordDb = infoDb = db = 0;
|
|
openDataBase ();
|
|
|
|
|
|
}
|
|
|
|
void
|
|
DataBaseManager::openDataBase ()
|
|
{
|
|
kdDebug () << "Opendatabase" << endl;
|
|
TQString directory;
|
|
directory = basedir;
|
|
TQString ll = "." + language;
|
|
if (ll == ".")
|
|
ll = ".NOLANG";
|
|
|
|
TQString transfilename = "%1/translations%2.db";
|
|
transfilename = transfilename.arg (directory).arg (ll);
|
|
|
|
TQString infofilename = "%1/catalogsinfo%2.db";
|
|
infofilename = infofilename.arg (directory).arg (ll);
|
|
|
|
TQString wordsfilename = "%1/wordsindex%2.db";
|
|
wordsfilename = wordsfilename.arg (directory).arg (ll);
|
|
|
|
TQString keysfilename = "%1/keysindex%2.db";
|
|
keysfilename = keysfilename.arg (directory).arg (ll);
|
|
|
|
cursor = 0;
|
|
int ret;
|
|
|
|
if (!db)
|
|
db_create (&db, 0, 0);
|
|
|
|
db_create (&infoDb, 0, 0);
|
|
db_create (&wordDb, 0, 0);
|
|
db_create (&indexDb, 0, 0);
|
|
|
|
ret = db->open (db,
|
|
#if DB_VERSION_MINOR > 0
|
|
NULL,
|
|
#endif
|
|
transfilename.local8Bit (), 0, DB_BTREE, 0, 0644);
|
|
|
|
if (ret == DB_OLD_VERSION)
|
|
{
|
|
kdDebug (KBABEL_SEARCH) << "Trying upgrade" << endl;
|
|
// try upgrade
|
|
|
|
KTempFile transFile, infoFile, keysFile, wordsFile;
|
|
|
|
// do the upgrade on the translation file
|
|
TQFile transfilenameFile (transfilename);
|
|
|
|
if ((ret = copy_hack (transfilenameFile, *transFile.file ())) == 0)
|
|
{
|
|
ret = db->upgrade (db, transFile.name ().local8Bit (), 0);
|
|
}
|
|
|
|
if (ret != 0)
|
|
{
|
|
kdDebug (KBABEL_SEARCH) << "Cannot upgrade translations, " <<
|
|
ret << endl;
|
|
// cleanup
|
|
transFile.unlink ();
|
|
iAmOk = false;
|
|
emit cannotOpenDB (ret);
|
|
return;
|
|
}
|
|
|
|
// do the upgrade on the info file
|
|
TQFile infofilenameFile (infofilename);
|
|
if ((ret = copy_hack (infofilenameFile, *infoFile.file ())) == 0)
|
|
{
|
|
ret =
|
|
infoDb->upgrade (infoDb, infoFile.name ().local8Bit (),
|
|
0);
|
|
}
|
|
|
|
if (ret != 0)
|
|
{
|
|
kdDebug (KBABEL_SEARCH) << "Cannot upgrade catalogsinfo" <<
|
|
endl;
|
|
// cleanup
|
|
transFile.unlink ();
|
|
infoFile.unlink ();
|
|
iAmOk = false;
|
|
emit cannotOpenDB (ret);
|
|
return;
|
|
}
|
|
|
|
// do the upgrade on the words file
|
|
TQFile wordfilenameFile (wordsfilename);
|
|
if ((ret = copy_hack (wordfilenameFile, *wordsFile.file ())) == 0)
|
|
{
|
|
ret =
|
|
wordDb->upgrade (wordDb, wordsFile.name ().local8Bit (),
|
|
0);
|
|
}
|
|
|
|
if (ret != 0)
|
|
{
|
|
kdDebug (KBABEL_SEARCH) << "Cannot upgrade words" << endl;
|
|
// cleanup
|
|
transFile.unlink ();
|
|
infoFile.unlink ();
|
|
wordsFile.unlink ();
|
|
iAmOk = false;
|
|
emit cannotOpenDB (ret);
|
|
return;
|
|
}
|
|
|
|
// do the upgrade on the keys file
|
|
TQFile keysfilenameFile (keysfilename);
|
|
if ((ret = copy_hack (keysfilenameFile, *keysFile.file ())) == 0)
|
|
{
|
|
ret =
|
|
indexDb->upgrade (indexDb, keysFile.name ().local8Bit (),
|
|
0);
|
|
}
|
|
|
|
if (ret != 0)
|
|
{
|
|
kdDebug (KBABEL_SEARCH) << "Cannot upgrade keys" << endl;
|
|
// cleanup
|
|
transFile.unlink ();
|
|
infoFile.unlink ();
|
|
wordsFile.unlink ();
|
|
keysFile.unlink ();
|
|
iAmOk = false;
|
|
emit cannotOpenDB (ret);
|
|
return;
|
|
}
|
|
|
|
kdDebug (KBABEL_SEARCH) << "Files upgraded, copying" << endl;
|
|
// use temporary file instead
|
|
if (ret == 0)
|
|
{
|
|
KIO::NetAccess::del (KURL::fromPathOrURL (transfilename));
|
|
copy_hack (*transFile.file (), transfilenameFile);
|
|
transFile.unlink ();
|
|
|
|
KIO::NetAccess::del (KURL::fromPathOrURL (infofilename));
|
|
copy_hack (*infoFile.file (), infofilenameFile);
|
|
infoFile.unlink ();
|
|
|
|
KIO::NetAccess::del (KURL::fromPathOrURL (wordsfilename));
|
|
copy_hack (*wordsFile.file (), wordfilenameFile);
|
|
wordsFile.unlink ();
|
|
|
|
KIO::NetAccess::del (KURL::fromPathOrURL (keysfilename));
|
|
copy_hack (*keysFile.file (), keysfilenameFile);
|
|
keysFile.unlink ();
|
|
|
|
ret = db->open (db,
|
|
#if DB_VERSION_MINOR > 0
|
|
NULL,
|
|
#endif
|
|
transfilename.local8Bit (), 0, DB_BTREE, 0,
|
|
0644);
|
|
if (ret != 0)
|
|
{
|
|
kdWarning (KBABEL_SEARCH) <<
|
|
"transFilename database can't be opened." << endl;
|
|
kdWarning (KBABEL_SEARCH) <<
|
|
"Please, report this incident and how to reproduce it to kbabel@kde.org."
|
|
<< endl;
|
|
iAmOk = false;
|
|
emit cannotOpenDB (ret);
|
|
return;
|
|
}
|
|
|
|
}
|
|
kdDebug (KBABEL_SEARCH) << "Upgrade done OK" << endl;
|
|
}
|
|
|
|
// Open catalogs information database
|
|
|
|
|
|
|
|
ret = infoDb->open (infoDb,
|
|
#if DB_VERSION_MINOR > 0
|
|
NULL,
|
|
#endif
|
|
infofilename.local8Bit (), 0, DB_RECNO, 0, 0644);
|
|
if (ret != 0)
|
|
{
|
|
iAmOk = false;
|
|
emit cannotOpenDB (ret);
|
|
//Process error here.
|
|
}
|
|
else
|
|
loadInfo ();
|
|
|
|
|
|
|
|
// Words index database
|
|
|
|
ret = wordDb->open (wordDb,
|
|
#if DB_VERSION_MINOR > 0
|
|
NULL,
|
|
#endif
|
|
wordsfilename.local8Bit (), 0, DB_BTREE, 0, 0644);
|
|
if (ret != 0)
|
|
{
|
|
iAmOk = false;
|
|
emit cannotOpenDB (ret);
|
|
//Process error here.
|
|
}
|
|
|
|
//Index of keys.
|
|
|
|
ret = indexDb->open (indexDb,
|
|
#if DB_VERSION_MINOR > 0
|
|
NULL,
|
|
#endif
|
|
keysfilename.local8Bit (), 0, DB_RECNO, 0, 0644);
|
|
if (ret != 0)
|
|
{
|
|
iAmOk = false;
|
|
emit cannotOpenDB (ret);
|
|
//Process error here.
|
|
}
|
|
|
|
|
|
}
|
|
|
|
void
|
|
DataBaseManager::closeDataBase ()
|
|
{
|
|
if (iAmOk)
|
|
{
|
|
db->sync (db, 0);
|
|
db->close (db, 0);
|
|
|
|
infoDb->sync (infoDb, 0);
|
|
infoDb->close (infoDb, 0);
|
|
|
|
wordDb->sync (wordDb, 0);
|
|
wordDb->close (wordDb, 0);
|
|
|
|
indexDb->sync (indexDb, 0);
|
|
indexDb->close (indexDb, 0);
|
|
|
|
// can not be opened again
|
|
indexDb = wordDb = infoDb = db = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// I'm not sure this is a good function !!!
|
|
|
|
void
|
|
DataBaseManager::sync ()
|
|
{
|
|
// if(iAmOk)
|
|
// {
|
|
// db->sync(db,0);
|
|
// infoDb->sync(infoDb,0);
|
|
// cursor=0;
|
|
// }
|
|
|
|
// closeDataBase();
|
|
// openDataBase();
|
|
|
|
db->sync (db, 0);
|
|
infoDb->sync (infoDb, 0);
|
|
wordDb->sync (wordDb, 0);
|
|
indexDb->sync (indexDb, 0);
|
|
loadInfo ();
|
|
}
|
|
|
|
|
|
DataBaseManager::~DataBaseManager ()
|
|
{
|
|
closeDataBase ();
|
|
}
|
|
|
|
int
|
|
DataBaseManager::putItem (DataBaseItem * item, bool ow)
|
|
{
|
|
DBT key, data;
|
|
|
|
memset (&key, 0, sizeof (DBT));
|
|
memset (&data, 0, sizeof (DBT));
|
|
|
|
bool ret;
|
|
|
|
uint32 loc = 0;
|
|
if (item->location == 0)
|
|
{
|
|
loc = item->location = appendKey (item->key);
|
|
// kdDebug(0) << "New key " << item->location << endl;
|
|
}
|
|
key.size = item->sizeKey ();
|
|
data.size = item->sizeData ();
|
|
|
|
key.data = malloc (key.size);
|
|
data.data = malloc (data.size);
|
|
|
|
|
|
item->toRawKey ((char *) key.data);
|
|
item->toRawData ((char *) data.data);
|
|
|
|
|
|
if (ow)
|
|
ret = db->put (db, 0, &key, &data, 0);
|
|
else
|
|
ret = db->put (db, 0, &key, &data, DB_NOOVERWRITE);
|
|
|
|
//check ret
|
|
|
|
if (loc != 0) //I'm new!
|
|
{
|
|
uint32 location = loc;
|
|
|
|
TQValueList < TQString > wlist;
|
|
|
|
wlist = wordsIn (item->key);
|
|
|
|
TQValueList < TQString >::Iterator wlistit;
|
|
|
|
for (wlistit = wlist.begin (); wlistit != wlist.end (); ++wlistit)
|
|
{
|
|
addLocation (*wlistit, location);
|
|
}
|
|
|
|
}
|
|
|
|
free (key.data);
|
|
free (data.data); //READ DOCU !!!!
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
DataBaseItem
|
|
DataBaseManager::getItem (TQString key)
|
|
{
|
|
if (!iAmOk)
|
|
return DataBaseItem ();
|
|
|
|
|
|
DBT _key, data;
|
|
|
|
memset (&_key, 0, sizeof (DBT));
|
|
|
|
memset (&data, 0, sizeof (DBT));
|
|
|
|
int len = strlen (key.utf8 ());
|
|
_key.data = malloc (len + 1);
|
|
_key.size = len + 1;
|
|
strcpy ((char *) _key.data, key.utf8 ());
|
|
|
|
|
|
int ret;
|
|
ret = db->get (db, 0, &_key, &data, 0);
|
|
|
|
if (ret != 0)
|
|
{
|
|
free (_key.data);
|
|
return DataBaseItem (); //return an empty database item
|
|
}
|
|
|
|
DataBaseItem returnItem =
|
|
DataBaseItem ((char *) _key.data, (char *) data.data);
|
|
|
|
free (_key.data);
|
|
return returnItem;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DataBaseItem
|
|
DataBaseManager::cursorGet (uint32 flags)
|
|
{
|
|
|
|
if (!iAmOk)
|
|
return DataBaseItem ();
|
|
int re;
|
|
DBT key, data;
|
|
|
|
memset (&key, 0, sizeof (DBT));
|
|
|
|
memset (&data, 0, sizeof (DBT));
|
|
|
|
if (cursor == 0)
|
|
re = db->cursor (db, 0, &cursor, 0);
|
|
|
|
int ret;
|
|
if ((ret = cursor->c_get (cursor, &key, &data, flags)) == 0)
|
|
{
|
|
return DataBaseItem ((char *) key.data, (char *) data.data);
|
|
}
|
|
else
|
|
{
|
|
kdDebug (KBABEL_SEARCH) << TQString ("...cursor getting...%1").
|
|
tqarg (ret) << endl;
|
|
|
|
return DataBaseItem ();
|
|
}
|
|
}
|
|
|
|
|
|
DataBaseItem
|
|
DataBaseManager::firstItem ()
|
|
{
|
|
return cursorGet (DB_FIRST);
|
|
}
|
|
|
|
DataBaseItem
|
|
DataBaseManager::currentItem ()
|
|
{
|
|
return cursorGet (DB_CURRENT);
|
|
}
|
|
|
|
|
|
DataBaseItem
|
|
DataBaseManager::nextItem ()
|
|
{
|
|
return cursorGet (DB_NEXT);
|
|
}
|
|
|
|
|
|
bool
|
|
DataBaseManager::isOk ()
|
|
{
|
|
return iAmOk;
|
|
}
|
|
|
|
int
|
|
DataBaseManager::count ()
|
|
{
|
|
DB_BTREE_STAT *dstat = 0;
|
|
#if (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3) || (DB_VERSION_MAJOR >= 5)
|
|
db->stat (db, NULL, &dstat, DB_FAST_STAT);
|
|
#else
|
|
db->stat (db, &dstat, DB_FAST_STAT);
|
|
#endif
|
|
int ret = dstat->bt_nkeys;
|
|
free (dstat);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
DataBaseManager::current ()
|
|
{
|
|
// THIS FUNCTION SEEM TO NOT WORK (not used)
|
|
if (!iAmOk)
|
|
return 0;
|
|
|
|
DBT key, data;
|
|
memset (&key, 0, sizeof (DBT));
|
|
memset (&data, 0, sizeof (DBT));
|
|
|
|
if (cursor != 0)
|
|
db->cursor (db, 0, &cursor, 0);
|
|
cursor->c_get (cursor, &key, &data, DB_GET_RECNO);
|
|
return *(uint32 *) (data.data);
|
|
|
|
}
|
|
|
|
int
|
|
DataBaseManager::createDataBase (TQString directory,
|
|
TQString language, int mode)
|
|
{
|
|
TQString filename;
|
|
TQString ll = "." + language;
|
|
if (ll == ".")
|
|
ll = ".NOLANG";
|
|
filename = "%1/translations%2.db";
|
|
filename = filename.arg (directory).arg (ll);
|
|
|
|
rename (filename.local8Bit (), filename.local8Bit () + ",old");
|
|
|
|
//kdDebug(0) << TQString("Creating %1").tqarg(filename) << endl;
|
|
|
|
iAmOk = true;
|
|
|
|
int ret;
|
|
|
|
if (!db)
|
|
{
|
|
if (db_create (&db, 0, 0) != 0)
|
|
{
|
|
kdDebug() << "db_create db failed" << endl;
|
|
iAmOk = false;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
db->set_flags (db, DB_RECNUM);
|
|
ret = db->open (db,
|
|
#if DB_VERSION_MINOR > 0
|
|
NULL,
|
|
#endif
|
|
filename.local8Bit (), 0, DB_BTREE, DB_CREATE | DB_EXCL,
|
|
mode);
|
|
if (ret != 0)
|
|
{
|
|
kdDebug() << "db->open " << filename << " " << mode << " failed" << endl;
|
|
iAmOk = false;
|
|
}
|
|
|
|
|
|
filename = "%1/catalogsinfo%2.db";
|
|
filename = filename.arg (directory).arg (ll);
|
|
rename (filename.local8Bit (), filename.local8Bit () + ",old");
|
|
|
|
db_create (&infoDb, 0, 0);
|
|
ret = infoDb->open (infoDb,
|
|
#if DB_VERSION_MINOR > 0
|
|
NULL,
|
|
#endif
|
|
filename.local8Bit (), 0, DB_RECNO, DB_CREATE, mode);
|
|
if (ret != 0)
|
|
iAmOk = false;
|
|
|
|
|
|
|
|
filename = "%1/wordsindex%2.db";
|
|
filename = filename.arg (directory).arg (ll);
|
|
rename (filename.local8Bit (), filename.local8Bit () + ",old");
|
|
|
|
db_create (&wordDb, 0, 0);
|
|
ret = wordDb->open (wordDb,
|
|
#if DB_VERSION_MINOR > 0
|
|
NULL,
|
|
#endif
|
|
filename.local8Bit (), 0, DB_BTREE, DB_CREATE, mode);
|
|
if (ret != 0)
|
|
iAmOk = false;
|
|
|
|
|
|
|
|
filename = "%1/keysindex%2.db";
|
|
filename = filename.arg (directory).arg (ll);
|
|
rename (filename.local8Bit (), filename.local8Bit () + ",old");
|
|
|
|
db_create (&indexDb, 0, 0);
|
|
ret = indexDb->open (indexDb,
|
|
#if DB_VERSION_MINOR > 0
|
|
NULL,
|
|
#endif
|
|
filename.local8Bit (), 0, DB_RECNO, DB_CREATE, mode);
|
|
if (ret != 0)
|
|
iAmOk = false;
|
|
|
|
|
|
|
|
if (iAmOk)
|
|
loadInfo ();
|
|
else
|
|
kdDebug (KBABEL_SEARCH) << TQString ("I am NOT ok : %1").
|
|
tqarg (ret) << endl;
|
|
|
|
//THIS IS WRONG, rewrite the error handling.
|
|
return iAmOk;
|
|
|
|
}
|
|
|
|
InfoItem
|
|
DataBaseManager::getCatalogInfo (int n)
|
|
{
|
|
|
|
DBT key;
|
|
DBT data;
|
|
|
|
memset (&key, 0, sizeof (DBT));
|
|
memset (&data, 0, sizeof (DBT));
|
|
|
|
key.data = &n;
|
|
key.size = 4;
|
|
|
|
//Check for errors
|
|
int ret = infoDb->get (infoDb, 0, &key, &data, 0); //DB_SET_RECNO);
|
|
|
|
if (ret)
|
|
{
|
|
return InfoItem ();
|
|
}
|
|
|
|
// kdDebug(0) << TQString("Trad %1").tqarg(ret) << endl;
|
|
|
|
InfoItem it ((char *) data.data, language);
|
|
//free(data.data); // Read docu for this!!!!
|
|
|
|
return it;
|
|
|
|
}
|
|
|
|
int
|
|
DataBaseManager::addCatalogInfo (InfoItem * catInfo, int cat = -1)
|
|
{
|
|
DBT data;
|
|
DBT key;
|
|
|
|
// clean up data
|
|
memset (&data, 0, sizeof (DBT));
|
|
memset (&key, 0, sizeof (DBT));
|
|
|
|
int ret = 0, err;
|
|
if (cat >= 0)
|
|
ret = cat;
|
|
key.size = 4;
|
|
key.data = &ret;
|
|
data.size = catInfo->size ();
|
|
data.data = malloc (data.size);
|
|
|
|
catInfo->rawData ((char *) data.data);
|
|
|
|
// store the catalog data into database
|
|
if (cat >= 0)
|
|
err = infoDb->put (infoDb, 0, &key, &data, 0);
|
|
else
|
|
err = infoDb->put (infoDb, 0, &key, &data, DB_APPEND);
|
|
|
|
|
|
ret = *(int *) key.data;
|
|
|
|
// Append to the list of catalogInfo
|
|
info.append (*catInfo);
|
|
|
|
// cleanup unneeded data memory
|
|
free (data.data);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
DataBaseManager::searchCatalogInfo (TQString location)
|
|
{
|
|
int n = 0;
|
|
TQValueList < InfoItem >::Iterator it;
|
|
for (it = info.begin (); it != info.end (); ++it)
|
|
{
|
|
n++;
|
|
if ((*it).catalogName == location)
|
|
return n;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
bool
|
|
DataBaseManager::putCatalogInfo (int refnum, InfoItem * catInfo)
|
|
{
|
|
DBT data;
|
|
DBT key;
|
|
|
|
memset (&key, 0, sizeof (DBT));
|
|
memset (&data, 0, sizeof (DBT));
|
|
|
|
int ret;
|
|
key.size = 4;
|
|
key.data = &refnum;
|
|
|
|
data.size = catInfo->size ();
|
|
data.data = malloc (data.size);
|
|
catInfo->rawData ((char *) data.data);
|
|
|
|
ret = infoDb->put (infoDb, 0, &key, &data, 0);
|
|
|
|
free (data.data);
|
|
|
|
return (ret == 0);
|
|
}
|
|
|
|
void
|
|
DataBaseManager::loadInfo ()
|
|
{
|
|
int nrec;
|
|
DB_BTREE_STAT *stat;
|
|
// memset(&stat,0,sizeof(DB_BTREE_STAT));
|
|
#if (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3) || (DB_VERSION_MAJOR >= 5)
|
|
if (infoDb->stat (infoDb, NULL, &stat, DB_FAST_STAT))
|
|
fprintf (stderr, "Cannot stat\n");
|
|
#else
|
|
if (infoDb->stat (infoDb, &stat, DB_FAST_STAT))
|
|
fprintf (stderr, "Cannot stat\n");
|
|
#endif
|
|
nrec = stat->bt_nkeys;
|
|
free (stat);
|
|
|
|
info.clear ();
|
|
for (int i = 1; i <= nrec; i++) // I think DB2 Recno are 1 based.
|
|
{
|
|
info.append (getCatalogInfo (i));
|
|
}
|
|
|
|
}
|
|
|
|
|
|
TQValueList < TQString > DataBaseManager::wordsIn (TQString string)
|
|
{
|
|
TQString
|
|
a;
|
|
TQValueList < TQString > words;
|
|
int
|
|
i,
|
|
l;
|
|
|
|
a = string.simplifyWhiteSpace ();
|
|
a = a.stripWhiteSpace ();
|
|
a = a.lower ();
|
|
l = a.length ();
|
|
|
|
int
|
|
c = 0;
|
|
//words.setAutoDelete(true); //Not sure... check if it is en.
|
|
TQString
|
|
m;
|
|
for (i = 0; i < l; i++)
|
|
if (a[i].isLetterOrNumber ())
|
|
{
|
|
m += a[i];
|
|
}
|
|
else if (a[i].isSpace ())
|
|
{
|
|
words.append (m);
|
|
c++; // C++ ;-)
|
|
m = "";
|
|
}
|
|
words.append (m);
|
|
|
|
return words;
|
|
}
|
|
|
|
|
|
|
|
WordItem
|
|
DataBaseManager::getWordLocations (TQString word)
|
|
{
|
|
|
|
TQString keystring = word.lower ();
|
|
|
|
DBT key;
|
|
DBT data;
|
|
|
|
char *keydata;
|
|
|
|
int intlen = strlen (keystring.utf8 ());
|
|
|
|
keydata = (char *) malloc (intlen + 1);
|
|
strcpy (keydata, keystring.utf8 ());
|
|
|
|
memset (&key, 0, sizeof (DBT));
|
|
memset (&data, 0, sizeof (DBT));
|
|
|
|
key.data = keydata;
|
|
key.size = intlen + 1;
|
|
|
|
//Check for errors
|
|
int ret = wordDb->get (wordDb, 0, &key, &data, 0); //DB_SET_RECNO);
|
|
//MAYBE THIS WORD IS NO WHERE!!
|
|
if (ret)
|
|
{
|
|
free (keydata);
|
|
return WordItem (keystring);
|
|
}
|
|
|
|
|
|
WordItem it ((char *) data.data, keystring);
|
|
|
|
free (keydata);
|
|
|
|
|
|
// kdDebug(0) << ((uint32 *)it.locations.data())[0] << endl;
|
|
|
|
return it;
|
|
|
|
}
|
|
|
|
|
|
bool
|
|
DataBaseManager::addLocation (TQString word, unsigned int location)
|
|
{
|
|
|
|
TQString keystring = word.lower ();
|
|
|
|
|
|
DBT key;
|
|
DBT data;
|
|
|
|
char *keydata;
|
|
char *newdata;
|
|
|
|
int intlen = strlen (keystring.utf8 ());
|
|
|
|
keydata = (char *) malloc (intlen + 1);
|
|
strcpy (keydata, keystring.utf8 ());
|
|
|
|
memset (&key, 0, sizeof (DBT));
|
|
memset (&data, 0, sizeof (DBT));
|
|
|
|
key.data = keydata;
|
|
key.size = intlen + 1;
|
|
|
|
strcpy ((char *) key.data, keystring.utf8 ());
|
|
|
|
//Check for errors
|
|
int ret = wordDb->get (wordDb, 0, &key, &data, 0); //DB_SET_RECNO);
|
|
|
|
//Check if you get something or not
|
|
if (ret == 0) // CHANGE IT!!!!! if found something
|
|
{
|
|
uint32 *d;
|
|
d = (uint32 *) data.data;
|
|
uint32 num = d[0];
|
|
uint32 loc = 0; //Position from 0 to num-1
|
|
int totalrecord = count ();
|
|
uint32 step = 1;
|
|
|
|
int antibounce = 0;
|
|
//d+=4;
|
|
//int score=d[1];
|
|
//d+=4;
|
|
bool forward, end = false;
|
|
|
|
d[1] = 0; //Before the begin...
|
|
|
|
d += 2; //1 uint32!
|
|
|
|
|
|
//Try to guess...
|
|
loc = location * num / totalrecord + 1;
|
|
if (loc >= num)
|
|
loc = num - 1;
|
|
|
|
|
|
|
|
|
|
//Continue if next is smaller or if previous is greater
|
|
//before the while check if it is ok
|
|
if (loc == 0)
|
|
{
|
|
if (d[loc] > location)
|
|
end = true;
|
|
else
|
|
loc = 1;
|
|
}
|
|
|
|
if ((loc == num) && !end)
|
|
{
|
|
if (d[loc - 1] < location)
|
|
end = true;
|
|
else
|
|
loc = num - 1;
|
|
}
|
|
|
|
|
|
|
|
while ((!end) && ((forward = (d[loc]) < location)
|
|
|| ((loc > 0) && (d[loc - 1] > location))))
|
|
{
|
|
|
|
antibounce++;
|
|
//calculate step or use antibounce
|
|
if (abs ((int) d[loc] - (int) location) < 50
|
|
|| antibounce > 100)
|
|
step = 1; //Go linear...
|
|
else
|
|
{
|
|
step =
|
|
(abs (d[loc] - location) * num) / totalrecord + 1;
|
|
|
|
}
|
|
|
|
kdDebug (KBABEL_SEARCH) << "Insert:" << location <<
|
|
" We are at: " << loc << " i.e. " << d[loc] << " Step:"
|
|
<< step << endl;
|
|
if (loc > 0)
|
|
{
|
|
if (forward)
|
|
kdDebug (KBABEL_SEARCH) << "Go " << "forward" <<
|
|
" prev is " << d[loc - 1] << endl;
|
|
else
|
|
kdDebug (KBABEL_SEARCH) << "Go " << "backward" <<
|
|
" prev is " << d[loc - 1] << endl;
|
|
}
|
|
|
|
if (forward)
|
|
{
|
|
if (loc + step < num)
|
|
loc += step; // Go forward
|
|
else
|
|
loc = num; // Go to num
|
|
}
|
|
else
|
|
{
|
|
if (loc > step)
|
|
loc -= step; //Go backward
|
|
else
|
|
loc = 0; // Go to 0
|
|
}
|
|
|
|
//Now check if I am in the right place [THIS IS NOT NECESSARY]
|
|
|
|
//check if loc and loc -1 are well defined 1<loc<num-1
|
|
if (loc > num)
|
|
loc = num; //Must not happen, idem
|
|
|
|
if (loc == 0)
|
|
{
|
|
if (d[loc] > location)
|
|
end = true;
|
|
else
|
|
loc = 1;
|
|
}
|
|
|
|
if ((loc == num) && !end)
|
|
{
|
|
if (d[loc - 1] < location)
|
|
end = true;
|
|
else
|
|
loc = num - 1;
|
|
}
|
|
|
|
}
|
|
|
|
if (loc == num)
|
|
kdDebug (KBABEL_SEARCH) << "END" << endl;
|
|
if (loc == 0)
|
|
kdDebug (KBABEL_SEARCH) << "BEGIN" << endl;
|
|
|
|
if (loc > 0)
|
|
kdDebug (KBABEL_SEARCH) << location << " inserted between " <<
|
|
d[loc - 1] << " and " << d[loc] << endl;
|
|
|
|
if ((loc < num && location == d[loc]) || (loc > 0 && location == d[loc - 1])) //What about word repetition
|
|
{
|
|
free (keydata);
|
|
return true; //Why true ??
|
|
}
|
|
|
|
|
|
|
|
//Now insert and put back in the database!
|
|
newdata = (char *) malloc (4 * (num + 3)); //uint32*(num+score+1..NUM+new)
|
|
memcpy (newdata, data.data, 4 + 4 + 4 * loc);
|
|
char *secondpart = (char *) data.data;
|
|
secondpart += 4 * (loc + 2);
|
|
memcpy ((newdata + 4 * (loc + 3)), secondpart, (num - loc) * 4);
|
|
uint32 *intdata = (uint32 *) newdata;
|
|
intdata[0] = num + 1;
|
|
//ADD HERE code to recalc score
|
|
intdata[loc + 2] = location;
|
|
|
|
//ok send it to database!
|
|
memset (&data, 0, sizeof (DBT));
|
|
|
|
data.data = newdata;
|
|
data.size = 4 * (3 + num);
|
|
} //found sounthing
|
|
else
|
|
{ //found nothing
|
|
newdata = (char *) malloc (4 * 3);
|
|
uint32 *intdata = (uint32 *) newdata;
|
|
intdata[0] = 1;
|
|
intdata[1] = 1;
|
|
intdata[2] = location;
|
|
memset (&data, 0, sizeof (DBT));
|
|
data.data = newdata;
|
|
data.size = 4 * 3;
|
|
}
|
|
|
|
memset (&key, 0, sizeof (DBT));
|
|
//memset(&data,0,sizeof(DBT));
|
|
|
|
key.data = keydata;
|
|
key.size = intlen + 1;
|
|
|
|
ret = wordDb->put (wordDb, 0, &key, &data, 0); //DB_SET_RECNO);
|
|
|
|
|
|
free (newdata);
|
|
free (keydata);
|
|
//return it;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool
|
|
DataBaseManager::removeLocation (TQString /*word */ , int /*location */ )
|
|
{
|
|
|
|
//#warning TODO: REMOVE LOCATION
|
|
return true;
|
|
|
|
}
|
|
|
|
uint32
|
|
DataBaseManager::appendKey (TQString _key)
|
|
{
|
|
|
|
DBT key;
|
|
DBT data;
|
|
|
|
memset (&key, 0, sizeof (DBT));
|
|
memset (&data, 0, sizeof (DBT));
|
|
|
|
|
|
uint32 ret = 0, err;
|
|
key.size = 4;
|
|
key.data = &ret;
|
|
|
|
data.size = strlen (_key.utf8 ()) + 1;
|
|
data.data = malloc (data.size);
|
|
|
|
strcpy ((char *) data.data, _key.utf8 ());
|
|
|
|
err = indexDb->put (indexDb, 0, &key, &data, DB_APPEND);
|
|
|
|
if (err)
|
|
ret = 0;
|
|
else
|
|
ret = *(uint32 *) key.data;
|
|
|
|
//kdDebug(0) << TQString("Append result %1,err = %1").tqarg(ret).tqarg(err) << endl;
|
|
|
|
|
|
free (data.data);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
TQString
|
|
DataBaseManager::getKey (uint32 n)
|
|
{
|
|
|
|
DBT key;
|
|
DBT data;
|
|
|
|
memset (&key, 0, sizeof (DBT));
|
|
memset (&data, 0, sizeof (DBT));
|
|
|
|
key.data = &n;
|
|
key.size = 4;
|
|
|
|
//Check for errors
|
|
int ret = indexDb->get (indexDb, 0, &key, &data, 0); //DB_SET_RECNO);
|
|
if (ret)
|
|
return TQString();
|
|
|
|
return TQString::fromUtf8 ((char *) data.data);
|
|
|
|
// kdDebug(0) << TQString("Trad %1").tqarg(ret) << endl;
|
|
|
|
}
|
|
|
|
int
|
|
DataBaseManager::catalogRef (TQString location, TQString author, TQString path)
|
|
{
|
|
InfoItem cinfo;
|
|
int cat, catnum;
|
|
cat = searchCatalogInfo (location);
|
|
|
|
if (cat == -1) //Not exist
|
|
{
|
|
cinfo.catalogName = location;
|
|
cinfo.lastTranslator = author;
|
|
cinfo.lastFullPath = path;
|
|
//TO DO:
|
|
// //Add date info
|
|
|
|
kdDebug (0) << "New catalog " << endl;
|
|
catnum = addCatalogInfo (&cinfo);
|
|
//sync();
|
|
kdDebug (0) << "Ref " << catnum << endl;
|
|
}
|
|
else
|
|
{
|
|
cinfo = getCatalogInfo (cat);
|
|
//Update date.
|
|
|
|
//last path must be updated
|
|
cinfo.lastFullPath = path;
|
|
kdDebug (0) << "New full path " << path << endl;
|
|
kdDebug (0) << "Ref " << cat << endl;
|
|
|
|
catnum = addCatalogInfo (&cinfo, cat);
|
|
kdDebug (0) << " must be equal to " << catnum << endl;
|
|
|
|
catnum = cat;
|
|
}
|
|
|
|
return catnum;
|
|
}
|
|
|
|
int
|
|
DataBaseManager::putNewTranslation (TQString key, TQString tran, int catalog,
|
|
bool ow)
|
|
{
|
|
int catnum = catalog;
|
|
int count = 0;
|
|
TQString msgid = key;
|
|
DataBaseItem dbit = getItem (msgid);
|
|
|
|
if (dbit.numTra == 0) //Not found
|
|
{
|
|
dbit.numTra += 1;
|
|
|
|
// use local variable, dbit.translations is TQValueList and
|
|
// will create own copy
|
|
TranslationItem tra;
|
|
tra.numRef = 1;
|
|
tra.translation = tran;
|
|
tra.infoRef.append (catnum);
|
|
dbit.translations.append (tra);
|
|
dbit.key = key;
|
|
|
|
//Check ret value
|
|
count++;
|
|
|
|
int aa = putItem (&dbit);
|
|
if (aa)
|
|
kdDebug (0) << TQString ("-----------put code ") << aa << endl;
|
|
}
|
|
else
|
|
{
|
|
// key exists
|
|
|
|
TQString msgstr = tran;
|
|
bool found_catalog_info = false, foundTr = false, isThisOne = false;
|
|
|
|
TQValueList < TranslationItem >::Iterator ittr;
|
|
bool rem = false;
|
|
|
|
// check all translations in the list
|
|
for (ittr = dbit.translations.begin ();
|
|
ittr != dbit.translations.end (); rem ? ittr : ++ittr)
|
|
{
|
|
rem = false;
|
|
found_catalog_info = false;
|
|
|
|
// is the translation one we should put there?
|
|
isThisOne = (*ittr).translation == msgstr;
|
|
|
|
// is there the catnum we are looking for?
|
|
if ((*ittr).infoRef.find (catnum) != (*ittr).infoRef.end ())
|
|
{
|
|
found_catalog_info = true;
|
|
if (ow && !isThisOne)
|
|
{
|
|
// I'll look for my catalog reference to del old
|
|
kdDebug (0) << "Removing the old translation " << endl;
|
|
(*ittr).numRef -= 1;
|
|
(*ittr).infoRef.remove (catnum);
|
|
if ((*ittr).numRef == 0)
|
|
{
|
|
dbit.numTra -= 1;
|
|
// point the iterator to the next valid item
|
|
ittr = dbit.translations.remove (ittr);
|
|
rem = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isThisOne)
|
|
{
|
|
if (!found_catalog_info)
|
|
{
|
|
//There are no reference of this catalog for this translation => add it
|
|
(*ittr).infoRef.append (catnum);
|
|
(*ittr).numRef += 1;
|
|
}
|
|
foundTr = true; // Ok, we found this translation, no need to add it.
|
|
}
|
|
}
|
|
|
|
if (!foundTr) //This translation is new => Add it !
|
|
{
|
|
count++;
|
|
TranslationItem tra;
|
|
tra.numRef = 1;
|
|
tra.translation = msgstr;
|
|
tra.infoRef.append (catnum);
|
|
|
|
dbit.translations.append (tra);
|
|
dbit.numTra += 1;
|
|
}
|
|
|
|
//put the new item in database overwriting the old one.
|
|
//Check ret value
|
|
int aa = putItem (&dbit, true);
|
|
if (aa)
|
|
kdDebug (0) << TQString ("-----------put code ") << aa << endl;
|
|
|
|
}
|
|
return count;
|
|
}
|
|
|
|
|
|
void
|
|
DataBaseManager::rebuildIndexes ()
|
|
{
|
|
// uint32 loc;
|
|
#if 0
|
|
//Reset the 2 databases here.
|
|
|
|
while (0 /*browse keys here */ )
|
|
{
|
|
loc = item->location = appendKey (item->key);
|
|
|
|
uint32 location = loc;
|
|
|
|
TQValueList < TQString > wlist;
|
|
|
|
wlist = wordsIn (item->key);
|
|
|
|
TQValueList < TQString >::Iterator wlistit;
|
|
|
|
for (wlistit = wlist.begin (); wlistit != wlist.end (); ++wlistit)
|
|
{
|
|
addLocation (*wlistit, location);
|
|
}
|
|
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
#include "database.moc"
|