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