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

Last change on this file since 928 was 928, checked in by kjm18, 24 years ago

search history stuff added.

  • Property svn:keywords set to Author Date Id Revision
File size: 15.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 * $Id: infodbclass.cpp 928 2000-02-15 22:53:52Z kjm18 $
25 *
26 *********************************************************************/
27
28/*
29 $Log$
30 Revision 1.6 2000/02/15 22:53:50 kjm18
31 search history stuff added.
32
33 Revision 1.5 1999/10/19 03:23:42 davidb
34 Collection building support through web pages
35 and internal and external link handling for collection documents
36
37 Revision 1.4 1999/09/07 04:56:56 sjboddie
38 added GPL notice
39
40 Revision 1.3 1999/09/02 00:26:10 rjmcnab
41 now there can be multiple values for a single key
42
43 Revision 1.2 1999/07/13 23:24:05 rjmcnab
44 Added functionality to modify a gdbm database.
45
46 Revision 1.1 1999/07/11 08:27:52 rjmcnab
47 Moved from src/colservr and added capability to write out data.
48
49 Revision 1.4 1999/05/10 03:43:48 sjboddie
50 lots of changes to lots of files - getting document action going
51
52 Revision 1.3 1999/04/30 02:00:46 sjboddie
53 lots of stuff to do with getting documentaction working
54
55 Revision 1.2 1999/04/06 22:20:31 rjmcnab
56 Got browsefilter working.
57
58 Revision 1.1 1999/03/30 05:10:07 rjmcnab
59 Initial revision.
60
61 */
62
63
64#include "infodbclass.h"
65#include "unitool.h"
66#include "gsdlunicode.h"
67#include "fileutil.h"
68#include "OIDtools.h"
69#include <stdlib.h>
70
71
72
73
74// constructors
75infodbclass::infodbclass () {
76}
77
78void infodbclass::setinfo (const text_t &key, const text_t &value) {
79 text_tarray &tarr = info[key];
80 tarr.erase(tarr.begin(), tarr.end());
81 tarr.push_back(value);
82}
83
84void infodbclass::setintinfo (const text_t &key, int value) {
85 setinfo (key, value);
86}
87
88void infodbclass::setcinfo (const text_t &key, unsigned short c) {
89 text_t t;
90 t.push_back (c);
91 setinfo (key, t);
92}
93
94text_t *infodbclass::getinfo (const text_t &key) {
95 iterator here = info.find (key);
96 if (here == info.end()) return NULL;
97
98 if ((*here).second.empty()) return NULL;
99
100 return &((*here).second[0]);
101}
102
103int infodbclass::getintinfo (const text_t &key) {
104 text_t *t = getinfo (key);
105 if (t == NULL) return 0;
106 return t->getint();
107}
108
109text_t &infodbclass::operator[] (const text_t &key) {
110 text_tarray &tarr = info[key];
111 if (tarr.empty()) {
112 text_t e;
113 tarr.push_back(e);
114 }
115 return tarr[0];
116}
117
118
119void infodbclass::addinfo (const text_t &key, const text_t &value) {
120 text_tarray &tarr = info[key];
121 tarr.push_back (value);
122}
123
124void infodbclass::addintinfo (const text_t &key, int value) {
125 addinfo (key, value);
126}
127
128void infodbclass::addcinfo (const text_t &key, unsigned short c) {
129 text_t t;
130 t.push_back(c);
131 addinfo (key, t);
132}
133
134text_tarray *infodbclass::getmultinfo (const text_t &key) {
135 iterator here = info.find (key);
136 if (here == info.end()) return NULL;
137
138 return &((*here).second);
139}
140
141
142
143
144// returns true if opened
145bool gdbmclass::opendatabase (const text_t &filename, int mode, int num_retrys,
146#ifdef __WIN32__
147 bool need_filelock
148#else
149 bool
150#endif
151 ) {
152 text_t data_location;
153 int block_size = 512;
154
155 if (gdbmfile != NULL) {
156 if (openfile == filename) return true;
157 else closedatabase ();
158 }
159
160 openfile = filename;
161
162 char *namebuffer = filename.getcstr();
163 do {
164#ifdef __WIN32__
165 gdbmfile = gdbm_open (namebuffer, block_size, mode, 00664, NULL, (need_filelock) ? 1 : 0);
166#else
167 gdbmfile = gdbm_open (namebuffer, block_size, mode, 00664, NULL);
168#endif
169 num_retrys--;
170 } while (num_retrys>0 && gdbmfile==NULL &&
171 (gdbm_errno==GDBM_CANT_BE_READER || gdbm_errno==GDBM_CANT_BE_WRITER));
172 delete namebuffer;
173
174 if (gdbmfile == NULL && logout != NULL) {
175 outconvertclass text_t2ascii;
176 (*logout) << text_t2ascii << "database open failed on: " << filename << "\n";
177 }
178
179 return (gdbmfile != NULL);
180}
181
182void gdbmclass::closedatabase () {
183 if (gdbmfile == NULL) return;
184
185 gdbm_close (gdbmfile);
186 gdbmfile = NULL;
187 openfile.clear();
188}
189
190
191// returns true on success
192bool gdbmclass::setinfo (const text_t &key, const infodbclass &info) {
193 if (gdbmfile == NULL) return false;
194
195 text_t subkey;
196 text_t data;
197
198 // get all the keys and values
199 infodbclass::const_iterator info_here = info.begin();
200 infodbclass::const_iterator info_end = info.end();
201 while (info_here != info_end) {
202 // add the key
203 subkey.clear();
204 subkey.push_back('<');
205 text_t::const_iterator subkey_here = (*info_here).first.begin();
206 text_t::const_iterator subkey_end = (*info_here).first.end();
207 while (subkey_here != subkey_end) {
208 if (*subkey_here == '>') {
209 subkey.push_back('\\'); subkey.push_back('>');
210 } else if (*subkey_here == '\n') {
211 subkey.push_back('\\'); subkey.push_back('n');
212 } else if (*subkey_here == '\r') {
213 subkey.push_back('\\'); subkey.push_back('r');
214 } else {
215 subkey.push_back (*subkey_here);
216 }
217 subkey_here++;
218 }
219 subkey.push_back('>');
220
221 // add the values
222 text_tarray::const_iterator subvalue_here = (*info_here).second.begin();
223 text_tarray::const_iterator subvalue_end = (*info_here).second.end();
224 while (subvalue_here != subvalue_end) {
225 data += subkey;
226
227 text_t::const_iterator thissubvalue_here = (*subvalue_here).begin();
228 text_t::const_iterator thissubvalue_end = (*subvalue_here).end();
229 while (thissubvalue_here != thissubvalue_end) {
230 if (*thissubvalue_here == '>') {
231 data.push_back('\\'); data.push_back('>');
232 } else if (*thissubvalue_here == '\n') {
233 data.push_back('\\'); data.push_back('n');
234 } else if (*thissubvalue_here == '\r') {
235 data.push_back('\\'); data.push_back('r');
236 } else {
237 data.push_back (*thissubvalue_here);
238 }
239
240 thissubvalue_here++;
241 }
242
243 data.push_back('\n');
244 subvalue_here++;
245 }
246
247 info_here++;
248 }
249
250 // store the value
251 datum key_data;
252 datum data_data;
253
254 // get a utf-8 encoded c string of the unicode key
255 key_data.dptr = (to_utf8(key)).getcstr();
256 if (key_data.dptr == NULL) {
257 if (logout != NULL) (*logout) << "gdbmclass: out of memory\n";
258 return false;
259 }
260 key_data.dsize = strlen (key_data.dptr);
261
262 data_data.dptr = (to_utf8(data)).getcstr();
263 if (data_data.dptr == NULL) {
264 if (logout != NULL) (*logout) << "gdbmclass: out of memory\n";
265 delete key_data.dptr;
266 }
267 data_data.dsize = strlen (data_data.dptr);
268
269 int ret = gdbm_store (gdbmfile, key_data, data_data, GDBM_REPLACE);
270 delete key_data.dptr;
271 delete data_data.dptr;
272
273 return (ret == 0);
274
275}
276
277//returns true on success
278bool gdbmclass::setinfo (const text_t &key, const text_t &data) {
279 if (gdbmfile == NULL) return false;
280
281 // store the value
282 datum key_data;
283 datum data_data;
284
285 // get a utf-8 encoded c string of the unicode key
286 key_data.dptr = (to_utf8(key)).getcstr();
287 if (key_data.dptr == NULL) {
288 if (logout != NULL) (*logout) << "gdbmclass: out of memory\n";
289 return false;
290 }
291 key_data.dsize = strlen (key_data.dptr);
292
293 data_data.dptr = (to_utf8(data)).getcstr();
294 if (data_data.dptr == NULL) {
295 if (logout != NULL) (*logout) << "gdbmclass: out of memory\n";
296 delete key_data.dptr;
297 }
298 data_data.dsize = strlen (data_data.dptr);
299
300 int ret = gdbm_store (gdbmfile, key_data, data_data, GDBM_REPLACE);
301 delete key_data.dptr;
302 delete data_data.dptr;
303
304 return (ret == 0);
305
306}
307
308
309void gdbmclass::deletekey (const text_t &key) {
310 if (gdbmfile == NULL) return;
311
312 // get a utf-8 encoded c string of the unicode key
313 datum key_data;
314 key_data.dptr = (to_utf8(key)).getcstr();
315 if (key_data.dptr == NULL) return;
316 key_data.dsize = strlen (key_data.dptr);
317
318 // delete the key
319 gdbm_delete (gdbmfile, key_data);
320
321 // free up the key memory
322 delete key_data.dptr;
323}
324
325
326// getfirstkey and getnextkey are used for traversing the database
327// no insertions or deletions should be carried out while traversing
328// the database. when there are no keys left to visit in the database
329// an empty string is returned.
330text_t gdbmclass::getfirstkey () {
331 if (gdbmfile == NULL) return "";
332
333 // get the first key
334 datum firstkey_data = gdbm_firstkey (gdbmfile);
335 if (firstkey_data.dptr == NULL) return "";
336
337 // convert it to text_t
338 text_t firstkey;
339 firstkey.setcarr (firstkey_data.dptr, firstkey_data.dsize);
340 free (firstkey_data.dptr);
341 return to_uni(firstkey); // convert to unicode
342}
343
344text_t gdbmclass::getnextkey (const text_t &key) {
345 if (gdbmfile == NULL || key.empty()) return "";
346
347 // get a utf-8 encoded c string of the unicode key
348 datum key_data;
349 key_data.dptr = (to_utf8(key)).getcstr();
350 if (key_data.dptr == NULL) return "";
351 key_data.dsize = strlen (key_data.dptr);
352
353 // get the next key
354 datum nextkey_data = gdbm_nextkey (gdbmfile, key_data);
355 if (nextkey_data.dptr == NULL) {
356 delete key_data.dptr;
357 return "";
358 }
359
360 // convert it to text_t
361 text_t nextkey;
362 nextkey.setcarr (nextkey_data.dptr, nextkey_data.dsize);
363 free (nextkey_data.dptr);
364 delete key_data.dptr;
365 return to_uni(nextkey); // convert to unicode
366}
367
368
369// replaces the .fc, .lc, .pr, .ns and .ps syntax (first child,
370// last child, parent, next sibling, previous sibling)
371// it expects child, parent, etc. to exist if syntax has been used
372// so you should test before using
373text_t gdbmclass::translate_OID (const text_t &inOID, infodbclass &info) {
374
375 if (inOID.size() < 4) return inOID;
376 if (findchar (inOID.begin(), inOID.end(), '.') == inOID.end()) return inOID;
377
378 text_t OID = inOID;
379 text_tarray tailarray;
380 text_t tail = substr (OID.end()-3, OID.end());
381 while (tail == ".fc" || tail == ".lc" || tail == ".pr" ||
382 tail == ".ns" || tail == ".ps") {
383 tailarray.push_back(tail);
384 OID.erase (OID.end()-3, OID.end());
385 tail = substr (OID.end()-3, OID.end());
386 }
387
388 if (!tailarray.size()) return inOID;
389 text_tarray::const_iterator begin = tailarray.begin();
390 text_tarray::const_iterator here = tailarray.end() - 1;
391
392 while (here >= begin) {
393
394 if (*here == ".fc")
395 get_first_child (OID, info);
396 else if (*here == ".lc")
397 get_last_child (OID, info);
398 else if (*here == ".pr")
399 OID = get_parent (OID);
400 else if (*here == ".ns")
401 get_next_sibling (OID, info);
402 else if (*here == ".ps")
403 get_previous_sibling (OID, info);
404
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 (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 (text_t key) {
509 text_t data;
510 return getkeydata (key, data);
511}
512
513// returns true on success
514bool gdbmclass::getkeydata (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}
585
586
Note: See TracBrowser for help on using the repository browser.