pdb Documentation
The P Database
v0.1
 
Contents

 
pdb Structures

A database structure
			struct pdb {
				struct pdb_node_t* data;		/*	the root node					*/
				char* file;				/*	the file name of the loaded database		*/
				int altered;				/*	was the database altered since last write?	*/
				long last_write;			/*	the time of the last disk write			*/
				int write_interval;			/*	in seconds					*/
				int settings;				/*	the enabled settings				*/
				pthread_mutex_t* mutex;			/*	local mutex lock for the thread safe feature 	*/
			};


A database node
			struct pdb_node_t {
				int type;				/*	the type of node it is		*/
				char* id;				/*	the node id (or key)		*/
				void* data;				/*	the data stored at this node	*/
				struct pdb_node_t* parent;		/*	the parent of this node		*/
				custom_free_cb_t custom_free_cb;	/*	custom free callback		*/
			};
 
pdb File Format

			pdb stores its structure to disk in a tree-like format.

			Each string literal is encapsulated within two double quotes, such that
			the string literal foo is represented in the file as "foo".
			
			A token is a string of characters which defines a new node that exists
			within the database. For example, code section 1.0 is a token.
				
Code 1.0
				"foo" "bar"
				
Tokens can either end with a token terminator (PDB_TOKEN_TERM defined in pdb.h), which is by default a semicolon (;), or a new line. It is generally a good idea to end all tokens with a terminator. Code section 1.0 would yield code section 1.1.
Code 1.1
				"foo" "bar";
				
The example in code section 1.1 is in a 1:1 radio. That is, there is a key and a data element, "foo" and "bar" respectively. It can be said that "foo" is set to "bar". Some string nodes do not require a data element, it is recommended to end these strings with a token terminator. For example, code section 1.2 is a string node "foo" as if it were defined with no data.
Code 1.2
				"foo";
				
pdb would consider "foo" to have no data, the data pointer for this node would be set to NULL. The following node types support disk writing and follow the token terminator rule: STRING_NODE_TYPE - a string INT_NODE_TYPE - an integer The rule of using a token terminator to end all tokens is not true for any container that has a greater data:key radio than 1:1. The following node types support disk writing and do not follow the token terminator rule: TREE_NODE_TYPE - binary tree LIST_NODE_TYPE - linked list HASH_NODE_TYPE - hash table The above use their own token to define the bounds of their scope in the file and in the structure. They only have one key, but since they can have multipule data members, the children are grouped within bounds. For example, an empty binary tree called "btree" may be defined as in code secton 1.3.
Code 1.3
				"btree" {
				}
				
You will notice the key "btree" is followed by an opening bracket, and later a closing bracket - this is the scope of the binary tree container "btree", everything between the two brackets is considered a child of that node. The tailing bracket may have a token terminator preceeding it, but it is not necessary. If it were existant, it would render a null token (a token that does nothing, ""). For example, code section 1.4 defines a string node called "str" that is a child of "btree".
Code 1.4
				"btree" {
					"str" "foobar";
				}
				
Notice "str" is now a child of "btree", and it is set to "foobar". The limit of children a container may have, or the number of children a child container may have, is limited only by the amount of memory the running machine has. There is no limit to the amount of recursive nested children that may exist either. These opening and closing tokens are unique per container type. The following is a list of supported container types of pdb along with their opening and closing tokens:
container type opening token closing token
TREE_NODE_TYPE { }
LIST_NODE_TYPE ( )
HASH_NODE_TYPE [ ]
STRING_NODE_TYPE " "
INT_NODE_TYPE ' '
ABSTRACT_NODE_TYPE No disk support No disk support
LINK_NODE_TYPE No disk support No disk support
Code section 1.5 defines a list of string nodes called "foo".
Code 1.5
				"foo" (
					"a" "A";
					"b" "B";
					"c" "C";
				)
				
Because these string node declarations are utilizing token terminators, the entire list may be defined on a single line, as in code section 1.6.
Code 1.6
				"foo" ("a" "A"; "b" "B"; "c" "C";)
				
This is especially useful if the string nodes contain no data.
Code 1.7
				"foo" ("a"; "b"; "c";)
				
