/* * $Id: ex-before.html,v 1.1.1.1 2001/10/25 16:15:04 mike Exp $ * * Small Z39.50 client that demonstrates the fundamental aspects of * the YAZ API. * * This application connects to a target, initializes with it, * and sends a search request. When search response is received * the application displays resultCount (number of hits). * If resultCount is positive a present request is sent * for retrieval of up to 10 records. When present response is * received records are displayed. */ #include <yaz/proto.h> #include <yaz/comstack.h> #include <yaz/pquery.h> #include <yaz/marcdisp.h> /* send_apdu: send APDU over the wire.*/ static void send_apdu (COMSTACK cs, ODR odr_out, Z_APDU *apdu) { /* encode it */ if (!z_APDU(odr_out, &apdu, 0, 0)) { odr_perror(odr_out, "Encoding APDU"); exit(1); } else { /* extract the BER buffer from the outgoing stream */ int len; char *buf = odr_getbuf(odr_out, &len, 0); /* buf and len now holds the PDU buffer */ /* now send it over the wire */ if (cs_put(cs, buf, len) < 0) { fprintf(stderr, "cs_put: %s\n", cs_errmsg(cs_errno(cs))); exit(1); } odr_reset(odr_out); /* release the APDU structure */ } } /* receive_apdu: receive APDU from peer. */ static void recv_apdu (COMSTACK cs, ODR odr_in, Z_APDU **apdup) { char *buf = 0; int len = 0; /* now wait for a response.*/ if (cs_get(cs, &buf, &len) < 0) { fprintf(stderr, "cs_get: %s", cs_errmsg(cs_errno(cs))); exit(1); } /* we have received a response in buf/len */ odr_reset(odr_in); /* let the ODR decoding stream have the BER buffer */ odr_setbuf(odr_in, buf, len, 0); /* decode and store result in apdu */ if (!z_APDU(odr_in, apdup, 0, 0)) { fprintf(stderr, "decoding failed\n"); exit(1); } xfree (buf); } static void print_record(const unsigned char *buf, size_t len) { fwrite (buf, 1, len, stdout); } /* display_record: display database record. This function handles transfer syntaxes XML, SUTRS and MARC records */ static void display_record(Z_DatabaseRecord *p) { Z_External *r = (Z_External*) p; oident *ent = oid_getentbyoid(r->direct_reference); /* * Tell the user what we got. */ if (r->direct_reference) { printf("Record type: "); if (ent) printf("%s\n", ent->desc); } if (r->which == Z_External_octet && p->u.octet_aligned->len) { /* consider records that fall into this category.. XML, ISO2709 */ const char *octet_buf = (char*)p->u.octet_aligned->buf; /* see if it's XML */ if (ent->value == VAL_TEXT_XML || ent->value == VAL_APPLICATION_XML || ent->value == VAL_HTML) print_record((const unsigned char *) octet_buf, p->u.octet_aligned->len); else { /* OK, probably it's MARC (ISO2709) */ if (marc_display (octet_buf, NULL) <= 0) { printf ("ISO2709 decoding failed, dumping record as is:\n"); print_record((const unsigned char*) octet_buf, p->u.octet_aligned->len); } } } else if (ent && ent->value == VAL_SUTRS) { if (r->which != Z_External_sutrs) { printf("Expecting single SUTRS type for SUTRS.\n"); return; } print_record(r->u.sutrs->buf, r->u.sutrs->len); } else if (ent && ent->value == VAL_GRS1) { printf ("GRS-1 record (not displayed)\n"); } else { printf("Unknown record representation.\n"); } } static void display_nameplusrecord(Z_NamePlusRecord *p) { /* print database for record (if supplied) */ if (p->databaseName) printf("[%s]", p->databaseName); /* see if we have a database record */ if (p->which == Z_NamePlusRecord_databaseRecord) display_record(p->u.databaseRecord); } void display_records (Z_Records *records) { if (!records) return; if (records->which == Z_Records_NSD || records->which == Z_Records_multipleNSD) { printf ("Error in search/present\n"); } else if (records->which == Z_Records_DBOSD) { int i; /* go through the list of returned records */ for (i = 0; i < records->u.databaseOrSurDiagnostics->num_records; i++) { printf ("---- %d ----\n", i+1); display_nameplusrecord( records->u.databaseOrSurDiagnostics->records[i]); } } } /* small utility that allows us to generate OID's easily. The ODR stream is used for memory allocation of the resulting OID. Class is class of OID, such as CLASS_ATTSET, oid_value is the attribute spec itself, such as VAL_BIB1 */ Odr_oid *oidval_to_z3950oid (ODR o, int oid_class, int oid_value) { oident ident; int oid[OID_SIZE]; ident.proto = PROTO_Z3950; ident.oclass = oid_class; ident.value = oid_value; if (ident.value == VAL_NONE) return 0; /* convert iden to OID and allocate it on the ODR stream */ return odr_oiddup(o, oid_ent_to_oid(&ident, oid)); } /* yaz_search: Search the target given by host using query */ static void yaz_search (const char *host, const char *query) { const char *dbp; void *add; COMSTACK cs; ODR odr_in, odr_out; Z_APDU *apdu; Z_InitRequest *init_req; Z_InitResponse *init_res; Z_PresentRequest *present_req; Z_PresentResponse *present_res; Z_SearchRequest *search_req; Z_SearchResponse *search_res; Z_RPNQuery *RPNquery; /* create ODR's for incoming - and outgoing requests */ odr_in = odr_createmem(ODR_DECODE); odr_out = odr_createmem(ODR_ENCODE); if (!odr_in || !odr_out) { printf ("Couldn't create ODR\n"); exit (1); } /* create COMSTACK and connect if possible */ cs = cs_create_host(host, 1, &add); if (!cs || cs_connect(cs, add) < 0) { printf ("Couldn't connect to %s\n", host); exit (1); } /* prepare init request APDU */ apdu = zget_APDU(odr_out, Z_APDU_initRequest); init_req = apdu->u.initRequest; /* this client is version 2 compatible */ ODR_MASK_SET(init_req->protocolVersion, Z_ProtocolVersion_1); ODR_MASK_SET(init_req->protocolVersion, Z_ProtocolVersion_2); /* it supports search and present */ ODR_MASK_SET(init_req->options, Z_Options_search); ODR_MASK_SET(init_req->options, Z_Options_present); /* send init request */ send_apdu (cs, odr_out, apdu); /* receive response */ recv_apdu (cs, odr_in, &apdu); if (apdu->which != Z_APDU_initResponse) { fprintf(stderr, "didn't receive init response"); exit(1); } /* we now have the initResponse in apdu */ init_res = apdu->u.initResponse; /* see if we succeeded */ if (!*init_res->result) printf("Connection rejected by target\n"); else printf("Connection accepted by target\n"); /* print information about target */ if (init_res->implementationId) printf("ID : %s\n", init_res->implementationId); if (init_res->implementationName) printf("Name : %s\n", init_res->implementationName); if (init_res->implementationVersion) printf("Version : %s\n", init_res->implementationVersion); /* prepare search request */ apdu = zget_APDU(odr_out, Z_APDU_searchRequest); search_req = apdu->u.searchRequest; /* prepare query for the search request */ search_req->query = odr_malloc (odr_out, sizeof(*search_req->query)); search_req->query->which = Z_Query_type_1; /* convert our prefix query string to RPN */ RPNquery = p_query_rpn(odr_out, PROTO_Z3950, query); if (!RPNquery) { printf ("bad RPN query\n"); exit (1); } /* it's OK, set it */ search_req->query->u.type_1 = RPNquery; /* set database for search request */ search_req->num_databaseNames = 1; search_req->databaseNames = odr_malloc (odr_out, sizeof(*search_req->databaseNames)); /* point to slash in host string (if any) */ dbp = strchr(host, '/'); /* set database to string after slash / "Default" */ search_req->databaseNames[0] = odr_strdup(odr_out, dbp ? dbp+1 : "Default"); /* send search request */ send_apdu (cs, odr_out, apdu); /* receive response */ recv_apdu (cs, odr_in, &apdu); if (apdu->which != Z_APDU_searchResponse) { fprintf (stderr, "Didn't receive search rsponse as expected\n"); exit (1); } /* we now have the search response in apdu */ search_res = apdu->u.searchResponse; if (search_res->resultCount) printf("Result Count: %d\n", *search_res->resultCount); /* display records and/or errors part of the search response */ display_records (search_res->records); if (search_res->resultCount && *search_res->resultCount > 0) { /* prepare present request */ apdu = zget_APDU(odr_out, Z_APDU_presentRequest); present_req = apdu->u.presentRequest; /* set range to 10 or less (if resultCount is less than 10) */ *present_req->numberOfRecordsRequested = (*search_res->resultCount > 10) ? 10 : *search_res->resultCount; /* set preferred record syntax / transfer syntax */ /* you can omit this completely in which no transfer syntax is given, or you may use, say, VAL_USMARC instead of VAL_SUTRS */ present_req->preferredRecordSyntax = oidval_to_z3950oid (odr_out, CLASS_RECSYN, VAL_SUTRS); /* send present request */ send_apdu (cs, odr_out, apdu); /* receive response */ recv_apdu (cs, odr_in, &apdu); if (apdu->which != Z_APDU_presentResponse) { fprintf (stderr, "Didn't receive present rsponse as expected\n"); exit (1); } /* we now have the present response in apdu */ present_res = apdu->u.presentResponse; display_records (present_res->records); } /* close communication */ cs_close (cs); /* and shut down the streams */ odr_destroy (odr_in); odr_destroy (odr_out); } int main(int argc, char **argv) { if (argc != 3) { printf ("Usage\n%s host/db query\n", *argv); exit (1); } nmem_init(); yaz_search (argv[1], argv[2]); return 0; }