26aab7b54f840f4bfc48c341be56fccf2c860eb9
[yaz-moved-to-github.git] / zoom / zoomsh.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) 1995-2012 Index Data
3  * See the file LICENSE for details.
4  */
5 /** \file zoomsh.c
6     \brief ZOOM C command line tool (shell)
7 */
8 #if HAVE_CONFIG_H
9 #include <config.h>
10 #endif
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <yaz/wrbuf.h>
16 #include <yaz/log.h>
17 #include <yaz/options.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/log.h>
27 #include <yaz/zoom.h>
28
29 #define MAX_CON 100
30
31 static void process_events(ZOOM_connection *c)
32 {
33     int i;
34
35     yaz_log(YLOG_DEBUG, "process_events");
36     while ((i = ZOOM_event(MAX_CON, c)) != 0)
37     {
38         int peek = ZOOM_connection_peek_event(c[i-1]);
39         int event = ZOOM_connection_last_event(c[i-1]);
40         yaz_log(YLOG_DEBUG, "no = %d peek = %d event = %d %s", i-1,
41                 peek,
42                 event,
43                 ZOOM_get_event_str(event));
44     }
45 }
46
47 static int next_token_chars(const char **cpp, const char **t_start,
48                             const char *tok_chars)
49 {
50     int len = 0;
51     const char *cp = *cpp;
52     while (*cp == ' ')
53         cp++;
54     if (*cp == '"')
55     {
56         cp++;
57         *t_start = cp;
58         while (*cp && *cp != '"')
59         {
60             cp++;
61             len++;
62         }
63         if (*cp)
64             cp++;
65     }
66     else
67     {
68         *t_start = cp;
69         while (*cp && !strchr(tok_chars, *cp))
70         {
71             cp++;
72             len++;
73         }
74         if (len == 0)
75             len = -1;
76     }
77     *cpp = cp;
78     return len;  /* return -1 if no token was read .. */
79 }
80
81 static int next_token(const char **cpp, const char **t_start)
82 {
83     return next_token_chars(cpp, t_start, "\r\n ");
84 }
85
86
87 static WRBUF next_token_new_wrbuf(const char **cpp)
88 {
89     WRBUF w = 0;
90     const char *start;
91     int len = next_token(cpp, &start);
92     if (len < 0)
93         return 0;
94     w = wrbuf_alloc();
95     if (len > 0)
96         wrbuf_write(w, start, len);
97     return w;
98 }
99
100 static int is_command(const char *cmd_str, const char *this_str, int this_len)
101 {
102     int cmd_len = strlen(cmd_str);
103     if (cmd_len != this_len)
104         return 0;
105     if (memcmp(cmd_str, this_str, cmd_len))
106         return 0;
107     return 1;
108 }
109
110 static int cmd_set(ZOOM_connection *c, ZOOM_resultset *r,
111                    ZOOM_options options,
112                    const char **args)
113 {
114     WRBUF key;
115     const char *val_buf;
116     int val_len;
117
118     if (!(key = next_token_new_wrbuf(args)))
119     {
120         printf("missing argument for set\n");
121         return 1;
122     }
123     val_len = next_token_chars(args, &val_buf, "");
124     if (val_len != -1)
125         ZOOM_options_setl(options, wrbuf_cstr(key), val_buf, val_len);
126     else
127         ZOOM_options_set(options, wrbuf_cstr(key), 0);
128     wrbuf_destroy(key);
129     return 0;
130 }
131
132 static int cmd_get(ZOOM_connection *c, ZOOM_resultset *r,
133                    ZOOM_options options,
134                    const char **args)
135 {
136     WRBUF key;
137     if (!(key = next_token_new_wrbuf(args)))
138     {
139         printf("missing argument for get\n");
140         return 1;
141     }
142     else
143     {
144         const char *val = ZOOM_options_get(options, wrbuf_cstr(key));
145         printf("%s = %s\n", wrbuf_cstr(key), val ? val : "<null>");
146         wrbuf_destroy(key);
147     }
148     return 0;
149 }
150
151 static int cmd_rget(ZOOM_connection *c, ZOOM_resultset *r,
152                     ZOOM_options options,
153                      const char **args)
154 {
155     WRBUF key;
156     if (!(key = next_token_new_wrbuf(args)))
157     {
158         printf("missing argument for get\n");
159         return 1;
160     }
161     else
162     {
163         int i;
164         for (i = 0; i<MAX_CON; i++)
165         {
166             const char *val;
167             if (!r[i])
168                 continue;
169             
170             val = ZOOM_resultset_option_get(r[i], wrbuf_cstr(key));
171             printf("%s = %s\n", wrbuf_cstr(key), val ? val : "<null>");
172         }
173         wrbuf_destroy(key);
174     }
175     return 0;
176 }
177
178 static int cmd_close(ZOOM_connection *c, ZOOM_resultset *r,
179                      ZOOM_options options,
180                      const char **args)
181 {
182     WRBUF host;
183     int i;
184     host = next_token_new_wrbuf(args);
185     for (i = 0; i<MAX_CON; i++)
186     {
187         const char *h;
188         if (!c[i])
189             continue;
190         if (!host)
191         {
192             ZOOM_connection_destroy(c[i]);
193             c[i] = 0;
194         }
195         else if ((h = ZOOM_connection_option_get(c[i], "host"))
196                  && !strcmp(h, wrbuf_cstr(host)))
197         {
198             ZOOM_connection_destroy(c[i]);
199             c[i] = 0;
200         }
201     }
202     if (host)
203         wrbuf_destroy(host);
204     return 0;
205 }
206
207 static void display_records(ZOOM_connection c,
208                             ZOOM_resultset r,
209                             size_t start, size_t count, const char *type)
210 {
211     size_t i;
212     for (i = 0; i < count; i++)
213     {
214         size_t pos = i + start;
215         ZOOM_record rec = ZOOM_resultset_record(r, pos);
216         const char *db = ZOOM_record_get(rec, "database", 0);
217         
218         if (ZOOM_record_error(rec, 0, 0, 0))
219         {
220             const char *msg;
221             const char *addinfo;
222             const char *diagset;
223             int error = ZOOM_record_error(rec, &msg, &addinfo, &diagset);
224             
225             printf("%lld %s: %s (%s:%d) %s\n", (long long) pos,
226                    (db ? db : "unknown"),
227                    msg, diagset, error, addinfo ? addinfo : "none");
228         }
229         else
230         {
231             int len;
232             const char *render = ZOOM_record_get(rec, type, &len);
233             const char *syntax = ZOOM_record_get(rec, "syntax", 0);
234             const char *schema = ZOOM_record_get(rec, "schema", 0);
235             /* if rec is non-null, we got a record for display */
236             if (rec)
237             {
238                 printf("%lld database=%s syntax=%s schema=%s\n",
239                        (long long) pos, (db ? db : "unknown"), syntax,
240                        schema ? schema : "unknown");
241                 if (render)
242                 {
243                     if (fwrite(render, 1, len, stdout) != (size_t) len)
244                     {
245                         printf("write to stdout failed\n");
246                     }
247                 }
248                 printf("\n");
249             }
250         }
251     }
252 }
253
254 static int cmd_show(ZOOM_connection *c, ZOOM_resultset *r,
255                     ZOOM_options options,
256                     const char **args)
257 {
258     int i;
259     size_t start = 0, count = 1;
260     const char *type = "render";
261     WRBUF render_str = 0;
262     int ret = 0;
263
264     {
265         WRBUF tmp;
266
267         if ((tmp = next_token_new_wrbuf(args)))
268         {
269             start = atoi(wrbuf_cstr(tmp));
270             wrbuf_destroy(tmp);
271         }
272
273         if ((tmp = next_token_new_wrbuf(args)))
274         {
275             count = atoi(wrbuf_cstr(tmp));
276             wrbuf_destroy(tmp);
277         }
278         render_str = next_token_new_wrbuf(args);
279     }
280     if (render_str)
281         type = wrbuf_cstr(render_str);
282
283     for (i = 0; i < MAX_CON; i++)
284         ZOOM_resultset_records(r[i], 0, start, count);
285     process_events(c);
286
287     for (i = 0; i < MAX_CON; i++)
288     {
289         int error;
290         const char *errmsg, *addinfo, *dset;
291         /* display errors if any */
292         if (!c[i])
293             continue;
294         if ((error = ZOOM_connection_error_x(c[i], &errmsg, &addinfo, &dset)))
295         {
296             printf("%s error: %s (%s:%d) %s\n",
297                    ZOOM_connection_option_get(c[i], "host"), errmsg,
298                    dset, error, addinfo);
299             ret = 1;
300         }
301         else if (r[i])
302         {
303             /* OK, no major errors. Display records... */
304             display_records(c[i], r[i], start, count, type);
305         }
306     }
307     if (render_str)
308         wrbuf_destroy(render_str);
309     return ret;
310 }
311
312 static void display_facets(ZOOM_facet_field *facets, int count) {
313     int index;
314     printf("Facets: \n");
315     for (index = 0; index <  count; index++) {
316         int term_index;
317         const char *facet_name = ZOOM_facet_field_name(facets[index]);
318         printf("  %s: \n", facet_name);
319         for (term_index = 0; term_index < ZOOM_facet_field_term_count(facets[index]); term_index++) {
320             int freq = 0;
321             const char *term = ZOOM_facet_field_get_term(facets[index], term_index, &freq);
322             printf("    %s(%d) \n", term,  freq);
323         }
324     }
325 }
326
327 static int cmd_facets(ZOOM_connection *c, ZOOM_resultset *r,
328                       ZOOM_options options,
329                       const char **args)
330 {
331     int i;
332     int ret = 0;
333
334     process_events(c);
335
336     for (i = 0; i < MAX_CON; i++)
337     {
338         int error;
339         const char *errmsg, *addinfo, *dset;
340         /* display errors if any */
341         if (!c[i])
342             continue;
343         if ((error = ZOOM_connection_error_x(c[i], &errmsg, &addinfo, &dset)))
344         {
345             printf("%s error: %s (%s:%d) %s\n",
346                    ZOOM_connection_option_get(c[i], "host"), errmsg,
347                    dset, error, addinfo);
348             ret = 1;
349         }
350         else if (r[i])
351         {
352             int num_facets = ZOOM_resultset_facets_size(r[i]);
353             if (num_facets) {
354                 ZOOM_facet_field  *facets = ZOOM_resultset_facets(r[i]);
355                 display_facets(facets, num_facets);
356             }
357         }
358     }
359     return ret;
360 }
361
362 static int cmd_suggestions(ZOOM_connection *c, ZOOM_resultset *r, ZOOM_options options, const char **args)
363 {
364     int i;
365     int ret = 0;
366
367     process_events(c);
368
369     for (i = 0; i < MAX_CON; i++)
370     {
371         int error;
372         const char *errmsg, *addinfo, *dset;
373         /* display errors if any */
374         if (!c[i])
375             continue;
376         if ((error = ZOOM_connection_error_x(c[i], &errmsg, &addinfo, &dset)))
377         {
378             printf("%s error: %s (%s:%d) %s\n",
379                    ZOOM_connection_option_get(c[i], "host"), errmsg,
380                    dset, error, addinfo);
381             ret = 1;
382         }
383         else if (r[i])
384         {
385             const char *suggestions = ZOOM_resultset_option_get(r[i], "suggestions");
386             if (suggestions) {
387                 printf("Suggestions: \n%s\n", suggestions);
388             }
389         }
390     }
391     return ret;
392 }
393
394
395 static int cmd_ext(ZOOM_connection *c, ZOOM_resultset *r,
396                    ZOOM_options options,
397                    const char **args)
398 {
399     ZOOM_package p[MAX_CON];
400     int i;
401     int ret = 0;
402     WRBUF ext_type_str = next_token_new_wrbuf(args);
403     
404     if (!ext_type_str)
405     {
406         printf("es: missing type "
407                "(itemorder, create, drop, commit, update, xmlupdate)\n");
408         return 1;
409     }
410     for (i = 0; i<MAX_CON; i++)
411     {
412         if (c[i])
413         {
414             p[i] = ZOOM_connection_package(c[i], 0);
415             ZOOM_package_send(p[i], ext_type_str ? wrbuf_cstr(ext_type_str):0);
416         }
417         else
418             p[i] = 0;
419     }
420
421     process_events(c);
422
423     for (i = 0; i<MAX_CON; i++)
424     {
425         int error;
426         const char *errmsg, *addinfo, *dset;
427         /* display errors if any */
428         if (!p[i])
429             continue;
430         if ((error = ZOOM_connection_error_x(c[i], &errmsg, &addinfo, &dset)))
431         {
432             printf("%s error: %s (%s:%d) %s\n",
433                    ZOOM_connection_option_get(c[i], "host"), errmsg,
434                    dset, error, addinfo);
435             ret = 1;
436         }
437         else if (p[i])
438         {
439             const char *v;
440             printf("ok\n");
441             v = ZOOM_package_option_get(p[i], "targetReference");
442             if (v)
443                 printf("targetReference: %s\n", v);
444             v = ZOOM_package_option_get(p[i], "xmlUpdateDoc");
445             if (v)
446                 printf("xmlUpdateDoc: %s\n", v);
447         }
448         ZOOM_package_destroy(p[i]);
449     }
450     if (ext_type_str)
451         wrbuf_destroy(ext_type_str);
452     return ret;
453 }
454
455 static int cmd_debug(ZOOM_connection *c, ZOOM_resultset *r,
456                      ZOOM_options options,
457                      const char **args)
458 {
459     yaz_log_init_level(YLOG_ALL);
460     return 0;
461 }
462
463 static int cmd_search(ZOOM_connection *c, ZOOM_resultset *r,
464                       ZOOM_options options,
465                       const char **args)
466 {
467     ZOOM_query s;
468     const char *query_str = *args;
469     int i;
470     int ret = 0;
471     
472     s = ZOOM_query_create();
473     while (*query_str == ' ')
474         query_str++;
475     if (memcmp(query_str, "cql:", 4) == 0)
476     {
477         ZOOM_query_cql(s, query_str + 4);
478     }
479     else if (ZOOM_query_prefix(s, query_str))
480     {
481         printf("Bad PQF: %s\n", query_str);
482         ZOOM_query_destroy(s);
483         return 1;
484     }
485     for (i = 0; i<MAX_CON; i++)
486     {
487
488         if (c[i])
489         {
490             ZOOM_resultset_destroy(r[i]);
491             r[i] = 0;
492         }
493         if (c[i])
494             r[i] = ZOOM_connection_search(c[i], s);
495     }
496     ZOOM_query_destroy(s);
497
498     process_events(c);
499
500     for (i = 0; i<MAX_CON; i++)
501     {
502         int error;
503         const char *errmsg, *addinfo, *dset;
504         /* display errors if any */
505         if (!c[i])
506             continue;
507         if ((error = ZOOM_connection_error_x(c[i], &errmsg, &addinfo, &dset)))
508         {
509             printf("%s error: %s (%s:%d) %s\n",
510                    ZOOM_connection_option_get(c[i], "host"), errmsg,
511                    dset, error, addinfo);
512             ret = 1;
513         }
514         else if (r[i])
515         {
516             /* OK, no major errors. Look at the result count */
517             int start = ZOOM_options_get_int(options, "start", 0);
518             int count = ZOOM_options_get_int(options, "count", 0);
519             int facet_num;
520
521             printf("%s: %lld hits\n", ZOOM_connection_option_get(c[i], "host"),
522                    (long long int) ZOOM_resultset_size(r[i]));
523             
524             facet_num = ZOOM_resultset_facets_size(r[i]);
525             if (facet_num)
526             {
527                 ZOOM_facet_field *facets = ZOOM_resultset_facets(r[i]);
528                 int facet_idx;
529                 for (facet_idx = 0; facet_idx < facet_num; facet_idx++)
530                 {
531                     const char *name = ZOOM_facet_field_name(facets[facet_idx]);
532                     size_t term_idx;
533                     size_t term_num = ZOOM_facet_field_term_count(facets[facet_idx]);
534                     printf("facet: %s\n", name);
535                     for (term_idx = 0; term_idx < term_num; term_idx++ )
536                     {
537                         int freq;
538                         const char *term =
539                             ZOOM_facet_field_get_term(facets[facet_idx], term_idx, &freq);
540                         printf("term: %s %d\n", term, freq);
541                     }
542                 }
543             }
544             /* and display */
545             display_records(c[i], r[i], start, count, "render");
546         }
547     }
548     return ret;
549 }
550
551 static int cmd_scan(ZOOM_connection *c, ZOOM_resultset *r,
552                     ZOOM_options options,
553                     const char **args)
554 {
555     const char *query_str = *args;
556     ZOOM_query query = ZOOM_query_create();
557     int i;
558     int ret = 0;
559     ZOOM_scanset s[MAX_CON];
560     
561     while (*query_str == ' ')
562         query_str++;
563
564     if (memcmp(query_str, "cql:", 4) == 0)
565     {
566         ZOOM_query_cql(query, query_str + 4);
567     }
568     else if (ZOOM_query_prefix(query, query_str))
569     {
570         printf("Bad PQF: %s\n", query_str);
571         ZOOM_query_destroy(query);
572         return 1;
573     }
574
575     for (i = 0; i<MAX_CON; i++)
576     {
577         if (c[i])
578             s[i] = ZOOM_connection_scan1(c[i], query);
579         else
580             s[i] = 0;
581     }
582     ZOOM_query_destroy(query);
583
584     process_events(c);
585
586     for (i = 0; i<MAX_CON; i++)
587     {
588         int error;
589         const char *errmsg, *addinfo, *dset;
590         /* display errors if any */
591         if (!c[i])
592             continue;
593         if ((error = ZOOM_connection_error_x(c[i], &errmsg, &addinfo, &dset)))
594         {
595             printf("%s error: %s (%s:%d) %s\n",
596                    ZOOM_connection_option_get(c[i], "host"), errmsg,
597                    dset, error, addinfo);
598             ret = 1;
599         }
600         if (s[i])
601         {
602             size_t p, sz = ZOOM_scanset_size(s[i]);
603             for (p = 0; p < sz; p++)
604             {
605                 size_t occ = 0;
606                 size_t len = 0;
607                 const char *term = ZOOM_scanset_display_term(s[i], p,
608                                                              &occ, &len);
609                 printf("%.*s %lld\n", (int) len, term, (long long int) occ);
610             }            
611             ZOOM_scanset_destroy(s[i]);
612         }
613     }
614     return ret;
615 }
616
617 static int cmd_sort(ZOOM_connection *c, ZOOM_resultset *r,
618                     ZOOM_options options,
619                     const char **args)
620 {
621     const char *sort_spec = *args;
622     int i;
623     int ret = 0;
624     
625     while (*sort_spec == ' ')
626         sort_spec++;
627     
628     for (i = 0; i<MAX_CON; i++)
629     {
630         if (r[i])
631             ZOOM_resultset_sort(r[i], "yaz", sort_spec);
632     }
633     process_events(c);
634     return ret;
635 }
636
637 static int cmd_help(ZOOM_connection *c, ZOOM_resultset *r,
638                     ZOOM_options options,
639                     const char **args)
640 {
641     printf("connect <zurl>\n");
642     printf("search <pqf>\n");
643     printf("show [<start> [<count> [<type]]]\n");
644     printf("facets\n");
645     printf("scan <term>\n");
646     printf("quit\n");
647     printf("close <zurl>\n");
648     printf("ext <type>\n");
649     printf("set <option> [<value>]\n");
650     printf("get <option>\n");
651     printf("\n");
652     printf("options:\n");
653     printf(" start\n");
654     printf(" count\n");
655     printf(" databaseName\n");
656     printf(" preferredRecordSyntax\n");
657     printf(" proxy\n");
658     printf(" elementSetName\n");
659     printf(" maximumRecordSize\n");
660     printf(" preferredRecordSize\n");
661     printf(" async\n");
662     printf(" piggyback\n");
663     printf(" group\n");
664     printf(" user\n");
665     printf(" password\n");
666     printf(" implementationName\n");
667     printf(" charset\n");
668     printf(" lang\n");
669     printf(" timeout\n");
670     printf(" facets\n");
671     printf(" extraArgs\n");
672     printf(" suggestions\n");
673     return 0;
674 }
675
676 static int cmd_connect(ZOOM_connection *c, ZOOM_resultset *r,
677                        ZOOM_options options,
678                        const char **args)
679 {
680     int ret = 0;
681     int error;
682     const char *errmsg, *addinfo, *dset;
683     int j, i;
684     WRBUF host = next_token_new_wrbuf(args);
685     if (!host)
686     {
687         printf("missing host after connect\n");
688         return 1;
689     }
690     for (j = -1, i = 0; i<MAX_CON; i++)
691     {
692         const char *h;
693         if (c[i] && (h = ZOOM_connection_option_get(c[i], "host")) &&
694             !strcmp(h, wrbuf_cstr(host)))
695         {
696             ZOOM_connection_destroy(c[i]);
697             break;
698         }
699         else if (c[i] == 0 && j == -1)
700             j = i;
701     }
702     if (i == MAX_CON)  /* no match .. */
703     {
704         if (j == -1)
705         {
706             printf("no more connection available\n");
707             wrbuf_destroy(host);
708             return 1;
709         }
710         i = j;   /* OK, use this one is available */
711     }
712     c[i] = ZOOM_connection_create(options);
713     ZOOM_connection_connect(c[i], wrbuf_cstr(host), 0);
714         
715     if ((error = ZOOM_connection_error_x(c[i], &errmsg, &addinfo, &dset)))
716     {
717         printf("%s error: %s (%s:%d) %s\n",
718                ZOOM_connection_option_get(c[i], "host"), errmsg,
719                dset, error, addinfo);
720         ret = 1;
721     }
722     wrbuf_destroy(host);
723     return ret;
724 }
725
726 /** \brief parse and execute zoomsh command
727     \param c connections
728     \param r result sets
729     \param options ZOOM options
730     \param buf command string and arguments
731     \retval 0 OK
732     \retval 1 failure to execute
733     \retval -1 EOF (no more commands or quit seen)
734 */
735 static int cmd_parse(ZOOM_connection *c, ZOOM_resultset *r,
736                      ZOOM_options options, 
737                      const char **buf)
738 {
739     int cmd_len;
740     const char *cmd_str;
741     int ret = 0;
742
743     cmd_len = next_token(buf, &cmd_str);
744     if (cmd_len < 0)
745         return -1;
746     if (is_command("quit", cmd_str, cmd_len))
747         return -1;
748     else if (is_command("set", cmd_str, cmd_len))
749         ret = cmd_set(c, r, options, buf);
750     else if (is_command("get", cmd_str, cmd_len))
751         ret = cmd_get(c, r, options, buf);
752     else if (is_command("rget", cmd_str, cmd_len))
753         ret = cmd_rget(c, r, options, buf);
754     else if (is_command("connect", cmd_str, cmd_len))
755         ret = cmd_connect(c, r, options, buf);
756     else if (is_command("open", cmd_str, cmd_len))
757         ret = cmd_connect(c, r, options, buf);
758     else if (is_command("search", cmd_str, cmd_len))
759         ret = cmd_search(c, r, options, buf);
760     else if (is_command("facets", cmd_str, cmd_len))
761         ret = cmd_facets(c, r, options, buf);
762     else if (is_command("find", cmd_str, cmd_len))
763         ret = cmd_search(c, r, options, buf);
764     else if (is_command("show", cmd_str, cmd_len))
765         ret = cmd_show(c, r, options, buf);
766     else if (is_command("suggestions", cmd_str, cmd_len))
767         ret = cmd_suggestions(c, r, options, buf);
768     else if (is_command("close", cmd_str, cmd_len))
769         ret = cmd_close(c, r, options, buf);
770     else if (is_command("help", cmd_str, cmd_len))
771         ret = cmd_help(c, r, options, buf);
772     else if (is_command("ext", cmd_str, cmd_len))
773         ret = cmd_ext(c, r, options, buf);
774     else if (is_command("debug", cmd_str, cmd_len))
775         ret = cmd_debug(c, r, options, buf);
776     else if (is_command("scan", cmd_str, cmd_len))
777         ret = cmd_scan(c, r, options, buf);
778     else if (is_command("sort", cmd_str, cmd_len))
779         ret = cmd_sort(c, r, options, buf);
780     else
781     {
782         printf("unknown command %.*s\n", cmd_len, cmd_str);
783         ret = 1;
784     }
785     return ret;
786 }
787
788 static int shell(ZOOM_connection *c, ZOOM_resultset *r,
789                  ZOOM_options options, int exit_on_error)
790 {
791     int res = 0;
792     while (res == 0)
793     {
794         char buf[100000];
795         char *cp;
796         const char *bp = buf;
797 #if HAVE_READLINE_READLINE_H
798         char* line_in;
799         line_in = readline("ZOOM>");
800         if (!line_in)
801         {
802             res = -1;
803             break;
804         }
805 #if HAVE_READLINE_HISTORY_H
806         if (*line_in)
807             add_history(line_in);
808 #endif
809         if (strlen(line_in) > sizeof(buf)-1)
810         {
811             printf("Input line too long\n");
812             res = 1;
813             break;
814         }
815         strcpy(buf,line_in);
816         free(line_in);
817 #else    
818         printf("ZOOM>"); fflush(stdout);
819         if (!fgets(buf, sizeof(buf)-1, stdin))
820         {
821             res = -1;
822             break;
823         }
824 #endif 
825         if ((cp = strchr(buf, '\n')))
826             *cp = '\0';
827         res = cmd_parse(c, r, options, &bp);
828         if (res == -1)
829             break;
830         if (!exit_on_error && res > 0)
831             res = 0;
832     }
833     return res;
834 }
835
836 static int zoomsh(int argc, char **argv)
837 {
838     ZOOM_options zoom_options = ZOOM_options_create();
839     int i, res = 0; /* -1: EOF; 0 = OK, > 0 ERROR */
840     int exit_on_error = 0;
841     ZOOM_connection z39_con[MAX_CON];
842     ZOOM_resultset  z39_res[MAX_CON];
843
844     for (i = 0; i<MAX_CON; i++)
845     {
846         z39_con[i] = 0;
847         z39_res[i] = 0;
848     }
849     while (res == 0)
850     {
851         int mask;
852         char *arg = 0;
853         int option_ret = options("ev:", argv, argc, &arg);
854         const char *bp = arg;
855         switch (option_ret)
856         {
857         case 0:
858             res = cmd_parse(z39_con, z39_res, zoom_options, &bp);
859             /* returns res == -1 on quit */
860             if (!exit_on_error && res > 0)
861                 res = 0;  /* hide error */
862             break;
863         case YAZ_OPTIONS_EOF:
864             res = shell(z39_con, z39_res, zoom_options, exit_on_error);
865             break;
866         case 'e':
867             exit_on_error = 1;
868             break;
869         case 'v':
870             mask = yaz_log_mask_str(arg);
871             yaz_log_init_level(mask);
872             break;
873         default:
874             fprintf(stderr, "zoomsh: [-e] [-v] [commands]\n");
875             res = 1;
876         }
877     }
878
879     for (i = 0; i<MAX_CON; i++)
880     {
881         ZOOM_connection_destroy(z39_con[i]);
882         ZOOM_resultset_destroy(z39_res[i]);
883     }
884     ZOOM_options_destroy(zoom_options);
885     if (res == -1) /* quit .. which is not an error */
886         res = 0;
887     return res;
888 }
889
890 int main(int argc, char **argv)
891 {
892     int ret = zoomsh(argc, argv);
893     exit(ret);
894 }
895 /*
896  * Local variables:
897  * c-basic-offset: 4
898  * c-file-style: "Stroustrup"
899  * indent-tabs-mode: nil
900  * End:
901  * vim: shiftwidth=4 tabstop=8 expandtab
902  */
903