The affect of code section 1.7 is a string node with multipule data entries, even though pdb still treats it as a list of strings. There is no limitation on the type of children any container type may have (given of course the container type is not a string or an integer). Code section 1.8 defines a hash table "foo" with two binary trees, three linked lists, and an integer.
Code 1.8
				"foo" [
					15;
					"btree1" {
					}
					"btree2" {
					}
					"list1" (
					)
					"list2" (
					)
					"list3" ()
					"int1" '1';
				]
				
You will notice two unusual aspects of section 1.8, 1) "list3" has the opening and closing block written consecutively, and 2) the first line of "foo" is 15 with a token terminator. For the first, pdb does not require any data to be present within a container, thus a new line is not required. For the second, a hash table's first line must be the size of the table. This is just one integer, followed by a token terminator. The size it used to create the hash table properly. pdb supports two types of comments. A comment is a token, or a string of tokens, which is ignored during the parsing and loading process from disk to memory. The following are a list of supported comment types, and their bounds:
comment type opening token closing token
Comment an entire line # (new line)
Comment a given block /* */
Consider section 1.9 (which has the lines numbered for convenience).
Code 1.9
			1:	"1_a" {
			2:		"2_a" {
			3:			"3_a" '1';
			4:			"3_b" "asdf";
			5:		}
			6:		"2_b" ("3_a"; "3_b";)
			7:		"2_c" [
			8:			10;
			9:			/*"3_a" "123";
			10:			"3_b" "456";*/
			11:		]
			12:	}
			13:	#"1_b" ()	/* asdf
			14:	/* qwerty */ #"1_c";
			15:	"1_d";;;;;
			
You should notice a few various things about section 1.9. Firstly, as the number of nested children increase, so does the first number of the key. This helps illustrate the tree structure. You should notice there are three individual instances of the key name 3_a, but they are all unique due to their position within the structure. 1_a/2_a/3_a is not the same as 1_a/2_b/3_a. Second, line 3 is an integer; I only mention this because it has not yet been explicitly defined. Third, lines 9 through 10 are commented out by a block comment. Forth, line 13 is commented out by a line comment. There is a second comment on line 13, you may expect this to open a comment block which will span until it is closed, but due to the hash being first, the entire line is ignored, including the opening block comment. Firth, on line 14 there is a small block comment around "qwerty", tailed by a line commenting out the rest of the line. Notice the hash can be anywhere on the line, and it will comment the remaining portion of that line. Lastly, line 15 is a string node with no data defined. We will take a closer look at this line in section 1.10.
Code 1.9
			"1_d";;;;;
			
Section 1.10 defines a single string node with no data. While this is a valid definition of "1_d", it is careless and should be avoided. pdb's tokenizer will read "1_d" and stop at the first token terminator. At this point it will load "1_d" into memory, then continue onward. Since the next character in the file is a token terminator, it will stop and attempt to load what was before it as well. This results in a null token. While it is harmless, and ultimarelly will yield the same structure in memory as if the extra ultimately were not there, it takes unnecessary time to parse them. There are four null tokens in section 1.9, it is wise to avoid them.
 
pdb_load()

Prototype
struct pdb* pdb_load(char* file);
Description
Create and load a database into memory.
Arguments
file - The database file to be loaded.
	If NULL, the database structure will be initialized as if
	a file was specified and was empty.
Return
A pointer to the created database structure.
Example
struct pdb* dbptr = pdb_load("weapons.pdb");
int* glock_clip_size = pdb_query(dbptr, "handheld/glock/clip_size");
printf("The glock clip size is: %i", (glock_clip_size ? *glock_clip_size : 0));
 
pdb_unload()

Prototype
int pdb_unload(struct pdb* dbptr);
Description
Unload the specified database structure from memory.
All children will be recursively unloaded.
Arguments
dbptr - The database structure to be unloaded from memory.
Return
1 on success
0 on failure.
Example
struct pdb* dbptr = pdb_load("weapons.pdb");
/*
 * If this database does not have the key "weap_set", unload it.
 * Otherwise, output the value (it is stored as an integer in the db).
 */
int* set = pdb_query(dbptr, "weap_set");
if (set)
	printf("Weap_set is: %i\n", *set);
else
	printf("Unload database... %s\n",
		(pdb_unload(dbptr) ? "success" : "failed"));
 
pdb_enable()

Prototype
void pdb_enable(struct pdb* dbptr, int settings);
Description
Enable a pdb setting.

Valid settings are:
	PDB_CASE_INSENSITIVE
		Allow case insensitive key searching.
		Ex: pdb_query(dbptr, "FoO/bar"); and db_query(dbptr, "fOO/BAR"); are equivalent.
	PDB_WRITE_SHUFFLE
		Attempt to shuffle the children of supported
		data containers before writing to disk.
	PDB_WRITE_NODE_FIRST
		Attempt to write PDB_WRITE_NODE_FIRST_ID
		(defined in pdb.h) at the top any parent block
		if it exists.  Generally this should not be set
		as the key id is hardcoded.
	PDB_THREAD_SAFE
		Enable thread safe mode.  pdb will use a local mutex
		in attempt to keep the structre safe if shared memory
		may be in use.  This should be enabled if the application
		uses threading.
Arguments
dbptr - A database pointer.
settings - The setting(s) to be enabled.  Settings may be OR'ed togther.
Return
No return.
Example
struct pdb* dbptr = pdb_load("users.pdb");
void* john = pdb_query(dbptr, "carmack/john");
void* carmack = pdb_query(dbptr, "caRMacK/JoHN");
if (john == carmack) {
	/*
	 * This will always be true if PDB_CASE_INSENSITIVE is set.
	 */
	printf("PDB_CASE_INSENSITIVE must be set!\n");
} else {
	/*
	 * PDB_CASE_INSENSITIVE is not set, lets set it and try again.
	 */
	pdb_enable(dbptr, PDB_CASE_INSENSITIVE);
	john = pdb_query(dbptr, "carmack/john");
	carmack = pdb_query(dbptr, "caRMacK/JoHN");
	if (john == carmack) {
		/*
		 * This will always be true.
		 */
		printf("PDB_CASE_INSENSITIVE is now set!\n");
	} else {
		/*
		 * This will never be true.
		 */
		printf("You will never see this line.\n");
	}
}
 
pdb_disable()

Prototype
void pdb_disable(struct pdb* dbptr, int settings);
Description
Disable a pdb setting.
See pdb_enable() for valid settings.
Arguments
dbptr - A database pointer.
settings - The setting(s) to be disabled.  Settings may be OR'ed togther.
Return
No return.
Example
struct pdb* dbptr = pdb_load("users.pdb");
/*
 * We are using pdb in an application which does not need shared memory
 * management (ie not using threads), so lets disable that option, but only
 * if it is already set.
 */
if (pdb_is_set(dbptr, PDB_THREAD_SAFE))
	pdb_disable(dbptr, PDB_THREAD_SAFE);
 
pdb_is_set()

Prototype
int pdb_is_set(struct pdb* dbptr, int setting);
Description
Determine if the specified flag(s) are set for the specified database.
Arguments
dbptr - A database pointer.
settings - The setting(s) to be checked.  Settings may be OR'ed togther.
Return
1 if all specified settings are enabled.
0 if one or more specified settings are disabled.
Example
struct pdb* dbptr = pdb_load("users.pdb");

/*
 * We need to query the database for foobar, and we do not know the exact
 * case in which it is stored, but we still need it (ie, it may be FOObar).
 */
pdb_enable(dbptr, PDB_CASE_INSENSITIVE);
void* f = pdb_query(dbptr, "foobar");

/*
 * For future queries we want the case to be sensitive, so lets disable
 * case insensitive searching.
 */
pdb_disable(dbptr, PDB_CASE_INSENSITIVE);
 
pdb_create_link()

Prototype
int pdb_create_link(struct pdb* dbptr, char* path, char* key, struct pdb_node_t* tnptr);
Description
Similar to a soft link on a Unix machine, this function will create a node which points
to the specified (tnptr) node.
The target node (tnptr) does not have to be in the specified database; it may reside in
another loaded database. This can be used to link an entire database into some other database,
or just reference one node to another.

WARNING
Do not create a link to itself, or to another link which points back; infinite recursion may occur.
A link creation may fail for several reasons. If for example, the link you are trying to make already exists, or there is insufficient system memory to make it, the link may fail.
Arguments
dbptr - A database pointer.
path - The path (excluding the key) to where the link should be made.
key - The link name (this will be stored at path/key in the database).
tnptr - The node in which the created link will point to.
Return
1 if the link was successfully created.
0 if the link could not be created.
Example
struct pdb* dbptr = pdb_load("users.pdb");
struct pdb_node_t* richard = pdb_query_node(dbptr, "names/army/richard");

