dickmeiss Mon Feb 12 15:31:11 2001 EDT Modified files: /php4/ext/yaz php_yaz.c php_yaz.h /phpdoc/en/functions yaz.xml Log: Implemented yaz_present, yaz_scan, yaz_scan_result, yaz_ccl_conf, yaz_ccl_parse and yaz_itemorder. Updated documentation.
Index: php4/ext/yaz/php_yaz.c diff -u php4/ext/yaz/php_yaz.c:1.8 php4/ext/yaz/php_yaz.c:1.9 --- php4/ext/yaz/php_yaz.c:1.8 Tue Jan 30 05:09:24 2001 +++ php4/ext/yaz/php_yaz.c Mon Feb 12 15:31:10 2001 @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: php_yaz.c,v 1.8 2001/01/30 13:09:24 dickmeiss Exp $ */ +/* $Id: php_yaz.c,v 1.9 2001/02/12 23:31:10 dickmeiss Exp $ */ #include "php.h" @@ -32,11 +32,11 @@ #include <yaz/marcdisp.h> #include <yaz/yaz-util.h> #include <yaz/yaz-version.h> +#include <yaz/yaz-ccl.h> +#include <yaz/ill.h> #define MAX_ASSOC 100 -#define PHP_YAZ_DEBUG 1 - typedef struct Yaz_ResultSetInfo *Yaz_ResultSet; typedef struct Yaz_AssociationInfo *Yaz_Association; typedef struct Yaz_RecordCacheInfo *Yaz_RecordCache; @@ -87,6 +87,8 @@ int reconnect_flag; ODR odr_in; ODR odr_out; + ODR odr_scan; + Z_ScanResponse *scan_response; char *buf_out; int len_out; char *buf_in; @@ -96,6 +98,10 @@ int numberOfRecordsRequested; char *elementSetNames; char *preferredRecordSyntax; + + CCL_parser ccl_parser; + char *ill_buf_out; + int ill_len_out; }; static Yaz_Association yaz_association_mk () @@ -119,15 +125,21 @@ p->reconnect_flag = 0; p->odr_in = odr_createmem (ODR_DECODE); p->odr_out = odr_createmem (ODR_ENCODE); + p->odr_scan = odr_createmem (ODR_ENCODE); + p->scan_response = 0; p->buf_out = 0; p->len_out = 0; p->buf_in = 0; p->len_in = 0; p->action = 0; + p->ill_buf_out = 0; + p->ill_len_out = 0; p->resultSetStartPoint = 1; p->numberOfRecordsRequested = 10; p->elementSetNames = 0; p->preferredRecordSyntax = 0; + p->ccl_parser = ccl_parser_create(); + p->ccl_parser->bibset = 0; return p; } @@ -150,11 +162,15 @@ xfree (p->addinfo); odr_destroy (p->odr_in); odr_destroy (p->odr_out); + odr_destroy (p->odr_scan); /* buf_out */ /* buf_in */ /* action */ + xfree (p->ill_buf_out); xfree (p->elementSetNames); xfree (p->preferredRecordSyntax); + ccl_qual_rm(&p->ccl_parser->bibset); + ccl_parser_destroy(p->ccl_parser); } static Yaz_ResultSet yaz_resultset_mk() @@ -186,27 +202,6 @@ static Yaz_Association *shared_associations; static int order_associations; -/* - when id = 0, it means all targes... - id = yaz_connect(zurl, user, group, pass); - yaz_set_db(id, db) - yaz_error(id) - yaz_errno(id) - yaz_addinfo(id) - - yaz_wait() - yaz_range(id, from, to, syntax, elementset) - yaz_search(id, type, query) - yaz_hits(id) - yaz_record(id, pos) - - yaz_scan(id) - yaz_scan_point(id, scanterm, before, after) - yaz_scan_result(id, pos) - - yaz_close(id) -*/ - function_entry yaz_functions [] = { PHP_FE(yaz_connect, NULL) PHP_FE(yaz_close, NULL) @@ -220,6 +215,12 @@ PHP_FE(yaz_syntax, NULL) PHP_FE(yaz_element, NULL) PHP_FE(yaz_range, NULL) + PHP_FE(yaz_itemorder, NULL) + PHP_FE(yaz_scan, NULL) + PHP_FE(yaz_scan_result, NULL) + PHP_FE(yaz_present, NULL) + PHP_FE(yaz_ccl_conf, NULL) + PHP_FE(yaz_ccl_parse, NULL) {NULL, NULL, NULL} }; @@ -405,6 +406,58 @@ handle_records (t, pr->records, 1); } +void scan_response (Yaz_Association t, Z_ScanResponse *res) +{ + NMEM nmem = odr_extract_mem (t->odr_in); + if (res->entries && res->entries->nonsurrogateDiagnostics) + response_diag(t, res->entries->nonsurrogateDiagnostics[0]); + t->scan_response = res; + nmem_transfer (t->odr_scan->mem, nmem); + nmem_destroy (nmem); +} + +void es_response (Yaz_Association t, + Z_ExtendedServicesResponse *res) +{ +#if 0 + Odr_oct *id = res->referenceId; + + if (id) + html_var_n (req, "refid", id->buf, id->len); + else + html_var (req, "refid", ""); + + html_var (req, "targetreference", ""); + if (res->taskPackage && + res->taskPackage->which == Z_External_extendedService) + { + Z_TaskPackage *taskPackage = res->taskPackage->u.extendedService; + Odr_oct *id = taskPackage->targetReference; + + if (id) + html_var_n (req, "targetreference", id->buf, id->len); + } + switch (*res->operationStatus) + { + case Z_ExtendedServicesResponse_done: + html_dump (req, "es-response done"); + break; + case Z_ExtendedServicesResponse_accepted: + html_dump (req, "es-response accepted"); + break; + case Z_ExtendedServicesResponse_failure: + html_dump (req, "es-response failure"); + break; + default: + html_dump (req, "es-response unknown"); + break; + } +#endif + if (res->diagnostics && res->num_diagnostics > 0) + response_diag(t, res->diagnostics[0]); +} + + static void handle_apdu (Yaz_Association t, Z_APDU *apdu) { Z_InitResponse *initrs; @@ -429,7 +482,8 @@ xfree(t->cookie); t->cookie = xstrdup(cookie); } - (*t->action) (t); + if (t->action) + (*t->action) (t); } break; case Z_APDU_searchResponse: @@ -440,18 +494,12 @@ present_response (t, apdu->u.presentResponse); send_present (t); break; -#if 0 case Z_APDU_scanResponse: - zlog (req, t->name, " scan response"); - scanResponse (req, t, apdu->u.scanResponse); + scan_response (t, apdu->u.scanResponse); break; -#endif -#if USE_ES case Z_APDU_extendedServicesResponse: - zlog (req, t->name, " ES response"); - esResponse (req, t, apdu->u.extendedServicesResponse); + es_response (t, apdu->u.extendedServicesResponse); break; -#endif case Z_APDU_close: do_close(t); if (t->reconnect_flag) @@ -505,11 +553,11 @@ return 1; } -static int do_write (Yaz_Association t) +static int do_write_ex (Yaz_Association t, char *buf_out, int len_out) { int r; - if ((r=cs_put (t->cs, t->buf_out, t->len_out)) < 0) + if ((r=cs_put (t->cs, buf_out, len_out)) < 0) { if (t->reconnect_flag) { @@ -539,33 +587,65 @@ return 0; } +static int do_write(Yaz_Association t) +{ + return do_write_ex (t, t->buf_out, t->len_out); +} -static int send_APDU (Yaz_Association t, Z_APDU *a) +static int send_packet (Yaz_Association t) +{ + return do_write_ex (t, t->ill_buf_out, t->ill_len_out); +} + +static int encode_APDU(Yaz_Association t, Z_APDU *a, ODR out) { + char str[120]; + + if (a == 0) + abort(); + sprintf (str, "send_APDU t=%p type=%d", t, a->which); +#if 0 + php_error (E_WARNING, str); +#endif if (t->cookie) { Z_OtherInformation **oi; yaz_oi_APDU(a, &oi); - yaz_oi_set_string_oidval(oi, t->odr_out, VAL_COOKIE, 1, t->cookie); + yaz_oi_set_string_oidval(oi, out, VAL_COOKIE, 1, t->cookie); } /* from ZAP */ #if 0 if (req->request->connection) { Z_OtherInformation **oi; - zlog (req, t->name, " encoding client_ip"); yaz_oi_APDU(a, &oi); - yaz_oi_set_string_oidval(oi, t->odr_out, VAL_CLIENT_IP, 1, + yaz_oi_set_string_oidval(oi, out, VAL_CLIENT_IP, 1, req->request->connection->remote_ip); } #endif - if (!z_APDU(t->odr_out, &a, 0, 0)) + if (!z_APDU(out, &a, 0, 0)) { + FILE *outf = fopen("/tmp/apdu.txt", "w"); + if (outf) + { + ODR odr_pr = odr_createmem(ODR_PRINT); + fprintf (outf, "a=%p\n", a); + odr_setprint(odr_pr, outf); + z_APDU(odr_pr, &a, 0, 0); + odr_destroy(odr_pr); + fclose (outf); + } + php_error (E_WARNING, "YAZ: Couldn't encode APDU"); do_close (t); t->error = PHP_YAZ_ERROR_ENCODE; return -1; } - /* apdu_log(req, t->odr_print, a); */ + return 0; +} + +static int send_APDU (Yaz_Association t, Z_APDU *a) +{ + encode_APDU(t, a, t->odr_out); t->buf_out = odr_getbuf(t->odr_out, &t->len_out, 0); odr_reset(t->odr_out); do_write (t); @@ -655,7 +735,7 @@ odr_oiddup (t->odr_out, oid_getoidbyent (&ident)); } sreq->databaseNames = set_DatabaseNames (t, &sreq->num_databaseNames); - + send_APDU (t, apdu); return 1; } @@ -666,16 +746,15 @@ Z_PresentRequest *req = apdu->u.presentRequest; int i = 0; - if (!t->resultSets->recordList) /* no records to retrieve at all .. */ + if (!t->resultSets) /* no result set yet? */ { return 0; } - while (1) + while (t->resultSets->recordList) { if (i >= t->resultSets->recordList->num_records) { /* got all records ... */ - /* searchHits (zreq, t); */ return 0; } if (!t->resultSets->recordList->records[i]) @@ -688,8 +767,15 @@ *req->resultSetStartPoint = t->resultSetStartPoint + i; req->numberOfRecordsRequested = odr_malloc (t->odr_out, sizeof(int)); - *req->numberOfRecordsRequested = t->resultSets->recordList->num_records - i; - + if (t->resultSets->recordList) + *req->numberOfRecordsRequested = + t->resultSets->recordList->num_records - i; + else + *req->numberOfRecordsRequested = t->numberOfRecordsRequested; + + if (*req->numberOfRecordsRequested <= 0) + return 0; + if (t->preferredRecordSyntax && *t->preferredRecordSyntax) { struct oident ident; @@ -1011,9 +1097,14 @@ pval **id, **type, **query; Yaz_Association p; Yaz_ResultSet r; - if (ZEND_NUM_ARGS() != 3) - WRONG_PARAM_COUNT; - if (zend_get_parameters_ex(3, &id, &type, &query) == FAILURE) + if (ZEND_NUM_ARGS() == 3) + { + if (zend_get_parameters_ex(3, &id, &type, &query) == FAILURE) + { + WRONG_PARAM_COUNT; + } + } + else { WRONG_PARAM_COUNT; } @@ -1022,6 +1113,7 @@ { RETURN_FALSE; } + p->action = 0; convert_to_string_ex (type); type_str = (*type)->value.str.val; convert_to_string_ex (query); @@ -1044,13 +1136,52 @@ RETVAL_TRUE; } } + else if (!strcmp(type_str, "ccl")) + { + r->query->which = Z_Query_type_2; + r->query->u.type_2 = odr_malloc (r->odr, sizeof(*r->query->u.type_2)); + r->query->u.type_2->buf = odr_strdup(r->odr, query_str); + r->query->u.type_2->len = strlen(query_str); + } else { yaz_resultset_destroy(r); p->resultSets = 0; RETVAL_FALSE; } + if (p->resultSets) + p->action = send_search; + release_assoc (p); +} +/* }}} */ + + +/* {{{ proto int yaz_present(int id) + Retrieve records */ +PHP_FUNCTION(yaz_present) +{ + pval **id; + Yaz_Association p; + if (ZEND_NUM_ARGS() != 1) + WRONG_PARAM_COUNT; + if (zend_get_parameters_ex(1, &id) == FAILURE) + { + WRONG_PARAM_COUNT; + } + p = get_assoc (id); + if (!p) + { + zend_error(E_WARNING, "get_assoc failed for present"); + RETURN_FALSE; + } + p->action = 0; + if (p->resultSets) + { + p->resultSets->recordList = 0; + p->action = send_present; + } release_assoc (p); + RETURN_TRUE; } /* }}} */ @@ -1066,9 +1197,8 @@ for (i = 0; i<MAX_ASSOC; i++) { Yaz_Association p = shared_associations[i]; - if (!p || p->order != order_associations || !p->resultSets) + if (!p || p->order != order_associations || !p->action) continue; - p->action = send_search; if (!p->cs) { do_connect (p); @@ -1076,7 +1206,7 @@ else { p->reconnect_flag = 1; - send_search (p); + (*p->action)(p); } } #ifdef ZTS @@ -1620,12 +1750,505 @@ { convert_to_long_ex (pval_start); p->resultSetStartPoint = (*pval_start)->value.lval; + if (p->resultSetStartPoint < 1) + p->resultSetStartPoint = 1; convert_to_long_ex (pval_number); p->numberOfRecordsRequested = (*pval_number)->value.lval; } release_assoc (p); } /* }}} */ + +static const char *array_lookup(HashTable *ht, const char *idx) +{ + pval **pvalue; + + if (ht && zend_hash_find(ht, (char*) idx, strlen(idx)+1, + (void**) &pvalue) == SUCCESS) + { + SEPARATE_ZVAL(pvalue); + convert_to_string(*pvalue); + return (*pvalue)->value.str.val; + } + return 0; +} + +static const char *ill_array_lookup (void *clientData, const char *idx) +{ + return array_lookup((HashTable *) clientData, idx+4); +} + +static Z_External *encode_ill_request (Yaz_Association t, HashTable *ht) +{ + ODR out = t->odr_out; + ILL_Request *req; + Z_External *r = 0; + struct ill_get_ctl ctl; + + ctl.odr = t->odr_out; + ctl.clientData = ht; + ctl.f = ill_array_lookup; + + req = ill_get_ILLRequest(&ctl, "ill", 0); + + if (!ill_Request (out, &req, 0, 0)) + { + int ill_request_size; + char *ill_request_buf = odr_getbuf (out, &ill_request_size, 0); + if (ill_request_buf) + odr_setbuf (out, ill_request_buf, ill_request_size, 1); + php_error(E_WARNING, "yaz_itemorder: Expected array parameter"); + return 0; + } + else + { + oident oid; + int illRequest_size = 0; + char *illRequest_buf = odr_getbuf (out, &illRequest_size, 0); + + oid.proto = PROTO_GENERAL; + oid.oclass = CLASS_GENERAL; + oid.value = VAL_ISO_ILL_1; + + r = (Z_External *) odr_malloc (out, sizeof(*r)); + r->direct_reference = odr_oiddup(out,oid_getoidbyent(&oid)); + r->indirect_reference = 0; + r->descriptor = 0; + r->which = Z_External_single; + + r->u.single_ASN1_type = (Odr_oct *) + odr_malloc (out, sizeof(*r->u.single_ASN1_type)); + r->u.single_ASN1_type->buf = odr_malloc (out, illRequest_size); + r->u.single_ASN1_type->len = illRequest_size; + r->u.single_ASN1_type->size = illRequest_size; + memcpy (r->u.single_ASN1_type->buf, illRequest_buf, illRequest_size); + } + return r; +} + +static Z_ItemOrder *encode_item_order(Yaz_Association t, + HashTable +*ht) +{ + Z_ItemOrder *req = odr_malloc (t->odr_out, sizeof(*req)); + const char *str; + +#ifdef ASN_COMPILED + req->which=Z_IOItemOrder_esRequest; +#else + req->which=Z_ItemOrder_esRequest; +#endif + req->u.esRequest = (Z_IORequest *) + odr_malloc(t->odr_out,sizeof(Z_IORequest)); + + /* to keep part ... */ + req->u.esRequest->toKeep = (Z_IOOriginPartToKeep *) + odr_malloc(t->odr_out,sizeof(Z_IOOriginPartToKeep)); + req->u.esRequest->toKeep->supplDescription = 0; + req->u.esRequest->toKeep->contact = + odr_malloc (t->odr_out, sizeof(*req->u.esRequest->toKeep->contact)); + + str = array_lookup (ht, "contact-name"); + req->u.esRequest->toKeep->contact->name = str ? + nmem_strdup (t->odr_out->mem, str) : 0; + + str = array_lookup (ht, "contact-phone"); + req->u.esRequest->toKeep->contact->phone = str ? + nmem_strdup (t->odr_out->mem, str) : 0; + + str = array_lookup (ht, "contact-email"); + req->u.esRequest->toKeep->contact->email = str ? + nmem_strdup (t->odr_out->mem, str) : 0; + + req->u.esRequest->toKeep->addlBilling = 0; + + /* not to keep part ... */ + req->u.esRequest->notToKeep = (Z_IOOriginPartNotToKeep *) + odr_malloc(t->odr_out,sizeof(Z_IOOriginPartNotToKeep)); + + req->u.esRequest->notToKeep->resultSetItem = (Z_IOResultSetItem *) + odr_malloc(t->odr_out, sizeof(Z_IOResultSetItem)); + req->u.esRequest->notToKeep->resultSetItem->resultSetId = "default"; + req->u.esRequest->notToKeep->resultSetItem->item = + (int *) odr_malloc(t->odr_out, sizeof(int)); + + str = array_lookup (ht, "itemorder-item"); + *req->u.esRequest->notToKeep->resultSetItem->item = + (str ? atoi(str) : 1); + + req->u.esRequest->notToKeep->itemRequest = + encode_ill_request(t, ht); + + return req; +} + +static Z_APDU *encode_es_itemorder (Yaz_Association t, HashTable *ht) +{ + Z_APDU *apdu = zget_APDU(t->odr_out, Z_APDU_extendedServicesRequest); + Z_ExtendedServicesRequest *req = apdu->u.extendedServicesRequest; + const char *str; + struct oident oident; + int oid[OID_SIZE]; + Z_External *r = odr_malloc (t->odr_out, sizeof(*r)); + + *req->function = Z_ExtendedServicesRequest_create; + oident.proto = PROTO_Z3950; + oident.oclass = CLASS_EXTSERV; + oident.value = VAL_ITEMORDER; + req->taskSpecificParameters = r; + r->direct_reference = + odr_oiddup(t->odr_out, oid_ent_to_oid(&oident, oid)); + r->indirect_reference = 0; + r->descriptor = 0; + r->which = Z_External_itemOrder; + r->u.itemOrder = encode_item_order (t, ht); + req->packageType = odr_oiddup(t->odr_out, oid_ent_to_oid(&oident, oid)); + + str = array_lookup(ht, "package-name"); + if (str && *str) + req->packageName = nmem_strdup (t->odr_out->mem, str); + + str = array_lookup(ht, "user-id"); + if (str) + req->userId = nmem_strdup (t->odr_out->mem, str); + + return apdu; +} + + +/* {{{ proto int yaz_itemorder(int id, array package) + Sends Item Order request */ + +PHP_FUNCTION(yaz_itemorder) +{ + pval **pval_id, **pval_package; + Yaz_Association p; + if (ZEND_NUM_ARGS() != 2 || + zend_get_parameters_ex(2, &pval_id, &pval_package) == + FAILURE) + { + WRONG_PARAM_COUNT; + } + if (Z_TYPE_PP(pval_package) != IS_ARRAY) + { + php_error(E_WARNING, "yaz_itemorder: Expected array parameter"); + RETURN_FALSE; + } + p = get_assoc (pval_id); + if (p) + { + Z_APDU *apdu; + p->action = 0; + apdu = encode_es_itemorder (p, Z_ARRVAL_PP(pval_package)); + if (apdu) + { + char *buf; + encode_APDU(p, apdu, p->odr_out); + buf = odr_getbuf(p->odr_out, &p->ill_len_out, 0); + xfree (p->ill_buf_out); + p->ill_buf_out = xmalloc (p->ill_len_out); + memcpy (p->ill_buf_out, buf, p->ill_len_out); + p->action = send_packet; + } + } + release_assoc (p); +} +/* }}} */ + +static Z_APDU *encode_scan (Yaz_Association t, const char *type, + const char *query, HashTable +*ht) +{ + Z_APDU *apdu = zget_APDU(t->odr_out, Z_APDU_scanRequest); + Z_ScanRequest *req = apdu->u.scanRequest; + const char *val; + if (!strcmp(type, "rpn")) + { + if (!(req->termListAndStartPoint = + p_query_scan(t->odr_out, PROTO_Z3950, &req->attributeSet, + query))) + { + char str[80]; + sprintf (str, "YAZ: Bad Scan query: '%.40s'", query); + php_error (E_WARNING, str); + return 0; + } + } + else + { + char str[80]; + sprintf (str, "YAZ: Bad Scan query type: '%.40s'", type); + php_error (E_WARNING, str); + return 0; + } + val = array_lookup(ht, "number"); + if (val && *val) + *req->numberOfTermsRequested = atoi(val); + val = array_lookup(ht, "position"); + if (val && *val) + { + req->preferredPositionInResponse = + odr_malloc (t->odr_out, sizeof(int)); + *req->preferredPositionInResponse = atoi(val); + } + val = array_lookup(ht, "stepsize"); + if (val && *val) + { + req->stepSize = odr_malloc (t->odr_out, sizeof(int)); + *req->stepSize = atoi(val); + } + req->databaseNames = set_DatabaseNames (t, &req->num_databaseNames); + return apdu; +} + +/* {{{ proto int yaz_scan(int id, type, query [, flags]) + Sends Scan Request */ +PHP_FUNCTION(yaz_scan) +{ + pval **pval_id, **pval_type, **pval_query, **pval_flags = 0; + HashTable *flags_ht = 0; + Yaz_Association p; + if (ZEND_NUM_ARGS() == 3) + { + if (zend_get_parameters_ex(3, &pval_id, &pval_type, &pval_query) == + FAILURE) + { + WRONG_PARAM_COUNT; + } + } + else if (ZEND_NUM_ARGS() == 4) + { + if (zend_get_parameters_ex(4, &pval_id, &pval_type, &pval_query, + &pval_flags) == + FAILURE) + { + WRONG_PARAM_COUNT; + } + if (Z_TYPE_PP(pval_flags) != IS_ARRAY) + { + php_error(E_WARNING, "yaz_scan: Bad flags parameter"); + RETURN_FALSE; + } + flags_ht = Z_ARRVAL_PP(pval_flags); + } + else + { + WRONG_PARAM_COUNT; + } + convert_to_string_ex (pval_type); + convert_to_string_ex (pval_query); + + p = get_assoc (pval_id); + if (p) + { + Z_APDU *apdu; + p->action = 0; + apdu = encode_scan (p, Z_STRVAL_PP(pval_type), Z_STRVAL_PP(pval_query), + flags_ht); + if (apdu) + { + char *buf; + odr_reset(p->odr_scan); + p->scan_response = 0; + encode_APDU(p, apdu, p->odr_out); + buf = odr_getbuf(p->odr_out, &p->ill_len_out, 0); + xfree (p->ill_buf_out); + p->ill_buf_out = xmalloc (p->ill_len_out); + memcpy (p->ill_buf_out, buf, p->ill_len_out); + p->action = send_packet; + } + } + release_assoc (p); +} +/* }}} */ + +/* {{{ proto int yaz_scan_result(int id, array options) + Inspects Scan Result */ +PHP_FUNCTION(yaz_scan_result) +{ + pval **pval_id, **pval_opt = 0; + Yaz_Association p; + if (ZEND_NUM_ARGS() == 2) + { + if (zend_get_parameters_ex(2, &pval_id, &pval_opt) == FAILURE) + { + WRONG_PARAM_COUNT; + } + if (!ParameterPassedByReference(ht, 2)) + { + WRONG_PARAM_COUNT; + } + } + else if (ZEND_NUM_ARGS() == 1) + { + if (zend_get_parameters_ex(1, &pval_id) == FAILURE) + { + WRONG_PARAM_COUNT; + } + } + else + { + WRONG_PARAM_COUNT; + } + if (array_init(return_value) == FAILURE) + { + RETURN_FALSE; + } + if (pval_opt && array_init(*pval_opt) == FAILURE) + { + RETURN_FALSE; + } + p = get_assoc (pval_id); + if (p && p->scan_response) + { + int i; + Z_ScanResponse *res = p->scan_response; + if (pval_opt) + { + if (res->numberOfEntriesReturned) + add_assoc_long(*pval_opt, "number", + +*res->numberOfEntriesReturned); + if (res->stepSize) + add_assoc_long(*pval_opt, "stepsize", *res->stepSize); + if (res->positionOfTerm) + add_assoc_long(*pval_opt, "position", +*res->positionOfTerm); + if (res->scanStatus) + add_assoc_long(*pval_opt, "status", *res->scanStatus); + } + for (i = 0; res->entries && i < res->entries->num_entries; i++) + { + zval *my_zval; + ALLOC_ZVAL(my_zval); + array_init(my_zval); + INIT_PZVAL(my_zval); + + if (res->entries->entries[i]->which == Z_Entry_termInfo) + { + Z_TermInfo *t = res->entries->entries[i]->u.termInfo; + add_next_index_string(my_zval, "term", 1); + + if (t->term->which == Z_Term_general) + add_next_index_stringl (my_zval, +t->term->u.general->buf, + + t->term->u.general->len, 1); + else + add_next_index_string (my_zval, "?", 1); + add_next_index_long (my_zval, t->globalOccurrences ? + *t->globalOccurrences : 0); + } + else + add_next_index_string(my_zval, "unknown", 1); + + zend_hash_next_index_insert ( + return_value->value.ht, (void *) &my_zval, sizeof(zval +*), + NULL); + } + + } + release_assoc (p); +} +/* }}} */ + +/* {{{ proto int yaz_ccl_conf(int id, array package) + Configure CCL package */ + +PHP_FUNCTION(yaz_ccl_conf) +{ + pval **pval_id, **pval_package; + Yaz_Association p; + if (ZEND_NUM_ARGS() != 2 || + zend_get_parameters_ex(2, &pval_id, &pval_package) == + FAILURE) + { + WRONG_PARAM_COUNT; + } + if (Z_TYPE_PP(pval_package) != IS_ARRAY) + { + php_error(E_WARNING, "yaz_ccl_conf: Expected array parameter"); + RETURN_FALSE; + } + p = get_assoc (pval_id); + if (p) + { + HashTable *ht = Z_ARRVAL_PP(pval_package); + HashPosition pos; + zval **ent; + char *key; + + ccl_qual_rm(&p->ccl_parser->bibset); + p->ccl_parser->bibset = ccl_qual_mk(); + for(zend_hash_internal_pointer_reset_ex(ht, &pos); + zend_hash_get_current_data_ex(ht, (void**) &ent, &pos) == +SUCCESS; + zend_hash_move_forward_ex(ht, &pos)) + { + ulong idx; + int type = zend_hash_get_current_key_ex(ht, &key, 0, + + &idx, 0, &pos); + if (type != HASH_KEY_IS_STRING || Z_TYPE_PP(ent) != IS_STRING) + continue; + ccl_qual_fitem(p->ccl_parser->bibset, (*ent)->value.str.val, +key); + } + } + release_assoc (p); +} +/* }}} */ + +/* {{{ proto int yaz_ccl_parse(int id, string query, array res) + Parse a CCL query */ + +PHP_FUNCTION(yaz_ccl_parse) +{ + pval **pval_id, **pval_query, **pval_res; + Yaz_Association p; + if (ZEND_NUM_ARGS() != 3 || + zend_get_parameters_ex(3, &pval_id, &pval_query, &pval_res) == + FAILURE) + { + WRONG_PARAM_COUNT; + } + if (!ParameterPassedByReference(ht, 3)) + { + WRONG_PARAM_COUNT; + } + if (array_init(*pval_res) == FAILURE) + { + RETURN_FALSE; + } + convert_to_string_ex (pval_query); + p = get_assoc (pval_id); + if (p) + { + const char *query_str = (*pval_query)->value.str.val; + struct ccl_rpn_node *rpn; + struct ccl_token *token_list = + ccl_parser_tokenize(p->ccl_parser, query_str); + rpn = ccl_parser_find(p->ccl_parser, token_list); + ccl_token_del(token_list); + + add_assoc_long(*pval_res, "errorcode", p->ccl_parser->error_code); + if (p->ccl_parser->error_code) + { + add_assoc_string(*pval_res, "errorstring", + (char*) +ccl_err_msg(p->ccl_parser->error_code), + 1); + add_assoc_long(*pval_res, "errorpos", + p->ccl_parser->error_pos - +query_str); + RETVAL_FALSE; + } + else + { + WRBUF wrbuf_pqf = wrbuf_alloc(); + ccl_pquery(wrbuf_pqf, rpn); + add_assoc_stringl(*pval_res, "rpn", wrbuf_buf(wrbuf_pqf), + wrbuf_len(wrbuf_pqf),1); + wrbuf_free(wrbuf_pqf, 1); + RETVAL_TRUE; + } + ccl_rpn_delete(rpn); + } + else + RETVAL_FALSE; + release_assoc (p); +} +/* }}} */ + PHP_MINIT_FUNCTION(yaz) { Index: php4/ext/yaz/php_yaz.h diff -u php4/ext/yaz/php_yaz.h:1.2 php4/ext/yaz/php_yaz.h:1.3 --- php4/ext/yaz/php_yaz.h:1.2 Wed Nov 1 14:10:54 2000 +++ php4/ext/yaz/php_yaz.h Mon Feb 12 15:31:10 2001 @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: php_yaz.h,v 1.2 2000/11/01 22:10:54 dickmeiss Exp $ */ +/* $Id: php_yaz.h,v 1.3 2001/02/12 23:31:10 dickmeiss Exp $ */ #ifndef PHP_YAZ_H #define PHP_YAZ_H @@ -42,6 +42,12 @@ PHP_FUNCTION(yaz_syntax); PHP_FUNCTION(yaz_element); PHP_FUNCTION(yaz_range); +PHP_FUNCTION(yaz_itemorder); +PHP_FUNCTION(yaz_scan); +PHP_FUNCTION(yaz_scan_result); +PHP_FUNCTION(yaz_present); +PHP_FUNCTION(yaz_ccl_conf); +PHP_FUNCTION(yaz_ccl_parse); #else Index: phpdoc/en/functions/yaz.xml diff -u phpdoc/en/functions/yaz.xml:1.7 phpdoc/en/functions/yaz.xml:1.8 --- phpdoc/en/functions/yaz.xml:1.7 Fri Feb 2 09:15:24 2001 +++ phpdoc/en/functions/yaz.xml Mon Feb 12 15:31:11 2001 @@ -8,8 +8,8 @@ This extension offers a PHP interface to the <productname>YAZ</productname> toolkit that implements the Z39.50 protocol for information retrieval. With this extension you can easily - implement a Z39.50 origin (client) that searches Z39.50 targets - (servers) in parallel. + implement a Z39.50 origin (client) that searches or scans Z39.50 + targets (servers) in parallel. </para> <para> <productname>YAZ</productname> is available at <ulink @@ -22,7 +22,8 @@ fairly easy to use. It supports persistent stateless connections very similar to those offered by the various SQL APIs that are available for PHP. This means that sessions are stateless but shared amongst - users, thus saving the connect and initialize phase steps in most cases. + users, thus saving the connect and initialize phase steps in most + cases. </para> </sect1> <sect1 id="yaz.install"> @@ -57,14 +58,14 @@ particular association. </para> <para> - The script below demonstrates the parallel searching feature of - the API. When invoked with no arguments it prints a query form; else - (arguments are supplied) it searches the targets as given in in array - host. - </para> - <para> <example> - <title><function>YAZ</function></title> + <title><function>Parallel searching using YAZ</function></title> + <simpara> + The script below demonstrates the parallel searching feature of + the API. When invoked with no arguments it prints a query form; else + (arguments are supplied) it searches the targets as given in in array + host. + </simpara> <programlisting role="php"> $num_hosts = count ($host); if (empty($term) || count($host) == 0) { @@ -152,8 +153,9 @@ </funcprototype> </funcsynopsis> <para> - Closes a connection to a target. The application can no longer - refer to the target with the given ID. + Closes the Z-association given by <parameter>id</parameter>. + The <parameter>id</parameter> is a target ID as returned by a + previous <function>yaz_connect</function> command. </para> </refsect1> </refentry> @@ -162,6 +164,7 @@ <refnamediv> <refname>yaz_connect</refname> <refpurpose> + Prepares for a connection and Z-association with a target. Returns a positive association ID on success; zero on failure </refpurpose> </refnamediv> @@ -170,8 +173,13 @@ <funcsynopsis> <funcprototype> <funcdef>int <function>yaz_connect</function></funcdef> - <paramdef>string <parameter>zurl</parameter></paramdef> - </funcprototype> + <paramdef>string + <parameter>zurl</parameter> + </paramdef> + <paramdef>string + <parameter><optional>authentication</optional></parameter> + </paramdef> + </funcprototype> </funcsynopsis> <para> <function>Yaz_connect</function> prepares for a connection to a @@ -206,7 +214,7 @@ question. </para> <para> - <function>Yaz_errno</function> should be called after network + <function>yaz_errno</function> should be called after network activity for each target - (after <function>yaz_wait</function> returns) to determine the success or failure of the last operation (e.g. search). @@ -232,7 +240,7 @@ is returned if last operation was a success. </para> <para> - <function>Yaz_error</function> returns a english message + <function>yaz_error</function> returns an english text message corresponding to the last error number as returned by <function>yaz_errno</function>. </para> @@ -277,9 +285,10 @@ </funcsynopsis> <para> This function is used in conjunction with - <function>yaz_search</function> to specify the element set name - for records to be retrieved. Most servers support F (full) and - B (brief). + <function>yaz_search</function> and <function>yaz_present</function> + to specify the element set name for records to be retrieved. + Most servers support <literal>F</literal> (full) and + <literal>B</literal> (brief). </para> <para> Returns true on success; false on error. @@ -458,12 +467,35 @@ </refsect1> </refentry> + <refentry id="function.yaz-present"> + <refnamediv> + <refname>yaz_present</refname> + <refpurpose> + Prepares for retrieval (Z39.50 present). + </refpurpose> + </refnamediv> + <refsect1> + <title>Description</title> + <funcsynopsis> + <funcprototype> + <funcdef>int <function>yaz_present</function></funcdef> + <void></void> + </funcprototype> + </funcsynopsis> + <para> + This function prepares for retrieval of records after + a successful search. The <function>yaz_range</function> should + be called prior to this function to specify the range of + records to be retrieved. + </para> + </refsect1> + </refentry> + <refentry id="function.yaz-syntax"> <refnamediv> <refname>yaz_syntax</refname> <refpurpose> - Specifies the object identifier (OID) for the preferred record syntax - for retrieval. + Specifies the preferred record syntax for retrieval. </refpurpose> </refnamediv> <refsect1> @@ -476,37 +508,356 @@ </funcprototype> </funcsynopsis> <para> - The syntax may be specified in a raw dot-notation (like - <literal>1.2.840.10003.5.10</literal>) or as one of the known - record syntaxes (sutrs, usmarc, grs1, xml, etc.). - This function is used in conjunction with - <function>yaz_search</function> to specify the preferred record - syntax for retrieval. + The syntax is specified as an OID (Object Identifier) in a + raw dot-notation (like <literal>1.2.840.10003.5.10</literal>) or + as one of the known registered record syntaxes (sutrs, usmarc, grs1, + xml, etc.). This function is used in conjunction with + <function>yaz_search</function> and <function>yaz_present</function> + to specify the preferred record syntax for retrieval. + </para> + </refsect1> + </refentry> + + <refentry id="function.yaz-scan"> + <refnamediv> + <refname>yaz_scan</refname> + <refpurpose>Prepares for a scan</refpurpose> + </refnamediv> + <refsect1> + <title>Description</title> + <funcsynopsis> + <funcprototype> + <funcdef>int <function>yaz_scan</function></funcdef> + <paramdef>int <parameter>id</parameter></paramdef> + <paramdef>string <parameter>type</parameter></paramdef> + <paramdef>string <parameter>startterm</parameter></paramdef> + <paramdef>array + <parameter><optional>flags</optional></parameter> + </paramdef> + </funcprototype> + </funcsynopsis> + <para> + This function prepares for a Z39.50 Scan Request. Argument + <parameter>id</parameter> specifies target ID. Starting term + point for the scan is given by <parameter>startterm</parameter>. + The form in which is the starting term is specified is given by + <parameter>type</parameter>. Currently type <literal>rpn</literal> + is supported. The optional <parameter>flags</parameter> + specifies additional information to control the behaviour of the + scan request. Three indexes are currently read from the flags: + <literal>number</literal> (number of terms requested), + <literal>position</literal> (preferred position of term) and + <literal>stepSize</literal> (preferred step size). + To actually tranfer the Scan Request to the target and receive the + Scan Response, <function>yaz_wait</function> must be called. Upon + completion of <function>yaz_wait</function> call + <function>yaz_error</function> and + <function>yaz_scan_result</function> to handle the response. + </para> + <para> + The syntax of <parameter>startterm</parameter> is similar to the + RPN query as described in <function>yaz_search</function>. The + startterm consists of zero or more <literal>@attr</literal>-operator + specifications, then followed by exactly one token. + </para> + <para> + <example> + <title>PHP function that scans titles</title> + <programlisting> + function scan_titles($id, $starterm) { + yaz_scan($id,"rpn", "@attr 1=4 " . $starterm); + yaz_wait(); + $errno = yaz_errno($id); + if ($errno == 0) { + $ar = yaz_scan_result($id,&$options); + echo 'Scan ok; '; + $ar = yaz_scan_result($id, &$options); + while(list($key,$val)=each($options)) { + echo "$key = $val "; + } + echo '<br><table><tr><td>'; + while(list($key,list($k, $term, $tcount))=each($ar)) { + if (empty($k)) continue; + echo "<tr><td>$term</td><td>"; + echo $tcount; + echo "</td></tr>"; + } + echo '</table>'; + } else { + echo "Scan failed. Error: " . yaz_error($id) . "<br>"; + } + } + </programlisting> + </example> </para> </refsect1> </refentry> + <refentry id="function.yaz-scan-result"> + <refnamediv> + <refname>yaz_scan_result</refname> + <refpurpose>Returns Scan Response result</refpurpose> + </refnamediv> + <refsect1> + <title>Description</title> + <funcsynopsis> + <funcprototype> + <funcdef>array <function>yaz_scan_result</function></funcdef> + <paramdef>int <parameter>id</parameter></paramdef> + <paramdef>array & + <parameter><optional>result</optional></parameter> + </paramdef> + </funcprototype> + </funcsynopsis> + <para> + Given a target ID this function returns and array with terms as received + from the target in the last Scan Response. + This function returns an array (0..n-1) where n is the number + of terms returned. Each value is a pair where first item is + term, second item is result-count. + If the <parameter>result</parameter> is given it will be modified to hold + additional information taken from the Scan Response: + <literal>number</literal> (number of entries returned), + <literal>stepsize</literal> (Step-size), + <literal>position</literal> (position of term), + <literal>status</literal> (Scan Status). + </para> + </refsect1> + </refentry> + + <refentry id="function.yaz-ccl-conf"> + <refnamediv> + <refname>yaz_ccl_conf</refname> + <refpurpose>Configure CCL parser</refpurpose> + </refnamediv> + <refsect1> + <title>Description</title> + <funcsynopsis> + <funcprototype> + <funcdef>int <function>yaz_ccl_conf</function></funcdef> + <paramdef>int <parameter>id</parameter></paramdef> + <paramdef>array <parameter>config</parameter></paramdef> + </funcprototype> + </funcsynopsis> + <para> + This function configures the CCL query parser for a target + with definitions of access points (CCL qualifiers) and their + mapping to RPN. To map a specific CCL query to RPN afterwards + call the <function>yaz_ccl_parse</function> function. + Each index of the array <parameter>config</parameter> is the + name of a CCL field and the corresponding value holds a string + that specifies a mapping to RPN. + The mapping is a sequence of attribute-type, attribute-value + pairs. Attribute-type and attribute-value is separated by an equal + sign (<literal>=</literal>). Each pair is separated by white space. + </para> + <para> + <example> + <title>CCL configuration</title> + <simpara> + In the example below, the CCL parser is configured to support + three CCL fields: <literal>ti</literal>, <literal>au</literal> and + <literal>isbn</literal>. Each field is mapped to their BIB-1 + equivalent. It is assumed that variable <literal>$id</literal> is a + target ID. + </simpara> + <programlisting> + $field["ti"] = "1=4"; + $field["au"] = "1=1"; + $field["isbn"] = "1=7"; + yaz_ccl_conf($id,$field); + </programlisting> + </example> + </para> + </refsect1> + </refentry> + + <refentry id="function.yaz-ccl-parse"> + <refnamediv> + <refname>yaz_ccl_parse</refname> + <refpurpose>Invoke CCL Parser</refpurpose> + </refnamediv> + <refsect1> + <title>Description</title> + <funcsynopsis> + <funcprototype> + <funcdef>int <function>yaz_ccl_parse</function></funcdef> + <paramdef>int <parameter>id</parameter></paramdef> + <paramdef>string <parameter>query</parameter></paramdef> + <paramdef>array &<parameter>result</parameter></paramdef> + </funcprototype> + </funcsynopsis> + <para> + This function invokes the CCL parser. It converts a given + CCL FIND query to an RPN query which may be passed to the + <function>yaz_search</function> function to perform a search. + To define a set of valid CCL fields call + <function>yaz_ccl_conf</function> prior to this function. + If the supplied <parameter>query</parameter> was successfully + converted to RPN, this function returns true, and the index + <literal>rpn</literal> of the supplied array + <parameter>result</parameter> holds a valid RPN query. + If the query could not be converted (because of invalid syntax, + unknown field, etc.) this function returns false and three + indexes are set in the resulting array to indicate the cause + of failure: <literal>errorcode</literal>CCL error code (integer), + <literal>errorstring</literal>CCL error string, and + <literal>errorpos</literal>approximate position in query of failure + (integer is character position). + </para> + </refsect1> + </refentry> + + <refentry id="function.yaz-itemorder"> + <refnamediv> + <refname>yaz_itemorder</refname> + <refpurpose> + Prepares for Z39.50 Item Order with an ILL-Request package + </refpurpose> + </refnamediv> + <refsect1> + <title>Description</title> + <funcsynopsis> + <funcprototype> + <funcdef>int <function>yaz_itemorder</function></funcdef> + <paramdef>array <parameter>args</parameter></paramdef> + </funcprototype> + </funcsynopsis> + <para> + This function prepares for an Extended Services request using the + Profile for the Use of Z39.50 Item Order Extended Service to + Transport ILL (Profile/1). See + <ulink url="http://www.nlc-bnc.ca/iso/ill/stanprf.htm">this</ulink> + and the + <ulink url="http://www.nlc-bnc.ca/iso/ill/document/standard/z-ill-1a.pdf"> + specification</ulink>. + The args parameter must be a hash array with information about the + Item Order request to be sent. The key of the hash is the name + of the corresponding ASN.1 tag path. For example, the ISBN below + the Item-ID has the key item-id,ISBN. + </para> + <para> + The ILL-Request parameters are: + </para> + <literallayout> +protocol-version-num +transaction-id,initial-requester-id,person-or-institution-symbol,person +transaction-id,initial-requester-id,person-or-institution-symbol,institution +transaction-id,initial-requester-id,name-of-person-or-institution,name-of-person +transaction-id,initial-requester-id,name-of-person-or-institution,name-of-institution +transaction-id,transaction-group-qualifier +transaction-id,transaction-qualifier +transaction-id,sub-transaction-qualifier +service-date-time,this,date +service-date-time,this,time +service-date-time,original,date +service-date-time,original,time +requester-id,person-or-institution-symbol,person +requester-id,person-or-institution-symbol,institution +requester-id,name-of-person-or-institution,name-of-person +requester-id,name-of-person-or-institution,name-of-institution +responder-id,person-or-institution-symbol,person +responder-id,person-or-institution-symbol,institution +responder-id,name-of-person-or-institution,name-of-person +responder-id,name-of-person-or-institution,name-of-institution +transaction-type +delivery-address,postal-address,name-of-person-or-institution,name-of-person +delivery-address,postal-address,name-of-person-or-institution,name-of-institution +delivery-address,postal-address,extended-postal-delivery-address +delivery-address,postal-address,street-and-number +delivery-address,postal-address,post-office-box +delivery-address,postal-address,city +delivery-address,postal-address,region +delivery-address,postal-address,country +delivery-address,postal-address,postal-code +delivery-address,electronic-address,telecom-service-identifier +delivery-address,electronic-address,telecom-service-addreess +billing-address,postal-address,name-of-person-or-institution,name-of-person +billing-address,postal-address,name-of-person-or-institution,name-of-institution +billing-address,postal-address,extended-postal-delivery-address +billing-address,postal-address,street-and-number +billing-address,postal-address,post-office-box +billing-address,postal-address,city +billing-address,postal-address,region +billing-address,postal-address,country +billing-address,postal-address,postal-code +billing-address,electronic-address,telecom-service-identifier +billing-address,electronic-address,telecom-service-addreess +ill-service-type +requester-optional-messages,can-send-RECEIVED +requester-optional-messages,can-send-RETURNED +requester-optional-messages,requester-SHIPPED +requester-optional-messages,requester-CHECKED-IN +search-type,level-of-service +search-type,need-before-date +search-type,expiry-date +search-type,expiry-flag +place-on-hold +client-id,client-name +client-id,client-status +client-id,client-identifier +item-id,item-type +item-id,call-number +item-id,author +item-id,title +item-id,sub-title +item-id,sponsoring-body +item-id,place-of-publication +item-id,publisher +item-id,series-title-number +item-id,volume-issue +item-id,edition +item-id,publication-date +item-id,publication-date-of-component +item-id,author-of-article +item-id,title-of-article +item-id,pagination +item-id,ISBN +item-id,ISSN +item-id,additional-no-letters +item-id,verification-reference-source +copyright-complicance +retry-flag +forward-flag +requester-note +forward-note + </literallayout> + <para> + There are also a few parameters that are part of the Extended + Services Request package and the ItemOrder package: + </para> + <literallayout> +package-name +user-id +contact-name +contact-phone +contact-email +itemorder-item + </literallayout> + </refsect1> + </refentry> + <refentry id="function.yaz-wait"> <refnamediv> <refname>yaz_wait</refname> - <refpurpose>Executes queries</refpurpose> + <refpurpose>Wait for Z39.50 requests to complete</refpurpose> </refnamediv> <refsect1> <title>Description</title> <funcsynopsis> <funcprototype> <funcdef>int <function>yaz_wait</function></funcdef> - <paramdef>int <parameter>id</parameter></paramdef> - <paramdef>string <parameter>syntax</parameter></paramdef> + <void></void> </funcprototype> </funcsynopsis> <para> This function carries out networked (blocked) activity for outstanding requests which have been prepared by the functions <function>yaz_connect</function>, - <function>yaz_search</function>. <function>yaz_wait</function> - returns when all targets have either completed all requests or - otherwise completed (in case of errors). + <function>yaz_search</function>, <function>yaz_present</function>, + <function>yaz_scan</function> and <function>yaz_itemorder</function>. + <function>yaz_wait</function> returns when all targets have either + completed all requests or aborted (in case of errors). </para> </refsect1> </refentry>