display_records() now displays the record syntax OID as well as its symbolic name...
[yaz-moved-to-github.git] / zoom / zoomsh.c
1 /*
2  * $Id: zoomsh.c,v 1.21 2003-07-09 23:00:21 mike 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 const char *oid_name_to_dotstring(const char *name) {
147     struct oident ent;
148     int oid[OID_SIZE];
149     static char oidbuf[100];    /* ### bad interface */
150     int i;
151
152     /* Translate syntax to oid_val */
153     oid_value value = oid_getvalbyname(name);
154
155     /* Build it into an oident */
156     ent.proto = PROTO_Z3950;
157     ent.oclass = CLASS_RECSYN;
158     ent.value = value;
159
160     /* Translate to an array of int */
161     (void) oid_ent_to_oid(&ent, oid);
162
163     /* Write the array of int into a dotted string (phew!) */
164     oidbuf[0] = '\0';
165     for (i = 0; oid[i] != -1; i++) {
166         char tmpbuf[20];
167         sprintf(tmpbuf, "%d", oid[i]);
168         if (i > 0) strcat(oidbuf, ".");
169         strcat(oidbuf, tmpbuf);
170     }
171
172     return oidbuf;
173 }
174
175 static void display_records (ZOOM_connection c,
176                              ZOOM_resultset r,
177                              int start, int count)
178 {
179     int i;
180     for (i = 0; i<count; i++)
181     {
182         int pos = i + start;
183         ZOOM_record rec = ZOOM_resultset_record (r, pos);
184         const char *db = ZOOM_record_get (rec, "database", 0);
185         int len;
186         const char *render = ZOOM_record_get (rec, "render", &len);
187         const char *syntax = ZOOM_record_get (rec, "syntax", 0);
188         /* if rec is non-null, we got a record for display */
189         if (rec)
190         {
191             const char *syntax_oid = oid_name_to_dotstring(syntax);
192             printf ("%d %s %s (%s)\n",
193                     pos+1, (db ? db : "unknown"), syntax, syntax_oid);
194             if (render)
195                 fwrite (render, 1, len, stdout);
196             printf ("\n");
197         }
198     }
199 }
200
201 static void cmd_show (ZOOM_connection *c, ZOOM_resultset *r,
202                       ZOOM_options options,
203                       const char **args)
204 {
205     int i;
206     char start_str[10], count_str[10];
207
208     if (next_token_copy (args, start_str, sizeof(start_str)) >= 0)
209         ZOOM_options_set (options, "start", start_str);
210
211     if (next_token_copy (args, count_str, sizeof(count_str)) >= 0)
212         ZOOM_options_set (options, "count", count_str);
213
214     for (i = 0; i<MAX_CON; i++)
215         ZOOM_resultset_records (r[i], 0, atoi(start_str), atoi(count_str));
216     while (ZOOM_event (MAX_CON, c))
217         ;
218
219     for (i = 0; i<MAX_CON; i++)
220     {
221         int error;
222         const char *errmsg, *addinfo, *dset;
223         /* display errors if any */
224         if (!c[i])
225             continue;
226         if ((error = ZOOM_connection_error_x(c[i], &errmsg, &addinfo, &dset)))
227             printf ("%s error: %s (%s:%d) %s\n",
228                      ZOOM_connection_option_get(c[i], "host"), errmsg,
229                      dset, error, addinfo);
230         else if (r[i])
231         {
232             /* OK, no major errors. Display records... */
233             int start = ZOOM_options_get_int (options, "start", 0);
234             int count = ZOOM_options_get_int (options, "count", 0);
235             display_records (c[i], r[i], start, count);
236         }
237     }
238     ZOOM_options_set (options, "count", "0");
239     ZOOM_options_set (options, "start", "0");
240 }
241
242 static void cmd_ext (ZOOM_connection *c, ZOOM_resultset *r,
243                      ZOOM_options options,
244                      const char **args)
245 {
246     ZOOM_package p[MAX_CON];
247     
248     int i;
249     
250     for (i = 0; i<MAX_CON; i++)
251     {
252         if (c[i])
253         {
254             p[i] = ZOOM_connection_package (c[i], 0);
255             ZOOM_package_send(p[i], "itemorder");
256         }
257         else
258             p[i] = 0;
259     }
260
261     while (ZOOM_event (MAX_CON, c))
262         ;
263
264     for (i = 0; i<MAX_CON; i++)
265     {
266         int error;
267         const char *errmsg, *addinfo, *dset;
268         /* display errors if any */
269         if (!p[i])
270             continue;
271         if ((error = ZOOM_connection_error_x(c[i], &errmsg, &addinfo, &dset)))
272             printf ("%s error: %s (%s:%d) %s\n",
273                      ZOOM_connection_option_get(c[i], "host"), errmsg,
274                      dset, error, addinfo);
275         else if (p[i])
276         {
277             printf ("ok\n");
278         }
279         ZOOM_package_destroy (p[i]);
280     }
281 }
282
283 static void cmd_debug (ZOOM_connection *c, ZOOM_resultset *r,
284                        ZOOM_options options,
285                        const char **args)
286 {
287     yaz_log_init_level(LOG_ALL);
288 }
289
290 static void cmd_search (ZOOM_connection *c, ZOOM_resultset *r,
291                         ZOOM_options options,
292                         const char **args)
293 {
294     ZOOM_query s;
295     const char *query_str = *args;
296     int i;
297     
298     s = ZOOM_query_create ();
299     while (*query_str == ' ')
300         query_str++;
301     if (memcmp(query_str, "cql:", 4) == 0)
302     {
303         ZOOM_query_cql (s, query_str + 4);
304     }
305     else if (ZOOM_query_prefix (s, query_str))
306     {
307         printf ("Bad PQF: %s\n", query_str);
308         return;
309     }
310     for (i = 0; i<MAX_CON; i++)
311     {
312         if (c[i])
313         {
314             ZOOM_resultset_destroy (r[i]);
315             r[i] = 0;
316         }
317         if (c[i])
318             r[i] = ZOOM_connection_search (c[i], s);
319     }
320
321     while (ZOOM_event (MAX_CON, c))
322         ;
323
324     for (i = 0; i<MAX_CON; i++)
325     {
326         int error;
327         const char *errmsg, *addinfo, *dset;
328         /* display errors if any */
329         if (!c[i])
330             continue;
331         if ((error = ZOOM_connection_error_x(c[i], &errmsg, &addinfo, &dset)))
332             printf ("%s error: %s (%s:%d) %s\n",
333                     ZOOM_connection_option_get(c[i], "host"), errmsg,
334                     dset, error, addinfo);
335         else if (r[i])
336         {
337             /* OK, no major errors. Look at the result count */
338             int start = ZOOM_options_get_int (options, "start", 0);
339             int count = ZOOM_options_get_int (options, "count", 0);
340
341             printf ("%s: %d hits\n", ZOOM_connection_option_get(c[i], "host"),
342                     ZOOM_resultset_size(r[i]));
343             /* and display */
344             display_records (c[i], r[i], start, count);
345         }
346     }
347     ZOOM_query_destroy (s);
348 }
349
350 static void cmd_scan (ZOOM_connection *c, ZOOM_resultset *r,
351                       ZOOM_options options,
352                       const char **args)
353 {
354     const char *start_term = *args;
355     int i;
356     ZOOM_scanset s[MAX_CON];
357     
358     while (*start_term == ' ')
359         start_term++;
360
361     for (i = 0; i<MAX_CON; i++)
362     {
363         if (c[i])
364             s[i] = ZOOM_connection_scan(c[i], start_term);
365         else
366             s[i] = 0;
367     }
368     while (ZOOM_event(MAX_CON, c))
369         ;
370     for (i = 0; i<MAX_CON; i++)
371     {
372         if (s[i]) {
373             size_t p, sz = ZOOM_scanset_size(s[i]);
374             for (p = 0; p < sz; p++)
375             {
376                 int occ = 0;
377                 int len = 0;
378                 const char *term = ZOOM_scanset_term(s[i], p, &occ, &len);
379                 fwrite(term, 1, len, stdout);
380                 printf (" %d\n", occ);
381             }            
382             ZOOM_scanset_destroy(s[i]);
383         }
384     }
385 }
386
387 static void cmd_help (ZOOM_connection *c, ZOOM_resultset *r,
388                       ZOOM_options options,
389                       const char **args)
390 {
391     printf ("connect <zurl>\n");
392     printf ("search <pqf>\n");
393     printf ("show [<start> [<count>]\n");
394     printf ("scan <term>\n");
395     printf ("quit\n");
396     printf ("close <zurl>\n");
397     printf ("set <option> [<value>]\n");
398     printf ("get <option>\n");
399     printf ("\n");
400     printf ("options:\n");
401     printf (" start\n");
402     printf (" count\n");
403     printf (" databaseName\n");
404     printf (" preferredRecordSyntax\n");
405     printf (" proxy\n");
406     printf (" elementSetName\n");
407     printf (" maximumRecordSize\n");
408     printf (" preferredRecordSize\n");
409     printf (" async\n");
410     printf (" piggyback\n");
411     printf (" group\n");
412     printf (" user\n");
413     printf (" pass\n");
414     printf (" implementationName\n");
415     printf (" charset\n");
416     printf (" lang\n");
417 }
418
419 static void cmd_connect (ZOOM_connection *c, ZOOM_resultset *r,
420                          ZOOM_options options,
421                          const char **args)
422 {
423     int error;
424     const char *errmsg, *addinfo, *dset;
425     char host[60];
426     int j, i;
427     if (next_token_copy (args, host, sizeof(host)) < 0)
428     {
429         printf ("missing host after connect\n");
430         return ;
431     }
432     for (j = -1, i = 0; i<MAX_CON; i++)
433     {
434         const char *h;
435         if (c[i] && (h = ZOOM_connection_option_get(c[i], "host")) &&
436             !strcmp (h, host))
437         {
438             ZOOM_connection_destroy (c[i]);
439             break;
440         }
441         else if (c[i] == 0 && j == -1)
442             j = i;
443     }
444     if (i == MAX_CON)  /* no match .. */
445     {
446         if (j == -1)
447         {
448             printf ("no more connection available\n");
449             return;
450         }
451         i = j;   /* OK, use this one is available */
452     }
453     c[i] = ZOOM_connection_create (options);
454     ZOOM_connection_connect (c[i], host, 0);
455         
456     if ((error = ZOOM_connection_error_x(c[i], &errmsg, &addinfo, &dset)))
457        printf ("%s error: %s (%s:%d) %s\n",
458             ZOOM_connection_option_get(c[i], "host"), errmsg,
459             dset, error, addinfo);
460 }
461
462 static int cmd_parse (ZOOM_connection *c, ZOOM_resultset *r,
463                       ZOOM_options options, 
464                       const char **buf)
465 {
466     int cmd_len;
467     const char *cmd_str;
468
469     cmd_len = next_token (buf, &cmd_str);
470     if (cmd_len < 0)
471         return 1;
472     if (is_command ("quit", cmd_str, cmd_len))
473         return 0;
474     else if (is_command ("set", cmd_str, cmd_len))
475         cmd_set (c, r, options, buf);
476     else if (is_command ("get", cmd_str, cmd_len))
477         cmd_get (c, r, options, buf);
478     else if (is_command ("connect", cmd_str, cmd_len))
479         cmd_connect (c, r, options, buf);
480     else if (is_command ("open", cmd_str, cmd_len))
481         cmd_connect (c, r, options, buf);
482     else if (is_command ("search", cmd_str, cmd_len))
483         cmd_search (c, r, options, buf);
484     else if (is_command ("find", cmd_str, cmd_len))
485         cmd_search (c, r, options, buf);
486     else if (is_command ("show", cmd_str, cmd_len))
487         cmd_show (c, r, options, buf);
488     else if (is_command ("close", cmd_str, cmd_len))
489         cmd_close (c, r, options, buf);
490     else if (is_command ("help", cmd_str, cmd_len))
491         cmd_help(c, r, options, buf);
492     else if (is_command ("ext", cmd_str, cmd_len))
493         cmd_ext(c, r, options, buf);
494     else if (is_command ("debug", cmd_str, cmd_len))
495         cmd_debug(c, r, options, buf);
496     else if (is_command ("scan", cmd_str, cmd_len))
497         cmd_scan(c, r, options, buf);
498     else
499         printf ("unknown command %.*s\n", cmd_len, cmd_str);
500     return 2;
501 }
502
503 void shell(ZOOM_connection *c, ZOOM_resultset *r,
504            ZOOM_options options)
505 {
506     while (1)
507     {
508         char buf[1000];
509         char *cp;
510         const char *bp = buf;
511 #if HAVE_READLINE_READLINE_H
512         char* line_in;
513         line_in=readline("ZOOM>");
514         if (!line_in)
515             break;
516 #if HAVE_READLINE_HISTORY_H
517         if (*line_in)
518             add_history(line_in);
519 #endif
520         if(strlen(line_in) > 999) {
521             printf("Input line too long\n");
522             break;
523         };
524         strcpy(buf,line_in);
525         free (line_in);
526 #else    
527         printf ("ZOOM>"); fflush (stdout);
528         if (!fgets (buf, 999, stdin))
529             break;
530 #endif 
531         if ((cp = strchr(buf, '\n')))
532             *cp = '\0';
533         if (!cmd_parse (c, r, options, &bp))
534             break;
535     }
536 }
537
538 int main (int argc, char **argv)
539 {
540     ZOOM_options options = ZOOM_options_create();
541     int i, res;
542     ZOOM_connection z39_con[MAX_CON];
543     ZOOM_resultset  z39_res[MAX_CON];
544
545     nmem_init();
546     for (i = 0; i<MAX_CON; i++)
547     {
548         z39_con[i] = 0;
549         z39_res[i] = 0;
550     }
551
552     for (i = 0; i<MAX_CON; i++)
553         z39_con[i] = 0;
554
555     res = 1;
556     for (i = 1; i<argc; i++)
557     {
558         const char *bp = argv[i];
559         res = cmd_parse(z39_con, z39_res, options, &bp);
560         if (res == 0)  /* received quit */
561             break;
562     }
563     if (res)  /* do cmdline shell only if not quitting */
564         shell(z39_con, z39_res, options);
565     ZOOM_options_destroy(options);
566
567     for (i = 0; i<MAX_CON; i++)
568     {
569         ZOOM_connection_destroy(z39_con[i]);
570         ZOOM_resultset_destroy(z39_res[i]);
571     }
572     nmem_exit();
573     exit (0);
574 }