Add GNU LGPL headers to all .c .C and .h files
[oweals/cde.git] / cde / programs / dtinfo / dtinfo / src / UAS / DtSR / DtSR_SearchEngine.C
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 // $TOG: DtSR_SearchEngine.C /main/19 1998/04/17 11:41:48 mgreess $
24 /*      Copyright (c) 1995,1996 FUJITSU LIMITED         */
25 /*      All Rights Reserved                             */
26
27 #include <assert.h>
28 #include <limits.h>
29 #include <string.h>
30 #include <sys/stat.h>
31
32 #include <fstream.h>
33
34 #include "Registration.hh"
35 #include "Managers/CatMgr.hh"
36
37 #include "Util_Classes/Dict.hh"
38 #include "Util_Classes/DictIter.hh"
39
40 #include "UAS_Factory.hh"
41 #include "UAS_Exceptions.hh"
42 #include "UAS_Collection.hh"
43 #include "UAS_ObjList.hh"
44 #include "DtSR_SearchEngine.hh"
45 #include "DtSR_BookcaseEntry.hh"
46 #include "DtSR_SearchResultsEntry.hh"
47
48 enum fine_scopes { scope_section, scope_book };
49
50 #ifndef True
51 #define True    1
52 #endif
53 #ifndef False
54 #define False   0
55 #endif
56
57 DtSR_SearchEngine *DtSR_SearchEngine::f_search_engine = NULL;
58
59 int DtSR_SearchEngine::f_init_count = 0;
60
61 // interface to DtSearchEngine constructor
62 DtSR_SearchEngine &
63 DtSR_SearchEngine::search_engine(UAS_PtrList<const char> *bcases)
64 {
65     if (f_search_engine == (DtSR_SearchEngine  *)NULL) {
66         // constructor does only what it really needs to do.
67         // let init do the rest of initialization.
68         f_search_engine = new DtSR_SearchEngine();
69     }
70
71     // init was originally introduced to prevent circular instantiation
72     // But now we need init anyway to implement re-initialization
73     if (bcases != NULL && bcases->numItems() > 0)
74         f_search_engine->init(bcases);
75
76     return *f_search_engine;
77 }
78
79 #ifndef KOSHER
80
81 // build UAS_Common object give a mmdb section id
82 static UAS_Pointer<UAS_Common>
83 uas_common(const UAS_String id)
84 {
85     UAS_String url = "mmdb:LOCATOR=";
86
87     url = url + id; 
88
89     UAS_Pointer<UAS_Common> doc = UAS_Common::create(url);
90
91     return doc;
92 }
93
94 // build url given mmdb section id
95 static UAS_String
96 build_url(const UAS_String id)
97 {
98     UAS_Pointer<UAS_Common> doc = uas_common(id);
99     UAS_String url(doc->locator());
100 #ifdef DEBUG
101     fprintf(stderr, "(DEBUG) URL=\"%s\"\n", (char*)url);
102 #endif
103
104     return url;
105 }
106
107 static UAS_String
108 build_bc_url(UAS_String bookcase_path)
109 {
110     const char* str = (char*)bookcase_path;
111     if (str == NULL || *str == '\0')
112         return UAS_String("");
113
114     char* bc_path = strdup((char*)bookcase_path);
115     char* sep = bc_path + strlen(bc_path) - 1;
116     for (; sep > bc_path && *sep != '/'; --sep);
117     
118     if (sep == bc_path) {
119         free(bc_path);
120         return UAS_String("");
121     }
122
123     *sep++ = '\0';
124     UAS_String bc_locator = "mmdb:INFOLIB=";
125     bc_locator = bc_locator + bc_path + "&BOOKCASE=" + sep;
126 #ifdef DEBUG
127     fprintf(stderr, "(DEBUG) URL=\"%s\"\n", (char*)bc_locator);
128 #endif
129
130     if (bc_path)
131         free(bc_path);
132
133     return bc_locator;
134 }
135
136 #endif
137
138 static int
139 IsAccessible(const char* path, mode_t type = S_IFREG)
140 {
141     if (path == NULL || *path == '\0')
142         return False;
143
144     struct stat stat_buf;
145
146     if (stat(path, &stat_buf) < 0) {
147 #ifdef DEBUG
148         fprintf(stderr, "(ERROR) cannot stat %s\n", (char*)path);
149 #endif
150         return False;
151     }
152     else if (type == S_IFDIR) {
153         if ((stat_buf.st_mode & S_IFMT & S_IFDIR) == 0) {
154 #ifdef DEBUG
155             fprintf(stderr, "(ERROR) %s not a directory\n",
156                                                         (char*)path);
157 #endif
158             return False;
159         }
160     }
161     else {
162         assert( type == S_IFREG );
163         if ((stat_buf.st_mode & S_IFMT & S_IFREG) == 0) {
164 #ifdef DEBUG
165             fprintf(stderr, "(ERROR) %s not a ordinary file\n",
166                                                         (char*)path);
167 #endif
168             return False;
169         }
170     }
171
172     if (access((char*)path, R_OK) < 0) {
173 #ifdef DEBUG
174         fprintf(stderr, "(ERROR) you do not have right to read %s\n",
175                                                                 (char*)path);
176 #endif
177         return False;
178     }
179     return True;
180 }
181
182 static unsigned int
183 UpdateConfigFile(UAS_PtrList<const char> *bcases, UAS_String ocf_path)
184 {
185     if (bcases == NULL || bcases->numItems() == 0)
186         return 0;
187
188     if ((char*)ocf_path == NULL || *(char*)ocf_path == '\0')
189         return 0;
190
191     ofstream dtiocf((char*)ocf_path,
192                         ios::out | ios::trunc | ios::nocreate);
193     if (! dtiocf) { // could not open ocf_path in specified mode
194 #ifdef DEBUG
195         fprintf(stderr, "(ERROR) could not open %s\n", (char*)ocf_path);
196 #endif
197         return 0;
198     }
199
200     unsigned int bitfield = 0;
201
202     // dbnames_dict is used to ensure uniqueness of dbnames
203     // NOTE: dbnames should really be ensured to be unique
204     //       at infolib build-time.
205     Dict<UAS_String, int> dbnames_dict(NULL, False);
206
207     for (int i = 0; i < bcases->numItems(); i++) {
208         UAS_String bcase_path = (*bcases)[i];
209         UAS_String dtsrpath   = bcase_path + "/dtsearch";
210         UAS_String ocfpath    = dtsrpath + "/dtsearch.ocf";
211
212         char* bcase = (char*)bcase_path + bcase_path.length();
213         for (; *bcase != '/' && bcase > (char*)bcase_path; bcase--);
214         ++bcase;
215
216         // check if austext files are in place
217         if (IsAccessible((char*)bcase_path, S_IFDIR) != True ||
218             IsAccessible((char*)dtsrpath, S_IFDIR) != True ||
219             IsAccessible((char*)ocfpath) != True ||
220             dbnames_dict[bcase] == True) // name has been used already
221         {
222             bitfield &= ~(0x01 << i);
223 #ifdef DEBUG
224             fprintf(stderr, "(ERROR) search files are not installed "
225                                 "correctly in %s\n", (char*)bcase_path);
226 #endif
227             continue;
228         }
229         else {
230             dbnames_dict[bcase] = True;
231             bitfield |= 0x01 << i;
232         }
233
234         // bcase : book case name; e.g. "OLIASDOC"
235         dtiocf << "PATH " << bcase <<  " = " << (char*)dtsrpath << '\n';
236
237         // append the contents of "dtsearch.ocf" to dtinfo's ocf
238         ifstream dtsrocf((char*)ocfpath);
239         char buf[256]; // buffer to get a line from "dtsearch.ocf"
240         while (dtsrocf.get(buf, 256, '\n')) {
241             dtsrocf.ignore(1);
242             dtiocf << buf << '\n';
243         }
244         dtsrocf.close();
245     }
246
247     dtiocf.close();
248
249     return bitfield;
250 }
251
252 DtSR_SearchEngine::~DtSR_SearchEngine()
253 {
254     if (*(char*)f_config_path != '\0') {
255         unlink((char*)f_config_path);
256 #ifdef DEBUG
257         fprintf(stderr, "(DEBUG) %s removed\n", (char*)f_config_path);
258 #endif
259         f_config_path = NULL;
260     }
261     if (f_oql_parser != NULL) {
262         delete f_oql_parser;
263         f_oql_parser = NULL;
264     }
265 }
266
267 // DtSearchEngine constructor
268 DtSR_SearchEngine::DtSR_SearchEngine()
269     : f_dbnames(NULL), f_dbcount(0), f_valid_bc_map(0)
270 {
271     char* ocf_path = tempnam(NULL, ".DtSR_");
272     f_config_path = ocf_path;
273
274     if (ocf_path)
275         free(ocf_path);
276
277     ofstream dtiocf((char*)f_config_path, ios::out | ios::noreplace);
278     if (! dtiocf) // could not open ocf_path in specified mode
279         throw(CASTEXCEPT Exception());
280
281     f_oql_parser = new DtSR_Parser();
282 }
283
284 void
285 DtSR_SearchEngine::init(UAS_PtrList<const char> *bcases)
286 {
287     if (bcases->numItems() == 0 || *(char*)f_config_path == '\0')
288         return;
289
290     if (bcases->numItems() == DtSR_BookcaseSearchEntry::bcases().length()) {
291         // if sets of bookcases are same, you do not have to (re)init
292     }
293
294     if ((f_valid_bc_map = UpdateConfigFile(bcases, f_config_path)) == 0) {
295         // no valid bookcases available
296         return;
297     }
298
299     int status;
300
301     if (f_init_count == 0) {
302 #ifdef DEBUG
303         fprintf(stderr, "(DEBUG) DtSearch is being initialized.\n");
304 #endif
305         status = DtSearchInit("DtSearch", NULL, 0, (char*)f_config_path,
306                                         NULL, &f_dbnames, &f_dbcount);
307     }
308     else {
309 #ifdef DEBUG
310         fprintf(stderr, "(DEBUG) DtSearch is being re-initialized.\n");
311 #endif
312         status = DtSearchReinit(&f_dbnames, &f_dbcount);
313     }
314
315     UAS_String msg;
316     if (status == DtSrOK || status == DtSrREINIT) {
317         if (f_init_count++ == 0) {
318             DtSearchSetMaxResults(INT_MAX);
319             assert( DtSearchGetMaxResults() == INT_MAX );
320         }
321     }
322     else { // DtSearch (re)initialization failed
323
324         if (f_init_count == 0) {
325             msg = "(ERROR) DtSearchInit:\n";
326         }
327         else {
328             msg = "(ERROR) DtSearchReinit:\n";
329         }
330
331         msg = msg + DtSearchGetMessages();
332         DtSearchFreeMessages();
333
334         // zero f_valid_bc_map should represent bad condition
335         f_valid_bc_map = 0;
336     }
337
338     // make DtSR_BookcaseSearchEntry::bcases() empty
339     while (DtSR_BookcaseSearchEntry::bcases().length() > 0)
340         DtSR_BookcaseSearchEntry::bcases().remove_item(0);
341
342     if (f_valid_bc_map == 0) {
343 #if 0
344         throw(CASTUASEXCEPT UAS_Exception(msg));
345 #else
346         return;
347 #endif
348     }
349
350     int i;
351 #ifdef DEBUG
352     fprintf(stderr, "(DEBUG) valid_bc_map = 0x%x\n", f_valid_bc_map);
353     fprintf(stderr, "\tdbcount = %d\n", f_dbcount);
354     for (i = 0; i < f_dbcount; i++)
355         fprintf(stderr, "\tdb[%d]=%s\n", i, f_dbnames[i]);
356 #endif
357
358     int dbn = 0;
359     for (i = 0; i < bcases->numItems(); i++) {
360         if ((f_valid_bc_map & (0x01 << i)) == 0)
361             continue;
362         else {
363             UAS_String bookcase_path((*bcases)[i]);
364             UAS_String bookcase_url = build_bc_url(bookcase_path);
365             if ((char*)bookcase_url == NULL || *(char*)bookcase_url == '\0')
366                 continue;
367
368             UAS_Pointer<UAS_Common> obj;
369             if ((obj = UAS_Factory::create(bookcase_url)) == (int)NULL)
370                 continue;
371             UAS_Pointer<UAS_Common> bookcase;
372             if ((bookcase =
373                         ((UAS_Collection*)(UAS_Common *)obj)->root()) == (int)NULL)
374                 continue;
375
376             new DtSR_BookcaseSearchEntry(dbn++, bookcase, True);
377         }
378     }
379 }
380
381 #ifndef KOSHER
382
383 // parse url (mmdb) out into infolib,infobase,id fields
384 static const char*
385 parse_url(char* locator, const char* &infolib, const char* &infobase,
386                 const char* &id)
387 {
388     if (locator == NULL || *locator == '\0')
389         return NULL;
390
391     char *head, *tail;
392
393     if ((head = strchr(locator, '=')) == NULL)
394         return NULL;
395     if ((tail = strchr(++head, '&')) == NULL)
396         return NULL;
397     assert( head < tail );
398     *tail = '\0';
399     infolib = head;
400
401     head = tail + 1;
402     if ((head = strchr(head, '=')) == NULL)
403         return NULL;
404     if ((tail = strchr(++head, '&')) == NULL)
405         return NULL;
406     assert( head < tail );
407     *tail = '\0';
408     infobase = head;
409
410     head = tail + 1;
411     if ((head = strchr(head, '=')) == NULL)
412         return NULL;
413     if ((tail = strchr(++head, '&')) == NULL)
414         return NULL;
415     assert( head < tail );
416     *tail = '\0';
417     id = head;
418
419     return id;
420 }
421
422 #endif
423
424 typedef int (*Scan_Abstract)(char*, const char*);
425
426 static DtSrResult*
427 apply_scope(DtSrResult* res, fine_scopes scope, const char* id, long &count)
428 {
429     if (res == NULL || id == NULL || *id == '\0') // garbage input
430         return NULL;
431
432     Scan_Abstract scan_abstract = DtSR_SearchResultsEntry::section_in_abstract;
433
434     DtSrResult *rval = NULL, *tail = NULL, *iter;
435
436     for (iter = res, count = 0; iter; iter = iter->link) {
437         if (scan_abstract(iter->abstractp, id)) {
438 #ifdef DEBUG
439             fprintf(stderr, "(DEBUG) match found in scan_abstract\n");
440 #endif
441             DtSrResult *item = (DtSrResult *)malloc(sizeof(DtSrResult));
442             *item = *iter; // copy as it is
443             item->abstractp = (char*)malloc(strlen(iter->abstractp) + 1);
444             strcpy(item->abstractp, iter->abstractp);
445             item->link = NULL;
446             if (rval == NULL)
447                 rval = tail = item;
448             else {
449                 tail->link = item;
450                 tail = item;
451             }
452 #ifdef DEBUG
453             fprintf(stderr, "(DEBUG) abstract=%s\n", item->abstractp);
454 #endif
455
456             count++;
457
458             if (scope == scope_section)
459                 break;
460         }
461     }
462
463     return rval;
464 }
465
466 // resolve bookid (NOTE: serial is 1-based)
467 static UAS_String
468 resolve_bookid(UAS_Pointer<UAS_Common> &bcase, int serial)
469 {
470     UAS_String rval;
471
472     if (bcase == (int)NULL || bcase->type() != UAS_BOOKCASE)
473         return rval;
474
475     if (serial < 1) // apparently wrong serial number
476         return rval;
477
478     UAS_Pointer<UAS_Common> book = bcase->children()[serial - 1];
479 #ifdef DEBUG
480     fprintf(stderr, "(DEBUG) resolved bookid=%s, type=%s\n",
481                         (char*)book->id(), (char*)book->content_type());
482 #endif
483
484     return rval = book->id();
485 }
486
487 UAS_Pointer<UAS_SearchResults>
488 DtSR_SearchEngine::search(UAS_String oql, UAS_SearchScope& scope,
489                                             unsigned int /* maxdocs */)
490 {
491     UAS_Pointer<DtSR_SearchResults> DtSR_result = NULL ;
492     UAS_Pointer<UAS_SearchResults>  UAS_result = NULL;
493
494     if (f_valid_bc_map == 0) // DtSearch initialization failed
495         return UAS_result;
496
497     UAS_PtrList<UAS_BookcaseEntry>& targets = scope.bookcases();
498     if (scope.search_zones().section() == False && targets.numItems() == 0) {
499         // no bookcases to search against
500         UAS_result = new UAS_SearchResults(new UAS_String(oql),
501                                                 new UAS_String(scope.name()));
502 #ifdef DEBUG
503         fprintf(stderr, "(WARNING) no bookcases specified to search against, "
504                         "returning an empty search result...\n");
505 #endif
506         return UAS_result;
507     }
508
509     UAS_Pointer<UAS_BookcaseEntry> current_bc = NULL;
510     if (scope.search_zones().section()) { // search for current section
511
512       if (targets.numItems() == 0) {
513
514         UAS_String url = build_url(scope.search_zones().search_section());
515
516         const char *infolib, *infobase, *section_id;
517         if (parse_url((char*)url, infolib, infobase, section_id) == NULL) {
518 #ifdef DEBUG
519             fprintf(stderr, "(ERROR) could not parse url\n");
520             abort();
521 #endif
522             // NOTE: parse_url is tampering url,
523             //       that's why it's cast to (char*)
524             throw(CASTEXCEPT Exception());
525         }
526
527         UAS_String bc_path = infolib;
528         bc_path = bc_path + "/" + infobase;
529
530         UAS_String bookcase_url = build_bc_url(bc_path);
531         assert( (char*)bookcase_url && *(char*)bookcase_url );
532
533         UAS_Pointer<UAS_Common> obj = UAS_Factory::create(bookcase_url);
534         UAS_Pointer<UAS_Common> bookcase =
535                         ((UAS_Collection*)(UAS_Common *)obj)->root();
536
537         // NOTE: hack! tampering bookcases
538         current_bc = new UAS_BookcaseEntry(bookcase, True);
539         targets.append(current_bc);
540
541       }
542 #ifdef DEBUG
543       else
544         assert( targets.numItems() == 1 );
545 #endif
546
547         scope.search_zones().all(True); // hack! tampering zones
548     }
549
550     UAS_String aus_query;
551     try {
552         aus_query = f_oql_parser->parse((char*)oql);
553     }
554     catch_any() { // OQL parse failed
555         rethrow;
556     }
557     end_try;
558
559     DtSR_BookcaseSearchEntry::search_zones(scope.search_zones());
560
561     // do search for each bookcase
562     UAS_List<DtSR_BookcaseSearchEntry>& bookcases =
563                                 DtSR_BookcaseSearchEntry::bcases();
564     int n;
565     DtSrResult* DtSr_res = NULL;
566     long rescount = 0;
567     // for each bookcase specified in scope
568     for (n = 0; n < targets.numItems(); n++, DtSr_res = NULL, rescount = 0) {
569         int index;
570         // look for the correspondent index
571         for (index = 0; index < f_dbcount; index++) {
572             if (bookcases[index]->bid() == targets[n]->bid() &&
573                 bookcases[index]->lid() == targets[n]->lid())
574                 break;
575         }
576         if (index == f_dbcount) {
577 #ifdef DEBUG
578             fprintf(stderr, "(ERROR) cannot not find bookcase, bid=\"%s\", "
579                                 "just ignore\n", (char*)targets[n]->bid());
580 #endif
581             continue;
582         }
583
584         bookcases[index]->stems()->clear();
585
586 #if 1
587         // switch austext search option based on completion being specified
588         int stype = ((DtSR_Parser*)f_oql_parser)->stemming_suggested() ? 'S' : 'W';
589 #else
590         // switch austext search option with regards to languages
591         int stype = (bookcases[index]->language() == DtSrLaJPN)? 'W' : 'S';
592 #endif
593
594         UAS_String eff_query = aus_query;
595 #ifdef DEBUG
596         fprintf(stderr, "(DEBUG) effective query=\"%s\"\n", (char*)eff_query);
597 #endif
598
599         int status = DtSearchQuery(
600                         (char*)eff_query, f_dbnames[index], stype, NULL, NULL,
601                         &DtSr_res, &rescount,
602                         (char*)(bookcases[index]->stems()->stems()),
603                         &(bookcases[index]->stems()->count())
604                      );
605
606         if (status != DtSrOK && status != DtSrNOTAVAIL) { // error
607
608             if (DtSr_res)
609                 DtSearchFreeResults(&DtSr_res);
610
611             UAS_String msg(CATGETS(Set_DtSR_SearchEngine, 1,
612                                 "DtSearch does not support the query."));
613             DtSearchFreeMessages();
614             throw(CASTUASEXCEPT UAS_Exception(msg));
615
616             continue;
617         }
618
619         if (scope.search_zones().section()) {
620
621             DtSrResult* res = apply_scope(DtSr_res, scope_section,
622                         (char*) scope.search_zones().search_section(),
623                         rescount);
624
625             if (DtSr_res)
626                 DtSearchFreeResults(&DtSr_res);
627
628             DtSr_res = res; // replace the results with an artifact
629         }
630
631         if (DtSr_res == NULL)
632             continue;
633
634         UAS_Pointer<UAS_List<UAS_SearchResultsEntry> > res;
635
636         if ((res = compress_DtSrResult(DtSr_res, rescount)) == (int)NULL)
637             continue;
638
639         // book#s specified, apply book-level scope here
640         if (targets[n]->book_list().numItems() > 0 && res->length() > 0) {
641             Dict<UAS_String, int> bookid_dict(NULL, False);
642             UAS_ObjList<int> &books = targets[n]->book_list();
643             // register bookids in Dict<UAS_String, int>
644             for (int i = 0; i < books.numItems(); i++) {
645                 UAS_Pointer<UAS_Common> bcase(bookcases[index]->bcase());
646                 UAS_String bookid = resolve_bookid(bcase, books[i]);
647                 if ((char*)bookid == (int)NULL || *(char*)bookid == '\0')
648                     continue;
649
650                 bookid_dict[bookid] = True;
651             }
652             for (i = 0; i < res->length(); i++) {
653
654                 // all these temporary variables are needed to get this 
655                 // code to compile on novell
656
657                 UAS_List<UAS_SearchResultsEntry> * temp_lst = res;
658                 
659                 UAS_Pointer<UAS_SearchResultsEntry> tmp_sre = temp_lst->item(i);
660
661                 UAS_String temp_id = tmp_sre->id();
662
663                 UAS_Pointer<UAS_Common> uas_book =
664                                 UAS_Common::create(temp_id);
665
666                 while (uas_book->type() != UAS_BOOK)
667                     uas_book = uas_book->parent();
668
669                 UAS_String uas_book_id = uas_book->id();
670
671                 if (bookid_dict[uas_book_id] == False)
672                     res->set_item(NULL, i);
673             }
674
675             for (i = 0; i < res->length(); i++) {
676                 if (res->item(i) == (int)NULL)
677                     res->remove_item(i--);
678             }
679         }
680
681         // take over stems from DtSR_BookcaseSearchEntry
682         UAS_Pointer<DtSR_Stems> stems = bookcases[index]->takeover_stems();
683
684         UAS_Pointer<UAS_String> q = new UAS_String(oql);
685         UAS_Pointer<UAS_String> n = new UAS_String(scope.name());
686
687         UAS_Pointer<DtSR_SearchResults> DtSR_res =
688                         new DtSR_SearchResults(q, n, res, res->length(),
689                                         stems, scope.search_zones(), stype);
690
691         if (DtSR_result == (int)NULL)
692             DtSR_result = DtSR_res;
693         else  // merge uas_res into result
694             DtSR_result->merge(DtSR_res);
695     }
696
697     if (DtSR_result == (int)NULL)
698         UAS_result = new UAS_SearchResults(new UAS_String(oql),
699                                         new UAS_String(scope.name()));
700
701
702     if (UAS_result == (int)NULL) {
703         assert( DtSR_result != (int)NULL );
704         UAS_result = (UAS_SearchResults*)(DtSR_SearchResults*)DtSR_result;
705     }
706
707     assert( UAS_result != (int)NULL );
708
709     if (current_bc != 0) {
710         targets.remove(current_bc);
711         current_bc = NULL;
712     }
713
714     return UAS_result;
715 }
716
717 UAS_Pointer<UAS_List<UAS_SearchResultsEntry> >
718 DtSR_SearchEngine::compress_DtSrResult(DtSrResult*& res, long& count)
719 {
720     UAS_Pointer<UAS_List<UAS_SearchResultsEntry> >
721         result_list = new UAS_List<UAS_SearchResultsEntry>;
722         
723     // garbage inputs
724     if (res == NULL) {
725         assert( count == 0 );
726         return result_list; // return empty list
727     }
728     else if (! count) { // should never enter here
729         DtSearchFreeResults(&res);
730         assert( res == NULL );
731         return result_list; // return empty list
732     }
733
734     Dict<UAS_String, UAS_Pointer<DtSR_SearchResultsEntry> >
735                                                 map(NULL, NULL);
736
737     DtSrResult* iter = res;
738     for (iter = res; iter; iter = iter->link) {
739         // may we change abstract in DtSrResult before free it?
740         char* abstract = iter->abstractp;
741         UAS_Pointer<UAS_String> id, book, section;
742         if (DtSR_SearchResultsEntry::
743                 parse_abstract(abstract, id, book, section) == (int)NULL) {
744 #ifdef DEBUG
745             fprintf(stderr, "parse_abstract failed\n");
746             abort();
747 #endif
748             continue;
749         }
750         UAS_String Id(*(UAS_String*)id);
751         UAS_Pointer<DtSR_SearchResultsEntry>& sre = map[Id];
752         if (sre == (int)NULL) { // not found in map, create one
753 #ifdef DEBUG
754             cerr << "(DEBUG) " << (char*)Id <<  ' ' <<
755                                 "not found in map" << '\n' << flush;
756 #endif
757             sre = new DtSR_SearchResultsEntry(
758                                 (char*)*(UAS_String*)id,
759                                 (char*)*(UAS_String*)book,
760                                 (char*)*(UAS_String*)section,
761                         iter->dbn, iter->language, NULL);
762         }
763
764         if (sre->set_proximity(DtSR_SearchZones::keytype2zone(*iter->reckey),
765                                                 iter->proximity) >= 0) {
766 #ifdef DEBUG
767             fprintf(stderr, "(DEBUG) keytype=%c, proximity=%d\n", *iter->reckey,
768                 sre->get_proximity(DtSR_SearchZones::
769                                                 keytype2zone(*iter->reckey)));
770 #endif
771         }
772         else if (sre->overlay_proximity
773                         (DtSR_SearchZones::keytype2zone(*iter->reckey),
774                          iter->proximity) >= 0) {
775 #ifdef DEBUG
776             fprintf(stderr, "(DEBUG) keytype=%c, overlayed proximity=%d\n", *iter->reckey,
777                 sre->get_proximity(DtSR_SearchZones::
778                                                 keytype2zone(*iter->reckey)));
779 #endif
780         }
781         else { // fail
782 #ifdef DEBUG
783             fprintf(stderr, "(ERROR) set_proximity failed\n");
784             abort();
785 #endif
786             // NOTE: need to delete sre here
787             continue;
788         }
789     }
790
791     assert( map.size() > 0 );
792
793     if (! map.size())
794         return NULL;
795
796     DictIter<UAS_String, UAS_Pointer<DtSR_SearchResultsEntry> > mapiter;
797     for (mapiter = map.first(); mapiter(); mapiter++) {
798         mapiter.value()->relevance(); // initialize relevance
799         // NASTY code to convert UAS_Pointer<DtSR_SearchResultsEntry>
800         // to UAS_Pointer<UAS_SearchResultsEntry>
801         DtSR_SearchResultsEntry *Cptr_DtSR_sre = mapiter.value();
802         UAS_SearchResultsEntry  *Cptr_UAS_sre  = Cptr_DtSR_sre;
803         UAS_Pointer<UAS_SearchResultsEntry> uas_res(Cptr_UAS_sre);
804         result_list->insert_item(uas_res);
805     }
806
807     DtSR_SearchResults::sort(result_list);
808
809     DtSearchFreeResults(&res);
810     count = 0;
811
812 #ifdef DEBUG
813     fprintf(stderr, "(DEBUG) # of results = %d\n", result_list->length());
814 #endif
815
816     return result_list;
817 }
818
819
820 UAS_Pointer<UAS_String>
821 DtSR_SearchEngine::db_name(int n)
822 {
823     if (n < 0 || n >= f_dbcount)
824         return NULL;
825
826     UAS_Pointer<UAS_String> rval = new UAS_String(f_dbnames[n]);
827
828     return rval;
829 }
830
831
832 char *
833 DtSR_SearchEngine::char_db_name(int n)
834 {
835     if (n < 0 || n >= f_dbcount) {
836         return NULL;
837     }
838     else {
839         return f_dbnames[n];
840     }
841 }
842