1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 1996,2008 Oracle. All rights reserved. 5 * 6 * $Id: code_capi.c,v 1.10 2008/01/08 20:58:12 bostic Exp $ 7 */ 8 9#include "db_codegen.h" 10 11static FILE *of; /* Output stream. */ 12 13static void api_c_callback __P((DB_OBJ *)); 14static void api_c_compare __P((DB_OBJ *)); 15static void api_c_data __P((void)); 16static void api_c_db __P((void)); 17static void api_c_env __P((void)); 18static void api_c_header __P((void)); 19static void api_c_public_data __P((void)); 20static void api_c_public_func __P((void)); 21static void api_c_shutdown __P((void)); 22 23int 24api_c(ofname) 25 char *ofname; 26{ 27 DB_OBJ *cur_db; 28 ENV_OBJ *cur_env; 29 30 /* Open the output file. */ 31 if (ofname == NULL) 32 ofname = "application.c"; 33 if ((of = fopen(ofname, "w")) == NULL) { 34 fprintf(stderr, 35 "%s: %s: %s\n", progname, ofname, strerror(errno)); 36 return (1); 37 } 38 39 /* File header. */ 40 api_c_header(); 41 42 /* Public information for the application. */ 43 api_c_public_data(); 44 45 /* Secondary callback functions. */ 46 TAILQ_FOREACH(cur_env, &env_tree, q) 47 TAILQ_FOREACH(cur_db, &cur_env->dbq, q) 48 if (cur_db->primary != NULL) 49 api_c_callback(cur_db); 50 51 /* Integral type comparison functions. */ 52 TAILQ_FOREACH(cur_env, &env_tree, q) 53 TAILQ_FOREACH(cur_db, &cur_env->dbq, q) 54 if (cur_db->key_type != NULL) 55 api_c_compare(cur_db); 56 57 /* Data structures. */ 58 api_c_data(); 59 60 /* Initialization functions. */ 61 api_c_public_func(); 62 63 /* Open the environments. */ 64 api_c_env(); 65 66 /* Open the databases. */ 67 api_c_db(); 68 69 /* Shutdown gracefully. */ 70 api_c_shutdown(); 71 72 return (0); 73} 74 75static void 76api_c_header() 77{ 78 /* Include files. */ 79 fprintf(of, "\ 80#include <sys/types.h>\n\ 81#include <sys/stat.h>\n\ 82\n\ 83#include <errno.h>\n\ 84#include <stdlib.h>\n\ 85#include <string.h>\n\ 86\n\ 87#ifdef _WIN32\n\ 88#include <direct.h>\n\ 89\n\ 90#define\tmkdir(dir, perm)\t_mkdir(dir)\n\ 91#endif\n\ 92\n\ 93#include \"db.h\"\n\n"); 94} 95 96static void 97api_c_public_data() 98{ 99 DB_OBJ *cur_db; 100 ENV_OBJ *cur_env; 101 102 /* 103 * Global handles. 104 * 105 * XXX 106 * Maybe we should put these all into a structure with some 107 * access methods? 108 */ 109 fprintf(of, "\ 110/* Global environment and database handles for use by the application */\n"); 111 TAILQ_FOREACH(cur_env, &env_tree, q) { 112 if (!cur_env->standalone) 113 fprintf(of, 114 "DB_ENV\t*%s_dbenv;\t\t\t/* Database environment handle */\n", 115 cur_env->prefix); 116 TAILQ_FOREACH(cur_db, &cur_env->dbq, q) 117 if (cur_env->standalone) 118 fprintf(of, 119 "DB\t*%s;\t\t\t/* Database handle */\n", 120 cur_db->name); 121 else 122 fprintf(of, 123 "DB\t*%s_%s;\t\t\t/* Database handle */\n", 124 cur_env->prefix, cur_db->name); 125 } 126 127 fprintf(of, "\ 128\n\ 129/* Public functions for use by the application */\n\ 130int bdb_startup(void);\n\ 131int bdb_shutdown(void);\n"); 132} 133 134static void 135api_c_data() 136{ 137 DB_OBJ *cur_db; 138 ENV_OBJ *cur_env; 139 int first; 140 141 fprintf(of, "\ 142\n\ 143/* DB_ENV initialization structures */\n\ 144typedef struct {\n\ 145\tDB_ENV **envpp;\n\ 146\tchar *home;\n\ 147\tu_int32_t gbytes;\n\ 148\tu_int32_t bytes;\n\ 149\tu_int32_t ncache;\n\ 150\tint private;\n\ 151\tint transaction;\n\ 152} env_list_t;\n\ 153static env_list_t env_list[] = {\n"); 154 155 first = 1; 156 TAILQ_FOREACH(cur_env, &env_tree, q) 157 if (!cur_env->standalone) { 158 fprintf(of, 159 "%s\t{ &%s_dbenv, \"%s\", %lu, %lu, %lu, %d, %d", 160 first ? "" : " },\n", 161 cur_env->prefix, 162 cur_env->home, 163 (u_long)cur_env->gbytes, 164 (u_long)cur_env->bytes, 165 (u_long)cur_env->ncache, 166 cur_env->private, 167 cur_env->transaction); 168 first = 0; 169 } 170 fprintf(of, " }\n};\n\n"); 171 172 fprintf(of, "\ 173/* DB initialization structures */\n\ 174typedef struct db_list_t {\n\ 175\tDB_ENV **envpp;\n\ 176\tDB **dbpp;\n\ 177\tchar *name;\n\ 178\tDBTYPE type;\n\ 179\tu_int32_t extentsize;\n\ 180\tu_int32_t pagesize;\n\ 181\tu_int32_t re_len;\n\ 182\tint (*key_compare)(DB *, const DBT *, const DBT *);\n\ 183\tDB **primaryp;\n\ 184\tint (*secondary_callback)(DB *, const DBT *, const DBT *, DBT *);\n\ 185\tint dupsort;\n\ 186\tint recnum;\n\ 187\tint transaction;\n\ 188} db_list_t;\n\ 189static db_list_t db_list[] = {\n"); 190 191 first = 1; 192 TAILQ_FOREACH(cur_env, &env_tree, q) 193 TAILQ_FOREACH(cur_db, &cur_env->dbq, q) { 194 fprintf(of, "\ 195%s\t{ %s%s%s, &%s%s%s, \"%s\", %s, %lu, %lu, %lu,\n\ 196\t\t%s%s%s, %s%s%s%s, %s%s%s, %d, %d, %d", 197 first ? "" : " },\n", 198 cur_env->standalone ? "" : "&", 199 cur_env->standalone ? "NULL" : cur_env->prefix, 200 cur_env->standalone ? "" : "_dbenv", 201 cur_env->prefix == NULL ? 202 cur_db->name : cur_env->prefix, 203 cur_env->prefix == NULL ? "" : "_", 204 cur_env->prefix == NULL ? "" : cur_db->name, 205 cur_db->name, 206 cur_db->dbtype, 207 (u_long)cur_db->extentsize, 208 (u_long)cur_db->pagesize, 209 (u_long)cur_db->re_len, 210 cur_db->key_type == NULL ? "NULL" : "bdb_", 211 cur_db->key_type == NULL ? "" : cur_db->key_type, 212 cur_db->key_type == NULL ? "" : "_compare", 213 cur_db->primary == NULL ? "NULL" : "&", 214 cur_db->primary == NULL ? "" : cur_env->prefix, 215 cur_db->primary == NULL ? "" : "_", 216 cur_db->primary == NULL ? "" : cur_db->primary, 217 cur_db->primary == NULL ? "NULL" : "bdb_", 218 cur_db->primary == NULL ? "" : cur_db->name, 219 cur_db->primary == NULL ? "" : "_callback", 220 cur_db->dupsort, 221 cur_db->recnum, 222 cur_db->transaction); 223 first = 0; 224 } 225 fprintf(of, " }\n};\n\n"); 226} 227 228static void 229api_c_public_func() 230{ 231 fprintf(of, "\ 232#ifdef BUILD_STANDALONE\n\ 233int\n\ 234main()\n\ 235{\n\ 236\treturn (bdb_startup() && bdb_shutdown() ? EXIT_FAILURE : EXIT_SUCCESS);\n\ 237}\n\ 238#endif\n\n"); 239 240 /* Function prototypes. */ 241 fprintf(of, "\ 242static int bdb_env_startup(env_list_t *);\n\ 243static int bdb_env_shutdown(env_list_t *);\n\ 244static int bdb_db_startup(db_list_t *);\n\ 245static int bdb_db_shutdown(db_list_t *);\n\ 246\n"); 247 248 fprintf(of, "\ 249/*\n\ 250 * bdb_startup --\n\ 251 *\tStart up the environments and databases.\n\ 252 */\n\ 253int\nbdb_startup()\n{\n\ 254\tu_int i;\n\ 255\n\ 256\t/* Open environments. */\n\ 257\tfor (i = 0; i < sizeof(env_list) / sizeof(env_list[0]); ++i)\n\ 258\t\tif (bdb_env_startup(&env_list[i]))\n\ 259\t\t\treturn (1);\n\ 260\t/* Open primary databases. */\n\ 261\tfor (i = 0; i < sizeof(db_list) / sizeof(db_list[0]); ++i)\n\ 262\t\tif (db_list[i].primaryp == NULL &&\n\ 263\t\t bdb_db_startup(&db_list[i]))\n\ 264\t\t\treturn (1);\n\ 265\t/* Open secondary databases. */\n\ 266\tfor (i = 0; i < sizeof(db_list) / sizeof(db_list[0]); ++i)\n\ 267\t\tif (db_list[i].primaryp != NULL &&\n\ 268\t\t bdb_db_startup(&db_list[i]))\n\ 269\t\t\treturn (1);\n\ 270\treturn (0);\n\ 271}\n"); 272 273 fprintf(of, "\ 274\n\ 275/*\n\ 276 * bdb_shutdown --\n\ 277 *\tShut down the environments and databases.\n\ 278 */\n\ 279int\nbdb_shutdown()\n{\n\ 280\tu_int i;\n\ 281\n\ 282\t/* Close secondary databases. */\n\ 283\tfor (i = 0; i < sizeof(db_list) / sizeof(db_list[0]); ++i)\n\ 284\t\tif (db_list[i].primaryp != NULL &&\n\ 285\t\t bdb_db_shutdown(&db_list[i]))\n\ 286\t\t\treturn (1);\n\ 287\t/* Close primary databases. */\n\ 288\tfor (i = 0; i < sizeof(db_list) / sizeof(db_list[0]); ++i)\n\ 289\t\tif (db_list[i].primaryp == NULL &&\n\ 290\t\t bdb_db_shutdown(&db_list[i]))\n\ 291\t\t\treturn (1);\n\ 292\t/* Close environments. */\n\ 293\tfor (i = 0; i < sizeof(env_list) / sizeof(env_list[0]); ++i)\n\ 294\t\tif (bdb_env_shutdown(&env_list[i]))\n\ 295\t\t\treturn (1);\n\ 296\treturn (0);\n\ 297}\n"); 298} 299 300static void 301api_c_env() 302{ 303 fprintf(of, "\ 304\n\ 305static int\nbdb_env_startup(env_list_t *ep)\n{\n\ 306\tstruct stat sb;\n\ 307\tDB_ENV *dbenv;\n\ 308\tu_int32_t open_flags;\n\ 309\tint ret;\n\ 310\n\ 311\t/*\n\ 312\t * If the directory doesn't exist, create it with permissions limited\n\ 313\t * to the owner. Assume errors caused by the directory not existing;\n\ 314\t * we'd like to avoid interpreting system errors and it won't hurt to\n\ 315\t * attempt to create an existing directory.\n\ 316\t *\n\ 317\t * !!!\n\ 318\t * We use octal for the permissions, nothing else is portable.\n\ 319\t */\n\ 320\tif (stat(ep->home, &sb) != 0)\n\ 321\t\t(void)mkdir(ep->home, 0700);\n\ 322\n\ 323\t/*\n\ 324\t * If the environment is not transactional, remove and re-create it.\n\ 325\t */\n\ 326\tif (!ep->transaction) {\n\ 327\t\tif ((ret = db_env_create(&dbenv, 0)) != 0) {\n\ 328\t\t\tfprintf(stderr, \"db_env_create: %%s\", db_strerror(ret));\n\ 329\t\t\treturn (1);\n\ 330\t\t}\n\ 331\t\tif ((ret = dbenv->remove(dbenv, ep->home, DB_FORCE)) != 0) {\n\ 332\t\t\tdbenv->err(dbenv, ret,\n\ 333\t\t\t \"DB_ENV->remove: %%s\", ep->home);\n\ 334\t\t\tgoto err;\n\ 335\t\t}\n\ 336\t}\n\n"); 337 338 fprintf(of, "\ 339\t/*\n\ 340\t * Create the DB_ENV handle and initialize error reporting.\n\ 341\t */\n\ 342\tif ((ret = db_env_create(&dbenv, 0)) != 0) {\n\ 343\t\tfprintf(stderr, \"db_env_create: %%s\", db_strerror(ret));\n\ 344\t\treturn (1);\n\ 345\t}\n"); 346 347 fprintf(of, "\ 348\tdbenv->set_errpfx(dbenv, ep->home);\n\ 349\tdbenv->set_errfile(dbenv, stderr);\n\n"); 350 351 fprintf(of, "\ 352\t /* Configure the cache size. */\n\ 353\tif ((ep->gbytes != 0 || ep->bytes != 0) &&\n\ 354\t (ret = dbenv->set_cachesize(dbenv,\n\ 355\t ep->gbytes, ep->bytes, ep->ncache)) != 0) {\n\ 356\t\tdbenv->err(dbenv, ret, \"DB_ENV->set_cachesize\");\n\ 357\t\tgoto err;\n\ 358\t}\n"); 359 360 fprintf(of, "\ 361\n\ 362\t/*\n\ 363\t * Open the environment.\n\ 364\t */\n\ 365\topen_flags = DB_CREATE | DB_INIT_MPOOL | DB_THREAD;\n\ 366\tif (ep->private)\n\ 367\t open_flags |= DB_PRIVATE;\n\ 368\tif (ep->transaction)\n\ 369\t open_flags |= DB_INIT_LOCK |\n\ 370\t DB_INIT_LOG | DB_INIT_TXN | DB_RECOVER;\n\ 371\tif ((ret = dbenv->open(dbenv, ep->home, open_flags, 0)) != 0) {\n\ 372\t dbenv->err(dbenv, ret, \"DB_ENV->open: %%s\", ep->home);\n\ 373\t goto err;\n\ 374\t}\n\ 375\n\ 376\t*ep->envpp = dbenv;\n\ 377\treturn (0);\n\ 378\n\ 379err:\t(void)dbenv->close(dbenv, 0);\n\ 380\treturn (1);\n\ 381}"); 382} 383 384static void 385api_c_db() 386{ 387 fprintf(of, "\ 388\n\ 389\nstatic int\nbdb_db_startup(db_list_t *dp)\n\ 390{\n\ 391\tDB_ENV *dbenv;\n\ 392\tDB *dbp;\n\ 393\tint ret;\n\ 394\n\ 395\tdbenv = dp->envpp == NULL ? NULL : *dp->envpp;\n\ 396\n\ 397\t/*\n\ 398\t * If the database is not transactional, remove it and re-create it.\n\ 399\t */\n\ 400\tif (!dp->transaction) {\n\ 401\t\tif ((ret = db_create(&dbp, dbenv, 0)) != 0) {\n\ 402\t\t\tif (dbenv == NULL)\n\ 403\t\t\t\tfprintf(stderr,\n\ 404\t\t\t\t \"db_create: %%s\\n\", db_strerror(ret));\n\ 405\t\t\telse\n\ 406\t\t\t\tdbenv->err(dbenv, ret, \"db_create\");\n\ 407\t\t\treturn (1);\n\ 408\t\t}\n\ 409\t\tif ((ret = dbp->remove(\n\ 410\t\t dbp, dp->name, NULL, 0)) != 0 && ret != ENOENT) {\n\ 411\t\t\tif (dbenv == NULL)\n\ 412\t\t\t\tfprintf(stderr,\n\ 413\t\t\t\t \"DB->remove: %%s: %%s\\n\",\n\ 414\t\t\t\t dp->name, db_strerror(ret));\n\ 415\t\t\telse\n\ 416\t\t\t\tdbenv->err(\n\ 417\t\t\t\t dbenv, ret, \"DB->remove: %%s\", dp->name);\n\ 418\t\t\treturn (1);\n\ 419\t\t}\n\ 420\t}\n"); 421 422 fprintf(of, "\ 423\n\ 424\tif ((ret = db_create(&dbp, dbenv, 0)) != 0) {\n\ 425\t\tif (dbenv == NULL)\n\ 426\t\t\tfprintf(stderr, \"db_create: %%s\\n\", db_strerror(ret));\n\ 427\t\telse\n\ 428\t\t\tdbenv->err(dbenv, ret, \"db_create\");\n\ 429\t\treturn (1);\n\ 430\t}\n\ 431\tif (dbenv == NULL) {\n\ 432\t\tdbp->set_errpfx(dbp, dp->name);\n\ 433\t\tdbp->set_errfile(dbp, stderr);\n\ 434\t}\n"); 435 436 fprintf(of, "\ 437\n\ 438\tif (dp->dupsort && (ret = dbp->set_flags(dbp, DB_DUPSORT)) != 0) {\n\ 439\t\tdbp->err(dbp, ret, \"DB->set_flags: DB_DUPSORT: %%s\", dp->name);\n\ 440\t\tgoto err;\n\ 441\t}\n"); 442 443 fprintf(of, "\ 444\n\ 445\tif (dp->recnum && (ret = dbp->set_flags(dbp, DB_RECNUM)) != 0) {\n\ 446\t\tdbp->err(dbp, ret, \"DB->set_flags: DB_RECNUM: %%s\", dp->name);\n\ 447\t\tgoto err;\n\ 448\t}\n"); 449 450 fprintf(of, "\ 451\n\ 452\tif (dp->extentsize != 0 &&\n\ 453\t (ret = dbp->set_q_extentsize(dbp, dp->extentsize)) != 0) {\n\ 454\t\tdbp->err(dbp, ret,\n\ 455\t\t \"DB->set_q_extentsize: %%lu: %%s\",\n\ 456\t\t (u_long)dp->extentsize, dp->name);\n\ 457\t\tgoto err;\n\ 458\t}\n"); 459 460 fprintf(of, "\ 461\n\ 462\tif (dp->pagesize != 0 &&\n\ 463\t (ret = dbp->set_pagesize(dbp, dp->pagesize)) != 0) {\n\ 464\t\tdbp->err(dbp, ret,\n\ 465\t\t \"DB->set_pagesize: %%lu: %%s\",\n\ 466\t\t (u_long)dp->pagesize, dp->name);\n\ 467\t\tgoto err;\n\ 468\t}\n"); 469 470 fprintf(of, "\ 471\n\ 472\tif (dp->re_len != 0 &&\n\ 473\t (ret = dbp->set_re_len(dbp, dp->re_len)) != 0) {\n\ 474\t\tdbp->err(dbp, ret,\n\ 475\t\t \"DB->set_re_len: %%lu: %%s\",\n\ 476\t\t (u_long)dp->re_len, dp->name);\n\ 477\t\tgoto err;\n\ 478\t}\n"); 479 480 fprintf(of, "\ 481\n\ 482\tif (dp->key_compare != NULL &&\n\ 483\t (ret = dbp->set_bt_compare(dbp, dp->key_compare)) != 0) {\n\ 484\t\tdbp->err(dbp, ret, \"DB->set_bt_compare\");\n\ 485\t\tgoto err;\n\ 486\t}\n"); 487 488 fprintf(of, "\ 489\n\ 490\tif ((ret = dbp->open(dbp, NULL, dp->name, NULL, dp->type,\n\ 491\t (dp->transaction ? DB_AUTO_COMMIT : 0) |\n\ 492\t DB_CREATE | DB_THREAD, 0)) != 0) {\n\ 493\t\tdbp->err(dbp, ret, \"DB->open: %%s\", dp->name);\n\ 494\t\tgoto err;\n\ 495\t}\n"); 496 497 fprintf(of, "\ 498\n\ 499\tif (dp->primaryp != NULL &&\n\ 500\t (ret = dbp->associate(*dp->primaryp,\n\ 501\t NULL, dbp, dp->secondary_callback, DB_CREATE)) != 0) {\n\ 502\t\tdbp->err(dbp, ret, \"DB->associate: %%s\", dp->name);\n\ 503\t\tgoto err;\n\ 504\t}\n"); 505 506 fprintf(of, "\ 507\n\ 508\t*dp->dbpp = dbp;\n\ 509\treturn (0);\n\ 510\nerr:\t(void)dbp->close(dbp, 0);\n\ 511\treturn (1);\n\ 512}\n"); 513} 514 515static void 516api_c_callback(cur_db) 517 DB_OBJ *cur_db; 518{ 519 fprintf(of, "\ 520\n\ 521static int\nbdb_%s_callback(DB *secondary, const DBT *key, const DBT *data , DBT *result)\n\ 522{\n", cur_db->name); 523 524 if (cur_db->custom) { 525 fprintf(of, "\ 526\tsecondary->errx(secondary,\n\ 527\t \"%s: missing callback comparison function\");\n\ 528\treturn (DB_DONOTINDEX);\n\ 529}\n", cur_db->name); 530 fprintf(stderr, 531 "Warning: you must write a comparison function for the %s database\n", 532 cur_db->name); 533 } else 534 fprintf(of, "\ 535\tresult->data = &((u_int8_t *)data->data)[%d];\n\ 536\tresult->size = %d;\n\ 537\treturn (0);\n\ 538}\n", cur_db->secondary_off, cur_db->secondary_len); 539} 540 541static void 542api_c_compare(cur_db) 543 DB_OBJ *cur_db; 544{ 545 DB_OBJ *t_db; 546 ENV_OBJ *t_env; 547 548 /* 549 * Check to see if we've already written this one. 550 * 551 * !!! 552 * N^2, but like I care. 553 */ 554 TAILQ_FOREACH(t_env, &env_tree, q) 555 TAILQ_FOREACH(t_db, &t_env->dbq, q) { 556 if (t_db == cur_db) 557 goto output; 558 if (t_db->key_type == NULL) 559 continue; 560 if (strcasecmp(t_db->key_type, cur_db->key_type) == 0) 561 return; 562 } 563 564 /* NOTREACHED */ 565 return; 566 567output: fprintf(of, "\ 568\n\ 569static int bdb_%s_compare(DB *, const DBT *, const DBT *);\n\ 570\n\ 571static int\nbdb_%s_compare(DB *dbp, const DBT *a, const DBT *b)\n\ 572{\n\ 573\t%s ai, bi;\n\ 574\n\ 575\tmemcpy(&ai, a->data, sizeof(ai));\n\ 576\tmemcpy(&bi, b->data, sizeof(bi));\n\ 577\treturn (ai < bi ? -1 : (ai > bi ? 1 : 0));\n\ 578}\n", cur_db->key_type, cur_db->key_type, cur_db->key_type); 579} 580 581static void 582api_c_shutdown() 583{ 584 fprintf(of, "\ 585\n\ 586static int\nbdb_env_shutdown(env_list_t *ep)\n\ 587{\n\ 588\tDB_ENV *dbenv;\n\ 589\tint ret;\n\ 590\n\ 591\tdbenv = ep->envpp == NULL ? NULL : *ep->envpp;\n\ 592\tret = 0;\n\ 593\n\ 594\tif (dbenv != NULL && (ret = dbenv->close(dbenv, 0)) != 0)\n\ 595\t\tfprintf(stderr,\n\ 596\t\t \"DB_ENV->close: %%s: %%s\\n\", ep->home, db_strerror(ret));\n\ 597\treturn (ret == 0 ? 0 : 1);\n\ 598}\n"); 599 600 fprintf(of, "\ 601\n\ 602static int\nbdb_db_shutdown(db_list_t *dp)\n\ 603{\n\ 604\tDB_ENV *dbenv;\n\ 605\tDB *dbp;\n\ 606\tint ret;\n\ 607\n\ 608\tdbenv = dp->envpp == NULL ? NULL : *dp->envpp;\n\ 609\tdbp = *dp->dbpp;\n\ 610\tret = 0;\n\ 611\n\ 612\t/*\n\ 613\t * If the database is transactionally protected, close without writing;\n\ 614\t * dirty pages; otherwise, flush dirty pages to disk.\n\ 615\t */\n\ 616\tif (dbp != NULL &&\n\ 617\t (ret = dbp->close(dbp, dp->transaction ? DB_NOSYNC : 0)) != 0) {\n\ 618\t\tif (dbenv == NULL)\n\ 619\t\t\tfprintf(stderr,\n\ 620\t\t\t \"DB->close: %%s: %%s\\n\", dp->name, db_strerror(ret));\n\ 621\t\telse\n\ 622\t\t\tdbenv->err(dbenv, ret, \"DB->close: %%s\", dp->name);\n\ 623\t}\n\ 624\treturn (ret == 0 ? 0 : 1);\n\ 625}\n"); 626} 627