/*
* $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;
}