source: trunk/gsdl/src/recpt/infodbclass.cpp@ 1861

Last change on this file since 1861 was 1459, checked in by sjboddie, 24 years ago

Did some tidying up of destructor functions as a result of a few hours
spent playing with ccmalloc

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