1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 1996, 1997, 1998
5 *	Sleepycat Software.  All rights reserved.
6 */
7
8#include "config.h"
9
10#ifndef lint
11static const char sccsid[] = "@(#)db_pr.c	10.40 (Sleepycat) 11/22/98";
12#endif /* not lint */
13
14#ifndef NO_SYSTEM_INCLUDES
15#include <sys/types.h>
16
17#include <ctype.h>
18#include <errno.h>
19#include <stdlib.h>
20#include <string.h>
21#include <unistd.h>
22#endif
23
24#include "db_int.h"
25#include "db_page.h"
26#include "btree.h"
27#include "hash.h"
28#include "db_am.h"
29
30static void __db_proff __P((void *));
31static void __db_psize __P((DB_MPOOLFILE *));
32
33/*
34 * __db_loadme --
35 *	Force loading of this file.
36 *
37 * PUBLIC: void __db_loadme __P((void));
38 */
39void
40__db_loadme()
41{
42	getpid();
43}
44
45static FILE *set_fp;
46
47/*
48 * 64K is the maximum page size, so by default we check for offsets
49 * larger than that, and, where possible, we refine the test.
50 */
51#define	PSIZE_BOUNDARY	(64 * 1024 + 1)
52static size_t set_psize = PSIZE_BOUNDARY;
53
54/*
55 * __db_prinit --
56 *	Initialize tree printing routines.
57 *
58 * PUBLIC: FILE *__db_prinit __P((FILE *));
59 */
60FILE *
61__db_prinit(fp)
62	FILE *fp;
63{
64	if (set_fp == NULL)
65		set_fp = fp == NULL ? stdout : fp;
66	return (set_fp);
67}
68
69/*
70 * __db_dump --
71 *	Dump the tree to a file.
72 *
73 * PUBLIC: int __db_dump __P((DB *, char *, int));
74 */
75int
76__db_dump(dbp, name, all)
77	DB *dbp;
78	char *name;
79	int all;
80{
81	FILE *fp, *save_fp;
82
83	COMPQUIET(save_fp, NULL);
84
85	if (set_psize == PSIZE_BOUNDARY)
86		__db_psize(dbp->mpf);
87
88	if (name != NULL) {
89		if ((fp = fopen(name, "w")) == NULL)
90			return (errno);
91		save_fp = set_fp;
92		set_fp = fp;
93	} else
94		fp = __db_prinit(NULL);
95
96	(void)__db_prdb(dbp);
97	if (dbp->type == DB_HASH)
98		(void)__db_prhash(dbp);
99	else
100		(void)__db_prbtree(dbp);
101	fprintf(fp, "%s\n", DB_LINE);
102	__db_prtree(dbp->mpf, all);
103
104	if (name != NULL) {
105		(void)fclose(fp);
106		set_fp = save_fp;
107	}
108	return (0);
109}
110
111/*
112 * __db_prdb --
113 *	Print out the DB structure information.
114 *
115 * PUBLIC: int __db_prdb __P((DB *));
116 */
117int
118__db_prdb(dbp)
119	DB *dbp;
120{
121	static const FN fn[] = {
122		{ DB_AM_DUP,		"duplicates" },
123		{ DB_AM_INMEM,		"in-memory" },
124		{ DB_AM_LOCKING,	"locking" },
125		{ DB_AM_LOGGING,	"logging" },
126		{ DB_AM_MLOCAL,		"local mpool" },
127		{ DB_AM_PGDEF,		"default page size" },
128		{ DB_AM_RDONLY,		"read-only" },
129		{ DB_AM_SWAP,		"needswap" },
130		{ DB_AM_THREAD,		"thread" },
131		{ DB_BT_RECNUM,		"btree:recnum" },
132		{ DB_DBM_ERROR,		"dbm/ndbm error" },
133		{ DB_RE_DELIMITER,	"recno:delimiter" },
134		{ DB_RE_FIXEDLEN,	"recno:fixed-length" },
135		{ DB_RE_PAD,		"recno:pad" },
136		{ DB_RE_RENUMBER,	"recno:renumber" },
137		{ DB_RE_SNAPSHOT,	"recno:snapshot" },
138		{ 0 },
139	};
140	FILE *fp;
141	const char *t;
142
143	fp = __db_prinit(NULL);
144
145	switch (dbp->type) {
146	case DB_BTREE:
147		t = "btree";
148		break;
149	case DB_HASH:
150		t = "hash";
151		break;
152	case DB_RECNO:
153		t = "recno";
154		break;
155	default:
156		t = "UNKNOWN";
157		break;
158	}
159
160	fprintf(fp, "%s ", t);
161	__db_prflags(dbp->flags, fn, fp);
162	fprintf(fp, "\n");
163
164	return (0);
165}
166
167/*
168 * __db_prbtree --
169 *	Print out the btree internal information.
170 *
171 * PUBLIC: int __db_prbtree __P((DB *));
172 */
173int
174__db_prbtree(dbp)
175	DB *dbp;
176{
177	static const FN mfn[] = {
178		{ BTM_DUP,	"duplicates" },
179		{ BTM_RECNO,	"recno" },
180		{ BTM_RECNUM,	"btree:recnum" },
181		{ BTM_FIXEDLEN,	"recno:fixed-length" },
182		{ BTM_RENUMBER,	"recno:renumber" },
183		{ 0 },
184	};
185	DBC *dbc;
186	BTMETA *mp;
187	BTREE *t;
188	FILE *fp;
189	PAGE *h;
190	RECNO *rp;
191	db_pgno_t i;
192	int cnt, ret;
193	const char *sep;
194
195	t = dbp->internal;
196	fp = __db_prinit(NULL);
197	if ((ret = dbp->cursor(dbp, NULL, &dbc, 0)) != 0)
198		return (ret);
199
200	(void)fprintf(fp, "%s\nOn-page metadata:\n", DB_LINE);
201
202	i = PGNO_METADATA;
203	if ((ret = memp_fget(dbp->mpf, &i, 0, (PAGE **)&mp)) != 0) {
204		(void)dbc->c_close(dbc);
205		return (ret);
206	}
207
208	fprintf(fp, "lsn.file: %lu lsn.offset: %lu\n",
209	    (u_long)LSN(mp).file, (u_long)LSN(mp).offset);
210	(void)fprintf(fp, "magic %#lx\n", (u_long)mp->magic);
211	(void)fprintf(fp, "version %#lx\n", (u_long)mp->version);
212	(void)fprintf(fp, "pagesize %lu\n", (u_long)mp->pagesize);
213	(void)fprintf(fp, "maxkey: %lu minkey: %lu\n",
214	    (u_long)mp->maxkey, (u_long)mp->minkey);
215
216	(void)fprintf(fp, "free list: %lu", (u_long)mp->free);
217	for (i = mp->free, cnt = 0, sep = ", "; i != PGNO_INVALID;) {
218		if ((ret = memp_fget(dbp->mpf, &i, 0, &h)) != 0)
219			return (ret);
220		i = h->next_pgno;
221		(void)memp_fput(dbp->mpf, h, 0);
222		(void)fprintf(fp, "%s%lu", sep, (u_long)i);
223		if (++cnt % 10 == 0) {
224			(void)fprintf(fp, "\n");
225			cnt = 0;
226			sep = "";
227		} else
228			sep = ", ";
229	}
230	(void)fprintf(fp, "\n");
231
232	(void)fprintf(fp, "flags %#lx", (u_long)mp->flags);
233	__db_prflags(mp->flags, mfn, fp);
234	(void)fprintf(fp, "\n");
235	(void)memp_fput(dbp->mpf, mp, 0);
236
237	(void)fprintf(fp, "%s\nDB_INFO:\n", DB_LINE);
238	(void)fprintf(fp, "bt_maxkey: %lu bt_minkey: %lu\n",
239	    (u_long)t->bt_maxkey, (u_long)t->bt_minkey);
240	(void)fprintf(fp, "bt_compare: %#lx bt_prefix: %#lx\n",
241	    (u_long)t->bt_compare, (u_long)t->bt_prefix);
242	if ((rp = t->recno) != NULL) {
243		(void)fprintf(fp,
244		    "re_delim: %#lx re_pad: %#lx re_len: %lu re_source: %s\n",
245		    (u_long)rp->re_delim, (u_long)rp->re_pad,
246		    (u_long)rp->re_len,
247		    rp->re_source == NULL ? "" : rp->re_source);
248		(void)fprintf(fp,
249		    "cmap: %#lx smap: %#lx emap: %#lx msize: %lu\n",
250		    (u_long)rp->re_cmap, (u_long)rp->re_smap,
251		    (u_long)rp->re_emap, (u_long)rp->re_msize);
252	}
253	(void)fprintf(fp, "ovflsize: %lu\n", (u_long)t->bt_ovflsize);
254	(void)fflush(fp);
255	return (dbc->c_close(dbc));
256}
257
258/*
259 * __db_prhash --
260 *	Print out the hash internal information.
261 *
262 * PUBLIC: int __db_prhash __P((DB *));
263 */
264int
265__db_prhash(dbp)
266	DB *dbp;
267{
268	FILE *fp;
269	DBC *dbc;
270	HASH_CURSOR *hcp;
271	int i, put_page, ret;
272	db_pgno_t pgno;
273
274	fp = __db_prinit(NULL);
275	if ((ret = dbp->cursor(dbp, NULL, &dbc, 0)) != 0)
276		return (ret);
277	hcp = (HASH_CURSOR *)dbc->internal;
278
279	/*
280	 * In this case,  hcp->hdr will never be null, if we decide
281	 * to pass dbc's to this routine instead, then it could be.
282	 */
283	if (hcp->hdr == NULL) {
284		pgno = PGNO_METADATA;
285		if ((ret = memp_fget(dbp->mpf, &pgno, 0, &hcp->hdr)) != 0)
286			return (ret);
287		put_page = 1;
288	} else
289		put_page = 0;
290
291	fprintf(fp, "\tmagic      %#lx\n", (u_long)hcp->hdr->magic);
292	fprintf(fp, "\tversion    %lu\n", (u_long)hcp->hdr->version);
293	fprintf(fp, "\tpagesize   %lu\n", (u_long)hcp->hdr->pagesize);
294	fprintf(fp, "\tovfl_point %lu\n", (u_long)hcp->hdr->ovfl_point);
295	fprintf(fp, "\tlast_freed %lu\n", (u_long)hcp->hdr->last_freed);
296	fprintf(fp, "\tmax_bucket %lu\n", (u_long)hcp->hdr->max_bucket);
297	fprintf(fp, "\thigh_mask  %#lx\n", (u_long)hcp->hdr->high_mask);
298	fprintf(fp, "\tlow_mask   %#lx\n", (u_long)hcp->hdr->low_mask);
299	fprintf(fp, "\tffactor    %lu\n", (u_long)hcp->hdr->ffactor);
300	fprintf(fp, "\tnelem      %lu\n", (u_long)hcp->hdr->nelem);
301	fprintf(fp, "\th_charkey  %#lx\n", (u_long)hcp->hdr->h_charkey);
302
303	for (i = 0; i < NCACHED; i++)
304		fprintf(fp, "%lu ", (u_long)hcp->hdr->spares[i]);
305	fprintf(fp, "\n");
306
307	(void)fflush(fp);
308	if (put_page) {
309		(void)memp_fput(dbp->mpf, (PAGE *)hcp->hdr, 0);
310		hcp->hdr = NULL;
311	}
312	return (dbc->c_close(dbc));
313}
314
315/*
316 * __db_prtree --
317 *	Print out the entire tree.
318 *
319 * PUBLIC: int __db_prtree __P((DB_MPOOLFILE *, int));
320 */
321int
322__db_prtree(mpf, all)
323	DB_MPOOLFILE *mpf;
324	int all;
325{
326	PAGE *h;
327	db_pgno_t i;
328
329	if (set_psize == PSIZE_BOUNDARY)
330		__db_psize(mpf);
331
332	for (i = PGNO_ROOT;; ++i) {
333		if (memp_fget(mpf, &i, 0, &h) != 0)
334			break;
335		(void)__db_prpage(h, all);
336		(void)memp_fput(mpf, h, 0);
337	}
338	(void)fflush(__db_prinit(NULL));
339	return (0);
340}
341
342/*
343 * __db_prnpage
344 *	-- Print out a specific page.
345 *
346 * PUBLIC: int __db_prnpage __P((DB_MPOOLFILE *, db_pgno_t));
347 */
348int
349__db_prnpage(mpf, pgno)
350	DB_MPOOLFILE *mpf;
351	db_pgno_t pgno;
352{
353	PAGE *h;
354	int ret;
355
356	if (set_psize == PSIZE_BOUNDARY)
357		__db_psize(mpf);
358
359	if ((ret = memp_fget(mpf, &pgno, 0, &h)) != 0)
360		return (ret);
361
362	ret = __db_prpage(h, 1);
363	(void)fflush(__db_prinit(NULL));
364
365	(void)memp_fput(mpf, h, 0);
366	return (ret);
367}
368
369/*
370 * __db_prpage
371 *	-- Print out a page.
372 *
373 * PUBLIC: int __db_prpage __P((PAGE *, int));
374 */
375int
376__db_prpage(h, all)
377	PAGE *h;
378	int all;
379{
380	BINTERNAL *bi;
381	BKEYDATA *bk;
382	HOFFPAGE a_hkd;
383	FILE *fp;
384	RINTERNAL *ri;
385	db_indx_t dlen, len, i;
386	db_pgno_t pgno;
387	int deleted, ret;
388	const char *s;
389	u_int8_t *ep, *hk, *p;
390	void *sp;
391
392	fp = __db_prinit(NULL);
393
394	switch (TYPE(h)) {
395	case P_DUPLICATE:
396		s = "duplicate";
397		break;
398	case P_HASH:
399		s = "hash";
400		break;
401	case P_IBTREE:
402		s = "btree internal";
403		break;
404	case P_INVALID:
405		s = "invalid";
406		break;
407	case P_IRECNO:
408		s = "recno internal";
409		break;
410	case P_LBTREE:
411		s = "btree leaf";
412		break;
413	case P_LRECNO:
414		s = "recno leaf";
415		break;
416	case P_OVERFLOW:
417		s = "overflow";
418		break;
419	default:
420		fprintf(fp, "ILLEGAL PAGE TYPE: page: %lu type: %lu\n",
421		    (u_long)h->pgno, (u_long)TYPE(h));
422			return (1);
423	}
424	fprintf(fp, "page %4lu: (%s)\n", (u_long)h->pgno, s);
425	fprintf(fp, "    lsn.file: %lu lsn.offset: %lu",
426	    (u_long)LSN(h).file, (u_long)LSN(h).offset);
427	if (TYPE(h) == P_IBTREE || TYPE(h) == P_IRECNO ||
428	    (TYPE(h) == P_LRECNO && h->pgno == PGNO_ROOT))
429		fprintf(fp, " total records: %4lu", (u_long)RE_NREC(h));
430	fprintf(fp, "\n");
431	if (TYPE(h) != P_IBTREE && TYPE(h) != P_IRECNO)
432		fprintf(fp, "    prev: %4lu next: %4lu",
433		    (u_long)PREV_PGNO(h), (u_long)NEXT_PGNO(h));
434	if (TYPE(h) == P_IBTREE || TYPE(h) == P_LBTREE)
435		fprintf(fp, " level: %2lu", (u_long)h->level);
436	if (TYPE(h) == P_OVERFLOW) {
437		fprintf(fp, " ref cnt: %4lu ", (u_long)OV_REF(h));
438		__db_pr((u_int8_t *)h + P_OVERHEAD, OV_LEN(h));
439		return (0);
440	}
441	fprintf(fp, " entries: %4lu", (u_long)NUM_ENT(h));
442	fprintf(fp, " offset: %4lu\n", (u_long)HOFFSET(h));
443
444	if (!all || TYPE(h) == P_INVALID)
445		return (0);
446
447	ret = 0;
448	for (i = 0; i < NUM_ENT(h); i++) {
449		if (P_ENTRY(h, i) - (u_int8_t *)h < P_OVERHEAD ||
450		    (size_t)(P_ENTRY(h, i) - (u_int8_t *)h) >= set_psize) {
451			fprintf(fp,
452			    "ILLEGAL PAGE OFFSET: indx: %lu of %lu\n",
453			    (u_long)i, (u_long)h->inp[i]);
454			ret = EINVAL;
455			continue;
456		}
457		deleted = 0;
458		switch (TYPE(h)) {
459		case P_HASH:
460		case P_IBTREE:
461		case P_IRECNO:
462			sp = P_ENTRY(h, i);
463			break;
464		case P_LBTREE:
465			sp = P_ENTRY(h, i);
466			deleted = i % 2 == 0 &&
467			    B_DISSET(GET_BKEYDATA(h, i + O_INDX)->type);
468			break;
469		case P_LRECNO:
470		case P_DUPLICATE:
471			sp = P_ENTRY(h, i);
472			deleted = B_DISSET(GET_BKEYDATA(h, i)->type);
473			break;
474		default:
475			fprintf(fp,
476			    "ILLEGAL PAGE ITEM: %lu\n", (u_long)TYPE(h));
477			ret = EINVAL;
478			continue;
479		}
480		fprintf(fp, "   %s[%03lu] %4lu ",
481		    deleted ? "D" : " ", (u_long)i, (u_long)h->inp[i]);
482		switch (TYPE(h)) {
483		case P_HASH:
484			hk = sp;
485			switch (HPAGE_PTYPE(hk)) {
486			case H_OFFDUP:
487				memcpy(&pgno,
488				    HOFFDUP_PGNO(hk), sizeof(db_pgno_t));
489				fprintf(fp,
490				    "%4lu [offpage dups]\n", (u_long)pgno);
491				break;
492			case H_DUPLICATE:
493				/*
494				 * If this is the first item on a page, then
495				 * we cannot figure out how long it is, so
496				 * we only print the first one in the duplicate
497				 * set.
498				 */
499				if (i != 0)
500					len = LEN_HKEYDATA(h, 0, i);
501				else
502					len = 1;
503
504				fprintf(fp, "Duplicates:\n");
505				for (p = HKEYDATA_DATA(hk),
506				    ep = p + len; p < ep;) {
507					memcpy(&dlen, p, sizeof(db_indx_t));
508					p += sizeof(db_indx_t);
509					fprintf(fp, "\t\t");
510					__db_pr(p, dlen);
511					p += sizeof(db_indx_t) + dlen;
512				}
513				break;
514			case H_KEYDATA:
515				if (i != 0)
516					__db_pr(HKEYDATA_DATA(hk),
517					    LEN_HKEYDATA(h, 0, i));
518				else
519					fprintf(fp, "%s\n", HKEYDATA_DATA(hk));
520				break;
521			case H_OFFPAGE:
522				memcpy(&a_hkd, hk, HOFFPAGE_SIZE);
523				fprintf(fp,
524				    "overflow: total len: %4lu page: %4lu\n",
525				    (u_long)a_hkd.tlen, (u_long)a_hkd.pgno);
526				break;
527			}
528			break;
529		case P_IBTREE:
530			bi = sp;
531			fprintf(fp, "count: %4lu pgno: %4lu ",
532			    (u_long)bi->nrecs, (u_long)bi->pgno);
533			switch (B_TYPE(bi->type)) {
534			case B_KEYDATA:
535				__db_pr(bi->data, bi->len);
536				break;
537			case B_DUPLICATE:
538			case B_OVERFLOW:
539				__db_proff(bi->data);
540				break;
541			default:
542				fprintf(fp, "ILLEGAL BINTERNAL TYPE: %lu\n",
543				    (u_long)B_TYPE(bi->type));
544				ret = EINVAL;
545				break;
546			}
547			break;
548		case P_IRECNO:
549			ri = sp;
550			fprintf(fp, "entries %4lu pgno %4lu\n",
551			    (u_long)ri->nrecs, (u_long)ri->pgno);
552			break;
553		case P_LBTREE:
554		case P_LRECNO:
555		case P_DUPLICATE:
556			bk = sp;
557			switch (B_TYPE(bk->type)) {
558			case B_KEYDATA:
559				__db_pr(bk->data, bk->len);
560				break;
561			case B_DUPLICATE:
562			case B_OVERFLOW:
563				__db_proff(bk);
564				break;
565			default:
566				fprintf(fp,
567			    "ILLEGAL DUPLICATE/LBTREE/LRECNO TYPE: %lu\n",
568				    (u_long)B_TYPE(bk->type));
569				ret = EINVAL;
570				break;
571			}
572			break;
573		}
574	}
575	(void)fflush(fp);
576	return (ret);
577}
578
579/*
580 * __db_isbad
581 *	-- Decide if a page is corrupted.
582 *
583 * PUBLIC: int __db_isbad __P((PAGE *, int));
584 */
585int
586__db_isbad(h, die)
587	PAGE *h;
588	int die;
589{
590	BINTERNAL *bi;
591	BKEYDATA *bk;
592	FILE *fp;
593	db_indx_t i;
594	u_int type;
595
596	fp = __db_prinit(NULL);
597
598	switch (TYPE(h)) {
599	case P_DUPLICATE:
600	case P_HASH:
601	case P_IBTREE:
602	case P_INVALID:
603	case P_IRECNO:
604	case P_LBTREE:
605	case P_LRECNO:
606	case P_OVERFLOW:
607		break;
608	default:
609		fprintf(fp, "ILLEGAL PAGE TYPE: page: %lu type: %lu\n",
610		    (u_long)h->pgno, (u_long)TYPE(h));
611		goto bad;
612	}
613
614	for (i = 0; i < NUM_ENT(h); i++) {
615		if (P_ENTRY(h, i) - (u_int8_t *)h < P_OVERHEAD ||
616		    (size_t)(P_ENTRY(h, i) - (u_int8_t *)h) >= set_psize) {
617			fprintf(fp,
618			    "ILLEGAL PAGE OFFSET: indx: %lu of %lu\n",
619			    (u_long)i, (u_long)h->inp[i]);
620			goto bad;
621		}
622		switch (TYPE(h)) {
623		case P_HASH:
624			type = HPAGE_TYPE(h, i);
625			if (type != H_OFFDUP &&
626			    type != H_DUPLICATE &&
627			    type != H_KEYDATA &&
628			    type != H_OFFPAGE) {
629				fprintf(fp, "ILLEGAL HASH TYPE: %lu\n",
630				    (u_long)type);
631				goto bad;
632			}
633			break;
634		case P_IBTREE:
635			bi = GET_BINTERNAL(h, i);
636			if (B_TYPE(bi->type) != B_KEYDATA &&
637			    B_TYPE(bi->type) != B_DUPLICATE &&
638			    B_TYPE(bi->type) != B_OVERFLOW) {
639				fprintf(fp, "ILLEGAL BINTERNAL TYPE: %lu\n",
640				    (u_long)B_TYPE(bi->type));
641				goto bad;
642			}
643			break;
644		case P_IRECNO:
645		case P_LBTREE:
646		case P_LRECNO:
647			break;
648		case P_DUPLICATE:
649			bk = GET_BKEYDATA(h, i);
650			if (B_TYPE(bk->type) != B_KEYDATA &&
651			    B_TYPE(bk->type) != B_DUPLICATE &&
652			    B_TYPE(bk->type) != B_OVERFLOW) {
653				fprintf(fp,
654			    "ILLEGAL DUPLICATE/LBTREE/LRECNO TYPE: %lu\n",
655				    (u_long)B_TYPE(bk->type));
656				goto bad;
657			}
658			break;
659		default:
660			fprintf(fp,
661			    "ILLEGAL PAGE ITEM: %lu\n", (u_long)TYPE(h));
662			goto bad;
663		}
664	}
665	return (0);
666
667bad:	if (die) {
668		abort();
669		/* NOTREACHED */
670	}
671	return (1);
672}
673
674/*
675 * __db_pr --
676 *	Print out a data element.
677 *
678 * PUBLIC: void __db_pr __P((u_int8_t *, u_int32_t));
679 */
680void
681__db_pr(p, len)
682	u_int8_t *p;
683	u_int32_t len;
684{
685	FILE *fp;
686	u_int lastch;
687	int i;
688
689	fp = __db_prinit(NULL);
690
691	fprintf(fp, "len: %3lu", (u_long)len);
692	lastch = '.';
693	if (len != 0) {
694		fprintf(fp, " data: ");
695		for (i = len <= 20 ? len : 20; i > 0; --i, ++p) {
696			lastch = *p;
697			if (isprint(*p) || *p == '\n')
698				fprintf(fp, "%c", *p);
699			else
700				fprintf(fp, "0x%.2x", (u_int)*p);
701		}
702		if (len > 20) {
703			fprintf(fp, "...");
704			lastch = '.';
705		}
706	}
707	if (lastch != '\n')
708		fprintf(fp, "\n");
709}
710
711/*
712 * __db_prdbt --
713 *	Print out a DBT data element.
714 *
715 * PUBLIC: int __db_prdbt __P((DBT *, int, FILE *));
716 */
717int
718__db_prdbt(dbtp, checkprint, fp)
719	DBT *dbtp;
720	int checkprint;
721	FILE *fp;
722{
723	static const char hex[] = "0123456789abcdef";
724	u_int8_t *p;
725	u_int32_t len;
726
727	/*
728	 * !!!
729	 * This routine is the routine that dumps out items in the format
730	 * used by db_dump(1) and db_load(1).  This means that the format
731	 * cannot change.
732	 */
733	if (checkprint) {
734		for (len = dbtp->size, p = dbtp->data; len--; ++p)
735			if (isprint(*p)) {
736				if (*p == '\\' && fprintf(fp, "\\") != 1)
737					return (EIO);
738				if (fprintf(fp, "%c", *p) != 1)
739					return (EIO);
740			} else
741				if (fprintf(fp, "\\%c%c",
742				    hex[(u_int8_t)(*p & 0xf0) >> 4],
743				    hex[*p & 0x0f]) != 3)
744					return (EIO);
745	} else
746		for (len = dbtp->size, p = dbtp->data; len--; ++p)
747			if (fprintf(fp, "%c%c",
748			    hex[(u_int8_t)(*p & 0xf0) >> 4],
749			    hex[*p & 0x0f]) != 2)
750				return (EIO);
751
752	return (fprintf(fp, "\n") == 1 ? 0 : EIO);
753}
754
755/*
756 * __db_proff --
757 *	Print out an off-page element.
758 */
759static void
760__db_proff(vp)
761	void *vp;
762{
763	FILE *fp;
764	BOVERFLOW *bo;
765
766	fp = __db_prinit(NULL);
767
768	bo = vp;
769	switch (B_TYPE(bo->type)) {
770	case B_OVERFLOW:
771		fprintf(fp, "overflow: total len: %4lu page: %4lu\n",
772		    (u_long)bo->tlen, (u_long)bo->pgno);
773		break;
774	case B_DUPLICATE:
775		fprintf(fp, "duplicate: page: %4lu\n", (u_long)bo->pgno);
776		break;
777	}
778}
779
780/*
781 * __db_prflags --
782 *	Print out flags values.
783 *
784 * PUBLIC: void __db_prflags __P((u_int32_t, const FN *, FILE *));
785 */
786void
787__db_prflags(flags, fn, fp)
788	u_int32_t flags;
789	FN const *fn;
790	FILE *fp;
791{
792	const FN *fnp;
793	int found;
794	const char *sep;
795
796	sep = " (";
797	for (found = 0, fnp = fn; fnp->mask != 0; ++fnp)
798		if (LF_ISSET(fnp->mask)) {
799			fprintf(fp, "%s%s", sep, fnp->name);
800			sep = ", ";
801			found = 1;
802		}
803	if (found)
804		fprintf(fp, ")");
805}
806
807/*
808 * __db_psize --
809 *	Get the page size.
810 */
811static void
812__db_psize(mpf)
813	DB_MPOOLFILE *mpf;
814{
815	BTMETA *mp;
816	db_pgno_t pgno;
817
818	set_psize = PSIZE_BOUNDARY - 1;
819
820	pgno = PGNO_METADATA;
821	if (memp_fget(mpf, &pgno, 0, &mp) != 0)
822		return;
823
824	switch (mp->magic) {
825	case DB_BTREEMAGIC:
826	case DB_HASHMAGIC:
827		set_psize = mp->pagesize;
828		break;
829	}
830	(void)memp_fput(mpf, mp, 0);
831}
832