source: gsdl/trunk/lib/gdbmclass.cpp@ 15429

Last change on this file since 15429 was 15429, checked in by mdewsnip, 16 years ago

(Adding new db support) Moved gdbmclass out of infodbclass into its own file.

File size: 13.2 KB
Line 
1/**********************************************************************
2 *
3 * gdbmclass.cpp --
4 * Copyright (C) 1999-2008 The New Zealand Digital Library Project
5 *
6 * A component of the Greenstone digital library software
7 * from the New Zealand Digital Library Project at the
8 * University of Waikato, New Zealand.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 *
24 *********************************************************************/
25
26#include "gdbmclass.h"
27#include "unitool.h"
28#include "gsdlunicode.h"
29#include "OIDtools.h"
30
31
32gdbmclass::~gdbmclass()
33{
34 closedatabase();
35}
36
37// returns true if opened
38bool gdbmclass::opendatabase (const text_t &filename, int mode, int num_retrys,
39#ifdef __WIN32__
40 bool need_filelock
41#else
42 bool
43#endif
44 )
45{
46 text_t data_location;
47 int block_size = 512;
48
49 if (gdbmfile != NULL) {
50 if (openfile == filename) return true;
51 else closedatabase ();
52 }
53
54 openfile = filename;
55
56 char *namebuffer = filename.getcstr();
57 do {
58#ifdef __WIN32__
59 gdbmfile = gdbm_open (namebuffer, block_size, mode, 00664, NULL, (need_filelock) ? 1 : 0);
60#else
61 gdbmfile = gdbm_open (namebuffer, block_size, mode, 00664, NULL);
62#endif
63 --num_retrys;
64 } while (num_retrys>0 && gdbmfile==NULL &&
65 (gdbm_errno==GDBM_CANT_BE_READER || gdbm_errno==GDBM_CANT_BE_WRITER));
66 delete []namebuffer;
67
68 if (gdbmfile == NULL && logout != NULL) {
69 outconvertclass text_t2ascii;
70 (*logout) << text_t2ascii << "database open failed on: " << filename << "\n";
71 }
72
73 return (gdbmfile != NULL);
74}
75
76
77void gdbmclass::closedatabase ()
78{
79 if (gdbmfile == NULL) return;
80
81 gdbm_close (gdbmfile);
82 gdbmfile = NULL;
83 openfile.clear();
84}
85
86
87// returns true on success
88bool gdbmclass::setinfo (const text_t &key, const infodbclass &info)
89{
90 if (gdbmfile == NULL) return false;
91
92 text_t subkey;
93 text_t data;
94
95 // get all the keys and values
96 infodbclass::const_iterator info_here = info.begin();
97 infodbclass::const_iterator info_end = info.end();
98 while (info_here != info_end) {
99 // add the key
100 subkey.clear();
101 subkey.push_back('<');
102 text_t::const_iterator subkey_here = (*info_here).first.begin();
103 text_t::const_iterator subkey_end = (*info_here).first.end();
104 while (subkey_here != subkey_end) {
105 if (*subkey_here == '>') {
106 subkey.push_back('\\'); subkey.push_back('>');
107 } else if (*subkey_here == '\n') {
108 subkey.push_back('\\'); subkey.push_back('n');
109 } else if (*subkey_here == '\r') {
110 subkey.push_back('\\'); subkey.push_back('r');
111 } else if (*subkey_here == '\\') {
112 subkey.push_back('\\'); subkey.push_back('\\');
113 } else {
114 subkey.push_back (*subkey_here);
115 }
116 ++subkey_here;
117 }
118 subkey.push_back('>');
119
120 // add the values
121 text_tarray::const_iterator subvalue_here = (*info_here).second.begin();
122 text_tarray::const_iterator subvalue_end = (*info_here).second.end();
123 while (subvalue_here != subvalue_end) {
124 data += subkey;
125
126 text_t::const_iterator thissubvalue_here = (*subvalue_here).begin();
127 text_t::const_iterator thissubvalue_end = (*subvalue_here).end();
128 while (thissubvalue_here != thissubvalue_end) {
129 if (*thissubvalue_here == '>') {
130 data.push_back('\\'); data.push_back('>');
131 } else if (*thissubvalue_here == '\n') {
132 data.push_back('\\'); data.push_back('n');
133 } else if (*thissubvalue_here == '\r') {
134 data.push_back('\\'); data.push_back('r');
135 } else if (*thissubvalue_here == '\\') {
136 data.push_back('\\'); data.push_back('\\');
137 } else {
138 data.push_back (*thissubvalue_here);
139 }
140
141 ++thissubvalue_here;
142 }
143
144 data.push_back('\n');
145 ++subvalue_here;
146 }
147
148 ++info_here;
149 }
150
151 // store the value
152 datum key_data;
153 datum data_data;
154
155 // get a utf-8 encoded c string of the unicode key
156 key_data.dptr = (to_utf8(key)).getcstr();
157 if (key_data.dptr == NULL) {
158 if (logout != NULL) (*logout) << "gdbmclass: out of memory\n";
159 return false;
160 }
161 key_data.dsize = strlen (key_data.dptr);
162
163 data_data.dptr = (to_utf8(data)).getcstr();
164 if (data_data.dptr == NULL) {
165 if (logout != NULL) (*logout) << "gdbmclass: out of memory\n";
166 delete []key_data.dptr;
167 return false;
168 }
169 data_data.dsize = strlen (data_data.dptr);
170
171 int ret = gdbm_store (gdbmfile, key_data, data_data, GDBM_REPLACE);
172 delete []key_data.dptr;
173 delete []data_data.dptr;
174
175 return (ret == 0);
176}
177
178
179//returns true on success
180bool gdbmclass::setinfo (const text_t &key, const text_t &data)
181{
182 if (gdbmfile == NULL) return false;
183
184 // store the value
185 datum key_data;
186 datum data_data;
187
188 // get a utf-8 encoded c string of the unicode key
189 key_data.dptr = (to_utf8(key)).getcstr();
190 if (key_data.dptr == NULL) {
191 if (logout != NULL) (*logout) << "gdbmclass: out of memory\n";
192 return false;
193 }
194 key_data.dsize = strlen (key_data.dptr);
195
196 data_data.dptr = (to_utf8(data)).getcstr();
197 if (data_data.dptr == NULL) {
198 if (logout != NULL) (*logout) << "gdbmclass: out of memory\n";
199 delete []key_data.dptr;
200 return false;
201 }
202 data_data.dsize = strlen (data_data.dptr);
203
204 int ret = gdbm_store (gdbmfile, key_data, data_data, GDBM_REPLACE);
205 delete []key_data.dptr;
206 delete []data_data.dptr;
207
208 return (ret == 0);
209}
210
211
212void gdbmclass::deletekey (const text_t &key)
213{
214 if (gdbmfile == NULL) return;
215
216 // get a utf-8 encoded c string of the unicode key
217 datum key_data;
218 key_data.dptr = (to_utf8(key)).getcstr();
219 if (key_data.dptr == NULL) return;
220 key_data.dsize = strlen (key_data.dptr);
221
222 // delete the key
223 gdbm_delete (gdbmfile, key_data);
224
225 // free up the key memory
226 delete []key_data.dptr;
227}
228
229
230// getfirstkey and getnextkey are used for traversing the database
231// no insertions or deletions should be carried out while traversing
232// the database. when there are no keys left to visit in the database
233// an empty string is returned.
234text_t gdbmclass::getfirstkey ()
235{
236 if (gdbmfile == NULL) return g_EmptyText;
237
238 // get the first key
239 datum firstkey_data = gdbm_firstkey (gdbmfile);
240 if (firstkey_data.dptr == NULL) return g_EmptyText;
241
242 // convert it to text_t
243 text_t firstkey;
244 firstkey.setcarr (firstkey_data.dptr, firstkey_data.dsize);
245 free (firstkey_data.dptr);
246 return to_uni(firstkey); // convert to unicode
247}
248
249
250text_t gdbmclass::getnextkey (const text_t &key)
251{
252 if (gdbmfile == NULL || key.empty()) return g_EmptyText;
253
254 // get a utf-8 encoded c string of the unicode key
255 datum key_data;
256 key_data.dptr = (to_utf8(key)).getcstr();
257 if (key_data.dptr == NULL) return g_EmptyText;
258 key_data.dsize = strlen (key_data.dptr);
259
260 // get the next key
261 datum nextkey_data = gdbm_nextkey (gdbmfile, key_data);
262 if (nextkey_data.dptr == NULL) {
263 delete []key_data.dptr;
264 return g_EmptyText;
265 }
266
267 // convert it to text_t
268 text_t nextkey;
269 nextkey.setcarr (nextkey_data.dptr, nextkey_data.dsize);
270 free (nextkey_data.dptr);
271 delete []key_data.dptr;
272 return to_uni(nextkey); // convert to unicode
273}
274
275
276// replaces the .fc, .lc, .pr, .rt, .ns and .ps syntax (first child,
277// last child, parent, root, next sibling, previous sibling)
278// it expects child, parent, etc. to exist if syntax has been used
279// so you should test before using
280text_t gdbmclass::translate_OID (const text_t &inOID, infodbclass &info)
281{
282 if (inOID.size() < 4) return inOID;
283 if (findchar (inOID.begin(), inOID.end(), '.') == inOID.end()) return inOID;
284
285 text_t OID = inOID;
286 text_tarray tailarray;
287 text_t tail = substr (OID.end()-3, OID.end());
288 if (tail == ".rt") {
289 get_top (inOID, OID);
290 return OID;
291 }
292 while (tail == ".fc" || tail == ".lc" || tail == ".pr" ||
293 tail == ".ns" || tail == ".ps") {
294 tailarray.push_back(tail);
295 OID.erase (OID.end()-3, OID.end());
296 tail = substr (OID.end()-3, OID.end());
297 if (tail == ".rt") {
298 get_top (inOID, OID);
299 return OID;
300 }
301 }
302
303 if (tailarray.empty()) return inOID;
304 text_tarray::const_iterator begin = tailarray.begin();
305 text_tarray::const_iterator here = tailarray.end() - 1;
306
307 while (here >= begin) {
308
309 if (*here == ".fc")
310 get_first_child (OID, info);
311 else if (*here == ".lc")
312 get_last_child (OID, info);
313 else if (*here == ".pr")
314 OID = get_parent (OID);
315 else if (*here == ".ns")
316 get_next_sibling (OID, info);
317 else if (*here == ".ps")
318 get_previous_sibling (OID, info);
319
320 if (here == begin)
321 break;
322 --here;
323 }
324
325 return OID;
326}
327
328
329void gdbmclass::get_first_child (text_t &OID, infodbclass &info)
330{
331 text_t firstchild;
332 if (getinfo (OID, info)) {
333 text_t &contains = info["contains"];
334 if (!contains.empty()) {
335 text_t parent = OID;
336 getdelimitstr (contains.begin(), contains.end(), ';', firstchild);
337 if (firstchild.empty()) OID = contains;
338 else OID = firstchild;
339 if (*(OID.begin()) == '"') translate_parent (OID, parent);
340 }
341 }
342}
343
344
345void gdbmclass::get_last_child (text_t &OID, infodbclass &info)
346{
347 text_tarray children;
348 if (getinfo (OID, info)) {
349 text_t &contains = info["contains"];
350 if (!contains.empty()) {
351 text_t parent = OID;
352 splitchar (contains.begin(), contains.end(), ';', children);
353 OID = children.back();
354 if (*(OID.begin()) == '"') translate_parent (OID, parent);
355 }
356 }
357}
358
359
360void gdbmclass::get_next_sibling (text_t &OID, infodbclass &info)
361{
362 text_tarray siblings;
363 text_t parent = get_parent (OID);
364
365 if (getinfo (parent, info)) {
366 text_t &contains = info["contains"];
367 if (!contains.empty()) {
368 splitchar (contains.begin(), contains.end(), ';', siblings);
369 text_tarray::const_iterator here = siblings.begin();
370 text_tarray::const_iterator end = siblings.end();
371 text_t shrunk_OID = OID;
372 shrink_parent (shrunk_OID);
373 while (here != end) {
374 if (*here == shrunk_OID && (here+1 != end)) {
375 OID = *(here+1);
376 if (*(OID.begin()) == '"') translate_parent (OID, parent);
377 break;
378 }
379 ++here;
380 }
381 }
382 }
383}
384
385
386void gdbmclass::get_previous_sibling (text_t &OID, infodbclass &info)
387{
388 text_tarray siblings;
389 text_t parent = get_parent (OID);
390
391 if (getinfo (parent, info)) {
392 text_t &contains = info["contains"];
393 if (!contains.empty()) {
394 splitchar (contains.begin(), contains.end(), ';', siblings);
395 text_tarray::const_iterator here = siblings.begin();
396 text_tarray::const_iterator end = siblings.end();
397 text_t shrunk_OID = OID;
398 shrink_parent (shrunk_OID);
399 while (here != end) {
400 if (*here == shrunk_OID && (here != siblings.begin())) {
401 OID = *(here-1);
402 if (*(OID.begin()) == '"') translate_parent (OID, parent);
403 break;
404 }
405 ++here;
406 }
407 }
408 }
409}
410
411
412// returns true on success
413bool gdbmclass::getinfo (const text_t& key, infodbclass &info)
414{
415 text_t data;
416
417 if (!getkeydata (key, data)) return false;
418 text_t::iterator here = data.begin ();
419 text_t::iterator end = data.end ();
420
421 text_t ikey, ivalue;
422 info.clear (); // reset info
423
424 while (getinfoline (here, end, ikey, ivalue)) {
425 info.addinfo (ikey, ivalue);
426 }
427
428 return true;
429}
430
431
432// returns true if exists
433bool gdbmclass::exists (const text_t& key)
434{
435 text_t data;
436 return getkeydata (key, data);
437}
438
439
440// returns true on success
441bool gdbmclass::getkeydata (const text_t& key, text_t &data)
442{
443 datum key_data;
444 datum return_data;
445
446 if (gdbmfile == NULL) return false;
447
448 // get a utf-8 encoded c string of the unicode key
449 key_data.dptr = (to_utf8(key)).getcstr();
450 if (key_data.dptr == NULL) {
451 if (logout != NULL) (*logout) << "gdbmclass: out of memory\n";
452 return false;
453 }
454 key_data.dsize = strlen (key_data.dptr);
455
456 // fetch the result
457 return_data = gdbm_fetch (gdbmfile, key_data);
458 delete []key_data.dptr;
459
460 if (return_data.dptr == NULL) return false;
461
462 data.setcarr (return_data.dptr, return_data.dsize);
463 free (return_data.dptr);
464 data = to_uni(data); // convert to unicode
465
466 return true;
467}
468
469
470// returns true on success
471bool gdbmclass::getinfoline (text_t::iterator &here, text_t::iterator end,
472 text_t &key, text_t &value)
473{
474 key.clear();
475 value.clear();
476
477 // ignore white space
478 while (here != end && is_unicode_space (*here)) ++here;
479
480 // get the '<'
481 if (here == end || *here != '<') return false;
482 ++here;
483
484 // get the key
485 while (here != end && *here != '>') {
486 key.push_back(*here);
487 ++here;
488 }
489
490 // get the '>'
491 if (here == end || *here != '>') return false;
492 ++here;
493
494 // get the value
495 while (here != end && *here != '\n') {
496 if (*here == '\\') {
497 // found escape character
498 ++here;
499 if (here != end) {
500 if (*here == 'n') value.push_back ('\n');
501 else if (*here == 'r') value.push_back ('\r');
502 else value.push_back(*here);
503 }
504
505 } else {
506 // a normal character
507 value.push_back(*here);
508 }
509
510 ++here;
511 }
512
513 return true;
514}
Note: See TracBrowser for help on using the repository browser.