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