2f3897faf66ccff0481639099185ac8f5de4817b
[yaz-moved-to-github.git] / zoom / zoomsh.c
1 /*
2  * $Id: zoomsh.c,v 1.1 2001-10-23 21:00:20 adam 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 #define HAVE_READLINE_READLINE_H 1
13 #define HAVE_READLINE_HISTORY_H 1
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/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     *t_start = cp;
35     while (*cp && *cp != ' ')
36     {
37         cp++;
38         len++;
39     }
40     *cpp = cp;
41     return len;
42 }
43
44 static int next_token_copy (const char **cpp, char *buf_out, int buf_max)
45 {
46     const char *start;
47     int len = next_token (cpp, &start);
48     if (!len)
49     {
50         *buf_out = 0;
51         return 0;
52     }
53     if (len >= buf_max)
54         len = buf_max-1;
55     memcpy (buf_out, start, len);
56     buf_out[len] = '\0';
57     return len;
58 }
59
60 static int is_command (const char *cmd_str, const char *this_str, int this_len)
61 {
62     int cmd_len = strlen(cmd_str);
63     if (cmd_len != this_len)
64         return 0;
65     if (memcmp (cmd_str, this_str, cmd_len))
66         return 0;
67     return 1;
68 }
69
70 static void cmd_set (Z3950_connection *c, Z3950_resultset *r,
71                      Z3950_options options,
72                      const char **args)
73 {
74     char key[40], val[80];
75
76     if (!next_token_copy (args, key, sizeof(key)))
77     {
78         printf ("missing argument for set\n");
79         return ;
80     }
81     if (!next_token_copy (args, val, sizeof(val)))
82     {
83         const char *val = Z3950_options_get(options, key);
84         printf ("%s = %s\n", key, val ? val : "<null>");
85     }
86     else
87         Z3950_options_set(options, key, val);
88 }
89
90 static void cmd_close (Z3950_connection *c, Z3950_resultset *r,
91                        Z3950_options options,
92                        const char **args)
93 {
94     char host[60];
95     int i;
96     next_token_copy (args, host, sizeof(host));
97     for (i = 0; i<MAX_CON; i++)
98     {
99         const char *h;
100         if (!c[i])
101             continue;
102         if ((h = Z3950_connection_host(c[i])) && !strcmp (h, host))
103         {
104             Z3950_connection_destroy (c[i]);
105             c[i] = 0;
106         }
107         else if (*host == '\0')
108         {
109             Z3950_connection_destroy (c[i]);
110             c[i] = 0;
111         }
112     }
113 }
114
115 static void display_records (Z3950_connection c,
116                              Z3950_resultset r,
117                              int start, int count)
118 {
119     int i;
120     for (i = 0; i<count; i++)
121     {
122         int pos = i + start;
123         const char *db = Z3950_resultset_get (r, pos, "database", 0);
124         int len;
125         const char *rec = Z3950_resultset_get (r, pos, "render", &len);
126         const char *syntax = Z3950_resultset_get (r, pos, "syntax", 0);
127         /* if rec is non-null, we got a record for display */
128         if (rec)
129         {
130             printf ("%d %s %s\n", pos+1, (db ? db : "unknown"), syntax);
131             if (rec)
132                 fwrite (rec, 1, len, stdout);
133             putchar ('\n');
134         }
135     }
136 }
137
138 static void cmd_show (Z3950_connection *c, Z3950_resultset *r,
139                       Z3950_options options,
140                       const char **args)
141 {
142     int i;
143     char start_str[10], count_str[10];
144
145     if (next_token_copy (args, start_str, sizeof(start_str)))
146         Z3950_options_set (options, "start", start_str);
147
148     if (next_token_copy (args, count_str, sizeof(count_str)))
149         Z3950_options_set (options, "count", count_str);
150
151     for (i = 0; i<MAX_CON; i++)
152         Z3950_resultset_records (r[i], 0, 0);
153     while (Z3950_event (MAX_CON, c))
154         ;
155
156     for (i = 0; i<MAX_CON; i++)
157     {
158         int error;
159         const char *errmsg, *addinfo;
160         /* display errors if any */
161         if (!c[i])
162             continue;
163         if ((error = Z3950_connection_error(c[i], &errmsg, &addinfo)))
164             fprintf (stderr, "%s error: %s (%d) %s\n",
165                      Z3950_connection_host(c[i]), errmsg,
166                      error, addinfo);
167         else if (r[i])
168         {
169             /* OK, no major errors. Display records... */
170             int start = Z3950_options_get_int (options, "start", 0);
171             int count = Z3950_options_get_int (options, "count", 0);
172             display_records (c[i], r[i], start, count);
173         }
174     }
175 }
176
177 static void cmd_search (Z3950_connection *c, Z3950_resultset *r,
178                         Z3950_options options,
179                         const char **args)
180 {
181     Z3950_search s;
182     int i;
183     
184     s = Z3950_search_create ();
185     if (Z3950_search_prefix (s, *args))
186     {
187         fprintf (stderr, "Bad PQF: %s\n", *args);
188         return;
189     }
190     for (i = 0; i<MAX_CON; i++)
191     {
192         if (c[i])
193         {
194             Z3950_resultset_destroy (r[i]);
195             r[i] = 0;
196         }
197         if (c[i])
198             r[i] = Z3950_connection_search (c[i], s);
199     }
200
201     while (Z3950_event (MAX_CON, c))
202         ;
203
204     for (i = 0; i<MAX_CON; i++)
205     {
206         int error;
207         const char *errmsg, *addinfo;
208         /* display errors if any */
209         if (!c[i])
210             continue;
211         if ((error = Z3950_connection_error(c[i], &errmsg, &addinfo)))
212             fprintf (stderr, "%s error: %s (%d) %s\n",
213                      Z3950_connection_host(c[i]), errmsg,
214                      error, addinfo);
215         else if (r[i])
216         {
217             /* OK, no major errors. Look at the result count */
218             int start = Z3950_options_get_int (options, "start", 0);
219             int count = Z3950_options_get_int (options, "count", 0);
220
221             printf ("%s: %d hits\n", Z3950_connection_host(c[i]),
222                     Z3950_resultset_size(r[i]));
223             /* and display */
224             display_records (c[i], r[i], start, count);
225         }
226     }
227     Z3950_search_destroy (s);
228 }
229
230 static void cmd_help (Z3950_connection *c, Z3950_resultset *r,
231                       Z3950_options options,
232                       const char **args)
233 {
234     printf ("connect <zurl>\n");
235     printf ("search <pqf>\n");
236     printf ("show [<start> [<count>]\n");
237     printf ("quit\n");
238     printf ("close <zurl>\n");
239     printf ("set <option> [<value>]]\n");
240     printf ("\n");
241     printf ("options:\n");
242     printf (" start\n");
243     printf (" count\n");
244     printf (" databaseName\n");
245     printf (" preferredRecordSyntax\n");
246     printf (" proxy\n");
247     printf (" elementSetName\n");
248     printf (" maximumRecordSize\n");
249     printf (" preferredRecordSize\n");
250     printf (" async\n");
251     printf (" piggyback\n");
252     printf (" group\n");
253     printf (" user\n");
254     printf (" pass\n");
255     printf (" implementationName\n");
256 }
257
258 static void cmd_connect (Z3950_connection *c, Z3950_resultset *r,
259                          Z3950_options options,
260                          const char **args)
261 {
262     int error;
263     const char *errmsg, *addinfo;
264     char host[60];
265     int j, i;
266     if (!next_token_copy (args, host, sizeof(host)))
267     {
268         printf ("missing host after connect\n");
269         return ;
270     }
271     for (j = -1, i = 0; i<MAX_CON; i++)
272     {
273         const char *h;
274         if (c[i] && (h = Z3950_connection_host(c[i])) &&
275             !strcmp (h, host))
276         {
277             Z3950_connection_destroy (c[i]);
278             break;
279         }
280         else if (c[i] == 0 && j == -1)
281             j = i;
282     }
283     if (i == MAX_CON)  /* no match .. */
284     {
285         if (j == -1)
286         {
287             printf ("no more connection available\n");
288             return;
289         }
290         i = j;   /* OK, use this one is available */
291     }
292     c[i] = Z3950_connection_create (options);
293     Z3950_connection_connect (c[i], host, 0);
294
295     if ((error = Z3950_connection_error(c[i], &errmsg, &addinfo)))
296         printf ("%s error: %s (%d) %s\n", Z3950_connection_host(c[i]),
297                 errmsg, error, addinfo);
298     
299 }
300
301 static int cmd_parse (Z3950_connection *c, Z3950_resultset *r,
302                       Z3950_options options, 
303                       const char **buf)
304 {
305     int cmd_len;
306     const char *cmd_str;
307
308     cmd_len = next_token (buf, &cmd_str);
309     if (!cmd_len)
310         return 1;
311     if (is_command ("quit", cmd_str, cmd_len))
312         return 0;
313     else if (is_command ("set", cmd_str, cmd_len))
314         cmd_set (c, r, options, buf);
315     else if (is_command ("connect", cmd_str, cmd_len))
316         cmd_connect (c, r, options, buf);
317     else if (is_command ("search", cmd_str, cmd_len))
318         cmd_search (c, r, options, buf);
319     else if (is_command ("show", cmd_str, cmd_len))
320         cmd_show (c, r, options, buf);
321     else if (is_command ("close", cmd_str, cmd_len))
322         cmd_close (c, r, options, buf);
323     else if (is_command ("help", cmd_str, cmd_len))
324         cmd_help(c, r, options, buf);
325     else
326         printf ("unknown command %.*s\n", cmd_len, cmd_str);
327     return 2;
328 }
329
330 void shell(Z3950_connection *c, Z3950_resultset *r, Z3950_options options)
331 {
332     while (1)
333     {
334         char buf[1000];
335         const char *bp = buf;
336 #if HAVE_READLINE_READLINE_H
337         char* line_in;
338         line_in=readline("ZOOM>");
339         if (!line_in)
340             break;
341 #if HAVE_READLINE_HISTORY_H
342         if (*line_in)
343             add_history(line_in);
344 #endif
345         if(strlen(line_in) > 999) {
346             fprintf(stderr,"Input line too long\n");
347             break;
348         };
349         strcpy(buf,line_in);
350         free (line_in);
351 #else    
352         printf ("ZOOM>"); fflush (stdout);
353         if (!fgets (buf, 999, stdin))
354             break;
355 #endif 
356         if (!cmd_parse (c, r, options, &bp))
357             break;
358     }
359 }
360
361 int main (int argc, char **argv)
362 {
363     Z3950_options options = Z3950_options_create();
364     int i, res;
365     Z3950_connection z39_con[MAX_CON];
366     Z3950_resultset  z39_res[MAX_CON];
367     for (i = 0; i<MAX_CON; i++)
368     {
369         z39_con[i] = 0;
370         z39_res[i] = 0;
371     }
372
373     for (i = 0; i<MAX_CON; i++)
374         z39_con[i] = 0;
375
376     res = 1;
377     for (i = 1; i<argc; i++)
378     {
379         const char *bp = argv[i];
380         res = cmd_parse(z39_con, z39_res, options, &bp);
381         if (res == 0)  /* received quit */
382             break;
383     }
384     if (res)  /* do cmdline shell only if not quitting */
385         shell(z39_con, z39_res, options);
386     Z3950_options_destroy(options);
387
388     for (i = 0; i<MAX_CON; i++)
389     {
390         Z3950_connection_destroy(z39_con[i]);
391         Z3950_resultset_destroy(z39_res[i]);
392     }
393     exit (0);
394 }