source: trunk/gsdl/src/colservr/mgq.c@ 324

Last change on this file since 324 was 324, checked in by rjmcnab, 25 years ago

Added a function to get the equivalent terms of a query term. I also
fixed a small bug that was causing massive slowdown :-

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 18.8 KB
Line 
1/**********************************************************************
2 *
3 * mgq.c -- cut-down version of mgquery
4 * Copyright (C) 1999 The New Zealand Digital Library Project
5 *
6 * PUT COPYRIGHT NOTICE HERE
7 *
8 * $Id: mgq.c 324 1999-07-01 03:52:05Z rjmcnab $
9 *
10 *********************************************************************/
11
12/*
13 $Log$
14 Revision 1.6 1999/07/01 03:52:05 rjmcnab
15 Added a function to get the equivalent terms of a query term. I also
16 fixed a small bug that was causing massive slowdown :-^
17
18 Revision 1.5 1999/06/30 04:04:11 rjmcnab
19 made stemming functions available from mgsearch and made the stems
20 for the query terms available in queryinfo
21
22 Revision 1.4 1999/06/28 08:56:29 rjmcnab
23 A bit of hacking to remove the restriction that the index to get
24 a document must be a level 2 index. Now both level 2 and level 3
25 indexes can be used to get the text of a document.
26
27 Revision 1.3 1999/01/19 01:38:16 rjmcnab
28
29 Made the source more portable.
30
31 Revision 1.2 1999/01/12 01:51:02 rjmcnab
32
33 Standard header.
34
35 Revision 1.1 1999/01/08 09:02:22 rjmcnab
36
37 Moved from src/library.
38
39 */
40
41
42#include "mgq.h"
43
44
45#include <stdio.h>
46#include <string.h>
47/* #include <io.h> */
48#include <fcntl.h>
49
50#ifdef __cplusplus
51extern "C" {
52#endif
53
54#include "sysfuncs.h"
55
56#include "messages.h"
57#include "memlib.h"
58
59#include "invf.h"
60#include "text.h"
61#include "lists.h"
62#include "backend.h"
63#include "environment.h"
64#include "globals.h"
65#include "mg_errors.h"
66#include "commands.h"
67#include "text_get.h"
68#include "term_lists.h"
69#include "local_strings.h"
70
71#include "words.h"
72#include "stemmer.h"
73#include "stem_search.h"
74
75#ifdef __cplusplus
76}
77#endif
78
79
80#include "mgq.h"
81
82/* get a reasonable database cache size */
83#ifndef MAXNUMDATABASEINFO
84# ifdef GSDLSERVER
85# define MAXNUMDATABASEINFO 10
86# else
87# define MAXNUMDATABASEINFO 2
88# endif
89#endif
90
91#define MAXCOLLECTIONLEN 16
92#define MAXMGDIRLEN 256
93#define MAXGENSUFFIXLEN 256
94#define MAXTEXTSUFFIXLEN 256
95
96typedef struct DatabaseInfo {
97 int accessnum; /* -1 = invalid record */
98 char collection[MAXCOLLECTIONLEN];
99 char mgdir[MAXMGDIRLEN];
100 char gensuffix[MAXGENSUFFIXLEN];
101 char textsuffix[MAXTEXTSUFFIXLEN];
102 query_data *qd;
103} DatabaseInfo;
104
105
106/* globals needed by some vague part of mg... */
107FILE *OutFile = NULL, *InFile = NULL;
108int OutPipe = 0, InPipe = 0;
109int Quitting = 0;
110
111/* globals needed to handle loading of databases */
112static int cur_cachenum = -1;
113
114/* globals needed by the database cache */
115static DatabaseInfo dbcache[MAXNUMDATABASEINFO];
116static int cache_nextaccessnum = 0;
117static int cache_numloaded = 0;
118
119
120
121#if defined(PARADOCNUM) || defined(NZDL)
122static int GetDocNumFromParaNum(query_data *qd, int paranum) {
123 int Documents = qd->td->cth.num_of_docs;
124 int *Paragraph = qd->paragraph;
125 int low = 1, high = Documents;
126 int mid = (low+high)/2;
127
128 while ((mid = (low+high)/2) >=1 && mid <= Documents)
129 {
130 if (paranum > Paragraph[mid])
131 low = mid+1;
132 else if (paranum <= Paragraph[mid-1])
133 high = mid-1;
134 else
135 return mid;
136 }
137 FatalError(1, "Bad paragraph number.\n");
138 return 0;
139}
140
141static int GetParaNumFromDocNum(query_data *qd, int docnum) {
142 int Documents = qd->td->cth.num_of_docs;
143 int *Paragraph = qd->paragraph;
144
145 if (docnum > 0 && docnum <= Documents)
146 return Paragraph[docnum-1]+1;
147 return 0;
148}
149#endif
150
151
152/*****************************************************************************/
153
154static void MGQError(char *emsg)
155{
156 fprintf(stderr,"Fatal error: %s\n", emsg);
157 exit(1);
158}
159
160static int ProcessDocs (query_data * qd, int skip, int howmany,
161 enum result_kinds kind,
162 int (*sender)(char *,int,int,float,void *), void *ptr) {
163 int max_buf = 0, output_failure = 0;
164 int DocCount = 0;
165 int need_text = (kind == result_docs);
166
167 /* skip the requested number of documents */
168 while (skip > 0) {
169 if (!NextDoc(qd)) return 0;
170 skip--;
171 }
172
173 /* find out the maximum size for the text buffer */
174 if (need_text) max_buf = atoi (GetDefEnv ("buffer", "1048576"));
175
176 /* process each document */
177 do {
178 u_char *UDoc = NULL;
179 unsigned long ULen=0;
180
181#if defined(PARADOCNUM) || defined(NZDL)
182 /* adjust the document number for paragraph level result_docs */
183 /* this is a bit of a hack ... */
184 if (kind==result_docs && qd->id->ifh.InvfLevel == 3 &&
185 qd->DL != NULL && (int)qd->doc_pos < (int)qd->DL->num)
186 qd->DL->DE[qd->doc_pos].DocNum = GetParaNumFromDocNum(qd, qd->DL->DE[qd->doc_pos].DocNum);
187#endif
188
189 if (need_text) {
190 /* load the compressed text */
191 if (LoadCompressedText (qd, max_buf))
192 MGQError("Unable to load compressed text(memory?).");
193
194 /* uncompress the loaded text */
195 UDoc = GetDocText (qd, &ULen);
196 if (UDoc == NULL) MGQError("UDoc is unexpectedly NULL");
197 }
198
199 if (UDoc != NULL || kind == result_docnums) {
200 int docnum = GetDocNum(qd);
201#if defined(PARADOCNUM) || defined(NZDL)
202 if (qd->id->ifh.InvfLevel == 3) docnum = GetDocNumFromParaNum(qd, docnum);
203#endif
204 switch (kind) {
205 case result_docnums:
206 if (sender != NULL)
207 output_failure = (*sender)("",0,docnum,GetDocWeight(qd),ptr);
208 break;
209 case result_docs:
210 if (sender != NULL)
211 output_failure = (*sender)((char *)UDoc,ULen,docnum,GetDocWeight(qd),ptr);
212 break;
213 default:
214 break;
215 }
216 }
217 DocCount++;
218
219 } while (NextDoc (qd) && output_failure == 0 && --howmany > 0);
220
221 /* if (need_text) FreeTextBuffer (qd);*/
222
223 return (DocCount);
224}
225
226
227static void send_query_term_freqs(QueryTermList *qtl,
228 int (*sender)(char *,int,int,float,void *), void *ptr)
229{
230 int i = 0;
231 for (i = 0; i < qtl->num; i++)
232 if (sender != NULL) {
233 /* word = word2str(qtl->QTE[i].Term);
234 (* sender)(word, strlen(word), qtl->QTE[i].Count, (float)0.0, ptr); */
235 (* sender)((char *)(qtl->QTE[i].Term+1), qtl->QTE[i].Term[0],
236 qtl->QTE[i].Count, (float)0.0, ptr);
237 }
238}
239
240
241static void send_terms (TermList * qtl,
242 int (*sender)(char *,int,int,float,void *), void *ptr)
243{
244 int i = 0;
245 if (sender == NULL) return;
246 for (i = 0; i < qtl->num; i++)
247 {
248 /* word = word2str(qtl->TE[i].Word);
249 (* sender)(word, strlen(word), qtl->TE[i].Count, (float)0.0, ptr);*/
250 (* sender)((char *)(qtl->TE[i].Word+1), qtl->TE[i].Word[0],
251 qtl->TE[i].Count, (float)0.0, ptr);
252 }
253}
254
255
256/* MoreDocs () */
257/* Displays all documents in list DocList. */
258/* Documents are fetched, then decompressed and displayed according to the */
259/* format implied in FormString(). */
260
261static void
262MoreDocs (query_data * qd, enum result_kinds kind,
263 int skip, int howmany,
264 int (*sender)(char *,int,int,float,void *), void *ptr)
265{
266 qd->num_of_ans = qd->DL->num;
267 switch (kind) {
268 case result_docs:
269 case result_docnums:
270 if (qd->num_of_ans > 0)
271 ProcessDocs (qd, skip, howmany, kind, sender, ptr);
272 break;
273 case result_termfreqs:
274 send_query_term_freqs(qd->QTL, sender, ptr);
275 break;
276 case result_terms:
277 send_terms(qd->TL, sender, ptr);
278 break;
279 }
280}
281
282
283
284
285
286
287/******************************************
288 * functions to handle the database cache *
289 ******************************************/
290
291/* init_dbcache should be called at the start of each */
292/* function which deals with the database cache */
293static void init_dbcache (void) {
294 static int dbcacheinited = 0;
295 int i = 0;
296
297 if (dbcacheinited) return;
298
299 cache_numloaded = 0;
300
301 for (i=0; i<MAXNUMDATABASEINFO; i++) {
302 dbcache[i].accessnum = -1;
303 dbcache[i].collection[0] = '\0';
304 dbcache[i].mgdir[0] = '\0';
305 dbcache[i].gensuffix[0] = '\0';
306 dbcache[i].textsuffix[0] = '\0';
307 dbcache[i].qd = NULL;
308 }
309
310 dbcacheinited = 1;
311}
312
313/* returns the next cache access number and increments it */
314static int get_next_accessnum (void) {
315 return cache_nextaccessnum++;
316}
317
318/* get_free_dbcache returns the cache number which */
319/* was used the longest time ago */
320/* init_dbcache should be called before this function */
321static int get_free_dbcache (void) {
322 int i = 0;
323 int minaccessnum = cache_nextaccessnum; /* the current max */
324 int minpos = 0;
325
326 for (i=0; i<MAXNUMDATABASEINFO; i++) {
327 if (dbcache[i].accessnum < minaccessnum) {
328 minaccessnum = dbcache[i].accessnum;
329 minpos = i;
330 }
331 }
332
333 return minpos;
334}
335
336/* search_collect will search for an index which */
337/* belongs to a certain collection It returns -1 if none could be found. */
338/* init_dbcache should be called before this function */
339static int search_collect (char *collection) {
340 int i = 0;
341
342 for (i=0; i<MAXNUMDATABASEINFO; i++) {
343 if ((dbcache[i].accessnum >= 0) &&
344 (dbcache[i].qd != NULL) &&
345 (strcmp (collection, dbcache[i].collection) == 0)
346 /* && (dbcache[i].qd->id->ifh.InvfLevel == 2)*/
347 ) {
348 dbcache[i].accessnum = get_next_accessnum ();
349 return i;
350 }
351 }
352
353 return -1;
354}
355
356/* search_gensuffix will search for an index which */
357/* has a certain gensuffix. It returns -1 if none could be found. */
358/* init_dbcache should be called before this function */
359static int search_gensuffix (char *gensuffix) {
360 int i = 0;
361
362 for (i=0; i<MAXNUMDATABASEINFO; i++) {
363 if ((dbcache[i].accessnum >= 0) &&
364 (dbcache[i].qd != NULL) &&
365 (strcmp (gensuffix, dbcache[i].gensuffix) == 0)) {
366 dbcache[i].accessnum = get_next_accessnum ();
367 return i;
368 }
369 }
370
371 return -1;
372}
373
374/* unload_database will unload a certain entry within */
375/* the database cache, clearing it for furture use. */
376static void unload_database (int i) {
377 /* check to see if it contains anything */
378 if (dbcache[i].accessnum < 0 || dbcache[i].qd == NULL)
379 return;
380
381 /* unload all the query information */
382 FinishQuerySystem(dbcache[i].qd);
383
384 /* reset all the db info */
385 dbcache[i].accessnum = -1;
386 dbcache[i].collection[0] = '\0';
387 dbcache[i].mgdir[0] = '\0';
388 dbcache[i].gensuffix[0] = '\0';
389 dbcache[i].textsuffix[0] = '\0';
390 dbcache[i].qd = NULL;
391
392 cache_numloaded--;
393 if (cache_numloaded < 0) cache_numloaded = 0;
394}
395
396/* cache_database will store the information about */
397/* a database in the database cache. */
398static void cache_database (int i, char *collection, char *mgdir, char *gensuffix,
399 char *textsuffix, query_data *qd) {
400 /* make sure this entry has been unloaded first */
401 if (dbcache[i].accessnum >= 0 && dbcache[i].qd != NULL)
402 unload_database (i);
403
404 /* store the db info */
405 dbcache[i].accessnum = get_next_accessnum ();
406 strcpy (dbcache[i].collection, collection);
407 strcpy (dbcache[i].mgdir, mgdir);
408 strcpy (dbcache[i].gensuffix, gensuffix);
409 strcpy (dbcache[i].textsuffix, textsuffix);
410 dbcache[i].qd = qd;
411
412 cache_numloaded++;
413}
414
415static void make_current (int i) {
416 /* see if it is the current index */
417 if (i == cur_cachenum) return;
418
419 /* unload the old index */
420 if (cur_cachenum >= 0) UninitEnv ();
421 cur_cachenum = -1;
422
423 /* make sure the new one is ok */
424 if (i < 0 || dbcache[i].accessnum < 0 || dbcache[i].qd == NULL)
425 return;
426
427 /* load the new one */
428
429 /* Initialise the environment with default values */
430 InitEnv ();
431
432 SetEnv("mgdir",dbcache[i].mgdir,NULL);
433 SetEnv("mgname",dbcache[i].gensuffix,NULL);
434 SetEnv("textname",dbcache[i].textsuffix,NULL);
435
436 PushEnv ();
437
438 cur_cachenum = i;
439}
440
441
442
443/********************
444 * public functions *
445 ********************/
446
447int mgq_ask(char *line)
448{
449 query_data *qd = (query_data *)NULL;
450 char QueryType = QUERY_BOOLEAN;
451 char OutputType = QUERY_DOCNUMS;
452 char *LinePtr = (char *)NULL;
453
454 if (cur_cachenum == -1) return 0;
455 qd = dbcache[cur_cachenum].qd;
456 if (qd == NULL) return 0;
457
458 ResetFileStats (qd);
459 qd->max_mem_in_use = qd->mem_in_use = 0;
460 qd->tot_hops_taken += qd->hops_taken;
461 qd->tot_num_of_ptrs += qd->num_of_ptrs;
462 qd->tot_num_of_accum += qd->num_of_accum;
463 qd->tot_num_of_terms += qd->num_of_terms;
464 qd->tot_num_of_ans += qd->num_of_ans;
465 qd->tot_text_idx_lookups += qd->text_idx_lookups;
466 qd->hops_taken = qd->num_of_ptrs = 0;
467 qd->num_of_accum = qd->num_of_ans = qd->num_of_terms = 0;
468 qd->text_idx_lookups = 0;
469
470 LinePtr = ProcessCommands (line, qd);
471 if (CommandsErrorStr) {
472 fprintf (stderr, "%s\n", CommandsErrorStr);
473 return 0;
474 }
475 if (*LinePtr == '\0') return 1;
476
477 FreeQueryDocs (qd);
478
479 QueryType = get_query_type ();
480 OutputType = get_output_type ();
481 /* No point in hiliting words on a docnum query */
482 if (OutputType == OUTPUT_HILITE && QueryType == QUERY_DOCNUMS)
483 OutputType = OUTPUT_TEXT;
484
485 switch (QueryType)
486 {
487 case QUERY_BOOLEAN:
488 {
489 char *maxdocs = (char *)NULL;
490 BooleanQueryInfo bqi;
491 maxdocs = GetDefEnv ("maxdocs", "all");
492 bqi.MaxDocsToRetrieve = strcmp (maxdocs, "all") ? atoi (maxdocs) : -1;
493 if (qd->sd->sdh.indexed)
494 BooleanQuery (qd, line, &bqi, (BooleanEnv (GetEnv ("casefold"), 0) |
495 (BooleanEnv (GetEnv ("stem"), 0) << 1)));
496 else
497 BooleanQuery (qd, line, &bqi, qd->sd->sdh.stem_method);
498 /* if (qd->sd->sdh.indexed) BooleanQuery (qd, line, &bqi, 3);
499 else BooleanQuery (qd, line, &bqi, qd->sd->sdh.stem_method); */
500 break;
501 }
502 case QUERY_APPROX:
503 case QUERY_RANKED:
504 {
505 char *maxdocs = (char *)NULL;
506 char *maxterms = (char *)NULL;
507 char *maxaccum = (char *)NULL;
508 RankedQueryInfo rqi;
509 maxdocs = GetDefEnv ("maxdocs", "all");
510 maxterms = GetDefEnv ("max_terms", "all");
511 maxaccum = GetDefEnv ("max_accumulators", "all");
512 rqi.Sort = BooleanEnv (GetEnv ("sorted_terms"), 0);
513 rqi.QueryFreqs = BooleanEnv (GetEnv ("qfreq"), 1);
514 rqi.Exact = QueryType == QUERY_RANKED;
515 rqi.MaxDocsToRetrieve = strcmp (maxdocs, "all") ? atoi (maxdocs) : -1;
516 rqi.MaxTerms = strcmp (maxterms, "all") ? atoi (maxterms) : -1;
517 rqi.MaxParasToRetrieve = rqi.MaxDocsToRetrieve;
518 if (qd->id->ifh.InvfLevel == 3 && GetEnv ("maxparas"))
519 rqi.MaxParasToRetrieve = atoi (GetEnv ("maxparas"));
520 rqi.AccumMethod = toupper (*GetDefEnv ("accumulator_method", "A"));
521 rqi.MaxAccums = strcmp (maxaccum, "all") ? atoi (maxaccum) : -1;
522 rqi.HashTblSize = IntEnv (GetEnv ("hash_tbl_size"), 1000);
523 rqi.StopAtMaxAccum = BooleanEnv (GetEnv ("stop_at_max_accum"), 0);
524 rqi.skip_dump = GetEnv ("skip_dump");
525 RankedQuery (qd, line, &rqi);
526 break;
527 }
528 case QUERY_DOCNUMS:
529 {
530 DocnumsQuery (qd, line);
531 break;
532 }
533 }
534
535 return 1;
536}
537
538int mgq_numdocs(void)
539{
540 query_data *qd = NULL;
541 if (cur_cachenum == -1) return 0;
542 qd = dbcache[cur_cachenum].qd;
543 if (qd == NULL) return 0;
544
545 if (qd->DL) return qd->DL->num;
546 else return 0;
547}
548
549int mgq_numterms(void)
550{
551 query_data *qd = NULL;
552 if (cur_cachenum == -1) return 0;
553 qd = dbcache[cur_cachenum].qd;
554 if (qd == NULL) return 0;
555
556 if (qd->QTL) return qd->QTL->num;
557 else return 0;
558}
559
560int mgq_results(enum result_kinds kind,int skip,int howmany, int (*sender)(char *, int, int, float, void *), void *ptr)
561{
562 query_data *qd = NULL;
563 if (cur_cachenum == -1) return 0;
564 qd = dbcache[cur_cachenum].qd;
565 if (qd == NULL) return 0;
566
567 if (qd->DL) {
568 qd->doc_pos = 0;
569 MoreDocs(qd, kind, skip, howmany, sender, ptr);
570 }
571 return 0;
572}
573
574/* get all the terms that match wordstem using the current stemmer and */
575/* stemming method. The callback is the same style used by mgq_results */
576int mgq_equivterms (unsigned char *wordstem, int (*sender)(char *, int, int, float, void *),
577 void *ptr) {
578 int stem_method = 0;
579 query_data *qd = NULL;
580 TermList *equivterms = NULL; /* used for equivalent terms */
581
582 if (cur_cachenum == -1) return 0;
583 qd = dbcache[cur_cachenum].qd;
584 if (qd == NULL || wordstem == NULL || sender == NULL) return 0;
585
586 if (qd->sd->sdh.indexed) {
587 stem_method = BooleanEnv(GetEnv("casefold"),0) | (BooleanEnv(GetEnv("stem"),0) << 1);
588 } else {
589 stem_method = qd->sd->sdh.stem_method;
590 }
591
592 /* make the term list */
593 equivterms = MakeTermList (0);
594
595 /* expand out this word */
596 if (FindWords (qd->sd, wordstem, stem_method, &equivterms) > 0) {
597 int i;
598 for (i=0; i < equivterms->num; i++) {
599 (* sender)((char *)(equivterms->TE[i].Word+1), equivterms->TE[i].Word[0],
600 equivterms->TE[i].Count, (float)0.0, ptr);
601 }
602 }
603
604 /* free the term list */
605 if (equivterms != NULL) FreeTermList (&equivterms);
606
607 return 0;
608}
609
610/* use mgq_getmaxstemlen to determine the length of the word stems to pass */
611/* to mgq_stemword */
612int mgq_getmaxstemlen () {
613 return MAXSTEMLEN;
614}
615
616/* note: the stemming method and the stemmer come from the last query */
617/* "word" should be at least maxstemlen+1 long and it is a string that */
618/* starts with the string length */
619void mgq_stemword (unsigned char *word) {
620 int stem_method = 0;
621 query_data *qd = NULL;
622
623 if (cur_cachenum == -1) return;
624 qd = dbcache[cur_cachenum].qd;
625 if (qd == NULL || word == NULL) return;
626
627 if (qd->sd->sdh.indexed) {
628 stem_method = BooleanEnv(GetEnv("casefold"),0) | (BooleanEnv(GetEnv("stem"),0) << 1);
629 } else {
630 stem_method = qd->sd->sdh.stem_method;
631 }
632
633 stemmer (stem_method, qd->sd->sdh.stemmer_num, word);
634}
635
636
637
638int is_dbcache_full (void) {
639 init_dbcache ();
640 if (cache_numloaded >= MAXNUMDATABASEINFO) return 1;
641 return 0;
642}
643
644int load_database (char *collection, char *mgdir,
645 char *gensuffix, char *textsuffix) {
646 int i = 0;
647 query_data *qd = NULL;
648 /* FILE *deb = NULL; */
649 init_dbcache ();
650
651 /* print out some debug information */
652/* deb = fopen ("/home/rjmcnab/gsdl/etc/deb.txt", "a");
653 fprintf (deb, "\ncache_nextaccessnum: %i\n", cache_nextaccessnum);
654 fprintf (deb, "cache_numloaded: %i\n", cache_numloaded);
655 fprintf (deb, "cur_cachenum: %i\n", cur_cachenum);
656 fprintf (deb, "MAXNUMDATABASEINFO: %i\n\n", MAXNUMDATABASEINFO);
657 for (i=0; i<MAXNUMDATABASEINFO; i++) {
658 fprintf (deb, "Entry %i\n", i);
659 fprintf (deb, " accessnum: %i\n", dbcache[i].accessnum);
660 fprintf (deb, " collection: %s\n", dbcache[i].collection);
661 fprintf (deb, " mgdir: %s\n", dbcache[i].mgdir);
662 fprintf (deb, " gensuffix: %s\n", dbcache[i].gensuffix);
663 fprintf (deb, " textsuffix: %s\n", dbcache[i].textsuffix);
664 fprintf (deb, " qd: %x\n", (int)(dbcache[i].qd));
665 }
666 fclose (deb); */
667
668 /* search for the index */
669 i = search_gensuffix (gensuffix);
670 if (i >= 0) {
671 make_current (i);
672 return 1;
673 }
674
675 /* if there was a current database then the */
676 /* environment needs uninitialising */
677 make_current (-1);
678
679 /* get a free cache number */
680 i = get_free_dbcache ();
681 unload_database (i);
682
683 /* load the index */
684 qd = InitQuerySystem (mgdir, gensuffix, textsuffix, NULL);
685 if (qd == NULL) return 0;
686
687 /* cache this index */
688 cache_database (i, collection, mgdir, gensuffix, textsuffix, qd);
689
690 /* make this index current */
691 make_current (i);
692
693 return 1;
694}
695
696/* load_text_database tries to make an index of the */
697/* specified collection current */
698int load_text_database (char *collection) {
699 int i = 0;
700 init_dbcache ();
701
702 /* search for the index */
703 i = search_collect (collection);
704
705 /* return if none were found */
706 if (i < 0) return 0;
707
708 /* make this index current */
709 make_current (i);
710 return 1;
711}
712
713void close_all_databases (void) {
714 int i = 0;
715 init_dbcache ();
716
717 /* unload all active databases */
718 for (i=0; i<MAXNUMDATABASEINFO; i++) {
719 unload_database (i);
720 }
721
722 /* if there was a current database then the */
723 /* environment needs uninitialising */
724 make_current (-1);
725}
726
727
728
Note: See TracBrowser for help on using the repository browser.