Avoid ctype.h .
[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     size_t start = 0, count = 1;
315     const char *type = "render";
316     WRBUF render_str = 0;
317
318     if (0)
319     {
320         WRBUF tmp;
321
322         if ((tmp = next_token_new_wrbuf(args)))
323         {
324             start = atoi(wrbuf_cstr(tmp));
325             wrbuf_destroy(tmp);
326         }
327
328         if ((tmp = next_token_new_wrbuf(args)))
329         {
330             count = atoi(wrbuf_cstr(tmp));
331             wrbuf_destroy(tmp);
332         }
333         render_str = next_token_new_wrbuf(args);
334     }
335     if (render_str)
336         type = wrbuf_cstr(render_str);
337
338     process_events(c);
339
340     for (i = 0; i < MAX_CON; i++)
341     {
342         int error;
343         const char *errmsg, *addinfo, *dset;
344         /* display errors if any */
345         if (!c[i])
346             continue;
347         if ((error = ZOOM_connection_error_x(c[i], &errmsg, &addinfo, &dset)))
348             printf("%s error: %s (%s:%d) %s\n",
349                    ZOOM_connection_option_get(c[i], "host"), errmsg,
350                    dset, error, addinfo);
351         else if (r[i])
352         {
353             int num_facets = ZOOM_resultset_facets_size(r[i]);
354             if (num_facets) {
355                 ZOOM_facet_field  *facets = ZOOM_resultset_facets(r[i]);
356                 display_facets(facets, num_facets);
357             }
358         }
359     }
360     if (render_str)
361         wrbuf_destroy(render_str);
362
363 }
364
365 static void cmd_ext(ZOOM_connection *c, ZOOM_resultset *r,
366                     ZOOM_options options,
367                     const char **args)
368 {
369     ZOOM_package p[MAX_CON];
370     int i;
371     WRBUF ext_type_str = next_token_new_wrbuf(args);
372     
373     for (i = 0; i<MAX_CON; i++)
374     {
375         if (c[i])
376         {
377             p[i] = ZOOM_connection_package(c[i], 0);
378             ZOOM_package_send(p[i], ext_type_str ? wrbuf_cstr(ext_type_str):0);
379         }
380         else
381             p[i] = 0;
382     }
383
384     process_events(c);
385
386     for (i = 0; i<MAX_CON; i++)
387     {
388         int error;
389         const char *errmsg, *addinfo, *dset;
390         /* display errors if any */
391         if (!p[i])
392             continue;
393         if ((error = ZOOM_connection_error_x(c[i], &errmsg, &addinfo, &dset)))
394             printf("%s error: %s (%s:%d) %s\n",
395                    ZOOM_connection_option_get(c[i], "host"), errmsg,
396                    dset, error, addinfo);
397         else if (p[i])
398         {
399             const char *v;
400             printf("ok\n");
401             v = ZOOM_package_option_get(p[i], "targetReference");
402             if (v)
403                 printf("targetReference: %s\n", v);
404             v = ZOOM_package_option_get(p[i], "xmlUpdateDoc");
405             if (v)
406                 printf("xmlUpdateDoc: %s\n", v);
407         }
408         ZOOM_package_destroy(p[i]);
409     }
410     if (ext_type_str)
411         wrbuf_destroy(ext_type_str);
412 }
413
414 static void cmd_debug(ZOOM_connection *c, ZOOM_resultset *r,
415                       ZOOM_options options,
416                       const char **args)
417 {
418     yaz_log_init_level(YLOG_ALL);
419 }
420
421 static void cmd_search(ZOOM_connection *c, ZOOM_resultset *r,
422                        ZOOM_options options,
423                        const char **args)
424 {
425     ZOOM_query s;
426     const char *query_str = *args;
427     int i;
428     
429     s = ZOOM_query_create();
430     while (*query_str == ' ')
431         query_str++;
432     if (memcmp(query_str, "cql:", 4) == 0)
433     {
434         ZOOM_query_cql(s, query_str + 4);
435     }
436     else if (ZOOM_query_prefix(s, query_str))
437     {
438         printf("Bad PQF: %s\n", query_str);
439         return;
440     }
441     for (i = 0; i<MAX_CON; i++)
442     {
443
444         if (c[i])
445         {
446             ZOOM_resultset_destroy(r[i]);
447             r[i] = 0;
448         }
449         if (c[i])
450             r[i] = ZOOM_connection_search(c[i], s);
451     }
452     ZOOM_query_destroy(s);
453
454     process_events(c);
455
456     for (i = 0; i<MAX_CON; i++)
457     {
458         int error;
459         const char *errmsg, *addinfo, *dset;
460         /* display errors if any */
461         if (!c[i])
462             continue;
463         if ((error = ZOOM_connection_error_x(c[i], &errmsg, &addinfo, &dset)))
464             printf("%s error: %s (%s:%d) %s\n",
465                    ZOOM_connection_option_get(c[i], "host"), errmsg,
466                    dset, error, addinfo);
467         else if (r[i])
468         {
469             /* OK, no major errors. Look at the result count */
470             int start = ZOOM_options_get_int(options, "start", 0);
471             int count = ZOOM_options_get_int(options, "count", 0);
472
473             printf("%s: %lld hits\n", ZOOM_connection_option_get(c[i], "host"),
474                    (long long int) ZOOM_resultset_size(r[i]));
475             /* and display */
476             display_records(c[i], r[i], start, count, "render");
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