1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 1997,2008 Oracle.  All rights reserved.
5 *
6 * $Id: BtRecExample.cpp,v 12.6 2008/01/08 20:58:26 bostic Exp $
7 */
8
9#include <sys/types.h>
10
11#include <errno.h>
12#include <iostream>
13#include <iomanip>
14#include <stddef.h>
15#include <stdio.h>
16#include <stdlib.h>
17#include <string.h>
18
19#include <db_cxx.h>
20
21using std::cout;
22using std::cerr;
23
24#define	DATABASE	"access.db"
25#define	WORDLIST	"../test/wordlist"
26
27const char *progname = "BtRecExample";		// Program name.
28
29class BtRecExample
30{
31public:
32	BtRecExample(FILE *fp);
33	~BtRecExample();
34	void run();
35	void stats();
36	void show(const char *msg, Dbt *key, Dbt *data);
37
38private:
39	Db *dbp;
40	Dbc *dbcp;
41};
42
43BtRecExample::BtRecExample(FILE *fp)
44{
45	char *p, *t, buf[1024], rbuf[1024];
46	int ret;
47
48	// Remove the previous database.
49	(void)remove(DATABASE);
50
51	dbp = new Db(NULL, 0);
52
53	dbp->set_error_stream(&cerr);
54	dbp->set_errpfx(progname);
55	dbp->set_pagesize(1024);			// 1K page sizes.
56
57	dbp->set_flags(DB_RECNUM);			// Record numbers.
58	dbp->open(NULL, DATABASE, NULL, DB_BTREE, DB_CREATE, 0664);
59
60	//
61	// Insert records into the database, where the key is the word
62	// preceded by its record number, and the data is the same, but
63	// in reverse order.
64	//
65
66	for (int cnt = 1; cnt <= 1000; ++cnt) {
67		(void)sprintf(buf, "%04d_", cnt);
68		if (fgets(buf + 4, sizeof(buf) - 4, fp) == NULL)
69			break;
70		u_int32_t len = (u_int32_t)strlen(buf);
71		buf[len - 1] = '\0';
72		for (t = rbuf, p = buf + (len - 2); p >= buf;)
73			*t++ = *p--;
74		*t++ = '\0';
75
76		// As a convenience for printing, we include the null terminator
77		// in the stored data.
78		//
79		Dbt key(buf, len);
80		Dbt data(rbuf, len);
81
82		if ((ret = dbp->put(NULL, &key, &data, DB_NOOVERWRITE)) != 0) {
83			dbp->err(ret, "Db::put");
84			if (ret != DB_KEYEXIST)
85				throw DbException(ret);
86		}
87	}
88}
89
90BtRecExample::~BtRecExample()
91{
92	if (dbcp != 0)
93		dbcp->close();
94	dbp->close(0);
95	delete dbp;
96}
97
98//
99// Print out the number of records in the database.
100//
101void BtRecExample::stats()
102{
103	DB_BTREE_STAT *statp;
104
105	dbp->stat(NULL, &statp, 0);
106	cout << progname << ": database contains "
107	     << (u_long)statp->bt_ndata << " records\n";
108
109	// Note: must use free, not delete.
110	// This struct is allocated by C.
111	//
112	free(statp);
113}
114
115void BtRecExample::run()
116{
117	db_recno_t recno;
118	int ret;
119	char buf[1024];
120
121	// Acquire a cursor for the database.
122	dbp->cursor(NULL, &dbcp, 0);
123
124	//
125	// Prompt the user for a record number, then retrieve and display
126	// that record.
127	//
128	for (;;) {
129		// Get a record number.
130		cout << "recno #> ";
131		cout.flush();
132		if (fgets(buf, sizeof(buf), stdin) == NULL)
133			break;
134		recno = atoi(buf);
135
136		//
137		// Start with a fresh key each time,
138		// the dbp->get() routine returns
139		// the key and data pair, not just the key!
140		//
141		Dbt key(&recno, sizeof(recno));
142		Dbt data;
143
144		if ((ret = dbcp->get(&key, &data, DB_SET_RECNO)) != 0) {
145			dbp->err(ret, "DBcursor->get");
146			throw DbException(ret);
147		}
148
149		// Display the key and data.
150		show("k/d\t", &key, &data);
151
152		// Move the cursor a record forward.
153		if ((ret = dbcp->get(&key, &data, DB_NEXT)) != 0) {
154			dbp->err(ret, "DBcursor->get");
155			throw DbException(ret);
156		}
157
158		// Display the key and data.
159		show("next\t", &key, &data);
160
161		//
162		// Retrieve the record number for the following record into
163		// local memory.
164		//
165		data.set_data(&recno);
166		data.set_size(sizeof(recno));
167		data.set_ulen(sizeof(recno));
168		data.set_flags(data.get_flags() | DB_DBT_USERMEM);
169
170		if ((ret = dbcp->get(&key, &data, DB_GET_RECNO)) != 0) {
171			if (ret != DB_NOTFOUND && ret != DB_KEYEMPTY) {
172				dbp->err(ret, "DBcursor->get");
173				throw DbException(ret);
174			}
175		}
176		else {
177			cout << "retrieved recno: " << (u_long)recno << "\n";
178		}
179	}
180
181	dbcp->close();
182	dbcp = NULL;
183}
184
185//
186// show --
187//	Display a key/data pair.
188//
189void BtRecExample::show(const char *msg, Dbt *key, Dbt *data)
190{
191	cout << msg << (char *)key->get_data()
192	     << " : " << (char *)data->get_data() << "\n";
193}
194
195int
196main()
197{
198	FILE *fp;
199
200	// Open the word database.
201	if ((fp = fopen(WORDLIST, "r")) == NULL) {
202		fprintf(stderr, "%s: open %s: %s\n",
203			progname, WORDLIST, db_strerror(errno));
204		return (EXIT_FAILURE);
205	}
206
207	try {
208		BtRecExample app(fp);
209
210		// Close the word database.
211		(void)fclose(fp);
212		fp = NULL;
213
214		app.stats();
215		app.run();
216	}
217	catch (DbException &dbe) {
218		cerr << "Exception: " << dbe.what() << "\n";
219		return (EXIT_FAILURE);
220	}
221
222	return (EXIT_SUCCESS);
223}
224