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

Last change on this file since 11259 was 11259, checked in by mdewsnip, 18 years ago

Various little bug fixes and improvements (many to get things working with Visual Studio 2005), by Emanuel Dejanu.

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