/*
 * While Richard is his legal name, we like to call him Dick.
 * We make a link from names/army/richard to names/army/dick so
 * we can query either and get the same node.
 */
if (pdb_create_link(dbptr, "names/army", "dick", richard)) {
	/*
	 * The link was successfully added.
	 * To make sure lets query names/army/dick and compare it with
	 * the richard pointer, which was names/army/richard.
	 */
	struct pdb_node_t* dick = pdb_query_node(dbptr, "names/army/dick");
	if (dick == richard) {
		printf("names/army/dick points to names/army/richard\n");
	} else {
		/*
		 * This will never be true.
		 */
		printf("You will never see this line.\n");
	}
} else {
	/*
	 * We were unable to create the link.
	 */
	printf("Link failed.\n");
}
 
pdb_query_node()

Prototype
struct pdb_node_t* pdb_query_node(struct pdb* dbptr, char* path);
Description
Search the specified database structure for the specified node.
Each key in the path is delimited by a slash (/).

Therefore, if there is a node in the root called A, a node in A called B,
and a node in B called C, the path to C is:
	A/B/C

NOTE
This function returns a pointer to the node, not the data at that node.
Arguments
dbptr - A database pointer.
path - The complete path to the node.
Return
A pointer to the specified node.
If the node was not found, NULL will be returned.
Example
struct pdb* dbptr = pdb_load("users.pdb");
/*
 * Is there a John Doe in this database?
 *
 * Assume the database file looks like:
 *
 * "names" {
 *	"foo" {
 *		"bar1" "a";
 *		"bar2" "b";
 *	}
 *	"john" {
 *		"doe" "a";
 *		"dime" "b";
 *	}
 * }
 *
 */
struct pdb_node_t* john = pdb_query_node(dbptr, "names/john/doe");
if (john) {
	printf("John Doe's node exists at memory location %x.\n", john);
} else {
	printf("John Doe is not in the database at that location.");
}
 
pdb_query()

Prototype
void* pdb_query(struct pdb* dbptr, char* path);
Description
Similar to pdb_query_node(), but return a pointer to the data at that node
rather than a pointer to the node itself.

NOTE
This function is equivalent to:
	(pdb_query_node(dbptr, path))->data
assuming it returns a non-NULL pointer.
Arguments
dbptr - A database pointer.
path - The complete path to the node.
Return
A pointer to the specified node's data.
If the node was not found, NULL will be returned.
Example
struct pdb* dbptr = pdb_load("users.pdb");
/*
 * What is John Doe's age?
 *
 * Assume the database file looks like:
 *
 * "names" {
 *	"john" {
 *		"doe" '35';
 *		"dime" '14';
 *	}
 * }
 *
 */
int* age = (int*)pdb_query(dbptr, "names/john/doe");
if (age)
	printf("John Doe is %i years old.\n", *age);
else
	printf("John Doe is not in the database at that location.\n");
 
pdb_set()

Prototype
struct pdb_node_t* pdb_set(struct pdb* dbptr, char* path, char* key, void* data);
Description
Create a node at the location path/key in the specified database.
The type of node created will be of the string container type.

The void pointer should be a pointer to the string which you want to set the
database entry to. This pointer does not have to be allocated, but if it is it can
be subsequently free'ed after pdb_set() completes, as pdb_set() will create a copy
of it in memory for its own use.

If you wish to create a container of a type other than string, use pdb_set_node().
There are also undocumented wrappers for various types defined in pdb_types.c.
These may include:
	struct pdb_node_t* pdb_create_tree(struct pdb* dbptr, char* path, char* key);
	struct pdb_node_t* pdb_create_list(struct pdb* dbptr, char* path, char* key);
	struct pdb_node_t* pdb_create_hash(struct pdb* dbptr, char* path, char* key, int size);
	struct pdb_node_t* pdb_create_abstract(struct pdb* dbptr, char* path, char* key, void* data, void* free_cb);
	struct pdb_node_t* pdb_set_int(struct pdb* dbptr, char* path, char* key, int data);
Arguments
dbptr - A database pointer.
path - The path (excluding the key) to the node that should be set.
key - The node name (this will be stored at path/key in the database).
data - A void pointer to the data that should be stored at that node.
Return
A pointer to the node which was created.
If an error occured, NULL will be returned.
Example
struct pdb* dbptr = pdb_load("websites.pdb");
/*
 * We want to create a node in the database's root called "slashdot"
 * which has the data "http://www.slashdot.org/".
 */
