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