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