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