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