Added zebrash test
[idzebra-moved-to-github.git] / index / zebrash.c
1 /* $Id: zebrash.c,v 1.18 2003-07-04 14:25:51 heikki Exp $
2    Copyright (C) 2002,2003
3    Index Data Aps
4
5 This file is part of the Zebra server.
6
7 Zebra is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
11
12 Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Zebra; see the file LICENSE.zebra.  If not, write to the
19 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
20 02111-1307, USA.
21 */
22
23 /* 
24    zebrash.c - command-line interface to zebra API
25 */
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h> 
30 #include <ctype.h>
31
32 #if HAVE_READLINE_READLINE_H
33 #include <readline/readline.h> 
34 #endif
35 #if HAVE_READLINE_HISTORY_H
36 #include <readline/history.h>
37 #endif
38
39 #include "zebraapi.h"
40 #include <yaz/log.h>
41 #include <yaz/proto.h>
42 #include <yaz/wrbuf.h>
43
44 #define MAX_NO_ARGS 32
45 #define MAX_OUT_BUFF 4096
46 #define MAX_ARG_LEN 1024
47 #define PROMPT "ZebraSh>"
48 #define DEFAULTCONFIG "./zebra.cfg"
49 #define DEFAULTDATABASE "Default"
50 #define DEFAULTRESULTSET "MyResultSet"
51
52
53 /**************************************
54  * Global variables (yuck!)
55  */
56
57 ZebraService zs=0;  /* our global handle to zebra */
58 ZebraHandle  zh=0;  /* the current session */
59 /* time being, only one session works */
60 int nextrecno=1;  /* record number to show next */
61
62 /**************************************
63  * Help functions
64  */
65
66  
67 static int split_args( char *line, char** args )
68 { /* splits line into individual null-terminated strings, 
69    * returns pointers to them in args */
70   /* FIXME - do we need to handle quoted args ?? */
71     char *p=line;
72     int i=0;
73     int n=0;
74     args[0]=0; /* by default */
75     while (*p==' ' || *p=='\t' || *p=='\n')
76             p++;
77     while (*p)
78     {
79             while (*p==' ' || *p=='\t' || *p=='\n')
80                 p++;
81             if (*p=='#')  /* skip comments */
82                 break;  
83             args[i++]=p;
84             args[i]=0;
85             while (*p && *p!=' ' && *p!='\t' && *p!='\n' && *p!='#')
86                 p++;
87             *p++='\0';
88     }
89     n=i;
90     while (n<MAX_NO_ARGS)
91             args[n++]=0;
92     return i;
93 }
94
95 static char *defarg( char *arg, char *def )
96 {
97     if (!arg)
98             return def;
99     if (!*arg)
100             return def;
101     return arg;
102 }
103 static int defargint( char *arg, int def )
104 {
105     int v=def;
106     char *a=defarg(arg,0);
107     if (a)
108             sscanf(a," %i", &v);
109     return v;
110 }
111
112 static char *restargs( char *args[], int n)
113 { /* Returns the rest of the arguments, starting at the nth, */
114   /* to the end of the command line. Assumes args[0] contains */
115   /* the original line, minus the command itself */
116     int skiplen= args[n]-args[1];
117     if (skiplen > strlen(args[0]))
118         return "";
119     return args[0]+skiplen;
120 }
121
122 int onecommand( char *line, WRBUF outbuff, const char *prevout); 
123  
124 /**************************************
125  * Simple support commands
126  */
127
128 int cmd_echo( char *args[], WRBUF outbuff)
129 {
130     wrbuf_printf(outbuff,"%s\n",restargs(args,1));
131     return 0;
132 }
133  
134 int cmd_quit( char *args[], WRBUF outbuff)
135 {
136     if (zs)
137     {
138         onecommand("zebra_close",outbuff,"");
139         zs=0;
140     }
141     if (zh)
142     {
143         onecommand("zebra_stop",outbuff,"");
144         zh=0;
145     }
146     wrbuf_puts(outbuff, "bye");
147     return -99; /* special stop signal */
148 }
149
150 /**************************************
151  * Tests for starting and stopping zebra, etc
152  */
153  
154 static int cmd_help( char *args[], WRBUF outbuff); 
155  
156 static int cmd_zebra_start( char *args[], WRBUF outbuff)
157 {
158     char *conf=args[1];
159     if (!conf || !*conf) {
160             wrbuf_puts(outbuff,"no config file specified, using "
161                DEFAULTCONFIG "\n" );
162             conf=DEFAULTCONFIG;
163     }
164     zs=zebra_start(conf);
165     if (!zs) {
166             wrbuf_puts(outbuff, "zebra_start failed" );
167             return 2;
168     }
169     return 0; /* ok */
170 }
171  
172 static int cmd_zebra_stop( char *args[], WRBUF outbuff)
173 {
174     if (!zs)
175         wrbuf_puts(outbuff,"zebra seems not to have been started, "
176                "stopping anyway\n");
177     zebra_stop(zs);
178     zs=0;
179     return 0; /* ok */
180 }
181
182 static int cmd_zebra_open( char *args[], WRBUF outbuff)
183 {
184     if (!zs)
185             wrbuf_puts(outbuff,"zebra seems not to have been started, "
186                "trying anyway\n");
187     zh=zebra_open(zs);
188     return 0; /* ok */
189 }
190
191 static int cmd_zebra_close( char *args[], WRBUF outbuff)
192 {
193     if (!zh)
194             wrbuf_puts(outbuff,"Seems like you have not called zebra_open,"
195                "trying anyway\n");
196     zebra_close(zh);
197     return 0; /* ok */
198 }
199
200 static int cmd_quickstart( char *args[], WRBUF outbuff)
201 {
202     char tmp[128];
203     int rc=0;
204     if (!rc)
205         rc=onecommand("yaz_log_file zebrash.log",outbuff,"");
206     if (!rc)
207         rc=onecommand("yaz_log_prefix ZebraSh", outbuff,"");
208     sprintf(tmp, "yaz_log_level 0x%x", LOG_DEFAULT_LEVEL | LOG_APP);
209     if (!rc)
210         rc=onecommand(tmp,outbuff,"");
211     logf(LOG_APP,"quickstart");
212     if (!zs)
213         if (!rc)
214             rc=onecommand("zebra_start",outbuff,"");
215     if (!zh)
216         if (!rc)
217             rc=onecommand("zebra_open",outbuff,"");
218     if (!rc)
219         rc=onecommand("select_database Default",outbuff,"");
220     return rc;
221 }
222
223 /**************************************
224  * Log file handling
225  */
226
227 static int cmd_yaz_log_file( char *args[], WRBUF outbuff)
228 {
229     char *fn = defarg(args[1],0);
230     wrbuf_printf(outbuff, "sending yaz-log to %s\n",fn);
231     yaz_log_init_file(fn);
232     return 0; /* ok */
233 }
234
235 static int cmd_yaz_log_level( char *args[], WRBUF outbuff)
236 {
237     int  lev = defargint(args[1],LOG_DEFAULT_LEVEL);
238     wrbuf_printf(outbuff, "setting yaz-log to level %d (ox%x)\n",lev,lev);
239     yaz_log_init_level(lev);
240     return 0; /* ok */
241 }
242
243 static int cmd_yaz_log_prefix( char *args[], WRBUF outbuff)
244 {
245     char *pref = defarg(args[1],"ZebraSh");
246     wrbuf_printf(outbuff, "setting yaz-log prefix to %s\n",pref);
247     yaz_log_init_prefix(pref);
248     return 0; /* ok */
249 }
250
251 static int cmd_logf( char *args[], WRBUF outbuff)
252 {
253     int lev = defargint(args[1],0);
254     int i=1;  
255     if (lev)
256             i=2;
257     else
258             lev=LOG_LOG; /* this is in the default set!*/
259     logf( lev, restargs(args,i));
260     return 0; /* ok */
261 }
262  
263 /****************
264  * Error handling 
265  */
266 static int cmd_err ( char *args[], WRBUF outbuff)
267 {
268     wrbuf_printf(outbuff, "errCode: %d \nerrStr:  %s\nerrAdd:  %s \n",
269             zebra_errCode (zh),
270             zebra_errString (zh),  
271             zebra_errAdd (zh) );
272     return 0; /* ok */
273 }
274 static int cmd_errcode ( char *args[], WRBUF outbuff)
275 {
276     wrbuf_printf(outbuff, "errCode: %d \n",
277             zebra_errCode (zh));
278     return 0; /* ok */
279 }
280 static int cmd_errstr ( char *args[], WRBUF outbuff)
281 {
282     wrbuf_printf(outbuff, "errStr:  %s\n",
283             zebra_errString (zh));
284     return 0; /* ok */
285 }
286 static int cmd_erradd ( char *args[], WRBUF outbuff)
287 {
288     wrbuf_printf(outbuff, "errAdd:  %s \n",
289             zebra_errAdd (zh) ); 
290     return 0; /* ok */
291 }
292
293 /**************************************
294  * Admin commands
295  */
296
297 static int cmd_init ( char *args[], WRBUF outbuff)
298 {
299     zebra_init(zh);
300     return 0; /* ok */
301 }
302
303 static int cmd_select_database ( char *args[], WRBUF outbuff)
304 {
305     char *db=defarg(args[1],DEFAULTDATABASE);
306         wrbuf_puts(outbuff,"Selecting database 'Default'\n");
307     return zebra_select_database(zh, db);
308 }
309  
310 static int cmd_create_database( char *args[], WRBUF outbuff)
311 {
312     char *db=defarg(args[1],DEFAULTDATABASE);
313     wrbuf_printf(outbuff,"Creating database '%s'\n",db);
314         
315     return zebra_create_database(zh, db);
316 }
317
318 static int cmd_drop_database( char *args[], WRBUF outbuff)
319 {
320     char *db=args[1];
321     if (!db)
322         db="Default";
323     wrbuf_printf(outbuff,"Dropping database '%s'\n",db);
324     return zebra_drop_database(zh, db);
325 }
326
327 static int cmd_begin_trans( char *args[], WRBUF outbuff)
328 {
329     int rw=0;
330     if (args[1] && ( (args[1][0]=='1') || (args[1][0]=='w') ))
331         rw=1;
332     return zebra_begin_trans(zh,rw);
333 }
334
335 static int cmd_end_trans( char *args[], WRBUF outbuff)
336 {
337     return zebra_end_trans(zh);
338 }
339 /*************************************
340  * Inserting and deleting
341  */
342
343 static int cmd_record_insert( char *args[], WRBUF outbuff)
344 {
345     int sysno=0;
346     int rc;
347     char *rec=restargs(args,1);
348     
349     rc=zebra_record_insert(zh,rec, strlen(rec), &sysno);
350     if (0==rc)
351     {
352         wrbuf_printf(outbuff,"ok sysno=%d\n",sysno);
353     }
354     return rc;
355 }
356
357
358 static int cmd_exchange_record( char *args[], WRBUF outbuff)
359 {
360     char *base=args[1];
361     char *id = args[2];
362     char *action = args[3];
363     int rc;
364     char *rec=restargs(args,4);
365     if (!(base && id && action && args[4] ))
366     {
367             wrbuf_puts(outbuff,"Missing arguments!\n");
368             onecommand("help exchange_record", outbuff, "");
369             return -90;
370     }
371     rc=zebra_admin_exchange_record(zh, base, rec, strlen(rec),
372         id, strlen(id), atoi(action));
373     return rc;
374 }
375
376 /**********************************
377  * Searching and retrieving
378  */
379
380 static int cmd_search_pqf( char *args[], WRBUF outbuff)
381 {
382     int hits=0;
383     char *set=args[1];
384     char *qry=restargs(args,2);
385     int rc;
386     rc=zebra_search_PQF(zh, qry, set, &hits);
387     if (0==rc)
388         wrbuf_printf(outbuff,"%d hits found\n",hits);
389     return rc;
390 }
391
392 static int cmd_find( char *args[], WRBUF outbuff)
393 {
394     char *setname=DEFAULTRESULTSET;
395     int rc;
396     int hits=0;
397     WRBUF qry=wrbuf_alloc();
398     if (0==strstr(args[0],"@attr"))
399         wrbuf_puts(qry, "@attr 1=/ ");
400     wrbuf_puts(qry,restargs(args,1));
401     if (!zh)
402             onecommand("quickstart", outbuff, "");
403     wrbuf_printf(outbuff, "find %s\n",wrbuf_buf(qry));
404     rc=zebra_search_PQF(zh, wrbuf_buf(qry), setname, &hits);
405     if (0==rc)
406     {
407         wrbuf_printf(outbuff,"%d hits found\n",hits);
408         nextrecno=1;
409     }
410     wrbuf_free(qry,1);
411     return rc;
412 }
413
414 static int cmd_show( char *args[], WRBUF outbuff)
415 {
416     int start=defargint(args[1], nextrecno);
417     int nrecs=defargint(args[2],1);
418     char *setname=defarg(args[3],DEFAULTRESULTSET);
419     int rc=0;
420     ODR odr;
421     Z_RecordComposition *pcomp=0;
422     int i;
423     oid_value format;
424
425     odr=odr_createmem(ODR_ENCODE);
426     ZebraRetrievalRecord *recs=
427               odr_malloc(odr,sizeof(ZebraRetrievalRecord)*nrecs);
428     rc =z_RecordComposition(odr, &pcomp, 0,"recordComposition");
429     format=oid_getvalbyname ("xml"); /*FIXME - let the user specify*/
430     for (i=0;i<nrecs;i++)
431         recs[i].position=start+i;
432
433     rc = zebra_records_retrieve (zh, odr, setname,
434             pcomp, format, nrecs,recs);
435     printf("rc=%d\n",rc);
436     if (0==rc)
437     {
438         for (i=0;i<nrecs;i++)
439         {
440             printf("Err %d: %d\n",i,recs[i].errCode);
441             if (recs[i].buf)
442             {
443                 wrbuf_printf(outbuff,"Record %d\n", recs[i].position);
444                 wrbuf_write(outbuff, recs[i].buf, recs[i].len);
445                 wrbuf_puts(outbuff, "\n");
446             } else
447                 wrbuf_printf(outbuff,"NO Record %d\n", recs[i].position);
448         }
449         nextrecno=start+nrecs+1;
450     }
451     odr_destroy(odr);
452     return rc;
453 }
454 /**************************************)
455  * Command table, parser, and help 
456  */
457
458 struct cmdstruct
459 {
460     char *cmd;
461     char *args;
462     char *explanation;
463     int (*testfunc)(char *args[], WRBUF outbuff);
464 } ;
465
466  
467 struct cmdstruct cmds[] = {
468     /* special cases:
469      *   if text is 0, does not list the command
470      *   if cmd is "", adds the args (and newline) in command listing
471      */
472     { "", "Starting and stopping:", "", 0 },
473     { "zebra_start", 
474       "[configfile]", 
475       "starts the zebra service. You need to call this first\n"
476       "if no configfile is given, assumes " DEFAULTCONFIG, 
477       cmd_zebra_start },
478     { "zebra_stop",   "", 
479       "stops the zebra service", 
480       cmd_zebra_stop },
481     { "zebra_open", "",  
482       "starts a zebra session. Once you have called zebra_start\n"
483       "you can call zebra_open to start working", 
484       cmd_zebra_open },
485     { "zebra_close", "", 
486       "closes a zebra session", 
487       cmd_zebra_close }, 
488     { "quickstart", "[configfile]", 
489       "Does a zebra_start, zebra_open, and sets up the log", 
490       cmd_quickstart }, 
491   
492     { "", "Log file:","", 0},  
493     { "yaz_log_file", 
494       "[filename]",
495       "Directs the log to filename (or stderr)",
496       cmd_yaz_log_file },
497     { "yaz_log_level", 
498       "[level]",
499       "Sets the logging level (or returns to default)",
500       cmd_yaz_log_level },
501     { "yaz_log_prefix", 
502       "[prefix]",
503       "Sets the log prefix",
504       cmd_yaz_log_prefix},    
505     { "logf", 
506       "[level] text...",
507       "writes an entry in the log",
508       cmd_logf},    
509
510     { "", "Error handling:","", 0},
511     { "err",  "",
512       "Displays zebra's error status (code, str, add)",
513       cmd_err},    
514     { "errcode",  "",
515       "Displays zebra's error code",
516       cmd_errcode},    
517     { "errstr",  "",
518       "Displays zebra's error string",
519       cmd_errstr},    
520     { "erradd",  "",
521       "Displays zebra's additional error message",
522       cmd_erradd},    
523   
524     { "", "Admin:","", 0}, 
525     { "init",  "",
526       "Initializes the zebra database, destroying all data in it",
527       cmd_init},    
528     { "select_database",  "basename",
529       "Selects a database",
530       cmd_select_database},    
531     { "create_database", "basename",
532       "Create database",
533       cmd_create_database},
534     { "drop_database", "basename",
535       "Drop database",
536       cmd_drop_database},
537     { "begin_trans", "[rw]",
538       "Begins a transaction. rw=1 means write, otherwise read-only",
539       cmd_begin_trans},
540     { "end_trans","",
541       "Ends a transaction",
542       cmd_end_trans},
543
544     { "","Updating:","",0},
545     { "record_insert","record",
546       "inserts an sgml record into Default",
547       cmd_record_insert},
548     { "exchange_record","database record-id action record",
549       "inserts (1), updates (2), or deletes (3) a record \n"
550       "record-id must be a unique identifier for the record",
551       cmd_exchange_record},
552     { "","Searching and retrieving:","",0},
553     { "search_pqf","setname query",
554       "search ",
555       cmd_search_pqf},
556     { "find","query",
557       "simplified search",
558       cmd_find},
559     { "f","query",
560       "simplified search",
561       cmd_find},
562     { "show","[start] [numrecs] [resultset]",
563       "shows a result",
564       cmd_show},
565     { "s","[start] [numrecs] [resultset]",
566       "shows a result",
567       cmd_show},
568     { "", "Misc:","", 0}, 
569     { "echo", "string", 
570       "ouputs the string", 
571       cmd_echo },
572     { "q", "", 
573       "exits the program", 
574       cmd_quit },
575     { "quit", "", 
576       "exits the program", 
577       cmd_quit },
578     { "help", "[command]", 
579       "Gives help on command, or lists them all", 
580       cmd_help },
581     { "", "help [command] gives more info on command", "",0 },   
582   
583     {0,0,0,0} /* end marker */
584 };
585  
586 int onecommand( 
587                 char *line,     /* input line */
588                 WRBUF outbuff,  /* output goes here */
589                 const char *prevout) /* prev output, for 'expect' */
590 {
591     int i;
592     char *args[MAX_NO_ARGS];
593     int nargs;
594     char argbuf[MAX_ARG_LEN];
595     logf(LOG_APP,"%s",line);
596     strncpy(argbuf,line, MAX_ARG_LEN-1);
597     argbuf[MAX_ARG_LEN-1]='\0'; /* just to be sure */
598     /*memset(args,'\0',MAX_NO_ARGS*sizeof(char *));*/
599     nargs=split_args(argbuf, args);
600
601 #if 0
602     for (i = 0; i <= n; i++)
603     {
604         const char *cp = args[i];
605         printf ("args %d :%s:\n", i, cp ? cp : "<null>");
606     }
607 #endif
608     if (0==nargs)
609             return -90; /* no command on line, too bad */
610
611     if (0==strcmp(args[0],"expect")) 
612     {
613             char *rest;
614         if (nargs>1) /* args[0] is not yet set, can't use restargs */
615             rest= line + (args[1]-argbuf); /* rest of the line */
616         else
617             return -1; /* need something to expect */
618             if (0==strstr(prevout,rest))
619             {
620                 printf( "Failed expectation, '%s' not found\n", rest);
621             exit(9); 
622             }
623             return 0;
624     }
625     for (i=0;cmds[i].cmd;i++)
626             if (0==strcmp(cmds[i].cmd, args[0])) 
627             {
628                 if (nargs>1)
629                         args[0]= line + (args[1]-argbuf); /* rest of the line */
630                 else
631                         args[0]=""; 
632                 return ((cmds[i].testfunc)(args,outbuff));
633             }
634     wrbuf_printf(outbuff, "Unknown command '%s'. Try help\n",args[0]);
635     logf(LOG_APP,"Unknown command");
636     return -90; 
637 }
638  
639 static int cmd_help( char *args[], WRBUF outbuff)
640
641     int i;
642     int linelen;
643     if (args[1]) 
644     { /* help for a single command */ 
645             for (i=0;cmds[i].cmd;i++)
646                 if (0==strcmp(cmds[i].cmd, args[1])) 
647                 {
648                 wrbuf_printf(outbuff,"%s  %s\n%s\n",
649                              cmds[i].cmd, cmds[i].args, 
650                      cmds[i].explanation);
651                         return 0;
652                 }
653                 wrbuf_printf(outbuff, "Unknown command '%s'", args[1]);
654         }
655     else 
656     { /* list all commands */
657         linelen=9999;
658             for (i=0;cmds[i].cmd;i++)
659         {
660             if (*cmds[i].cmd)
661             { /* ordinary command */
662                 if (linelen>50)
663                 {
664                     wrbuf_puts(outbuff,"\n   ");
665                     linelen=0;
666                 }
667                 linelen += strlen(cmds[i].cmd) + 2;
668                 wrbuf_printf(outbuff,"%s ", cmds[i].cmd);
669             } else
670             { /* section head */
671                 wrbuf_printf(outbuff,"\n%s\n   ",cmds[i].args);
672                 linelen=0;
673             }
674             } /* for */
675         wrbuf_puts(outbuff,"\n");
676     }
677     return 0;
678 }
679  
680 /* If Zebra reports an error after an operation,
681  * append it to the outbuff and log it */
682 static void Zerrors ( WRBUF outbuff)
683 {
684     int ec;
685     if (!zh)
686             return ;
687     ec=zebra_errCode (zh);
688     if (ec)
689     {
690             logf(LOG_APP, "   Zebra error %d: %s, (%s)",
691                     ec, zebra_errString (zh),
692                     zebra_errAdd (zh) );
693             wrbuf_printf(outbuff, "   Zebra error %d: %s, (%s)\n",
694                     ec, zebra_errString (zh),
695                     zebra_errAdd (zh) );
696             zebra_clearError(zh);
697     }
698 }
699   
700 /************************************** 
701  * The shell
702  */
703  
704 void shell()
705 {
706     int rc=0;
707     WRBUF outbuff=wrbuf_alloc();
708     char prevout[MAX_OUT_BUFF]=""; /* previous output for 'expect' */
709     wrbuf_puts(outbuff,"Zebrash at your service");
710     while (rc!=-99)
711     {
712             char buf[MAX_ARG_LEN];
713 #if HAVE_READLINE_READLINE_H
714             char* line_in;
715             line_in=readline(PROMPT);
716             if (!line_in)
717                 break;
718 #if HAVE_READLINE_HISTORY_H
719             if (*line_in)
720                 add_history(line_in);
721 #endif
722             if(strlen(line_in) > MAX_ARG_LEN-1) {
723                 fprintf(stderr,"Input line too long\n");
724                 break;
725             }
726             strcpy(buf,line_in);
727             free (line_in);
728 #else    
729             printf (PROMPT); 
730             fflush (stdout);
731             if (!fgets (buf, MAX_ARG_LEN-1, stdin))
732                 break; 
733 #endif 
734         
735             strncpy(prevout, wrbuf_buf(outbuff), MAX_OUT_BUFF);
736         wrbuf_rewind(outbuff);
737             rc=onecommand(buf, outbuff, prevout);
738             if (rc==0)
739             {
740                 wrbuf_puts(outbuff, "   OK\n");
741                 logf(LOG_APP, "OK");
742             }
743             else if (rc>-90)
744             {
745                 wrbuf_printf(outbuff, "   command returned %d\n",rc);
746             } 
747             Zerrors(outbuff);
748             printf("%s\n", wrbuf_buf(outbuff));
749     } /* while */
750     wrbuf_free(outbuff,1);
751 } /* shell() */
752   
753  
754 /**************************************
755  * Main 
756  */
757  
758 int main (int argc, char ** args)
759 {
760     shell();
761     return 0;
762 } /* main */