Towards 2.0.32
[yaz-moved-to-github.git] / zoom / zoomsh.c
1 /*
2  * Copyright (c) 2002-2004, Index Data.
3  * See the file LICENSE for details.
4  *
5  * $Id: zoomsh.c,v 1.31 2004-12-13 14:21:58 heikki 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             printf ("ok\n");
263         }
264         ZOOM_package_destroy (p[i]);
265     }
266 }
267
268 static void cmd_debug (ZOOM_connection *c, ZOOM_resultset *r,
269                        ZOOM_options options,
270                        const char **args)
271 {
272     yaz_log_init_level(YLOG_ALL);
273 }
274
275 static void cmd_search (ZOOM_connection *c, ZOOM_resultset *r,
276                         ZOOM_options options,
277                         const char **args)
278 {
279     ZOOM_query s;
280     const char *query_str = *args;
281     int i;
282     
283     s = ZOOM_query_create ();
284     while (*query_str == ' ')
285         query_str++;
286     if (memcmp(query_str, "cql:", 4) == 0)
287     {
288         ZOOM_query_cql (s, query_str + 4);
289     }
290     else if (ZOOM_query_prefix (s, query_str))
291     {
292         printf ("Bad PQF: %s\n", query_str);
293         return;
294     }
295     for (i = 0; i<MAX_CON; i++)
296     {
297         if (c[i])
298         {
299             ZOOM_resultset_destroy (r[i]);
300             r[i] = 0;
301         }
302         if (c[i])
303             r[i] = ZOOM_connection_search (c[i], s);
304     }
305
306     while (ZOOM_event (MAX_CON, c))
307         ;
308
309     for (i = 0; i<MAX_CON; i++)
310     {
311         int error;
312         const char *errmsg, *addinfo, *dset;
313         /* display errors if any */
314         if (!c[i])
315             continue;
316         if ((error = ZOOM_connection_error_x(c[i], &errmsg, &addinfo, &dset)))
317             printf ("%s error: %s (%s:%d) %s\n",
318                     ZOOM_connection_option_get(c[i], "host"), errmsg,
319                     dset, error, addinfo);
320         else if (r[i])
321         {
322             /* OK, no major errors. Look at the result count */
323             int start = ZOOM_options_get_int (options, "start", 0);
324             int count = ZOOM_options_get_int (options, "count", 0);
325
326             printf ("%s: %d hits\n", ZOOM_connection_option_get(c[i], "host"),
327                     ZOOM_resultset_size(r[i]));
328             /* and display */
329             display_records (c[i], r[i], start, count);
330         }
331     }
332     ZOOM_query_destroy (s);
333 }
334
335 static void cmd_scan (ZOOM_connection *c, ZOOM_resultset *r,
336                       ZOOM_options options,
337                       const char **args)
338 {
339     const char *start_term = *args;
340     int i;
341     ZOOM_scanset s[MAX_CON];
342     
343     while (*start_term == ' ')
344         start_term++;
345
346     for (i = 0; i<MAX_CON; i++)
347     {
348         if (c[i])
349             s[i] = ZOOM_connection_scan(c[i], start_term);
350         else
351             s[i] = 0;
352     }
353     while (ZOOM_event(MAX_CON, c))
354         ;
355     for (i = 0; i<MAX_CON; i++)
356     {
357         if (s[i]) {
358             size_t p, sz = ZOOM_scanset_size(s[i]);
359             for (p = 0; p < sz; p++)
360             {
361                 int occ = 0;
362                 int len = 0;
363                 const char *term = ZOOM_scanset_display_term(s[i], p,
364                                 &occ, &len);
365                 fwrite(term, 1, len, stdout);
366                 printf (" %d\n", occ);
367             }            
368             ZOOM_scanset_destroy(s[i]);
369         }
370     }
371 }
372
373 static void cmd_sort (ZOOM_connection *c, ZOOM_resultset *r,
374                       ZOOM_options options,
375                       const char **args)
376 {
377     const char *sort_spec = *args;
378     int i;
379     
380     while (*sort_spec == ' ')
381         sort_spec++;
382     
383     for (i = 0; i<MAX_CON; i++)
384     {
385         if (r[i])
386             ZOOM_resultset_sort(r[i], "yaz", sort_spec);
387     }
388     while (ZOOM_event(MAX_CON, c))
389         ;
390 }
391
392 static void cmd_help (ZOOM_connection *c, ZOOM_resultset *r,
393                       ZOOM_options options,
394                       const char **args)
395 {
396     printf ("connect <zurl>\n");
397     printf ("search <pqf>\n");
398     printf ("show [<start> [<count>]\n");
399     printf ("scan <term>\n");
400     printf ("quit\n");
401     printf ("close <zurl>\n");
402     printf ("set <option> [<value>]\n");
403     printf ("get <option>\n");
404     printf ("\n");
405     printf ("options:\n");
406     printf (" start\n");
407     printf (" count\n");
408     printf (" databaseName\n");
409     printf (" preferredRecordSyntax\n");
410     printf (" proxy\n");
411     printf (" elementSetName\n");
412     printf (" maximumRecordSize\n");
413     printf (" preferredRecordSize\n");
414     printf (" async\n");
415     printf (" piggyback\n");
416     printf (" group\n");
417     printf (" user\n");
418     printf (" password\n");
419     printf (" implementationName\n");
420     printf (" charset\n");
421     printf (" lang\n");
422 }
423
424 static void cmd_connect (ZOOM_connection *c, ZOOM_resultset *r,
425                          ZOOM_options options,
426                          const char **args)
427 {
428     int error;
429     const char *errmsg, *addinfo, *dset;
430     char host[60];
431     int j, i;
432     if (next_token_copy (args, host, sizeof(host)) < 0)
433     {
434         printf ("missing host after connect\n");
435         return ;
436     }
437     for (j = -1, i = 0; i<MAX_CON; i++)
438     {
439         const char *h;
440         if (c[i] && (h = ZOOM_connection_option_get(c[i], "host")) &&
441             !strcmp (h, host))
442         {
443             ZOOM_connection_destroy (c[i]);
444             break;
445         }
446         else if (c[i] == 0 && j == -1)
447             j = i;
448     }
449     if (i == MAX_CON)  /* no match .. */
450     {
451         if (j == -1)
452         {
453             printf ("no more connection available\n");
454             return;
455         }
456         i = j;   /* OK, use this one is available */
457     }
458     c[i] = ZOOM_connection_create (options);
459     ZOOM_connection_connect (c[i], host, 0);
460         
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 }
466
467 static int cmd_parse (ZOOM_connection *c, ZOOM_resultset *r,
468                       ZOOM_options options, 
469                       const char **buf)
470 {
471     int cmd_len;
472     const char *cmd_str;
473
474     cmd_len = next_token (buf, &cmd_str);
475     if (cmd_len < 0)
476         return 1;
477     if (is_command ("quit", cmd_str, cmd_len))
478         return 0;
479     else if (is_command ("set", cmd_str, cmd_len))
480         cmd_set (c, r, options, buf);
481     else if (is_command ("get", cmd_str, cmd_len))
482         cmd_get (c, r, options, buf);
483     else if (is_command ("connect", cmd_str, cmd_len))
484         cmd_connect (c, r, options, buf);
485     else if (is_command ("open", cmd_str, cmd_len))
486         cmd_connect (c, r, options, buf);
487     else if (is_command ("search", cmd_str, cmd_len))
488         cmd_search (c, r, options, buf);
489     else if (is_command ("find", cmd_str, cmd_len))
490         cmd_search (c, r, options, buf);
491     else if (is_command ("show", cmd_str, cmd_len))
492         cmd_show (c, r, options, buf);
493     else if (is_command ("close", cmd_str, cmd_len))
494         cmd_close (c, r, options, buf);
495     else if (is_command ("help", cmd_str, cmd_len))
496         cmd_help(c, r, options, buf);
497     else if (is_command ("ext", cmd_str, cmd_len))
498         cmd_ext(c, r, options, buf);
499     else if (is_command ("debug", cmd_str, cmd_len))
500         cmd_debug(c, r, options, buf);
501     else if (is_command ("scan", cmd_str, cmd_len))
502         cmd_scan(c, r, options, buf);
503     else if (is_command ("sort", cmd_str, cmd_len))
504         cmd_sort(c, r, options, buf);
505     else
506         printf ("unknown command %.*s\n", cmd_len, cmd_str);
507     return 2;
508 }
509
510 void shell(ZOOM_connection *c, ZOOM_resultset *r,
511            ZOOM_options options)
512 {
513     while (1)
514     {
515         char buf[1000];
516         char *cp;
517         const char *bp = buf;
518 #if HAVE_READLINE_READLINE_H
519         char* line_in;
520         line_in=readline("ZOOM>");
521         if (!line_in)
522             break;
523 #if HAVE_READLINE_HISTORY_H
524         if (*line_in)
525             add_history(line_in);
526 #endif
527         if(strlen(line_in) > 999) {
528             printf("Input line too long\n");
529             break;
530         };
531         strcpy(buf,line_in);
532         free (line_in);
533 #else    
534         printf ("ZOOM>"); fflush (stdout);
535         if (!fgets (buf, 999, stdin))
536             break;
537 #endif 
538         if ((cp = strchr(buf, '\n')))
539             *cp = '\0';
540         if (!cmd_parse (c, r, options, &bp))
541             break;
542     }
543 }
544
545 int main (int argc, char **argv)
546 {
547     ZOOM_options options = ZOOM_options_create();
548     int i, res;
549     ZOOM_connection z39_con[MAX_CON];
550     ZOOM_resultset  z39_res[MAX_CON];
551
552     nmem_init();
553     for (i = 0; i<MAX_CON; i++)
554     {
555         z39_con[i] = 0;
556         z39_res[i] = 0;
557     }
558
559     for (i = 0; i<MAX_CON; i++)
560         z39_con[i] = 0;
561
562     res = 1;
563     for (i = 1; i<argc; i++)
564     {
565         const char *bp = argv[i];
566         res = cmd_parse(z39_con, z39_res, options, &bp);
567         if (res == 0)  /* received quit */
568             break;
569     }
570     if (res)  /* do cmdline shell only if not quitting */
571         shell(z39_con, z39_res, options);
572     ZOOM_options_destroy(options);
573
574     for (i = 0; i<MAX_CON; i++)
575     {
576         ZOOM_connection_destroy(z39_con[i]);
577         ZOOM_resultset_destroy(z39_res[i]);
578     }
579     nmem_exit();
580     exit (0);
581 }