source: branches/z3950-branch/gsdl/src/recpt/infodbclass.cpp@ 1342

Last change on this file since 1342 was 1342, checked in by johnmcp, 24 years ago

Relatively stable z39.50 implementation now, merged with the mgpp source.
(Still needs a decent interface and query language though...)

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