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