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