ZOOM: Example - before (without ZOOM)


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