X-Git-Url: http://git.indexdata.com/?p=yaz-moved-to-github.git;a=blobdiff_plain;f=client%2Fclient.c;h=f0416fef1d71ca27164c5738634480019b7ae30b;hp=f85f0e1da51a660908ad6f3f68887a72f71816fb;hb=b7e0b586c47f8cc0c99f0195e0b554dc5f1af072;hpb=fb6d99a0c7e07d9cc4a315c447deaf6564a85505 diff --git a/client/client.c b/client/client.c index f85f0e1..f0416fe 100644 --- a/client/client.c +++ b/client/client.c @@ -1,8 +1,11 @@ /* - * Copyright (C) 1995-2005, Index Data ApS + * Copyright (C) 1995-2007, Index Data ApS * See the file LICENSE for details. * - * $Id: client.c,v 1.292 2005-06-25 15:46:01 adam Exp $ + * $Id: client.c,v 1.327 2007-01-24 23:10:01 adam Exp $ + */ +/** \file client.c + * \brief yaz-client program */ #include @@ -10,6 +13,9 @@ #include #include #include +#ifndef WIN32 +#include +#endif #if HAVE_SYS_TYPES_H #include #endif @@ -75,14 +81,18 @@ #include "admin.h" #include "tabcomplete.h" +#include "fhistory.h" #define C_PROMPT "Z> " +static file_history_t file_history = 0; + +static char *sru_method = "soap"; static char *codeset = 0; /* character set for output */ static int hex_dump = 0; static char *dump_file_prefix = 0; static ODR out, in, print; /* encoding and decoding streams */ -#if HAVE_XML2 +#if YAZ_HAVE_XML2 static ODR srw_sr_odr_out = 0; static Z_SRW_PDU *srw_sr = 0; #endif @@ -100,7 +110,10 @@ static int mediumSetPresentNumber = 0; static Z_ElementSetNames *elementSetNames = 0; static int setno = 1; /* current set offset */ static enum oid_proto protocol = PROTO_Z3950; /* current app protocol */ -static enum oid_value recordsyntax = VAL_USMARC; +#define RECORDSYNTAX_MAX 20 +static enum oid_value recordsyntax_list[RECORDSYNTAX_MAX] = { VAL_USMARC }; +static int recordsyntax_size = 1; + static char *record_schema = 0; static int sent_close = 0; static NMEM session_mem = NULL; /* memory handle for init-response */ @@ -119,7 +132,6 @@ static int negotiationCharsetVersion = 3; static char *outputCharset = 0; static char *marcCharset = 0; static char* yazLang = 0; -static char* http_version = "1.1"; static char last_cmd[32] = "?"; static FILE *marc_file = 0; @@ -133,6 +145,7 @@ static int scan_stepSize = 0; static int scan_position = 1; static int scan_size = 20; static char cur_host[200]; +static int last_hit_count = 0; typedef enum { QueryType_Prefix, @@ -165,14 +178,15 @@ struct { void process_cmd_line(char* line); -char ** readline_completer(char *text, int start, int end); -char *command_generator(const char *text, int state); -char** curret_global_list=NULL; +#if HAVE_READLINE_READLINE_H +char **readline_completer(char *text, int start, int end); +#endif +static char *command_generator(const char *text, int state); int cmd_register_tab(const char* arg); static void close_session (void); -ODR getODROutputStream() +ODR getODROutputStream(void) { return out; } @@ -337,12 +351,10 @@ static void send_initRequest(const char* type_and_host) p0->which = Z_OtherInfo_externallyDefinedInfo; p0->information.externallyDefinedInfo = - yaz_set_proposal_charneg( - out, - (const char**)&negotiationCharset, - negotiationCharset ? 1 : 0, - (const char**)&yazLang, yazLang ? 1 : 0, - negotiationCharsetRecords); + yaz_set_proposal_charneg_list(out, ",", + negotiationCharset, + yazLang, + negotiationCharsetRecords); } } @@ -405,7 +417,7 @@ static int process_initResponse(Z_InitResponse *res) if (oid->value == VAL_OCLCUI) { Z_OCLC_UserInformation *oclc_ui; ODR decode = odr_createmem(ODR_DECODE); - odr_setbuf(decode, sat->buf, sat->len, 0); + odr_setbuf(decode, (char *) sat->buf, sat->len, 0); if (!z_OCLC_UserInformation(decode, &oclc_ui, 0, 0)) printf ("Bad OCLC UserInformation:\n"); else @@ -680,10 +692,10 @@ int session_connect(const char *arg) conn = cs_create_host(arg, 1, &add); if (!conn) { - printf ("Couldn't create comstack\n"); + printf ("Could not resolve address %s\n", arg); return 0; } -#if HAVE_XML2 +#if YAZ_HAVE_XML2 if (conn->protocol == PROTO_HTTP) queryType = QueryType_CQL; #else @@ -705,12 +717,6 @@ int session_connect(const char *arg) if (cs_connect(conn, add) < 0) { printf ("error = %s\n", cs_strerror(conn)); - if (conn->cerrno == CSYSERR) - { - char msg[256]; - yaz_strerror(msg, sizeof(msg)); - printf ("%s\n", msg); - } cs_close(conn); conn = 0; return 0; @@ -760,7 +766,7 @@ int cmd_open(const char *arg) return session_connect(cur_host); } -void try_reconnect() +void try_reconnect(void) { char* open_command; @@ -878,7 +884,7 @@ static void display_record(Z_External *r) if (!(*type->fun)(in, &rr, 0, 0)) { odr_perror(in, "Decoding constructed record."); - fprintf(stdout, "[Near %d]\n", odr_offset(in)); + fprintf(stdout, "[Near %ld]\n", (long) odr_offset(in)); fprintf(stdout, "Packet dump:\n---------\n"); odr_dumpBER(stdout, (char*)r->u.octet_aligned->buf, r->u.octet_aligned->len); @@ -936,62 +942,41 @@ static void display_record(Z_External *r) int rlen; yaz_iconv_t cd = 0; yaz_marc_t mt = yaz_marc_create(); - - if (yaz_marc_decode_buf(mt, octet_buf,r->u.octet_aligned->len, - &result, &rlen)> 0) + const char *from = 0; + + if (marcCharset && !strcmp(marcCharset, "auto")) { - char *from = 0; - if (marcCharset && !strcmp(marcCharset, "auto")) + if (ent->value == VAL_USMARC) { - if (ent->value == VAL_USMARC) - { - if (octet_buf[9] == 'a') - from = "UTF-8"; - else - from = "MARC-8"; - } + if (octet_buf[9] == 'a') + from = "UTF-8"; else - from = "ISO-8859-1"; - } - else if (marcCharset) - from = marcCharset; - if (outputCharset && from) - { - cd = yaz_iconv_open(outputCharset, from); - printf ("convert from %s to %s", from, - outputCharset); - if (!cd) - printf (" unsupported\n"); - else - printf ("\n"); + from = "MARC-8"; } + else + from = "ISO-8859-1"; + } + else if (marcCharset) + from = marcCharset; + if (outputCharset && from) + { + cd = yaz_iconv_open(outputCharset, from); + printf ("convert from %s to %s", from, + outputCharset); if (!cd) - fwrite (result, 1, rlen, stdout); + printf (" unsupported\n"); else { - char outbuf[6]; - size_t inbytesleft = rlen; - const char *inp = result; - - while (inbytesleft) - { - size_t outbytesleft = sizeof(outbuf); - char *outp = outbuf; - size_t r; - - r = yaz_iconv (cd, (char**) &inp, - &inbytesleft, - &outp, &outbytesleft); - if (r == (size_t) (-1)) - { - int e = yaz_iconv_error(cd); - if (e != YAZ_ICONV_E2BIG) - break; - } - fwrite (outbuf, outp - outbuf, 1, stdout); - } + yaz_marc_iconv(mt, cd); + printf ("\n"); } } + + if (yaz_marc_decode_buf(mt, octet_buf,r->u.octet_aligned->len, + &result, &rlen)> 0) + { + fwrite (result, rlen, 1, stdout); + } else { printf ("bad MARC. Dumping as it is:\n"); @@ -1251,74 +1236,32 @@ static int send_deleteResultSetRequest(const char *arg) return 2; } -#if HAVE_XML2 +#if YAZ_HAVE_XML2 static int send_srw(Z_SRW_PDU *sr) { const char *charset = negotiationCharset; const char *host_port = cur_host; - char *path = 0; - char ctype[50]; - Z_SOAP_Handler h[2] = { - {"http://www.loc.gov/zing/srw/", 0, (Z_SOAP_fun) yaz_srw_codec}, - {0, 0, 0} - }; - ODR o = odr_createmem(ODR_ENCODE); - int ret; - Z_SOAP *p = odr_malloc(o, sizeof(*p)); Z_GDU *gdu; + char *path = 0; - path = odr_malloc(out, strlen(databaseNames[0])+2); + path = odr_malloc(out, 2+strlen(databaseNames[0])); *path = '/'; strcpy(path+1, databaseNames[0]); - gdu = z_get_HTTP_Request(out); - gdu->u.HTTP_Request->version = http_version; - gdu->u.HTTP_Request->path = odr_strdup(out, path); + gdu = z_get_HTTP_Request_host_path(out, host_port, path); - if (host_port) + if (!strcmp(sru_method, "get")) { - const char *cp0 = strstr(host_port, "://"); - const char *cp1 = 0; - if (cp0) - cp0 = cp0+3; - else - cp0 = host_port; - - cp1 = strchr(cp0, '/'); - if (!cp1) - cp1 = cp0+strlen(cp0); - - if (cp0 && cp1) - { - char *h = odr_malloc(out, cp1 - cp0 + 1); - memcpy (h, cp0, cp1 - cp0); - h[cp1-cp0] = '\0'; - z_HTTP_header_add(out, &gdu->u.HTTP_Request->headers, - "Host", h); - } + yaz_sru_get_encode(gdu->u.HTTP_Request, sr, out, charset); } - - strcpy(ctype, "text/xml"); - if (charset && strlen(charset) < 20) + else if (!strcmp(sru_method, "post")) { - strcat(ctype, "; charset="); - strcat(ctype, charset); + yaz_sru_post_encode(gdu->u.HTTP_Request, sr, out, charset); + } + else if (!strcmp(sru_method, "soap")) + { + yaz_sru_soap_encode(gdu->u.HTTP_Request, sr, out, charset); } - z_HTTP_header_add(out, &gdu->u.HTTP_Request->headers, - "Content-Type", ctype); - z_HTTP_header_add(out, &gdu->u.HTTP_Request->headers, - "SOAPAction", "\"\""); - p->which = Z_SOAP_generic; - p->u.generic = odr_malloc(o, sizeof(*p->u.generic)); - p->u.generic->no = 0; - p->u.generic->ns = 0; - p->u.generic->p = sr; - p->ns = "http://schemas.xmlsoap.org/soap/envelope/"; - - ret = z_soap_codec_enc(o, &p, - &gdu->u.HTTP_Request->content_buf, - &gdu->u.HTTP_Request->content_len, h, - charset); if (z_GDU(out, &gdu, 0, 0)) { @@ -1329,7 +1272,7 @@ static int send_srw(Z_SRW_PDU *sr) if (apdu_file) { if (!z_GDU(print, &gdu, 0, 0)) - printf ("Failed to print outgoing APDU\n"); + printf ("Failed to print outgoing SRU package\n"); odr_reset(print); } buf_out = odr_getbuf(out, &len_out, 0); @@ -1340,8 +1283,6 @@ static int send_srw(Z_SRW_PDU *sr) r = cs_put(conn, buf_out, len_out); - odr_destroy(o); - if (r >= 0) return 2; } @@ -1349,7 +1290,7 @@ static int send_srw(Z_SRW_PDU *sr) } #endif -#if HAVE_XML2 +#if YAZ_HAVE_XML2 static char *encode_SRW_term(ODR o, const char *q) { const char *in_charset = "ISO-8859-1"; @@ -1447,8 +1388,8 @@ static int send_SRW_searchRequest(const char *arg) if (record_schema) sr->u.request->recordSchema = record_schema; - if (recordsyntax == VAL_TEXT_XML) - sr->u.explain_request->recordPacking = "xml"; + if (recordsyntax_size == 1 && recordsyntax_list[0] == VAL_TEXT_XML) + sr->u.request->recordPacking = "xml"; return send_srw(sr); } #endif @@ -1526,9 +1467,9 @@ static int send_searchRequest(const char *arg) if (smallSetUpperBound > 0 || (largeSetLowerBound > 1 && mediumSetPresentNumber > 0)) { - req->preferredRecordSyntax = - yaz_oidval_to_z3950oid(out, CLASS_RECSYN, recordsyntax); - + if (recordsyntax_size > 0) + req->preferredRecordSyntax = + yaz_oidval_to_z3950oid(out, CLASS_RECSYN, recordsyntax_list[0]); req->smallSetElementSetNames = req->mediumSetElementSetNames = elementSetNames; } @@ -1548,7 +1489,8 @@ static int send_searchRequest(const char *arg) const char *pqf_msg; size_t off; int code = yaz_pqf_error (pqf_parser, &pqf_msg, &off); - printf("%*s^\n", off+4, ""); + int ioff = off; + printf("%*s^\n", ioff+4, ""); printf("Prefix query error: %s (code %d)\n", pqf_msg, code); yaz_pqf_destroy (pqf_parser); @@ -1595,10 +1537,11 @@ static int send_searchRequest(const char *arg) } /* display Query Expression as part of searchResult-1 */ -static void display_queryExpression (Z_QueryExpression *qe) +static void display_queryExpression (const char *lead, Z_QueryExpression *qe) { if (!qe) return; + printf(" %s=", lead); if (qe->which == Z_QueryExpression_term) { if (qe->u.term->queryTerm) @@ -1607,16 +1550,16 @@ static void display_queryExpression (Z_QueryExpression *qe) switch (term->which) { case Z_Term_general: - printf (" %.*s", term->u.general->len, term->u.general->buf); + printf ("%.*s", term->u.general->len, term->u.general->buf); break; case Z_Term_characterString: - printf (" %s", term->u.characterString); + printf ("%s", term->u.characterString); break; case Z_Term_numeric: - printf (" %d", *term->u.numeric); + printf ("%d", *term->u.numeric); break; case Z_Term_null: - printf (" null"); + printf ("null"); break; } } @@ -1642,16 +1585,20 @@ static void display_searchResult (Z_OtherInformation *o) printf ("SearchResult-1:"); for (j = 0; j < sr->num; j++) { + if (j) + printf(","); if (!sr->elements[j]->subqueryExpression) - printf (" %d", j); - display_queryExpression ( + printf("%d", j); + display_queryExpression("term", sr->elements[j]->subqueryExpression); - display_queryExpression ( + display_queryExpression("interpretation", sr->elements[j]->subqueryInterpretation); - display_queryExpression ( + display_queryExpression("recommendation", sr->elements[j]->subqueryRecommendation); if (sr->elements[j]->subqueryCount) - printf ("(%d)", *sr->elements[j]->subqueryCount); + printf(" cnt=%d", *sr->elements[j]->subqueryCount); + if (sr->elements[j]->subqueryId) + printf(" id=%s ", sr->elements[j]->subqueryId); } printf ("\n"); } @@ -1668,9 +1615,28 @@ static int process_searchResponse(Z_SearchResponse *res) else printf("Search was a bloomin' failure.\n"); printf("Number of hits: %d", *res->resultCount); + last_hit_count = *res->resultCount; if (setnumber >= 0) printf (", setno %d", setnumber); - printf ("\n"); + putchar('\n'); + if (res->resultSetStatus) + { + printf("Result Set Status: "); + switch(*res->resultSetStatus) + { + case Z_SearchResponse_subset: + printf("subset"); break; + case Z_SearchResponse_interim: + printf("interim"); break; + case Z_SearchResponse_none: + printf("none"); break; + case Z_SearchResponse_estimate: + printf("estimate"); break; + default: + printf("%d", *res->resultSetStatus); + } + putchar('\n'); + } display_searchResult (res->additionalSearchInfo); printf("records returned: %d\n", *res->numberOfRecordsReturned); @@ -1922,7 +1888,7 @@ const char *get_ill_element (void *clientData, const char *element) return 0; } -static Z_External *create_external_itemRequest() +static Z_External *create_external_itemRequest(void) { struct ill_get_ctl ctl; ILL_ItemRequest *req; @@ -2118,6 +2084,9 @@ static int send_itemorder(const char *type, int itemno) Z_ExtendedServicesRequest *req = apdu->u.extendedServicesRequest; oident ItemOrderRequest; + + req->referenceId = set_refid (out); + ItemOrderRequest.proto = PROTO_Z3950; ItemOrderRequest.oclass = CLASS_EXTSERV; ItemOrderRequest.value = VAL_ITEMORDER; @@ -2130,8 +2099,13 @@ static int send_itemorder(const char *type, int itemno) return 0; } -static int only_z3950() +static int only_z3950(void) { + if (!conn) + { + printf ("Not connected yet\n"); + return 1; + } if (protocol == PROTO_HTTP) { printf ("Not supported by SRW\n"); @@ -2152,20 +2126,20 @@ static int cmd_update0(const char *arg) return cmd_update_common(arg, 0); } +static int cmd_update_Z3950(int version, int action_no, const char *recid, + char *rec_buf, int rec_len); + +static int cmd_update_SRW(int action_no, const char *recid, + char *rec_buf, int rec_len); + static int cmd_update_common(const char *arg, int version) { - Z_APDU *apdu = zget_APDU(out, Z_APDU_extendedServicesRequest ); - Z_ExtendedServicesRequest *req = apdu->u.extendedServicesRequest; - Z_External *r; char action[20], recid[20]; char *rec_buf; int rec_len; int action_no; int noread = 0; - Z_External *record_this = 0; - if (only_z3950()) - return 0; *action = 0; *recid = 0; sscanf (arg, "%19s %19s%n", action, recid, &noread); @@ -2196,6 +2170,60 @@ static int cmd_update_common(const char *arg, int version) if (parse_cmd_doc(&arg, out, &rec_buf, &rec_len, 1) == 0) return 0; +#if YAZ_HAVE_XML2 + if (protocol == PROTO_HTTP) + return cmd_update_SRW(action_no, recid, rec_buf, rec_len); +#endif + return cmd_update_Z3950(version, action_no, recid, rec_buf, rec_len); +} + +#if YAZ_HAVE_XML2 +static int cmd_update_SRW(int action_no, const char *recid, + char *rec_buf, int rec_len) +{ + if (!conn) + cmd_open(0); + if (!conn) + return 0; + else + { + Z_SRW_PDU *srw = yaz_srw_get(out, Z_SRW_update_request); + Z_SRW_updateRequest *sr = srw->u.update_request; + + switch(action_no) + { + case Z_IUOriginPartToKeep_recordInsert: + sr->operation = "info:srw/action/1/create"; + break; + case Z_IUOriginPartToKeep_recordReplace: + sr->operation = "info:srw/action/1/replace"; + break; + case Z_IUOriginPartToKeep_recordDelete: + sr->operation = "info:srw/action/1/delete"; + break; + } + if (rec_buf) + { + sr->record = yaz_srw_get_record(out); + sr->record->recordData_buf = rec_buf; + sr->record->recordData_len = rec_len; + sr->record->recordSchema = record_schema; + } + if (recid) + sr->recordId = odr_strdup(out, recid); + return send_srw(srw); + } +} +#endif + +static int cmd_update_Z3950(int version, int action_no, const char *recid, + char *rec_buf, int rec_len) +{ + Z_APDU *apdu = zget_APDU(out, Z_APDU_extendedServicesRequest ); + Z_ExtendedServicesRequest *req = apdu->u.extendedServicesRequest; + Z_External *r; + Z_External *record_this = 0; + if (rec_buf) record_this = z_ext_record (out, VAL_TEXT_XML, rec_buf, rec_len); else @@ -2249,7 +2277,7 @@ static int cmd_update_common(const char *arg, int version) notToKeep->elements[0] = (Z_IU0SuppliedRecords_elem *) odr_malloc(out, sizeof(**notToKeep->elements)); notToKeep->elements[0]->which = Z_IUSuppliedRecords_elem_opaque; - if (*recid) + if (*recid && strcmp(recid, "none")) { notToKeep->elements[0]->u.opaque = (Odr_oct *) odr_malloc (out, sizeof(Odr_oct)); @@ -2313,43 +2341,55 @@ static int cmd_update_common(const char *arg, int version) static int cmd_xmles(const char *arg) { - int noread = 0; - char oid_str[51]; - int oid_value_xmles = VAL_XMLES; - Z_APDU *apdu = zget_APDU(out, Z_APDU_extendedServicesRequest); - Z_ExtendedServicesRequest *req = apdu->u.extendedServicesRequest; - - Z_External *ext = (Z_External *) odr_malloc(out, sizeof(*ext)); - req->taskSpecificParameters = ext; - ext->descriptor = 0; - ext->which = Z_External_octet; - ext->u.single_ASN1_type = (Odr_oct *) odr_malloc (out, sizeof(Odr_oct)); - - sscanf(arg, "%50s%n", oid_str, &noread); - if (noread == 0) - { - printf("Missing OID for xmles\n"); - return 0; - } - arg += noread; - oid_value_xmles = oid_getvalbyname(oid_str); - if (oid_value_xmles == VAL_NONE) + if (only_z3950()) + return 1; + else { - printf("Bad OID: %s\n", oid_str); - return 0; - } + char *asn_buf = 0; + int noread = 0; + char oid_str[51]; + int oid_value_xmles = VAL_XMLES; + Z_APDU *apdu = zget_APDU(out, Z_APDU_extendedServicesRequest); + Z_ExtendedServicesRequest *req = apdu->u.extendedServicesRequest; + - if (parse_cmd_doc(&arg, out, (char **) &ext->u.single_ASN1_type->buf, - &ext->u.single_ASN1_type->len, 0) == 0) - return 0; - req->packageType = yaz_oidval_to_z3950oid(out, CLASS_EXTSERV, - oid_value_xmles); + Z_External *ext = (Z_External *) odr_malloc(out, sizeof(*ext)); + + req->referenceId = set_refid (out); + req->taskSpecificParameters = ext; + ext->indirect_reference = 0; + ext->descriptor = 0; + ext->which = Z_External_octet; + ext->u.single_ASN1_type = (Odr_oct *) odr_malloc (out, sizeof(Odr_oct)); + sscanf(arg, "%50s%n", oid_str, &noread); + if (noread == 0) + { + printf("Missing OID for xmles\n"); + return 0; + } + arg += noread; + oid_value_xmles = oid_getvalbyname(oid_str); + if (oid_value_xmles == VAL_NONE) + { + printf("Bad OID: %s\n", oid_str); + return 0; + } - ext->direct_reference = yaz_oidval_to_z3950oid(out, CLASS_EXTSERV, - oid_value_xmles); - send_apdu(apdu); + if (parse_cmd_doc(&arg, out, &asn_buf, + &ext->u.single_ASN1_type->len, 0) == 0) + return 0; - return 2; + ext->u.single_ASN1_type->buf = (unsigned char *) asn_buf; + + req->packageType = yaz_oidval_to_z3950oid(out, CLASS_EXTSERV, + oid_value_xmles); + + ext->direct_reference = yaz_oidval_to_z3950oid(out, CLASS_EXTSERV, + oid_value_xmles); + send_apdu(apdu); + + return 2; + } } static int cmd_itemorder(const char *arg) @@ -2358,7 +2398,7 @@ static int cmd_itemorder(const char *arg) int itemno; if (only_z3950()) - return 0; + return 1; if (sscanf (arg, "%10s %d", type, &itemno) != 2) return 0; @@ -2404,7 +2444,7 @@ static int cmd_explain(const char *arg) { if (protocol != PROTO_HTTP) return 0; -#if HAVE_XML2 +#if YAZ_HAVE_XML2 if (!conn) cmd_open(0); if (conn) @@ -2415,7 +2455,7 @@ static int cmd_explain(const char *arg) /* save this for later .. when fetching individual records */ sr = yaz_srw_get(out, Z_SRW_explain_request); - if (recordsyntax == VAL_TEXT_XML) + if (recordsyntax_size > 0 && recordsyntax_list[0] == VAL_TEXT_XML) sr->u.explain_request->recordPacking = "xml"; send_srw(sr); return 2; @@ -2431,12 +2471,35 @@ static int cmd_init(const char *arg) strncpy (cur_host, arg, sizeof(cur_host)-1); cur_host[sizeof(cur_host)-1] = 0; } - if (!conn || protocol != PROTO_Z3950) - return 0; + if (only_z3950()) + return 1; send_initRequest(cur_host); return 2; } +static int cmd_sru(const char *arg) +{ + if (!*arg) + { + printf("SRU method is: %s\n", sru_method); + } + else + { + if (!yaz_matchstr(arg, "post")) + sru_method = "post"; + else if (!yaz_matchstr(arg, "get")) + sru_method = "get"; + else if (!yaz_matchstr(arg, "soap")) + sru_method = "soap"; + else + { + printf("Unknown SRU method: %s\n", arg); + printf("Specify one of POST, GET, SOAP\n"); + } + } + return 0; +} + static int cmd_find(const char *arg) { if (!*arg) @@ -2446,7 +2509,7 @@ static int cmd_find(const char *arg) } if (protocol == PROTO_HTTP) { -#if HAVE_XML2 +#if YAZ_HAVE_XML2 if (!conn) cmd_open(0); if (!conn) @@ -2476,11 +2539,6 @@ static int cmd_find(const char *arg) static int cmd_delete(const char *arg) { - if (!conn) - { - printf("Not connected yet\n"); - return 0; - } if (only_z3950()) return 0; if (!send_deleteResultSetRequest(arg)) @@ -2497,8 +2555,6 @@ static int cmd_ssub(const char *arg) static int cmd_lslb(const char *arg) { - if (only_z3950()) - return 0; if (!(largeSetLowerBound = atoi(arg))) return 0; return 1; @@ -2506,8 +2562,6 @@ static int cmd_lslb(const char *arg) static int cmd_mspn(const char *arg) { - if (only_z3950()) - return 0; if (!(mediumSetPresentNumber = atoi(arg))) return 0; return 1; @@ -2556,7 +2610,15 @@ static void parse_show_args(const char *arg_c, char *setstring, *p = '\0'; } if (*arg) - *start = atoi(arg); + { + if (!strcmp(arg, "all")) + { + *number = last_hit_count; + *start = 1; + } + else + *start = atoi(arg); + } if (p && (p=strchr(p+1, '+'))) strcpy (setstring, p+1); else if (setnumber >= 0) @@ -2582,10 +2644,11 @@ static int send_presentRequest(const char *arg) req->resultSetStartPoint = &setno; req->numberOfRecordsRequested = &nos; - req->preferredRecordSyntax = - yaz_oidval_to_z3950oid(out, CLASS_RECSYN, recordsyntax); + if (recordsyntax_size == 1) + req->preferredRecordSyntax = + yaz_oidval_to_z3950oid(out, CLASS_RECSYN, recordsyntax_list[0]); - if (record_schema) + if (record_schema || recordsyntax_size >= 2) { req->recordComposition = &compo; compo.which = Z_RecordComp_complex; @@ -2597,16 +2660,21 @@ static int send_presentRequest(const char *arg) compo.u.complex->generic = (Z_Specification *) odr_malloc(out, sizeof(*compo.u.complex->generic)); + compo.u.complex->generic->which = Z_Schema_oid; - - compo.u.complex->generic->schema.oid = - yaz_str_to_z3950oid(out, CLASS_SCHEMA, record_schema); - - if (!compo.u.complex->generic->schema.oid) + if (!record_schema) + compo.u.complex->generic->schema.oid = 0; + else { - /* OID wasn't a schema! Try record syntax instead. */ - compo.u.complex->generic->schema.oid = (Odr_oid *) - yaz_str_to_z3950oid(out, CLASS_RECSYN, record_schema); + compo.u.complex->generic->schema.oid = + yaz_str_to_z3950oid(out, CLASS_SCHEMA, record_schema); + + if (!compo.u.complex->generic->schema.oid) + { + /* OID wasn't a schema! Try record syntax instead. */ + compo.u.complex->generic->schema.oid = (Odr_oid *) + yaz_str_to_z3950oid(out, CLASS_RECSYN, record_schema); + } } if (!elementSetNames) compo.u.complex->generic->elementSpec = 0; @@ -2621,8 +2689,22 @@ static int send_presentRequest(const char *arg) } compo.u.complex->num_dbSpecific = 0; compo.u.complex->dbSpecific = 0; - compo.u.complex->num_recordSyntax = 0; - compo.u.complex->recordSyntax = 0; + if (recordsyntax_size >= 2) + { + int i; + compo.u.complex->num_recordSyntax = recordsyntax_size; + compo.u.complex->recordSyntax = (Odr_oid **) + odr_malloc(out, recordsyntax_size * sizeof(Odr_oid*)); + for (i = 0; i < recordsyntax_size; i++) + compo.u.complex->recordSyntax[i] = + yaz_oidval_to_z3950oid(out, CLASS_RECSYN, + recordsyntax_list[i]); + } + else + { + compo.u.complex->num_recordSyntax = 0; + compo.u.complex->recordSyntax = 0; + } } else if (elementSetNames) { @@ -2635,7 +2717,7 @@ static int send_presentRequest(const char *arg) return 2; } -#if HAVE_XML2 +#if YAZ_HAVE_XML2 static int send_SRW_presentRequest(const char *arg) { char setstring[100]; @@ -2649,7 +2731,7 @@ static int send_SRW_presentRequest(const char *arg) sr->u.request->maximumRecords = odr_intdup(out, nos); if (record_schema) sr->u.request->recordSchema = record_schema; - if (recordsyntax == VAL_TEXT_XML) + if (recordsyntax_size == 1 && recordsyntax_list[0] == VAL_TEXT_XML) sr->u.request->recordPacking = "xml"; return send_srw(sr); } @@ -2664,6 +2746,7 @@ static void close_session (void) odr_reset(out); odr_reset(in); odr_reset(print); + last_hit_count = 0; } void process_close(Z_Close *req) @@ -2702,7 +2785,7 @@ static int cmd_show(const char *arg) { if (protocol == PROTO_HTTP) { -#if HAVE_XML2 +#if YAZ_HAVE_XML2 if (!conn) cmd_open(0); if (!conn) @@ -2726,11 +2809,18 @@ static int cmd_show(const char *arg) return 2; } +void exit_client(int code) +{ + file_history_save(file_history); + file_history_destroy(&file_history); + exit(code); +} + int cmd_quit(const char *arg) { printf("See you later, alligator.\n"); xmalloc_trav (""); - exit(0); + exit_client(0); return 0; } @@ -2740,12 +2830,11 @@ int cmd_cancel(const char *arg) Z_TriggerResourceControlRequest *req = apdu->u.triggerResourceControlRequest; bool_t rfalse = 0; - - if (!conn) - { - printf("Session not initialized yet\n"); - return 0; - } + char command[16]; + + *command = '\0'; + sscanf(arg, "%15s", command); + if (only_z3950()) return 0; if (session_initResponse && @@ -2761,7 +2850,9 @@ int cmd_cancel(const char *arg) send_apdu(apdu); printf("Sent cancel request\n"); - return 2; + if (!strcmp(command, "wait")) + return 2; + return 1; } @@ -2811,7 +2902,8 @@ int send_scanrequest(const char *query, int pp, int num, const char *term) const char *pqf_msg; size_t off; int code = yaz_pqf_error (pqf_parser, &pqf_msg, &off); - printf("%*s^\n", off+7, ""); + int ioff = off; + printf("%*s^\n", ioff+7, ""); printf("Prefix query error: %s (code %d)\n", pqf_msg, code); yaz_pqf_destroy (pqf_parser); return -1; @@ -2968,11 +3060,6 @@ void process_deleteResultSetResponse (Z_DeleteResultSetResponse *res) int cmd_sort_generic(const char *arg, int newset) { - if (!conn) - { - printf("Session not initialized yet\n"); - return 0; - } if (only_z3950()) return 0; if (session_initResponse && @@ -3026,7 +3113,7 @@ int cmd_scan(const char *arg) { if (protocol == PROTO_HTTP) { -#if HAVE_XML2 +#if YAZ_HAVE_XML2 if (!conn) cmd_open(0); if (!conn) @@ -3089,19 +3176,32 @@ int cmd_schema(const char *arg) int cmd_format(const char *arg) { - oid_value nsyntax; + const char *cp = arg; + int nor; + int idx = 0; + oid_value nsyntax[RECORDSYNTAX_MAX]; + char form_str[41]; if (!arg || !*arg) { printf("Usage: format \n"); return 0; } - nsyntax = oid_getvalbyname (arg); - if (strcmp(arg, "none") && nsyntax == VAL_NONE) + while (sscanf(cp, "%40s%n", form_str, &nor) >= 1 && nor > 0 + && idx < RECORDSYNTAX_MAX) { - printf ("unknown record syntax\n"); - return 0; + nsyntax[idx] = oid_getvalbyname(form_str); + if (!strcmp(form_str, "none")) + break; + if (nsyntax[idx] == VAL_NONE) + { + printf ("unknown record syntax: %s\n", form_str); + return 0; + } + cp += nor; + idx++; } - recordsyntax = nsyntax; + recordsyntax_size = idx; + memcpy(recordsyntax_list, nsyntax, idx * sizeof(*nsyntax)); return 1; } @@ -3178,11 +3278,8 @@ int cmd_close(const char *arg) { Z_APDU *apdu; Z_Close *req; - if (!conn) - return 0; if (only_z3950()) return 0; - apdu = zget_APDU(out, Z_APDU_close); req = apdu->u.close; *req->closeReason = Z_Close_finished; @@ -3203,8 +3300,8 @@ int cmd_packagename(const char* arg) int cmd_proxy(const char* arg) { - xfree (yazProxy); - yazProxy = NULL; + xfree(yazProxy); + yazProxy = 0; if (*arg) yazProxy = xstrdup (arg); return 1; @@ -3603,28 +3700,28 @@ int cmd_push_command(const char* arg) return 1; } -void source_rcfile() +void source_rcfile(void) { /* Look for a $HOME/.yazclientrc and source it if it exists */ struct stat statbuf; - char buffer[1000]; - char* homedir=getenv("HOME"); + char fname[1000]; + char* homedir = getenv("HOME"); - if( homedir ) { - - sprintf(buffer,"%s/.yazclientrc",homedir); + sprintf(fname, "%.500s%s%s", homedir ? homedir : "", + homedir ? "/" : "", + ".yazclientrc"); - if(stat(buffer,&statbuf)==0) { - cmd_source(buffer, 0 ); - } - - }; - - if(stat(".yazclientrc",&statbuf)==0) { - cmd_source(".yazclientrc", 0 ); - } + if (stat(fname,&statbuf)==0) + cmd_source(fname, 0 ); } +void add_to_readline_history(void *client_data, const char *line) +{ +#if HAVE_READLINE_HISTORY_H + if (strlen(line)) + add_history(line); +#endif +} static void initialize(void) { @@ -3658,13 +3755,15 @@ static void initialize(void) #if HAVE_READLINE_READLINE_H rl_attempted_completion_function = (CPPFunction*)readline_completer; #endif - - - for(i=0; irecordPosition) @@ -3776,16 +3875,15 @@ static void http_response(Z_HTTP_Response *hres) { Z_SOAP *soap_package = 0; ODR o = odr_createmem(ODR_DECODE); - Z_SOAP_Handler soap_handlers[2] = { - {"http://www.loc.gov/zing/srw/", 0, - (Z_SOAP_fun) yaz_srw_codec}, + Z_SOAP_Handler soap_handlers[3] = { + {YAZ_XMLNS_SRU_v1_1, 0, (Z_SOAP_fun) yaz_srw_codec}, + {YAZ_XMLNS_UPDATE_v0_9, 0, (Z_SOAP_fun) yaz_ucp_codec}, {0, 0, 0} }; ret = z_soap_codec(o, &soap_package, &hres->content_buf, &hres->content_len, soap_handlers); - if (!ret && soap_package->which == Z_SOAP_generic && - soap_package->u.generic->no == 0) + if (!ret && soap_package->which == Z_SOAP_generic) { Z_SRW_PDU *sr = soap_package->u.generic->p; if (sr->which == Z_SRW_searchRetrieve_response) @@ -3794,11 +3892,14 @@ static void http_response(Z_HTTP_Response *hres) handle_srw_explain_response(sr->u.explain_response); else if (sr->which == Z_SRW_scan_response) handle_srw_scan_response(sr->u.scan_response); + else if (sr->which == Z_SRW_update_response) + printf("Got update response. Status: %s\n", + sr->u.update_response->operationStatus); else ret = -1; } else if (soap_package && (soap_package->which == Z_SOAP_fault - || soap_package->which == Z_SOAP_error)) + || soap_package->which == Z_SOAP_error)) { printf ("HTTP Error Status=%d\n", hres->code); printf ("SOAP Fault code %s\n", @@ -3810,7 +3911,10 @@ static void http_response(Z_HTTP_Response *hres) soap_package->u.fault->details); } else + { + printf("z_soap_codec failed. (no SOAP error)\n"); ret = -1; + } odr_destroy(o); } if (ret) @@ -3900,7 +4004,7 @@ void wait_and_handle_response(int one_response_only) { FILE *f = ber_file ? ber_file : stdout; odr_perror(in, "Decoding incoming APDU"); - fprintf(f, "[Near %d]\n", odr_offset(in)); + fprintf(f, "[Near %ld]\n", (long) odr_offset(in)); fprintf(f, "Packet dump:\n---------\n"); odr_dumpBER(f, netbuffer, res); fprintf(f, "---------\n"); @@ -3971,7 +4075,7 @@ void wait_and_handle_response(int one_response_only) close_session (); } } -#if HAVE_XML2 +#if YAZ_HAVE_XML2 else if (gdu->which == Z_GDU_HTTP_Response) { http_response(gdu->u.HTTP_Response); @@ -4010,7 +4114,8 @@ int cmd_cclparse(const char* arg) rpn = ccl_find_str (bibset, arg, &error, &pos); if (error) { - printf ("%*s^ - ", 3+strlen(last_cmd)+1+pos, " "); + int ioff = 3+strlen(last_cmd)+1+pos; + printf ("%*s^ - ", ioff, " "); printf ("%s\n", ccl_err_msg (error)); } else @@ -4036,7 +4141,8 @@ int cmd_set_otherinfo(const char* args) int sscan_res; int oidval; - sscan_res = sscanf (args, "%d %100[^ ] %100s", &otherinfoNo, oidstr, otherinfoString); + sscan_res = sscanf (args, "%d %100[^ ] %100s", + &otherinfoNo, oidstr, otherinfoString); if (sscan_res==1) { /* reset this otherinfo */ if(otherinfoNo>=maxOtherInfosSupported) { @@ -4045,7 +4151,8 @@ int cmd_set_otherinfo(const char* args) } extraOtherInfos[otherinfoNo].oidval = -1; if (extraOtherInfos[otherinfoNo].value) - free(extraOtherInfos[otherinfoNo].value); + xfree(extraOtherInfos[otherinfoNo].value); + extraOtherInfos[otherinfoNo].value = 0; return 0; } if (sscan_res<3) { @@ -4054,18 +4161,20 @@ int cmd_set_otherinfo(const char* args) } if (otherinfoNo>=maxOtherInfosSupported) { - printf("Error otherinfo index to large (%d>%d)\n", + printf("Error otherinfo index too large (%d>=%d)\n", otherinfoNo,maxOtherInfosSupported); } oidval = oid_getvalbyname (oidstr); - if (oidval == -1 ) { + if (oidval == VAL_NONE) + { printf("Error in set_otherinfo command unknown oid %s \n",oidstr); return 0; } extraOtherInfos[otherinfoNo].oidval = oidval; - if(extraOtherInfos[otherinfoNo].value) free(extraOtherInfos[otherinfoNo].value); - extraOtherInfos[otherinfoNo].value = strdup(otherinfoString); + if (extraOtherInfos[otherinfoNo].value) + xfree(extraOtherInfos[otherinfoNo].value); + extraOtherInfos[otherinfoNo].value = xstrdup(otherinfoString); return 0; } @@ -4174,7 +4283,10 @@ int cmd_list_all(const char* args) { printf("ssub/lslb/mspn : %d/%d/%d\n",smallSetUpperBound,largeSetLowerBound,mediumSetPresentNumber); /* print present related options */ - printf("Format : %s\n",yaz_z3950_oid_value_to_str(recordsyntax,CLASS_RECSYN)); + printf("Format : %s\n", + (recordsyntax_size > 0) ? + yaz_z3950_oid_value_to_str(recordsyntax_list[0], CLASS_RECSYN) : + "none"); printf("Schema : %s\n",record_schema ? record_schema : "not set"); printf("Elements : %s\n",elementSetNames?elementSetNames->u.generic:""); @@ -4192,25 +4304,26 @@ int cmd_list_all(const char* args) { int cmd_clear_otherinfo(const char* args) { if(strlen(args)>0) { - int otherinfoNo; - otherinfoNo = atoi(args); - if( otherinfoNo >= maxOtherInfosSupported ) { - printf("Error otherinfo index to large (%d>%d)\n",otherinfoNo,maxOtherInfosSupported); + int otherinfoNo = atoi(args); + if (otherinfoNo >= maxOtherInfosSupported) { + printf("Error otherinfo index too large (%d>=%d)\n", + otherinfoNo, maxOtherInfosSupported); return 0; } - - if(extraOtherInfos[otherinfoNo].oidval != -1) { + if (extraOtherInfos[otherinfoNo].oidval != -1) + { /* only clear if set. */ - extraOtherInfos[otherinfoNo].oidval=-1; - free(extraOtherInfos[otherinfoNo].value); + extraOtherInfos[otherinfoNo].oidval = -1; + xfree(extraOtherInfos[otherinfoNo].value); } } else { int i; - - for(i=0; i",complete_attributeset,0,NULL}, {"querytype", cmd_querytype, "",complete_querytype,0,NULL}, {"refid", cmd_refid, "",NULL,0,NULL}, - {"itemorder", cmd_itemorder, "ill|item ",NULL,0,NULL}, + {"itemorder", cmd_itemorder, "ill|item|xml ",NULL,0,NULL}, {"update", cmd_update, " []",NULL,0,NULL}, {"update0", cmd_update0, " []",NULL,0,NULL}, {"xmles", cmd_xmles, " ",NULL,0,NULL}, @@ -4281,6 +4394,7 @@ static struct { {"displaycharset", cmd_displaycharset, "",NULL,0,NULL}, {"marccharset", cmd_marccharset, "",NULL,0,NULL}, {"lang", cmd_lang, "",NULL,0,NULL}, + {"source", cmd_source_echo, "",NULL,1,NULL}, {".", cmd_source_echo, "",NULL,1,NULL}, {"!", cmd_subshell, "Subshell command",NULL,1,NULL}, {"set_apdufile", cmd_set_apdufile, "",NULL,1,NULL}, @@ -4315,6 +4429,8 @@ static struct { {"zversion", cmd_zversion, "", NULL, 0, NULL}, {"help", cmd_help, "", NULL,0,NULL}, {"init", cmd_init, "", NULL,0,NULL}, + {"sru", cmd_sru, "", NULL,0,NULL}, + {"exit", cmd_quit, "",NULL,0,NULL}, {0,0,0,0,0,0} }; @@ -4377,8 +4493,9 @@ static int cmd_help (const char *line) return 1; } -int cmd_register_tab(const char* arg) { - +int cmd_register_tab(const char* arg) +{ +#if HAVE_READLINE_READLINE_H char command[101], tabargument[101]; int i; int num_of_tabs; @@ -4395,34 +4512,36 @@ int cmd_register_tab(const char* arg) { } } - if(!cmd_array[i].cmd) { + if (!cmd_array[i].cmd) { fprintf(stderr,"Unknown command %s\n",command); return 1; } - if(!cmd_array[i].local_tabcompletes) + if (!cmd_array[i].local_tabcompletes) cmd_array[i].local_tabcompletes = (char **) calloc(1,sizeof(char**)); num_of_tabs=0; tabslist = cmd_array[i].local_tabcompletes; - for(;tabslist && *tabslist;tabslist++) { + for(; tabslist && *tabslist; tabslist++) { num_of_tabs++; } - cmd_array[i].local_tabcompletes = (char **) - realloc(cmd_array[i].local_tabcompletes,(num_of_tabs+2)*sizeof(char**)); - tabslist=cmd_array[i].local_tabcompletes; - tabslist[num_of_tabs]=strdup(tabargument); - tabslist[num_of_tabs+1]=NULL; + cmd_array[i].local_tabcompletes = (char **) + realloc(cmd_array[i].local_tabcompletes, + (num_of_tabs+2)*sizeof(char**)); + tabslist = cmd_array[i].local_tabcompletes; + tabslist[num_of_tabs] = strdup(tabargument); + tabslist[num_of_tabs+1] = NULL; +#endif return 1; } void process_cmd_line(char* line) { - int i,res; + int i, res; char word[32], arg[10240]; #if HAVE_GETTIMEOFDAY @@ -4477,38 +4596,47 @@ void process_cmd_line(char* line) fflush(marc_file); } - -char *command_generator(const char *text, int state) +static char *command_generator(const char *text, int state) { +#if HAVE_READLINE_READLINE_H static int idx; if (state==0) { idx = 0; } for( ; cmd_array[idx].cmd; ++idx) { - if (!strncmp(cmd_array[idx].cmd,text,strlen(text))) { + if (!strncmp(cmd_array[idx].cmd, text, strlen(text))) { ++idx; /* skip this entry on the next run */ return strdup(cmd_array[idx-1].cmd); } } +#endif return NULL; } +#if HAVE_READLINE_READLINE_H +static char** default_completer_list = NULL; + +static char* default_completer(const char* text, int state) +{ + return complete_from_list(default_completer_list, text, state); +} +#endif + +#if HAVE_READLINE_READLINE_H /* This function only known how to complete on the first word */ -char ** readline_completer(char *text, int start, int end) { -#if HAVE_READLINE_READLINE_H - - completerFunctionType completerToUse; - +char **readline_completer(char *text, int start, int end) +{ + completerFunctionType completerToUse; + if(start == 0) { #if HAVE_READLINE_RL_COMPLETION_MATCHES - char** res=rl_completion_matches(text, - command_generator); + char** res = rl_completion_matches(text, command_generator); #else - char** res=completion_matches(text, - (CPFunction*)command_generator); + char** res = completion_matches(text, + (CPFunction*)command_generator); #endif rl_attempted_completion_over = 1; return res; @@ -4520,44 +4648,46 @@ char ** readline_completer(char *text, int start, int end) { return NULL; } - for (i = 0; cmd_array[i].cmd; i++) { - if (!strncmp(cmd_array[i].cmd, word, strlen(word))) { + for (i = 0; cmd_array[i].cmd; i++) + if (!strncmp(cmd_array[i].cmd, word, strlen(word))) break; - } - } - if(!cmd_array[i].cmd) return NULL; + if(!cmd_array[i].cmd) + return NULL; - curret_global_list = cmd_array[i].local_tabcompletes; + default_completer_list = cmd_array[i].local_tabcompletes; completerToUse = cmd_array[i].rl_completerfunction; - if(completerToUse==NULL) /* if no pr. command completer is defined use the default completer */ + if (!completerToUse) + { /* if command completer is not defined use the default completer */ completerToUse = default_completer; - - if(completerToUse) { + } + if (completerToUse) { #ifdef HAVE_READLINE_RL_COMPLETION_MATCHES char** res= - rl_completion_matches(text, - completerToUse); + rl_completion_matches(text, completerToUse); #else char** res= - completion_matches(text, - (CPFunction*)completerToUse); + completion_matches(text, (CPFunction*)completerToUse); #endif - if(!cmd_array[i].complete_filenames) + if (!cmd_array[i].complete_filenames) rl_attempted_completion_over = 1; return res; } else { - if(!cmd_array[i].complete_filenames) + if (!cmd_array[i].complete_filenames) rl_attempted_completion_over = 1; return 0; } } -#else - return 0; -#endif } +#endif +#ifndef WIN32 +void ctrl_c_handler(int x) +{ + exit_client(0); +} +#endif static void client(void) { @@ -4565,6 +4695,10 @@ static void client(void) line[10239] = '\0'; +#ifndef WIN32 + signal(SIGINT, ctrl_c_handler); +#endif + #if HAVE_GETTIMEOFDAY gettimeofday (&tv_start, 0); #endif @@ -4582,8 +4716,8 @@ static void client(void) if (*line_in) add_history(line_in); #endif - strncpy(line, line_in, 10239); - free (line_in); + strncpy(line, line_in, sizeof(line)-1); + free(line_in); } #endif if (!line_in) @@ -4591,11 +4725,13 @@ static void client(void) char *end_p; printf (C_PROMPT); fflush(stdout); - if (!fgets(line, 10239, stdin)) + if (!fgets(line, sizeof(line)-1, stdin)) break; if ((end_p = strchr (line, '\n'))) *end_p = '\0'; } + if (isatty(0)) + file_history_add_line(file_history, line); process_cmd_line(line); } } @@ -4698,7 +4834,7 @@ int main(int argc, char **argv) hex_dump = 1; break; case 'p': - yazProxy=strdup(arg); + yazProxy = xstrdup(arg); break; case 'u': if (!auth_command) @@ -4754,8 +4890,9 @@ int main(int argc, char **argv) #endif xfree(open_command); } - client (); - exit (0); + client(); + exit_client(0); + return 0; } /* * Local variables: