3. Documentation Database
3.1. doc_db.cc
#include "table.hh"
#include <vector>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <popt.h>
using namespace libdoc;
void decode_options (int argc, const char **argv);
bool get_record_args (const Table &tab, vector <TableField> &vals);
bool parse_number (const string &s, unsigned int &n);
void print_record (FILE *f, const TableRecord &rec, const Table::Iterator *it);
int command_create (const string &filename);
int command_exists (const string &filename);
int command_find (const string &filename);
int command_header (const string &filename);
int command_insert (const string &filename);
int command_list (const string &filename);
int command_tree (const string &filename);
int command_update (const string &filename);
The name of the porgram, taken from argv[0].
const char *progname;
static int option_create = 0;
static int option_exists = 0;
static const char *option_fields = 0;
static int option_find = 0;
static int option_header = 0;
static Key option_id = 0;
static const char *option_id_str = 0;
static const char *option_primkey = 0;
static const char *option_seckey = 0;
static int option_insert = 0;
static int option_list = 0;
static const char *option_secondary = 0;
static int option_tree = 0;
static int option_update = 0;
static vector <string> leftovers;
The table of command line options.
poptOption options_table[] =
{
POPT_AUTOHELP
{ "create", 'c', POPT_ARG_NONE, &option_create, 0,
"Create a new table in the specified file", 0 },
{ "exists", 'e', POPT_ARG_NONE, &option_exists, 0,
"Exit code is 127 if the given ID is found in the table", 0 },
{ "find", 'f', POPT_ARG_NONE, &option_find, 0,
"Print the record associated with the given key ID", 0 },
{ "fields", 'F', POPT_ARG_STRING, &option_fields, 0,
"Specify the types of the fields, used with -c", "FIELDTYPES" },
{ "header", 'H', POPT_ARG_NONE, &option_header, 0,
"Print information from the header of the table file", 0 },
{ "key", 'k', POPT_ARG_STRING, &option_id_str, 'k',
"Specify an ID number for use as the primary key", "ID" },
{ "primary-key", 'P', POPT_ARG_STRING, &option_primkey, 0,
"Specify the type of primary key to use (AUTO, INT or STRING)",
"TYPENAME" },
{ "secondary-key", 'K', POPT_ARG_STRING, &option_seckey, 0,
"Specify a string ID to look up as the secondary key", "ID" },
{ "insert", 'i', POPT_ARG_NONE, &option_insert, 0,
"Add a new record to a table", 0 },
{ "list", 'l', POPT_ARG_NONE, &option_list, 0,
"Print all the records from a table to stdout", 0 },
{ "secondary", 's', POPT_ARG_STRING, &option_secondary, 0,
"Specify the field number and name of a secondary index", "FIELD:NAME" },
{ "tree", 't', POPT_ARG_NONE, &option_tree, 0,
"Print the contents of the table file as a binary search tree", 0 },
{ "update", 'u', POPT_ARG_NONE, &option_update, 0,
"Alter the specified record", 0 },
{ NULL, 0, 0, NULL, 0, NULL, NULL }
};
Strings used as the names of the types of primary keys. The array indices
match the enum codes.
const char *key_type_names[3] =
{
"integer",
"string",
"automaticly generated integer"
};
int
main (int argc, const char **argv)
{
decode_options (argc, argv);
int exitcode = 0;
try
{
string filename;
if (leftovers.size() ≥ 1 and strcmp (leftovers[0].c_str(), "-") ≠ 0)
filename = leftovers[0];
if (option_create)
exitcode = command_create (filename);
else if (option_exists)
exitcode = command_exists (filename);
else if (option_find)
exitcode = command_find (filename);
else if (option_header)
exitcode = command_header (filename);
else if (option_insert)
exitcode = command_insert (filename);
else if (option_list)
exitcode = command_list (filename);
else if (option_tree)
exitcode = command_tree (filename);
else if (option_update)
exitcode = command_update (filename);
else
{
fprintf (stderr, "%s: a command option must be specified.\n",
progname);
exit (1);
}
}
catch (Exception &e)
{
fprintf (stderr, "%s: %s\n", progname, e.explain().c_str());
return 1;
}
return exitcode;
}
Decode the command line options, using the popt library.
void
decode_options (int argc, const char **argv)
{
progname = argv[0];
poptContext context = poptGetContext ("doc_db", argc, argv,
options_table, 0);
int rc;
while ((rc = poptGetNextOpt (context)) > 0)
{
if (rc == 'k')
{
if (!parse_number (option_id_str, option_id))
{
fprintf (stderr, "%s: bad number `%s' as argument to `-k'.\n",
progname, option_id_str);
exit (1);
}
}
else
assert (false);
}
if (rc < -1)
{
fprintf (stderr, "%s: error in command line options.\n", progname);
exit (1);
}
while (poptPeekArg (context))
leftovers.push_back (poptGetArg (context));
poptFreeContext (context);
}
bool
get_record_args (const Table &tab, vector <TableField> &vals)
{
const string &flds = tab.get_field_types();
for (size_t i = 0; i < flds.size(); ++i)
{
TableField fl;
if (leftovers.size() ≤ i + 1)
{
fprintf (stderr, "%s: not enough fields given on command line.\n",
progname);
return false;
}
fl.type = flds[i];
if (fl.type == 'i')
{
if (!parse_number (leftovers[i + 1].c_str(), fl.v_int))
{
fprintf (stderr, "%s: field number %d must be an integer.\n",
progname, (int) i + 1);
return false;
}
}
else if (fl.type == 's')
fl.v_string = new string (leftovers[i + 1]);
else if (fl.type == 'I')
{
const char *s = leftovers[i + 1].c_str();
if (*s == '{')
++s;
fl.v_int_a = new vector <unsigned int>;
unsigned int val = 0;
bool val_used = false;
do
{
if (isdigit (*s))
val = val * 10 + (*s - '0'), val_used = true;
else if ((*s == ',' || *s == '\0') && val_used)
{
fl.v_int_a->push_back (val);
val = 0;
val_used = false;
}
else
{
fprintf (stderr, "%s: field number %d is bad syntax for a list"
" of integers.\n", progname, (int) i + 1);
return false;
}
}
while (*s++);
}
else
assert (false);
vals.push_back (fl);
}
return true;
}
bool
parse_number (const string &s, unsigned int &n)
{
char *endptr;
unsigned long i = strtoul (s.c_str(), &endptr, 0);
if (s.c_str() == endptr or *endptr ≠ '\0')
return false;
n = i;
return true;
}
void
print_record (FILE *f, const TableRecord &rec, const Table::Iterator *it)
{
if (rec.id)
fprintf (f, "%u", rec.id);
else
{
assert (it);
char *p = new char[it->dat.dsize + 1];
strncpy (p, it->dat.dptr, it->dat.dsize);
p[it->dat.dsize] = '\0';
fprintf (f, "%s", p);
delete p;
}
for (size_t i = 0; i < rec.fields.size(); ++i)
{
switch (rec.fields[i].type)
{
case 'i':
fprintf (f, ":%u", rec.fields[i].v_int);
break;
case 's':
fprintf (f, ":%s", rec.fields[i].v_string->c_str());
break;
case 'I':
fputs (":{", f);
for (size_t j = 0; j < rec.fields[i].v_int_a->size(); ++j)
fprintf (f, (j == 0 ? "%u" : ",%u"), (*rec.fields[i].v_int_a)[j]);
fputc ('}', f);
break;
default:
assert (false);
}
}
fputc ('\n', f);
}
int
command_create (const string &filename)
{
PrimKeyType primkey;
if (option_primkey == 0)
primkey = PRIMKEY_AUTO;
else if (strncasecmp (option_primkey, "auto", 4) == 0)
primkey = PRIMKEY_AUTO;
else if (strncasecmp (option_primkey, "int", 3) == 0)
primkey = PRIMKEY_INTEGER;
else if (strncasecmp (option_primkey, "str", 3) == 0)
primkey = PRIMKEY_STRING;
else
{
fprintf (stderr, "%s: bad primary key type `%s'.\n", progname,
option_primkey);
return 1;
}
Table::create (filename, option_fields ? option_fields : "s",
primkey, option_secondary ? option_secondary : "");
return 0;
}
int
command_exists (const string &filename)
{
if (option_id == 0)
{
fprintf (stderr, "%s: ID number required for key.\n", progname);
exit (1);
}
Table *tab = new Table (filename, true);
bool found = tab->exists (option_id);
delete tab;
return found ? 0 : 127;
}
int
command_find (const string &filename)
{
if (option_id == 0 && option_seckey == 0)
{
fprintf (stderr, "%s: `-k' or `-K' required to specify key.\n",
progname);
exit (1);
}
if (option_id && option_seckey)
fprintf (stderr, "%s: warning: ignoring `-K', using `-k' instead.\n",
progname);
Table *tab = new Table (filename, true);
TableRecord rec;
if (option_id)
{
rec.id = option_id;
if (!tab->find (option_id, rec.fields))
{
fprintf (stderr, "No record with primary key `%u' exists.\n",
option_id);
return 127;
}
}
else
{
TableField keyfld;
keyfld.type = 's';
string k = option_seckey;
keyfld.v_string = &k;
if (!tab->find (keyfld, rec))
{
fprintf (stderr, "No record with secondary key `%s' exists.\n",
option_seckey);
return 127;
}
}
print_record (stdout, rec, 0);
delete tab;
return 0;
}
int
command_header (const string &filename)
{
Table *tab = new Table (filename, true);
printf ("Filename: %s\nPrimary key type: %s\nFields: %s\n"
"Root node offset: 0x%lX\nNumber of records: %u\nLength: %u\n",
tab->get_filename().c_str(),
key_type_names[tab->get_primary_key_type()],
tab->get_field_types().c_str(), tab->get_root(),
tab->get_num_recs(), tab->get_length());
if (!tab->get_secondary_filename().empty())
{
printf ("Secondary index: `%s' for field %u\n",
tab->get_secondary_filename().c_str(),
tab->get_secondary_field() + 1);
}
delete tab;
return 0;
}
int
command_insert (const string &filename)
{
int exitcode = 0;
Table *tab = new Table (filename, false);
TableRecord rec;
rec.id = option_id;
if (!get_record_args (*tab, rec.fields))
exitcode = 1;
else if (!tab->insert (rec))
{
fprintf (stderr, "%s: there is already a record with the key `%d'.\n",
progname, (int) rec.id);
exitcode = 7;
}
delete tab;
return exitcode;
}
int
command_list (const string &filename)
{
Table *tab = new Table (filename, true);
Table::Iterator iter;
bool notend = tab->iter_begin (iter);
while (notend)
{
TableRecord rec;
tab->iter_deref (iter, rec);
print_record (stdout, rec, &iter);
notend = tab->iter_next (iter);
}
tab->iter_delete (iter);
delete tab;
return 0;
}
int
command_tree (const string &filename)
{
int exitcode = 0;
Table *tab = new Table (filename, true);
if (tab->get_primary_key_type() == PRIMKEY_STRING)
{
fprintf (stderr, "%s: this table isn't stored as a binary tree.\n",
progname);
exitcode = 23;
}
tab->debugging_tree (stdout);
delete tab;
return exitcode;
}
int
command_update (const string &filename)
{
int exitcode = 0;
Table *tab = new Table (filename, false);
TableRecord rec;
rec.id = option_id;
if (!get_record_args (*tab, rec.fields))
exitcode = 1;
else if (!tab->update (rec))
{
fprintf (stderr, "%s: the record with the key `%d' couldn't be found.\n",
progname, (int) rec.id);
exitcode = 7;
}
delete tab;
return exitcode;
}