minor fix
[idzebra-moved-to-github.git] / index / zebrash.c
1 /* $Id: zebrash.c,v 1.19 2003-07-04 14:27:19 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     if (0==rc)
436     {
437         for (i=0;i<nrecs;i++)
438         {
439             printf("Err %d: %d\n",i,recs[i].errCode);
440             if (recs[i].buf)
441             {
442                 wrbuf_printf(outbuff,"Record %d\n", recs[i].position);
443                 wrbuf_write(outbuff, recs[i].buf, recs[i].len);
444                 wrbuf_puts(outbuff, "\n");
445             } else
446                 wrbuf_printf(outbuff,"NO Record %d\n", recs[i].position);
447         }
448         nextrecno=start+nrecs+1;
449     }
450     odr_destroy(odr);
451     return rc;
452 }
453 /**************************************)
454  * Command table, parser, and help 
455  */
456
457 struct cmdstruct
458 {
459     char *cmd;
460     char *args;
461     char *explanation;
462     int (*testfunc)(char *args[], WRBUF outbuff);
463 } ;
464
465  
466 struct cmdstruct cmds[] = {
467     /* special cases:
468      *   if text is 0, does not list the command
469      *   if cmd is "", adds the args (and newline) in command listing
470      */
471     { "", "Starting and stopping:", "", 0 },
472     { "zebra_start", 
473       "[configfile]", 
474       "starts the zebra service. You need to call this first\n"
475       "if no configfile is given, assumes " DEFAULTCONFIG, 
476       cmd_zebra_start },
477     { "zebra_stop",   "", 
478       "stops the zebra service", 
479       cmd_zebra_stop },
480     { "zebra_open", "",  
481       "starts a zebra session. Once you have called zebra_start\n"
482       "you can call zebra_open to start working", 
483       cmd_zebra_open },
484     { "zebra_close", "", 
485       "closes a zebra session", 
486       cmd_zebra_close }, 
487     { "quickstart", "[configfile]", 
488       "Does a zebra_start, zebra_open, and sets up the log", 
489       cmd_quickstart }, 
490   
491     { "", "Log file:","", 0},  
492     { "yaz_log_file", 
493       "[filename]",
494       "Directs the log to filename (or stderr)",
495       cmd_yaz_log_file },
496     { "yaz_log_level", 
497       "[level]",
498       "Sets the logging level (or returns to default)",
499       cmd_yaz_log_level },
500     { "yaz_log_prefix", 
501       "[prefix]",
502       "Sets the log prefix",
503       cmd_yaz_log_prefix},    
504     { "logf", 
505       "[level] text...",
506       "writes an entry in the log",
507       cmd_logf},    
508
509     { "", "Error handling:","", 0},
510     { "err",  "",
511       "Displays zebra's error status (code, str, add)",
512       cmd_err},    
513     { "errcode",  "",
514       "Displays zebra's error code",
515       cmd_errcode},    
516     { "errstr",  "",
517       "Displays zebra's error string",
518       cmd_errstr},    
519     { "erradd",  "",
520       "Displays zebra's additional error message",
521       cmd_erradd},    
522   
523     { "", "Admin:","", 0}, 
524     { "init",  "",
525       "Initializes the zebra database, destroying all data in it",
526       cmd_init},    
527     { "select_database",  "basename",
528       "Selects a database",
529       cmd_select_database},    
530     { "create_database", "basename",
531       "Create database",
532       cmd_create_database},
533     { "drop_database", "basename",
534       "Drop database",
535       cmd_drop_database},
536     { "begin_trans", "[rw]",
537       "Begins a transaction. rw=1 means write, otherwise read-only",
538       cmd_begin_trans},
539     { "end_trans","",
540       "Ends a transaction",
541       cmd_end_trans},
542
543     { "","Updating:","",0},
544     { "record_insert","record",
545       "inserts an sgml record into Default",
546       cmd_record_insert},
547     { "exchange_record","database record-id action record",
548       "inserts (1), updates (2), or deletes (3) a record \n"
549       "record-id must be a unique identifier for the record",
550       cmd_exchange_record},
551     { "","Searching and retrieving:","",0},
552     { "search_pqf","setname query",
553       "search ",
554       cmd_search_pqf},
555     { "find","query",
556       "simplified search",
557       cmd_find},
558     { "f","query",
559       "simplified search",
560       cmd_find},
561     { "show","[start] [numrecs] [resultset]",
562       "shows a result",
563       cmd_show},
564     { "s","[start] [numrecs] [resultset]",
565       "shows a result",
566       cmd_show},
567     { "", "Misc:","", 0}, 
568     { "echo", "string", 
569       "ouputs the string", 
570       cmd_echo },
571     { "q", "", 
572       "exits the program", 
573       cmd_quit },
574     { "quit", "", 
575       "exits the program", 
576       cmd_quit },
577     { "help", "[command]", 
578       "Gives help on command, or lists them all", 
579       cmd_help },
580     { "", "help [command] gives more info on command", "",0 },   
581   
582     {0,0,0,0} /* end marker */
583 };
584  
585 int onecommand( 
586                 char *line,     /* input line */
587                 WRBUF outbuff,  /* output goes here */
588                 const char *prevout) /* prev output, for 'expect' */
589 {
590     int i;
591     char *args[MAX_NO_ARGS];
592     int nargs;
593     char argbuf[MAX_ARG_LEN];
594     logf(LOG_APP,"%s",line);
595     strncpy(argbuf,line, MAX_ARG_LEN-1);
596     argbuf[MAX_ARG_LEN-1]='\0'; /* just to be sure */
597     /*memset(args,'\0',MAX_NO_ARGS*sizeof(char *));*/
598     nargs=split_args(argbuf, args);
599
600 #if 0
601     for (i = 0; i <= n; i++)
602     {
603         const char *cp = args[i];
604         printf ("args %d :%s:\n", i, cp ? cp : "<null>");
605     }
606 #endif
607     if (0==nargs)
608             return -90; /* no command on line, too bad */
609
610     if (0==strcmp(args[0],"expect")) 
611     {
612             char *rest;
613         if (nargs>1) /* args[0] is not yet set, can't use restargs */
614             rest= line + (args[1]-argbuf); /* rest of the line */
615         else
616             return -1; /* need something to expect */
617             if (0==strstr(prevout,rest))
618             {
619                 printf( "Failed expectation, '%s' not found\n", rest);
620             exit(9); 
621             }
622             return 0;
623     }
624     for (i=0;cmds[i].cmd;i++)
625             if (0==strcmp(cmds[i].cmd, args[0])) 
626             {
627                 if (nargs>1)
628                         args[0]= line + (args[1]-argbuf); /* rest of the line */
629                 else
630                         args[0]=""; 
631                 return ((cmds[i].testfunc)(args,outbuff));
632             }
633     wrbuf_printf(outbuff, "Unknown command '%s'. Try help\n",args[0]);
634     logf(LOG_APP,"Unknown command");
635     return -90; 
636 }
637  
638 static int cmd_help( char *args[], WRBUF outbuff)
639
640     int i;
641     int linelen;
642     if (args[1]) 
643     { /* help for a single command */ 
644             for (i=0;cmds[i].cmd;i++)
645                 if (0==strcmp(cmds[i].cmd, args[1])) 
646                 {
647                 wrbuf_printf(outbuff,"%s  %s\n%s\n",
648                              cmds[i].cmd, cmds[i].args, 
649                      cmds[i].explanation);
650                         return 0;
651                 }
652                 wrbuf_printf(outbuff, "Unknown command '%s'", args[1]);
653         }
654     else 
655     { /* list all commands */
656         linelen=9999;
657             for (i=0;cmds[i].cmd;i++)
658         {
659             if (*cmds[i].cmd)
660             { /* ordinary command */
661                 if (linelen>50)
662                 {
663                     wrbuf_puts(outbuff,"\n   ");
664                     linelen=0;
665                 }
666                 linelen += strlen(cmds[i].cmd) + 2;
667                 wrbuf_printf(outbuff,"%s ", cmds[i].cmd);
668             } else
669             { /* section head */
670                 wrbuf_printf(outbuff,"\n%s\n   ",cmds[i].args);
671                 linelen=0;
672             }
673             } /* for */
674         wrbuf_puts(outbuff,"\n");
675     }
676     return 0;
677 }
678  
679 /* If Zebra reports an error after an operation,
680  * append it to the outbuff and log it */
681 static void Zerrors ( WRBUF outbuff)
682 {
683     int ec;
684     if (!zh)
685             return ;
686     ec=zebra_errCode (zh);
687     if (ec)
688     {
689             logf(LOG_APP, "   Zebra error %d: %s, (%s)",
690                     ec, zebra_errString (zh),
691                     zebra_errAdd (zh) );
692             wrbuf_printf(outbuff, "   Zebra error %d: %s, (%s)\n",
693                     ec, zebra_errString (zh),
694                     zebra_errAdd (zh) );
695             zebra_clearError(zh);
696     }
697 }
698   
699 /************************************** 
700  * The shell
701  */
702  
703 void shell()
704 {
705     int rc=0;
706     WRBUF outbuff=wrbuf_alloc();
707     char prevout[MAX_OUT_BUFF]=""; /* previous output for 'expect' */
708     wrbuf_puts(outbuff,"Zebrash at your service");
709     while (rc!=-99)
710     {
711             char buf[MAX_ARG_LEN];
712 #if HAVE_READLINE_READLINE_H
713             char* line_in;
714             line_in=readline(PROMPT);
715             if (!line_in)
716                 break;
717 #if HAVE_READLINE_HISTORY_H
718             if (*line_in)
719                 add_history(line_in);
720 #endif
721             if(strlen(line_in) > MAX_ARG_LEN-1) {
722                 fprintf(stderr,"Input line too long\n");
723                 break;
724             }
725             strcpy(buf,line_in);
726             free (line_in);
727 #else    
728             printf (PROMPT); 
729             fflush (stdout);
730             if (!fgets (buf, MAX_ARG_LEN-1, stdin))
731                 break; 
732 #endif 
733         
734             strncpy(prevout, wrbuf_buf(outbuff), MAX_OUT_BUFF);
735         wrbuf_rewind(outbuff);
736             rc=onecommand(buf, outbuff, prevout);
737             if (rc==0)
738             {
739                 wrbuf_puts(outbuff, "   OK\n");
740                 logf(LOG_APP, "OK");
741             }
742             else if (rc>-90)
743             {
744                 wrbuf_printf(outbuff, "   command returned %d\n",rc);
745             } 
746             Zerrors(outbuff);
747             printf("%s\n", wrbuf_buf(outbuff));
748     } /* while */
749     wrbuf_free(outbuff,1);
750 } /* shell() */
751   
752  
753 /**************************************
754  * Main 
755  */
756  
757 int main (int argc, char ** args)
758 {
759     shell();
760     return 0;
761 } /* main */