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