/********************************************************************** * * infodbclass.cpp -- * Copyright (C) 1999 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 "infodbclass.h" #include "unitool.h" #include "gsdlunicode.h" #include "fileutil.h" #include "OIDtools.h" #include // constructors infodbclass::infodbclass () { } void infodbclass::setinfo (const text_t &key, const text_t &value) { text_tarray &tarr = info[key]; tarr.erase(tarr.begin(), tarr.end()); tarr.push_back(value); } void infodbclass::setintinfo (const text_t &key, int value) { setinfo (key, value); } void infodbclass::setcinfo (const text_t &key, unsigned short c) { text_t t; t.push_back (c); setinfo (key, t); } text_t *infodbclass::getinfo (const text_t &key) { iterator here = info.find (key); if (here == info.end()) return NULL; if ((*here).second.empty()) return NULL; return &((*here).second[0]); } int infodbclass::getintinfo (const text_t &key) { text_t *t = getinfo (key); if (t == NULL) return 0; return t->getint(); } text_t &infodbclass::operator[] (const text_t &key) { text_tarray &tarr = info[key]; if (tarr.empty()) { text_t e; tarr.push_back(e); } return tarr[0]; } void infodbclass::addinfo (const text_t &key, const text_t &value) { text_tarray &tarr = info[key]; tarr.push_back (value); } void infodbclass::addintinfo (const text_t &key, int value) { addinfo (key, value); } void infodbclass::addcinfo (const text_t &key, unsigned short c) { text_t t; t.push_back(c); addinfo (key, t); } text_tarray *infodbclass::getmultinfo (const text_t &key) { iterator here = info.find (key); if (here == info.end()) return NULL; return &((*here).second); } 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; char *namebuffer = filename.getcstr(); do { #ifdef __WIN32__ gdbmfile = gdbm_open (namebuffer, block_size, mode, 00664, NULL, (need_filelock) ? 1 : 0); #else gdbmfile = gdbm_open (namebuffer, block_size, 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 { 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 { 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; } 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; } 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; } // 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 ""; // get the first key datum firstkey_data = gdbm_firstkey (gdbmfile); if (firstkey_data.dptr == NULL) return ""; // 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 ""; // 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); // get the next key datum nextkey_data = gdbm_nextkey (gdbmfile, key_data); if (nextkey_data.dptr == NULL) { delete key_data.dptr; return ""; } // 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 } // replaces the .fc, .lc, .pr, .ns and .ps syntax (first child, // last child, parent, next sibling, previous sibling) // it expects child, parent, etc. to exist if syntax has been used // so you should test before using text_t gdbmclass::translate_OID (const text_t &inOID, infodbclass &info) { if (inOID.size() < 4) return inOID; if (findchar (inOID.begin(), inOID.end(), '.') == inOID.end()) return inOID; text_t OID = inOID; text_tarray tailarray; text_t tail = substr (OID.end()-3, OID.end()); while (tail == ".fc" || tail == ".lc" || tail == ".pr" || tail == ".ns" || tail == ".ps") { tailarray.push_back(tail); OID.erase (OID.end()-3, OID.end()); tail = substr (OID.end()-3, OID.end()); } if (!tailarray.size()) return inOID; text_tarray::const_iterator begin = tailarray.begin(); text_tarray::const_iterator here = tailarray.end() - 1; while (here >= begin) { if (*here == ".fc") get_first_child (OID, info); else if (*here == ".lc") get_last_child (OID, info); else if (*here == ".pr") OID = get_parent (OID); else if (*here == ".ns") get_next_sibling (OID, info); else if (*here == ".ps") get_previous_sibling (OID, info); here --; } return OID; } void gdbmclass::get_first_child (text_t &OID, infodbclass &info) { text_t firstchild; if (getinfo (OID, info)) { text_t &contains = info["contains"]; if (!contains.empty()) { text_t parent = OID; getdelimitstr (contains.begin(), contains.end(), ';', firstchild); if (firstchild.empty()) OID = contains; else OID = firstchild; if (*(OID.begin()) == '"') translate_parent (OID, parent); } } } void gdbmclass::get_last_child (text_t &OID, infodbclass &info) { text_tarray children; if (getinfo (OID, info)) { text_t &contains = info["contains"]; if (!contains.empty()) { text_t parent = OID; splitchar (contains.begin(), contains.end(), ';', children); OID = children.back(); if (*(OID.begin()) == '"') translate_parent (OID, parent); } } } void gdbmclass::get_next_sibling (text_t &OID, infodbclass &info) { text_tarray siblings; text_t parent = get_parent (OID); if (getinfo (parent, info)) { text_t &contains = info["contains"]; if (!contains.empty()) { splitchar (contains.begin(), contains.end(), ';', siblings); text_tarray::const_iterator here = siblings.begin(); text_tarray::const_iterator end = siblings.end(); text_t shrunk_OID = OID; shrink_parent (shrunk_OID); while (here != end) { if (*here == shrunk_OID && (here+1 != end)) { OID = *(here+1); if (*(OID.begin()) == '"') translate_parent (OID, parent); break; } here ++; } } } } void gdbmclass::get_previous_sibling (text_t &OID, infodbclass &info) { text_tarray siblings; text_t parent = get_parent (OID); if (getinfo (parent, info)) { text_t &contains = info["contains"]; if (!contains.empty()) { splitchar (contains.begin(), contains.end(), ';', siblings); text_tarray::const_iterator here = siblings.begin(); text_tarray::const_iterator end = siblings.end(); text_t shrunk_OID = OID; shrink_parent (shrunk_OID); while (here != end) { if (*here == shrunk_OID && (here != siblings.begin())) { OID = *(here-1); if (*(OID.begin()) == '"') translate_parent (OID, parent); break; } here ++; } } } } // returns true on success bool gdbmclass::getinfo (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 (text_t key) { text_t data; return getkeydata (key, data); } // returns true on success bool gdbmclass::getkeydata (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; }