struct pdb_node_t* sd = pdb_set(dbptr, "/", "slashdot", "http://www.slashdot.org/");
if (sd) {
	/*
	 * For confirmation it was added properly (even though we know it is as the
	 * return was non-NULL, query it and compare the pointers.
	 */
	struct pdb_node_t* sd2 = pdb_query_node(dbptr, "slashdot");
	if (sd == sd2)
		printf("Indeed, it was added corrently, and the data is \"%s\"\n", (char*)sd2->data);
	else
		printf("You will never see this line.\n");
} else {
	/*
	 * The node could not be set.
	 */
	printf("An error occured and the node could not be set.\n");
}
 
pdb_set_node()

Prototype
struct pdb_node_t* pdb_set_node(struct pdb* dbptr, char* path, char* key, void* data, int type);
Description
Create a node at the location path/key in the specified database.
The type of node created will be of the type container type.

Valid types are:
	TREE_NODE_TYPE
		A binary tree.
	LIST_NODE_TYPE
		A linked list.
	HASH_NODE_TYPE
		A hash table.
	STRING_NODE_TYPE
		A string.
	INT_NODE_TYPE
		An integer.
	ABSTRACT_NODE_TYPE
		An abstract type.  This can be any type of data you desire.
		See the note below for important information about this container type.
	LINK_NODE_TYPE
		A node which points to another node.
		For additional information on this container type, see pdb_create_link().

It is important to note that the following container types data should not be
manually unallocated after it has been set -- pdb will handle the deallocation of this
memory when the database is unloaded, or the node is free'd:
	ABSTRACT_NODE_TYPE
	LINK_NODE_TYPE

If you are creating one of the following container types, the void pointer data should be NULL:
	TREE_NODE_TYPE
	LIST_NODE_TYPE
	HASH_NODE_TYPE

NOTE
If you are creating a container type of ABSTRACT_NODE_TYPE, you should invoke the function
void pdb_set_free_method(struct pdb* dbptr, struct pdb_node_t* nptr, void* free_cb);
on this node immediately after the node has been successfully set.
Otherwise, when the node is free'd by pdb, the data pointed to by the void data pointer
will not be unallocated! This will result in a memory leak.
Arguments
dbptr - A database pointer.
path - The path (excluding the key) to the node that should be set.
key - The node name (this will be stored at path/key in the database).
data - A void pointer to the data that should be stored at that node.
type - The type of container to create.
Return
A pointer to the node which was created.
If an error occured, NULL will be returned.
Example
struct pdb* dbptr = pdb_load("websites.pdb");
/*
 * Create a binary tree in the database root called "foo_tree".
 */
struct pdb_node_t* bt = pdb_set_node(dbptr, "/", "foo_tree", NULL, TREE_NODE_TYPE);

/*
 * Create a hash table within the binary tree we just created called "hash".
 */
struct pdb_node_t* ht = pdb_set_node(dbptr, "foo_tree", "hash", NULL, HASH_NODE_TYPE);

/*
 * Finally create a string node within the hash table we just created
 * called "foo" with the data "bar".
 */
pdb_set(dbptr, "foo_tree/hash", "foo", "bar");

/*
 * Oops, we forgot to store the location of the "foo" node we just created.
 * Now we need the data at that node, use pdb_query() to obtain it.
 */
char* d = pdb_query(dbptr, "foo_tree/hash/foo");
printf("We set \"foo_tree/hash/foo\" to \"%s\"\n", d);
 
pdb_del()

Prototype
int pdb_del(struct pdb* dbptr, char* path);
Description
Delete the node at the location path/key in the specified database.
This will also recursively free all children of that node (if it has any), and
the data contained at that location.
Arguments
dbptr - A database pointer.
path - The path (including the key) to the node that should be deleted.
Return
1 if the node was successfully deleted from memory.
0 if an error occured while attempting to delete the node.
Example
struct pdb* dbptr = pdb_load(NULL);
/*
 * Assume this database structure is completely empty (this is evident because
 * NULL was passed to pdb_load() for the file name), it has nothing in it.
 * Let us create a string node in the root called "slashdot" with the data
 * "http://www.slashdot.org/".
 */
pdb_set(dbptr, "slashdot", "http://www.slashdot.org/");

/*
 * Query the node and output the data. If printf() says the data is NULL,
 * we can conclude the above function call failed.
 */
printf("Data is \"%s\".\n", pdb_query(dbptr, "slashdot"));

/*
 * Now delete it.
 * Even if pdb_set() failed, we can still safely attempt to delete it,
 * pdb should return 0 telling us it could not find the node to be deleted.
 * But we assume that it was added successfully, and we should get a 1 returned
 * on this line.
 */
int ret = pdb_del(dbptr, "slashdot");
printf("pdb_del() returned %i.\n", ret);
 
pdb_trace()

Prototype
char* pdb_trace(struct pdb_node_t* nptr);
Description
This function accepts a database node pointer and will return an allocated string
which holds the full path to that node, relative to the database root.

For example, assume the database root has a node called A, A has a node called B, B
has a node called C, and C has a node called D. If pdb_trace() is called with a
pointer to the node D, pdb_trace() will return the string:
	A/B/C/D

NOTE
This function returns an allocated string. It will be necessary to free it using
free() when the data is no longer required. Otherwise there may be a memory leak.
Arguments
nptr - A database node pointer.
Return
The full path to the specified node relative to the database root where it resides.
Example
struct pdb* dbptr = pdb_load("bork.pdb");
/*
 * Assume bork.pdb looks like:
 *
 * "a" {
 * 	"b" {
 * 		"c" {
 * 			"d" {
 *				"e" '1';
 *			}
 *		}
 *	}
 * }
 *
 * We have a pointer to "e", but we want to know
 * exactly where in the database it resides.
 */
struct pdb_node_t* eptr = 0xDEADBEEF /* we already have this */

char* path = pdb_trace(dbptr, eptr);

/*
 * The following line will output:
 *	The path to e is "/a/b/c/d/e".
 *
 * The preceeding slash is acceptable, it just means
 * a is in the root. This is the same as "a/b/c/d/e".
 */
printf("The path to e is \"%s\".\n", path);

/*
 * Be sure to free this!
 */
free(path);
 
pdb_set_write_interval()

Prototype
void pdb_set_write_interval(struct pdb* dbptr, int seconds);
Description
Databases can be written to disk on given intervals.
This function allows you to set the time in seconds between these writes.

NOTE
It is the responsibility of the programmer, if they wish to use the write feature,
to include something similar to the following code in a main event loop:

if (pdb_need_write(dbptr))
	pdb_write(dbptr, file);

For more information see pdb_write().
Arguments
dbptr - A database pointer.
seconds - The number of seconds between write intervals.
Return
No return.
Example
struct pdb* dbptr = pdb_load("websites.pdb");
/*
 * Set the database write interval to 2 minutes (120 seconds).
 */
pdb_set_write_interval(dbptr, 120);
 
pdb_need_write()

Prototype
int pdb_need_write(struct pdb* dbptr);
Description
Determine if it has been X seconds since the last disk write of the specified database,
and if the database requires a write due to a change; both of these conditions must be
met for this function to return 1. X is either 120 seconds (defined by
PDB_DEFAULT_WRITE_INTERVAL in pdb.h), or whatever was set with pdb_set_write_interval().
Arguments
dbptr - A database pointer.
Return
1 if the specified database requires a disk write.
0 if no disk write is needed.
Example
struct pdb* dbptr = pdb_load("websites.pdb");
/*
 * If it has been 120 seconds since the last write, and the
 * database was changed, write it.
 */
if (pdb_need_write(dbptr))
	pdb_write(dbptr, "/home/XXX/websites.pdb");

/*
 * Now add a node called "foo" to the root.
 */
if (pdb_set(dbptr, "foo", "bar")) {
	/*
	 * The database was changed, wait 120 seconds.
	 */
	sleep(120);
	if (pdb_need_write(dbptr)) {
		/*
		 * This will be true, unless the write interval was changed to something
		 * longer than 120 seconds via pdb_set_write_interval().
		 * Write the database again.
		 */
		pdb_write(dbptr, "/home/XXX/websites.pdb");
	} else {
		/*
		 * This will only occur if the write interval is longer than 120 seconds.
		 */
	}
} else {
	/*
	 * The database was not changed, if we wait 120 seconds, assuming there are no
	 * changes during that time, no write will be needed.
	 */
	sleep(120);
	printf("Write needed? %i\n", pdb_need_write(dbptr));	/* should be 0	*/
}
 
