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

Last change on this file since 3036 was 2400, checked in by sjboddie, 23 years ago

Fixed a bug in infodbclass that prevented backslashes from being stored
properly in a gdbm database.

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