403 lines
11 KiB
C
403 lines
11 KiB
C
/* -*- Mode: C; indent-tabs-mode: nil; -*-
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public
|
|
* License Version 1.1 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS
|
|
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
|
* implied. See the License for the specific language governing
|
|
* rights and limitations under the License.
|
|
*
|
|
* The Original Code is the TripleDB database library.
|
|
*
|
|
* The Initial Developer of the Original Code is Geocast Network Systems.
|
|
* Portions created by Geocast are
|
|
* Copyright (C) 2000 Geocast Network Systems. All
|
|
* Rights Reserved.
|
|
*
|
|
* Contributor(s): Terry Weissman <terry@mozilla.org>
|
|
*/
|
|
|
|
#include <ctype.h>
|
|
|
|
#include "tdbtypes.h"
|
|
|
|
#include "tdb.h"
|
|
#include "util.h"
|
|
#include "vector.h"
|
|
#include "windex.h"
|
|
|
|
#define MAXWORDLEN 10
|
|
|
|
typedef struct {
|
|
char word[MAXWORDLEN + 1];
|
|
TDBInt32 length;
|
|
TDBUint16 position;
|
|
} WordInfo;
|
|
|
|
struct _TDBWindex {
|
|
TDBBase* base;
|
|
DB* db;
|
|
};
|
|
|
|
|
|
struct _TDBWindexCursor {
|
|
TDBWindex* windex;
|
|
char* string;
|
|
DBC* cursor;
|
|
WordInfo* winfo;
|
|
TDBInt32 numwinfo;
|
|
TDBInt32 main;
|
|
int cmdflag;
|
|
};
|
|
|
|
|
|
TDBWindex*
|
|
tdbWindexNew(TDBBase* base)
|
|
{
|
|
TDBWindex* windex;
|
|
int dbstatus;
|
|
windex = tdb_NEWZAP(TDBWindex);
|
|
if (!windex) return NULL;
|
|
windex->base = base;
|
|
dbstatus = db_create(&(windex->db), tdbGetDBEnv(base), 0);
|
|
if (dbstatus != DB_OK) goto FAIL;
|
|
dbstatus = windex->db->open(windex->db, "windex", NULL, DB_BTREE,
|
|
DB_CREATE, 0666);
|
|
if (dbstatus != DB_OK) goto FAIL;
|
|
return windex;
|
|
|
|
FAIL:
|
|
tdbWindexFree(windex);
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
tdbWindexFree(TDBWindex* windex)
|
|
{
|
|
if (windex == NULL) {
|
|
tdb_ASSERT(0);
|
|
return;
|
|
}
|
|
if (windex->db) {
|
|
windex->db->close(windex->db, 0);
|
|
}
|
|
tdb_Free(windex);
|
|
}
|
|
|
|
|
|
TDBStatus
|
|
tdbWindexSync(TDBWindex* windex)
|
|
{
|
|
if (windex == NULL) {
|
|
tdb_ASSERT(0);
|
|
return TDB_FAILURE;
|
|
}
|
|
if (windex->db->sync(windex->db, 0) != DB_OK) return TDB_FAILURE;
|
|
return TDB_SUCCESS;
|
|
}
|
|
|
|
|
|
static WordInfo*
|
|
splitIntoWords(char* str, WordInfo* winfobuf, TDBInt32 winfobuflen)
|
|
{
|
|
WordInfo* winfo;
|
|
WordInfo* tmp;
|
|
TDBInt32 cur;
|
|
TDBInt32 max;
|
|
char* ptr;
|
|
char* ptr1;
|
|
char* ptr2;
|
|
TDBInt32 i;
|
|
TDBInt32 length;
|
|
|
|
|
|
winfo = winfobuf;
|
|
max = winfobuflen - 1;
|
|
cur = 0;
|
|
for (ptr1 = str ; *ptr1 ; ptr1++) {
|
|
if (!isalnum(*ptr1)) continue;
|
|
for (ptr2 = ptr1 + 1 ; *ptr2 && isalnum(*ptr2) ; ptr2++) { }
|
|
if (ptr2 - ptr1 > 2) {
|
|
/* Should check for stopwords here. XXX */
|
|
if (cur >= max) {
|
|
max = cur + 20;
|
|
tmp = tdb_Calloc(max + 1, sizeof(WordInfo));
|
|
for (i=0 ; i<cur ; i++) {
|
|
tmp[i] = winfo[i];
|
|
}
|
|
if (winfo != winfobuf) tdb_Free(winfo);
|
|
winfo = tmp;
|
|
}
|
|
length = ptr2 - ptr1;
|
|
if (length > MAXWORDLEN) length = MAXWORDLEN;
|
|
winfo[cur].length = length;
|
|
winfo[cur].position = ptr1 - str;
|
|
for (ptr = winfo[cur].word ; length > 0 ; length--) {
|
|
*ptr++ = tolower(*ptr1++);
|
|
}
|
|
*ptr = '\0';
|
|
cur++;
|
|
}
|
|
ptr1 = ptr2;
|
|
if (!*ptr1) break;
|
|
}
|
|
if (winfo) winfo[cur].length = 0;
|
|
return winfo;
|
|
}
|
|
|
|
|
|
TDBStatus
|
|
tdbWindexAdd(TDBWindex* windex, TDBVector* vector)
|
|
{
|
|
TDBNodePtr node;
|
|
WordInfo winfobuf[20];
|
|
WordInfo* winfo;
|
|
WordInfo* wptr;
|
|
char buf[100];
|
|
DBT key;
|
|
DBT data;
|
|
char* ptr;
|
|
char* endptr;
|
|
int dbstatus;
|
|
if (windex == NULL || vector == NULL) {
|
|
tdb_ASSERT(0);
|
|
return TDB_FAILURE;
|
|
}
|
|
node = tdbVectorGetNonInternedNode(vector, 2 /* XXX Stupid hardcoding */);
|
|
if (node == NULL) return TDB_FAILURE;
|
|
if (node->type != TDBTYPE_STRING) {
|
|
return TDB_SUCCESS; /* Quietly do nothing if it's not a string. */
|
|
}
|
|
winfo = splitIntoWords(node->d.str.string, winfobuf,
|
|
sizeof(winfobuf) / sizeof(winfobuf[0]));
|
|
if (!winfo) return TDB_FAILURE;
|
|
memset(&key, 0, sizeof(key));
|
|
memset(&data, 0, sizeof(data));
|
|
for (wptr = winfo ; wptr->length > 0 ; wptr++) {
|
|
ptr = buf;
|
|
endptr = buf + sizeof(buf);
|
|
tdbPutString(&ptr, wptr->word, endptr);
|
|
tdbPutPtr(&ptr, tdbVectorGetRecordNumber(vector), endptr);
|
|
tdbPutUInt16(&ptr, (TDBUint16) wptr->position, endptr);
|
|
if (ptr >= endptr) {
|
|
tdb_ASSERT(0);
|
|
} else {
|
|
key.size = ptr - buf;
|
|
key.data = buf;
|
|
dbstatus = windex->db->put(windex->db,
|
|
tdbGetTransaction(windex->base),
|
|
&key, &data, 0);
|
|
if (dbstatus != DB_OK) return TDB_FAILURE;
|
|
}
|
|
}
|
|
if (winfo != winfobuf) {
|
|
tdb_Free(winfo);
|
|
}
|
|
return TDB_SUCCESS;
|
|
}
|
|
|
|
|
|
TDBStatus
|
|
tdbWindexRemove(TDBWindex* windex, TDBVector* vector)
|
|
{
|
|
TDBNodePtr node;
|
|
WordInfo winfobuf[20];
|
|
WordInfo* winfo;
|
|
WordInfo* wptr;
|
|
DBT key;
|
|
DBT data;
|
|
char* ptr;
|
|
char* endptr;
|
|
char buf[100];
|
|
int dbstatus;
|
|
if (windex == NULL || vector == NULL) {
|
|
tdb_ASSERT(0);
|
|
return TDB_FAILURE;
|
|
}
|
|
node = tdbVectorGetNonInternedNode(vector, 2 /* XXX Stupid hardcoding */);
|
|
if (node == NULL) return TDB_FAILURE;
|
|
if (node->type != TDBTYPE_STRING) {
|
|
return TDB_SUCCESS; /* Quietly do nothing if it's not a string. */
|
|
}
|
|
winfo = splitIntoWords(node->d.str.string, winfobuf,
|
|
sizeof(winfobuf) / sizeof(winfobuf[0]));
|
|
if (!winfo) return TDB_FAILURE;
|
|
memset(&key, 0, sizeof(key));
|
|
memset(&data, 0, sizeof(data));
|
|
for (wptr = winfo ; wptr->length > 0 ; wptr++) {
|
|
ptr = buf;
|
|
endptr = buf + sizeof(buf);
|
|
tdbPutString(&ptr, wptr->word, endptr);
|
|
tdbPutPtr(&ptr, tdbVectorGetRecordNumber(vector), endptr);
|
|
tdbPutUInt16(&ptr, (TDBUint16) wptr->position, endptr);
|
|
if (ptr >= endptr) {
|
|
tdb_ASSERT(0);
|
|
} else {
|
|
key.size = ptr - buf;
|
|
key.data = buf;
|
|
dbstatus = windex->db->del(windex->db,
|
|
tdbGetTransaction(windex->base),
|
|
&key, 0);
|
|
if (dbstatus != DB_OK) return TDB_FAILURE;
|
|
}
|
|
}
|
|
if (winfo != winfobuf) {
|
|
tdb_Free(winfo);
|
|
}
|
|
return TDB_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
TDBWindexCursor*
|
|
tdbWindexGetCursor(TDBWindex* windex, const char* string)
|
|
{
|
|
DBT key;
|
|
DBT data;
|
|
int dbstatus;
|
|
TDBWindexCursor* cursor;
|
|
TDBInt32 i;
|
|
TDBInt32 l;
|
|
if (windex == NULL || string == NULL) {
|
|
tdb_ASSERT(0);
|
|
return NULL;
|
|
}
|
|
cursor = tdb_NEWZAP(TDBWindexCursor);
|
|
if (!cursor) return NULL;
|
|
cursor->windex = windex;
|
|
cursor->string = tdb_strdup(string);
|
|
if (!cursor->string) goto FAIL;
|
|
cursor->winfo = splitIntoWords(cursor->string, NULL, 0);
|
|
if (cursor->winfo == NULL) goto FAIL;
|
|
|
|
cursor->main = 0;
|
|
cursor->cmdflag = DB_CURRENT;
|
|
|
|
for (i=0 ; cursor->winfo[i].length > 0 ; i++) {
|
|
l = cursor->winfo[i].length;
|
|
if (l > cursor->winfo[cursor->main].length) {
|
|
cursor->main = i;
|
|
}
|
|
cursor->numwinfo = i + 1;
|
|
}
|
|
|
|
if (cursor->numwinfo == 0) goto FAIL;
|
|
|
|
|
|
dbstatus = windex->db->cursor(windex->db, NULL,
|
|
&(cursor->cursor), 0);
|
|
if (dbstatus != DB_OK) goto FAIL;
|
|
|
|
memset(&key, 0, sizeof(key));
|
|
memset(&data, 0, sizeof(data));
|
|
|
|
key.data = cursor->winfo[cursor->main].word;
|
|
key.size = cursor->winfo[cursor->main].length + 1;
|
|
|
|
dbstatus = cursor->cursor->c_get(cursor->cursor, &key, &data,
|
|
DB_SET_RANGE);
|
|
if (dbstatus != DB_OK && dbstatus != DB_NOTFOUND) goto FAIL;
|
|
|
|
return cursor;
|
|
FAIL:
|
|
tdbWindexCursorFree(cursor);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
TDBStatus
|
|
tdbWindexCursorFree(TDBWindexCursor* cursor)
|
|
{
|
|
if (!cursor) {
|
|
tdb_ASSERT(0);
|
|
return TDB_FAILURE;
|
|
}
|
|
if (cursor->string) tdb_Free(cursor->string);
|
|
if (cursor->cursor) cursor->cursor->c_close(cursor->cursor);
|
|
if (cursor->winfo) tdb_Free(cursor->winfo);
|
|
tdb_Free(cursor);
|
|
return TDB_SUCCESS;
|
|
}
|
|
|
|
|
|
TDBVector*
|
|
tdbWindexGetCursorValue(TDBWindexCursor* cursor)
|
|
{
|
|
TDBInt32 l;
|
|
DBT key;
|
|
DBT data;
|
|
TDBBool match;
|
|
TDBPtr recordnum;
|
|
TDBInt32 position;
|
|
TDBInt32 p;
|
|
char* ptr;
|
|
char* endptr;
|
|
TDBInt32 i;
|
|
char buf[MAXWORDLEN + 20];
|
|
int dbstatus;
|
|
TDBWindex* windex;
|
|
TDBVector* vector;
|
|
TDBNodePtr node;
|
|
if (cursor == NULL) {
|
|
tdb_ASSERT(0);
|
|
return NULL;
|
|
}
|
|
windex = cursor->windex;
|
|
while (1) {
|
|
memset(&key, 0, sizeof(key));
|
|
memset(&data, 0, sizeof(data));
|
|
dbstatus = cursor->cursor->c_get(cursor->cursor, &key, &data,
|
|
cursor->cmdflag);
|
|
cursor->cmdflag = DB_NEXT;
|
|
if (dbstatus != DB_OK) return NULL;
|
|
l = cursor->winfo[cursor->main].length;
|
|
if (key.size <= l ||
|
|
memcmp(key.data, cursor->winfo[cursor->main].word, l + 1) != 0) {
|
|
/* We're past the end of matching things. */
|
|
return NULL;
|
|
}
|
|
ptr = key.data + l + 1;
|
|
endptr = key.data + key.size;
|
|
recordnum = tdbGetPtr(&ptr, endptr);
|
|
position = tdbGetUInt16(&ptr, endptr);
|
|
position -= cursor->winfo[cursor->main].position;
|
|
match = TDB_TRUE;
|
|
for (i=0 ; i<cursor->numwinfo ; i++) {
|
|
if (i == cursor->main) continue;
|
|
p = position + cursor->winfo[i].position;
|
|
if (p < 0) {
|
|
match = TDB_FALSE;
|
|
break;
|
|
}
|
|
ptr = buf;
|
|
endptr = buf + sizeof(buf);
|
|
tdbPutString(&ptr, cursor->winfo[i].word, endptr);
|
|
tdbPutPtr(&ptr, recordnum, endptr);
|
|
tdbPutUInt16(&ptr, p, endptr);
|
|
key.data = buf;
|
|
key.size = ptr - buf;
|
|
memset(&data, 0, sizeof(data));
|
|
dbstatus = windex->db->get(windex->db,
|
|
tdbGetTransaction(windex->base),
|
|
&key, &data, 0);
|
|
if (dbstatus != DB_OK) {
|
|
match = TDB_FALSE;
|
|
break;
|
|
}
|
|
}
|
|
if (match) {
|
|
vector = tdbVectorNewFromRecord(windex->base, recordnum);
|
|
node = tdbVectorGetNonInternedNode(vector, 2);
|
|
if (node && node->type == TDBTYPE_STRING &&
|
|
tdb_strcasestr(node->d.str.string, cursor->string) != NULL) {
|
|
return vector;
|
|
}
|
|
tdbVectorFree(vector);
|
|
}
|
|
}
|
|
}
|
|
|