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