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