2 * Copyright (c) 1998-1999, Index Data.
3 * See the file LICENSE for details.
4 * Sebastian Hammer, Adam Dickmeiss
6 * $Log: yaz-client.cpp,v $
7 * Revision 1.6 1999-04-20 10:30:05 adam
8 * Implemented various stuff for client and proxy. Updated calls
9 * to ODR to reflect new name parameter.
11 * Revision 1.5 1999/04/09 11:46:57 adam
12 * Added object Yaz_Z_Assoc. Much more functional client.
14 * Revision 1.4 1999/03/23 14:17:57 adam
15 * More work on timeout handling. Work on yaz-client.
17 * Revision 1.3 1999/02/02 14:01:18 adam
18 * First WIN32 port of YAZ++.
20 * Revision 1.2 1999/01/28 13:08:42 adam
21 * Yaz_PDU_Assoc better encapsulated. Memory leak fix in
22 * yaz-socket-manager.cc.
24 * Revision 1.1.1.1 1999/01/28 09:41:07 adam
25 * First implementation of YAZ++.
32 #include <yaz-ir-assoc.h>
33 #include <yaz-pdu-assoc.h>
34 #include <yaz-socket-manager.h>
37 #if HAVE_READLINE_READLINE_H
38 #include <readline/readline.h>
40 #if HAVE_READLINE_HISTORY_H
41 #include <readline/history.h>
45 class YAZ_EXPORT MyClient : public Yaz_IR_Assoc {
47 int m_interactive_flag;
48 char m_thisCommand[1024];
49 char m_lastCommand[1024];
51 Yaz_SocketManager *m_socketManager;
53 MyClient(IYaz_PDU_Observable *the_PDU_Observable,
54 Yaz_SocketManager *the_SocketManager);
55 IYaz_PDU_Observer *clone(IYaz_PDU_Observable *the_PDU_Observable);
56 int args(Yaz_SocketManager *socketManager, int argc, char **argv);
57 int interactive(Yaz_SocketManager *socketManager);
59 void recv_initResponse(Z_InitResponse *initResponse);
60 void recv_searchResponse(Z_SearchResponse *searchResponse);
61 void recv_presentResponse(Z_PresentResponse *presentResponse);
62 void recv_records (Z_Records *records);
63 void recv_diagrecs(Z_DiagRec **pp, int num);
64 void recv_namePlusRecord (Z_NamePlusRecord *zpr, int offset);
65 void recv_record(Z_DatabaseRecord *record, int offset,
66 const char *databaseName);
67 void recv_textRecord(int type, const char *buf, size_t len);
68 void recv_genericRecord(Z_GenericRecord *r);
69 void display_genericRecord(Z_GenericRecord *r, int level);
70 void display_variant(Z_Variant *v, int level);
71 int processCommand(const char *cmd);
72 const char *MyClient::getCommand();
73 int cmd_open(char *host);
74 int cmd_quit(char *args);
75 int cmd_close(char *args);
76 int cmd_find(char *args);
77 int cmd_show(char *args);
78 int cmd_cookie(char *args);
81 IYaz_PDU_Observer *MyClient::clone(IYaz_PDU_Observable *the_PDU_Observable)
83 return new MyClient(the_PDU_Observable, m_socketManager);
86 MyClient::MyClient(IYaz_PDU_Observable *the_PDU_Observable,
87 Yaz_SocketManager *the_socketManager) :
88 Yaz_IR_Assoc (the_PDU_Observable)
91 m_interactive_flag = 1;
92 m_thisCommand[0] = '\0';
93 m_lastCommand[0] = '\0';
94 m_socketManager = the_socketManager;
97 void usage(char *prog)
99 fprintf (stderr, "%s: [-v log] [-p proxy] [zurl]\n", prog);
103 void MyClient::recv_initResponse(Z_InitResponse *initResponse)
105 printf ("Got InitResponse. Status ");
106 if (*initResponse->result)
112 void MyClient::recv_diagrecs(Z_DiagRec **pp, int num)
116 Z_DefaultDiagFormat *r;
118 printf("Diagnostic message(s) from database:\n");
119 for (i = 0; i<num; i++)
121 Z_DiagRec *p = pp[i];
122 if (p->which != Z_DiagRec_defaultFormat)
124 printf("Diagnostic record not in default format.\n");
128 r = p->u.defaultFormat;
129 if (!(ent = oid_getentbyoid(r->diagnosticSetId)) ||
130 ent->oclass != CLASS_DIAGSET || ent->value != VAL_BIB1)
131 printf("Missing or unknown diagset\n");
132 printf(" [%d] %s", *r->condition, diagbib1_str(*r->condition));
136 case Z_DefaultDiagFormat_v2Addinfo:
137 printf (" -- v2 addinfo '%s'\n", r->u.v2Addinfo);
139 case Z_DefaultDiagFormat_v3Addinfo:
140 printf (" -- v3 addinfo '%s'\n", r->u.v3Addinfo);
144 if (r->addinfo && *r->addinfo)
145 printf(" -- '%s'\n", r->addinfo);
152 void MyClient::recv_textRecord(int type, const char *buf, size_t len)
154 fwrite (buf, 1, len, stdout);
155 fputc ('\n', stdout);
158 void MyClient::display_variant(Z_Variant *v, int level)
162 for (i = 0; i < v->num_triples; i++)
164 printf("%*sclass=%d,type=%d", level * 4, "", *v->triples[i]->zclass,
165 *v->triples[i]->type);
166 if (v->triples[i]->which == Z_Triple_internationalString)
167 printf(",value=%s\n", v->triples[i]->value.internationalString);
173 void MyClient::display_genericRecord(Z_GenericRecord *r, int level)
179 for (i = 0; i < r->num_elements; i++)
183 printf("%*s", level * 4, "");
187 printf("%d,", *t->tagType);
190 if (t->tagValue->which == Z_StringOrNumeric_numeric)
191 printf("%d) ", *t->tagValue->u.numeric);
193 printf("%s) ", t->tagValue->u.string);
194 if (t->content->which == Z_ElementData_subtree)
197 display_genericRecord(t->content->u.subtree, level+1);
199 else if (t->content->which == Z_ElementData_string)
200 printf("%s\n", t->content->u.string);
201 else if (t->content->which == Z_ElementData_numeric)
202 printf("%d\n", *t->content->u.numeric);
203 else if (t->content->which == Z_ElementData_oid)
205 int *ip = t->content->u.oid;
208 if ((oent = oid_getentbyoid(t->content->u.oid)))
209 printf("OID: %s\n", oent->desc);
213 while (ip && *ip >= 0)
214 printf(" %d", *(ip++));
218 else if (t->content->which == Z_ElementData_noDataRequested)
219 printf("[No data requested]\n");
220 else if (t->content->which == Z_ElementData_elementEmpty)
221 printf("[Element empty]\n");
222 else if (t->content->which == Z_ElementData_elementNotThere)
223 printf("[Element not there]\n");
226 if (t->appliedVariant)
227 display_variant(t->appliedVariant, level+1);
228 if (t->metaData && t->metaData->supportedVariants)
232 printf("%*s---- variant list\n", (level+1)*4, "");
233 for (c = 0; c < t->metaData->num_supportedVariants; c++)
235 printf("%*svariant #%d\n", (level+1)*4, "", c);
236 display_variant(t->metaData->supportedVariants[c], level + 2);
242 void MyClient::recv_genericRecord(Z_GenericRecord *r)
244 display_genericRecord(r, 0);
247 void MyClient::recv_record(Z_DatabaseRecord *record, int offset,
248 const char *databaseName)
250 Z_External *r = (Z_External*) record;
251 oident *ent = oid_getentbyoid(r->direct_reference);
254 * Tell the user what we got.
256 if (r->direct_reference)
258 printf("Record type: ");
260 printf("%s\n", ent->desc);
262 /* Check if this is a known, ASN.1 type tucked away in an octet string */
263 Z_ext_typeent *etype = z_ext_getentbyref(ent->value);
264 if (ent && (r->which == Z_External_octet || r->which == Z_External_single)
265 && (etype = z_ext_getentbyref(ent->value)))
270 * Call the given decoder to process the record.
272 odr_setbuf(odr_decode(), (char*)record->u.octet_aligned->buf,
273 record->u.octet_aligned->len, 0);
274 if (!(*etype->fun)(odr_decode(), (char **)&rr, 0, 0))
276 odr_perror(odr_decode(), "Decoding constructed record.");
277 fprintf(stderr, "[Near %d]\n", odr_offset(odr_decode()));
278 fprintf(stderr, "Packet dump:\n---------\n");
279 odr_dumpBER(stderr, (char*)record->u.octet_aligned->buf,
280 record->u.octet_aligned->len);
281 fprintf(stderr, "---------\n");
283 if (etype->what == Z_External_sutrs)
285 Z_SUTRS *sutrs = (Z_SUTRS *) rr;
286 recv_textRecord ((int) VAL_SUTRS, (const char *) sutrs->buf,
287 (size_t) sutrs->len);
291 if (r->which == Z_External_octet && record->u.octet_aligned->len)
293 recv_textRecord((int) ent->value,
294 (const char *) record->u.octet_aligned->buf,
295 (size_t) record->u.octet_aligned->len);
297 else if (ent && ent->value == VAL_SUTRS && r->which == Z_External_sutrs)
298 recv_textRecord((int) VAL_SUTRS, (const char *) r->u.sutrs->buf,
299 (size_t) r->u.sutrs->len);
300 else if (ent && ent->value == VAL_GRS1 && r->which == Z_External_grs1)
301 recv_genericRecord(r->u.grs1);
304 printf("Unknown record representation.\n");
305 if (!z_External(odr_print(), &r, 0))
307 odr_perror(odr_print(), "Printing external");
308 odr_reset(odr_print());
313 void MyClient::recv_namePlusRecord (Z_NamePlusRecord *zpr, int offset)
315 if (zpr->databaseName)
316 printf("[%s]", zpr->databaseName);
317 if (zpr->which == Z_NamePlusRecord_surrogateDiagnostic)
318 recv_diagrecs(&zpr->u.surrogateDiagnostic, 1);
320 recv_record(zpr->u.databaseRecord, offset, zpr->databaseName);
323 void MyClient::recv_records (Z_Records *records)
325 Z_DiagRec dr, *dr_p = &dr;
327 switch (records->which)
329 case Z_Records_DBOSD:
330 for (i = 0; i < records->u.databaseOrSurDiagnostics->num_records; i++)
331 recv_namePlusRecord(records->u.databaseOrSurDiagnostics->
332 records[i], i + m_setOffset);
333 m_setOffset += records->u.databaseOrSurDiagnostics->num_records;
337 dr.which = Z_DiagRec_defaultFormat;
338 dr.u.defaultFormat = records->u.nonSurrogateDiagnostic;
339 recv_diagrecs (&dr_p, 1);
341 recv_diagrecs (&records->u.nonSurrogateDiagnostic, 1);
344 case Z_Records_multipleNSD:
345 recv_diagrecs (records->u.multipleNonSurDiagnostics->diagRecs,
346 records->u.multipleNonSurDiagnostics->num_diagRecs);
351 void MyClient::recv_searchResponse(Z_SearchResponse *searchResponse)
353 printf ("Got SearchResponse. Status ");
354 if (!*searchResponse->searchStatus)
360 printf ("Hits: %d\n", *searchResponse->resultCount);
361 if (searchResponse->records)
362 recv_records (searchResponse->records);
365 void MyClient::recv_presentResponse(Z_PresentResponse *presentResponse)
367 printf ("Got PresentResponse\n");
368 recv_records (presentResponse->records);
374 while (m_socketManager->processEvent() > 0)
376 if (get_lastReceived())
382 #define C_PROMPT "Z>"
384 int MyClient::cmd_open(char *host)
387 if (send_initRequest() >= 0)
394 int MyClient::cmd_quit(char *args)
399 int MyClient::cmd_close(char *args)
405 int MyClient::cmd_find(char *args)
409 if (query.set_rpn(args) <= 0)
411 printf ("Bad RPN query\n");
414 if (send_searchRequest(&query) >= 0)
419 int MyClient::cmd_show(char *args)
421 int start = m_setOffset, number = 1;
423 sscanf (args, "%d %d", &start, &number);
424 if (send_presentRequest(start, number) >= 0)
429 int MyClient::cmd_cookie(char *args)
435 int MyClient::processCommand(const char *commandLine)
437 char cmdStr[1024], cmdArgs[1024];
442 int (MyClient::*fun)(char *arg);
445 {"open", &cmd_open, "<host>[':'<port>][/<database>]"},
446 {"quit", &cmd_quit, ""},
447 {"close", &cmd_close, ""},
448 {"find", &cmd_find, "<query>"},
449 {"show", &cmd_show, "[<start> [<number>]]"},
450 {"cookie", &cmd_cookie, "<cookie>"},
454 if (sscanf(commandLine, "%s %[^;]", cmdStr, cmdArgs) < 1)
457 for (i = 0; cmd[i].cmd; i++)
458 if (!strncmp(cmd[i].cmd, cmdStr, strlen(cmdStr)))
462 if (cmd[i].cmd) // Invoke command handler
463 res = (this->*cmd[i].fun)(cmdArgs);
464 else // Dump help screen
466 printf("Unknown command: %s.\n", cmdStr);
467 printf("Currently recognized commands:\n");
468 for (i = 0; cmd[i].cmd; i++)
469 printf(" %s %s\n", cmd[i].cmd, cmd[i].ad);
474 const char *MyClient::getCommand()
476 #if HAVE_READLINE_READLINE_H
477 // Read using GNU readline
479 line_in=readline(C_PROMPT);
482 #if HAVE_READLINE_HISTORY_H
484 add_history(line_in);
486 strncpy(m_thisCommand,line_in, 1023);
487 m_thisCommand[1023] = '\0';
490 // Read using fgets(3)
493 if (!fgets(m_thisCommand, 1023, stdin))
496 // Remove trailing whitespace
497 char *cp = m_thisCommand + strlen(m_thisCommand);
498 while (cp != m_thisCommand && strchr("\t \n", cp[-1]))
502 // Remove leading spaces...
503 while (*cp && strchr ("\t \n", *cp))
505 // Save command if non-empty
507 strcpy (m_lastCommand, cp);
508 return m_lastCommand;
511 int MyClient::interactive(Yaz_SocketManager *socketManager)
514 if (!m_interactive_flag)
516 while ((cmd = getCommand()))
518 if (!processCommand(cmd))
524 int MyClient::args(Yaz_SocketManager *socketManager, int argc, char **argv)
529 char *prog = argv[0];
532 while ((ret = options("p:v:q", argv, argc, &arg)) != -2)
553 log_init_level (log_mask_str(arg));
556 m_interactive_flag = 0;
572 int main(int argc, char **argv)
574 Yaz_SocketManager mySocketManager;
575 Yaz_PDU_Assoc *some = new Yaz_PDU_Assoc(&mySocketManager, 0);
577 MyClient z(some, &mySocketManager);
579 if (z.args(&mySocketManager, argc, argv))
581 if (z.interactive(&mySocketManager))