1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 1996,2008 Oracle.  All rights reserved.
5 *
6 * $Id: db_pr.c,v 12.46 2008/01/08 20:58:10 bostic Exp $
7 */
8
9#include "db_config.h"
10
11#include "db_int.h"
12#include "dbinc/db_page.h"
13#include "dbinc/btree.h"
14#include "dbinc/hash.h"
15#include "dbinc/mp.h"
16#include "dbinc/qam.h"
17#include "dbinc/db_verify.h"
18
19/*
20 * __db_loadme --
21 *	A nice place to put a breakpoint.
22 *
23 * PUBLIC: void __db_loadme __P((void));
24 */
25void
26__db_loadme()
27{
28	pid_t pid;
29
30	__os_id(NULL, &pid, NULL);
31}
32
33#ifdef HAVE_STATISTICS
34static int	 __db_bmeta __P((DB *, BTMETA *, u_int32_t));
35static int	 __db_hmeta __P((DB *, HMETA *, u_int32_t));
36static void	 __db_meta __P((DB *, DBMETA *, FN const *, u_int32_t));
37static const char *__db_pagetype_to_string __P((u_int32_t));
38static void	 __db_prdb __P((DB *, u_int32_t));
39static void	 __db_proff __P((ENV *, DB_MSGBUF *, void *));
40static int	 __db_prtree __P((DB *, DB_TXN *, u_int32_t));
41static int	 __db_qmeta __P((DB *, QMETA *, u_int32_t));
42
43/*
44 * __db_dumptree --
45 *	Dump the tree to a file.
46 *
47 * PUBLIC: int __db_dumptree __P((DB *, DB_TXN *, char *, char *));
48 */
49int
50__db_dumptree(dbp, txn, op, name)
51	DB *dbp;
52	DB_TXN *txn;
53	char *op, *name;
54{
55	ENV *env;
56	FILE *fp, *orig_fp;
57	u_int32_t flags;
58	int ret;
59
60	env = dbp->env;
61
62	for (flags = 0; *op != '\0'; ++op)
63		switch (*op) {
64		case 'a':
65			LF_SET(DB_PR_PAGE);
66			break;
67		case 'h':
68			break;
69		case 'r':
70			LF_SET(DB_PR_RECOVERYTEST);
71			break;
72		default:
73			return (EINVAL);
74		}
75
76	if (name != NULL) {
77		if ((fp = fopen(name, "w")) == NULL)
78			return (__os_get_errno());
79
80		orig_fp = dbp->dbenv->db_msgfile;
81		dbp->dbenv->db_msgfile = fp;
82	} else
83		fp = orig_fp = NULL;
84
85	__db_prdb(dbp, flags);
86
87	__db_msg(env, "%s", DB_GLOBAL(db_line));
88
89	ret = __db_prtree(dbp, txn, flags);
90
91	if (fp != NULL) {
92		(void)fclose(fp);
93		env->dbenv->db_msgfile = orig_fp;
94	}
95
96	return (ret);
97}
98
99static const FN __db_flags_fn[] = {
100	{ DB_AM_CHKSUM,			"checksumming" },
101	{ DB_AM_COMPENSATE,		"created by compensating transaction" },
102	{ DB_AM_CREATED,		"database created" },
103	{ DB_AM_CREATED_MSTR,		"encompassing file created" },
104	{ DB_AM_DBM_ERROR,		"dbm/ndbm error" },
105	{ DB_AM_DELIMITER,		"variable length" },
106	{ DB_AM_DISCARD,		"discard cached pages" },
107	{ DB_AM_DUP,			"duplicates" },
108	{ DB_AM_DUPSORT,		"sorted duplicates" },
109	{ DB_AM_ENCRYPT,		"encrypted" },
110	{ DB_AM_FIXEDLEN,		"fixed-length records" },
111	{ DB_AM_INMEM,			"in-memory" },
112	{ DB_AM_IN_RENAME,		"file is being renamed" },
113	{ DB_AM_NOT_DURABLE,		"changes not logged" },
114	{ DB_AM_OPEN_CALLED,		"open called" },
115	{ DB_AM_PAD,			"pad value" },
116	{ DB_AM_PGDEF,			"default page size" },
117	{ DB_AM_RDONLY,			"read-only" },
118	{ DB_AM_READ_UNCOMMITTED,	"read-uncommitted" },
119	{ DB_AM_RECNUM,			"Btree record numbers" },
120	{ DB_AM_RECOVER,		"opened for recovery" },
121	{ DB_AM_RENUMBER,		"renumber" },
122	{ DB_AM_REVSPLITOFF,		"no reverse splits" },
123	{ DB_AM_SECONDARY,		"secondary" },
124	{ DB_AM_SNAPSHOT,		"load on open" },
125	{ DB_AM_SUBDB,			"subdatabases" },
126	{ DB_AM_SWAP,			"needswap" },
127	{ DB_AM_TXN,			"transactional" },
128	{ DB_AM_VERIFYING,		"verifier" },
129	{ 0,				NULL }
130};
131
132/*
133 * __db_get_flags_fn --
134 *	Return the __db_flags_fn array.
135 *
136 * PUBLIC: const FN * __db_get_flags_fn __P((void));
137 */
138const FN *
139__db_get_flags_fn()
140{
141	return (__db_flags_fn);
142}
143
144/*
145 * __db_prdb --
146 *	Print out the DB structure information.
147 */
148static void
149__db_prdb(dbp, flags)
150	DB *dbp;
151	u_int32_t flags;
152{
153	BTREE *bt;
154	DB_MSGBUF mb;
155	ENV *env;
156	HASH *h;
157	QUEUE *q;
158
159	env = dbp->env;
160
161	DB_MSGBUF_INIT(&mb);
162	__db_msg(env, "In-memory DB structure:");
163	__db_msgadd(env, &mb, "%s: %#lx",
164	    __db_dbtype_to_string(dbp->type), (u_long)dbp->flags);
165	__db_prflags(env, &mb, dbp->flags, __db_flags_fn, " (", ")");
166	DB_MSGBUF_FLUSH(env, &mb);
167
168	switch (dbp->type) {
169	case DB_BTREE:
170	case DB_RECNO:
171		bt = dbp->bt_internal;
172		__db_msg(env, "bt_meta: %lu bt_root: %lu",
173		    (u_long)bt->bt_meta, (u_long)bt->bt_root);
174		__db_msg(env, "bt_minkey: %lu", (u_long)bt->bt_minkey);
175		if (!LF_ISSET(DB_PR_RECOVERYTEST))
176			__db_msg(env, "bt_compare: %#lx bt_prefix: %#lx",
177			    P_TO_ULONG(bt->bt_compare),
178			    P_TO_ULONG(bt->bt_prefix));
179		__db_msg(env, "bt_lpgno: %lu", (u_long)bt->bt_lpgno);
180		if (dbp->type == DB_RECNO) {
181			__db_msg(env,
182		    "re_pad: %#lx re_delim: %#lx re_len: %lu re_source: %s",
183			    (u_long)bt->re_pad, (u_long)bt->re_delim,
184			    (u_long)bt->re_len,
185			    bt->re_source == NULL ? "" : bt->re_source);
186			__db_msg(env,
187			    "re_modified: %d re_eof: %d re_last: %lu",
188			    bt->re_modified, bt->re_eof, (u_long)bt->re_last);
189		}
190		break;
191	case DB_HASH:
192		h = dbp->h_internal;
193		__db_msg(env, "meta_pgno: %lu", (u_long)h->meta_pgno);
194		__db_msg(env, "h_ffactor: %lu", (u_long)h->h_ffactor);
195		__db_msg(env, "h_nelem: %lu", (u_long)h->h_nelem);
196		if (!LF_ISSET(DB_PR_RECOVERYTEST))
197			__db_msg(env, "h_hash: %#lx", P_TO_ULONG(h->h_hash));
198		break;
199	case DB_QUEUE:
200		q = dbp->q_internal;
201		__db_msg(env, "q_meta: %lu", (u_long)q->q_meta);
202		__db_msg(env, "q_root: %lu", (u_long)q->q_root);
203		__db_msg(env, "re_pad: %#lx re_len: %lu",
204		    (u_long)q->re_pad, (u_long)q->re_len);
205		__db_msg(env, "rec_page: %lu", (u_long)q->rec_page);
206		__db_msg(env, "page_ext: %lu", (u_long)q->page_ext);
207		break;
208	case DB_UNKNOWN:
209	default:
210		break;
211	}
212}
213
214/*
215 * __db_prtree --
216 *	Print out the entire tree.
217 */
218static int
219__db_prtree(dbp, txn, flags)
220	DB *dbp;
221	DB_TXN *txn;
222	u_int32_t flags;
223{
224	DB_MPOOLFILE *mpf;
225	PAGE *h;
226	db_pgno_t i, last;
227	int ret;
228
229	mpf = dbp->mpf;
230
231	if (dbp->type == DB_QUEUE)
232		return (__db_prqueue(dbp, flags));
233
234	/*
235	 * Find out the page number of the last page in the database, then
236	 * dump each page.
237	 */
238	if ((ret = __memp_get_last_pgno(mpf, &last)) != 0)
239		return (ret);
240	for (i = 0; i <= last; ++i) {
241		if ((ret = __memp_fget(mpf, &i, NULL, txn, 0, &h)) != 0)
242			return (ret);
243		(void)__db_prpage(dbp, h, flags);
244		if ((ret = __memp_fput(mpf, NULL, h, dbp->priority)) != 0)
245			return (ret);
246	}
247
248	return (0);
249}
250
251/*
252 * __db_meta --
253 *	Print out common metadata information.
254 */
255static void
256__db_meta(dbp, dbmeta, fn, flags)
257	DB *dbp;
258	DBMETA *dbmeta;
259	FN const *fn;
260	u_int32_t flags;
261{
262	DB_MPOOLFILE *mpf;
263	DB_MSGBUF mb;
264	ENV *env;
265	PAGE *h;
266	db_pgno_t pgno;
267	u_int8_t *p;
268	int cnt, ret;
269	const char *sep;
270
271	env = dbp->env;
272	mpf = dbp->mpf;
273	DB_MSGBUF_INIT(&mb);
274
275	__db_msg(env, "\tmagic: %#lx", (u_long)dbmeta->magic);
276	__db_msg(env, "\tversion: %lu", (u_long)dbmeta->version);
277	__db_msg(env, "\tpagesize: %lu", (u_long)dbmeta->pagesize);
278	__db_msg(env, "\ttype: %lu", (u_long)dbmeta->type);
279	__db_msg(env, "\tkeys: %lu\trecords: %lu",
280	    (u_long)dbmeta->key_count, (u_long)dbmeta->record_count);
281
282	/*
283	 * If we're doing recovery testing, don't display the free list,
284	 * it may have changed and that makes the dump diff not work.
285	 */
286	if (!LF_ISSET(DB_PR_RECOVERYTEST)) {
287		__db_msgadd(
288		    env, &mb, "\tfree list: %lu", (u_long)dbmeta->free);
289		for (pgno = dbmeta->free,
290		    cnt = 0, sep = ", "; pgno != PGNO_INVALID;) {
291			if ((ret = __memp_fget(mpf,
292			     &pgno, NULL, NULL, 0, &h)) != 0) {
293				DB_MSGBUF_FLUSH(env, &mb);
294				__db_msg(env,
295			    "Unable to retrieve free-list page: %lu: %s",
296				    (u_long)pgno, db_strerror(ret));
297				break;
298			}
299			pgno = h->next_pgno;
300			(void)__memp_fput(mpf, NULL, h, dbp->priority);
301			__db_msgadd(env, &mb, "%s%lu", sep, (u_long)pgno);
302			if (++cnt % 10 == 0) {
303				DB_MSGBUF_FLUSH(env, &mb);
304				cnt = 0;
305				sep = "\t";
306			} else
307				sep = ", ";
308		}
309		DB_MSGBUF_FLUSH(env, &mb);
310		__db_msg(env, "\tlast_pgno: %lu", (u_long)dbmeta->last_pgno);
311	}
312
313	if (fn != NULL) {
314		DB_MSGBUF_FLUSH(env, &mb);
315		__db_msgadd(env, &mb, "\tflags: %#lx", (u_long)dbmeta->flags);
316		__db_prflags(env, &mb, dbmeta->flags, fn, " (", ")");
317	}
318
319	DB_MSGBUF_FLUSH(env, &mb);
320	__db_msgadd(env, &mb, "\tuid: ");
321	for (p = (u_int8_t *)dbmeta->uid,
322	    cnt = 0; cnt < DB_FILE_ID_LEN; ++cnt) {
323		__db_msgadd(env, &mb, "%x", *p++);
324		if (cnt < DB_FILE_ID_LEN - 1)
325			__db_msgadd(env, &mb, " ");
326	}
327	DB_MSGBUF_FLUSH(env, &mb);
328}
329
330/*
331 * __db_bmeta --
332 *	Print out the btree meta-data page.
333 */
334static int
335__db_bmeta(dbp, h, flags)
336	DB *dbp;
337	BTMETA *h;
338	u_int32_t flags;
339{
340	static const FN fn[] = {
341		{ BTM_DUP,	"duplicates" },
342		{ BTM_RECNO,	"recno" },
343		{ BTM_RECNUM,	"btree:recnum" },
344		{ BTM_FIXEDLEN,	"recno:fixed-length" },
345		{ BTM_RENUMBER,	"recno:renumber" },
346		{ BTM_SUBDB,	"multiple-databases" },
347		{ BTM_DUPSORT,	"sorted duplicates" },
348		{ 0,		NULL }
349	};
350	ENV *env;
351
352	env = dbp->env;
353
354	__db_meta(dbp, (DBMETA *)h, fn, flags);
355
356	__db_msg(env, "\tminkey: %lu", (u_long)h->minkey);
357	if (dbp->type == DB_RECNO)
358		__db_msg(env, "\tre_len: %#lx re_pad: %#lx",
359		    (u_long)h->re_len, (u_long)h->re_pad);
360	__db_msg(env, "\troot: %lu", (u_long)h->root);
361
362	return (0);
363}
364
365/*
366 * __db_hmeta --
367 *	Print out the hash meta-data page.
368 */
369static int
370__db_hmeta(dbp, h, flags)
371	DB *dbp;
372	HMETA *h;
373	u_int32_t flags;
374{
375	static const FN fn[] = {
376		{ DB_HASH_DUP,		"duplicates" },
377		{ DB_HASH_SUBDB,	"multiple-databases" },
378		{ DB_HASH_DUPSORT,	"sorted duplicates" },
379		{ 0,			NULL }
380	};
381	ENV *env;
382	DB_MSGBUF mb;
383	int i;
384
385	env = dbp->env;
386	DB_MSGBUF_INIT(&mb);
387
388	__db_meta(dbp, (DBMETA *)h, fn, flags);
389
390	__db_msg(env, "\tmax_bucket: %lu", (u_long)h->max_bucket);
391	__db_msg(env, "\thigh_mask: %#lx", (u_long)h->high_mask);
392	__db_msg(env, "\tlow_mask:  %#lx", (u_long)h->low_mask);
393	__db_msg(env, "\tffactor: %lu", (u_long)h->ffactor);
394	__db_msg(env, "\tnelem: %lu", (u_long)h->nelem);
395	__db_msg(env, "\th_charkey: %#lx", (u_long)h->h_charkey);
396	__db_msgadd(env, &mb, "\tspare points: ");
397	for (i = 0; i < NCACHED; i++)
398		__db_msgadd(env, &mb, "%lu ", (u_long)h->spares[i]);
399	DB_MSGBUF_FLUSH(env, &mb);
400
401	return (0);
402}
403
404/*
405 * __db_qmeta --
406 *	Print out the queue meta-data page.
407 */
408static int
409__db_qmeta(dbp, h, flags)
410	DB *dbp;
411	QMETA *h;
412	u_int32_t flags;
413{
414	ENV *env;
415
416	env = dbp->env;
417
418	__db_meta(dbp, (DBMETA *)h, NULL, flags);
419
420	__db_msg(env, "\tfirst_recno: %lu", (u_long)h->first_recno);
421	__db_msg(env, "\tcur_recno: %lu", (u_long)h->cur_recno);
422	__db_msg(env, "\tre_len: %#lx re_pad: %lu",
423	    (u_long)h->re_len, (u_long)h->re_pad);
424	__db_msg(env, "\trec_page: %lu", (u_long)h->rec_page);
425	__db_msg(env, "\tpage_ext: %lu", (u_long)h->page_ext);
426
427	return (0);
428}
429
430/*
431 * __db_prnpage
432 *	-- Print out a specific page.
433 *
434 * PUBLIC: int __db_prnpage __P((DB *, DB_TXN *, db_pgno_t));
435 */
436int
437__db_prnpage(dbp, txn, pgno)
438	DB *dbp;
439	DB_TXN *txn;
440	db_pgno_t pgno;
441{
442	DB_MPOOLFILE *mpf;
443	PAGE *h;
444	int ret, t_ret;
445
446	mpf = dbp->mpf;
447
448	if ((ret = __memp_fget(mpf, &pgno, NULL, txn, 0, &h)) != 0)
449		return (ret);
450
451	ret = __db_prpage(dbp, h, DB_PR_PAGE);
452
453	if ((t_ret = __memp_fput(mpf, NULL, h, dbp->priority)) != 0 && ret == 0)
454		ret = t_ret;
455
456	return (ret);
457}
458
459/*
460 * __db_prpage
461 *	-- Print out a page.
462 *
463 * PUBLIC: int __db_prpage __P((DB *, PAGE *, u_int32_t));
464 */
465int
466__db_prpage(dbp, h, flags)
467	DB *dbp;
468	PAGE *h;
469	u_int32_t flags;
470{
471	BINTERNAL *bi;
472	BKEYDATA *bk;
473	DB_MSGBUF mb;
474	ENV *env;
475	HOFFPAGE a_hkd;
476	QAMDATA *qp, *qep;
477	RINTERNAL *ri;
478	db_indx_t dlen, len, i, *inp;
479	db_pgno_t pgno;
480	db_recno_t recno;
481	u_int32_t pagesize, qlen;
482	u_int8_t *ep, *hk, *p;
483	int deleted, ret;
484	const char *s;
485	void *sp;
486
487	env = dbp->env;
488	DB_MSGBUF_INIT(&mb);
489
490	/*
491	 * If we're doing recovery testing and this page is P_INVALID,
492	 * assume it's a page that's on the free list, and don't display it.
493	 */
494	if (LF_ISSET(DB_PR_RECOVERYTEST) && TYPE(h) == P_INVALID)
495		return (0);
496
497	if ((s = __db_pagetype_to_string(TYPE(h))) == NULL) {
498		__db_msg(env, "ILLEGAL PAGE TYPE: page: %lu type: %lu",
499		    (u_long)h->pgno, (u_long)TYPE(h));
500		return (EINVAL);
501	}
502
503	/*
504	 * !!!
505	 * Find out the page size.  We don't want to do it the "right" way,
506	 * by reading the value from the meta-data page, that's going to be
507	 * slow.  Reach down into the mpool region.
508	 */
509	pagesize = (u_int32_t)dbp->mpf->mfp->stat.st_pagesize;
510
511	/* Page number, page type. */
512	__db_msgadd(env, &mb, "page %lu: %s:", (u_long)h->pgno, s);
513
514	/*
515	 * LSNs on a metadata page will be different from the original after an
516	 * abort, in some cases.  Don't display them if we're testing recovery.
517	 */
518	if (!LF_ISSET(DB_PR_RECOVERYTEST) ||
519	    (TYPE(h) != P_BTREEMETA && TYPE(h) != P_HASHMETA &&
520	    TYPE(h) != P_QAMMETA && TYPE(h) != P_QAMDATA))
521		__db_msgadd(env, &mb, " LSN [%lu][%lu]:",
522		    (u_long)LSN(h).file, (u_long)LSN(h).offset);
523
524	/*
525	 * Page level (only applicable for Btree/Recno, but we always display
526	 * it, for no particular reason.
527	 */
528	__db_msgadd(env, &mb, " level %lu", (u_long)h->level);
529
530	/* Record count. */
531	if (TYPE(h) == P_IBTREE ||
532	    TYPE(h) == P_IRECNO || (TYPE(h) == P_LRECNO &&
533	    h->pgno == ((BTREE *)dbp->bt_internal)->bt_root))
534		__db_msgadd(env, &mb, " records: %lu", (u_long)RE_NREC(h));
535	DB_MSGBUF_FLUSH(env, &mb);
536
537	switch (TYPE(h)) {
538	case P_BTREEMETA:
539		return (__db_bmeta(dbp, (BTMETA *)h, flags));
540	case P_HASHMETA:
541		return (__db_hmeta(dbp, (HMETA *)h, flags));
542	case P_QAMMETA:
543		return (__db_qmeta(dbp, (QMETA *)h, flags));
544	case P_QAMDATA:				/* Should be meta->start. */
545		if (!LF_ISSET(DB_PR_PAGE))
546			return (0);
547
548		qlen = ((QUEUE *)dbp->q_internal)->re_len;
549		recno = (h->pgno - 1) * QAM_RECNO_PER_PAGE(dbp) + 1;
550		i = 0;
551		qep = (QAMDATA *)((u_int8_t *)h + pagesize - qlen);
552		for (qp = QAM_GET_RECORD(dbp, h, i); qp < qep;
553		    recno++, i++, qp = QAM_GET_RECORD(dbp, h, i)) {
554			if (!F_ISSET(qp, QAM_SET))
555				continue;
556
557			__db_msgadd(env, &mb, "%s",
558			    F_ISSET(qp, QAM_VALID) ? "\t" : "       D");
559			__db_msgadd(env, &mb, "[%03lu] %4lu ", (u_long)recno,
560			    (u_long)((u_int8_t *)qp - (u_int8_t *)h));
561			__db_prbytes(env, &mb, qp->data, qlen);
562		}
563		return (0);
564	default:
565		break;
566	}
567
568	s = "\t";
569	if (TYPE(h) != P_IBTREE && TYPE(h) != P_IRECNO) {
570		__db_msgadd(env, &mb, "%sprev: %4lu next: %4lu",
571		    s, (u_long)PREV_PGNO(h), (u_long)NEXT_PGNO(h));
572		s = " ";
573	}
574	if (TYPE(h) == P_OVERFLOW) {
575		__db_msgadd(env, &mb,
576		    "%sref cnt: %4lu ", s, (u_long)OV_REF(h));
577		__db_prbytes(env,
578		    &mb, (u_int8_t *)h + P_OVERHEAD(dbp), OV_LEN(h));
579		return (0);
580	}
581	__db_msgadd(env, &mb, "%sentries: %4lu", s, (u_long)NUM_ENT(h));
582	__db_msgadd(env, &mb, " offset: %4lu", (u_long)HOFFSET(h));
583	DB_MSGBUF_FLUSH(env, &mb);
584
585	if (TYPE(h) == P_INVALID || !LF_ISSET(DB_PR_PAGE))
586		return (0);
587
588	ret = 0;
589	inp = P_INP(dbp, h);
590	for (i = 0; i < NUM_ENT(h); i++) {
591		if ((uintptr_t)(P_ENTRY(dbp, h, i) - (u_int8_t *)h) <
592		    (uintptr_t)(P_OVERHEAD(dbp)) ||
593		    (size_t)(P_ENTRY(dbp, h, i) - (u_int8_t *)h) >= pagesize) {
594			__db_msg(env,
595			    "ILLEGAL PAGE OFFSET: indx: %lu of %lu",
596			    (u_long)i, (u_long)inp[i]);
597			ret = EINVAL;
598			continue;
599		}
600		deleted = 0;
601		switch (TYPE(h)) {
602		case P_HASH_UNSORTED:
603		case P_HASH:
604		case P_IBTREE:
605		case P_IRECNO:
606			sp = P_ENTRY(dbp, h, i);
607			break;
608		case P_LBTREE:
609			sp = P_ENTRY(dbp, h, i);
610			deleted = i % 2 == 0 &&
611			    B_DISSET(GET_BKEYDATA(dbp, h, i + O_INDX)->type);
612			break;
613		case P_LDUP:
614		case P_LRECNO:
615			sp = P_ENTRY(dbp, h, i);
616			deleted = B_DISSET(GET_BKEYDATA(dbp, h, i)->type);
617			break;
618		default:
619			goto type_err;
620		}
621		__db_msgadd(env, &mb, "%s", deleted ? "       D" : "\t");
622		__db_msgadd(
623		    env, &mb, "[%03lu] %4lu ", (u_long)i, (u_long)inp[i]);
624		switch (TYPE(h)) {
625		case P_HASH_UNSORTED:
626		case P_HASH:
627			hk = sp;
628			switch (HPAGE_PTYPE(hk)) {
629			case H_OFFDUP:
630				memcpy(&pgno,
631				    HOFFDUP_PGNO(hk), sizeof(db_pgno_t));
632				__db_msgadd(env, &mb,
633				    "%4lu [offpage dups]", (u_long)pgno);
634				DB_MSGBUF_FLUSH(env, &mb);
635				break;
636			case H_DUPLICATE:
637				/*
638				 * If this is the first item on a page, then
639				 * we cannot figure out how long it is, so
640				 * we only print the first one in the duplicate
641				 * set.
642				 */
643				if (i != 0)
644					len = LEN_HKEYDATA(dbp, h, 0, i);
645				else
646					len = 1;
647
648				__db_msgadd(env, &mb, "Duplicates:");
649				DB_MSGBUF_FLUSH(env, &mb);
650				for (p = HKEYDATA_DATA(hk),
651				    ep = p + len; p < ep;) {
652					memcpy(&dlen, p, sizeof(db_indx_t));
653					p += sizeof(db_indx_t);
654					__db_msgadd(env, &mb, "\t\t");
655					__db_prbytes(env, &mb, p, dlen);
656					p += sizeof(db_indx_t) + dlen;
657				}
658				break;
659			case H_KEYDATA:
660				__db_prbytes(env, &mb, HKEYDATA_DATA(hk),
661				    LEN_HKEYDATA(dbp, h, i == 0 ?
662				    pagesize : 0, i));
663				break;
664			case H_OFFPAGE:
665				memcpy(&a_hkd, hk, HOFFPAGE_SIZE);
666				__db_msgadd(env, &mb,
667				    "overflow: total len: %4lu page: %4lu",
668				    (u_long)a_hkd.tlen, (u_long)a_hkd.pgno);
669				DB_MSGBUF_FLUSH(env, &mb);
670				break;
671			default:
672				DB_MSGBUF_FLUSH(env, &mb);
673				__db_msg(env, "ILLEGAL HASH PAGE TYPE: %lu",
674				    (u_long)HPAGE_PTYPE(hk));
675				ret = EINVAL;
676				break;
677			}
678			break;
679		case P_IBTREE:
680			bi = sp;
681
682			if (F_ISSET(dbp, DB_AM_RECNUM))
683				__db_msgadd(env, &mb,
684				    "count: %4lu ", (u_long)bi->nrecs);
685			__db_msgadd(env, &mb,
686			    "pgno: %4lu type: %lu ",
687			    (u_long)bi->pgno, (u_long)bi->type);
688			switch (B_TYPE(bi->type)) {
689			case B_KEYDATA:
690				__db_prbytes(env, &mb, bi->data, bi->len);
691				break;
692			case B_DUPLICATE:
693			case B_OVERFLOW:
694				__db_proff(env, &mb, bi->data);
695				break;
696			default:
697				DB_MSGBUF_FLUSH(env, &mb);
698				__db_msg(env, "ILLEGAL BINTERNAL TYPE: %lu",
699				    (u_long)B_TYPE(bi->type));
700				ret = EINVAL;
701				break;
702			}
703			break;
704		case P_IRECNO:
705			ri = sp;
706			__db_msgadd(env, &mb, "entries %4lu pgno %4lu",
707			    (u_long)ri->nrecs, (u_long)ri->pgno);
708			DB_MSGBUF_FLUSH(env, &mb);
709			break;
710		case P_LBTREE:
711		case P_LDUP:
712		case P_LRECNO:
713			bk = sp;
714			switch (B_TYPE(bk->type)) {
715			case B_KEYDATA:
716				__db_prbytes(env, &mb, bk->data, bk->len);
717				break;
718			case B_DUPLICATE:
719			case B_OVERFLOW:
720				__db_proff(env, &mb, bk);
721				break;
722			default:
723				DB_MSGBUF_FLUSH(env, &mb);
724				__db_msg(env,
725			    "ILLEGAL DUPLICATE/LBTREE/LRECNO TYPE: %lu",
726				    (u_long)B_TYPE(bk->type));
727				ret = EINVAL;
728				break;
729			}
730			break;
731		default:
732type_err:		DB_MSGBUF_FLUSH(env, &mb);
733			__db_msg(env,
734			    "ILLEGAL PAGE TYPE: %lu", (u_long)TYPE(h));
735			ret = EINVAL;
736			continue;
737		}
738	}
739	return (ret);
740}
741
742/*
743 * __db_prbytes --
744 *	Print out a data element.
745 *
746 * PUBLIC: void __db_prbytes __P((ENV *, DB_MSGBUF *, u_int8_t *, u_int32_t));
747 */
748void
749__db_prbytes(env, mbp, bytes, len)
750	ENV *env;
751	DB_MSGBUF *mbp;
752	u_int8_t *bytes;
753	u_int32_t len;
754{
755	u_int8_t *p;
756	u_int32_t i;
757	int msg_truncated;
758
759	__db_msgadd(env, mbp, "len: %3lu", (u_long)len);
760	if (len != 0) {
761		__db_msgadd(env, mbp, " data: ");
762
763		/*
764		 * Print the first 20 bytes of the data.   If that chunk is
765		 * all printable characters, print it as text, else print it
766		 * in hex.  We have this heuristic because we're displaying
767		 * things like lock objects that could be either text or data.
768		 */
769		if (len > 20) {
770			len = 20;
771			msg_truncated = 1;
772		} else
773			msg_truncated = 0;
774		for (p = bytes, i = len; i > 0; --i, ++p)
775			if (!isprint((int)*p) && *p != '\t' && *p != '\n')
776				break;
777		if (i == 0)
778			for (p = bytes, i = len; i > 0; --i, ++p)
779				__db_msgadd(env, mbp, "%c", *p);
780		else
781			for (p = bytes, i = len; i > 0; --i, ++p)
782				__db_msgadd(env, mbp, "%#.2x", (u_int)*p);
783		if (msg_truncated)
784			__db_msgadd(env, mbp, "...");
785	}
786	DB_MSGBUF_FLUSH(env, mbp);
787}
788
789/*
790 * __db_proff --
791 *	Print out an off-page element.
792 */
793static void
794__db_proff(env, mbp, vp)
795	ENV *env;
796	DB_MSGBUF *mbp;
797	void *vp;
798{
799	BOVERFLOW *bo;
800
801	bo = vp;
802	switch (B_TYPE(bo->type)) {
803	case B_OVERFLOW:
804		__db_msgadd(env, mbp, "overflow: total len: %4lu page: %4lu",
805		    (u_long)bo->tlen, (u_long)bo->pgno);
806		break;
807	case B_DUPLICATE:
808		__db_msgadd(
809		    env, mbp, "duplicate: page: %4lu", (u_long)bo->pgno);
810		break;
811	default:
812		/* NOTREACHED */
813		break;
814	}
815	DB_MSGBUF_FLUSH(env, mbp);
816}
817
818/*
819 * __db_prflags --
820 *	Print out flags values.
821 *
822 * PUBLIC: void __db_prflags __P((ENV *, DB_MSGBUF *,
823 * PUBLIC:     u_int32_t, const FN *, const char *, const char *));
824 */
825void
826__db_prflags(env, mbp, flags, fn, prefix, suffix)
827	ENV *env;
828	DB_MSGBUF *mbp;
829	u_int32_t flags;
830	FN const *fn;
831	const char *prefix, *suffix;
832{
833	DB_MSGBUF mb;
834	const FN *fnp;
835	int found, standalone;
836	const char *sep;
837
838	if (fn == NULL)
839		return;
840
841	/*
842	 * If it's a standalone message, output the suffix (which will be the
843	 * label), regardless of whether we found anything or not, and flush
844	 * the line.
845	 */
846	if (mbp == NULL) {
847		standalone = 1;
848		mbp = &mb;
849		DB_MSGBUF_INIT(mbp);
850	} else
851		standalone = 0;
852
853	sep = prefix == NULL ? "" : prefix;
854	for (found = 0, fnp = fn; fnp->mask != 0; ++fnp)
855		if (LF_ISSET(fnp->mask)) {
856			__db_msgadd(env, mbp, "%s%s", sep, fnp->name);
857			sep = ", ";
858			found = 1;
859		}
860
861	if ((standalone || found) && suffix != NULL)
862		__db_msgadd(env, mbp, "%s", suffix);
863	if (standalone)
864		DB_MSGBUF_FLUSH(env, mbp);
865}
866
867/*
868 * __db_lockmode_to_string --
869 *	Return the name of the lock mode.
870 *
871 * PUBLIC: const char * __db_lockmode_to_string __P((db_lockmode_t));
872 */
873const char *
874__db_lockmode_to_string(mode)
875	db_lockmode_t mode;
876{
877	switch (mode) {
878	case DB_LOCK_NG:
879		return ("Not granted");
880	case DB_LOCK_READ:
881		return ("Shared/read");
882	case DB_LOCK_WRITE:
883		return ("Exclusive/write");
884	case DB_LOCK_WAIT:
885		return ("Wait for event");
886	case DB_LOCK_IWRITE:
887		return ("Intent exclusive/write");
888	case DB_LOCK_IREAD:
889		return ("Intent shared/read");
890	case DB_LOCK_IWR:
891		return ("Intent to read/write");
892	case DB_LOCK_READ_UNCOMMITTED:
893		return ("Read uncommitted");
894	case DB_LOCK_WWRITE:
895		return ("Was written");
896	default:
897		break;
898	}
899	return ("UNKNOWN LOCK MODE");
900}
901
902/*
903 * __db_pagetype_to_string --
904 *	Return the name of the specified page type.
905 */
906static const char *
907__db_pagetype_to_string(type)
908	u_int32_t type;
909{
910	char *s;
911
912	s = NULL;
913	switch (type) {
914	case P_BTREEMETA:
915		s = "btree metadata";
916		break;
917	case P_LDUP:
918		s = "duplicate";
919		break;
920	case P_HASH_UNSORTED:
921		s = "hash unsorted";
922		break;
923	case P_HASH:
924		s = "hash";
925		break;
926	case P_HASHMETA:
927		s = "hash metadata";
928		break;
929	case P_IBTREE:
930		s = "btree internal";
931		break;
932	case P_INVALID:
933		s = "invalid";
934		break;
935	case P_IRECNO:
936		s = "recno internal";
937		break;
938	case P_LBTREE:
939		s = "btree leaf";
940		break;
941	case P_LRECNO:
942		s = "recno leaf";
943		break;
944	case P_OVERFLOW:
945		s = "overflow";
946		break;
947	case P_QAMMETA:
948		s = "queue metadata";
949		break;
950	case P_QAMDATA:
951		s = "queue";
952		break;
953	default:
954		/* Just return a NULL. */
955		break;
956	}
957	return (s);
958}
959
960#else /* !HAVE_STATISTICS */
961
962/*
963 * __db_dumptree --
964 *	Dump the tree to a file.
965 *
966 * PUBLIC: int __db_dumptree __P((DB *, DB_TXN *, char *, char *));
967 */
968int
969__db_dumptree(dbp, txn, op, name)
970	DB *dbp;
971	DB_TXN *txn;
972	char *op, *name;
973{
974	COMPQUIET(txn, NULL);
975	COMPQUIET(op, NULL);
976	COMPQUIET(name, NULL);
977
978	return (__db_stat_not_built(dbp->env));
979}
980
981/*
982 * __db_get_flags_fn --
983 *	Return the __db_flags_fn array.
984 *
985 * PUBLIC: const FN * __db_get_flags_fn __P((void));
986 */
987const FN *
988__db_get_flags_fn()
989{
990	/*
991	 * !!!
992	 * The Tcl API uses this interface, stub it off.
993	 */
994	return (NULL);
995}
996#endif
997
998/*
999 * __db_dump_pp --
1000 *	DB->dump pre/post processing.
1001 *
1002 * PUBLIC: int __db_dump_pp __P((DB *, const char *,
1003 * PUBLIC:     int (*)(void *, const void *), void *, int, int));
1004 */
1005int
1006__db_dump_pp(dbp, subname, callback, handle, pflag, keyflag)
1007	DB *dbp;
1008	const char *subname;
1009	int (*callback) __P((void *, const void *));
1010	void *handle;
1011	int pflag, keyflag;
1012{
1013	DB_THREAD_INFO *ip;
1014	ENV *env;
1015	int handle_check, ret, t_ret;
1016
1017	env = dbp->env;
1018
1019	DB_ILLEGAL_BEFORE_OPEN(dbp, "DB->dump");
1020
1021	ENV_ENTER(env, ip);
1022
1023	/* Check for replication block. */
1024	handle_check = IS_ENV_REPLICATED(env);
1025	if (handle_check && (ret = __db_rep_enter(dbp, 1, 0, 1)) != 0) {
1026		handle_check = 0;
1027		goto err;
1028	}
1029
1030	ret = __db_dump(dbp, subname, callback, handle, pflag, keyflag);
1031
1032	/* Release replication block. */
1033	if (handle_check && (t_ret = __env_db_rep_exit(env)) != 0 && ret == 0)
1034		ret = t_ret;
1035
1036err:	ENV_LEAVE(env, ip);
1037	return (ret);
1038}
1039
1040/*
1041 * __db_dump --
1042 *	DB->dump.
1043 *
1044 * PUBLIC: int __db_dump __P((DB *, const char *,
1045 * PUBLIC:     int (*)(void *, const void *), void *, int, int));
1046 */
1047int
1048__db_dump(dbp, subname, callback, handle, pflag, keyflag)
1049	DB *dbp;
1050	const char *subname;
1051	int (*callback) __P((void *, const void *));
1052	void *handle;
1053	int pflag, keyflag;
1054{
1055	DBC *dbcp;
1056	DBT key, data;
1057	DBT keyret, dataret;
1058	ENV *env;
1059	db_recno_t recno;
1060	int is_recno, ret, t_ret;
1061	void *pointer;
1062
1063	env = dbp->env;
1064
1065	if ((ret = __db_prheader(
1066	    dbp, subname, pflag, keyflag, handle, callback, NULL, 0)) != 0)
1067		return (ret);
1068
1069	/*
1070	 * Get a cursor and step through the database, printing out each
1071	 * key/data pair.
1072	 */
1073	if ((ret = __db_cursor(dbp, NULL, NULL, &dbcp, 0)) != 0)
1074		return (ret);
1075
1076	memset(&key, 0, sizeof(key));
1077	memset(&data, 0, sizeof(data));
1078	if ((ret = __os_malloc(env, 1024 * 1024, &data.data)) != 0)
1079		goto err;
1080	data.ulen = 1024 * 1024;
1081	data.flags = DB_DBT_USERMEM;
1082	is_recno = (dbp->type == DB_RECNO || dbp->type == DB_QUEUE);
1083	keyflag = is_recno ? keyflag : 1;
1084	if (is_recno) {
1085		keyret.data = &recno;
1086		keyret.size = sizeof(recno);
1087	}
1088
1089retry: while ((ret =
1090	    __dbc_get(dbcp, &key, &data, DB_NEXT | DB_MULTIPLE_KEY)) == 0) {
1091		DB_MULTIPLE_INIT(pointer, &data);
1092		for (;;) {
1093			if (is_recno)
1094				DB_MULTIPLE_RECNO_NEXT(pointer, &data,
1095				    recno, dataret.data, dataret.size);
1096			else
1097				DB_MULTIPLE_KEY_NEXT(pointer,
1098				    &data, keyret.data,
1099				    keyret.size, dataret.data, dataret.size);
1100
1101			if (dataret.data == NULL)
1102				break;
1103
1104			if ((keyflag &&
1105			    (ret = __db_prdbt(&keyret, pflag, " ",
1106			    handle, callback, is_recno)) != 0) ||
1107			    (ret = __db_prdbt(&dataret, pflag, " ",
1108			    handle, callback, 0)) != 0)
1109				goto err;
1110		}
1111	}
1112	if (ret == DB_BUFFER_SMALL) {
1113		data.size = (u_int32_t)DB_ALIGN(data.size, 1024);
1114		if ((ret = __os_realloc(env, data.size, &data.data)) != 0)
1115			goto err;
1116		data.ulen = data.size;
1117		goto retry;
1118	}
1119	if (ret == DB_NOTFOUND)
1120		ret = 0;
1121
1122	if ((t_ret = __db_prfooter(handle, callback)) != 0 && ret == 0)
1123		ret = t_ret;
1124
1125err:	if ((t_ret = __dbc_close(dbcp)) != 0 && ret == 0)
1126		ret = t_ret;
1127	if (data.data != NULL)
1128		__os_free(env, data.data);
1129
1130	return (ret);
1131}
1132
1133/*
1134 * __db_prdbt --
1135 *	Print out a DBT data element.
1136 *
1137 * PUBLIC: int __db_prdbt __P((DBT *, int, const char *, void *,
1138 * PUBLIC:     int (*)(void *, const void *), int));
1139 */
1140int
1141__db_prdbt(dbtp, checkprint, prefix, handle, callback, is_recno)
1142	DBT *dbtp;
1143	int checkprint;
1144	const char *prefix;
1145	void *handle;
1146	int (*callback) __P((void *, const void *));
1147	int is_recno;
1148{
1149	static const u_char hex[] = "0123456789abcdef";
1150	db_recno_t recno;
1151	size_t len;
1152	int ret;
1153#define	DBTBUFLEN	100
1154	u_int8_t *p, *hp;
1155	char buf[DBTBUFLEN], hbuf[DBTBUFLEN];
1156
1157	/*
1158	 * !!!
1159	 * This routine is the routine that dumps out items in the format
1160	 * used by db_dump(1) and db_load(1).  This means that the format
1161	 * cannot change.
1162	 */
1163	if (prefix != NULL && (ret = callback(handle, prefix)) != 0)
1164		return (ret);
1165	if (is_recno) {
1166		/*
1167		 * We're printing a record number, and this has to be done
1168		 * in a platform-independent way.  So we use the numeral in
1169		 * straight ASCII.
1170		 */
1171		(void)__ua_memcpy(&recno, dbtp->data, sizeof(recno));
1172		snprintf(buf, DBTBUFLEN, "%lu", (u_long)recno);
1173
1174		/* If we're printing data as hex, print keys as hex too. */
1175		if (!checkprint) {
1176			for (len = strlen(buf), p = (u_int8_t *)buf,
1177			    hp = (u_int8_t *)hbuf; len-- > 0; ++p) {
1178				*hp++ = hex[(u_int8_t)(*p & 0xf0) >> 4];
1179				*hp++ = hex[*p & 0x0f];
1180			}
1181			*hp = '\0';
1182			ret = callback(handle, hbuf);
1183		} else
1184			ret = callback(handle, buf);
1185
1186		if (ret != 0)
1187			return (ret);
1188	} else if (checkprint) {
1189		for (len = dbtp->size, p = dbtp->data; len--; ++p)
1190			if (isprint((int)*p)) {
1191				if (*p == '\\' &&
1192				    (ret = callback(handle, "\\")) != 0)
1193					return (ret);
1194				snprintf(buf, DBTBUFLEN, "%c", *p);
1195				if ((ret = callback(handle, buf)) != 0)
1196					return (ret);
1197			} else {
1198				snprintf(buf, DBTBUFLEN, "\\%c%c",
1199				    hex[(u_int8_t)(*p & 0xf0) >> 4],
1200				    hex[*p & 0x0f]);
1201				if ((ret = callback(handle, buf)) != 0)
1202					return (ret);
1203			}
1204	} else
1205		for (len = dbtp->size, p = dbtp->data; len--; ++p) {
1206			snprintf(buf, DBTBUFLEN, "%c%c",
1207			    hex[(u_int8_t)(*p & 0xf0) >> 4],
1208			    hex[*p & 0x0f]);
1209			if ((ret = callback(handle, buf)) != 0)
1210				return (ret);
1211		}
1212
1213	return (callback(handle, "\n"));
1214}
1215
1216/*
1217 * __db_prheader --
1218 *	Write out header information in the format expected by db_load.
1219 *
1220 * PUBLIC: int	__db_prheader __P((DB *, const char *, int, int, void *,
1221 * PUBLIC:     int (*)(void *, const void *), VRFY_DBINFO *, db_pgno_t));
1222 */
1223int
1224__db_prheader(dbp, subname, pflag, keyflag, handle, callback, vdp, meta_pgno)
1225	DB *dbp;
1226	const char *subname;
1227	int pflag, keyflag;
1228	void *handle;
1229	int (*callback) __P((void *, const void *));
1230	VRFY_DBINFO *vdp;
1231	db_pgno_t meta_pgno;
1232{
1233	DBT dbt;
1234	DBTYPE dbtype;
1235	ENV *env;
1236	VRFY_PAGEINFO *pip;
1237	u_int32_t flags, tmp_u_int32;
1238	size_t buflen;
1239	char *buf;
1240	int using_vdp, ret, t_ret, tmp_int;
1241
1242	ret = 0;
1243	buf = NULL;
1244	COMPQUIET(buflen, 0);
1245
1246	/*
1247	 * If dbp is NULL, then pip is guaranteed to be non-NULL; we only ever
1248	 * call __db_prheader with a NULL dbp from one case inside __db_prdbt,
1249	 * and this is a special subdatabase for "lost" items.  In this case
1250	 * we have a vdp (from which we'll get a pip).  In all other cases, we
1251	 * will have a non-NULL dbp (and vdp may or may not be NULL depending
1252	 * on whether we're salvaging).
1253	 */
1254	if (dbp == NULL)
1255		env = NULL;
1256	else
1257		env = dbp->env;
1258	DB_ASSERT(env, dbp != NULL || vdp != NULL);
1259
1260	/*
1261	 * If we've been passed a verifier statistics object, use that;  we're
1262	 * being called in a context where dbp->stat is unsafe.
1263	 *
1264	 * Also, the verifier may set the pflag on a per-salvage basis.  If so,
1265	 * respect that.
1266	 */
1267	if (vdp != NULL) {
1268		if ((ret = __db_vrfy_getpageinfo(vdp, meta_pgno, &pip)) != 0)
1269			return (ret);
1270
1271		if (F_ISSET(vdp, SALVAGE_PRINTABLE))
1272			pflag = 1;
1273		using_vdp = 1;
1274	} else {
1275		pip = NULL;
1276		using_vdp = 0;
1277	}
1278
1279	/*
1280	 * If dbp is NULL, make it a btree.  Otherwise, set dbtype to whatever
1281	 * appropriate type for the specified meta page, or the type of the dbp.
1282	 */
1283	if (dbp == NULL)
1284		dbtype = DB_BTREE;
1285	else if (using_vdp)
1286		switch (pip->type) {
1287		case P_BTREEMETA:
1288			if (F_ISSET(pip, VRFY_IS_RECNO))
1289				dbtype = DB_RECNO;
1290			else
1291				dbtype = DB_BTREE;
1292			break;
1293		case P_HASHMETA:
1294			dbtype = DB_HASH;
1295			break;
1296		case P_QAMMETA:
1297			dbtype = DB_QUEUE;
1298			break;
1299		default:
1300			/*
1301			 * If the meta page is of a bogus type, it's because
1302			 * we have a badly corrupt database.  (We must be in
1303			 * the verifier for pip to be non-NULL.) Pretend we're
1304			 * a Btree and salvage what we can.
1305			 */
1306			DB_ASSERT(env, F_ISSET(dbp, DB_AM_VERIFYING));
1307			dbtype = DB_BTREE;
1308			break;
1309		}
1310	else
1311		dbtype = dbp->type;
1312
1313	if ((ret = callback(handle, "VERSION=3\n")) != 0)
1314		goto err;
1315	if (pflag) {
1316		if ((ret = callback(handle, "format=print\n")) != 0)
1317			goto err;
1318	} else if ((ret = callback(handle, "format=bytevalue\n")) != 0)
1319		goto err;
1320
1321	/*
1322	 * 64 bytes is long enough, as a minimum bound, for any of the
1323	 * fields besides subname.  Subname uses __db_prdbt and therefore
1324	 * does not need buffer space here.
1325	 */
1326	buflen = 64;
1327	if ((ret = __os_malloc(env, buflen, &buf)) != 0)
1328		goto err;
1329	if (subname != NULL) {
1330		snprintf(buf, buflen, "database=");
1331		if ((ret = callback(handle, buf)) != 0)
1332			goto err;
1333		DB_INIT_DBT(dbt, subname, strlen(subname));
1334		if ((ret = __db_prdbt(&dbt, 1, NULL, handle, callback, 0)) != 0)
1335			goto err;
1336	}
1337	switch (dbtype) {
1338	case DB_BTREE:
1339		if ((ret = callback(handle, "type=btree\n")) != 0)
1340			goto err;
1341		if (using_vdp)
1342			tmp_int = F_ISSET(pip, VRFY_HAS_RECNUMS) ? 1 : 0;
1343		else {
1344			if ((ret = __db_get_flags(dbp, &flags)) != 0) {
1345				__db_err(env, ret, "DB->get_flags");
1346				goto err;
1347			}
1348			tmp_int = F_ISSET(dbp, DB_AM_RECNUM) ? 1 : 0;
1349		}
1350		if (tmp_int && (ret = callback(handle, "recnum=1\n")) != 0)
1351			goto err;
1352
1353		if (using_vdp)
1354			tmp_u_int32 = pip->bt_minkey;
1355		else
1356			if ((ret =
1357			    __bam_get_bt_minkey(dbp, &tmp_u_int32)) != 0) {
1358				__db_err(env, ret, "DB->get_bt_minkey");
1359				goto err;
1360			}
1361		if (tmp_u_int32 != 0 && tmp_u_int32 != DEFMINKEYPAGE) {
1362			snprintf(buf, buflen,
1363			    "bt_minkey=%lu\n", (u_long)tmp_u_int32);
1364			if ((ret = callback(handle, buf)) != 0)
1365				goto err;
1366		}
1367		break;
1368	case DB_HASH:
1369#ifdef HAVE_HASH
1370		if ((ret = callback(handle, "type=hash\n")) != 0)
1371			goto err;
1372		if (using_vdp)
1373			tmp_u_int32 = pip->h_ffactor;
1374		else
1375			if ((ret =
1376			    __ham_get_h_ffactor(dbp, &tmp_u_int32)) != 0) {
1377				__db_err(env, ret, "DB->get_h_ffactor");
1378				goto err;
1379			}
1380		if (tmp_u_int32 != 0) {
1381			snprintf(buf, buflen,
1382			    "h_ffactor=%lu\n", (u_long)tmp_u_int32);
1383			if ((ret = callback(handle, buf)) != 0)
1384				goto err;
1385		}
1386
1387		if (using_vdp)
1388			tmp_u_int32 = pip->h_nelem;
1389		else
1390			if ((ret = __ham_get_h_nelem(dbp, &tmp_u_int32)) != 0) {
1391				__db_err(env, ret, "DB->get_h_nelem");
1392				goto err;
1393			}
1394		/*
1395		 * Hash databases have an h_nelem field of 0 or 1, neither
1396		 * of those values is interesting.
1397		 */
1398		if (tmp_u_int32 > 1) {
1399			snprintf(buf, buflen,
1400			    "h_nelem=%lu\n", (u_long)tmp_u_int32);
1401			if ((ret = callback(handle, buf)) != 0)
1402				goto err;
1403		}
1404		break;
1405#else
1406		ret = __db_no_hash_am(env);
1407		goto err;
1408#endif
1409	case DB_QUEUE:
1410#ifdef HAVE_QUEUE
1411		if ((ret = callback(handle, "type=queue\n")) != 0)
1412			goto err;
1413		if (using_vdp)
1414			tmp_u_int32 = vdp->re_len;
1415		else
1416			if ((ret = __ram_get_re_len(dbp, &tmp_u_int32)) != 0) {
1417				__db_err(env, ret, "DB->get_re_len");
1418				goto err;
1419			}
1420		snprintf(buf, buflen, "re_len=%lu\n", (u_long)tmp_u_int32);
1421		if ((ret = callback(handle, buf)) != 0)
1422			goto err;
1423
1424		if (using_vdp)
1425			tmp_int = (int)vdp->re_pad;
1426		else
1427			if ((ret = __ram_get_re_pad(dbp, &tmp_int)) != 0) {
1428				__db_err(env, ret, "DB->get_re_pad");
1429				goto err;
1430			}
1431		if (tmp_int != 0 && tmp_int != ' ') {
1432			snprintf(buf, buflen, "re_pad=%#x\n", tmp_int);
1433			if ((ret = callback(handle, buf)) != 0)
1434				goto err;
1435		}
1436
1437		if (using_vdp)
1438			tmp_u_int32 = vdp->page_ext;
1439		else
1440			if ((ret =
1441			    __qam_get_extentsize(dbp, &tmp_u_int32)) != 0) {
1442				__db_err(env, ret, "DB->get_q_extentsize");
1443				goto err;
1444			}
1445		if (tmp_u_int32 != 0) {
1446			snprintf(buf, buflen,
1447			    "extentsize=%lu\n", (u_long)tmp_u_int32);
1448			if ((ret = callback(handle, buf)) != 0)
1449				goto err;
1450		}
1451		break;
1452#else
1453		ret = __db_no_queue_am(env);
1454		goto err;
1455#endif
1456	case DB_RECNO:
1457		if ((ret = callback(handle, "type=recno\n")) != 0)
1458			goto err;
1459		if (using_vdp)
1460			tmp_int = F_ISSET(pip, VRFY_IS_RRECNO) ? 1 : 0;
1461		else
1462			tmp_int = F_ISSET(dbp, DB_AM_RENUMBER) ? 1 : 0;
1463		if (tmp_int != 0 &&
1464		    (ret = callback(handle, "renumber=1\n")) != 0)
1465				goto err;
1466
1467		if (using_vdp)
1468			tmp_int = F_ISSET(pip, VRFY_IS_FIXEDLEN) ? 1 : 0;
1469		else
1470			tmp_int = F_ISSET(dbp, DB_AM_FIXEDLEN) ? 1 : 0;
1471		if (tmp_int) {
1472			if (using_vdp)
1473				tmp_u_int32 = pip->re_len;
1474			else
1475				if ((ret =
1476				    __ram_get_re_len(dbp, &tmp_u_int32)) != 0) {
1477					__db_err(env, ret, "DB->get_re_len");
1478					goto err;
1479				}
1480			snprintf(buf, buflen,
1481			    "re_len=%lu\n", (u_long)tmp_u_int32);
1482			if ((ret = callback(handle, buf)) != 0)
1483				goto err;
1484
1485			if (using_vdp)
1486				tmp_int = (int)pip->re_pad;
1487			else
1488				if ((ret =
1489				    __ram_get_re_pad(dbp, &tmp_int)) != 0) {
1490					__db_err(env, ret, "DB->get_re_pad");
1491					goto err;
1492				}
1493			if (tmp_int != 0 && tmp_int != ' ') {
1494				snprintf(buf,
1495				    buflen, "re_pad=%#x\n", (u_int)tmp_int);
1496				if ((ret = callback(handle, buf)) != 0)
1497					goto err;
1498			}
1499		}
1500		break;
1501	case DB_UNKNOWN:			/* Impossible. */
1502		ret = __db_unknown_path(env, "__db_prheader");
1503		goto err;
1504	}
1505
1506	if (using_vdp) {
1507		if (F_ISSET(pip, VRFY_HAS_CHKSUM))
1508			if ((ret = callback(handle, "chksum=1\n")) != 0)
1509				goto err;
1510		if (F_ISSET(pip, VRFY_HAS_DUPS))
1511			if ((ret = callback(handle, "duplicates=1\n")) != 0)
1512				goto err;
1513		if (F_ISSET(pip, VRFY_HAS_DUPSORT))
1514			if ((ret = callback(handle, "dupsort=1\n")) != 0)
1515				goto err;
1516		/*
1517		 * !!!
1518		 * We don't know if the page size was the default if we're
1519		 * salvaging.  It doesn't seem that interesting to have, so
1520		 * we ignore it for now.
1521		 */
1522	} else {
1523		if (F_ISSET(dbp, DB_AM_CHKSUM))
1524			if ((ret = callback(handle, "chksum=1\n")) != 0)
1525				goto err;
1526		if (F_ISSET(dbp, DB_AM_DUP))
1527			if ((ret = callback(handle, "duplicates=1\n")) != 0)
1528				goto err;
1529		if (F_ISSET(dbp, DB_AM_DUPSORT))
1530			if ((ret = callback(handle, "dupsort=1\n")) != 0)
1531				goto err;
1532		if (!F_ISSET(dbp, DB_AM_PGDEF)) {
1533			snprintf(buf, buflen,
1534			    "db_pagesize=%lu\n", (u_long)dbp->pgsize);
1535			if ((ret = callback(handle, buf)) != 0)
1536				goto err;
1537		}
1538	}
1539
1540	if (keyflag && (ret = callback(handle, "keys=1\n")) != 0)
1541		goto err;
1542
1543	ret = callback(handle, "HEADER=END\n");
1544
1545err:	if (using_vdp &&
1546	    (t_ret = __db_vrfy_putpageinfo(env, vdp, pip)) != 0 && ret == 0)
1547		ret = t_ret;
1548	if (buf != NULL)
1549		__os_free(env, buf);
1550
1551	return (ret);
1552}
1553
1554/*
1555 * __db_prfooter --
1556 *	Print the footer that marks the end of a DB dump.  This is trivial,
1557 *	but for consistency's sake we don't want to put its literal contents
1558 *	in multiple places.
1559 *
1560 * PUBLIC: int __db_prfooter __P((void *, int (*)(void *, const void *)));
1561 */
1562int
1563__db_prfooter(handle, callback)
1564	void *handle;
1565	int (*callback) __P((void *, const void *));
1566{
1567	return (callback(handle, "DATA=END\n"));
1568}
1569
1570/*
1571 * __db_pr_callback --
1572 *	Callback function for using pr_* functions from C.
1573 *
1574 * PUBLIC: int  __db_pr_callback __P((void *, const void *));
1575 */
1576int
1577__db_pr_callback(handle, str_arg)
1578	void *handle;
1579	const void *str_arg;
1580{
1581	char *str;
1582	FILE *f;
1583
1584	str = (char *)str_arg;
1585	f = (FILE *)handle;
1586
1587	if (fprintf(f, "%s", str) != (int)strlen(str))
1588		return (EIO);
1589
1590	return (0);
1591}
1592
1593/*
1594 * __db_dbtype_to_string --
1595 *	Return the name of the database type.
1596 *
1597 * PUBLIC: const char * __db_dbtype_to_string __P((DBTYPE));
1598 */
1599const char *
1600__db_dbtype_to_string(type)
1601	DBTYPE type;
1602{
1603	switch (type) {
1604	case DB_BTREE:
1605		return ("btree");
1606	case DB_HASH:
1607		return ("hash");
1608	case DB_RECNO:
1609		return ("recno");
1610	case DB_QUEUE:
1611		return ("queue");
1612	case DB_UNKNOWN:
1613	default:
1614		break;
1615	}
1616	return ("UNKNOWN TYPE");
1617}
1618