|
Packit |
cdaae3 |
/*
|
|
Packit |
cdaae3 |
Copyright David Anderson 2010-2014.
|
|
Packit |
cdaae3 |
This is free software.
|
|
Packit |
cdaae3 |
Permission hereby granted for anyone to copy or
|
|
Packit |
cdaae3 |
use this code for any purpose without restriction.
|
|
Packit |
cdaae3 |
Attribution may be given or may not, it is your choice.
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
September 8, 2011: The tdelete example code was wrong in that
|
|
Packit |
cdaae3 |
it did not clean up entirely. So was the tsearch example code.
|
|
Packit |
cdaae3 |
Both are now fixed (it's surprisingly difficult to do this
|
|
Packit |
cdaae3 |
all correctly, but then the available documentation is at best
|
|
Packit |
cdaae3 |
a hint).
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
The GNU/Linux tsearch man page (in the man-page 3.54 edition)
|
|
Packit |
cdaae3 |
suggests that tdestroy() takes a pointer to a variable which
|
|
Packit |
cdaae3 |
points to the root. In reality tdestroy() takes a pointer
|
|
Packit |
cdaae3 |
to the root. As revealed by trying tdestroy() both ways.
|
|
Packit |
cdaae3 |
*/
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
/* tsearch() tfind() tdelete() twalk() tdestroy() example.
|
|
Packit |
cdaae3 |
*/
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
#include <stdio.h>
|
|
Packit |
cdaae3 |
#include <unistd.h>
|
|
Packit |
cdaae3 |
#include <stdlib.h>
|
|
Packit |
cdaae3 |
#include <string.h>
|
|
Packit |
cdaae3 |
#include <errno.h>
|
|
Packit |
cdaae3 |
/* __USE_GNU exposes the GNU tdestroy() function, a
|
|
Packit |
cdaae3 |
function that is not mentioned by the Single Unix
|
|
Packit |
cdaae3 |
Specification. */
|
|
Packit |
cdaae3 |
#define __USE_GNU 1
|
|
Packit |
cdaae3 |
#include <search.h>
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
/* The struct is trivially usable to implement a set or
|
|
Packit |
cdaae3 |
map (mapping an integer to a string).
|
|
Packit |
cdaae3 |
The following struct is the example basis
|
|
Packit |
cdaae3 |
because that is the capability I wanted to use.
|
|
Packit |
cdaae3 |
tsearch has no idea what data is involved, only the comparison function
|
|
Packit |
cdaae3 |
mt_compare_func() and the free function mt_free_func()
|
|
Packit |
cdaae3 |
(passed in to tsearch calls) know what data is involved.
|
|
Packit |
cdaae3 |
Making tsearch very flexible indeed.
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
Obviously the use of a struct is arbitrary, it is just
|
|
Packit |
cdaae3 |
an example.
|
|
Packit |
cdaae3 |
*/
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
struct my_tentry {
|
|
Packit |
cdaae3 |
unsigned mt_key;
|
|
Packit |
cdaae3 |
/* When using this as a set of mt_key the mt_name
|
|
Packit |
cdaae3 |
field is set to 0 (NULL). */
|
|
Packit |
cdaae3 |
char * mt_name;
|
|
Packit |
cdaae3 |
};
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
/* We allow a NULL name so this struct acts sort of like a set
|
|
Packit |
cdaae3 |
and sort of like a map.
|
|
Packit |
cdaae3 |
*/
|
|
Packit |
cdaae3 |
struct my_tentry *
|
|
Packit |
cdaae3 |
make_my_tentry(unsigned k,char *name)
|
|
Packit |
cdaae3 |
{
|
|
Packit |
cdaae3 |
struct my_tentry *mt =
|
|
Packit |
cdaae3 |
(struct my_tentry *)calloc(sizeof(struct my_tentry),1);
|
|
Packit |
cdaae3 |
if(!mt) {
|
|
Packit |
cdaae3 |
printf("calloc fail\n");
|
|
Packit |
cdaae3 |
exit(1);
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
mt->mt_key = k;
|
|
Packit |
cdaae3 |
if(name) {
|
|
Packit |
cdaae3 |
mt->mt_name = strdup(name);
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
return mt;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
void
|
|
Packit |
cdaae3 |
mt_free_func(void *mt_data)
|
|
Packit |
cdaae3 |
{
|
|
Packit |
cdaae3 |
struct my_tentry *m = mt_data;
|
|
Packit |
cdaae3 |
if(!m) {
|
|
Packit |
cdaae3 |
return;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
free(m->mt_name);
|
|
Packit |
cdaae3 |
free(mt_data);
|
|
Packit |
cdaae3 |
return;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
int mt_compare_func(const void *l, const void *r)
|
|
Packit |
cdaae3 |
{
|
|
Packit |
cdaae3 |
const struct my_tentry *ml = l;
|
|
Packit |
cdaae3 |
const struct my_tentry *mr = r;
|
|
Packit |
cdaae3 |
if(ml->mt_key < mr->mt_key) {
|
|
Packit |
cdaae3 |
return -1;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
if(ml->mt_key > mr->mt_key) {
|
|
Packit |
cdaae3 |
return 1;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
return 0;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
void
|
|
Packit |
cdaae3 |
walk_entry(const void *mt_data,VISIT x,int level)
|
|
Packit |
cdaae3 |
{
|
|
Packit |
cdaae3 |
struct my_tentry *m = *(struct my_tentry **)mt_data;
|
|
Packit |
cdaae3 |
printf("<%d>Walk on node %s %u %s \n",
|
|
Packit |
cdaae3 |
level,
|
|
Packit |
cdaae3 |
x == preorder?"preorder":
|
|
Packit |
cdaae3 |
x == postorder?"postorder":
|
|
Packit |
cdaae3 |
x == endorder?"endorder":
|
|
Packit |
cdaae3 |
x == leaf?"leaf":
|
|
Packit |
cdaae3 |
"unknown",
|
|
Packit |
cdaae3 |
m->mt_key,m->mt_name);
|
|
Packit |
cdaae3 |
return;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
int main()
|
|
Packit |
cdaae3 |
{
|
|
Packit |
cdaae3 |
unsigned i;
|
|
Packit |
cdaae3 |
void *tree1 = 0;
|
|
Packit |
cdaae3 |
#define RECMAX 3
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
for(i = 0 ; i < RECMAX ; ++i) {
|
|
Packit |
cdaae3 |
int k = 0;
|
|
Packit |
cdaae3 |
char kbuf[40];
|
|
Packit |
cdaae3 |
char dbuf[60];
|
|
Packit |
cdaae3 |
struct my_tentry *mt = 0;
|
|
Packit |
cdaae3 |
struct my_tentry *retval = 0;
|
|
Packit |
cdaae3 |
snprintf(kbuf,sizeof(kbuf),"%u",i);
|
|
Packit |
cdaae3 |
strcpy(dbuf," data for ");
|
|
Packit |
cdaae3 |
strcat(dbuf,kbuf);
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
/* Do it twice so we have test the case where
|
|
Packit |
cdaae3 |
tsearch adds and one
|
|
Packit |
cdaae3 |
where it finds an existing record. */
|
|
Packit |
cdaae3 |
for (k = 0; k < 2 ;++k) {
|
|
Packit |
cdaae3 |
mt = make_my_tentry(i,dbuf);
|
|
Packit |
cdaae3 |
errno = 0;
|
|
Packit |
cdaae3 |
/* tsearch adds an entry if its not present already. */
|
|
Packit |
cdaae3 |
retval = tsearch(mt,&tree1, mt_compare_func );
|
|
Packit |
cdaae3 |
if(retval == 0) {
|
|
Packit |
cdaae3 |
printf("Fail ENOMEM\n");
|
|
Packit |
cdaae3 |
exit(1);
|
|
Packit |
cdaae3 |
} else {
|
|
Packit |
cdaae3 |
struct my_tentry *re = 0;
|
|
Packit |
cdaae3 |
re = *(struct my_tentry **)retval;
|
|
Packit |
cdaae3 |
if(re != mt) {
|
|
Packit |
cdaae3 |
printf("found existing ok %u\n",i);
|
|
Packit |
cdaae3 |
/* Prevents data leak: mt was
|
|
Packit |
cdaae3 |
already present. */
|
|
Packit |
cdaae3 |
mt_free_func(mt);
|
|
Packit |
cdaae3 |
} else {
|
|
Packit |
cdaae3 |
printf("insert ok %u\n",i);
|
|
Packit |
cdaae3 |
/* New entry mt was added. */
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
for(i = 0 ; i < 5 ; ++i) {
|
|
Packit |
cdaae3 |
char kbuf[40];
|
|
Packit |
cdaae3 |
char dbuf[60];
|
|
Packit |
cdaae3 |
dbuf[0] = 0;
|
|
Packit |
cdaae3 |
struct my_tentry *mt = 0;
|
|
Packit |
cdaae3 |
struct my_tentry *retval = 0;
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
snprintf(kbuf,sizeof(kbuf),"%u",i);
|
|
Packit |
cdaae3 |
mt = make_my_tentry(i,dbuf);
|
|
Packit |
cdaae3 |
retval = tfind(mt,&tree1,mt_compare_func);
|
|
Packit |
cdaae3 |
if(!retval) {
|
|
Packit |
cdaae3 |
if(i < RECMAX) {
|
|
Packit |
cdaae3 |
printf("Fail TSRCH on %s is FAILURE\n",kbuf);
|
|
Packit |
cdaae3 |
exit(1);
|
|
Packit |
cdaae3 |
} else {
|
|
Packit |
cdaae3 |
printf("Fail TSRCH on %s is ok\n",kbuf);
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
} else {
|
|
Packit |
cdaae3 |
printf("found ok %u\n",i);
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
mt_free_func(mt);
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
twalk(tree1,walk_entry);
|
|
Packit |
cdaae3 |
{
|
|
Packit |
cdaae3 |
struct my_tentry *mt = 0;
|
|
Packit |
cdaae3 |
struct my_tentry *re3 = 0;
|
|
Packit |
cdaae3 |
void *r = 0;
|
|
Packit |
cdaae3 |
mt = make_my_tentry(1,0);
|
|
Packit |
cdaae3 |
r = tfind(mt,&tree1,mt_compare_func);
|
|
Packit |
cdaae3 |
if (r) {
|
|
Packit |
cdaae3 |
/* This is what tdelete will delete.
|
|
Packit |
cdaae3 |
tdelete just removes the reference from
|
|
Packit |
cdaae3 |
the tree, it does not actually delete
|
|
Packit |
cdaae3 |
the memory for the entry itself. */
|
|
Packit |
cdaae3 |
re3 = *(struct my_tentry **)r;
|
|
Packit |
cdaae3 |
r = tdelete(mt,&tree1,mt_compare_func);
|
|
Packit |
cdaae3 |
/* We don't want the 'test' node left around. */
|
|
Packit |
cdaae3 |
mt_free_func(mt);
|
|
Packit |
cdaae3 |
if(r) {
|
|
Packit |
cdaae3 |
struct my_tentry *re2 = 0;
|
|
Packit |
cdaae3 |
re2 = *(struct my_tentry **)r;
|
|
Packit |
cdaae3 |
printf("tdelete returned parent: %u %s\n",
|
|
Packit |
cdaae3 |
re2->mt_key, re2->mt_name);
|
|
Packit |
cdaae3 |
} else {
|
|
Packit |
cdaae3 |
printf("tdelete returned NULL, tree now empty.\n");
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
/* Delete the content of the node that tdelete removed. */
|
|
Packit |
cdaae3 |
mt_free_func(re3);
|
|
Packit |
cdaae3 |
} else {
|
|
Packit |
cdaae3 |
/* There is no node like this to delete. */
|
|
Packit |
cdaae3 |
/* We don't want the 'test' node left around. */
|
|
Packit |
cdaae3 |
mt_free_func(mt);
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
twalk(tree1,walk_entry);
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
tdestroy(tree1,mt_free_func);
|
|
Packit |
cdaae3 |
printf("PASS tsearch test.\n");
|
|
Packit |
cdaae3 |
exit(0);
|
|
Packit |
cdaae3 |
}
|