/********************************************************************** * * gdbmclass.cpp -- * Copyright (C) 1999-2008 The New Zealand Digital Library Project * * A component of the Greenstone digital library software * from the New Zealand Digital Library Project at the * University of Waikato, New Zealand. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * *********************************************************************/ #include "gdbmclass.h" #include "unitool.h" #include "gsdlunicode.h" #include "OIDtools.h" gdbmclass::~gdbmclass() { closedatabase(); } // returns true if opened bool gdbmclass::opendatabase (const text_t &filename, int mode, int num_retrys, #ifdef __WIN32__ bool need_filelock #else bool #endif ) { text_t data_location; int block_size = 512; if (gdbmfile != NULL) { if (openfile == filename) return true; else closedatabase (); } openfile = filename; // Map the DB mode values into GDBM mode values int gdbm_mode = GDBM_READER; if (mode == DB_WRITER) { gdbm_mode = GDBM_WRITER; } else if (mode == DB_WRITER_CREATE) { gdbm_mode = GDBM_WRCREAT; } char *namebuffer = filename.getcstr(); do { #ifdef __WIN32__ gdbmfile = gdbm_open (namebuffer, block_size, gdbm_mode, 00664, NULL, (need_filelock) ? 1 : 0); #else gdbmfile = gdbm_open (namebuffer, block_size, gdbm_mode, 00664, NULL); #endif --num_retrys; } while (num_retrys>0 && gdbmfile==NULL && (gdbm_errno==GDBM_CANT_BE_READER || gdbm_errno==GDBM_CANT_BE_WRITER)); delete []namebuffer; if (gdbmfile == NULL && logout != NULL) { outconvertclass text_t2ascii; (*logout) << text_t2ascii << "database open failed on: " << filename << "\n"; } return (gdbmfile != NULL); } void gdbmclass::closedatabase () { if (gdbmfile == NULL) return; gdbm_close (gdbmfile); gdbmfile = NULL; openfile.clear(); } // returns true on success bool gdbmclass::setinfo (const text_t &key, const infodbclass &info) { if (gdbmfile == NULL) return false; text_t subkey; text_t data; // get all the keys and values infodbclass::const_iterator info_here = info.begin(); infodbclass::const_iterator info_end = info.end(); while (info_here != info_end) { // add the key subkey.clear(); subkey.push_back('<'); text_t::const_iterator subkey_here = (*info_here).first.begin(); text_t::const_iterator subkey_end = (*info_here).first.end(); while (subkey_here != subkey_end) { if (*subkey_here == '>') { subkey.push_back('\\'); subkey.push_back('>'); } else if (*subkey_here == '\n') { subkey.push_back('\\'); subkey.push_back('n'); } else if (*subkey_here == '\r') { subkey.push_back('\\'); subkey.push_back('r'); } else if (*subkey_here == '\\') { subkey.push_back('\\'); subkey.push_back('\\'); } else { subkey.push_back (*subkey_here); } ++subkey_here; } subkey.push_back('>'); // add the values text_tarray::const_iterator subvalue_here = (*info_here).second.begin(); text_tarray::const_iterator subvalue_end = (*info_here).second.end(); while (subvalue_here != subvalue_end) { data += subkey; text_t::const_iterator thissubvalue_here = (*subvalue_here).begin(); text_t::const_iterator thissubvalue_end = (*subvalue_here).end(); while (thissubvalue_here != thissubvalue_end) { if (*thissubvalue_here == '>') { data.push_back('\\'); data.push_back('>'); } else if (*thissubvalue_here == '\n') { data.push_back('\\'); data.push_back('n'); } else if (*thissubvalue_here == '\r') { data.push_back('\\'); data.push_back('r'); } else if (*thissubvalue_here == '\\') { data.push_back('\\'); data.push_back('\\'); } else { data.push_back (*thissubvalue_here); } ++thissubvalue_here; } data.push_back('\n'); ++subvalue_here; } ++info_here; } // store the value datum key_data; datum data_data; // get a utf-8 encoded c string of the unicode key key_data.dptr = (to_utf8(key)).getcstr(); if (key_data.dptr == NULL) { if (logout != NULL) (*logout) << "gdbmclass: out of memory\n"; return false; } key_data.dsize = strlen (key_data.dptr); data_data.dptr = (to_utf8(data)).getcstr(); if (data_data.dptr == NULL) { if (logout != NULL) (*logout) << "gdbmclass: out of memory\n"; delete []key_data.dptr; return false; } data_data.dsize = strlen (data_data.dptr); int ret = gdbm_store (gdbmfile, key_data, data_data, GDBM_REPLACE); delete []key_data.dptr; delete []data_data.dptr; return (ret == 0); } //returns true on success bool gdbmclass::setinfo (const text_t &key, const text_t &data) { if (gdbmfile == NULL) return false; // store the value datum key_data; datum data_data; // get a utf-8 encoded c string of the unicode key key_data.dptr = (to_utf8(key)).getcstr(); if (key_data.dptr == NULL) { if (logout != NULL) (*logout) << "gdbmclass: out of memory\n"; return false; } key_data.dsize = strlen (key_data.dptr); data_data.dptr = (to_utf8(data)).getcstr(); if (data_data.dptr == NULL) { if (logout != NULL) (*logout) << "gdbmclass: out of memory\n"; delete []key_data.dptr; return false; } data_data.dsize = strlen (data_data.dptr); int ret = gdbm_store (gdbmfile, key_data, data_data, GDBM_REPLACE); delete []key_data.dptr; delete []data_data.dptr; return (ret == 0); } void gdbmclass::deletekey (const text_t &key) { if (gdbmfile == NULL) return; // get a utf-8 encoded c string of the unicode key datum key_data; key_data.dptr = (to_utf8(key)).getcstr(); if (key_data.dptr == NULL) return; key_data.dsize = strlen (key_data.dptr); // delete the key gdbm_delete (gdbmfile, key_data); // free up the key memory delete []key_data.dptr; } text_tarray gdbmclass::getkeys () { text_tarray keys; text_t key = getfirstkey(); while (!key.empty()) { keys.push_back(key); key = getnextkey(key); } return keys; } // getfirstkey and getnextkey are used for traversing the database // no insertions or deletions should be carried out while traversing // the database. when there are no keys left to visit in the database // an empty string is returned. text_t gdbmclass::getfirstkey () { if (gdbmfile == NULL) return g_EmptyText; // get the first key datum firstkey_data = gdbm_firstkey (gdbmfile); if (firstkey_data.dptr == NULL) return g_EmptyText; // convert it to text_t text_t firstkey; firstkey.setcarr (firstkey_data.dptr, firstkey_data.dsize); free (firstkey_data.dptr); return to_uni(firstkey); // convert to unicode } text_t gdbmclass::getnextkey (const text_t &key) { if (gdbmfile == NULL || key.empty()) return g_EmptyText; // get a utf-8 encoded c string of the unicode key datum key_data; key_data.dptr = (to_utf8(key)).getcstr(); if (key_data.dptr == NULL) return g_EmptyText; key_data.dsize = strlen (key_data.dptr); // get the next key datum nextkey_data = gdbm_nextkey (gdbmfile, key_data); if (nextkey_data.dptr == NULL) { delete []key_data.dptr; return g_EmptyText; } // convert it to text_t text_t nextkey; nextkey.setcarr (nextkey_data.dptr, nextkey_data.dsize); free (nextkey_data.dptr); delete []key_data.dptr; return to_uni(nextkey); // convert to unicode } // returns true on success bool gdbmclass::getinfo (const text_t& key, infodbclass &info) { text_t data; if (!getkeydata (key, data)) return false; text_t::iterator here = data.begin (); text_t::iterator end = data.end (); text_t ikey, ivalue; info.clear (); // reset info while (getinfoline (here, end, ikey, ivalue)) { info.addinfo (ikey, ivalue); } return true; } // returns true if exists bool gdbmclass::exists (const text_t& key) { text_t data; return getkeydata (key, data); } // returns true on success bool gdbmclass::getkeydata (const text_t& key, text_t &data) { datum key_data; datum return_data; if (gdbmfile == NULL) return false; // get a utf-8 encoded c string of the unicode key key_data.dptr = (to_utf8(key)).getcstr(); if (key_data.dptr == NULL) { if (logout != NULL) (*logout) << "gdbmclass: out of memory\n"; return false; } key_data.dsize = strlen (key_data.dptr); // fetch the result return_data = gdbm_fetch (gdbmfile, key_data); delete []key_data.dptr; if (return_data.dptr == NULL) return false; data.setcarr (return_data.dptr, return_data.dsize); free (return_data.dptr); data = to_uni(data); // convert to unicode return true; } // returns true on success bool gdbmclass::getinfoline (text_t::iterator &here, text_t::iterator end, text_t &key, text_t &value) { key.clear(); value.clear(); // ignore white space while (here != end && is_unicode_space (*here)) ++here; // get the '<' if (here == end || *here != '<') return false; ++here; // get the key while (here != end && *here != '>') { key.push_back(*here); ++here; } // get the '>' if (here == end || *here != '>') return false; ++here; // get the value while (here != end && *here != '\n') { if (*here == '\\') { // found escape character ++here; if (here != end) { if (*here == 'n') value.push_back ('\n'); else if (*here == 'r') value.push_back ('\r'); else value.push_back(*here); } } else { // a normal character value.push_back(*here); } ++here; } return true; }