db_pr.c
上传用户:romrleung
上传日期:2022-05-23
资源大小:18897k
文件大小:30k
源码类别:

MySQL数据库

开发平台:

Visual C++

  1. /*-
  2.  * See the file LICENSE for redistribution information.
  3.  *
  4.  * Copyright (c) 1996-2002
  5.  * Sleepycat Software.  All rights reserved.
  6.  */
  7. #include "db_config.h"
  8. #ifndef lint
  9. static const char revid[] = "$Id: db_pr.c,v 11.84 2002/09/10 02:45:20 bostic Exp $";
  10. #endif /* not lint */
  11. #ifndef NO_SYSTEM_INCLUDES
  12. #include <sys/types.h>
  13. #include <ctype.h>
  14. #include <stdlib.h>
  15. #include <string.h>
  16. #include <unistd.h>
  17. #endif
  18. #include "db_int.h"
  19. #include "dbinc/db_page.h"
  20. #include "dbinc/db_shash.h"
  21. #include "dbinc/btree.h"
  22. #include "dbinc/hash.h"
  23. #include "dbinc/mp.h"
  24. #include "dbinc/qam.h"
  25. #include "dbinc/db_verify.h"
  26. static int  __db_bmeta __P((DB *, FILE *, BTMETA *, u_int32_t));
  27. static int  __db_hmeta __P((DB *, FILE *, HMETA *, u_int32_t));
  28. static void  __db_meta __P((DB *, DBMETA *, FILE *, FN const *, u_int32_t));
  29. static const char *__db_pagetype_to_string __P((u_int32_t));
  30. static void  __db_prdb __P((DB *, FILE *));
  31. static void  __db_proff __P((void *, FILE *));
  32. static int  __db_prtree __P((DB *, FILE *, u_int32_t));
  33. static int  __db_qmeta __P((DB *, FILE *, QMETA *, u_int32_t));
  34. /*
  35.  * __db_loadme --
  36.  * A nice place to put a breakpoint.
  37.  *
  38.  * PUBLIC: void __db_loadme __P((void));
  39.  */
  40. void
  41. __db_loadme()
  42. {
  43. u_int32_t id;
  44. __os_id(&id);
  45. }
  46. /*
  47.  * __db_dump --
  48.  * Dump the tree to a file.
  49.  *
  50.  * PUBLIC: int __db_dump __P((DB *, char *, char *));
  51.  */
  52. int
  53. __db_dump(dbp, op, name)
  54. DB *dbp;
  55. char *op, *name;
  56. {
  57. FILE *fp;
  58. u_int32_t flags;
  59. int ret;
  60. for (flags = 0; *op != ''; ++op)
  61. switch (*op) {
  62. case 'a':
  63. LF_SET(DB_PR_PAGE);
  64. break;
  65. case 'h':
  66. break;
  67. case 'r':
  68. LF_SET(DB_PR_RECOVERYTEST);
  69. break;
  70. default:
  71. return (EINVAL);
  72. }
  73. if (name == NULL)
  74. fp = stdout;
  75. else {
  76. if ((fp = fopen(name, "w")) == NULL)
  77. return (__os_get_errno());
  78. }
  79. __db_prdb(dbp, fp);
  80. fprintf(fp, "%sn", DB_LINE);
  81. ret = __db_prtree(dbp, fp, flags);
  82. fflush(fp);
  83. if (name != NULL)
  84. fclose(fp);
  85. return (ret);
  86. }
  87. /*
  88.  * __db_inmemdbflags --
  89.  * Call a callback for printing or other handling of strings associated
  90.  * with whatever in-memory DB structure flags are set.
  91.  *
  92.  * PUBLIC: void __db_inmemdbflags __P((u_int32_t, void *,
  93.  * PUBLIC:     void (*)(u_int32_t, const FN *, void *)));
  94.  */
  95. void
  96. __db_inmemdbflags(flags, cookie, callback)
  97. u_int32_t flags;
  98. void *cookie;
  99. void (*callback) __P((u_int32_t, const FN *, void *));
  100. {
  101. static const FN fn[] = {
  102. { DB_AM_CHKSUM, "checksumming" },
  103. { DB_AM_CL_WRITER, "client replica writer" },
  104. { DB_AM_COMPENSATE, "created by compensating transaction" },
  105. { DB_AM_CREATED, "database created" },
  106. { DB_AM_CREATED_MSTR, "encompassing file created" },
  107. { DB_AM_DBM_ERROR, "dbm/ndbm error" },
  108. { DB_AM_DELIMITER, "variable length" },
  109. { DB_AM_DIRTY, "dirty reads" },
  110. { DB_AM_DISCARD, "discard cached pages" },
  111. { DB_AM_DUP, "duplicates" },
  112. { DB_AM_DUPSORT, "sorted duplicates" },
  113. { DB_AM_ENCRYPT, "encrypted" },
  114. { DB_AM_FIXEDLEN, "fixed-length records" },
  115. { DB_AM_INMEM, "in-memory" },
  116. { DB_AM_IN_RENAME, "file is being renamed" },
  117. { DB_AM_OPEN_CALLED, "DB->open called" },
  118. { DB_AM_PAD, "pad value" },
  119. { DB_AM_PGDEF, "default page size" },
  120. { DB_AM_RDONLY, "read-only" },
  121. { DB_AM_RECNUM, "Btree record numbers" },
  122. { DB_AM_RECOVER, "opened for recovery" },
  123. { DB_AM_RENUMBER, "renumber" },
  124. { DB_AM_REVSPLITOFF, "no reverse splits" },
  125. { DB_AM_SECONDARY, "secondary" },
  126. { DB_AM_SNAPSHOT, "load on open" },
  127. { DB_AM_SUBDB, "subdatabases" },
  128. { DB_AM_SWAP, "needswap" },
  129. { DB_AM_TXN, "transactional" },
  130. { DB_AM_VERIFYING, "verifier" },
  131. { 0, NULL }
  132. };
  133. callback(flags, fn, cookie);
  134. }
  135. /*
  136.  * __db_prdb --
  137.  * Print out the DB structure information.
  138.  */
  139. static void
  140. __db_prdb(dbp, fp)
  141. DB *dbp;
  142. FILE *fp;
  143. {
  144. BTREE *bt;
  145. HASH *h;
  146. QUEUE *q;
  147. fprintf(fp,
  148.     "In-memory DB structure:n%s: %#lx",
  149.     __db_dbtype_to_string(dbp->type), (u_long)dbp->flags);
  150. __db_inmemdbflags(dbp->flags, fp, __db_prflags);
  151. fprintf(fp, "n");
  152. switch (dbp->type) {
  153. case DB_BTREE:
  154. case DB_RECNO:
  155. bt = dbp->bt_internal;
  156. fprintf(fp, "bt_meta: %lu bt_root: %lun",
  157.     (u_long)bt->bt_meta, (u_long)bt->bt_root);
  158. fprintf(fp, "bt_maxkey: %lu bt_minkey: %lun",
  159.     (u_long)bt->bt_maxkey, (u_long)bt->bt_minkey);
  160. fprintf(fp, "bt_compare: %#lx bt_prefix: %#lxn",
  161.     P_TO_ULONG(bt->bt_compare), P_TO_ULONG(bt->bt_prefix));
  162. fprintf(fp, "bt_lpgno: %lun", (u_long)bt->bt_lpgno);
  163. if (dbp->type == DB_RECNO) {
  164. fprintf(fp,
  165.     "re_pad: %#lx re_delim: %#lx re_len: %lu re_source: %sn",
  166.     (u_long)bt->re_pad, (u_long)bt->re_delim,
  167.     (u_long)bt->re_len,
  168.     bt->re_source == NULL ? "" : bt->re_source);
  169. fprintf(fp, "re_modified: %d re_eof: %d re_last: %lun",
  170.     bt->re_modified, bt->re_eof, (u_long)bt->re_last);
  171. }
  172. break;
  173. case DB_HASH:
  174. h = dbp->h_internal;
  175. fprintf(fp, "meta_pgno: %lun", (u_long)h->meta_pgno);
  176. fprintf(fp, "h_ffactor: %lun", (u_long)h->h_ffactor);
  177. fprintf(fp, "h_nelem: %lun", (u_long)h->h_nelem);
  178. fprintf(fp, "h_hash: %#lxn", P_TO_ULONG(h->h_hash));
  179. break;
  180. case DB_QUEUE:
  181. q = dbp->q_internal;
  182. fprintf(fp, "q_meta: %lun", (u_long)q->q_meta);
  183. fprintf(fp, "q_root: %lun", (u_long)q->q_root);
  184. fprintf(fp, "re_pad: %#lx re_len: %lun",
  185.     (u_long)q->re_pad, (u_long)q->re_len);
  186. fprintf(fp, "rec_page: %lun", (u_long)q->rec_page);
  187. fprintf(fp, "page_ext: %lun", (u_long)q->page_ext);
  188. break;
  189. default:
  190. break;
  191. }
  192. }
  193. /*
  194.  * __db_prtree --
  195.  * Print out the entire tree.
  196.  */
  197. static int
  198. __db_prtree(dbp, fp, flags)
  199. DB *dbp;
  200. FILE *fp;
  201. u_int32_t flags;
  202. {
  203. DB_MPOOLFILE *mpf;
  204. PAGE *h;
  205. db_pgno_t i, last;
  206. int ret;
  207. mpf = dbp->mpf;
  208. if (dbp->type == DB_QUEUE)
  209. return (__db_prqueue(dbp, fp, flags));
  210. /*
  211.  * Find out the page number of the last page in the database, then
  212.  * dump each page.
  213.  */
  214. mpf->last_pgno(mpf, &last);
  215. for (i = 0; i <= last; ++i) {
  216. if ((ret = mpf->get(mpf, &i, 0, &h)) != 0)
  217. return (ret);
  218. (void)__db_prpage(dbp, h, fp, flags);
  219. if ((ret = mpf->put(mpf, h, 0)) != 0)
  220. return (ret);
  221. }
  222. return (0);
  223. }
  224. /*
  225.  * __db_meta --
  226.  * Print out common metadata information.
  227.  */
  228. static void
  229. __db_meta(dbp, dbmeta, fp, fn, flags)
  230. DB *dbp;
  231. DBMETA *dbmeta;
  232. FILE *fp;
  233. FN const *fn;
  234. u_int32_t flags;
  235. {
  236. DB_MPOOLFILE *mpf;
  237. PAGE *h;
  238. db_pgno_t pgno;
  239. u_int8_t *p;
  240. int cnt, ret;
  241. const char *sep;
  242. mpf = dbp->mpf;
  243. fprintf(fp, "tmagic: %#lxn", (u_long)dbmeta->magic);
  244. fprintf(fp, "tversion: %lun", (u_long)dbmeta->version);
  245. fprintf(fp, "tpagesize: %lun", (u_long)dbmeta->pagesize);
  246. fprintf(fp, "ttype: %lun", (u_long)dbmeta->type);
  247. fprintf(fp, "tkeys: %lutrecords: %lun",
  248.     (u_long)dbmeta->key_count, (u_long)dbmeta->record_count);
  249. if (!LF_ISSET(DB_PR_RECOVERYTEST)) {
  250. /*
  251.  * If we're doing recovery testing, don't display the free
  252.  * list, it may have changed and that makes the dump diff
  253.  * not work.
  254.  */
  255. fprintf(fp, "tfree list: %lu", (u_long)dbmeta->free);
  256. for (pgno = dbmeta->free,
  257.     cnt = 0, sep = ", "; pgno != PGNO_INVALID;) {
  258. if ((ret = mpf->get(mpf, &pgno, 0, &h)) != 0) {
  259. fprintf(fp,
  260.     "Unable to retrieve free-list page: %lu: %sn",
  261.     (u_long)pgno, db_strerror(ret));
  262. break;
  263. }
  264. pgno = h->next_pgno;
  265. (void)mpf->put(mpf, h, 0);
  266. fprintf(fp, "%s%lu", sep, (u_long)pgno);
  267. if (++cnt % 10 == 0) {
  268. fprintf(fp, "n");
  269. cnt = 0;
  270. sep = "t";
  271. } else
  272. sep = ", ";
  273. }
  274. fprintf(fp, "n");
  275. fprintf(fp, "tlast_pgno: %lun", (u_long)dbmeta->last_pgno);
  276. }
  277. if (fn != NULL) {
  278. fprintf(fp, "tflags: %#lx", (u_long)dbmeta->flags);
  279. __db_prflags(dbmeta->flags, fn, fp);
  280. fprintf(fp, "n");
  281. }
  282. fprintf(fp, "tuid: ");
  283. for (p = (u_int8_t *)dbmeta->uid,
  284.     cnt = 0; cnt < DB_FILE_ID_LEN; ++cnt) {
  285. fprintf(fp, "%x", *p++);
  286. if (cnt < DB_FILE_ID_LEN - 1)
  287. fprintf(fp, " ");
  288. }
  289. fprintf(fp, "n");
  290. }
  291. /*
  292.  * __db_bmeta --
  293.  * Print out the btree meta-data page.
  294.  */
  295. static int
  296. __db_bmeta(dbp, fp, h, flags)
  297. DB *dbp;
  298. FILE *fp;
  299. BTMETA *h;
  300. u_int32_t flags;
  301. {
  302. static const FN mfn[] = {
  303. { BTM_DUP, "duplicates" },
  304. { BTM_RECNO, "recno" },
  305. { BTM_RECNUM, "btree:recnum" },
  306. { BTM_FIXEDLEN, "recno:fixed-length" },
  307. { BTM_RENUMBER, "recno:renumber" },
  308. { BTM_SUBDB, "multiple-databases" },
  309. { 0, NULL }
  310. };
  311. __db_meta(dbp, (DBMETA *)h, fp, mfn, flags);
  312. fprintf(fp, "tmaxkey: %lu minkey: %lun",
  313.     (u_long)h->maxkey, (u_long)h->minkey);
  314. if (dbp->type == DB_RECNO)
  315. fprintf(fp, "tre_len: %#lx re_pad: %lun",
  316.     (u_long)h->re_len, (u_long)h->re_pad);
  317. fprintf(fp, "troot: %lun", (u_long)h->root);
  318. return (0);
  319. }
  320. /*
  321.  * __db_hmeta --
  322.  * Print out the hash meta-data page.
  323.  */
  324. static int
  325. __db_hmeta(dbp, fp, h, flags)
  326. DB *dbp;
  327. FILE *fp;
  328. HMETA *h;
  329. u_int32_t flags;
  330. {
  331. static const FN mfn[] = {
  332. { DB_HASH_DUP,  "duplicates" },
  333. { DB_HASH_SUBDB, "multiple-databases" },
  334. { 0,  NULL }
  335. };
  336. int i;
  337. __db_meta(dbp, (DBMETA *)h, fp, mfn, flags);
  338. fprintf(fp, "tmax_bucket: %lun", (u_long)h->max_bucket);
  339. fprintf(fp, "thigh_mask: %#lxn", (u_long)h->high_mask);
  340. fprintf(fp, "tlow_mask:  %#lxn", (u_long)h->low_mask);
  341. fprintf(fp, "tffactor: %lun", (u_long)h->ffactor);
  342. fprintf(fp, "tnelem: %lun", (u_long)h->nelem);
  343. fprintf(fp, "th_charkey: %#lxn", (u_long)h->h_charkey);
  344. fprintf(fp, "tspare points: ");
  345. for (i = 0; i < NCACHED; i++)
  346. fprintf(fp, "%lu ", (u_long)h->spares[i]);
  347. fprintf(fp, "n");
  348. return (0);
  349. }
  350. /*
  351.  * __db_qmeta --
  352.  * Print out the queue meta-data page.
  353.  */
  354. static int
  355. __db_qmeta(dbp, fp, h, flags)
  356. DB *dbp;
  357. FILE *fp;
  358. QMETA *h;
  359. u_int32_t flags;
  360. {
  361. __db_meta(dbp, (DBMETA *)h, fp, NULL, flags);
  362. fprintf(fp, "tfirst_recno: %lun", (u_long)h->first_recno);
  363. fprintf(fp, "tcur_recno: %lun", (u_long)h->cur_recno);
  364. fprintf(fp, "tre_len: %#lx re_pad: %lun",
  365.     (u_long)h->re_len, (u_long)h->re_pad);
  366. fprintf(fp, "trec_page: %lun", (u_long)h->rec_page);
  367. fprintf(fp, "tpage_ext: %lun", (u_long)h->page_ext);
  368. return (0);
  369. }
  370. /*
  371.  * __db_prnpage
  372.  * -- Print out a specific page.
  373.  *
  374.  * PUBLIC: int __db_prnpage __P((DB *, db_pgno_t, FILE *));
  375.  */
  376. int
  377. __db_prnpage(dbp, pgno, fp)
  378. DB *dbp;
  379. db_pgno_t pgno;
  380. FILE *fp;
  381. {
  382. DB_MPOOLFILE *mpf;
  383. PAGE *h;
  384. int ret, t_ret;
  385. mpf = dbp->mpf;
  386. if ((ret = mpf->get(mpf, &pgno, 0, &h)) != 0)
  387. return (ret);
  388. ret = __db_prpage(dbp, h, fp, DB_PR_PAGE);
  389. if ((t_ret = mpf->put(mpf, h, 0)) != 0 && ret == 0)
  390. ret = t_ret;
  391. return (ret);
  392. }
  393. /*
  394.  * __db_prpage
  395.  * -- Print out a page.
  396.  *
  397.  * PUBLIC: int __db_prpage __P((DB *, PAGE *, FILE *, u_int32_t));
  398.  */
  399. int
  400. __db_prpage(dbp, h, fp, flags)
  401. DB *dbp;
  402. PAGE *h;
  403. FILE *fp;
  404. u_int32_t flags;
  405. {
  406. BINTERNAL *bi;
  407. BKEYDATA *bk;
  408. HOFFPAGE a_hkd;
  409. QAMDATA *qp, *qep;
  410. RINTERNAL *ri;
  411. db_indx_t dlen, len, i, *inp;
  412. db_pgno_t pgno;
  413. db_recno_t recno;
  414. u_int32_t pagesize, qlen;
  415. u_int8_t *ep, *hk, *p;
  416. int deleted, ret;
  417. const char *s;
  418. void *sp;
  419. /*
  420.  * If we're doing recovery testing and this page is P_INVALID,
  421.  * assume it's a page that's on the free list, and don't display it.
  422.  */
  423. if (LF_ISSET(DB_PR_RECOVERYTEST) && TYPE(h) == P_INVALID)
  424. return (0);
  425. s = __db_pagetype_to_string(TYPE(h));
  426. if (s == NULL) {
  427. fprintf(fp, "ILLEGAL PAGE TYPE: page: %lu type: %lun",
  428.     (u_long)h->pgno, (u_long)TYPE(h));
  429. return (1);
  430. }
  431. /*
  432.  * !!!
  433.  * Find out the page size.  We don't want to do it the "right" way,
  434.  * by reading the value from the meta-data page, that's going to be
  435.  * slow.  Reach down into the mpool region.
  436.  */
  437. pagesize = (u_int32_t)dbp->mpf->mfp->stat.st_pagesize;
  438. /* Page number, page type. */
  439. fprintf(fp, "page %lu: %s level: %lu",
  440.     (u_long)h->pgno, s, (u_long)h->level);
  441. /* Record count. */
  442. if (TYPE(h) == P_IBTREE ||
  443.     TYPE(h) == P_IRECNO || (TYPE(h) == P_LRECNO &&
  444.     h->pgno == ((BTREE *)dbp->bt_internal)->bt_root))
  445. fprintf(fp, " records: %lu", (u_long)RE_NREC(h));
  446. /* LSN. */
  447. if (!LF_ISSET(DB_PR_RECOVERYTEST))
  448. fprintf(fp, " (lsn.file: %lu lsn.offset: %lu)n",
  449.     (u_long)LSN(h).file, (u_long)LSN(h).offset);
  450. switch (TYPE(h)) {
  451. case P_BTREEMETA:
  452. return (__db_bmeta(dbp, fp, (BTMETA *)h, flags));
  453. case P_HASHMETA:
  454. return (__db_hmeta(dbp, fp, (HMETA *)h, flags));
  455. case P_QAMMETA:
  456. return (__db_qmeta(dbp, fp, (QMETA *)h, flags));
  457. case P_QAMDATA: /* Should be meta->start. */
  458. if (!LF_ISSET(DB_PR_PAGE))
  459. return (0);
  460. qlen = ((QUEUE *)dbp->q_internal)->re_len;
  461. recno = (h->pgno - 1) * QAM_RECNO_PER_PAGE(dbp) + 1;
  462. i = 0;
  463. qep = (QAMDATA *)((u_int8_t *)h + pagesize - qlen);
  464. for (qp = QAM_GET_RECORD(dbp, h, i); qp < qep;
  465.     recno++, i++, qp = QAM_GET_RECORD(dbp, h, i)) {
  466. if (!F_ISSET(qp, QAM_SET))
  467. continue;
  468. fprintf(fp, "%s",
  469.     F_ISSET(qp, QAM_VALID) ? "t" : "       D");
  470. fprintf(fp, "[%03lu] %4lu ", (u_long)recno,
  471.     (u_long)((u_int8_t *)qp - (u_int8_t *)h));
  472. __db_pr(qp->data, qlen, fp);
  473. }
  474. return (0);
  475. }
  476. /* LSN. */
  477. if (LF_ISSET(DB_PR_RECOVERYTEST))
  478. fprintf(fp, " (lsn.file: %lu lsn.offset: %lu)n",
  479.     (u_long)LSN(h).file, (u_long)LSN(h).offset);
  480. s = "t";
  481. if (TYPE(h) != P_IBTREE && TYPE(h) != P_IRECNO) {
  482. fprintf(fp, "%sprev: %4lu next: %4lu",
  483.     s, (u_long)PREV_PGNO(h), (u_long)NEXT_PGNO(h));
  484. s = " ";
  485. }
  486. if (TYPE(h) == P_OVERFLOW) {
  487. fprintf(fp, "%sref cnt: %4lu ", s, (u_long)OV_REF(h));
  488. __db_pr((u_int8_t *)h + P_OVERHEAD(dbp), OV_LEN(h), fp);
  489. return (0);
  490. }
  491. fprintf(fp, "%sentries: %4lu", s, (u_long)NUM_ENT(h));
  492. fprintf(fp, " offset: %4lun", (u_long)HOFFSET(h));
  493. if (TYPE(h) == P_INVALID || !LF_ISSET(DB_PR_PAGE))
  494. return (0);
  495. ret = 0;
  496. inp = P_INP(dbp, h);
  497. for (i = 0; i < NUM_ENT(h); i++) {
  498. if ((db_alignp_t)(P_ENTRY(dbp, h, i) - (u_int8_t *)h) <
  499.     (db_alignp_t)(P_OVERHEAD(dbp)) ||
  500.     (size_t)(P_ENTRY(dbp, h, i) - (u_int8_t *)h) >= pagesize) {
  501. fprintf(fp,
  502.     "ILLEGAL PAGE OFFSET: indx: %lu of %lun",
  503.     (u_long)i, (u_long)inp[i]);
  504. ret = EINVAL;
  505. continue;
  506. }
  507. deleted = 0;
  508. switch (TYPE(h)) {
  509. case P_HASH:
  510. case P_IBTREE:
  511. case P_IRECNO:
  512. sp = P_ENTRY(dbp, h, i);
  513. break;
  514. case P_LBTREE:
  515. sp = P_ENTRY(dbp, h, i);
  516. deleted = i % 2 == 0 &&
  517.     B_DISSET(GET_BKEYDATA(dbp, h, i + O_INDX)->type);
  518. break;
  519. case P_LDUP:
  520. case P_LRECNO:
  521. sp = P_ENTRY(dbp, h, i);
  522. deleted = B_DISSET(GET_BKEYDATA(dbp, h, i)->type);
  523. break;
  524. default:
  525. fprintf(fp,
  526.     "ILLEGAL PAGE ITEM: %lun", (u_long)TYPE(h));
  527. ret = EINVAL;
  528. continue;
  529. }
  530. fprintf(fp, "%s", deleted ? "       D" : "t");
  531. fprintf(fp, "[%03lu] %4lu ", (u_long)i, (u_long)inp[i]);
  532. switch (TYPE(h)) {
  533. case P_HASH:
  534. hk = sp;
  535. switch (HPAGE_PTYPE(hk)) {
  536. case H_OFFDUP:
  537. memcpy(&pgno,
  538.     HOFFDUP_PGNO(hk), sizeof(db_pgno_t));
  539. fprintf(fp,
  540.     "%4lu [offpage dups]n", (u_long)pgno);
  541. break;
  542. case H_DUPLICATE:
  543. /*
  544.  * If this is the first item on a page, then
  545.  * we cannot figure out how long it is, so
  546.  * we only print the first one in the duplicate
  547.  * set.
  548.  */
  549. if (i != 0)
  550. len = LEN_HKEYDATA(dbp, h, 0, i);
  551. else
  552. len = 1;
  553. fprintf(fp, "Duplicates:n");
  554. for (p = HKEYDATA_DATA(hk),
  555.     ep = p + len; p < ep;) {
  556. memcpy(&dlen, p, sizeof(db_indx_t));
  557. p += sizeof(db_indx_t);
  558. fprintf(fp, "tt");
  559. __db_pr(p, dlen, fp);
  560. p += sizeof(db_indx_t) + dlen;
  561. }
  562. break;
  563. case H_KEYDATA:
  564. __db_pr(HKEYDATA_DATA(hk),
  565.     LEN_HKEYDATA(dbp, h, i == 0 ?
  566.     pagesize : 0, i), fp);
  567. break;
  568. case H_OFFPAGE:
  569. memcpy(&a_hkd, hk, HOFFPAGE_SIZE);
  570. fprintf(fp,
  571.     "overflow: total len: %4lu page: %4lun",
  572.     (u_long)a_hkd.tlen, (u_long)a_hkd.pgno);
  573. break;
  574. }
  575. break;
  576. case P_IBTREE:
  577. bi = sp;
  578. fprintf(fp, "count: %4lu pgno: %4lu type: %4lu",
  579.     (u_long)bi->nrecs, (u_long)bi->pgno,
  580.     (u_long)bi->type);
  581. switch (B_TYPE(bi->type)) {
  582. case B_KEYDATA:
  583. __db_pr(bi->data, bi->len, fp);
  584. break;
  585. case B_DUPLICATE:
  586. case B_OVERFLOW:
  587. __db_proff(bi->data, fp);
  588. break;
  589. default:
  590. fprintf(fp, "ILLEGAL BINTERNAL TYPE: %lun",
  591.     (u_long)B_TYPE(bi->type));
  592. ret = EINVAL;
  593. break;
  594. }
  595. break;
  596. case P_IRECNO:
  597. ri = sp;
  598. fprintf(fp, "entries %4lu pgno %4lun",
  599.     (u_long)ri->nrecs, (u_long)ri->pgno);
  600. break;
  601. case P_LBTREE:
  602. case P_LDUP:
  603. case P_LRECNO:
  604. bk = sp;
  605. switch (B_TYPE(bk->type)) {
  606. case B_KEYDATA:
  607. __db_pr(bk->data, bk->len, fp);
  608. break;
  609. case B_DUPLICATE:
  610. case B_OVERFLOW:
  611. __db_proff(bk, fp);
  612. break;
  613. default:
  614. fprintf(fp,
  615.     "ILLEGAL DUPLICATE/LBTREE/LRECNO TYPE: %lun",
  616.     (u_long)B_TYPE(bk->type));
  617. ret = EINVAL;
  618. break;
  619. }
  620. break;
  621. }
  622. }
  623. (void)fflush(fp);
  624. return (ret);
  625. }
  626. /*
  627.  * __db_pr --
  628.  * Print out a data element.
  629.  *
  630.  * PUBLIC: void __db_pr __P((u_int8_t *, u_int32_t, FILE *));
  631.  */
  632. void
  633. __db_pr(p, len, fp)
  634. u_int8_t *p;
  635. u_int32_t len;
  636. FILE *fp;
  637. {
  638. u_int lastch;
  639. int i;
  640. fprintf(fp, "len: %3lu", (u_long)len);
  641. lastch = '.';
  642. if (len != 0) {
  643. fprintf(fp, " data: ");
  644. for (i = len <= 20 ? len : 20; i > 0; --i, ++p) {
  645. lastch = *p;
  646. if (isprint((int)*p) || *p == 'n')
  647. fprintf(fp, "%c", *p);
  648. else
  649. fprintf(fp, "0x%.2x", (u_int)*p);
  650. }
  651. if (len > 20) {
  652. fprintf(fp, "...");
  653. lastch = '.';
  654. }
  655. }
  656. if (lastch != 'n')
  657. fprintf(fp, "n");
  658. }
  659. /*
  660.  * __db_prdbt --
  661.  * Print out a DBT data element.
  662.  *
  663.  * PUBLIC: int __db_prdbt __P((DBT *, int, const char *, void *,
  664.  * PUBLIC:     int (*)(void *, const void *), int, VRFY_DBINFO *));
  665.  */
  666. int
  667. __db_prdbt(dbtp, checkprint, prefix, handle, callback, is_recno, vdp)
  668. DBT *dbtp;
  669. int checkprint;
  670. const char *prefix;
  671. void *handle;
  672. int (*callback) __P((void *, const void *));
  673. int is_recno;
  674. VRFY_DBINFO *vdp;
  675. {
  676. static const char hex[] = "0123456789abcdef";
  677. db_recno_t recno;
  678. u_int32_t len;
  679. int ret;
  680. #define DBTBUFLEN 100
  681. char *p, *hp, buf[DBTBUFLEN], hbuf[DBTBUFLEN];
  682. if (vdp != NULL) {
  683. /*
  684.  * If vdp is non-NULL, we might be the first key in the
  685.  * "fake" subdatabase used for key/data pairs we can't
  686.  * associate with a known subdb.
  687.  *
  688.  * Check and clear the SALVAGE_PRINTHEADER flag;  if
  689.  * it was set, print a subdatabase header.
  690.  */
  691. if (F_ISSET(vdp, SALVAGE_PRINTHEADER))
  692. (void)__db_prheader(NULL, "__OTHER__", 0, 0,
  693.     handle, callback, vdp, 0);
  694. F_CLR(vdp, SALVAGE_PRINTHEADER);
  695. F_SET(vdp, SALVAGE_PRINTFOOTER);
  696. /*
  697.  * Even if the printable flag wasn't set by our immediate
  698.  * caller, it may be set on a salvage-wide basis.
  699.  */
  700. if (F_ISSET(vdp, SALVAGE_PRINTABLE))
  701. checkprint = 1;
  702. }
  703. /*
  704.  * !!!
  705.  * This routine is the routine that dumps out items in the format
  706.  * used by db_dump(1) and db_load(1).  This means that the format
  707.  * cannot change.
  708.  */
  709. if (prefix != NULL && (ret = callback(handle, prefix)) != 0)
  710. return (ret);
  711. if (is_recno) {
  712. /*
  713.  * We're printing a record number, and this has to be done
  714.  * in a platform-independent way.  So we use the numeral in
  715.  * straight ASCII.
  716.  */
  717. (void)__ua_memcpy(&recno, dbtp->data, sizeof(recno));
  718. snprintf(buf, DBTBUFLEN, "%lu", (u_long)recno);
  719. /* If we're printing data as hex, print keys as hex too. */
  720. if (!checkprint) {
  721. for (len = (u_int32_t)strlen(buf), p = buf, hp = hbuf;
  722.     len-- > 0; ++p) {
  723. *hp++ = hex[(u_int8_t)(*p & 0xf0) >> 4];
  724. *hp++ = hex[*p & 0x0f];
  725. }
  726. *hp = '';
  727. ret = callback(handle, hbuf);
  728. } else
  729. ret = callback(handle, buf);
  730. if (ret != 0)
  731. return (ret);
  732. } else if (checkprint) {
  733. for (len = dbtp->size, p = dbtp->data; len--; ++p)
  734. if (isprint((int)*p)) {
  735. if (*p == '\' &&
  736.     (ret = callback(handle, "\")) != 0)
  737. return (ret);
  738. snprintf(buf, DBTBUFLEN, "%c", *p);
  739. if ((ret = callback(handle, buf)) != 0)
  740. return (ret);
  741. } else {
  742. snprintf(buf, DBTBUFLEN, "\%c%c",
  743.     hex[(u_int8_t)(*p & 0xf0) >> 4],
  744.     hex[*p & 0x0f]);
  745. if ((ret = callback(handle, buf)) != 0)
  746. return (ret);
  747. }
  748. } else
  749. for (len = dbtp->size, p = dbtp->data; len--; ++p) {
  750. snprintf(buf, DBTBUFLEN, "%c%c",
  751.     hex[(u_int8_t)(*p & 0xf0) >> 4],
  752.     hex[*p & 0x0f]);
  753. if ((ret = callback(handle, buf)) != 0)
  754. return (ret);
  755. }
  756. return (callback(handle, "n"));
  757. }
  758. /*
  759.  * __db_proff --
  760.  * Print out an off-page element.
  761.  */
  762. static void
  763. __db_proff(vp, fp)
  764. void *vp;
  765. FILE *fp;
  766. {
  767. BOVERFLOW *bo;
  768. bo = vp;
  769. switch (B_TYPE(bo->type)) {
  770. case B_OVERFLOW:
  771. fprintf(fp, "overflow: total len: %4lu page: %4lun",
  772.     (u_long)bo->tlen, (u_long)bo->pgno);
  773. break;
  774. case B_DUPLICATE:
  775. fprintf(fp, "duplicate: page: %4lun", (u_long)bo->pgno);
  776. break;
  777. }
  778. }
  779. /*
  780.  * __db_prflags --
  781.  * Print out flags values.
  782.  *
  783.  * PUBLIC: void __db_prflags __P((u_int32_t, const FN *, void *));
  784.  */
  785. void
  786. __db_prflags(flags, fn, vfp)
  787. u_int32_t flags;
  788. FN const *fn;
  789. void *vfp;
  790. {
  791. FILE *fp;
  792. const FN *fnp;
  793. int found;
  794. const char *sep;
  795. /*
  796.  * We pass the FILE * through a void * so that we can use
  797.  * this function as as a callback.
  798.  */
  799. fp = (FILE *)vfp;
  800. sep = " (";
  801. for (found = 0, fnp = fn; fnp->mask != 0; ++fnp)
  802. if (LF_ISSET(fnp->mask)) {
  803. fprintf(fp, "%s%s", sep, fnp->name);
  804. sep = ", ";
  805. found = 1;
  806. }
  807. if (found)
  808. fprintf(fp, ")");
  809. }
  810. /*
  811.  * __db_dbtype_to_string --
  812.  * Return the name of the database type.
  813.  * PUBLIC: const char * __db_dbtype_to_string __P((DBTYPE));
  814.  */
  815. const char *
  816. __db_dbtype_to_string(type)
  817. DBTYPE type;
  818. {
  819. switch (type) {
  820. case DB_BTREE:
  821. return ("btree");
  822. case DB_HASH:
  823. return ("hash");
  824. case DB_RECNO:
  825. return ("recno");
  826. case DB_QUEUE:
  827. return ("queue");
  828. default:
  829. return ("UNKNOWN TYPE");
  830. }
  831. /* NOTREACHED */
  832. }
  833. /*
  834.  * __db_pagetype_to_string --
  835.  * Return the name of the specified page type.
  836.  */
  837. static const char *
  838. __db_pagetype_to_string(type)
  839. u_int32_t type;
  840. {
  841. char *s;
  842. s = NULL;
  843. switch (type) {
  844. case P_BTREEMETA:
  845. s = "btree metadata";
  846. break;
  847. case P_LDUP:
  848. s = "duplicate";
  849. break;
  850. case P_HASH:
  851. s = "hash";
  852. break;
  853. case P_HASHMETA:
  854. s = "hash metadata";
  855. break;
  856. case P_IBTREE:
  857. s = "btree internal";
  858. break;
  859. case P_INVALID:
  860. s = "invalid";
  861. break;
  862. case P_IRECNO:
  863. s = "recno internal";
  864. break;
  865. case P_LBTREE:
  866. s = "btree leaf";
  867. break;
  868. case P_LRECNO:
  869. s = "recno leaf";
  870. break;
  871. case P_OVERFLOW:
  872. s = "overflow";
  873. break;
  874. case P_QAMMETA:
  875. s = "queue metadata";
  876. break;
  877. case P_QAMDATA:
  878. s = "queue";
  879. break;
  880. default:
  881. /* Just return a NULL. */
  882. break;
  883. }
  884. return (s);
  885. }
  886. /*
  887.  * __db_prheader --
  888.  * Write out header information in the format expected by db_load.
  889.  *
  890.  * PUBLIC: int __db_prheader __P((DB *, char *, int, int, void *,
  891.  * PUBLIC:     int (*)(void *, const void *), VRFY_DBINFO *, db_pgno_t));
  892.  */
  893. int
  894. __db_prheader(dbp, subname, pflag, keyflag, handle, callback, vdp, meta_pgno)
  895. DB *dbp;
  896. char *subname;
  897. int pflag, keyflag;
  898. void *handle;
  899. int (*callback) __P((void *, const void *));
  900. VRFY_DBINFO *vdp;
  901. db_pgno_t meta_pgno;
  902. {
  903. DB_BTREE_STAT *btsp;
  904. DB_ENV *dbenv;
  905. DB_HASH_STAT *hsp;
  906. DB_QUEUE_STAT *qsp;
  907. DBT dbt;
  908. VRFY_PAGEINFO *pip;
  909. char *buf;
  910. int buflen, ret, t_ret;
  911. u_int32_t dbtype;
  912. btsp = NULL;
  913. hsp = NULL;
  914. qsp = NULL;
  915. ret = 0;
  916. buf = NULL;
  917. COMPQUIET(buflen, 0);
  918. if (dbp == NULL)
  919. dbenv = NULL;
  920. else
  921. dbenv = dbp->dbenv;
  922. /*
  923.  * If we've been passed a verifier statistics object, use
  924.  * that;  we're being called in a context where dbp->stat
  925.  * is unsafe.
  926.  *
  927.  * Also, the verifier may set the pflag on a per-salvage basis.
  928.  * If so, respect that.
  929.  */
  930. if (vdp != NULL) {
  931. if ((ret = __db_vrfy_getpageinfo(vdp, meta_pgno, &pip)) != 0)
  932. return (ret);
  933. if (F_ISSET(vdp, SALVAGE_PRINTABLE))
  934. pflag = 1;
  935. } else
  936. pip = NULL;
  937. /*
  938.  * If dbp is NULL, we're being called from inside __db_prdbt,
  939.  * and this is a special subdatabase for "lost" items.  Make it a btree.
  940.  * Otherwise, set dbtype to the appropriate type for the specified
  941.  * meta page, or the type of the dbp.
  942.  */
  943. if (dbp == NULL)
  944. dbtype = DB_BTREE;
  945. else if (pip != NULL)
  946. switch (pip->type) {
  947. case P_BTREEMETA:
  948. if (F_ISSET(pip, VRFY_IS_RECNO))
  949. dbtype = DB_RECNO;
  950. else
  951. dbtype = DB_BTREE;
  952. break;
  953. case P_HASHMETA:
  954. dbtype = DB_HASH;
  955. break;
  956. default:
  957. /*
  958.  * If the meta page is of a bogus type, it's
  959.  * because we have a badly corrupt database.
  960.  * (We must be in the verifier for pip to be non-NULL.)
  961.  * Pretend we're a Btree and salvage what we can.
  962.  */
  963. DB_ASSERT(F_ISSET(dbp, DB_AM_VERIFYING));
  964. dbtype = DB_BTREE;
  965. break;
  966. }
  967. else
  968. dbtype = dbp->type;
  969. if ((ret = callback(handle, "VERSION=3n")) != 0)
  970. goto err;
  971. if (pflag) {
  972. if ((ret = callback(handle, "format=printn")) != 0)
  973. goto err;
  974. } else if ((ret = callback(handle, "format=bytevaluen")) != 0)
  975. goto err;
  976. /*
  977.  * 64 bytes is long enough, as a minimum bound, for any of the
  978.  * fields besides subname.  Subname uses __db_prdbt and therefore
  979.  * does not need buffer space here.
  980.  */
  981. buflen = 64;
  982. if ((ret = __os_malloc(dbenv, buflen, &buf)) != 0)
  983. goto err;
  984. if (subname != NULL) {
  985. snprintf(buf, buflen, "database=");
  986. if ((ret = callback(handle, buf)) != 0)
  987. goto err;
  988. memset(&dbt, 0, sizeof(dbt));
  989. dbt.data = subname;
  990. dbt.size = (u_int32_t)strlen(subname);
  991. if ((ret = __db_prdbt(&dbt,
  992.     1, NULL, handle, callback, 0, NULL)) != 0)
  993. goto err;
  994. }
  995. switch (dbtype) {
  996. case DB_BTREE:
  997. if ((ret = callback(handle, "type=btreen")) != 0)
  998. goto err;
  999. if (pip != NULL) {
  1000. if (F_ISSET(pip, VRFY_HAS_RECNUMS))
  1001. if ((ret =
  1002.     callback(handle, "recnum=1n")) != 0)
  1003. goto err;
  1004. if (pip->bt_maxkey != 0) {
  1005. snprintf(buf, buflen,
  1006.     "bt_maxkey=%lun", (u_long)pip->bt_maxkey);
  1007. if ((ret = callback(handle, buf)) != 0)
  1008. goto err;
  1009. }
  1010. if (pip->bt_minkey != 0 &&
  1011.     pip->bt_minkey != DEFMINKEYPAGE) {
  1012. snprintf(buf, buflen,
  1013.     "bt_minkey=%lun", (u_long)pip->bt_minkey);
  1014. if ((ret = callback(handle, buf)) != 0)
  1015. goto err;
  1016. }
  1017. break;
  1018. }
  1019. if ((ret = dbp->stat(dbp, &btsp, 0)) != 0) {
  1020. dbp->err(dbp, ret, "DB->stat");
  1021. goto err;
  1022. }
  1023. if (F_ISSET(dbp, DB_AM_RECNUM))
  1024. if ((ret = callback(handle, "recnum=1n")) != 0)
  1025. goto err;
  1026. if (btsp->bt_maxkey != 0) {
  1027. snprintf(buf, buflen,
  1028.     "bt_maxkey=%lun", (u_long)btsp->bt_maxkey);
  1029. if ((ret = callback(handle, buf)) != 0)
  1030. goto err;
  1031. }
  1032. if (btsp->bt_minkey != 0 && btsp->bt_minkey != DEFMINKEYPAGE) {
  1033. snprintf(buf, buflen,
  1034.     "bt_minkey=%lun", (u_long)btsp->bt_minkey);
  1035. if ((ret = callback(handle, buf)) != 0)
  1036. goto err;
  1037. }
  1038. break;
  1039. case DB_HASH:
  1040. if ((ret = callback(handle, "type=hashn")) != 0)
  1041. goto err;
  1042. if (pip != NULL) {
  1043. if (pip->h_ffactor != 0) {
  1044. snprintf(buf, buflen,
  1045.     "h_ffactor=%lun", (u_long)pip->h_ffactor);
  1046. if ((ret = callback(handle, buf)) != 0)
  1047. goto err;
  1048. }
  1049. if (pip->h_nelem != 0) {
  1050. snprintf(buf, buflen,
  1051.     "h_nelem=%lun", (u_long)pip->h_nelem);
  1052. if ((ret = callback(handle, buf)) != 0)
  1053. goto err;
  1054. }
  1055. break;
  1056. }
  1057. if ((ret = dbp->stat(dbp, &hsp, 0)) != 0) {
  1058. dbp->err(dbp, ret, "DB->stat");
  1059. goto err;
  1060. }
  1061. if (hsp->hash_ffactor != 0) {
  1062. snprintf(buf, buflen,
  1063.     "h_ffactor=%lun", (u_long)hsp->hash_ffactor);
  1064. if ((ret = callback(handle, buf)) != 0)
  1065. goto err;
  1066. }
  1067. if (hsp->hash_nkeys != 0) {
  1068. snprintf(buf, buflen,
  1069.     "h_nelem=%lun", (u_long)hsp->hash_nkeys);
  1070. if ((ret = callback(handle, buf)) != 0)
  1071. goto err;
  1072. }
  1073. break;
  1074. case DB_QUEUE:
  1075. if ((ret = callback(handle, "type=queuen")) != 0)
  1076. goto err;
  1077. if (vdp != NULL) {
  1078. snprintf(buf,
  1079.     buflen, "re_len=%lun", (u_long)vdp->re_len);
  1080. if ((ret = callback(handle, buf)) != 0)
  1081. goto err;
  1082. break;
  1083. }
  1084. if ((ret = dbp->stat(dbp, &qsp, 0)) != 0) {
  1085. dbp->err(dbp, ret, "DB->stat");
  1086. goto err;
  1087. }
  1088. snprintf(buf, buflen, "re_len=%lun", (u_long)qsp->qs_re_len);
  1089. if ((ret = callback(handle, buf)) != 0)
  1090. goto err;
  1091. if (qsp->qs_re_pad != 0 && qsp->qs_re_pad != ' ') {
  1092. snprintf(buf, buflen, "re_pad=%#xn", qsp->qs_re_pad);
  1093. if ((ret = callback(handle, buf)) != 0)
  1094. goto err;
  1095. }
  1096. if (qsp->qs_extentsize != 0) {
  1097. snprintf(buf, buflen,
  1098.     "extentsize=%lun", (u_long)qsp->qs_extentsize);
  1099. if ((ret = callback(handle, buf)) != 0)
  1100. goto err;
  1101. }
  1102. break;
  1103. case DB_RECNO:
  1104. if ((ret = callback(handle, "type=recnon")) != 0)
  1105. goto err;
  1106. if (pip != NULL) {
  1107. if (F_ISSET(pip, VRFY_IS_RRECNO))
  1108. if ((ret =
  1109.     callback(handle, "renumber=1n")) != 0)
  1110. goto err;
  1111. if (pip->re_len > 0) {
  1112. snprintf(buf, buflen,
  1113.     "re_len=%lun", (u_long)pip->re_len);
  1114. if ((ret = callback(handle, buf)) != 0)
  1115. goto err;
  1116. }
  1117. break;
  1118. }
  1119. if ((ret = dbp->stat(dbp, &btsp, 0)) != 0) {
  1120. dbp->err(dbp, ret, "DB->stat");
  1121. goto err;
  1122. }
  1123. if (F_ISSET(dbp, DB_AM_RENUMBER))
  1124. if ((ret = callback(handle, "renumber=1n")) != 0)
  1125. goto err;
  1126. if (F_ISSET(dbp, DB_AM_FIXEDLEN)) {
  1127. snprintf(buf, buflen,
  1128.     "re_len=%lun", (u_long)btsp->bt_re_len);
  1129. if ((ret = callback(handle, buf)) != 0)
  1130. goto err;
  1131. }
  1132. if (btsp->bt_re_pad != 0 && btsp->bt_re_pad != ' ') {
  1133. snprintf(buf, buflen, "re_pad=%#xn", btsp->bt_re_pad);
  1134. if ((ret = callback(handle, buf)) != 0)
  1135. goto err;
  1136. }
  1137. break;
  1138. case DB_UNKNOWN:
  1139. DB_ASSERT(0); /* Impossible. */
  1140. __db_err(dbp->dbenv, "Impossible DB type in __db_prheader");
  1141. ret = EINVAL;
  1142. goto err;
  1143. }
  1144. if (pip != NULL) {
  1145. if (F_ISSET(pip, VRFY_HAS_DUPS))
  1146. if ((ret = callback(handle, "duplicates=1n")) != 0)
  1147. goto err;
  1148. if (F_ISSET(pip, VRFY_HAS_DUPSORT))
  1149. if ((ret = callback(handle, "dupsort=1n")) != 0)
  1150. goto err;
  1151. /* We should handle page size. XXX */
  1152. } else {
  1153. if (F_ISSET(dbp, DB_AM_CHKSUM))
  1154. if ((ret = callback(handle, "chksum=1n")) != 0)
  1155. goto err;
  1156. if (F_ISSET(dbp, DB_AM_DUP))
  1157. if ((ret = callback(handle, "duplicates=1n")) != 0)
  1158. goto err;
  1159. if (F_ISSET(dbp, DB_AM_DUPSORT))
  1160. if ((ret = callback(handle, "dupsort=1n")) != 0)
  1161. goto err;
  1162. if (!F_ISSET(dbp, DB_AM_PGDEF)) {
  1163. snprintf(buf, buflen,
  1164.     "db_pagesize=%lun", (u_long)dbp->pgsize);
  1165. if ((ret = callback(handle, buf)) != 0)
  1166. goto err;
  1167. }
  1168. }
  1169. if (keyflag && (ret = callback(handle, "keys=1n")) != 0)
  1170. goto err;
  1171. ret = callback(handle, "HEADER=ENDn");
  1172. err: if (pip != NULL &&
  1173.     (t_ret = __db_vrfy_putpageinfo(dbenv, vdp, pip)) != 0 && ret == 0)
  1174. ret = t_ret;
  1175. if (btsp != NULL)
  1176. __os_ufree(dbenv, btsp);
  1177. if (hsp != NULL)
  1178. __os_ufree(dbenv, hsp);
  1179. if (qsp != NULL)
  1180. __os_ufree(dbenv, qsp);
  1181. if (buf != NULL)
  1182. __os_free(dbenv, buf);
  1183. return (ret);
  1184. }
  1185. /*
  1186.  * __db_prfooter --
  1187.  * Print the footer that marks the end of a DB dump.  This is trivial,
  1188.  * but for consistency's sake we don't want to put its literal contents
  1189.  * in multiple places.
  1190.  *
  1191.  * PUBLIC: int __db_prfooter __P((void *, int (*)(void *, const void *)));
  1192.  */
  1193. int
  1194. __db_prfooter(handle, callback)
  1195. void *handle;
  1196. int (*callback) __P((void *, const void *));
  1197. {
  1198. return (callback(handle, "DATA=ENDn"));
  1199. }