Allow inspection of result set options with rget command
[yaz-moved-to-github.git] / zoom / zoomsh.c
1 /*
2  * Copyright (C) 1995-2005, Index Data ApS
3  * See the file LICENSE for details.
4  *
5  * $Id: zoomsh.c,v 1.38 2005-11-01 15:07:29 adam Exp $
6  */
7
8 /* ZOOM-C Shell */
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <ctype.h>
14
15 #include <yaz/comstack.h>
16
17 #if HAVE_READLINE_READLINE_H
18 #include <readline/readline.h> 
19 #endif
20 #if HAVE_READLINE_HISTORY_H
21 #include <readline/history.h>
22 #endif
23
24 #include <yaz/xmalloc.h>
25
26 #include <yaz/log.h>
27 #include <yaz/nmem.h>
28 #include <yaz/zoom.h>
29 #include <yaz/oid.h>
30
31 #define MAX_CON 100
32
33 static int next_token (const char **cpp, const char **t_start)
34 {
35     int len = 0;
36     const char *cp = *cpp;
37     while (*cp == ' ')
38         cp++;
39     if (*cp == '"')
40     {
41         cp++;
42         *t_start = cp;
43         while (*cp && *cp != '"')
44         {
45             cp++;
46             len++;
47         }
48         if (*cp)
49             cp++;
50     }
51     else
52     {
53         *t_start = cp;
54         while (*cp && *cp != ' ' && *cp != '\r' && *cp != '\n')
55         {
56             cp++;
57             len++;
58         }
59         if (len == 0)
60             len = -1;
61     }
62     *cpp = cp;
63     return len;  /* return -1 if no token was read .. */
64 }
65
66 static int next_token_copy (const char **cpp, char *buf_out, int buf_max)
67 {
68     const char *start;
69     int len = next_token (cpp, &start);
70     if (len < 0)
71     {
72         *buf_out = 0;
73         return len;
74     }
75     if (len >= buf_max)
76         len = buf_max-1;
77     memcpy (buf_out, start, len);
78     buf_out[len] = '\0';
79     return len;
80 }
81
82 static int is_command (const char *cmd_str, const char *this_str, int this_len)
83 {
84     int cmd_len = strlen(cmd_str);
85     if (cmd_len != this_len)
86         return 0;
87     if (memcmp (cmd_str, this_str, cmd_len))
88         return 0;
89     return 1;
90 }
91
92 static void cmd_set (ZOOM_connection *c, ZOOM_resultset *r,
93                      ZOOM_options options,
94                      const char **args)
95 {
96     char key[40], val[80];
97
98     if (next_token_copy (args, key, sizeof(key)) < 0)
99     {
100         printf ("missing argument for set\n");
101         return ;
102     }
103     if (next_token_copy (args, val, sizeof(val)) < 0)
104         ZOOM_options_set(options, key, 0);
105     else
106         ZOOM_options_set(options, key, val);
107 }
108
109 static void cmd_get (ZOOM_connection *c, ZOOM_resultset *r,
110                      ZOOM_options options,
111                      const char **args)
112 {
113     char key[40];
114     if (next_token_copy (args, key, sizeof(key)) < 0)
115     {
116         printf ("missing argument for get\n");
117     }
118     else
119     {
120         const char *val = ZOOM_options_get(options, key);
121         printf ("%s = %s\n", key, val ? val : "<null>");
122     }
123 }
124
125 static void cmd_rget(ZOOM_connection *c, ZOOM_resultset *r,
126                      ZOOM_options options,
127                      const char **args)
128 {
129     char key[40];
130     if (next_token_copy (args, key, sizeof(key)) < 0)
131     {
132         printf ("missing argument for get\n");
133     }
134     else
135     {
136         int i;
137         for (i = 0; i<MAX_CON; i++)
138         {
139             if (!r[i])
140                 continue;
141             
142             const char *val = ZOOM_resultset_option_get(r[i], key);
143             printf ("%s = %s\n", key, val ? val : "<null>");
144         }
145     }
146 }
147
148 static void cmd_close (ZOOM_connection *c, ZOOM_resultset *r,
149                        ZOOM_options options,
150                        const char **args)
151 {
152     char host[60];
153     int i;
154     next_token_copy (args, host, sizeof(host));
155     for (i = 0; i<MAX_CON; i++)
156     {
157         const char *h;
158         if (!c[i])
159             continue;
160         if ((h = ZOOM_connection_option_get(c[i], "host"))
161             && !strcmp (h, host))
162         {
163             ZOOM_connection_destroy (c[i]);
164             c[i] = 0;
165         }
166         else if (*host == '\0')
167         {
168             ZOOM_connection_destroy (c[i]);
169             c[i] = 0;
170         }
171     }
172 }
173
174 static void display_records (ZOOM_connection c,
175                              ZOOM_resultset r,
176                              int start, int count)
177 {
178     int i;
179     for (i = 0; i<count; i++)
180     {
181         int pos = i + start;
182         ZOOM_record rec = ZOOM_resultset_record (r, pos);
183         const char *db = ZOOM_record_get (rec, "database", 0);
184         int len, opac_len;
185         const char *render = ZOOM_record_get (rec, "render", &len);
186         const char *opac_render = ZOOM_record_get (rec, "opac", &opac_len);
187         const char *syntax = ZOOM_record_get (rec, "syntax", 0);
188         /* if rec is non-null, we got a record for display */
189         if (rec)
190         {
191             char oidbuf[100];
192             (void) oid_name_to_dotstring(CLASS_RECSYN, syntax, oidbuf);
193             printf ("%d %s %s (%s)\n",
194                     pos+1, (db ? db : "unknown"), syntax, oidbuf);
195             if (render)
196                 fwrite (render, 1, len, stdout);
197             printf ("\n");
198             if (opac_render)
199                 fwrite (opac_render, 1, opac_len, stdout);
200         }
201             
202     }
203 }
204
205 static void cmd_show (ZOOM_connection *c, ZOOM_resultset *r,
206                       ZOOM_options options,
207                       const char **args)
208 {
209     int i;
210     char start_str[10], count_str[10];
211
212     if (next_token_copy (args, start_str, sizeof(start_str)) >= 0)
213         ZOOM_options_set (options, "start", start_str);
214
215     if (next_token_copy (args, count_str, sizeof(count_str)) >= 0)
216         ZOOM_options_set (options, "count", count_str);
217
218     for (i = 0; i<MAX_CON; i++)
219         ZOOM_resultset_records (r[i], 0, atoi(start_str), atoi(count_str));
220     while (ZOOM_event (MAX_CON, c))
221         ;
222
223     for (i = 0; i<MAX_CON; i++)
224     {
225         int error;
226         const char *errmsg, *addinfo, *dset;
227         /* display errors if any */
228         if (!c[i])
229             continue;
230         if ((error = ZOOM_connection_error_x(c[i], &errmsg, &addinfo, &dset)))
231             printf ("%s error: %s (%s:%d) %s\n",
232                      ZOOM_connection_option_get(c[i], "host"), errmsg,
233                      dset, error, addinfo);
234         else if (r[i])
235         {
236             /* OK, no major errors. Display records... */
237             int start = ZOOM_options_get_int (options, "start", 0);
238             int count = ZOOM_options_get_int (options, "count", 0);
239             display_records (c[i], r[i], start, count);
240         }
241     }
242     ZOOM_options_set (options, "count", "0");
243     ZOOM_options_set (options, "start", "0");
244 }
245
246 static void cmd_ext (ZOOM_connection *c, ZOOM_resultset *r,
247                      ZOOM_options options,
248                      const char **args)
249 {
250     ZOOM_package p[MAX_CON];
251     char ext_type_str[10];
252     
253     int i;
254
255     if (next_token_copy (args, ext_type_str, sizeof(ext_type_str)) < 0)
256         return;
257     
258     for (i = 0; i<MAX_CON; i++)
259     {
260         if (c[i])
261         {
262             p[i] = ZOOM_connection_package (c[i], 0);
263             ZOOM_package_send(p[i], ext_type_str);
264         }
265         else
266             p[i] = 0;
267     }
268
269     while (ZOOM_event (MAX_CON, c))
270         ;
271
272     for (i = 0; i<MAX_CON; i++)
273     {
274         int error;
275         const char *errmsg, *addinfo, *dset;
276         /* display errors if any */
277         if (!p[i])
278             continue;
279         if ((error = ZOOM_connection_error_x(c[i], &errmsg, &addinfo, &dset)))
280             printf ("%s error: %s (%s:%d) %s\n",
281                      ZOOM_connection_option_get(c[i], "host"), errmsg,
282                      dset, error, addinfo);
283         else if (p[i])
284         {
285             const char *v;
286             printf ("ok\n");
287             v = ZOOM_package_option_get (p[i], "targetReference");
288             if (v)
289                 printf("targetReference: %s\n", v);
290             v = ZOOM_package_option_get (p[i], "xmlUpdateDoc");
291             if (v)
292                 printf("xmlUpdateDoc: %s\n", v);
293         }
294         ZOOM_package_destroy (p[i]);
295     }
296 }
297
298 static void cmd_debug (ZOOM_connection *c, ZOOM_resultset *r,
299                        ZOOM_options options,
300                        const char **args)
301 {
302     yaz_log_init_level(YLOG_ALL);
303 }
304
305 static void cmd_search (ZOOM_connection *c, ZOOM_resultset *r,
306                         ZOOM_options options,
307                         const char **args)
308 {
309     ZOOM_query s;
310     const char *query_str = *args;
311     int i;
312     
313     s = ZOOM_query_create ();
314     while (*query_str == ' ')
315         query_str++;
316     if (memcmp(query_str, "cql:", 4) == 0)
317     {
318         ZOOM_query_cql (s, query_str + 4);
319     }
320     else if (ZOOM_query_prefix (s, query_str))
321     {
322         printf ("Bad PQF: %s\n", query_str);
323         return;
324     }
325     for (i = 0; i<MAX_CON; i++)
326     {
327         if (c[i])
328         {
329             ZOOM_resultset_destroy (r[i]);
330             r[i] = 0;
331         }
332         if (c[i])
333             r[i] = ZOOM_connection_search (c[i], s);
334     }
335
336     while (ZOOM_event (MAX_CON, c))
337         ;
338
339     for (i = 0; i<MAX_CON; i++)
340     {
341         int error;
342         const char *errmsg, *addinfo, *dset;
343         /* display errors if any */
344         if (!c[i])
345             continue;
346         if ((error = ZOOM_connection_error_x(c[i], &errmsg, &addinfo, &dset)))
347             printf ("%s error: %s (%s:%d) %s\n",
348                     ZOOM_connection_option_get(c[i], "host"), errmsg,
349                     dset, error, addinfo);
350         else if (r[i])
351         {
352             /* OK, no major errors. Look at the result count */
353             int start = ZOOM_options_get_int (options, "start", 0);
354             int count = ZOOM_options_get_int (options, "count", 0);
355
356             printf ("%s: %d hits\n", ZOOM_connection_option_get(c[i], "host"),
357                     ZOOM_resultset_size(r[i]));
358             /* and display */
359             display_records (c[i], r[i], start, count);
360         }
361     }
362     ZOOM_query_destroy (s);
363 }
364
365 static void cmd_scan (ZOOM_connection *c, ZOOM_resultset *r,
366                       ZOOM_options options,
367                       const char **args)
368 {
369     const char *start_term = *args;
370     int i;
371     ZOOM_scanset s[MAX_CON];
372     
373     while (*start_term == ' ')
374         start_term++;
375
376     for (i = 0; i<MAX_CON; i++)
377     {
378         if (c[i])
379             s[i] = ZOOM_connection_scan(c[i], start_term);
380         else
381             s[i] = 0;
382     }
383     while (ZOOM_event(MAX_CON, c))
384         ;
385     for (i = 0; i<MAX_CON; i++)
386     {
387         if (s[i]) {
388             size_t p, sz = ZOOM_scanset_size(s[i]);
389             for (p = 0; p < sz; p++)
390             {
391                 int occ = 0;
392                 int len = 0;
393                 const char *term = ZOOM_scanset_display_term(s[i], p,
394                                 &occ, &len);
395                 fwrite(term, 1, len, stdout);
396                 printf (" %d\n", occ);
397             }            
398             ZOOM_scanset_destroy(s[i]);
399         }
400     }
401 }
402
403 static void cmd_sort (ZOOM_connection *c, ZOOM_resultset *r,
404                       ZOOM_options options,
405                       const char **args)
406 {
407     const char *sort_spec = *args;
408     int i;
409     
410     while (*sort_spec == ' ')
411         sort_spec++;
412     
413     for (i = 0; i<MAX_CON; i++)
414     {
415         if (r[i])
416             ZOOM_resultset_sort(r[i], "yaz", sort_spec);
417     }
418     while (ZOOM_event(MAX_CON, c))
419         ;
420 }
421
422 static void cmd_help (ZOOM_connection *c, ZOOM_resultset *r,
423                       ZOOM_options options,
424                       const char **args)
425 {
426     printf ("connect <zurl>\n");
427     printf ("search <pqf>\n");
428     printf ("show [<start> [<count>]\n");
429     printf ("scan <term>\n");
430     printf ("quit\n");
431     printf ("close <zurl>\n");
432     printf ("ext <type>\n");
433     printf ("set <option> [<value>]\n");
434     printf ("get <option>\n");
435     printf ("\n");
436     printf ("options:\n");
437     printf (" start\n");
438     printf (" count\n");
439     printf (" databaseName\n");
440     printf (" preferredRecordSyntax\n");
441     printf (" proxy\n");
442     printf (" elementSetName\n");
443     printf (" maximumRecordSize\n");
444     printf (" preferredRecordSize\n");
445     printf (" async\n");
446     printf (" piggyback\n");
447     printf (" group\n");
448     printf (" user\n");
449     printf (" password\n");
450     printf (" implementationName\n");
451     printf (" charset\n");
452     printf (" lang\n");
453 }
454
455 static void cmd_connect (ZOOM_connection *c, ZOOM_resultset *r,
456                          ZOOM_options options,
457                          const char **args)
458 {
459     int error;
460     const char *errmsg, *addinfo, *dset;
461     char host[60];
462     int j, i;
463     if (next_token_copy (args, host, sizeof(host)) < 0)
464     {
465         printf ("missing host after connect\n");
466         return ;
467     }
468     for (j = -1, i = 0; i<MAX_CON; i++)
469     {
470         const char *h;
471         if (c[i] && (h = ZOOM_connection_option_get(c[i], "host")) &&
472             !strcmp (h, host))
473         {
474             ZOOM_connection_destroy (c[i]);
475             break;
476         }
477         else if (c[i] == 0 && j == -1)
478             j = i;
479     }
480     if (i == MAX_CON)  /* no match .. */
481     {
482         if (j == -1)
483         {
484             printf ("no more connection available\n");
485             return;
486         }
487         i = j;   /* OK, use this one is available */
488     }
489     c[i] = ZOOM_connection_create (options);
490     ZOOM_connection_connect (c[i], host, 0);
491         
492     if ((error = ZOOM_connection_error_x(c[i], &errmsg, &addinfo, &dset)))
493        printf ("%s error: %s (%s:%d) %s\n",
494             ZOOM_connection_option_get(c[i], "host"), errmsg,
495             dset, error, addinfo);
496 }
497
498 static int cmd_parse (ZOOM_connection *c, ZOOM_resultset *r,
499                       ZOOM_options options, 
500                       const char **buf)
501 {
502     int cmd_len;
503     const char *cmd_str;
504
505     cmd_len = next_token (buf, &cmd_str);
506     if (cmd_len < 0)
507         return 1;
508     if (is_command ("quit", cmd_str, cmd_len))
509         return 0;
510     else if (is_command ("set", cmd_str, cmd_len))
511         cmd_set (c, r, options, buf);
512     else if (is_command ("get", cmd_str, cmd_len))
513         cmd_get (c, r, options, buf);
514     else if (is_command ("rget", cmd_str, cmd_len))
515         cmd_rget (c, r, options, buf);
516     else if (is_command ("connect", cmd_str, cmd_len))
517         cmd_connect (c, r, options, buf);
518     else if (is_command ("open", cmd_str, cmd_len))
519         cmd_connect (c, r, options, buf);
520     else if (is_command ("search", cmd_str, cmd_len))
521         cmd_search (c, r, options, buf);
522     else if (is_command ("find", cmd_str, cmd_len))
523         cmd_search (c, r, options, buf);
524     else if (is_command ("show", cmd_str, cmd_len))
525         cmd_show (c, r, options, buf);
526     else if (is_command ("close", cmd_str, cmd_len))
527         cmd_close (c, r, options, buf);
528     else if (is_command ("help", cmd_str, cmd_len))
529         cmd_help(c, r, options, buf);
530     else if (is_command ("ext", cmd_str, cmd_len))
531         cmd_ext(c, r, options, buf);
532     else if (is_command ("debug", cmd_str, cmd_len))
533         cmd_debug(c, r, options, buf);
534     else if (is_command ("scan", cmd_str, cmd_len))
535         cmd_scan(c, r, options, buf);
536     else if (is_command ("sort", cmd_str, cmd_len))
537         cmd_sort(c, r, options, buf);
538     else
539         printf ("unknown command %.*s\n", cmd_len, cmd_str);
540     return 2;
541 }
542
543 void shell(ZOOM_connection *c, ZOOM_resultset *r,
544            ZOOM_options options)
545 {
546     while (1)
547     {
548         char buf[1000];
549         char *cp;
550         const char *bp = buf;
551 #if HAVE_READLINE_READLINE_H
552         char* line_in;
553         line_in=readline("ZOOM>");
554         if (!line_in)
555             break;
556 #if HAVE_READLINE_HISTORY_H
557         if (*line_in)
558             add_history(line_in);
559 #endif
560         if(strlen(line_in) > 999) {
561             printf("Input line too long\n");
562             break;
563         };
564         strcpy(buf,line_in);
565         free (line_in);
566 #else    
567         printf ("ZOOM>"); fflush (stdout);
568         if (!fgets (buf, 999, stdin))
569             break;
570 #endif 
571         if ((cp = strchr(buf, '\n')))
572             *cp = '\0';
573         if (!cmd_parse (c, r, options, &bp))
574             break;
575     }
576 }
577
578 static void zoomsh(int argc, char **argv)
579 {
580     ZOOM_options options = ZOOM_options_create();
581     int i, res;
582     ZOOM_connection z39_con[MAX_CON];
583     ZOOM_resultset  z39_res[MAX_CON];
584
585     for (i = 0; i<MAX_CON; i++)
586     {
587         z39_con[i] = 0;
588         z39_res[i] = 0;
589     }
590
591     for (i = 0; i<MAX_CON; i++)
592         z39_con[i] = 0;
593
594     res = 1;
595     for (i = 1; i<argc; i++)
596     {
597         const char *bp = argv[i];
598         res = cmd_parse(z39_con, z39_res, options, &bp);
599         if (res == 0)  /* received quit */
600             break;
601     }
602     if (res)  /* do cmdline shell only if not quitting */
603         shell(z39_con, z39_res, options);
604     ZOOM_options_destroy(options);
605
606     for (i = 0; i<MAX_CON; i++)
607     {
608         ZOOM_connection_destroy(z39_con[i]);
609         ZOOM_resultset_destroy(z39_res[i]);
610     }
611 }
612
613 int main(int argc, char **argv)
614 {
615     const char *maskstr = 0;
616     if (argc > 2 && !strcmp(argv[1], "-v"))
617     {
618         maskstr = argv[2];
619         argv += 2;
620         argc -= 2;
621     }
622     else if (argc > 1 && !strncmp(argv[1], "-v", 2))
623     {
624         maskstr = argv[1]+2;
625         argv++;
626         argc--;
627     }
628     if (maskstr)
629     {
630         int mask = yaz_log_mask_str(maskstr);
631         yaz_log_init_level(mask);
632     }
633     nmem_init();
634     zoomsh(argc, argv);
635     nmem_exit();
636     exit (0);
637 }
638 /*
639  * Local variables:
640  * c-basic-offset: 4
641  * indent-tabs-mode: nil
642  * End:
643  * vim: shiftwidth=4 tabstop=8 expandtab
644  */
645