pdb_write()

Prototype
int pdb_write(struct pdb* dbptr, char* file);
Description
Write the specified database to the specified file.
This can be done at any time, and it is not required to wait X seconds to write
to disk. It is a good idea, however, to use pdb_need_write() to check if a disk write
is needed, otherwise there will be wasted cycles.

NOTE
It is the responsibility of the programmer, if they wish to use the write feature,
to include something similar to the following code in a main event loop:

if (pdb_need_write(dbptr))
	pdb_write(dbptr, file);
Arguments
dbptr - A database pointer.
file - The file to write the database to.
Return
1 if the disk write was successful.
0 if the disk write failed.
Example
struct pdb* dbptr = pdb_load("websites.pdb");
/*
 * Add "foo" to the root and save the altered database to disk.
 * Note that here we are forcing a write, as we are not checking
 * if a write is actually needed with pdb_need_write().
 * See pdb_need_write() if you wish to see an example of this.
 */
pdb_write(dbptr, "websites.pdb");
 
pdb_set_free_method()

Prototype
void pdb_set_free_method(struct pdb* dbptr, struct pdb_node_t* nptr, void* free_cb);
Description
From time to time it may be necessary use a custom function to free a given
database node.  This is especially useful for abstract node types (ABSTRACT_NODE_TYPE).

The callback should be in the form of:
	void free_cb(void* dptr);
That is, a function which returns void, and accepts only one parameter, a void pointer.
The function should do whatever necessary to deallocate the data at the pointer.

This function will be invoked by pdb when the set node is deleted, and the parameter
passed to the callback will be the data set at that node.

WARNING
It is essential to call this function after creating an abstract node type. Even if the data
set at that node can be free'd with free(), a callback must be set. If you wish, and if the
data can be deallocated with a single free() call, you may set free() as the callback.
However, it is advised that you use your own function to free the data.

Failure to set this for an abstract node type will result in a memory leak.
Arguments
dbptr - A database pointer.
nptr - The node to set the free method for.
free_cb - A callback to free the data.
Return
No return.
Example
Assume this structure is defined:
struct foo_t {
	char* a;
	char* b;
};

Assume this function is defined:
void free_foo(void* dptr) {
	struct foo_t* f = dptr;
	free(f->a);
	free(f->b);
	free(f);
}

Example
struct foo_t* d = (struct foo_t*)malloc(sizeof(struct foo_t));
d->a = strdup("foo");
d->b = strdup("bar");

struct pdb* dbptr = pdb_load("asdf.pdb");
/*
 * Create an abstract node called "foo" in the database root.
 */
struct pdb_node_t* bt = pdb_set_node(dbptr, "/", "foo", d, ABSTRACT_NODE_TYPE);

/*
 * If the next line is never called, there will be three memory leaks.
 * One for not deallocating d->a, one for d->b, and finally one for d itself.
 */
pdb_set_free_method(dbptr, bt, (void*)&foo_free);

/*
 * Unload the database.
 * When pdb unloads the "foo" node, it will call free_foo with a pointer that points
 * to the structure which it was set to (in this case "d").
 */
pdb_unload(dbptr);
 
pdb_count_children()

Prototype
int pdb_count_children(struct pdb* dbptr, char* path);
Description
Count the number of children the specified node has. Note that this is not recursive,
it will only return the number of chilren the specified node has, not the specified node's
number of children in addition to all the childrens children.
Arguments
dbptr - A database pointer.
path - The path (including the key) to the node that has the children to be counted.
Return
The number of children a node has.
Example
struct pdb* dbptr = pdb_load("websites.pdb");
/*
 * Assume websites.pdb looks like:
 *
 * "http" {
 *	"slashdot" "slashdot.org";
 *	"google" "google.com";
 *	"misc" {
 *		"qmm" "quake3mm.net";
 *		"finop" "finop.net";
 *	}
 * }
 *
 * The following lines will output:
 *	"http" has 3 children.
 * "slashdot", "google", and "misc" are counted.
 */
int c = pdb_count_children(dbptr, "http");
printf("\"http\" has %i children.\n", c);
 
pdb was written by Michael Laforest (Para) <paralizer -AT- users -DOT- sourceforge -DOT- net>
Copyright 2005 Michael Laforest, all rights reserved.

pdb is released under the GPL.