1 /* This file is part of GDBM, the GNU data base manager.
2    Copyright (C) 1990-2021 Free Software Foundation, Inc.
3 
4    GDBM is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3, or (at your option)
7    any later version.
8 
9    GDBM is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with GDBM. If not, see <http://www.gnu.org/licenses/>.    */
16 
17 #include "gdbmtool.h"
18 #include "gdbm.h"
19 #include "gram.h"
20 
21 #include <errno.h>
22 #include <ctype.h>
23 #include <signal.h>
24 #include <pwd.h>
25 #include <sys/ioctl.h>
26 #include <sys/wait.h>
27 #include <sys/time.h>
28 #include <sys/resource.h>
29 #include <termios.h>
30 #include <stdarg.h>
31 #ifdef HAVE_LOCALE_H
32 # include <locale.h>
33 #endif
34 
35 static GDBM_FILE gdbm_file = NULL;   /* Database to operate upon */
36 static datum key_data;               /* Current key */
37 static datum return_data;            /* Current data */
38 
39 /* Return values for hanlders: */
40 enum
41   {
42     GDBMSHELL_OK,       /* Success */
43     GDBMSHELL_GDBM_ERR, /* GDBM error */
44     GDBMSHELL_SYNTAX,   /* Syntax error (invalid argument etc) */
45     GDBMSHELL_ERR,      /* Other error */
46     GDBMSHELL_CANCEL    /* Operation canceled */
47   };
48 
49 static void
datum_free(datum * dp)50 datum_free (datum *dp)
51 {
52   free (dp->dptr);
53   dp->dptr = NULL;
54 }
55 
56 
57 int
gdbmshell_setopt(char * name,int opt,int val)58 gdbmshell_setopt (char *name, int opt, int val)
59 {
60   if (gdbm_file)
61     {
62       if (gdbm_setopt (gdbm_file, opt, &val, sizeof (val)) == -1)
63 	{
64 	  dberror (_("%s failed"), name);
65 	  return 1;
66 	}
67     }
68   return 0;
69 }
70 
71 static void
closedb(void)72 closedb (void)
73 {
74   if (gdbm_file)
75     {
76       gdbm_close (gdbm_file);
77       gdbm_file = NULL;
78       variable_unset ("fd");
79     }
80 
81   datum_free (&key_data);
82   datum_free (&return_data);
83 }
84 
85 static int
opendb(char * dbname,int fd)86 opendb (char *dbname, int fd)
87 {
88   int cache_size = 0;
89   int block_size = 0;
90   int flags;
91   int filemode;
92   GDBM_FILE db;
93   int n;
94 
95   switch (variable_get ("cachesize", VART_INT, (void**) &cache_size))
96     {
97     case VAR_OK:
98     case VAR_ERR_NOTSET:
99       break;
100     default:
101       abort ();
102     }
103   switch (variable_get ("blocksize", VART_INT, (void**) &block_size))
104     {
105     case VAR_OK:
106     case VAR_ERR_NOTSET:
107       break;
108     default:
109       abort ();
110     }
111 
112   if (variable_get ("open", VART_INT, (void**) &flags) != VAR_OK)
113     abort ();
114 
115   if (flags == GDBM_NEWDB)
116     {
117       if (interactive () && variable_is_true ("confirm") &&
118 	  access (dbname, F_OK) == 0)
119 	{
120 	  if (!getyn (_("database %s already exists; overwrite"), dbname))
121 	    return GDBMSHELL_CANCEL;
122 	}
123     }
124 
125   if (variable_get ("format", VART_INT, (void**) &n) != VAR_OK)
126     abort ();
127 
128   flags |= n;
129 
130   if (!variable_is_true ("lock"))
131     flags |= GDBM_NOLOCK;
132   if (!variable_is_true ("mmap"))
133     flags |= GDBM_NOMMAP;
134   if (variable_is_true ("sync"))
135     flags |= GDBM_SYNC;
136 
137   if (variable_get ("filemode", VART_INT, (void**) &filemode))
138     abort ();
139 
140   if (fd > 0)
141     db = gdbm_fd_open (fd, dbname, block_size, flags | GDBM_CLOERROR, NULL);
142   else
143     {
144       char *name = tildexpand (dbname);
145       db = gdbm_open (name, block_size, flags, filemode, NULL);
146       free (name);
147     }
148 
149   if (db == NULL)
150     {
151       dberror (_("cannot open database %s"), dbname);
152       return GDBMSHELL_GDBM_ERR;
153     }
154 
155   if (cache_size &&
156       gdbm_setopt (db, GDBM_CACHESIZE, &cache_size, sizeof (int)) == -1)
157     dberror (_("%s failed"), "GDBM_CACHESIZE");
158 
159   if (variable_is_true ("coalesce"))
160     {
161       gdbmshell_setopt ("GDBM_SETCOALESCEBLKS", GDBM_SETCOALESCEBLKS, 1);
162     }
163   if (variable_is_true ("centfree"))
164     {
165       gdbmshell_setopt ("GDBM_SETCENTFREE", GDBM_SETCENTFREE, 1);
166     }
167 
168   if (gdbm_file)
169     gdbm_close (gdbm_file);
170 
171   gdbm_file = db;
172   return GDBMSHELL_OK;
173 }
174 
175 static int
checkdb(void)176 checkdb (void)
177 {
178   if (!gdbm_file)
179     {
180       char *filename;
181       int fd = -1;
182       variable_get ("filename", VART_STRING, (void**) &filename);
183       variable_get ("fd", VART_INT, (void**) &fd);
184       return opendb (filename, fd);
185     }
186   return GDBMSHELL_OK;
187 }
188 
189 static int
checkdb_begin(struct command_param * param GDBM_ARG_UNUSED,struct command_environ * cenv GDBM_ARG_UNUSED,size_t * exp_count GDBM_ARG_UNUSED)190 checkdb_begin (struct command_param *param GDBM_ARG_UNUSED,
191 	       struct command_environ *cenv GDBM_ARG_UNUSED,
192 	       size_t *exp_count GDBM_ARG_UNUSED)
193 {
194   return checkdb ();
195 }
196 
197 static size_t
bucket_print_lines(hash_bucket * bucket)198 bucket_print_lines (hash_bucket *bucket)
199 {
200   return 6 + gdbm_file->header->bucket_elems + 3 + bucket->av_count;
201 }
202 
203 static void
format_key_start(FILE * fp,bucket_element * elt)204 format_key_start (FILE *fp, bucket_element *elt)
205 {
206   int size = SMALL < elt->key_size ? SMALL : elt->key_size;
207   int i;
208 
209   for (i = 0; i < size; i++)
210     {
211       if (isprint (elt->key_start[i]))
212 	fprintf (fp, "   %c", elt->key_start[i]);
213       else
214 	fprintf (fp, " %03o", elt->key_start[i]);
215     }
216 }
217 
218 /* Debug procedure to print the contents of the current hash bucket. */
219 static void
print_bucket(FILE * fp,hash_bucket * bucket,const char * mesg,...)220 print_bucket (FILE *fp, hash_bucket *bucket, const char *mesg, ...)
221 {
222   int index;
223   va_list ap;
224 
225   fprintf (fp, "******* ");
226   va_start(ap, mesg);
227   vfprintf (fp, mesg, ap);
228   va_end (ap);
229   fprintf (fp, " **********\n\n");
230   fprintf (fp,
231 	   _("bits = %d\ncount= %d\nHash Table:\n"),
232 	   bucket->bucket_bits, bucket->count);
233   fprintf (fp,
234 	   _("    #    hash value     key size    data size     data adr home  key start\n"));
235   for (index = 0; index < gdbm_file->header->bucket_elems; index++)
236     {
237       fprintf (fp, " %4d  %12x  %11d  %11d  %11lu %4d", index,
238 	       bucket->h_table[index].hash_value,
239 	       bucket->h_table[index].key_size,
240 	       bucket->h_table[index].data_size,
241 	       (unsigned long) bucket->h_table[index].data_pointer,
242 	       bucket->h_table[index].hash_value %
243 	       gdbm_file->header->bucket_elems);
244       if (bucket->h_table[index].key_size)
245 	{
246 	  fprintf (fp, " ");
247 	  format_key_start (fp, &bucket->h_table[index]);
248 	}
249       fprintf (fp, "\n");
250     }
251 
252   fprintf (fp, _("\nAvail count = %1d\n"), bucket->av_count);
253   fprintf (fp, _("Address           size\n"));
254   for (index = 0; index < bucket->av_count; index++)
255     fprintf (fp, "%11lu%9d\n",
256 	     (unsigned long) bucket->bucket_avail[index].av_adr,
257 	     bucket->bucket_avail[index].av_size);
258 }
259 
260 struct avail_list_counter
261 {
262   size_t min_size;
263   size_t lines;
264 };
265 
266 static int
avail_list_count(avail_block * avblk,off_t off,void * data)267 avail_list_count (avail_block *avblk, off_t off, void *data)
268 {
269   struct avail_list_counter *ctr = data;
270 
271   ctr->lines += avblk->count;
272   return ctr->lines > ctr->min_size;
273 }
274 
275 static size_t
_gdbm_avail_list_size(GDBM_FILE dbf,size_t min_size)276 _gdbm_avail_list_size (GDBM_FILE dbf, size_t min_size)
277 {
278   struct avail_list_counter ctr;
279   ctr.min_size = 0;
280   ctr.lines = 0;
281   gdbm_avail_traverse (dbf, avail_list_count, &ctr);
282   return ctr.lines;
283 }
284 
285 static void
av_table_display(avail_elem * av_table,int count,FILE * fp)286 av_table_display (avail_elem *av_table, int count, FILE *fp)
287 {
288   int i;
289 
290   for (i = 0; i < count; i++)
291     {
292       fprintf (fp, "  %15d   %10lu \n",
293 	       av_table[i].av_size, (unsigned long) av_table[i].av_adr);
294     }
295 }
296 
297 static int
avail_list_print(avail_block * avblk,off_t n,void * data)298 avail_list_print (avail_block *avblk, off_t n, void *data)
299 {
300   FILE *fp = data;
301 
302   fputc ('\n', fp);
303   if (n == 0)//FIXME
304     fprintf (fp, "%s", _("header block"));
305   else
306     fprintf (fp, _("block = %lu"), (unsigned long) n);
307   fprintf (fp, _("\nsize  = %d\ncount = %d\n"),
308 	   avblk->size, avblk->count);
309   av_table_display (avblk->av_table, avblk->count, fp);
310   return 0;
311 }
312 
313 static int
_gdbm_print_avail_list(FILE * fp,GDBM_FILE dbf)314 _gdbm_print_avail_list (FILE *fp, GDBM_FILE dbf)
315 {
316   int rc = gdbm_avail_traverse (dbf, avail_list_print, fp);
317   if (rc)
318     dberror (_("%s failed"), "gdbm_avail_traverse");
319   return GDBMSHELL_GDBM_ERR;
320 }
321 
322 static void
_gdbm_print_bucket_cache(FILE * fp,GDBM_FILE dbf)323 _gdbm_print_bucket_cache (FILE *fp, GDBM_FILE dbf)
324 {
325   if (dbf->cache_num)
326     {
327       int i;
328       cache_elem *elem;
329 
330       fprintf (fp,
331 	_("Bucket Cache (size %zu/%zu):\n  Index:         Address  Changed  Data_Hash \n"),
332 	       dbf->cache_num, dbf->cache_size);
333       for (elem = dbf->cache_entry, i = 0; elem; elem = elem->ca_next, i++)
334 	{
335 	  fprintf (fp, "  %5d:  %15lu %7s  %x\n",
336 		   i,
337 		   (unsigned long) elem->ca_adr,
338 		   (elem->ca_changed ? _("True") : _("False")),
339 		   elem->ca_data.hash_val);
340 	}
341     }
342   else
343     fprintf (fp, _("Bucket cache is empty.\n"));
344 }
345 
346 static int
trimnl(char * str)347 trimnl (char *str)
348 {
349   int len = strlen (str);
350 
351   if (str[len - 1] == '\n')
352     {
353       str[--len] = 0;
354       return 1;
355     }
356   return 0;
357 }
358 
359 static int
get_screen_lines(void)360 get_screen_lines (void)
361 {
362 #ifdef TIOCGWINSZ
363   if (isatty (1))
364     {
365       struct winsize ws;
366 
367       ws.ws_col = ws.ws_row = 0;
368       if ((ioctl(1, TIOCGWINSZ, (char *) &ws) < 0) || ws.ws_row == 0)
369 	{
370 	  const char *lines = getenv ("LINES");
371 	  if (lines)
372 	    ws.ws_row = strtol (lines, NULL, 10);
373 	}
374       return ws.ws_row;
375     }
376 #else
377   const char *lines = getenv ("LINES");
378   if (lines)
379     return strtol (lines, NULL, 10);
380 #endif
381   return -1;
382 }
383 
384 /* Open database */
385 static int
open_handler(struct command_param * param,struct command_environ * cenv GDBM_ARG_UNUSED)386 open_handler (struct command_param *param,
387 	      struct command_environ *cenv GDBM_ARG_UNUSED)
388 {
389   char *filename;
390   int fd = -1;
391   int rc;
392 
393   closedb ();
394 
395   if (param->argc == 1)
396     filename = PARAM_STRING (param, 0);
397   else
398     {
399       variable_get ("filename", VART_STRING, (void**) &filename);
400       variable_get ("fd", VART_INT, (void**) &fd);
401     }
402 
403   if ((rc = opendb (filename, fd)) == GDBMSHELL_OK)
404     {
405       variable_set ("filename", VART_STRING, filename);
406       if (fd >= 0)
407 	variable_set ("fd", VART_INT, &fd);
408       else
409 	variable_unset ("fd");
410     }
411   return rc;
412 }
413 
414 /* Close database */
415 static int
close_handler(struct command_param * param GDBM_ARG_UNUSED,struct command_environ * cenv GDBM_ARG_UNUSED)416 close_handler (struct command_param *param GDBM_ARG_UNUSED,
417 	       struct command_environ *cenv GDBM_ARG_UNUSED)
418 {
419   if (!gdbm_file)
420     terror ("%s", _("nothing to close"));
421   else
422     closedb ();
423   return GDBMSHELL_OK;
424 }
425 
426 static char *
count_to_str(gdbm_count_t count,char * buf,size_t bufsize)427 count_to_str (gdbm_count_t count, char *buf, size_t bufsize)
428 {
429   char *p = buf + bufsize;
430 
431   *--p = 0;
432   if (count == 0)
433     *--p = '0';
434   else
435     while (count)
436       {
437 	if (p == buf)
438 	  return NULL;
439 	*--p = '0' + count % 10;
440 	count /= 10;
441       }
442   return p;
443 }
444 
445 /* count - count items in the database */
446 static int
count_handler(struct command_param * param GDBM_ARG_UNUSED,struct command_environ * cenv)447 count_handler (struct command_param *param GDBM_ARG_UNUSED,
448 	       struct command_environ *cenv)
449 {
450   gdbm_count_t count;
451 
452   if (gdbm_count (gdbm_file, &count))
453     {
454       dberror (_("%s failed"), "gdbm_count");
455       return GDBMSHELL_GDBM_ERR;
456     }
457   else
458     {
459       char buf[128];
460       char *p = count_to_str (count, buf, sizeof buf);
461 
462       if (!p)
463 	terror ("%s", _("count buffer overflow"));
464       else
465 	fprintf (cenv->fp,
466 		 ngettext ("There is %s item in the database.\n",
467 			   "There are %s items in the database.\n",
468 			   count),
469 		 p);
470     }
471   return GDBMSHELL_OK;
472 }
473 
474 /* delete KEY - delete a key*/
475 static int
delete_handler(struct command_param * param,struct command_environ * cenv)476 delete_handler (struct command_param *param, struct command_environ *cenv)
477 {
478   if (gdbm_delete (gdbm_file, PARAM_DATUM (param, 0)) != 0)
479     {
480       if (gdbm_errno == GDBM_ITEM_NOT_FOUND)
481 	{
482 	  if (!gdbm_error_is_masked (gdbm_errno))
483 	    terror ("%s", _("No such item found"));
484 	}
485       else
486 	dberror ("%s", _("Can't delete"));
487       return GDBMSHELL_GDBM_ERR;
488     }
489   return GDBMSHELL_OK;
490 }
491 
492 /* fetch KEY - fetch a record by its key */
493 static int
fetch_handler(struct command_param * param,struct command_environ * cenv)494 fetch_handler (struct command_param *param, struct command_environ *cenv)
495 {
496   return_data = gdbm_fetch (gdbm_file, PARAM_DATUM (param, 0));
497   if (return_data.dptr != NULL)
498     {
499       datum_format (cenv->fp, &return_data, dsdef[DS_CONTENT]);
500       fputc ('\n', cenv->fp);
501       datum_free (&return_data);
502       return GDBMSHELL_OK;
503     }
504   else if (gdbm_errno == GDBM_ITEM_NOT_FOUND)
505     {
506       if (!gdbm_error_is_masked (gdbm_errno))
507 	terror ("%s", _("No such item found"));
508     }
509   else
510     dberror ("%s", _("Can't fetch data"));
511   return GDBMSHELL_GDBM_ERR;
512 }
513 
514 /* store KEY DATA - store data */
515 static int
store_handler(struct command_param * param,struct command_environ * cenv GDBM_ARG_UNUSED)516 store_handler (struct command_param *param,
517 	       struct command_environ *cenv GDBM_ARG_UNUSED)
518 {
519   if (gdbm_store (gdbm_file,
520 		  PARAM_DATUM (param, 0), PARAM_DATUM (param, 1),
521 		  GDBM_REPLACE) != 0)
522     {
523       dberror ("%s", _("Item not inserted"));
524       return GDBMSHELL_GDBM_ERR;
525     }
526   return GDBMSHELL_OK;
527 }
528 
529 /* first - begin iteration */
530 
531 static int
firstkey_handler(struct command_param * param,struct command_environ * cenv)532 firstkey_handler (struct command_param *param, struct command_environ *cenv)
533 {
534   datum_free (&key_data);
535   key_data = gdbm_firstkey (gdbm_file);
536   if (key_data.dptr != NULL)
537     {
538       datum_format (cenv->fp, &key_data, dsdef[DS_KEY]);
539       fputc ('\n', cenv->fp);
540 
541       return_data = gdbm_fetch (gdbm_file, key_data);
542       datum_format (cenv->fp, &return_data, dsdef[DS_CONTENT]);
543       fputc ('\n', cenv->fp);
544 
545       datum_free (&return_data);
546       return GDBMSHELL_OK;
547     }
548   else if (gdbm_errno == GDBM_ITEM_NOT_FOUND)
549     {
550       if (!gdbm_error_is_masked (gdbm_errno))
551 	fprintf (cenv->fp, _("No such item found.\n"));
552     }
553   else
554     dberror ("%s", _("Can't find first key"));
555   return GDBMSHELL_GDBM_ERR;
556 }
557 
558 /* next [KEY] - next key */
559 static int
nextkey_handler(struct command_param * param,struct command_environ * cenv)560 nextkey_handler (struct command_param *param, struct command_environ *cenv)
561 {
562   if (param->argc == 1)
563     {
564       datum_free (&key_data);
565       key_data.dptr = emalloc (PARAM_DATUM (param, 0).dsize);
566       key_data.dsize = PARAM_DATUM (param, 0).dsize;
567       memcpy (key_data.dptr, PARAM_DATUM (param, 0).dptr, key_data.dsize);
568     }
569   return_data = gdbm_nextkey (gdbm_file, key_data);
570   if (return_data.dptr != NULL)
571     {
572       datum_free (&key_data);
573       key_data = return_data;
574       datum_format (cenv->fp, &key_data, dsdef[DS_KEY]);
575       fputc ('\n', cenv->fp);
576 
577       return_data = gdbm_fetch (gdbm_file, key_data);
578       datum_format (cenv->fp, &return_data, dsdef[DS_CONTENT]);
579       fputc ('\n', cenv->fp);
580 
581       datum_free (&return_data);
582       return GDBMSHELL_OK;
583     }
584   else if (gdbm_errno == GDBM_ITEM_NOT_FOUND)
585     {
586       if (!gdbm_error_is_masked (gdbm_errno))
587 	terror ("%s", _("No such item found"));
588       datum_free (&key_data);
589     }
590   else
591     dberror ("%s", _("Can't find next key"));
592   return GDBMSHELL_GDBM_ERR;
593 }
594 
595 /* reorganize */
596 static int
reorganize_handler(struct command_param * param GDBM_ARG_UNUSED,struct command_environ * cenv)597 reorganize_handler (struct command_param *param GDBM_ARG_UNUSED,
598 		    struct command_environ *cenv)
599 {
600   if (gdbm_reorganize (gdbm_file))
601     {
602       dberror ("%s", _("Reorganization failed"));
603       return GDBMSHELL_GDBM_ERR;
604     }
605   else
606     fprintf (cenv->fp, "%s\n", _("Reorganization succeeded."));
607   return GDBMSHELL_OK;
608 }
609 
610 static void
err_printer(void * data GDBM_ARG_UNUSED,char const * fmt,...)611 err_printer (void *data GDBM_ARG_UNUSED, char const *fmt, ...)
612 {
613   va_list ap;
614 
615   va_start (ap, fmt);
616   vfprintf (stderr, fmt, ap);
617   va_end (ap);
618   fprintf (stderr, "\n");
619 }
620 
621 /* recover sumamry verbose backup max-failed-keys=N max-failed-buckets=N max-failures=N */
622 static int
recover_handler(struct command_param * param,struct command_environ * cenv)623 recover_handler (struct command_param *param, struct command_environ *cenv)
624 {
625   gdbm_recovery rcvr;
626   int flags = 0;
627   int rc;
628   int i;
629   char *p;
630   int summary = 0;
631 
632   for (i = 0; i < param->argc; i++)
633     {
634       char *arg = PARAM_STRING (param, i);
635       if (strcmp (arg, "verbose") == 0)
636 	{
637 	  rcvr.errfun = err_printer;
638 	  flags |= GDBM_RCVR_ERRFUN;
639 	}
640       else if (strcmp (arg, "force") == 0)
641 	{
642 	  flags |= GDBM_RCVR_FORCE;
643 	}
644       else if (strcmp (arg, "summary") == 0)
645 	{
646 	  summary = 1;
647 	}
648       else if (strcmp (arg, "backup") == 0)
649 	{
650 	  flags |= GDBM_RCVR_BACKUP;
651 	}
652       else if (strncmp (arg, "max-failures=", 13) == 0)
653 	{
654 	  rcvr.max_failures = strtoul (arg + 13, &p, 10);
655 	  if (*p)
656 	    {
657 	      terror (_("not a number (stopped near %s)"), p);
658 	      return 1;
659 	    }
660 	  flags |= GDBM_RCVR_MAX_FAILURES;
661 	}
662       else if (strncmp (arg, "max-failed-keys=", 16) == 0)
663 	{
664 	  rcvr.max_failed_keys = strtoul (arg + 16, &p, 10);
665 	  if (*p)
666 	    {
667 	      terror (_("not a number (stopped near %s)"), p);
668 	      return 1;
669 	    }
670 	  flags |= GDBM_RCVR_MAX_FAILED_KEYS;
671 	}
672       else if (strncmp (arg, "max-failed-buckets=", 19) == 0)
673 	{
674 	  rcvr.max_failures = strtoul (arg + 19, &p, 10);
675 	  if (*p)
676 	    {
677 	      terror (_("not a number (stopped near %s)"), p);
678 	      return 1;
679 	    }
680 	  flags |= GDBM_RCVR_MAX_FAILED_BUCKETS;
681 	}
682       else
683 	{
684 	  terror (_("unrecognized argument: %s"), arg);
685 	  return GDBMSHELL_SYNTAX;
686 	}
687     }
688 
689   rc = gdbm_recover (gdbm_file, &rcvr, flags);
690 
691   if (rc == 0)
692     {
693       fprintf (cenv->fp, _("Recovery succeeded.\n"));
694       if (summary)
695 	{
696 	  fprintf (cenv->fp,
697 		   _("Keys recovered: %lu, failed: %lu, duplicate: %lu\n"),
698 		   (unsigned long) rcvr.recovered_keys,
699 		   (unsigned long) rcvr.failed_keys,
700 		   (unsigned long) rcvr.duplicate_keys);
701 	  fprintf (cenv->fp,
702 		   _("Buckets recovered: %lu, failed: %lu\n"),
703 		   (unsigned long) rcvr.recovered_buckets,
704 		   (unsigned long) rcvr.failed_buckets);
705 	}
706 
707       if (rcvr.backup_name)
708 	{
709 	  fprintf (cenv->fp,
710 		   _("Original database preserved in file %s"),
711 		   rcvr.backup_name);
712 	  free (rcvr.backup_name);
713 	}
714       fputc ('\n', cenv->fp);
715     }
716   else
717     {
718       dberror ("%s", _("Recovery failed"));
719       rc = GDBMSHELL_GDBM_ERR;
720     }
721   return rc;
722 }
723 
724 /* avail - print available list */
725 static int
avail_begin(struct command_param * param GDBM_ARG_UNUSED,struct command_environ * cenv GDBM_ARG_UNUSED,size_t * exp_count)726 avail_begin (struct command_param *param GDBM_ARG_UNUSED,
727 	     struct command_environ *cenv GDBM_ARG_UNUSED,
728 	     size_t *exp_count)
729 {
730   int rc = checkdb ();
731   if (rc == GDBMSHELL_OK)
732     {
733       if (exp_count)
734 	*exp_count = _gdbm_avail_list_size (gdbm_file, SIZE_T_MAX);
735     }
736   return rc;
737 }
738 
739 static int
avail_handler(struct command_param * param GDBM_ARG_UNUSED,struct command_environ * cenv)740 avail_handler (struct command_param *param GDBM_ARG_UNUSED,
741 	       struct command_environ *cenv)
742 {
743   return _gdbm_print_avail_list (cenv->fp, gdbm_file);
744 }
745 
746 /* print current bucket */
747 static int
print_current_bucket_begin(struct command_param * param GDBM_ARG_UNUSED,struct command_environ * cenv GDBM_ARG_UNUSED,size_t * exp_count)748 print_current_bucket_begin (struct command_param *param GDBM_ARG_UNUSED,
749 			    struct command_environ *cenv GDBM_ARG_UNUSED,
750 			    size_t *exp_count)
751 {
752   int rc = checkdb ();
753 
754   if (rc == GDBMSHELL_OK)
755     {
756       if (exp_count)
757 	*exp_count = gdbm_file->bucket
758                        ? bucket_print_lines (gdbm_file->bucket) + 3
759                        : 1;
760     }
761   return rc;
762 }
763 
764 static int
print_current_bucket_handler(struct command_param * param,struct command_environ * cenv)765 print_current_bucket_handler (struct command_param *param,
766 			      struct command_environ *cenv)
767 {
768   if (!gdbm_file->bucket)
769     fprintf (cenv->fp, _("no current bucket\n"));
770   else
771     {
772       if (param->argc)
773 	print_bucket (cenv->fp, gdbm_file->bucket, _("Bucket #%s"),
774 		      PARAM_STRING (param, 0));
775       else
776 	print_bucket (cenv->fp, gdbm_file->bucket, "%s", _("Current bucket"));
777       fprintf (cenv->fp, _("\n current directory entry = %d.\n"),
778 	       gdbm_file->bucket_dir);
779       fprintf (cenv->fp, _(" current bucket address  = %lu.\n"),
780 	       (unsigned long) gdbm_file->cache_entry->ca_adr);
781     }
782   return GDBMSHELL_OK;
783 }
784 
785 int
getnum(int * pnum,char * arg,char ** endp)786 getnum (int *pnum, char *arg, char **endp)
787 {
788   char *p;
789   unsigned long x = strtoul (arg, &p, 10);
790   if (*p && !isspace (*p))
791     {
792       terror (_("not a number (stopped near %s)"), p);
793       return 1;
794     }
795   while (*p && isspace (*p))
796     p++;
797   if (endp)
798     *endp = p;
799   else if (*p)
800     {
801       terror (_("not a number (stopped near %s)"), p);
802       return 1;
803     }
804   *pnum = x;
805   return 0;
806 }
807 
808 /* bucket NUM - print a bucket and set it as a current one.
809    Uses print_current_bucket_handler */
810 static int
print_bucket_begin(struct command_param * param,struct command_environ * cenv GDBM_ARG_UNUSED,size_t * exp_count)811 print_bucket_begin (struct command_param *param,
812 		    struct command_environ *cenv GDBM_ARG_UNUSED,
813 		    size_t *exp_count)
814 {
815   int rc;
816   int temp;
817 
818   if ((rc = checkdb ()) != GDBMSHELL_OK)
819     return rc;
820 
821   if (getnum (&temp, PARAM_STRING (param, 0), NULL))
822     return GDBMSHELL_SYNTAX;
823 
824   if (temp >= GDBM_DIR_COUNT (gdbm_file))
825     {
826       terror (_("bucket number out of range (0..%lu)"),
827 	      GDBM_DIR_COUNT (gdbm_file));
828       return GDBMSHELL_SYNTAX;
829     }
830   if (_gdbm_get_bucket (gdbm_file, temp))
831     {
832       dberror (_("%s failed"), "_gdbm_get_bucket");
833       return GDBMSHELL_GDBM_ERR;
834     }
835   if (exp_count)
836     *exp_count = bucket_print_lines (gdbm_file->bucket) + 3;
837   return GDBMSHELL_OK;
838 }
839 
840 /* dir - print hash directory */
841 static int
print_dir_begin(struct command_param * param GDBM_ARG_UNUSED,struct command_environ * cenv GDBM_ARG_UNUSED,size_t * exp_count)842 print_dir_begin (struct command_param *param GDBM_ARG_UNUSED,
843 		 struct command_environ *cenv GDBM_ARG_UNUSED,
844 		 size_t *exp_count)
845 {
846   int rc;
847 
848   if ((rc = checkdb ()) == GDBMSHELL_OK)
849     {
850       if (exp_count)
851 	*exp_count = GDBM_DIR_COUNT (gdbm_file) + 3;
852     }
853   return rc;
854 }
855 
856 static size_t
bucket_count(void)857 bucket_count (void)
858 {
859   size_t count = 0;
860 
861   if (gdbm_bucket_count (gdbm_file, &count))
862     {
863       dberror ("%s", "gdbm_bucket_count");
864     }
865   return count;
866 }
867 
868 static int
print_dir_handler(struct command_param * param GDBM_ARG_UNUSED,struct command_environ * cenv)869 print_dir_handler (struct command_param *param GDBM_ARG_UNUSED,
870 		   struct command_environ *cenv)
871 {
872   int i;
873 
874   fprintf (cenv->fp, _("Hash table directory.\n"));
875   fprintf (cenv->fp, _("  Size =  %d.  Bits = %d,  Buckets = %zu.\n\n"),
876 	   gdbm_file->header->dir_size, gdbm_file->header->dir_bits,
877 	   bucket_count ());
878 
879   for (i = 0; i < GDBM_DIR_COUNT (gdbm_file); i++)
880     fprintf (cenv->fp, "  %10d:  %12lu\n",
881 	     i, (unsigned long) gdbm_file->dir[i]);
882 
883   return GDBMSHELL_OK;
884 }
885 
886 /* header - print file handler */
887 static int
print_header_begin(struct command_param * param GDBM_ARG_UNUSED,struct command_environ * cenv GDBM_ARG_UNUSED,size_t * exp_count)888 print_header_begin (struct command_param *param GDBM_ARG_UNUSED,
889 		    struct command_environ *cenv GDBM_ARG_UNUSED,
890 		    size_t *exp_count)
891 {
892   int rc;
893   int n;
894 
895   if ((rc = checkdb ()) != GDBMSHELL_OK)
896     return rc;
897 
898   switch (gdbm_file->header->header_magic)
899     {
900     case GDBM_OMAGIC:
901     case GDBM_MAGIC:
902       n = 14;
903       break;
904 
905     case GDBM_NUMSYNC_MAGIC:
906       n = 19;
907       break;
908 
909     default:
910       abort ();
911     }
912 
913   if (exp_count)
914     *exp_count = n;
915 
916   return GDBMSHELL_OK;
917 }
918 
919 static int
print_header_handler(struct command_param * param GDBM_ARG_UNUSED,struct command_environ * cenv)920 print_header_handler (struct command_param *param GDBM_ARG_UNUSED,
921 		      struct command_environ *cenv)
922 {
923   FILE *fp = cenv->fp;
924   char const *type;
925 
926   switch (gdbm_file->header->header_magic)
927     {
928     case GDBM_OMAGIC:
929       type = "GDBM (old)";
930       break;
931 
932     case GDBM_MAGIC:
933       type = "GDBM (standard)";
934       break;
935 
936     case GDBM_NUMSYNC_MAGIC:
937       type = "GDBM (numsync)";
938       break;
939 
940     default:
941       abort ();
942     }
943 
944   fprintf (fp, _("\nFile Header: \n\n"));
945   fprintf (fp, _("  type         = %s\n"), type);
946   fprintf (fp, _("  table        = %lu\n"),
947 	   (unsigned long) gdbm_file->header->dir);
948   fprintf (fp, _("  table size   = %d\n"), gdbm_file->header->dir_size);
949   fprintf (fp, _("  table bits   = %d\n"), gdbm_file->header->dir_bits);
950   fprintf (fp, _("  block size   = %d\n"), gdbm_file->header->block_size);
951   fprintf (fp, _("  bucket elems = %d\n"), gdbm_file->header->bucket_elems);
952   fprintf (fp, _("  bucket size  = %d\n"), gdbm_file->header->bucket_size);
953   fprintf (fp, _("  header magic = %x\n"), gdbm_file->header->header_magic);
954   fprintf (fp, _("  next block   = %lu\n"),
955 	   (unsigned long) gdbm_file->header->next_block);
956 
957   fprintf (fp, _("  avail size   = %d\n"), gdbm_file->avail->size);
958   fprintf (fp, _("  avail count  = %d\n"), gdbm_file->avail->count);
959   fprintf (fp, _("  avail nx blk = %lu\n"),
960 	   (unsigned long) gdbm_file->avail->next_block);
961 
962   if (gdbm_file->xheader)
963     {
964       fprintf (fp, _("\nExtended Header: \n\n"));
965       fprintf (fp, _("       version = %d\n"), gdbm_file->xheader->version);
966       fprintf (fp, _("       numsync = %u\n"), gdbm_file->xheader->numsync);
967     }
968 
969   return GDBMSHELL_OK;
970 }
971 
972 static int
sync_handler(struct command_param * param GDBM_ARG_UNUSED,struct command_environ * cenv GDBM_ARG_UNUSED)973 sync_handler (struct command_param *param GDBM_ARG_UNUSED,
974 	      struct command_environ *cenv GDBM_ARG_UNUSED)
975 {
976   if (gdbm_sync (gdbm_file))
977     {
978       dberror ("%s", "gdbm_sync");
979       return GDBMSHELL_GDBM_ERR;
980     }
981   return GDBMSHELL_OK;
982 }
983 
984 static int
upgrade_handler(struct command_param * param GDBM_ARG_UNUSED,struct command_environ * cenv GDBM_ARG_UNUSED)985 upgrade_handler (struct command_param *param GDBM_ARG_UNUSED,
986 		 struct command_environ *cenv GDBM_ARG_UNUSED)
987 {
988   if (gdbm_convert (gdbm_file, GDBM_NUMSYNC))
989     {
990       dberror ("%s", "gdbm_convert");
991       return GDBMSHELL_GDBM_ERR;
992     }
993   return GDBMSHELL_OK;
994 }
995 
996 static int
downgrade_handler(struct command_param * param GDBM_ARG_UNUSED,struct command_environ * cenv GDBM_ARG_UNUSED)997 downgrade_handler (struct command_param *param GDBM_ARG_UNUSED,
998 		   struct command_environ *cenv GDBM_ARG_UNUSED)
999 {
1000   if (gdbm_convert (gdbm_file, 0))
1001     {
1002       dberror ("%s", "gdbm_convert");
1003       return GDBMSHELL_GDBM_ERR;
1004     }
1005   return GDBMSHELL_OK;
1006 }
1007 
1008 struct snapshot_status_info
1009 {
1010   char const *code;
1011   char const *descr;
1012   void (*fn) (FILE *, char const *, char const *);
1013 };
1014 
1015 #define MODBUFSIZE 10
1016 
1017 static char *
decode_mode(mode_t mode,char * buf)1018 decode_mode (mode_t mode, char *buf)
1019 {
1020   char *s = buf;
1021   *s++ = mode & S_IRUSR ? 'r' : '-';
1022   *s++ = mode & S_IWUSR ? 'w' : '-';
1023   *s++ = (mode & S_ISUID
1024 	       ? (mode & S_IXUSR ? 's' : 'S')
1025 	       : (mode & S_IXUSR ? 'x' : '-'));
1026   *s++ = mode & S_IRGRP ? 'r' : '-';
1027   *s++ = mode & S_IWGRP ? 'w' : '-';
1028   *s++ = (mode & S_ISGID
1029 	       ? (mode & S_IXGRP ? 's' : 'S')
1030 	       : (mode & S_IXGRP ? 'x' : '-'));
1031   *s++ = mode & S_IROTH ? 'r' : '-';
1032   *s++ = mode & S_IWOTH ? 'w' : '-';
1033   *s++ = (mode & S_ISVTX
1034 	       ? (mode & S_IXOTH ? 't' : 'T')
1035 	       : (mode & S_IXOTH ? 'x' : '-'));
1036   *s = '\0';
1037   return buf;
1038 }
1039 
1040 struct error_entry
1041 {
1042   const char *msg;
1043   int gdbm_err;
1044   int sys_err;
1045 };
1046 
1047 static void
error_push(struct error_entry * stk,int * tos,int maxstk,char const * text,int gdbm_err,int sys_err)1048 error_push (struct error_entry *stk, int *tos, int maxstk, char const *text,
1049 	    int gdbm_err, int sys_err)
1050 {
1051   if (*tos == maxstk)
1052     abort ();
1053   stk += *tos;
1054   ++ *tos;
1055   stk->msg = text;
1056   stk->gdbm_err = gdbm_err;
1057   stk->sys_err = sys_err;
1058 }
1059 
1060 static void
print_snapshot(char const * snapname,FILE * fp)1061 print_snapshot (char const *snapname, FILE *fp)
1062 {
1063   struct stat st;
1064   char buf[MODBUFSIZE];
1065 
1066   if (stat (snapname, &st) == 0)
1067     {
1068 # define MAXERRS 4
1069       struct error_entry errs[MAXERRS];
1070       int errn = 0;
1071       int i;
1072 
1073       switch (st.st_mode & ~S_IFREG)
1074 	{
1075 	case S_IRUSR:
1076 	case S_IWUSR:
1077 	  break;
1078 
1079 	default:
1080 	  error_push (errs, &errn, ARRAY_SIZE (errs), N_("bad file mode"),
1081                       0, 0);
1082 	}
1083 
1084       fprintf (fp, "%s: ", snapname);
1085       fprintf (fp, "%03o %s ", st.st_mode & 0777,
1086 	       decode_mode (st.st_mode, buf));
1087 #if HAVE_STRUCT_STAT_ST_MTIM
1088       fprintf (fp, "%ld.%09ld", st.st_mtim.tv_sec, st.st_mtim.tv_nsec);
1089 #else
1090       fprintf (fp, "%ld [%s]", st.st_mtime, _("insufficient precision"));
1091 #endif
1092       if (S_ISREG (st.st_mode))
1093 	{
1094 	  GDBM_FILE dbf;
1095 
1096 	  dbf = gdbm_open (snapname, 0, GDBM_READER, 0, NULL);
1097 	  if (dbf)
1098 	    {
1099 	      if (dbf->xheader)
1100 		fprintf (fp, " %u", dbf->xheader->numsync);
1101 	      else
1102 		/* TRANSLATORS: Stands for "Not Available". */
1103 		fprintf (fp, " %s", _("N/A"));
1104 	    }
1105 	  else if (gdbm_check_syserr (gdbm_errno))
1106 	    {
1107 	      if (errno == EACCES)
1108 		fprintf (fp, " ?");
1109 	      else
1110 		error_push (errs, &errn, ARRAY_SIZE (errs),
1111 			    N_("can't open database"),
1112 			    gdbm_errno, errno);
1113 	    }
1114 	  else
1115 	    error_push (errs, &errn, ARRAY_SIZE (errs),
1116 			N_("can't open database"),
1117 			gdbm_errno, 0);
1118 	}
1119       else
1120 	error_push (errs, &errn, ARRAY_SIZE (errs),
1121 		    N_("not a regular file"),
1122 		    0, 0);
1123       fputc ('\n', fp);
1124       for (i = 0; i < errn; i++)
1125 	{
1126 	  fprintf (fp, "%s: %s: %s", snapname, _("ERROR"), gettext (errs[i].msg));
1127 	  if (errs[i].gdbm_err)
1128 	    fprintf (fp, ": %s", gdbm_strerror (errs[i].gdbm_err));
1129 	  if (errs[i].sys_err)
1130 	    fprintf (fp, ": %s", strerror (errs[i].sys_err));
1131 	  fputc ('\n', fp);
1132 	}
1133     }
1134   else
1135     {
1136       fprintf (fp, _("%s: ERROR: can't stat: %s"), snapname, strerror (errno));
1137       return;
1138     }
1139 }
1140 
1141 static void
snapshot_print_fn(FILE * fp,char const * sa,char const * sb)1142 snapshot_print_fn (FILE *fp, char const *sa, char const *sb)
1143 {
1144   print_snapshot (sa, fp);
1145   print_snapshot (sb, fp);
1146 }
1147 
1148 static void
snapshot_err_fn(FILE * fp,char const * sa,char const * sb)1149 snapshot_err_fn (FILE *fp, char const *sa, char const *sb)
1150 {
1151   switch (errno)
1152     {
1153     default:
1154       print_snapshot (sa, fp);
1155       print_snapshot (sb, fp);
1156       break;
1157 
1158     case EINVAL:
1159       fprintf (fp, "%s.\n",
1160 	       _("Invalid arguments in call to gdbm_latest_snapshot"));
1161       break;
1162 
1163     case ENOSYS:
1164       fprintf (fp, "%s.\n",
1165 	       _("Function is not implemented: GDBM is built without crash-tolerance support"));
1166       break;
1167     }
1168 }
1169 
1170 static struct snapshot_status_info snapshot_status_info[] = {
1171   [GDBM_SNAPSHOT_OK] = {
1172     "GDBM_SNAPSHOT_OK",
1173     N_("Selected the most recent snapshot")
1174   },
1175   [GDBM_SNAPSHOT_BAD] = {
1176     "GDBM_SNAPSHOT_BAD",
1177     N_("Neither snapshot is readable"),
1178     snapshot_print_fn
1179   },
1180   [GDBM_SNAPSHOT_ERR] = {
1181     "GDBM_SNAPSHOT_ERR",
1182     N_("Error selecting snapshot"),
1183     snapshot_err_fn
1184   },
1185   [GDBM_SNAPSHOT_SAME] = {
1186     "GDBM_SNAPSHOT_SAME",
1187     N_("Snapshot modes and dates are the same"),
1188     snapshot_print_fn
1189   },
1190   [GDBM_SNAPSHOT_SUSPICIOUS] = {
1191     "GDBM_SNAPSHOT_SUSPICIOUS",
1192     N_("Snapshot sync counters differ by more than 1"),
1193     snapshot_print_fn
1194   }
1195 };
1196 
1197 static int
snapshot_handler(struct command_param * param,struct command_environ * cenv)1198 snapshot_handler (struct command_param *param, struct command_environ *cenv)
1199 {
1200   char *sa = tildexpand (PARAM_STRING (param, 0));
1201   char *sb = tildexpand (PARAM_STRING (param, 1));
1202   char const *sel;
1203   int rc = gdbm_latest_snapshot (sa, sb, &sel);
1204 
1205   if (rc >= 0 && rc < ARRAY_SIZE (snapshot_status_info))
1206     {
1207       fprintf (cenv->fp,
1208 	       "%s: %s.\n",
1209 	       snapshot_status_info[rc].code,
1210 	       gettext (snapshot_status_info[rc].descr));
1211       if (snapshot_status_info[rc].fn)
1212 	snapshot_status_info[rc].fn (cenv->fp, sa, sb);
1213       if (rc == GDBM_SNAPSHOT_OK)
1214 	print_snapshot (sel, cenv->fp);
1215     }
1216   else
1217     {
1218       terror (_("unexpected error code: %d"), rc);
1219       return GDBMSHELL_ERR;
1220     }
1221   return GDBMSHELL_OK;
1222 }
1223 
1224 
1225 /* hash KEY - hash the key */
1226 static int
hash_handler(struct command_param * param GDBM_ARG_UNUSED,struct command_environ * cenv)1227 hash_handler (struct command_param *param GDBM_ARG_UNUSED,
1228 	      struct command_environ *cenv)
1229 {
1230   if (gdbm_file)
1231     {
1232       int hashval, bucket, off;
1233       _gdbm_hash_key (gdbm_file, PARAM_DATUM (param, 0),
1234 		       &hashval, &bucket, &off);
1235       fprintf (cenv->fp, _("hash value = %x, bucket #%u, slot %u"),
1236 	       hashval,
1237 	       hashval >> (GDBM_HASH_BITS - gdbm_file->header->dir_bits),
1238 	       hashval % gdbm_file->header->bucket_elems);
1239     }
1240   else
1241     fprintf (cenv->fp, _("hash value = %x"),
1242 	     _gdbm_hash (PARAM_DATUM (param, 0)));
1243   fprintf (cenv->fp, ".\n");
1244   return GDBMSHELL_OK;
1245 }
1246 
1247 /* cache - print the bucket cache */
1248 static int
print_cache_begin(struct command_param * param GDBM_ARG_UNUSED,struct command_environ * cenv GDBM_ARG_UNUSED,size_t * exp_count)1249 print_cache_begin (struct command_param *param GDBM_ARG_UNUSED,
1250 		   struct command_environ *cenv GDBM_ARG_UNUSED,
1251 		   size_t *exp_count)
1252 {
1253   int rc;
1254 
1255   if ((rc = checkdb ()) == GDBMSHELL_OK)
1256     {
1257       if (exp_count)
1258 	*exp_count = gdbm_file->cache_num + 1;
1259     }
1260   return rc;
1261 }
1262 
1263 static int
print_cache_handler(struct command_param * param GDBM_ARG_UNUSED,struct command_environ * cenv)1264 print_cache_handler (struct command_param *param GDBM_ARG_UNUSED,
1265 		     struct command_environ *cenv)
1266 {
1267   _gdbm_print_bucket_cache (cenv->fp, gdbm_file);
1268   return GDBMSHELL_OK;
1269 }
1270 
1271 /* version - print GDBM version */
1272 static int
print_version_handler(struct command_param * param GDBM_ARG_UNUSED,struct command_environ * cenv)1273 print_version_handler (struct command_param *param GDBM_ARG_UNUSED,
1274 		       struct command_environ *cenv)
1275 {
1276   fprintf (cenv->fp, "%s\n", gdbm_version);
1277   return GDBMSHELL_OK;
1278 }
1279 
1280 /* list - List all entries */
1281 static int
list_begin(struct command_param * param GDBM_ARG_UNUSED,struct command_environ * cenv GDBM_ARG_UNUSED,size_t * exp_count)1282 list_begin (struct command_param *param GDBM_ARG_UNUSED,
1283 	    struct command_environ *cenv GDBM_ARG_UNUSED,
1284 	    size_t *exp_count)
1285 {
1286   int rc;
1287 
1288   if ((rc = checkdb ()) == GDBMSHELL_OK)
1289     {
1290       if (exp_count)
1291 	{
1292 	  gdbm_count_t count;
1293 
1294 	  if (gdbm_count (gdbm_file, &count))
1295 	    *exp_count = 0;
1296 	  else if (count > SIZE_T_MAX)
1297 	    *exp_count = SIZE_T_MAX;
1298 	  else
1299 	    *exp_count = count;
1300 	}
1301     }
1302 
1303   return rc;
1304 }
1305 
1306 static int
list_handler(struct command_param * param GDBM_ARG_UNUSED,struct command_environ * cenv)1307 list_handler (struct command_param *param GDBM_ARG_UNUSED,
1308 	      struct command_environ *cenv)
1309 {
1310   datum key;
1311   datum data;
1312   int rc = GDBMSHELL_OK;
1313 
1314   key = gdbm_firstkey (gdbm_file);
1315   if (!key.dptr && gdbm_errno != GDBM_ITEM_NOT_FOUND)
1316     {
1317       dberror ("%s", "gdbm_firstkey");
1318       return GDBMSHELL_GDBM_ERR;
1319     }
1320   while (key.dptr)
1321     {
1322       datum nextkey;
1323 
1324       data = gdbm_fetch (gdbm_file, key);
1325       if (!data.dptr)
1326 	 {
1327 	   dberror ("%s", "gdbm_fetch");
1328 	   terror ("%s", _("the key was:"));
1329 	   datum_format (stderr, &key, dsdef[DS_KEY]);
1330 	   rc = GDBMSHELL_GDBM_ERR;
1331 	 }
1332       else
1333 	 {
1334 	   datum_format (cenv->fp, &key, dsdef[DS_KEY]);
1335 	   fputc (' ', cenv->fp);
1336 	   datum_format (cenv->fp, &data, dsdef[DS_CONTENT]);
1337 	   fputc ('\n', cenv->fp);
1338 	   free (data.dptr);
1339 	 }
1340       nextkey = gdbm_nextkey (gdbm_file, key);
1341       free (key.dptr);
1342       key = nextkey;
1343     }
1344   if (gdbm_errno != GDBM_ITEM_NOT_FOUND)
1345     {
1346       dberror ("%s", "gdbm_nextkey");
1347       rc = GDBMSHELL_GDBM_ERR;
1348     }
1349   return rc;
1350 }
1351 
1352 /* quit - quit the program */
1353 static int
quit_handler(struct command_param * param GDBM_ARG_UNUSED,struct command_environ * cenv GDBM_ARG_UNUSED)1354 quit_handler (struct command_param *param GDBM_ARG_UNUSED,
1355 	      struct command_environ *cenv GDBM_ARG_UNUSED)
1356 {
1357   input_context_drain ();
1358   if (input_context_push (instream_null_create ()))
1359     exit (EXIT_FATAL);
1360   return GDBMSHELL_OK;
1361 }
1362 
1363 /* export FILE [truncate] - export to a flat file format */
1364 static int
export_handler(struct command_param * param,struct command_environ * cenv GDBM_ARG_UNUSED)1365 export_handler (struct command_param *param,
1366 		struct command_environ *cenv GDBM_ARG_UNUSED)
1367 {
1368   int format = GDBM_DUMP_FMT_ASCII;
1369   int flags = GDBM_WRCREAT;
1370   int i;
1371   int filemode;
1372   int rc = GDBMSHELL_OK;
1373 
1374   for (i = 1; i < param->argc; i++)
1375     {
1376       if (strcmp (PARAM_STRING (param, i), "truncate") == 0)
1377 	 flags = GDBM_NEWDB;
1378       else if (strcmp (PARAM_STRING (param, i), "binary") == 0)
1379 	 format = GDBM_DUMP_FMT_BINARY;
1380       else if (strcmp (PARAM_STRING (param, i), "ascii") == 0)
1381 	 format = GDBM_DUMP_FMT_ASCII;
1382       else
1383 	 {
1384 	   terror (_("unrecognized argument: %s"), PARAM_STRING (param, i));
1385 	   return GDBMSHELL_SYNTAX;
1386 	 }
1387     }
1388 
1389   if (variable_get ("filemode", VART_INT, (void**) &filemode))
1390     abort ();
1391   if (gdbm_dump (gdbm_file, PARAM_STRING (param, 0), format, flags, filemode))
1392     {
1393       dberror ("%s", _("error dumping database"));
1394       rc = GDBMSHELL_GDBM_ERR;
1395     }
1396   return rc;
1397 }
1398 
1399 /* import FILE [replace] [nometa] - import from a flat file */
1400 static int
import_handler(struct command_param * param,struct command_environ * cenv GDBM_ARG_UNUSED)1401 import_handler (struct command_param *param,
1402 		struct command_environ *cenv GDBM_ARG_UNUSED)
1403 {
1404   int flag = GDBM_INSERT;
1405   unsigned long err_line;
1406   int meta_mask = 0;
1407   int i;
1408   int rc = GDBMSHELL_OK;
1409   char *file_name;
1410 
1411   for (i = 0; i < param->argc; i++)
1412     {
1413       if (strcmp (PARAM_STRING (param, i), "replace") == 0)
1414 	 flag = GDBM_REPLACE;
1415       else if (strcmp (PARAM_STRING (param, i), "nometa") == 0)
1416 	 meta_mask = GDBM_META_MASK_MODE | GDBM_META_MASK_OWNER;
1417       else
1418 	 {
1419 	   terror (_("unrecognized argument: %s"),
1420 		   PARAM_STRING (param, i));
1421 	   return GDBMSHELL_SYNTAX;
1422 	 }
1423     }
1424 
1425   rc = gdbm_load (&gdbm_file, PARAM_STRING (param, 0), flag,
1426 		   meta_mask, &err_line);
1427   if (rc && gdbm_errno == GDBM_NO_DBNAME)
1428     {
1429       char *save_mode;
1430 
1431       variable_get ("open", VART_STRING, (void**) &save_mode);
1432       save_mode = estrdup (save_mode);
1433       variable_set ("open", VART_STRING, "newdb");
1434 
1435       rc = checkdb ();
1436       variable_set ("open", VART_STRING, save_mode);
1437       free (save_mode);
1438 
1439       if (rc)
1440 	 return rc;
1441 
1442       rc = gdbm_load (&gdbm_file, PARAM_STRING (param, 0), flag,
1443 		       meta_mask, &err_line);
1444     }
1445   if (rc)
1446     {
1447       switch (gdbm_errno)
1448 	 {
1449 	 case GDBM_ERR_FILE_OWNER:
1450 	 case GDBM_ERR_FILE_MODE:
1451 	   dberror ("%s", _("error restoring metadata"));
1452 	   break;
1453 
1454 	 default:
1455 	   if (err_line)
1456 	     dberror ("%s:%lu", PARAM_STRING (param, 0), err_line);
1457 	   else
1458 	     dberror (_("cannot load from %s"), PARAM_STRING (param, 0));
1459 	 }
1460       return GDBMSHELL_GDBM_ERR;
1461     }
1462 
1463   free (file_name);
1464   if (gdbm_setopt (gdbm_file, GDBM_GETDBNAME, &file_name, sizeof (file_name)))
1465     {
1466       dberror ("%s", "GDBM_GETDBNAME");
1467       rc = GDBMSHELL_GDBM_ERR;
1468     }
1469   else
1470     {
1471       variable_set ("filename", VART_STRING, file_name);
1472       variable_unset ("fd");
1473     }
1474   return rc;
1475 }
1476 
1477 /* status - print current program status */
1478 static int
status_handler(struct command_param * param GDBM_ARG_UNUSED,struct command_environ * cenv)1479 status_handler (struct command_param *param GDBM_ARG_UNUSED,
1480 		struct command_environ *cenv)
1481 {
1482   char *file_name;
1483 
1484   variable_get ("filename", VART_STRING, (void**) &file_name);
1485   fprintf (cenv->fp, _("Database file: %s\n"), file_name);
1486   if (gdbm_file)
1487     fprintf (cenv->fp, "%s\n", _("Database is open"));
1488   else
1489     fprintf (cenv->fp, "%s\n", _("Database is not open"));
1490   dsprint (cenv->fp, DS_KEY, dsdef[DS_KEY]);
1491   dsprint (cenv->fp, DS_CONTENT, dsdef[DS_CONTENT]);
1492   return GDBMSHELL_OK;
1493 }
1494 
1495 #if GDBM_DEBUG_ENABLE
1496 static int
debug_flag_printer(void * data,int flag,char const * tok)1497 debug_flag_printer (void *data, int flag, char const *tok)
1498 {
1499   FILE *fp = data;
1500   fprintf (fp, " %s", tok);
1501   return 0;
1502 }
1503 #endif
1504 
1505 static int
debug_handler(struct command_param * param,struct command_environ * cenv)1506 debug_handler (struct command_param *param, struct command_environ *cenv)
1507 {
1508 #if GDBM_DEBUG_ENABLE
1509   if (param->vararg)
1510     {
1511       struct gdbmarg *arg;
1512       int i;
1513 
1514       for (arg = param->vararg, i = 0; arg; arg = arg->next, i++)
1515 	{
1516 	  if (arg->type == GDBM_ARG_STRING)
1517 	    {
1518 	      int flag;
1519 	      int negate;
1520 	      char const *tok = arg->v.string;
1521 
1522 	      if (tok[0] == '-')
1523 		{
1524 		  ++tok;
1525 		  negate = 1;
1526 		}
1527 	      else if (tok[0] == '+')
1528 		{
1529 		  ++tok;
1530 		  negate = 0;
1531 		}
1532 	      else
1533 		negate = 0;
1534 
1535               flag = gdbm_debug_token (tok);
1536 	      if (flag)
1537 		{
1538 		  if (negate)
1539 		    gdbm_debug_flags &= ~flag;
1540 		  else
1541 		    gdbm_debug_flags |= flag;
1542 		}
1543 	      else
1544 		terror (_("unknown debug flag: %s"), tok);
1545 	    }
1546 	  else
1547 	    terror (_("invalid type of argument %d"), i);
1548 	}
1549     }
1550   else
1551     {
1552       fprintf (cenv->fp, _("Debug flags:"));
1553       if (gdbm_debug_flags)
1554 	{
1555 	  gdbm_debug_parse_state (debug_flag_printer, cenv->fp);
1556 	}
1557       else
1558 	fprintf (cenv->fp, " %s", _("none"));
1559       fputc ('\n', cenv->fp);
1560     }
1561 #else
1562   terror ("%s", _("compiled without debug support"));
1563 #endif
1564   return GDBMSHELL_OK;
1565 }
1566 
1567 static int
shell_handler(struct command_param * param,struct command_environ * cenv GDBM_ARG_UNUSED)1568 shell_handler (struct command_param *param,
1569 	       struct command_environ *cenv GDBM_ARG_UNUSED)
1570 {
1571   char *argv[4];
1572   pid_t pid, rc;
1573   int status;
1574 
1575   argv[0] = getenv ("$SHELL");
1576   if (!argv[0])
1577     argv[0] = "/bin/sh";
1578   if (param->vararg)
1579     {
1580       argv[1] = "-c";
1581       argv[2] = param->vararg->v.string;
1582       argv[3] = NULL;
1583     }
1584   else
1585     {
1586       argv[1] = NULL;
1587     }
1588 
1589   pid = fork ();
1590   if (pid == -1)
1591     {
1592       terror ("fork: %s", strerror (errno));
1593       return GDBMSHELL_ERR;
1594     }
1595   if (pid == 0)
1596     {
1597       execv (argv[0], argv);
1598       _exit (127);
1599     }
1600 
1601   rc = waitpid (pid, &status, 0);
1602   if (rc == -1)
1603     {
1604       terror ("waitpid: %s", strerror (errno));
1605       rc = GDBMSHELL_ERR;
1606     }
1607   else if (!interactive ())
1608     {
1609       if (WIFEXITED (status))
1610 	{
1611 	  if (WEXITSTATUS (status) != 0)
1612 	    terror (_("command failed with status %d"), WEXITSTATUS (status));
1613 	}
1614       else if (WIFSIGNALED (status))
1615 	terror (_("command terminated on signal %d"), WTERMSIG (status));
1616     }
1617   return rc;
1618 }
1619 
1620 static int
source_handler(struct command_param * param,struct command_environ * cenv GDBM_ARG_UNUSED)1621 source_handler (struct command_param *param,
1622 		struct command_environ *cenv GDBM_ARG_UNUSED)
1623 {
1624   char *fname = tildexpand (PARAM_STRING (param, 0));
1625   instream_t istr = instream_file_create (fname);
1626   free (fname);
1627   if (istr && input_context_push (istr) == 0)
1628     {
1629       yyparse ();
1630       input_context_drain ();
1631       yylex_destroy ();
1632     }
1633   return GDBMSHELL_OK;
1634 }
1635 
1636 static int
perror_handler(struct command_param * param,struct command_environ * cenv)1637 perror_handler (struct command_param *param, struct command_environ *cenv)
1638 {
1639   int n;
1640 
1641   if (param->argc)
1642     {
1643       if (getnum (&n, PARAM_STRING (param, 0), NULL))
1644 	return GDBMSHELL_SYNTAX;
1645     }
1646   else if ((n = checkdb ()) != GDBMSHELL_OK)
1647     {
1648       return n;
1649     }
1650   else
1651     {
1652       n = gdbm_last_errno (gdbm_file);
1653     }
1654   fprintf (cenv->fp, "GDBM error code %d: \"%s\"\n", n, gdbm_strerror (n));
1655   if (gdbm_check_syserr (n))
1656     {
1657       if (param->argc)
1658 	fprintf (cenv->fp, "Examine errno.\n");
1659       else
1660 	fprintf (cenv->fp, "System error code %d: \"%s\"\n",
1661 		 gdbm_last_syserr (gdbm_file),
1662 		 strerror (gdbm_last_syserr (gdbm_file)));
1663     }
1664   return GDBMSHELL_OK;
1665 }
1666 
1667 struct history_param
1668 {
1669   int from;
1670   int count;
1671 };
1672 
1673 static int
input_history_begin(struct command_param * param,struct command_environ * cenv GDBM_ARG_UNUSED,size_t * exp_count)1674 input_history_begin (struct command_param *param,
1675 		     struct command_environ *cenv GDBM_ARG_UNUSED,
1676 		     size_t *exp_count)
1677 {
1678   struct history_param *p;
1679   int hlen = input_history_size ();
1680   int from = 0, count = hlen;
1681 
1682   if (hlen == -1)
1683     {
1684       /* TRANSLATORS: %s is the stream name */
1685       terror (_("input history is not available for %s input stream"),
1686 	      input_stream_name ());
1687       return GDBMSHELL_OK;
1688     }
1689 
1690   switch (param->argc)
1691     {
1692     case 1:
1693       if (getnum (&count, param->argv[0]->v.string, NULL))
1694 	return 1;
1695       if (count > hlen)
1696 	count = hlen;
1697       else
1698 	from = hlen - count;
1699       break;
1700 
1701     case 2:
1702       if (getnum (&from, param->argv[0]->v.string, NULL))
1703 	return 1;
1704       if (from)
1705 	--from;
1706       if (getnum (&count, param->argv[1]->v.string, NULL))
1707 	return GDBMSHELL_OK;
1708 
1709       if (count > hlen)
1710 	count = hlen;
1711     }
1712   p = emalloc (sizeof *p);
1713   p->from = from;
1714   p->count = count;
1715   cenv->data = p;
1716   if (exp_count)
1717     *exp_count = count;
1718   return GDBMSHELL_OK;
1719 }
1720 
1721 static int
input_history_handler(struct command_param * param GDBM_ARG_UNUSED,struct command_environ * cenv)1722 input_history_handler (struct command_param *param GDBM_ARG_UNUSED,
1723 		       struct command_environ *cenv)
1724 {
1725   struct history_param *p = cenv->data;
1726   int i;
1727   FILE *fp = cenv->fp;
1728 
1729   for (i = 0; i < p->count; i++)
1730     {
1731       const char *s = input_history_get (p->from + i);
1732       if (!s)
1733 	break;
1734       fprintf (fp, "%4d) %s\n", p->from + i + 1, s);
1735     }
1736   return GDBMSHELL_OK;
1737 }
1738 
1739 
1740 static int help_handler (struct command_param *, struct command_environ *);
1741 static int help_begin (struct command_param *, struct command_environ *,
1742 		       size_t *);
1743 
1744 struct argdef
1745 {
1746   char *name;
1747   int type;
1748   int ds;
1749 };
1750 
1751 #define NARGS 10
1752 
1753 enum command_repeat_type
1754   {
1755     REPEAT_NEVER,
1756     REPEAT_ALWAYS,
1757     REPEAT_NOARG
1758   };
1759 
1760 struct command
1761 {
1762   char *name;           /* Command name */
1763   size_t len;           /* Name length */
1764   int tok;
1765   int (*begin) (struct command_param *param, struct command_environ *cenv, size_t *);
1766   int (*handler) (struct command_param *param, struct command_environ *cenv);
1767   void (*end) (void *data);
1768   struct argdef args[NARGS];
1769   int variadic;
1770   enum command_repeat_type repeat;
1771   char *doc;
1772 };
1773 
1774 static struct command command_tab[] = {
1775   {
1776     .name = "count",
1777     .doc = N_("count (number of entries)"),
1778     .tok = T_CMD,
1779     .begin = checkdb_begin,
1780     .handler = count_handler,
1781     .variadic = FALSE,
1782     .repeat = REPEAT_NEVER,
1783   },
1784   {
1785     .name = "delete",
1786     .args = {
1787       { N_("KEY"), GDBM_ARG_DATUM, DS_KEY },
1788       { NULL }
1789     },
1790     .doc = N_("delete a record"),
1791     .tok = T_CMD,
1792     .begin = checkdb_begin,
1793     .handler = delete_handler,
1794     .variadic = FALSE,
1795     .repeat = REPEAT_NEVER,
1796   },
1797   {
1798     .name = "export",
1799     .args = {
1800       { N_("FILE"), GDBM_ARG_STRING },
1801       { "[truncate]", GDBM_ARG_STRING },
1802       { "[binary|ascii]", GDBM_ARG_STRING },
1803       { NULL }
1804     },
1805     .doc = N_("export"),
1806     .tok = T_CMD,
1807     .begin = checkdb_begin,
1808     .handler = export_handler,
1809     .variadic = FALSE,
1810     .repeat = REPEAT_NEVER,
1811   },
1812   {
1813     .name = "fetch",
1814     .args = {
1815       { N_("KEY"), GDBM_ARG_DATUM, DS_KEY },
1816       { NULL }
1817     },
1818     .doc = N_("fetch record"),
1819     .tok = T_CMD,
1820     .begin = checkdb_begin,
1821     .handler = fetch_handler,
1822     .variadic = FALSE,
1823     .repeat = REPEAT_NEVER,
1824   },
1825   {
1826     .name = "import",
1827     .args = {
1828       { N_("FILE"), GDBM_ARG_STRING },
1829       { "[replace]", GDBM_ARG_STRING },
1830       { "[nometa]" , GDBM_ARG_STRING },
1831       { NULL }
1832     },
1833     .doc = N_("import"),
1834     .tok = T_CMD,
1835     .handler = import_handler,
1836     .variadic = FALSE,
1837     .repeat = REPEAT_NEVER,
1838   },
1839   {
1840     .name = "list",
1841     .doc = N_("list"),
1842     .tok = T_CMD,
1843     .begin = list_begin,
1844     .handler = list_handler,
1845     .variadic = FALSE,
1846     .repeat = REPEAT_NEVER,
1847   },
1848   {
1849     .name = "next",
1850     .args = {
1851       { N_("[KEY]"), GDBM_ARG_DATUM, DS_KEY },
1852       { NULL }
1853     },
1854     .doc = N_("continue iteration: get next key and datum"),
1855     .tok = T_CMD,
1856     .begin = checkdb_begin,
1857     .handler = nextkey_handler,
1858     .variadic = FALSE,
1859     .repeat = REPEAT_NOARG,
1860   },
1861   {
1862     .name = "store",
1863     .args = {
1864       { N_("KEY"), GDBM_ARG_DATUM, DS_KEY },
1865       { N_("DATA"), GDBM_ARG_DATUM, DS_CONTENT },
1866       { NULL }
1867     },
1868     .doc = N_("store"),
1869     .tok = T_CMD,
1870     .begin = checkdb_begin,
1871     .handler = store_handler,
1872     .variadic = FALSE,
1873     .repeat = REPEAT_NEVER,
1874   },
1875   {
1876     .name = "first",
1877     .doc = N_("begin iteration: get first key and datum"),
1878     .tok = T_CMD,
1879     .begin = checkdb_begin,
1880     .handler = firstkey_handler,
1881     .variadic = FALSE,
1882     .repeat = REPEAT_NEVER,
1883   },
1884   {
1885     .name = "reorganize",
1886     .doc = N_("reorganize"),
1887     .tok = T_CMD,
1888     .begin = checkdb_begin,
1889     .handler = reorganize_handler,
1890     .variadic = FALSE,
1891     .repeat = REPEAT_NEVER,
1892   },
1893   {
1894     .name = "recover",
1895     .args = {
1896       { "[verbose]", GDBM_ARG_STRING },
1897       { "[summary]", GDBM_ARG_STRING },
1898       { "[backup]",  GDBM_ARG_STRING },
1899       { "[force]",   GDBM_ARG_STRING },
1900       { "[max-failed-keys=N]", GDBM_ARG_STRING },
1901       { "[max-failed-buckets=N]", GDBM_ARG_STRING },
1902       { "[max-failures=N]", GDBM_ARG_STRING },
1903       { NULL }
1904     },
1905     .doc = N_("recover the database"),
1906     .tok = T_CMD,
1907     .begin = checkdb_begin,
1908     .handler = recover_handler,
1909     .variadic = FALSE,
1910     .repeat = REPEAT_NEVER,
1911   },
1912   {
1913     .name = "avail",
1914     .doc = N_("print avail list"),
1915     .tok = T_CMD,
1916     .begin = avail_begin,
1917     .handler = avail_handler,
1918     .variadic = FALSE,
1919     .repeat = REPEAT_NEVER,
1920   },
1921   {
1922     .name = "bucket",
1923     .args = {
1924       { N_("NUMBER"), GDBM_ARG_STRING },
1925       { NULL }
1926     },
1927     .doc = N_("print a bucket"),
1928     .tok = T_CMD,
1929     .begin = print_bucket_begin,
1930     .handler = print_current_bucket_handler,
1931     .variadic = FALSE,
1932     .repeat = REPEAT_NEVER,
1933   },
1934   {
1935     .name = "current",
1936     .doc = N_("print current bucket"),
1937     .tok = T_CMD,
1938     .begin = print_current_bucket_begin,
1939     .handler = print_current_bucket_handler,
1940     .variadic = FALSE,
1941     .repeat = REPEAT_NEVER,
1942   },
1943   {
1944     .name = "dir",
1945     .doc = N_("print hash directory"),
1946     .tok = T_CMD,
1947     .begin = print_dir_begin,
1948     .handler = print_dir_handler,
1949     .variadic = FALSE,
1950     .repeat = REPEAT_NEVER,
1951   },
1952   {
1953     .name = "header",
1954     .doc = N_("print database file header"),
1955     .tok = T_CMD,
1956     .begin = print_header_begin,
1957     .handler = print_header_handler,
1958     .variadic = FALSE,
1959     .repeat = REPEAT_NEVER,
1960   },
1961   {
1962     .name = "hash",
1963     .args = {
1964       { N_("KEY"), GDBM_ARG_DATUM, DS_KEY },
1965       { NULL }
1966     },
1967     .doc = N_("hash value of key"),
1968     .tok = T_CMD,
1969     .handler = hash_handler,
1970     .variadic = FALSE,
1971     .repeat = REPEAT_NEVER,
1972   },
1973   {
1974     .name = "cache",
1975     .doc = N_("print the bucket cache"),
1976     .tok = T_CMD,
1977     .begin = print_cache_begin,
1978     .handler = print_cache_handler,
1979     .variadic = FALSE,
1980     .repeat = REPEAT_NEVER,
1981   },
1982   {
1983     .name = "status",
1984     .doc = N_("print current program status"),
1985     .tok = T_CMD,
1986     .handler = status_handler,
1987     .variadic = FALSE,
1988     .repeat = REPEAT_NEVER,
1989   },
1990   {
1991     .name = "sync",
1992     .doc = N_("Synchronize the database with disk copy"),
1993     .tok = T_CMD,
1994     .begin = checkdb_begin,
1995     .handler = sync_handler,
1996     .variadic = FALSE,
1997     .repeat = REPEAT_NEVER,
1998   },
1999   {
2000     .name = "upgrade",
2001     .doc = N_("Upgrade the database to extended format"),
2002     .tok = T_CMD,
2003     .begin = checkdb_begin,
2004     .handler = upgrade_handler,
2005     .variadic = FALSE,
2006     .repeat = REPEAT_NEVER,
2007   },
2008   {
2009     .name = "downgrade",
2010     .doc = N_("Downgrade the database to standard format"),
2011     .tok = T_CMD,
2012     .begin = checkdb_begin,
2013     .handler = downgrade_handler,
2014     .variadic = FALSE,
2015     .repeat = REPEAT_NEVER,
2016   },
2017   {
2018     .name = "snapshot",
2019     .args = {
2020       { "FILE", GDBM_ARG_STRING },
2021       { "FILE", GDBM_ARG_STRING },
2022       { NULL }
2023     },
2024     .doc = N_("analyze two database snapshots"),
2025     .tok = T_CMD,
2026     .handler = snapshot_handler,
2027     .variadic = FALSE,
2028     .repeat = REPEAT_NEVER,
2029   },
2030   {
2031     .name = "version",
2032     .doc = N_("print version of gdbm"),
2033     .tok = T_CMD,
2034     .handler = print_version_handler,
2035     .variadic = FALSE,
2036     .repeat = REPEAT_NEVER,
2037   },
2038   {
2039     .name = "help",
2040     .doc = N_("print this help list"),
2041     .tok = T_CMD,
2042     .begin = help_begin,
2043     .handler = help_handler,
2044     .variadic = FALSE,
2045     .repeat = REPEAT_NEVER,
2046   },
2047   {
2048     .name = "quit",
2049     .doc = N_("quit the program"),
2050     .tok = T_CMD,
2051     .handler = quit_handler,
2052     .variadic = FALSE,
2053     .repeat = REPEAT_NEVER,
2054   },
2055   {
2056     .name = "set",
2057     .args = {
2058       { "[VAR=VALUE...]" },
2059       { NULL }
2060     },
2061     .doc = N_("set or list variables"),
2062     .tok = T_SET,
2063     .variadic = FALSE,
2064     .repeat = REPEAT_NEVER,
2065   },
2066   {
2067     .name = "unset",
2068     .args = {
2069       { "VAR..." },
2070       { NULL }
2071     },
2072     .doc = N_("unset variables"),
2073     .tok = T_UNSET,
2074     .variadic = FALSE,
2075     .repeat = REPEAT_NEVER,
2076   },
2077   {
2078     .name = "define",
2079     .args = {
2080       { "key|content", GDBM_ARG_STRING },
2081       { "{ FIELD-LIST }", GDBM_ARG_STRING },
2082       { NULL }
2083     },
2084     .doc = N_("define datum structure"),
2085     .tok = T_DEF,
2086     .variadic = FALSE,
2087     .repeat = REPEAT_NEVER,
2088   },
2089   {
2090     .name = "source",
2091     .args = {
2092       { "FILE", GDBM_ARG_STRING },
2093       { NULL }
2094     },
2095     .doc = N_("source command script"),
2096     .tok = T_CMD,
2097     .handler = source_handler,
2098     .variadic = FALSE,
2099     .repeat = REPEAT_NEVER,
2100   },
2101   {
2102     .name = "close",
2103     .doc = N_("close the database"),
2104     .tok = T_CMD,
2105     .handler = close_handler,
2106     .variadic = FALSE,
2107     .repeat = REPEAT_NEVER,
2108   },
2109   {
2110     .name = "open",
2111     .args = {
2112       { "[FILE]", GDBM_ARG_STRING },
2113       { NULL }
2114     },
2115     .doc = N_("open new database"),
2116     .tok = T_CMD,
2117     .handler = open_handler,
2118     .variadic = FALSE,
2119     .repeat = REPEAT_NEVER,
2120   },
2121   {
2122     .name = "history",
2123     .args = {
2124       { N_("[FROM]"), GDBM_ARG_STRING },
2125       { N_("[COUNT]"), GDBM_ARG_STRING },
2126       { NULL }
2127     },
2128     .doc = N_("show input history"),
2129     .tok = T_CMD,
2130     .begin = input_history_begin,
2131     .handler = input_history_handler,
2132     .variadic = FALSE,
2133     .repeat = REPEAT_NEVER,
2134   },
2135   {
2136     .name = "debug",
2137     .doc = N_("query/set debug level"),
2138     .tok = T_CMD,
2139     .handler = debug_handler,
2140     .variadic = TRUE,
2141     .repeat = REPEAT_NEVER,
2142   },
2143   {
2144     .name = "shell",
2145     .doc = N_("invoke the shell"),
2146     .tok = T_SHELL,
2147     .handler = shell_handler,
2148     .variadic = TRUE,
2149     .repeat = REPEAT_NEVER,
2150   },
2151   {
2152     .name = "perror",
2153     .args = {
2154       { "[CODE]", GDBM_ARG_STRING },
2155       { NULL }
2156     },
2157     .doc = N_("describe GDBM error code"),
2158     .tok = T_CMD,
2159     .handler = perror_handler,
2160     .variadic = FALSE,
2161     .repeat = REPEAT_NEVER,
2162   },
2163   { NULL }
2164 };
2165 
2166 static int commands_sorted;
2167 
2168 static int
cmdcmp(const void * a,const void * b)2169 cmdcmp (const void *a, const void *b)
2170 {
2171   struct command const *ac = a;
2172   struct command const *bc = b;
2173   return strcmp (ac->name, bc->name);
2174 }
2175 
2176 /* Generator function for command completion.  STATE lets us know whether
2177    to start from scratch; without any state (i.e. STATE == 0), then we
2178    start at the top of the list. */
2179 char *
command_generator(const char * text,int state)2180 command_generator (const char *text, int state)
2181 {
2182   const char *name;
2183   static int len;
2184   static struct command *cmd;
2185 
2186   /* If this is a new word to complete, initialize now.  This includes
2187      saving the length of TEXT for efficiency, and initializing the index
2188      variable to 0. */
2189   if (!state)
2190     {
2191       cmd = command_tab;
2192       len = strlen (text);
2193     }
2194 
2195   if (!cmd || !cmd->name)
2196     return NULL;
2197 
2198   /* Return the next name which partially matches from the command list. */
2199   while ((name = cmd->name))
2200     {
2201       cmd++;
2202       if (strncmp (name, text, len) == 0)
2203         return strdup (name);
2204     }
2205 
2206   /* If no names matched, then return NULL. */
2207   return NULL;
2208 }
2209 
2210 /* ? - help handler */
2211 #define CMDCOLS 30
2212 
2213 static int
help_begin(struct command_param * param GDBM_ARG_UNUSED,struct command_environ * cenv GDBM_ARG_UNUSED,size_t * exp_count)2214 help_begin (struct command_param *param GDBM_ARG_UNUSED,
2215 	    struct command_environ *cenv GDBM_ARG_UNUSED,
2216 	    size_t *exp_count)
2217 {
2218   if (exp_count)
2219     *exp_count = ARRAY_SIZE (command_tab) + 1;
2220   return 0;
2221 }
2222 
2223 static int
help_handler(struct command_param * param GDBM_ARG_UNUSED,struct command_environ * cenv)2224 help_handler (struct command_param *param GDBM_ARG_UNUSED,
2225 	      struct command_environ *cenv)
2226 {
2227   struct command *cmd;
2228   FILE *fp = cenv->fp;
2229 
2230   for (cmd = command_tab; cmd->name; cmd++)
2231     {
2232       int i;
2233       int n;
2234       int optoff;
2235 
2236       n = fprintf (fp, " %s", cmd->name);
2237       optoff = n;
2238 
2239       for (i = 0; i < NARGS && cmd->args[i].name; i++)
2240 	{
2241 	  if (n >= CMDCOLS)
2242 	    {
2243 	      fputc ('\n', fp);
2244 	      n = fprintf (fp, "%*.*s", optoff, optoff, "");
2245 	    }
2246 	  n += fprintf (fp, " %s", gettext (cmd->args[i].name));
2247 	}
2248 
2249       if (n < CMDCOLS)
2250 	fprintf (fp, "%*.s", CMDCOLS-n, "");
2251       else
2252 	fprintf (fp, "\n%*.*s", CMDCOLS, CMDCOLS, "");
2253       fprintf (fp, " %s", gettext (cmd->doc));
2254       fputc ('\n', fp);
2255     }
2256   return 0;
2257 }
2258 
2259 int
command_lookup(const char * str,struct locus * loc,struct command ** pcmd)2260 command_lookup (const char *str, struct locus *loc, struct command **pcmd)
2261 {
2262   enum { fcom_init, fcom_found, fcom_ambig, fcom_abort } state = fcom_init;
2263   struct command *cmd, *found = NULL;
2264   size_t len = strlen (str);
2265 
2266   for (cmd = command_tab; state != fcom_abort && cmd->name; cmd++)
2267     {
2268       size_t n = len < cmd->len ? len : cmd->len;
2269       if (memcmp (cmd->name, str, n) == 0 && str[n] == 0)
2270 	{
2271 	  switch (state)
2272 	    {
2273 	    case fcom_init:
2274 	      found = cmd;
2275 	      state = fcom_found;
2276 	      break;
2277 
2278 	    case fcom_found:
2279 	      if (!interactive ())
2280 		{
2281 		  state = fcom_abort;
2282 		  found = NULL;
2283 		  continue;
2284 		}
2285 	      fprintf (stderr, "ambiguous command: %s\n", str);
2286 	      fprintf (stderr, "    %s\n", found->name);
2287 	      found = NULL;
2288 	      state = fcom_ambig;
2289 	      /* fall through */
2290 	    case fcom_ambig:
2291 	      fprintf (stderr, "    %s\n", cmd->name);
2292 	      break;
2293 
2294 	    case fcom_abort:
2295 	      /* should not happen */
2296 	      abort ();
2297 	    }
2298 	}
2299     }
2300 
2301   if (state == fcom_init)
2302     lerror (loc, interactive () ? _("Invalid command. Try ? for help.") :
2303 	                          _("Unknown command"));
2304   if (!found)
2305     return T_BOGUS;
2306 
2307   *pcmd = found;
2308   return found->tok;
2309 }
2310 
2311 struct gdbmarg *
gdbmarg_string(char * string,struct locus * loc)2312 gdbmarg_string (char *string, struct locus *loc)
2313 {
2314   struct gdbmarg *arg = ecalloc (1, sizeof (*arg));
2315   arg->next = NULL;
2316   arg->type = GDBM_ARG_STRING;
2317   arg->ref = 1;
2318   if (loc)
2319     arg->loc = *loc;
2320   arg->v.string = string;
2321   return arg;
2322 }
2323 
2324 struct gdbmarg *
gdbmarg_datum(datum * dat,struct locus * loc)2325 gdbmarg_datum (datum *dat, struct locus *loc)
2326 {
2327   struct gdbmarg *arg = ecalloc (1, sizeof (*arg));
2328   arg->next = NULL;
2329   arg->type = GDBM_ARG_DATUM;
2330   arg->ref = 1;
2331   if (loc)
2332     arg->loc = *loc;
2333   arg->v.dat = *dat;
2334   return arg;
2335 }
2336 
2337 struct gdbmarg *
gdbmarg_kvpair(struct kvpair * kvp,struct locus * loc)2338 gdbmarg_kvpair (struct kvpair *kvp, struct locus *loc)
2339 {
2340   struct gdbmarg *arg = ecalloc (1, sizeof (*arg));
2341   arg->next = NULL;
2342   arg->type = GDBM_ARG_KVPAIR;
2343   arg->ref = 1;
2344   if (loc)
2345     arg->loc = *loc;
2346   arg->v.kvpair = kvp;
2347   return arg;
2348 }
2349 
2350 struct slist *
slist_new_s(char * s)2351 slist_new_s (char *s)
2352 {
2353   struct slist *lp = emalloc (sizeof (*lp));
2354   lp->next = NULL;
2355   lp->str = s;
2356   return lp;
2357 }
2358 
2359 struct slist *
slist_new(char const * s)2360 slist_new (char const *s)
2361 {
2362   return slist_new_s (estrdup (s));
2363 }
2364 
2365 struct slist *
slist_new_l(char const * s,size_t l)2366 slist_new_l (char const *s, size_t l)
2367 {
2368   char *copy = emalloc (l + 1);
2369   memcpy (copy, s, l);
2370   copy[l] = 0;
2371   return slist_new_s (copy);
2372 }
2373 
2374 void
slist_free(struct slist * lp)2375 slist_free (struct slist *lp)
2376 {
2377   while (lp)
2378     {
2379       struct slist *next = lp->next;
2380       free (lp->str);
2381       free (lp);
2382       lp = next;
2383     }
2384 }
2385 
2386 void
slist_insert(struct slist ** where,struct slist * what)2387 slist_insert (struct slist **where, struct slist *what)
2388 {
2389   if (*where)
2390     {
2391       while (what->next)
2392 	what = what->next;
2393       what->next = (*where)->next;
2394       (*where)->next = what;
2395     }
2396   else
2397     what->next = NULL;
2398   *where = what;
2399 }
2400 
2401 struct kvpair *
kvpair_string(struct locus * loc,char * val)2402 kvpair_string (struct locus *loc, char *val)
2403 {
2404   struct kvpair *p = ecalloc (1, sizeof (*p));
2405   p->type = KV_STRING;
2406   if (loc)
2407     p->loc = *loc;
2408   p->val.s = val;
2409   return p;
2410 }
2411 
2412 struct kvpair *
kvpair_list(struct locus * loc,struct slist * s)2413 kvpair_list (struct locus *loc, struct slist *s)
2414 {
2415   struct kvpair *p = ecalloc (1, sizeof (*p));
2416   p->type = KV_LIST;
2417   if (loc)
2418     p->loc = *loc;
2419   p->val.l = s;
2420   return p;
2421 }
2422 
2423 void
kvlist_free(struct kvpair * kvp)2424 kvlist_free (struct kvpair *kvp)
2425 {
2426   while (kvp)
2427     {
2428       struct kvpair *next = kvp->next;
2429       free (kvp->key);
2430       switch (kvp->type)
2431 	{
2432 	case KV_STRING:
2433 	  free (kvp->val.s);
2434 	  break;
2435 
2436 	case KV_LIST:
2437 	  slist_free (kvp->val.l);
2438 	  break;
2439 	}
2440       free (kvp);
2441       kvp = next;
2442     }
2443 }
2444 
2445 struct kvpair *
kvlist_find(struct kvpair * kv,char const * tag)2446 kvlist_find (struct kvpair *kv, char const *tag)
2447 {
2448   for (; kv; kv = kv->next)
2449     if (kv->key && strcmp (kv->key, tag) == 0)
2450       break;
2451   return kv;
2452 }
2453 
2454 int
gdbmarg_free(struct gdbmarg * arg)2455 gdbmarg_free (struct gdbmarg *arg)
2456 {
2457   if (arg && --arg->ref == 0)
2458     {
2459       switch (arg->type)
2460 	{
2461 	case GDBM_ARG_STRING:
2462 	  free (arg->v.string);
2463 	  break;
2464 
2465 	case GDBM_ARG_KVPAIR:
2466 	  kvlist_free (arg->v.kvpair);
2467 	  break;
2468 
2469 	case GDBM_ARG_DATUM:
2470 	  free (arg->v.dat.dptr);
2471 	  break;
2472 	}
2473       free (arg);
2474       return 0;
2475     }
2476   return 1;
2477 }
2478 
2479 void
gdbmarg_destroy(struct gdbmarg ** parg)2480 gdbmarg_destroy (struct gdbmarg **parg)
2481 {
2482   if (parg && gdbmarg_free (*parg))
2483     *parg = NULL;
2484 }
2485 
2486 void
gdbmarglist_init(struct gdbmarglist * lst,struct gdbmarg * arg)2487 gdbmarglist_init (struct gdbmarglist *lst, struct gdbmarg *arg)
2488 {
2489   if (arg)
2490     arg->next = NULL;
2491   lst->head = lst->tail = arg;
2492 }
2493 
2494 void
gdbmarglist_add(struct gdbmarglist * lst,struct gdbmarg * arg)2495 gdbmarglist_add (struct gdbmarglist *lst, struct gdbmarg *arg)
2496 {
2497   arg->next = NULL;
2498   if (lst->tail)
2499     lst->tail->next = arg;
2500   else
2501     lst->head = arg;
2502   lst->tail = arg;
2503 }
2504 
2505 void
gdbmarglist_free(struct gdbmarglist * lst)2506 gdbmarglist_free (struct gdbmarglist *lst)
2507 {
2508   struct gdbmarg *arg;
2509 
2510   for (arg = lst->head; arg; )
2511     {
2512       struct gdbmarg *next = arg->next;
2513       gdbmarg_free (arg);
2514       arg = next;
2515     }
2516   lst->head = lst->tail = NULL;
2517 }
2518 
2519 static void
param_expand(struct command_param * p)2520 param_expand (struct command_param *p)
2521 {
2522   if (p->argc == p->argmax)
2523     p->argv = e2nrealloc (p->argv, &p->argmax, sizeof (p->argv[0]));
2524 }
2525 
2526 static void
param_free_argv(struct command_param * p)2527 param_free_argv (struct command_param *p)
2528 {
2529   size_t i;
2530 
2531   for (i = 0; i < p->argc; i++)
2532     gdbmarg_destroy (&p->argv[i]);
2533   p->argc = 0;
2534 }
2535 
2536 static void
param_free(struct command_param * p)2537 param_free (struct command_param *p)
2538 {
2539   param_free_argv (p);
2540   free (p->argv);
2541   p->argv = NULL;
2542   p->argmax = 0;
2543 }
2544 
2545 static struct gdbmarg *coerce (struct gdbmarg *arg, struct argdef *def);
2546 
2547 static int
param_push_arg(struct command_param * p,struct gdbmarg * arg,struct argdef * def)2548 param_push_arg (struct command_param *p, struct gdbmarg *arg,
2549 		struct argdef *def)
2550 {
2551   param_expand (p);
2552   if ((p->argv[p->argc] = coerce (arg, def)) == NULL)
2553     {
2554       return 1;
2555     }
2556   p->argc++;
2557   return 0;
2558 }
2559 
2560 static void
param_term(struct command_param * p)2561 param_term (struct command_param *p)
2562 {
2563   param_expand (p);
2564   p->argv[p->argc] = NULL;
2565 }
2566 
2567 typedef struct gdbmarg *(*coerce_type_t) (struct gdbmarg *arg,
2568 					  struct argdef *def);
2569 
2570 struct gdbmarg *
2571 coerce_ref (struct gdbmarg *arg, struct argdef *def)
2572 {
2573   ++arg->ref;
2574   return arg;
2575 }
2576 
2577 struct gdbmarg *
2578 coerce_k2d (struct gdbmarg *arg, struct argdef *def)
2579 {
2580   datum d;
2581 
2582   if (datum_scan (&d, dsdef[def->ds], arg->v.kvpair))
2583     return NULL;
2584   return gdbmarg_datum (&d, &arg->loc);
2585 }
2586 
2587 struct gdbmarg *
2588 coerce_s2d (struct gdbmarg *arg, struct argdef *def)
2589 {
2590   datum d;
2591   struct kvpair kvp;
2592 
2593   memset (&kvp, 0, sizeof (kvp));
2594   kvp.type = KV_STRING;
2595   kvp.val.s = arg->v.string;
2596 
2597   if (datum_scan (&d, dsdef[def->ds], &kvp))
2598     return NULL;
2599   return gdbmarg_datum (&d, &arg->loc);
2600 }
2601 
2602 #define coerce_fail NULL
2603 
2604 coerce_type_t coerce_tab[GDBM_ARG_MAX][GDBM_ARG_MAX] = {
2605   /*             s            d            k */
2606   /* s */  { coerce_ref,  coerce_fail, coerce_fail },
2607   /* d */  { coerce_s2d,  coerce_ref,  coerce_k2d },
2608   /* k */  { coerce_fail, coerce_fail, coerce_ref }
2609 };
2610 
2611 char *argtypestr[] = { "string", "datum", "k/v pair" };
2612 
2613 static struct gdbmarg *
coerce(struct gdbmarg * arg,struct argdef * def)2614 coerce (struct gdbmarg *arg, struct argdef *def)
2615 {
2616   if (!coerce_tab[def->type][arg->type])
2617     {
2618       lerror (&arg->loc, _("cannot coerce %s to %s"),
2619 		    argtypestr[arg->type], argtypestr[def->type]);
2620       return NULL;
2621     }
2622   return coerce_tab[def->type][arg->type] (arg, def);
2623 }
2624 
2625 static struct command *last_cmd;
2626 static struct gdbmarglist last_args;
2627 
2628 int
run_last_command(void)2629 run_last_command (void)
2630 {
2631   if (interactive ())
2632     {
2633       if (last_cmd)
2634 	{
2635 	  switch (last_cmd->repeat)
2636 	    {
2637 	    case REPEAT_NEVER:
2638 	      break;
2639 	    case REPEAT_NOARG:
2640 	      gdbmarglist_free (&last_args);
2641 	      /* FALLTHROUGH */
2642 	    case REPEAT_ALWAYS:
2643 	      return run_command (last_cmd, &last_args);
2644 
2645 	    default:
2646 	      abort ();
2647 	    }
2648 	}
2649     }
2650   return 0;
2651 }
2652 
2653 static void
format_arg(struct gdbmarg * arg,struct argdef * def,FILE * fp)2654 format_arg (struct gdbmarg *arg, struct argdef *def, FILE *fp)
2655 {
2656   switch (arg->type)
2657     {
2658     case GDBM_ARG_STRING:
2659       fprintf (fp, " %s", arg->v.string);
2660       break;
2661 
2662     case GDBM_ARG_DATUM:
2663       if (def && def->type == GDBM_ARG_DATUM)
2664 	{
2665 	  fputc (' ', fp);
2666 	  datum_format (fp, &arg->v.dat, dsdef[def->ds]);
2667 	}
2668       else
2669 	/* Shouldn't happen */
2670 	terror ("%s:%d: INTERNAL ERROR: unexpected data type in arglist",
2671 		__FILE__, __LINE__);
2672       break;
2673 
2674     case GDBM_ARG_KVPAIR:
2675       {
2676 	struct kvpair *kvp = arg->v.kvpair;
2677 	fprintf (fp, " %s ", kvp->key);
2678 	switch (kvp->type)
2679 	  {
2680 	  case KV_STRING:
2681 	    fprintf (fp, "%s", kvp->val.s);
2682 	    break;
2683 
2684 	  case KV_LIST:
2685 	    {
2686 	      struct slist *p = kvp->val.l;
2687 	      fprintf (fp, "%s", p->str);
2688 	      while ((p = p->next) != NULL)
2689 		fprintf (fp, ", %s", p->str);
2690 	    }
2691 	  }
2692       }
2693     }
2694 }
2695 
2696 struct timing
2697 {
2698   struct timeval real;
2699   struct timeval user;
2700   struct timeval sys;
2701 };
2702 
2703 void
timing_start(struct timing * t)2704 timing_start (struct timing *t)
2705 {
2706   struct rusage r;
2707   gettimeofday (&t->real, NULL);
2708   getrusage (RUSAGE_SELF, &r);
2709   t->user  = r.ru_utime;
2710   t->sys = r.ru_stime;
2711 }
2712 
2713 static inline struct timeval
timeval_sub(struct timeval a,struct timeval b)2714 timeval_sub (struct timeval a, struct timeval b)
2715 {
2716   struct timeval diff;
2717 
2718   diff.tv_sec = a.tv_sec - b.tv_sec;
2719   diff.tv_usec = a.tv_usec - b.tv_usec;
2720   if (diff.tv_usec < 0)
2721     {
2722       --diff.tv_sec;
2723       diff.tv_usec += 1000000;
2724     }
2725 
2726   return diff;
2727 }
2728 
2729 void
timing_stop(struct timing * t)2730 timing_stop (struct timing *t)
2731 {
2732   struct rusage r;
2733   struct timeval now;
2734 
2735   gettimeofday (&now, NULL);
2736   getrusage (RUSAGE_SELF, &r);
2737   t->real = timeval_sub (now, t->real);
2738   t->user = timeval_sub (r.ru_utime, t->user);
2739   t->sys = timeval_sub (r.ru_stime, t->sys);
2740 }
2741 
2742 int
run_command(struct command * cmd,struct gdbmarglist * arglist)2743 run_command (struct command *cmd, struct gdbmarglist *arglist)
2744 {
2745   int i;
2746   struct gdbmarg *arg;
2747   char *pager = NULL;
2748   char argbuf[128];
2749   size_t expected_lines, *expected_lines_ptr;
2750   FILE *pagfp = NULL;
2751   struct command_param param = HANDLER_PARAM_INITIALIZER;
2752   struct command_environ cenv = COMMAND_ENVIRON_INITIALIZER;
2753   int rc = 0;
2754   struct timing tm;
2755 
2756   variable_get ("pager", VART_STRING, (void**) &pager);
2757 
2758   arg = arglist ? arglist->head : NULL;
2759 
2760   for (i = 0; cmd->args[i].name && arg; i++, arg = arg->next)
2761     {
2762       if (param_push_arg (&param, arg, &cmd->args[i]))
2763 	{
2764 	  param_free (&param);
2765 	  return 1;
2766 	}
2767     }
2768 
2769   for (; cmd->args[i].name; i++)
2770     {
2771       char *argname = cmd->args[i].name;
2772       struct gdbmarg *t;
2773 
2774       if (*argname == '[')
2775 	/* Optional argument */
2776 	break;
2777 
2778       if (!interactive ())
2779 	{
2780 	  terror (_("%s: not enough arguments"), cmd->name);
2781 	  param_free (&param);
2782 	  return 1;
2783 	}
2784       printf ("%s? ", argname);
2785       fflush (stdout);
2786       if (fgets (argbuf, sizeof argbuf, stdin) == NULL)
2787 	{
2788 	  terror (_("unexpected eof"));
2789 	  exit (EXIT_USAGE);
2790 	}
2791 
2792       trimnl (argbuf);
2793 
2794       t = gdbmarg_string (estrdup (argbuf), &yylloc);
2795       if (param_push_arg (&param, t, &cmd->args[i]))
2796 	{
2797 	  param_free (&param);
2798 	  gdbmarg_free (t);
2799 	  return 1;
2800 	}
2801     }
2802 
2803   if (arg && !cmd->variadic)
2804     {
2805       terror (_("%s: too many arguments"), cmd->name);
2806       param_free (&param);
2807       return 1;
2808     }
2809 
2810   /* Prepare for calling the handler */
2811   param_term (&param);
2812   param.vararg = arg;
2813   pagfp = NULL;
2814 
2815   if (variable_is_true ("trace"))
2816     {
2817       fprintf (stderr, "+ %s", cmd->name);
2818       for (i = 0; i < param.argc; i++)
2819 	{
2820 	  format_arg (param.argv[i], &cmd->args[i], stderr);
2821 	}
2822 
2823       if (param.vararg)
2824 	{
2825 	  struct gdbmarg *arg;
2826 	  for (arg = param.vararg; arg; arg = arg->next)
2827 	    format_arg (arg, NULL, stderr);
2828 	}
2829       fputc ('\n', stderr);
2830     }
2831 
2832   expected_lines = 0;
2833   expected_lines_ptr = (interactive () && pager) ? &expected_lines : NULL;
2834   rc = 0;
2835   if (!(cmd->begin && (rc = cmd->begin (&param, &cenv, expected_lines_ptr)) != 0))
2836     {
2837       if (pager && expected_lines > get_screen_lines ())
2838 	{
2839 	  pagfp = popen (pager, "w");
2840 	  if (pagfp)
2841 	    cenv.fp = pagfp;
2842 	  else
2843 	    {
2844 	      terror (_("cannot run pager `%s': %s"), pager,
2845 		      strerror (errno));
2846 	      pager = NULL;
2847 	      cenv.fp = stdout;
2848 	    }
2849 	}
2850       else
2851 	cenv.fp = stdout;
2852 
2853       timing_start (&tm);
2854       rc = cmd->handler (&param, &cenv);
2855       timing_stop (&tm);
2856       if (cmd->end)
2857 	cmd->end (cenv.data);
2858       else if (cenv.data)
2859 	free (cenv.data);
2860 
2861       if (variable_is_true ("timing"))
2862 	{
2863 	  fprintf (cenv.fp, "[%s r=%lu.%06lu u=%lu.%06lu s=%lu.%06lu]\n",
2864 		   cmd->name,
2865 		   tm.real.tv_sec, tm.real.tv_usec,
2866 		   tm.user.tv_sec, tm.user.tv_usec,
2867 		   tm.sys.tv_sec, tm.sys.tv_usec);
2868 	}
2869 
2870       if (pagfp)
2871 	pclose (pagfp);
2872     }
2873 
2874   param_free (&param);
2875 
2876   last_cmd = cmd;
2877   if (arglist->head != last_args.head)
2878     {
2879       gdbmarglist_free (&last_args);
2880       last_args = *arglist;
2881     }
2882 
2883   if (rc == GDBMSHELL_GDBM_ERR && variable_has_errno ("errorexit", gdbm_errno))
2884     rc = 1;
2885   else
2886     rc = 0;
2887 
2888   return rc;
2889 }
2890 
2891 int
gdbmshell_run(int (* init)(void *,instream_t *),void * data)2892 gdbmshell_run (int (*init) (void *, instream_t *), void *data)
2893 {
2894   int rc;
2895   int i;
2896   instream_t instream;
2897 
2898   if (!commands_sorted)
2899     {
2900       /* Initialize .len fields */
2901       for (i = 0; command_tab[i].name; i++)
2902 	command_tab[i].len = strlen (command_tab[i].name);
2903       /* Sort the entries by name. */
2904       qsort (command_tab, i, sizeof (command_tab[0]), cmdcmp);
2905       commands_sorted = 1;
2906     }
2907 
2908   /* Initialize variables. */
2909   dsdef[DS_KEY] = dsegm_new_field (datadef_lookup ("string"), NULL, 1);
2910   dsdef[DS_CONTENT] = dsegm_new_field (datadef_lookup ("string"), NULL, 1);
2911 
2912   variables_init ();
2913   variable_set ("open", VART_STRING, "wrcreat");
2914   variable_set ("pager", VART_STRING, getenv ("PAGER"));
2915 
2916   last_cmd = NULL;
2917   gdbmarglist_init (&last_args, NULL);
2918 
2919   lex_trace (0);
2920 
2921   rc = init (data, &instream);
2922   if (rc == 0)
2923     {
2924       rc = input_context_push (instream);
2925       if (rc == 0)
2926 	{
2927 	  struct sigaction act, old_act;
2928 
2929 	  act.sa_flags = 0;
2930 	  sigemptyset(&act.sa_mask);
2931 	  act.sa_handler = SIG_IGN;
2932 	  sigaction (SIGPIPE, &act, &old_act);
2933 	  /* Welcome message. */
2934 	  if (instream_interactive (instream) && !variable_is_true ("quiet"))
2935 	    printf (_("\nWelcome to the gdbm tool.  Type ? for help.\n\n"));
2936 	  rc = yyparse ();
2937 	  input_context_drain ();
2938 	  yylex_destroy ();
2939 	  closedb ();
2940 	  sigaction (SIGPIPE, &old_act, NULL);
2941 	}
2942       else
2943 	instream_close (instream);
2944     }
2945 
2946   gdbmarglist_free (&last_args);
2947 
2948   for (i = 0; i < DS_MAX; i++)
2949     {
2950       dsegm_list_free (dsdef[i]);
2951       dsdef[i] = NULL;
2952     }
2953 
2954   variables_free ();
2955 
2956   return rc;
2957 }
2958 
2959 static int
init(void * data,instream_t * pinstr)2960 init (void *data, instream_t *pinstr)
2961 {
2962   *pinstr = data;
2963   return 0;
2964 }
2965 
2966 int
gdbmshell(instream_t input)2967 gdbmshell (instream_t input)
2968 {
2969   return gdbmshell_run (init, input);
2970 }
2971