a00ebb904b9ac37b13e7f086ccc38e8409dc4199
[ccan] / ccan / tdb2 / tools / tdbtool.c
1 /* 
2    Unix SMB/CIFS implementation.
3    Samba database functions
4    Copyright (C) Andrew Tridgell              1999-2000
5    Copyright (C) Paul `Rusty' Russell              2000
6    Copyright (C) Jeremy Allison                    2000
7    Copyright (C) Andrew Esh                        2001
8
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include <ccan/tdb2/tdb2.h>
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <ctype.h>
27 #include <sys/time.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31 #include <errno.h>
32 #include <string.h>
33 #include <stdarg.h>
34
35 static int do_command(void);
36 const char *cmdname;
37 char *arg1, *arg2;
38 size_t arg1len, arg2len;
39 int bIterate = 0;
40 char *line;
41 TDB_DATA iterate_kbuf;
42 char cmdline[1024];
43 static int disable_mmap;
44
45 enum commands {
46         CMD_CREATE_TDB,
47         CMD_OPEN_TDB,
48         CMD_TRANSACTION_START,
49         CMD_TRANSACTION_COMMIT,
50         CMD_TRANSACTION_CANCEL,
51         CMD_ERASE,
52         CMD_DUMP,
53         CMD_INSERT,
54         CMD_MOVE,
55         CMD_STORE,
56         CMD_SHOW,
57         CMD_KEYS,
58         CMD_HEXKEYS,
59         CMD_DELETE,
60 #if 0
61         CMD_LIST_HASH_FREE,
62         CMD_LIST_FREE,
63 #endif
64         CMD_INFO,
65         CMD_MMAP,
66         CMD_SPEED,
67         CMD_FIRST,
68         CMD_NEXT,
69         CMD_SYSTEM,
70         CMD_CHECK,
71         CMD_QUIT,
72         CMD_HELP
73 };
74
75 typedef struct {
76         const char *name;
77         enum commands cmd;
78 } COMMAND_TABLE;
79
80 COMMAND_TABLE cmd_table[] = {
81         {"create",      CMD_CREATE_TDB},
82         {"open",        CMD_OPEN_TDB},
83 #if 0
84         {"transaction_start",   CMD_TRANSACTION_START},
85         {"transaction_commit",  CMD_TRANSACTION_COMMIT},
86         {"transaction_cancel",  CMD_TRANSACTION_CANCEL},
87 #endif
88         {"erase",       CMD_ERASE},
89         {"dump",        CMD_DUMP},
90         {"insert",      CMD_INSERT},
91         {"move",        CMD_MOVE},
92         {"store",       CMD_STORE},
93         {"show",        CMD_SHOW},
94         {"keys",        CMD_KEYS},
95         {"hexkeys",     CMD_HEXKEYS},
96         {"delete",      CMD_DELETE},
97 #if 0
98         {"list",        CMD_LIST_HASH_FREE},
99         {"free",        CMD_LIST_FREE},
100 #endif
101         {"info",        CMD_INFO},
102         {"speed",       CMD_SPEED},
103         {"mmap",        CMD_MMAP},
104         {"first",       CMD_FIRST},
105         {"1",           CMD_FIRST},
106         {"next",        CMD_NEXT},
107         {"n",           CMD_NEXT},
108         {"check",       CMD_CHECK},
109         {"quit",        CMD_QUIT},
110         {"q",           CMD_QUIT},
111         {"!",           CMD_SYSTEM},
112         {NULL,          CMD_HELP}
113 };
114
115 struct timeval tp1,tp2;
116
117 static void _start_timer(void)
118 {
119         gettimeofday(&tp1,NULL);
120 }
121
122 static double _end_timer(void)
123 {
124         gettimeofday(&tp2,NULL);
125         return((tp2.tv_sec - tp1.tv_sec) + 
126                (tp2.tv_usec - tp1.tv_usec)*1.0e-6);
127 }
128
129 static void tdb_log(struct tdb_context *tdb, enum tdb_log_level level,
130                     void *priv, const char *message)
131 {
132         fputs(message, stderr);
133 }
134
135 /* a tdb tool for manipulating a tdb database */
136
137 static struct tdb_context *tdb;
138
139 static int print_rec(struct tdb_context *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state);
140 static int print_key(struct tdb_context *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state);
141 static int print_hexkey(struct tdb_context *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state);
142
143 static void print_asc(const char *buf,int len)
144 {
145         int i;
146
147         /* We're probably printing ASCII strings so don't try to display
148            the trailing NULL character. */
149
150         if (buf[len - 1] == 0)
151                 len--;
152
153         for (i=0;i<len;i++)
154                 printf("%c",isprint(buf[i])?buf[i]:'.');
155 }
156
157 static void print_data(const char *buf,int len)
158 {
159         int i=0;
160         if (len<=0) return;
161         printf("[%03X] ",i);
162         for (i=0;i<len;) {
163                 printf("%02X ",(int)((unsigned char)buf[i]));
164                 i++;
165                 if (i%8 == 0) printf(" ");
166                 if (i%16 == 0) {      
167                         print_asc(&buf[i-16],8); printf(" ");
168                         print_asc(&buf[i-8],8); printf("\n");
169                         if (i<len) printf("[%03X] ",i);
170                 }
171         }
172         if (i%16) {
173                 int n;
174                 
175                 n = 16 - (i%16);
176                 printf(" ");
177                 if (n>8) printf(" ");
178                 while (n--) printf("   ");
179                 
180                 n = i%16;
181                 if (n > 8) n = 8;
182                 print_asc(&buf[i-(i%16)],n); printf(" ");
183                 n = (i%16) - n;
184                 if (n>0) print_asc(&buf[i-n],n); 
185                 printf("\n");    
186         }
187 }
188
189 static void help(void)
190 {
191         printf("\n"
192 "tdbtool: \n"
193 "  create    dbname     : create a database\n"
194 "  open      dbname     : open an existing database\n"
195 "  openjh    dbname     : open an existing database (jenkins hash)\n"
196 "  transaction_start    : start a transaction\n"
197 "  transaction_commit   : commit a transaction\n"
198 "  transaction_cancel   : cancel a transaction\n"
199 "  erase                : erase the database\n"
200 "  dump                 : dump the database as strings\n"
201 "  keys                 : dump the database keys as strings\n"
202 "  hexkeys              : dump the database keys as hex values\n"
203 "  info                 : print summary info about the database\n"
204 "  insert    key  data  : insert a record\n"
205 "  move      key  file  : move a record to a destination tdb\n"
206 "  store     key  data  : store a record (replace)\n"
207 "  show      key        : show a record by key\n"
208 "  delete    key        : delete a record by key\n"
209 #if 0
210 "  list                 : print the database hash table and freelist\n"
211 "  free                 : print the database freelist\n"
212 #endif
213 "  check                : check the integrity of an opened database\n"
214 "  speed                : perform speed tests on the database\n"
215 "  ! command            : execute system command\n"
216 "  1 | first            : print the first record\n"
217 "  n | next             : print the next record\n"
218 "  q | quit             : terminate\n"
219 "  \\n                   : repeat 'next' command\n"
220 "\n");
221 }
222
223 static void terror(enum TDB_ERROR err, const char *why)
224 {
225         if (err != TDB_SUCCESS)
226                 printf("%s:%s\n", tdb_errorstr(err), why);
227         else
228                 printf("%s\n", why);
229 }
230
231 static void create_tdb(const char *tdbname)
232 {
233         union tdb_attribute log_attr;
234         log_attr.base.attr = TDB_ATTRIBUTE_LOG;
235         log_attr.base.next = NULL;
236         log_attr.log.log_fn = tdb_log;
237
238         if (tdb) tdb_close(tdb);
239         tdb = tdb_open(tdbname, (disable_mmap?TDB_NOMMAP:0),
240                        O_RDWR | O_CREAT | O_TRUNC, 0600, &log_attr);
241         if (!tdb) {
242                 printf("Could not create %s: %s\n", tdbname, strerror(errno));
243         }
244 }
245
246 static void open_tdb(const char *tdbname)
247 {
248         union tdb_attribute log_attr;
249         log_attr.base.attr = TDB_ATTRIBUTE_LOG;
250         log_attr.base.next = NULL;
251         log_attr.log.log_fn = tdb_log;
252
253         if (tdb) tdb_close(tdb);
254         tdb = tdb_open(tdbname, disable_mmap?TDB_NOMMAP:0, O_RDWR, 0600,
255                        &log_attr);
256         if (!tdb) {
257                 printf("Could not open %s: %s\n", tdbname, strerror(errno));
258         }
259 }
260
261 static void insert_tdb(char *keyname, size_t keylen, char* data, size_t datalen)
262 {
263         TDB_DATA key, dbuf;
264         enum TDB_ERROR ecode;
265
266         if ((keyname == NULL) || (keylen == 0)) {
267                 terror(TDB_SUCCESS, "need key");
268                 return;
269         }
270
271         key.dptr = (unsigned char *)keyname;
272         key.dsize = keylen;
273         dbuf.dptr = (unsigned char *)data;
274         dbuf.dsize = datalen;
275
276         ecode = tdb_store(tdb, key, dbuf, TDB_INSERT);
277         if (ecode) {
278                 terror(ecode, "insert failed");
279         }
280 }
281
282 static void store_tdb(char *keyname, size_t keylen, char* data, size_t datalen)
283 {
284         TDB_DATA key, dbuf;
285         enum TDB_ERROR ecode;
286
287         if ((keyname == NULL) || (keylen == 0)) {
288                 terror(TDB_SUCCESS, "need key");
289                 return;
290         }
291
292         if ((data == NULL) || (datalen == 0)) {
293                 terror(TDB_SUCCESS, "need data");
294                 return;
295         }
296
297         key.dptr = (unsigned char *)keyname;
298         key.dsize = keylen;
299         dbuf.dptr = (unsigned char *)data;
300         dbuf.dsize = datalen;
301
302         printf("Storing key:\n");
303         print_rec(tdb, key, dbuf, NULL);
304
305         ecode = tdb_store(tdb, key, dbuf, TDB_REPLACE);
306         if (ecode) {
307                 terror(ecode, "store failed");
308         }
309 }
310
311 static void show_tdb(char *keyname, size_t keylen)
312 {
313         TDB_DATA key, dbuf;
314         enum TDB_ERROR ecode;
315
316         if ((keyname == NULL) || (keylen == 0)) {
317                 terror(TDB_SUCCESS, "need key");
318                 return;
319         }
320
321         key.dptr = (unsigned char *)keyname;
322         key.dsize = keylen;
323
324         ecode = tdb_fetch(tdb, key, &dbuf);
325         if (ecode) {
326                 terror(ecode, "fetch failed");
327                 return;
328         }
329         
330         print_rec(tdb, key, dbuf, NULL);
331         
332         free( dbuf.dptr );
333 }
334
335 static void delete_tdb(char *keyname, size_t keylen)
336 {
337         TDB_DATA key;
338         enum TDB_ERROR ecode;
339
340         if ((keyname == NULL) || (keylen == 0)) {
341                 terror(TDB_SUCCESS, "need key");
342                 return;
343         }
344
345         key.dptr = (unsigned char *)keyname;
346         key.dsize = keylen;
347
348         ecode = tdb_delete(tdb, key);
349         if (ecode) {
350                 terror(ecode, "delete failed");
351         }
352 }
353
354 static void move_rec(char *keyname, size_t keylen, char* tdbname)
355 {
356         TDB_DATA key, dbuf;
357         struct tdb_context *dst_tdb;
358         enum TDB_ERROR ecode;
359
360         if ((keyname == NULL) || (keylen == 0)) {
361                 terror(TDB_SUCCESS, "need key");
362                 return;
363         }
364
365         if ( !tdbname ) {
366                 terror(TDB_SUCCESS, "need destination tdb name");
367                 return;
368         }
369
370         key.dptr = (unsigned char *)keyname;
371         key.dsize = keylen;
372
373         ecode = tdb_fetch(tdb, key, &dbuf);
374         if (ecode) {
375                 terror(ecode, "fetch failed");
376                 return;
377         }
378         
379         print_rec(tdb, key, dbuf, NULL);
380         
381         dst_tdb = tdb_open(tdbname, 0, O_RDWR, 0600, NULL);
382         if ( !dst_tdb ) {
383                 terror(TDB_SUCCESS, "unable to open destination tdb");
384                 return;
385         }
386         
387         ecode = tdb_store( dst_tdb, key, dbuf, TDB_REPLACE);
388         if (ecode)
389                 terror(ecode, "failed to move record");
390         else
391                 printf("record moved\n");
392         
393         tdb_close( dst_tdb );
394 }
395
396 static int print_rec(struct tdb_context *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
397 {
398         printf("\nkey %d bytes\n", (int)key.dsize);
399         print_asc((const char *)key.dptr, key.dsize);
400         printf("\ndata %d bytes\n", (int)dbuf.dsize);
401         print_data((const char *)dbuf.dptr, dbuf.dsize);
402         return 0;
403 }
404
405 static int print_key(struct tdb_context *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
406 {
407         printf("key %d bytes: ", (int)key.dsize);
408         print_asc((const char *)key.dptr, key.dsize);
409         printf("\n");
410         return 0;
411 }
412
413 static int print_hexkey(struct tdb_context *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
414 {
415         printf("key %d bytes\n", (int)key.dsize);
416         print_data((const char *)key.dptr, key.dsize);
417         printf("\n");
418         return 0;
419 }
420
421 static int total_bytes;
422
423 static int traverse_fn(struct tdb_context *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
424 {
425         total_bytes += dbuf.dsize;
426         return 0;
427 }
428
429 static void info_tdb(void)
430 {
431         enum TDB_ERROR ecode;
432         char *summary;
433
434         ecode = tdb_summary(tdb, TDB_SUMMARY_HISTOGRAMS, &summary);
435
436         if (ecode) {
437                 terror(ecode, "Getting summary");
438         } else {
439                 printf("%s", summary);
440                 free(summary);
441         }
442 }
443
444 static void speed_tdb(const char *tlimit)
445 {
446         unsigned timelimit = tlimit?atoi(tlimit):0;
447         double t;
448         int ops;
449         if (timelimit == 0) timelimit = 5;
450
451         ops = 0;
452         printf("Testing store speed for %u seconds\n", timelimit);
453         _start_timer();
454         do {
455                 long int r = random();
456                 TDB_DATA key, dbuf;
457                 key.dptr = (unsigned char *)"store test";
458                 key.dsize = strlen((char *)key.dptr);
459                 dbuf.dptr = (unsigned char *)&r;
460                 dbuf.dsize = sizeof(r);
461                 tdb_store(tdb, key, dbuf, TDB_REPLACE);
462                 t = _end_timer();
463                 ops++;
464         } while (t < timelimit);
465         printf("%10.3f ops/sec\n", ops/t);
466
467         ops = 0;
468         printf("Testing fetch speed for %u seconds\n", timelimit);
469         _start_timer();
470         do {
471                 long int r = random();
472                 TDB_DATA key, dbuf;
473                 key.dptr = (unsigned char *)"store test";
474                 key.dsize = strlen((char *)key.dptr);
475                 dbuf.dptr = (unsigned char *)&r;
476                 dbuf.dsize = sizeof(r);
477                 tdb_fetch(tdb, key, &dbuf);
478                 t = _end_timer();
479                 ops++;
480         } while (t < timelimit);
481         printf("%10.3f ops/sec\n", ops/t);
482
483         ops = 0;
484         printf("Testing transaction speed for %u seconds\n", timelimit);
485         _start_timer();
486         do {
487                 long int r = random();
488                 TDB_DATA key, dbuf;
489                 key.dptr = (unsigned char *)"transaction test";
490                 key.dsize = strlen((char *)key.dptr);
491                 dbuf.dptr = (unsigned char *)&r;
492                 dbuf.dsize = sizeof(r);
493                 tdb_transaction_start(tdb);
494                 tdb_store(tdb, key, dbuf, TDB_REPLACE);
495                 tdb_transaction_commit(tdb);
496                 t = _end_timer();
497                 ops++;
498         } while (t < timelimit);
499         printf("%10.3f ops/sec\n", ops/t);
500
501         ops = 0;
502         printf("Testing traverse speed for %u seconds\n", timelimit);
503         _start_timer();
504         do {
505                 tdb_traverse(tdb, traverse_fn, NULL);
506                 t = _end_timer();
507                 ops++;
508         } while (t < timelimit);
509         printf("%10.3f ops/sec\n", ops/t);
510 }
511
512 static void toggle_mmap(void)
513 {
514         disable_mmap = !disable_mmap;
515         if (disable_mmap) {
516                 printf("mmap is disabled\n");
517         } else {
518                 printf("mmap is enabled\n");
519         }
520 }
521
522 static char *tdb_getline(const char *prompt)
523 {
524         static char thisline[1024];
525         char *p;
526         fputs(prompt, stdout);
527         thisline[0] = 0;
528         p = fgets(thisline, sizeof(thisline)-1, stdin);
529         if (p) p = strchr(p, '\n');
530         if (p) *p = 0;
531         return p?thisline:NULL;
532 }
533
534 static int do_delete_fn(struct tdb_context *the_tdb, TDB_DATA key, TDB_DATA dbuf,
535                      void *state)
536 {
537     return tdb_delete(the_tdb, key);
538 }
539
540 static void first_record(struct tdb_context *the_tdb, TDB_DATA *pkey)
541 {
542         TDB_DATA dbuf;
543         enum TDB_ERROR ecode;
544         ecode = tdb_firstkey(the_tdb, pkey);
545         if (!ecode)
546                 ecode = tdb_fetch(the_tdb, *pkey, &dbuf);
547         if (ecode) terror(ecode, "fetch failed");
548         else {
549                 print_rec(the_tdb, *pkey, dbuf, NULL);
550         }
551 }
552
553 static void next_record(struct tdb_context *the_tdb, TDB_DATA *pkey)
554 {
555         TDB_DATA dbuf;
556         enum TDB_ERROR ecode;
557         ecode = tdb_nextkey(the_tdb, pkey);
558
559         if (!ecode)
560                 ecode = tdb_fetch(the_tdb, *pkey, &dbuf);
561         if (ecode) 
562                 terror(ecode, "fetch failed");
563         else
564                 print_rec(the_tdb, *pkey, dbuf, NULL);
565 }
566
567 static void check_db(struct tdb_context *the_tdb)
568 {
569         if (!the_tdb) {
570                 printf("Error: No database opened!\n");
571         } else {
572                 if (tdb_check(the_tdb, NULL, NULL) != 0)
573                         printf("Integrity check for the opened database failed.\n");
574                 else
575                         printf("Database integrity is OK.\n");
576         }
577 }
578
579 static int do_command(void)
580 {
581         COMMAND_TABLE *ctp = cmd_table;
582         enum commands mycmd = CMD_HELP;
583         int cmd_len;
584
585         if (cmdname && strlen(cmdname) == 0) {
586                 mycmd = CMD_NEXT;
587         } else {
588                 while (ctp->name) {
589                         cmd_len = strlen(ctp->name);
590                         if (strncmp(ctp->name,cmdname,cmd_len) == 0) {
591                                 mycmd = ctp->cmd;
592                                 break;
593                         }
594                         ctp++;
595                 }
596         }
597
598         switch (mycmd) {
599         case CMD_CREATE_TDB:
600                 bIterate = 0;
601                 create_tdb(arg1);
602                 return 0;
603         case CMD_OPEN_TDB:
604                 bIterate = 0;
605                 open_tdb(arg1);
606                 return 0;
607         case CMD_SYSTEM:
608                 /* Shell command */
609                 if (system(arg1) == -1) {
610                         terror(TDB_SUCCESS, "system() call failed\n");
611                 }
612                 return 0;
613         case CMD_QUIT:
614                 return 1;
615         default:
616                 /* all the rest require a open database */
617                 if (!tdb) {
618                         bIterate = 0;
619                         terror(TDB_SUCCESS, "database not open");
620                         help();
621                         return 0;
622                 }
623                 switch (mycmd) {
624                 case CMD_TRANSACTION_START:
625                         bIterate = 0;
626                         tdb_transaction_start(tdb);
627                         return 0;
628                 case CMD_TRANSACTION_COMMIT:
629                         bIterate = 0;
630                         tdb_transaction_commit(tdb);
631                         return 0;
632                 case CMD_TRANSACTION_CANCEL:
633                         bIterate = 0;
634                         tdb_transaction_cancel(tdb);
635                         return 0;
636                 case CMD_ERASE:
637                         bIterate = 0;
638                         tdb_traverse(tdb, do_delete_fn, NULL);
639                         return 0;
640                 case CMD_DUMP:
641                         bIterate = 0;
642                         tdb_traverse(tdb, print_rec, NULL);
643                         return 0;
644                 case CMD_INSERT:
645                         bIterate = 0;
646                         insert_tdb(arg1, arg1len,arg2,arg2len);
647                         return 0;
648                 case CMD_MOVE:
649                         bIterate = 0;
650                         move_rec(arg1,arg1len,arg2);
651                         return 0;
652                 case CMD_STORE:
653                         bIterate = 0;
654                         store_tdb(arg1,arg1len,arg2,arg2len);
655                         return 0;
656                 case CMD_SHOW:
657                         bIterate = 0;
658                         show_tdb(arg1, arg1len);
659                         return 0;
660                 case CMD_KEYS:
661                         tdb_traverse(tdb, print_key, NULL);
662                         return 0;
663                 case CMD_HEXKEYS:
664                         tdb_traverse(tdb, print_hexkey, NULL);
665                         return 0;
666                 case CMD_DELETE:
667                         bIterate = 0;
668                         delete_tdb(arg1,arg1len);
669                         return 0;
670 #if 0
671                 case CMD_LIST_HASH_FREE:
672                         tdb_dump_all(tdb);
673                         return 0;
674                 case CMD_LIST_FREE:
675                         tdb_printfreelist(tdb);
676                         return 0;
677 #endif
678                 case CMD_INFO:
679                         info_tdb();
680                         return 0;
681                 case CMD_SPEED:
682                         speed_tdb(arg1);
683                         return 0;
684                 case CMD_MMAP:
685                         toggle_mmap();
686                         return 0;
687                 case CMD_FIRST:
688                         bIterate = 1;
689                         first_record(tdb, &iterate_kbuf);
690                         return 0;
691                 case CMD_NEXT:
692                         if (bIterate)
693                                 next_record(tdb, &iterate_kbuf);
694                         return 0;
695                 case CMD_CHECK:
696                         check_db(tdb);
697                         return 0;
698                 case CMD_HELP:
699                         help();
700                         return 0;
701                 case CMD_CREATE_TDB:
702                 case CMD_OPEN_TDB:
703                 case CMD_SYSTEM:
704                 case CMD_QUIT:
705                         /*
706                          * unhandled commands.  cases included here to avoid compiler
707                          * warnings.
708                          */
709                         return 0;
710                 }
711         }
712
713         return 0;
714 }
715
716 static char *convert_string(char *instring, size_t *sizep)
717 {
718         size_t length = 0;
719         char *outp, *inp;
720         char temp[3];
721
722         outp = inp = instring;
723
724         while (*inp) {
725                 if (*inp == '\\') {
726                         inp++;
727                         if (*inp && strchr("0123456789abcdefABCDEF",(int)*inp)) {
728                                 temp[0] = *inp++;
729                                 temp[1] = '\0';
730                                 if (*inp && strchr("0123456789abcdefABCDEF",(int)*inp)) {
731                                         temp[1] = *inp++;
732                                         temp[2] = '\0';
733                                 }
734                                 *outp++ = (char)strtol((const char *)temp,NULL,16);
735                         } else {
736                                 *outp++ = *inp++;
737                         }
738                 } else {
739                         *outp++ = *inp++;
740                 }
741                 length++;
742         }
743         *sizep = length;
744         return instring;
745 }
746
747 int main(int argc, char *argv[])
748 {
749         cmdname = "";
750         arg1 = NULL;
751         arg1len = 0;
752         arg2 = NULL;
753         arg2len = 0;
754
755         if (argv[1]) {
756                 cmdname = "open";
757                 arg1 = argv[1];
758                 do_command();
759                 cmdname =  "";
760                 arg1 = NULL;
761         }
762
763         switch (argc) {
764         case 1:
765         case 2:
766                 /* Interactive mode */
767                 while ((cmdname = tdb_getline("tdb> "))) {
768                         arg2 = arg1 = NULL;
769                         if ((arg1 = strchr((const char *)cmdname,' ')) != NULL) {
770                                 arg1++;
771                                 arg2 = arg1;
772                                 while (*arg2) {
773                                         if (*arg2 == ' ') {
774                                                 *arg2++ = '\0';
775                                                 break;
776                                         }
777                                         if ((*arg2++ == '\\') && (*arg2 == ' ')) {
778                                                 arg2++;
779                                         }
780                                 }
781                         }
782                         if (arg1) arg1 = convert_string(arg1,&arg1len);
783                         if (arg2) arg2 = convert_string(arg2,&arg2len);
784                         if (do_command()) break;
785                 }
786                 break;
787         case 5:
788                 arg2 = convert_string(argv[4],&arg2len);
789         case 4:
790                 arg1 = convert_string(argv[3],&arg1len);
791         case 3:
792                 cmdname = argv[2];
793         default:
794                 do_command();
795                 break;
796         }
797
798         if (tdb) tdb_close(tdb);
799
800         return 0;
801 }