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