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