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