// Standard headers #if defined(GSDL_USE_OBJECTSPACE) # include # include # include #elif defined(GSDL_USE_IOS_H) # include # include # include #else # include # include # include #endif #include #include #include #include #include #include // Greenstone headers #include "fileutil.h" #include "comtypes.h" #include "nullproto.h" // Z39.50 server headers #include "z3950parser.h" #include "z3950explain.h" #include "z3950server.h" // Dublin Core -> MARC (d2m) headers and source files #include "d2m4gs.h" // these globals are local to each thread, // one Z39.50 connection = one thread. text_t gsdlhome; // this is set by the collectset object from the gsdlsite.cfg file text_t collecthome; // this is set by the collectset object list Response_tlist; // list of response sets list Response_tlist_colnames; // collection names for the above list Response_tlist_sizes; // sizes (number of records) for the above z3950Server *Server = NULL; collectset *Cservers = NULL; nullproto *Protocol = NULL; map Collection_map; // info and tools for collections map Resultsets; // mapping from ResultSetId to GSQuery int z3950_verbosity_ = 3; // *** initialize things here *** bend_initresult *bend_init(bend_initrequest *q) { // *** called when client connects to the server *** if (z3950_verbosity_ > 1) { cerr << "Entering bend_init" << endl; } Protocol = new nullproto(); Cservers = new collectset(gsdlhome, collecthome); Protocol->set_collectset(Cservers); cerr << "Starting Z39.50 Server ..." << endl; Server = new z3950Server(Protocol, gsdlhome); cerr << "Server constructed" << endl; const bool status = Server->initialise(); cerr << "Initialised server: return status = " << status << endl; text_tarray collist; comerror_t err; Protocol->get_collection_list( collist, err, cout ); // display the list of available Greenstone collections text_tarray::iterator cols_here=collist.begin(); text_tarray::iterator cols_end=collist.end(); while (cols_here != cols_end) { gsdlCollection tmpcol(*cols_here, gsdlhome); Collection_map[*cols_here] = tmpcol; if (tmpcol.z3950Capeable()) { cerr << " " << tmpcol.getName() << "\tis Z39.50 capable" << endl; } cols_here++; } bend_initresult *r = (bend_initresult *) odr_malloc (q->stream, sizeof(*r)); //static char *dummy = "Hej fister"; // what's this for? r->errcode = 0; r->errstring = 0; r->handle = Protocol; q->bend_sort = ztest_sort; /* register sort handler */ q->bend_search = ztest_search; /* register search handler */ q->bend_present = ztest_present; /* register present handle */ q->bend_esrequest = ztest_esrequest; q->bend_delete = ztest_delete; q->bend_fetch = ztest_fetch; q->bend_scan = ztest_scan; return r; } // *** perform query, get number of hits *** int ztest_search (void *handle, bend_search_rr *rr) { // *** called when client issues a "find" command if (z3950_verbosity_>1) { cerr << "Entering ztest_search" << endl; } Response_tlist.clear(); Response_tlist_colnames.clear(); Response_tlist_sizes.clear(); comerror_t err; FilterRequest_t request; request.filterName = "QueryFilter"; request.filterResultOptions = FROID | FRtermFreq | FRmetadata; OptionValue_t option; // how many results to get. grouping all results together into // one set instead of breaking up into "pages". // note: see if Z39.50 has a concept of pages. option.name = "StartResults"; option.value = "1"; request.filterOptions.push_back (option); option.name = "EndResults"; option.value = MAXHITS; request.filterOptions.push_back (option); option.name = "Maxdocs"; option.value = MAXHITS; request.filterOptions.push_back (option); text_t GSQuery = ZQueryToGSQuery(rr); option.name = "Term"; option.value = GSQuery; request.filterOptions.push_back (option); Resultsets[Resultsets.size()+1] = GSQuery; option.name = "QueryType"; option.value = "boolean"; request.filterOptions.push_back (option); option.name = "MatchMode"; option.value = "all"; request.filterOptions.push_back (option); option.name = "Casefold"; option.value = "true"; request.filterOptions.push_back (option); option.name = "Stem"; option.value = "false"; request.filterOptions.push_back (option); option.name = "AccentFold"; option.value = "false"; request.filterOptions.push_back (option); // MG queries don't need this, MGPP queries do. // doesn't do any harm to include it anyway. // note: not all collections have document-level granularity... option.name = "Level"; option.value = "Doc"; request.filterOptions.push_back (option); // Z39.50 supports the searching of multiple databases rr->hits = 0; for (int i = 0; i < rr->num_bases; i++) { FilterResponse_t response; cerr << "Searching basename = " << rr->basenames[i] << endl; // intercept Explain requests if (strcmp(rr->basenames[i], "IR-Explain-1")==0) { //ColInfoResponse_t collectinfo; //Protocol->get_collectinfo("csbib", collectinfo, err, cerr); //response.numDocs = 1; //ResultDocInfo_t docinfo; //docinfo.metadata["foo"].values.push_back("bar"); getExplainInfo(rr, response, err); //response.docInfo.push_back(docinfo); } else { cerr << "calling filter..." << endl; Protocol->filter(rr->basenames[i], request, response, err, cerr); cerr << "returned from filter." << endl; if (response.numDocs > MAXHITS) { response.numDocs = MAXHITS; } } rr->hits += response.numDocs; Response_tlist.push_back(response); text_t basename = rr->basenames[i]; Response_tlist_colnames.push_back(basename); Response_tlist_sizes.push_back(response.numDocs); cerr << "search complete." << endl; } if (rr->hits > MAXHITS) rr->hits = MAXHITS; return 0; } // *** extra code for showing results, not needed *** int ztest_present (void *handle, bend_present_rr *rr) { // *** called when client issues a "show" command, 1st of 5 if (z3950_verbosity_>1) { cerr << "entering ztest_present" << endl; } return 0; } int ztest_esrequest (void *handle, bend_esrequest_rr *rr) { if (z3950_verbosity_>1) { cerr << "Entering ztest_esrequest" << endl; } yaz_log(LOG_LOG, "function: %d", *rr->esr->function); if (rr->esr->packageName) yaz_log(LOG_LOG, "packagename: %s", rr->esr->packageName); yaz_log(LOG_LOG, "Waitaction: %d", *rr->esr->waitAction); if (!rr->esr->taskSpecificParameters) { yaz_log (LOG_WARN, "No task specific parameters"); } else if (rr->esr->taskSpecificParameters->which == Z_External_itemOrder) { Z_ItemOrder *it = rr->esr->taskSpecificParameters->u.itemOrder; yaz_log (LOG_LOG, "Received ItemOrder"); switch (it->which) { #ifdef ASN_COMPILED case Z_IOItemOrder_esRequest: #else case Z_ItemOrder_esRequest: #endif { Z_IORequest *ir = it->u.esRequest; Z_IOOriginPartToKeep *k = ir->toKeep; Z_IOOriginPartNotToKeep *n = ir->notToKeep; if (k && k->contact) { if (k->contact->name) yaz_log(LOG_LOG, "contact name %s", k->contact->name); if (k->contact->phone) yaz_log(LOG_LOG, "contact phone %s", k->contact->phone); if (k->contact->email) yaz_log(LOG_LOG, "contact email %s", k->contact->email); } if (k->addlBilling) { yaz_log(LOG_LOG, "Billing info (not shown)"); } if (n->resultSetItem) { yaz_log(LOG_LOG, "resultsetItem"); yaz_log(LOG_LOG, "setId: %s", n->resultSetItem->resultSetId); yaz_log(LOG_LOG, "item: %d", *n->resultSetItem->item); } #ifdef ASN_COMPILED if (n->itemRequest) { Z_External *r = (Z_External*) n->itemRequest; ILL_ItemRequest *item_req = 0; ILL_Request *ill_req = 0; if (r->direct_reference) { oident *ent = oid_getentbyoid(r->direct_reference); if (ent) yaz_log(LOG_LOG, "OID %s", ent->desc); if (ent && ent->value == VAL_ISO_ILL_1) { yaz_log (LOG_LOG, "ItemRequest"); if (r->which == ODR_EXTERNAL_single) { odr_setbuf(rr->decode, (char*)(r->u.single_ASN1_type->buf), r->u.single_ASN1_type->len, 0); if (!ill_ItemRequest (rr->decode, &item_req, 0, 0)) { yaz_log (LOG_LOG, "Couldn't decode ItemRequest %s near %d", odr_errmsg(odr_geterror(rr->decode)), odr_offset(rr->decode)); yaz_log(LOG_LOG, "PDU dump:"); odr_dumpBER(yaz_log_file(), (const char*)(r->u.single_ASN1_type->buf), r->u.single_ASN1_type->len); } if (rr->print) { ill_ItemRequest (rr->print, &item_req, 0, "ItemRequest"); odr_reset (rr->print); } } if (!item_req && r->which == ODR_EXTERNAL_single) { yaz_log (LOG_LOG, "ILLRequest"); odr_setbuf(rr->decode, (char*)(r->u.single_ASN1_type->buf), r->u.single_ASN1_type->len, 0); if (!ill_Request (rr->decode, &ill_req, 0, 0)) { yaz_log (LOG_LOG, "Couldn't decode ILLRequest %s near %d", odr_errmsg(odr_geterror(rr->decode)), odr_offset(rr->decode)); yaz_log(LOG_LOG, "PDU dump:"); odr_dumpBER(yaz_log_file(), (const char*)(r->u.single_ASN1_type->buf), r->u.single_ASN1_type->len); } if (rr->print) { ill_Request (rr->print, &ill_req, 0, "ILLRequest"); odr_reset (rr->print); } } } } if (item_req) { yaz_log (LOG_LOG, "ILL protocol version = %d", *item_req->protocol_version_num); } } #endif } break; } } else if (rr->esr->taskSpecificParameters->which == Z_External_update) { Z_IUUpdate *up = rr->esr->taskSpecificParameters->u.update; yaz_log (LOG_LOG, "Received DB Update"); if (up->which == Z_IUUpdate_esRequest) { Z_IUUpdateEsRequest *esRequest = up->u.esRequest; Z_IUOriginPartToKeep *toKeep = esRequest->toKeep; Z_IUSuppliedRecords *notToKeep = esRequest->notToKeep; yaz_log (LOG_LOG, "action"); if (toKeep->action) { switch (*toKeep->action) { case Z_IUOriginPartToKeep_recordInsert: yaz_log (LOG_LOG, " recordInsert"); break; case Z_IUOriginPartToKeep_recordReplace: yaz_log (LOG_LOG, " recordUpdate"); break; case Z_IUOriginPartToKeep_recordDelete: yaz_log (LOG_LOG, " recordDelete"); break; case Z_IUOriginPartToKeep_elementUpdate: yaz_log (LOG_LOG, " elementUpdate"); break; case Z_IUOriginPartToKeep_specialUpdate: yaz_log (LOG_LOG, " specialUpdate"); break; default: yaz_log (LOG_LOG, " unknown (%d)", *toKeep->action); } } if (toKeep->databaseName) { yaz_log (LOG_LOG, "database: %s", toKeep->databaseName); if (!strcmp(toKeep->databaseName, "fault")) { rr->errcode = 109; rr->errstring = toKeep->databaseName; } if (!strcmp(toKeep->databaseName, "accept")) rr->errcode = -1; } if (notToKeep) { int i; for (i = 0; i < notToKeep->num; i++) { Z_External *rec = notToKeep->elements[i]->record; if (rec->direct_reference) { struct oident *oident; oident = oid_getentbyoid(rec->direct_reference); if (oident) yaz_log (LOG_LOG, "record %d type %s", i, oident->desc); } switch (rec->which) { case Z_External_sutrs: if (rec->u.octet_aligned->len > 170) yaz_log (LOG_LOG, "%d bytes:\n%.168s ...", rec->u.sutrs->len, rec->u.sutrs->buf); else yaz_log (LOG_LOG, "%d bytes:\n%s", rec->u.sutrs->len, rec->u.sutrs->buf); break; case Z_External_octet : if (rec->u.octet_aligned->len > 170) yaz_log (LOG_LOG, "%d bytes:\n%.168s ...", rec->u.octet_aligned->len, rec->u.octet_aligned->buf); else yaz_log (LOG_LOG, "%d bytes\n%s", rec->u.octet_aligned->len, rec->u.octet_aligned->buf); } } } } } else { yaz_log (LOG_WARN, "Unknown Extended Service(%d)", rr->esr->taskSpecificParameters->which); } return 0; } int ztest_delete (void *handle, bend_delete_rr *rr) { // *** called when client issues a "delete" command if (z3950_verbosity_>1) { cerr << "Entering ztest_delete" << endl; } if (rr->num_setnames == 1 && !strcmp (rr->setnames[0], "1")) rr->delete_status = Z_DeleteStatus_success; else rr->delete_status = Z_DeleteStatus_resultSetDidNotExist; return 0; } // *** do we want to sort the results? *** /* Our sort handler really doesn't sort... */ int ztest_sort (void *handle, bend_sort_rr *rr) { if (z3950_verbosity_>1) { cerr << "entering ztest_sort" << endl; } rr->errcode = 0; rr->sort_status = Z_SortStatus_success; return 0; } static int atoin (const char *buf, int n) { // *** called when client issues a "show" command, 5th of 5 // *** NOT called if the "show" command has an invalid argument if (z3950_verbosity_>1) { cerr << "Entering atoin" << endl; } int val = 0; while (--n >= 0) { if (isdigit(*buf)) val = val*10 + (*buf - '0'); buf++; } return val; } bool get_sutrs_record (int recnum, text_t &MetadataResult, text_t &CollectionName) { if (z3950_verbosity_>1) { cerr << "Entering get_sutrs_record" << endl; } int total_records = 0; list::iterator RESPONSE_here = Response_tlist.begin(); list::iterator RESPONSE_end = Response_tlist.end(); list::iterator RESPONSE_COLNAMES_here = Response_tlist_colnames.begin(); list::iterator RESPONSE_COLNAMES_end = Response_tlist_colnames.end(); list::iterator RESPONSE_SIZES_here = Response_tlist_sizes.begin(); list::iterator RESPONSE_SIZES_end = Response_tlist_sizes.end(); RESPONSE_here = Response_tlist.begin(); RESPONSE_end = Response_tlist.end(); while (RESPONSE_here != RESPONSE_end) { total_records += (*RESPONSE_here).numDocs; RESPONSE_here++; } // check that the index is within bounds if (recnum < 1 || recnum > total_records) { cerr << "get_sutrs_record failed" << endl; return false; // failed } else { FilterResponse_t response; // iterate through Response_tlist to find the right response RESPONSE_here = Response_tlist.begin(); RESPONSE_end = Response_tlist.end(); RESPONSE_COLNAMES_here = Response_tlist_colnames.begin(); RESPONSE_COLNAMES_end = Response_tlist_colnames.end(); RESPONSE_SIZES_here = Response_tlist_sizes.begin(); RESPONSE_SIZES_end = Response_tlist_sizes.end(); int n = 0; while (RESPONSE_here != RESPONSE_end) { n += *RESPONSE_SIZES_here; if (n >= recnum) { response = *RESPONSE_here; CollectionName = *RESPONSE_COLNAMES_here; recnum = *RESPONSE_SIZES_here - (n - recnum); break; } // these should all have the same number of elements, // so only need to check that one is within bounds. // may want to combine these into a larger data structure later. RESPONSE_here++; RESPONSE_COLNAMES_here++; RESPONSE_SIZES_here++; } // locate the desired record in the response MetadataInfo_tmap::iterator METAFIELD_here = response.docInfo[recnum-1].metadata.begin(); MetadataInfo_tmap::iterator METAFIELD_end = response.docInfo[recnum-1].metadata.end(); while (METAFIELD_here!=METAFIELD_end) { text_tarray::iterator METAVALUE_here = METAFIELD_here->second.values.begin(); text_tarray::iterator METAVALUE_end = METAFIELD_here->second.values.end(); while (METAVALUE_here!=METAVALUE_end) { // construct a text_t containing the record: // there are multiple metadata fields, // each field can have multiple values. /* // don't include fields starting with a lowercase letter, // these are system fields. // later: include these anyway, since Explain fields start with lowercase letters if (METAFIELD_here->first[0] >= 'A' && METAFIELD_here->first[0] <= 'Z') { */ MetadataResult += METAFIELD_here->first; // field name MetadataResult += " "; MetadataResult += *METAVALUE_here; // field contents MetadataResult += "\n"; //} METAVALUE_here++; } METAFIELD_here++; } return true; // succeeded } } bool get_marc_record (int recnum, list &Metatag_list, text_t &CollectionName) { if (z3950_verbosity_>1) { cerr << "Entering get_marc_record" << endl; } int total_records = 0; list::iterator RESPONSE_here = Response_tlist.begin(); list::iterator RESPONSE_end = Response_tlist.end(); list::iterator RESPONSE_COLNAMES_here = Response_tlist_colnames.begin(); list::iterator RESPONSE_COLNAMES_end = Response_tlist_colnames.end(); list::iterator RESPONSE_SIZES_here = Response_tlist_sizes.begin(); list::iterator RESPONSE_SIZES_end = Response_tlist_sizes.end(); RESPONSE_here = Response_tlist.begin(); RESPONSE_end = Response_tlist.end(); while (RESPONSE_here != RESPONSE_end) { total_records += (*RESPONSE_here).numDocs; RESPONSE_here++; } // check that the index is within bounds if (recnum < 1 || recnum > total_records) { cerr << "get_marc_record failed" << endl; return false; // failed } FilterResponse_t response; // iterate through Response_tlist to find the right response RESPONSE_here = Response_tlist.begin(); RESPONSE_end = Response_tlist.end(); RESPONSE_COLNAMES_here = Response_tlist_colnames.begin(); RESPONSE_COLNAMES_end = Response_tlist_colnames.end(); RESPONSE_SIZES_here = Response_tlist_sizes.begin(); RESPONSE_SIZES_end = Response_tlist_sizes.end(); int n = 0; while (RESPONSE_here != RESPONSE_end) { n += *RESPONSE_SIZES_here; if (n >= recnum) { response = *RESPONSE_here; CollectionName = *RESPONSE_COLNAMES_here; recnum = *RESPONSE_SIZES_here - (n - recnum); break; } // these should all have the same number of elements, // so only need to check that one is within bounds. // may want to combine these into a larger data structure later. RESPONSE_here++; RESPONSE_COLNAMES_here++; RESPONSE_SIZES_here++; } // locate the desired record in the response and gather all the metadata MetadataInfo_tmap::iterator METAFIELD_here = response.docInfo[recnum-1].metadata.begin(); MetadataInfo_tmap::iterator METAFIELD_end = response.docInfo[recnum-1].metadata.end(); while (METAFIELD_here!=METAFIELD_end) { text_t meta_name = METAFIELD_here->first; text_t::const_iterator pos = findchar(meta_name.begin(), meta_name.end(), '.'); // strip off namespace and subfield if (pos != meta_name.end()) { meta_name = substr(pos+1, meta_name.end()); } pos = findchar(meta_name.begin(), meta_name.end(), '^'); if (pos != meta_name.end()) { meta_name = substr(meta_name.begin(), pos); } if (meta_name[0] >= 'A' && meta_name[0] <= 'Z') { text_tarray::iterator METAVALUE_here = METAFIELD_here->second.values.begin(); text_tarray::iterator METAVALUE_end = METAFIELD_here->second.values.end(); while (METAVALUE_here!=METAVALUE_end) { metatag *tmptag = new metatag(); tmptag->name = meta_name.getcstr(); tmptag->type = ""; tmptag->scheme = ""; tmptag->value = (*METAVALUE_here).getcstr(); Metatag_list.push_back(tmptag); METAVALUE_here++; } } METAFIELD_here++; } return true; // succeeded } // *** implement this *** int ztest_fetch(void *handle, bend_fetch_rr *r) { // *** called when client issues a "show" command, 2nd of 5 if (z3950_verbosity_>1) { cerr << "Entering ztest_fetch" << endl; } char *cp; r->errstring = 0; r->last_in_set = 0; r->basename = "DUMMY"; // this is set below r->output_format = r->request_format; // *** use this until found a good way of mapping Greenstone // *** metadata fields to USMARC fields if (r->request_format == VAL_SUTRS) { // copies record into r, errcode will be 13 if fails text_t MetadataResult; text_t CollectionName; if (!get_sutrs_record(r->number, MetadataResult, CollectionName)) { r->errcode = 13; return 0; } else { r->len = MetadataResult.size(); r->record = (char*) odr_malloc(r->stream, r->len+1); char *tmptxt = MetadataResult.getcstr(); strcpy(r->record, tmptxt); delete tmptxt; r->basename = (char*) odr_malloc(r->stream, CollectionName.size()+1); tmptxt = CollectionName.getcstr(); strcpy(r->basename, tmptxt); delete tmptxt; } } // *** do we want to use this format? *** else if (r->request_format == VAL_GRS1) { // bail out, since we don't support grs (yet?) r->errcode = 107; // "Query type not supported" return 0; } // assume that here on in is some sort of MARC format else { marcrec *mrec = new marcrec; mrec = (marcrec*) malloc(sizeof(*mrec)); mrec->marcline = (char*) malloc(100000); mrec->partitle = (char*) malloc(10000); mrec->subtitle = (char*) malloc(10000); mrec->year = (char*) malloc(1000); mrec->url = (char*) malloc(1000); mrec->fmat = (char*) malloc(1000); mrec->s008 = (char*) malloc(41); mrec->ncreators = 0; mrec->ntitles = 0; *mrec->marcline = 0; *mrec->subtitle = 0; *mrec->partitle = 0; *mrec->year = 0; *mrec->url = 0; *mrec->fmat = 0; strcpy(mrec->s008, " "); text_t CollectionName; list Metatag_list; if (!get_marc_record(r->number, Metatag_list, CollectionName)) { r->errcode = 13; return 0; } // work out the d2m format number int record_format; switch(r->request_format) { case VAL_DANMARC: record_format = DANMARC; break; case VAL_FINMARC: record_format = FINMARC; break; case VAL_NORMARC: record_format = NORMARC; break; case VAL_SWEMARC: record_format = SWEMARC; break; case VAL_UNIMARC: record_format = UNIMARC; break; default: // we don;t know any other format, so assume usMARC record_format = USMARC; // set above to be request_format, but this may change it r->output_format = VAL_USMARC; } list::iterator METATAG_here = Metatag_list.begin(); list::iterator METATAG_end = Metatag_list.end(); while (METATAG_here != METATAG_end) { //usMARC(*METATAG_here, mrec); MARCmake(*METATAG_here, mrec, record_format); METATAG_here++; } // set the date in the marc record to the current date char today[32]; time_t d; struct tm *time_struct; //putenv("TZ=NFT-1DFT"); d = time(NULL); time_struct = localtime(&d); strftime(today, 16, "%y%m%d", time_struct); put008(mrec->s008, today, F008_DATE_ENTERED); put008(mrec->s008, "s", F008_TYPE_OF_DATE); // to do: make this safer + more efficient cp = new char[1000000]; char *cp2 = new char[1000000]; MARCtidy(mrec, cp, record_format); sprintf(cp2, "%s", (char*) MARC2709(cp, 0, 6, 'n', 'm', ' ')); r->len = strlen(cp2); r->record = (char*) odr_malloc(r->stream, r->len+1); strcpy(r->record, cp2); r->basename = (char*) odr_malloc(r->stream, CollectionName.size()+1); char *tmptxt; tmptxt = CollectionName.getcstr(); strcpy(r->basename, tmptxt); delete tmptxt; delete cp2; cerr << "dumping record:`" << cp << "'" << endl; delete cp; } r->errcode = 0; return 0; } /* * silly dummy-scan what reads words from a file. */ int ztest_scan(void *handle, bend_scan_rr *q) { // *** called when client issues a "scan" command if (z3950_verbosity_>1) { cerr << "Entering ztest_scan" << endl; } // bail out, since we don't support scan (yet?) q->errcode = 107; // "Query type not supported" return 0; static FILE *f = 0; static struct scan_entry list[200]; static char entries[200][80]; int hits[200]; char term[80], *p; int i, pos; int term_position_req = q->term_position; int num_entries_req = q->num_entries; q->errcode = 0; q->errstring = 0; q->entries = list; q->status = BEND_SCAN_SUCCESS; if (!f && !(f = fopen("dummy-words", "r"))) { perror("dummy-words"); exit(1); } if (q->term->term->which != Z_Term_general) { q->errcode = 229; /* unsupported term type */ return 0; } if (*q->step_size != 0) { q->errcode = 205; /*Only zero step size supported for Scan */ return 0; } if (q->term->term->u.general->len >= 80) { q->errcode = 11; /* term too long */ return 0; } if (q->num_entries > 200) { q->errcode = 31; return 0; } memcpy(term, q->term->term->u.general->buf, q->term->term->u.general->len); term[q->term->term->u.general->len] = '\0'; for (p = term; *p; p++) if (islower(*p)) *p = toupper(*p); fseek(f, 0, SEEK_SET); q->num_entries = 0; for (i = 0, pos = 0; fscanf(f, " %79[^:]:%d", entries[pos], &hits[pos]) == 2; i++, pos < 199 ? pos++ : (pos = 0)) { if (!q->num_entries && strcmp(entries[pos], term) >= 0) /* s-point fnd */ { if ((q->term_position = term_position_req) > i + 1) { q->term_position = i + 1; q->status = BEND_SCAN_PARTIAL; } for (; q->num_entries < q->term_position; q->num_entries++) { int po; po = pos - q->term_position + q->num_entries+1; /* find pos */ if (po < 0) po += 200; if (!strcmp (term, "SD") && q->num_entries == 2) { list[q->num_entries].term = entries[pos]; list[q->num_entries].occurrences = -1; list[q->num_entries].errcode = 233; list[q->num_entries].errstring = "SD for Scan Term"; } else { list[q->num_entries].term = entries[po]; list[q->num_entries].occurrences = hits[po]; } } } else if (q->num_entries) { list[q->num_entries].term = entries[pos]; list[q->num_entries].occurrences = hits[pos]; q->num_entries++; } if (q->num_entries >= num_entries_req) break; } if (feof(f)) q->status = BEND_SCAN_PARTIAL; return 0; } void bend_close(void *handle) { if (z3950_verbosity_>1) { cerr << "Entering bend_close" << endl; } delete Server; delete Cservers; delete Protocol; if (z3950_verbosity_>1) { cerr << "heap cleaned up, exiting bend_close" << endl; } return; } int main (int argc, char *argv[]) { const int statserv_var = statserv_main(argc, argv, bend_init, bend_close); cerr << "statserv_main returns: " << statserv_var << endl; return statserv_var; return 0; }