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