Coverage Report

Created: 2025-08-29 06:34

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