1/* testgdbm.c - Driver program to test the database routines and to
2   help debug gdbm.  Uses inside information to show "system" information */
3
4/*  This file is part of GDBM, the GNU data base manager, by Philip A. Nelson.
5    Copyright (C) 1990, 1991, 1993  Free Software Foundation, Inc.
6
7    GDBM is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2, or (at your option)
10    any later version.
11
12    GDBM is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with GDBM; see the file COPYING.  If not, write to
19    the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
20
21    You may contact the author by:
22       e-mail:  phil@cs.wwu.edu
23      us-mail:  Philip A. Nelson
24                Computer Science Department
25                Western Washington University
26                Bellingham, WA 98226
27
28*************************************************************************/
29
30
31/* include system configuration before all else. */
32#include "autoconf.h"
33
34#include "gdbmdefs.h"
35#include "gdbmerrno.h"
36#include "extern.h"
37
38#include "getopt.h"
39
40extern const char * gdbm_version;
41
42extern const char *gdbm_strerror __P((gdbm_error));
43
44gdbm_file_info *gdbm_file;
45
46/* Debug procedure to print the contents of the current hash bucket. */
47void
48print_bucket (bucket, mesg)
49     hash_bucket *bucket;
50     char *mesg;
51{
52  int  index;
53
54  printf ("******* %s **********\n\nbits = %d\ncount= %d\nHash Table:\n",
55	 mesg, bucket->bucket_bits, bucket->count);
56  printf ("     #    hash value     key size    data size     data adr  home\n");
57  for (index = 0; index < gdbm_file->header->bucket_elems; index++)
58    printf ("  %4d  %12x  %11d  %11d  %11d %5d\n", index,
59	   bucket->h_table[index].hash_value,
60	   bucket->h_table[index].key_size,
61	   bucket->h_table[index].data_size,
62	   bucket->h_table[index].data_pointer,
63	   bucket->h_table[index].hash_value % gdbm_file->header->bucket_elems);
64
65  printf ("\nAvail count = %1d\n", bucket->av_count);
66  printf ("Avail  adr     size\n");
67  for (index = 0; index < bucket->av_count; index++)
68    printf ("%9d%9d\n", bucket->bucket_avail[index].av_adr,
69	                bucket->bucket_avail[index].av_size);
70}
71
72
73void
74_gdbm_print_avail_list (dbf)
75     gdbm_file_info *dbf;
76{
77  int temp;
78  int size;
79  avail_block *av_stk;
80
81  /* Print the the header avail block.  */
82  printf ("\nheader block\nsize  = %d\ncount = %d\n",
83	  dbf->header->avail.size, dbf->header->avail.count);
84  for (temp = 0; temp < dbf->header->avail.count; temp++)
85    {
86      printf ("  %15d   %10d \n", dbf->header->avail.av_table[temp].av_size,
87	      dbf->header->avail.av_table[temp].av_adr);
88    }
89
90  /* Initialize the variables for a pass throught the avail stack. */
91  temp = dbf->header->avail.next_block;
92  size = ( ( (dbf->header->avail.size * sizeof (avail_elem)) >> 1)
93	  + sizeof (avail_block));
94  av_stk = (avail_block *) malloc (size);
95  if (av_stk == NULL)
96    {
97      printf("Out of memory\n");
98      exit (2);
99    }
100
101  /* Print the stack. */
102  while (FALSE)
103    {
104      lseek (dbf->desc, temp, L_SET);
105      read  (dbf->desc, av_stk, size);
106
107      /* Print the block! */
108      printf ("\nblock = %d\nsize  = %d\ncount = %d\n", temp,
109	      av_stk->size, av_stk->count);
110      for (temp = 0; temp < av_stk->count; temp++)
111	{
112	  printf ("  %15d   %10d \n", av_stk->av_table[temp].av_size,
113	    av_stk->av_table[temp].av_adr);
114	}
115      temp = av_stk->next_block;
116    }
117}
118
119void
120_gdbm_print_bucket_cache (dbf)
121     gdbm_file_info *dbf;
122{
123  register int index;
124  char changed;
125
126  if (dbf->bucket_cache != NULL) {
127    printf(
128      "Bucket Cache (size %d):\n  Index:  Address  Changed  Data_Hash \n",
129      dbf->cache_size);
130    for (index=0; index < dbf->cache_size; index++) {
131      changed = dbf->bucket_cache[index].ca_changed;
132      printf ("  %5d:  %7d  %7s  %x\n",
133	      index,
134	      dbf->bucket_cache[index].ca_adr,
135	      (changed ? "True" : "False"),
136	      dbf->bucket_cache[index].ca_data.hash_val);
137    }
138  } else
139    printf("Bucket cache has not been initialized.\n");
140}
141
142void
143usage (s)
144     char *s;
145{
146  printf(
147      "Usage: %s [-r or -ns] [-b block-size] [-c cache-size] [-g gdbm-file]\n",
148      s);
149  exit (2);
150}
151
152
153/* The test program allows one to call all the routines plus the hash function.
154   The commands are single letter commands.  The user is prompted for all other
155   information.  See the help command (?) for a list of all commands. */
156
157int
158main (argc, argv)
159     int argc;
160     char *argv[];
161
162{
163
164  char  cmd_ch;
165
166  datum key_data;
167  datum data_data;
168  datum return_data;
169
170  char key_line[500];
171  char data_line[1000];
172
173  char done = FALSE;
174  int  opt;
175  char reader = FALSE;
176  char newdb = FALSE;
177  int  fast  = 0;
178
179  int  cache_size = DEFAULT_CACHESIZE;
180  int  block_size = 0;
181
182  char *file_name = NULL;
183
184
185  /* Argument checking. */
186  opterr = 0;
187  while ((opt = getopt (argc, argv, "srnc:b:g:")) != -1)
188    switch (opt) {
189    case 's':  fast = GDBM_SYNC;
190               if (reader) usage (argv[0]);
191               break;
192    case 'r':  reader = TRUE;
193               if (newdb) usage (argv[0]);
194               break;
195    case 'n':  newdb = TRUE;
196               if (reader) usage (argv[0]);
197               break;
198    case 'c':  cache_size = atoi(optarg);
199    	       break;
200    case 'b':  block_size = atoi(optarg);
201               break;
202    case 'g':  file_name = optarg;
203               break;
204    default:  usage(argv[0]);
205    }
206
207  if(file_name == NULL)
208    file_name = "junk.gdbm";
209
210  /* Initialize variables. */
211  key_data.dptr = NULL;
212  data_data.dptr = data_line;
213
214  if (reader)
215    {
216      gdbm_file = gdbm_open (file_name, block_size, GDBM_READER, 00664, NULL);
217    }
218  else if (newdb)
219    {
220      gdbm_file =
221    	gdbm_open (file_name, block_size, GDBM_NEWDB | fast, 00664, NULL);
222    }
223  else
224    {
225      gdbm_file =
226    	gdbm_open (file_name, block_size, GDBM_WRCREAT | fast, 00664, NULL);
227    }
228  if (gdbm_file == NULL)
229    {
230      printf("gdbm_open failed, %s\n", gdbm_strerror(gdbm_errno));
231      exit (2);
232    }
233
234  if (gdbm_setopt(gdbm_file, GDBM_CACHESIZE, &cache_size, sizeof(int)) == -1)
235    {
236      printf("gdbm_setopt failed, %s\n", gdbm_strerror(gdbm_errno));
237      exit(2);
238    }
239
240  /* Welcome message. */
241  printf ("\nWelcome to the gdbm test program.  Type ? for help.\n\n");
242
243  while (!done)
244    {
245      printf ("com -> ");
246      cmd_ch = getchar ();
247      if (cmd_ch != '\n')
248	{
249	  char temp;
250	  do
251	      temp = getchar ();
252	  while (temp != '\n' && temp != EOF);
253	}
254      if (cmd_ch == EOF) cmd_ch = 'q';
255      switch (cmd_ch)
256	{
257
258	/* Standard cases found in all test{dbm,ndbm,gdbm} programs. */
259	case '\n':
260	  printf ("\n");
261	  break;
262
263	case 'c':
264	  {
265	    int temp;
266	    temp = 0;
267	    if (key_data.dptr != NULL) free (key_data.dptr);
268	    return_data = gdbm_firstkey (gdbm_file);
269	    while (return_data.dptr != NULL)
270	      {
271		temp++;
272		key_data = return_data;
273		return_data = gdbm_nextkey (gdbm_file, key_data);
274		free (key_data.dptr);
275	      }
276	    printf ("There are %d items in the database.\n\n", temp);
277	    key_data.dptr = NULL;
278	  }
279	  break;
280
281	case 'd':
282	  if (key_data.dptr != NULL) free (key_data.dptr);
283	  printf ("key -> ");
284	  gets (key_line);
285	  key_data.dptr = key_line;
286	  key_data.dsize = strlen (key_line)+1;
287	  if (gdbm_delete (gdbm_file, key_data) != 0)
288	    printf ("Item not found or deleted\n");
289	  printf ("\n");
290	  key_data.dptr = NULL;
291	  break;
292
293	case 'f':
294	  if (key_data.dptr != NULL) free (key_data.dptr);
295	  printf ("key -> ");
296	  gets (key_line);
297	  key_data.dptr = key_line;
298	  key_data.dsize = strlen (key_line)+1;
299	  return_data = gdbm_fetch (gdbm_file, key_data);
300	  if (return_data.dptr != NULL)
301	    {
302	      printf ("data is ->%s\n\n", return_data.dptr);
303	      free (return_data.dptr);
304	    }
305	  else
306	    printf ("No such item found.\n\n");
307	  key_data.dptr = NULL;
308	  break;
309
310	case 'n':
311	  if (key_data.dptr != NULL) free (key_data.dptr);
312	  printf ("key -> ");
313	  gets (key_line);
314	  key_data.dptr = key_line;
315	  key_data.dsize = strlen (key_line)+1;
316	  return_data = gdbm_nextkey (gdbm_file, key_data);
317	  if (return_data.dptr != NULL)
318	    {
319	      key_data = return_data;
320	      printf ("key is  ->%s\n", key_data.dptr);
321	      return_data = gdbm_fetch (gdbm_file, key_data);
322	      printf ("data is ->%s\n\n", return_data.dptr);
323	      free (return_data.dptr);
324	    }
325	  else
326	    {
327	      printf ("No such item found.\n\n");
328	      key_data.dptr = NULL;
329	    }
330	  break;
331
332	case 'q':
333	  done = TRUE;
334	  break;
335
336	case 's':
337	  if (key_data.dptr != NULL) free (key_data.dptr);
338	  printf ("key -> ");
339	  gets (key_line);
340	  key_data.dptr = key_line;
341	  key_data.dsize = strlen (key_line)+1;
342	  printf ("data -> ");
343	  gets (data_line);
344	  data_data.dsize = strlen (data_line)+1;
345	  if (gdbm_store (gdbm_file, key_data, data_data, GDBM_REPLACE) != 0)
346	    printf ("Item not inserted. \n");
347	  printf ("\n");
348	  key_data.dptr = NULL;
349	  break;
350
351	case '1':
352	  if (key_data.dptr != NULL) free (key_data.dptr);
353	  key_data = gdbm_firstkey (gdbm_file);
354	  if (key_data.dptr != NULL)
355	    {
356	      printf ("key is  ->%s\n", key_data.dptr);
357	      return_data = gdbm_fetch (gdbm_file, key_data);
358	      printf ("data is ->%s\n\n", return_data.dptr);
359	      free (return_data.dptr);
360	    }
361	  else
362	    printf ("No such item found.\n\n");
363	  break;
364
365	case '2':
366	  return_data = gdbm_nextkey (gdbm_file, key_data);
367	  if (return_data.dptr != NULL)
368	    {
369	      free (key_data.dptr);
370	      key_data = return_data;
371	      printf ("key is  ->%s\n", key_data.dptr);
372	      return_data = gdbm_fetch (gdbm_file, key_data);
373	      printf ("data is ->%s\n\n", return_data.dptr);
374	      free (return_data.dptr);
375	    }
376	  else
377	    printf ("No such item found.\n\n");
378	  break;
379
380
381	/* Special cases for the testgdbm program. */
382	case 'r':
383	  {
384	    if (gdbm_reorganize (gdbm_file))
385	      printf ("Reorganization failed. \n\n");
386	    else
387	      printf ("Reorganization succeeded. \n\n");
388	  }
389	  break;
390
391	case 'A':
392	  _gdbm_print_avail_list (gdbm_file);
393	  printf ("\n");
394	  break;
395
396	case 'B':
397	  {
398	    int temp;
399	    char number[80];
400
401	    printf ("bucket? ");
402	    gets (number);
403	    sscanf (number,"%d",&temp);
404
405	    if (temp >= gdbm_file->header->dir_size /4)
406	      {
407		printf ("Not a bucket. \n\n");
408		break;
409	      }
410	    _gdbm_get_bucket (gdbm_file, temp);
411	  }
412	  printf ("Your bucket is now ");
413
414	case 'C':
415	  print_bucket (gdbm_file->bucket, "Current bucket");
416	  printf ("\n current directory entry = %d.\n", gdbm_file->bucket_dir);
417	  printf (" current bucket address  = %d.\n\n",
418		  gdbm_file->cache_entry->ca_adr);
419	  break;
420
421	case 'D':
422	  printf ("Hash table directory.\n");
423	  printf ("  Size =  %d.  Bits = %d. \n\n",gdbm_file->header->dir_size,
424		  gdbm_file->header->dir_bits);
425	  {
426	    int temp;
427
428	    for (temp = 0; temp < gdbm_file->header->dir_size / 4; temp++)
429	      {
430		printf ("  %10d:  %12d\n", temp, gdbm_file->dir[temp]);
431		if ( (temp+1) % 20 == 0 && isatty (0))
432		  {
433		    printf ("*** CR to continue: ");
434		    while (getchar () != '\n') /* Do nothing. */;
435		  }
436	      }
437	  }
438	  printf ("\n");
439	  break;
440
441	case 'F':
442	  {
443	    printf ("\nFile Header: \n\n");
444	    printf ("  table        = %d\n", gdbm_file->header->dir);
445	    printf ("  table size   = %d\n", gdbm_file->header->dir_size);
446	    printf ("  table bits   = %d\n", gdbm_file->header->dir_bits);
447	    printf ("  block size   = %d\n", gdbm_file->header->block_size);
448	    printf ("  bucket elems = %d\n", gdbm_file->header->bucket_elems);
449	    printf ("  bucket size  = %d\n", gdbm_file->header->bucket_size);
450	    printf ("  header magic = %x\n", gdbm_file->header->header_magic);
451	    printf ("  next block   = %d\n", gdbm_file->header->next_block);
452	    printf ("  avail size   = %d\n", gdbm_file->header->avail.size);
453	    printf ("  avail count  = %d\n", gdbm_file->header->avail.count);
454	    printf ("  avail nx blk = %d\n", gdbm_file->header->avail.next_block);
455	    printf ("\n");
456	  }
457	  break;
458
459        case 'H':
460	  if (key_data.dptr != NULL) free (key_data.dptr);
461	  printf ("key -> ");
462	  gets (key_line);
463	  key_data.dptr = key_line;
464	  key_data.dsize = strlen (key_line)+1;
465	  printf ("hash value = %x. \n\n", _gdbm_hash (key_data));
466	  key_data.dptr = NULL;
467	  break;
468
469	case 'K':
470	  _gdbm_print_bucket_cache (gdbm_file);
471	  break;
472
473	case 'V':
474	  printf ("%s\n\n", gdbm_version);
475	  break;
476
477	case '?':
478	  printf ("c - count (number of entries)\n");
479	  printf ("d - delete\n");
480	  printf ("f - fetch\n");
481	  printf ("n - nextkey\n");
482	  printf ("q - quit\n");
483	  printf ("s - store\n");
484	  printf ("1 - firstkey\n");
485	  printf ("2 - nextkey on last key (from n, 1 or 2)\n\n");
486
487	  printf ("r - reorganize\n");
488	  printf ("A - print avail list\n");
489	  printf ("B - get and print current bucket n\n");
490	  printf ("C - print current bucket\n");
491	  printf ("D - print hash directory\n");
492	  printf ("F - print file header\n");
493	  printf ("H - hash value of key\n");
494	  printf ("K - print the bucket cache\n");
495	  printf ("V - print version of gdbm\n");
496	  break;
497
498	default:
499	  printf ("What? \n\n");
500	  break;
501
502	}
503    }
504
505  /* Quit normally. */
506  exit (0);
507
508}
509