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