source: gs2-extensions/tdb-edit/trunk/src/src/tdbcli-src/tdbcli.cpp@ 25392

Last change on this file since 25392 was 25392, checked in by jmt12, 12 years ago

A new CLI tool for TDB to allow bidirectional pipe ala GDBMServer

  • Property svn:executable set to *
File size: 10.1 KB
Line 
1/**********************************************************************
2 *
3 * tdbcli.cpp --
4 *
5 * Similar to txt2db (GDBM) executable in that you open a pipe to it and
6 * write commands to be applied to a GDBM database. However, unlike txt2db,
7 * this executable allows bidirectional streams (so you need to open the
8 * pipe for both reading and writing). It then supports commands of this
9 * form:
10 *
11 * \[<key>\][+\-\?]
12 * (<value>)?
13 * -{70}
14 *
15 * where: + is for add or update
16 * - is for delete
17 * ? is for lookup
18 *
19 * The aim of this executable is to allow a single, persistent, connection
20 * to a TDB database, accessed through some kind of multithreaded daemon, so
21 * as to support multiple readers and writers and thus parallel collection
22 * building.
23 *
24 *
25 * A component of the Greenstone digital library software
26 * from the New Zealand Digital Library Project at the
27 * University of Waikato, New Zealand.
28 *
29 * Copyright (C) 2012 The New Zealand Digital Library Project
30 *
31 * This program is free software; you can redistribute it and/or modify
32 * it under the terms of the GNU General Public License as published by
33 * the Free Software Foundation; either version 2 of the License, or
34 * (at your option) any later version.
35 *
36 * This program is distributed in the hope that it will be useful,
37 * but WITHOUT ANY WARRANTY; without even the implied warranty of
38 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
39 * GNU General Public License for more details.
40 *
41 * You should have received a copy of the GNU General Public License
42 * along with this program; if not, write to the Free Software
43 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
44 *
45 **********************************************************************/
46
47#if defined(GSDL_USE_OBJECTSPACE)
48# include <ospace\std\iostream>
49#elif defined(GSDL_USE_IOS_H)
50# include <iostream.h>
51#else
52# include <iostream>
53#endif
54
55#include <stdlib.h>
56#include <cstring>
57
58#include "tdb.h"
59#include "text_t.h"
60
61// use the standard namespace
62#if !defined (GSDL_NAMESPACE_BROKEN)
63#if defined(GSDL_USE_OBJECTSPACE)
64using namespace ospace::std;
65#else
66using namespace std;
67#endif
68#endif
69
70void
71printUsage ()
72{
73 cerr << "===== TDB Command Line Interface v1.0 =====" << endl << endl;
74 cerr << "usage: tdbcli <db path> [-empty] [-debug]" << endl << endl;
75 cerr << "where: -empty Clear out any contents of database first (opposite" << endl;
76 cerr << " of the legacy command -append)" << endl;
77 cerr << " -debug Print debug messages" << endl << endl;
78 cerr << "Once the program is running, the first thing it expects is the" << endl;
79 cerr << "path to the TDB database to open, after which you can use the" << endl;
80 cerr << "commands below:" << endl;
81 cerr << " [key]+<newline>value <= adds the pair key:value" << endl;
82 cerr << " [key]<newline>value <= also adds the pair" << endl;
83 cerr << " [key]- <= deleted the pair identified by key" << endl;
84 cerr << " [key]? <= lookup the value for key" << endl;
85 cerr << " [*] <= retrieve a list of keys" << endl;
86 cerr << " [] <= exit " << endl << endl;
87}
88
89int
90main (int argc, char *argv[])
91{
92 if (argc < 2)
93 {
94 printUsage();
95 exit(0);
96 }
97
98 char *dbname = argv[1];
99 bool empty = false;
100 bool debug = false;
101 for (int i = 2; i < argc; i++)
102 {
103 if (strcmp(argv[i], "-empty") == 0)
104 {
105 empty = true;
106 }
107 if (strcmp(argv[i], "-debug") == 0)
108 {
109 debug = true;
110 }
111 }
112
113 // open the database
114 int hash_size = 0;
115 int tdb_flags = TDB_DEFAULT; // Default = 0
116 if (empty)
117 {
118 tdb_flags = TDB_CLEAR_IF_FIRST;
119 }
120 // Disable file IO for testing purposes
121 /*tdb_flags = tdb_flags | TDB_INTERNAL;*/
122 int open_flags = O_RDWR | O_CREAT;
123 int tdb_store_flags = TDB_DEFAULT; // used later when storing
124 TDB_CONTEXT *dbf = tdb_open(dbname, hash_size, tdb_flags, open_flags, 0664);
125 if (!dbf)
126 {
127 cerr << "tdbcli::main() - couldn't create " << dbname << endl;
128 exit (0);
129 }
130
131 // Start reading commands from STDIN
132 bool quit = false;
133 char c;
134 cin.get(c);
135 while (!cin.eof() && !quit)
136 {
137 text_t key = "";
138 bool action_delete = false;
139 bool action_lookup = false;
140 bool action_keys = false;
141 text_t value = "";
142 int num_dashes = 0;
143
144 // Parse out 'key' from [key]\n
145 // scan for first occurrence of [
146 while (!cin.eof() && c != '[')
147 {
148 cin.get(c);
149 }
150 if (!cin.eof())
151 {
152 cin.get(c); // skip [
153 }
154
155 // now look for closing ], building up 'key' as we go
156 while (!cin.eof() && c != ']')
157 {
158 key.push_back ((unsigned char)c);
159 cin.get(c);
160 }
161
162 // Empty key means exit
163 if (key.empty())
164 {
165 quit = true;
166 }
167 else
168 {
169 // * as a key means return a list of keys
170 if (key == "*")
171 {
172 action_keys = true;
173 }
174
175 if (key == "?")
176 {
177 cout << "0.1" << endl;
178 tdb_close (dbf);
179 return 0;
180 }
181
182 // retrieve the command token
183 if (!cin.eof())
184 {
185 cin.get(c);
186 if (c == '+')
187 {
188 // defaults to action_add anyway
189 cin.get(c);
190 }
191 else if (c == '?')
192 {
193 action_lookup = true;
194 cin.get(c);
195 }
196 else if (c == '-')
197 {
198 action_delete = true;
199 cin.get(c);
200 }
201 }
202
203 // Returning the list of all keys (one per line) is of highest priority
204 if (action_keys)
205 {
206 TDB_DATA key_data = tdb_firstkey (dbf);
207 bool first_key = true;
208 while (key_data.dptr != NULL)
209 {
210 if (first_key)
211 {
212 first_key = false;
213 }
214 else
215 {
216 cout << endl;
217 }
218 for (int i = 0; i < key_data.dsize; ++i)
219 {
220 cout << key_data.dptr[i];
221 }
222 /* get next key */
223 TDB_DATA nextkey_data = tdb_nextkey (dbf, key_data);
224 /* free old key's dptr, otherwise causes memory leak */
225 free(key_data.dptr);
226 /* can now safely copy content of nextkey into key */
227 key_data = nextkey_data;
228 }
229 }
230 // if we've been asked for a lookup, retrieve the information for this
231 // key, write to STDOUT, and then continue processing STDIN for further
232 // commands
233 else if (action_lookup)
234 {
235 // convert key to a TDB_DATA datatype
236 TDB_DATA key_data;
237 key_data.dptr = (unsigned char*)key.getcstr();
238 if (key_data.dptr == NULL)
239 {
240 cerr << "NULL key_data.dptr" << endl;
241 exit (0);
242 }
243 key_data.dsize = key.size();
244
245 TDB_DATA value_data;
246 value_data = tdb_fetch (dbf, key_data);
247 for (int i = 0; i < value_data.dsize; ++i)
248 {
249 cout << value_data.dptr[i];
250 }
251 free(value_data.dptr);
252 free(key_data.dptr);
253 }
254 // if we've been asked to delete a key/value pair, do so and then
255 // continue processing STDIN for further commands
256 else if (action_delete)
257 {
258 // convert key to a TDB_DATA datatype
259 TDB_DATA key_data;
260 key_data.dptr = (unsigned char*)key.getcstr();
261 if (key_data.dptr == NULL)
262 {
263 cerr << "NULL key_data.dptr" << endl;
264 exit (0);
265 }
266 key_data.dsize = key.size();
267 // delete the given key
268 if (tdb_delete(dbf, key_data) < 0)
269 {
270 cerr << "tdb_delete returned an error trying to delete key " << key << endl;
271 }
272 free(key_data.dptr);
273 }
274 // Everything else is an add/update. Read in value, action and then
275 // continue processing STDIN for further commands
276 else
277 {
278 // convert key to a TDB_DATA datatype
279 TDB_DATA key_data;
280 key_data.dptr = (unsigned char*)key.getcstr();
281 if (key_data.dptr == NULL)
282 {
283 cerr << "NULL key_data.dptr" << endl;
284 exit (0);
285 }
286 key_data.dsize = key.size();
287
288 // eat up whitespace
289 while (!cin.eof() && (c == '\t' || c == ' ' || c == '\n' || c == '\r'))
290 {
291 cin.get(c);
292 }
293 // parse in value (if any), watching for the 70 dashes that mark the end
294 text_t tmp = "";
295 while (!cin.eof() && (num_dashes < 70))
296 {
297 // - reset number of dashes on newline
298 if (c == '\n')
299 {
300 tmp.push_back ((unsigned char)c);
301 num_dashes = 0;
302 }
303 // Here we are able to process both Windows-specific text files
304 // (containing carriage-return, newline) and Linux text files
305 // (containing only newline characters) by ignoring the Windows'
306 // carriage-return altogether so that we produce a uniform database
307 // file from either system's type of text file.
308 // If we don't ignore the carriage return here, txt.gz files
309 // produced on Windows cause a GS library running on Linux to break.
310 // - reset number of dashes on carriage return
311 else if (c == '\r')
312 {
313 num_dashes = 0;
314 }
315 else if (c == '-')
316 {
317 tmp.push_back ((unsigned char)c);
318 ++num_dashes;
319 }
320 else
321 {
322 value += tmp;
323 value.push_back ((unsigned char)c);
324 tmp = "";
325 num_dashes = 0;
326 }
327 cin.get(c);
328 }
329
330 // convert value to a TDB_DATA datatype
331 TDB_DATA value_data;
332 value_data.dptr = (unsigned char*)value.getcstr();
333 if (value_data.dptr == NULL)
334 {
335 cerr << "NULL value_data.dptr" << endl;
336 exit (0);
337 }
338 value_data.dsize = value.size();
339 // store the value
340 if (tdb_store (dbf, key_data, value_data, tdb_store_flags) < 0)
341 {
342 cerr << "tdb_store returned an error" << endl;
343 exit (0);
344 }
345 // done with value
346 free(value_data.dptr);
347 free(key_data.dptr);
348 }
349 // - always output 70 hyphens when finished
350 cout << endl << "----------------------------------------------------------------------" << endl;
351 // done with key too
352 }
353 }
354 tdb_close (dbf);
355 cout << "Database updated. Goodbye." << endl;
356 return 0;
357}
Note: See TracBrowser for help on using the repository browser.