Add tsearch and friends, and a test program

tsearch is missing from windows. use the netbsd version as it's
license-compatible.
This commit is contained in:
Derrick Brashear
2011-04-08 15:06:01 +01:00
committed by Simon Wilkinson
parent 3d36172090
commit 2a32bf67f0
9 changed files with 525 additions and 4 deletions

View File

@@ -65,6 +65,7 @@ AC_CHECK_HEADERS([\
poll.h \
pwd.h \
rpcsvc/ypclnt.h \
search.h \
shadow.h \
stdint.h \
sys/bswap.h \
@@ -149,6 +150,7 @@ AC_REQUIRE([CHECK_NETINET_IP_AND_TCP])
AM_CONDITIONAL(have_err_h, test "$ac_cv_header_err_h" = yes)
AM_CONDITIONAL(have_ifaddrs_h, test "$ac_cv_header_ifaddrs_h" = yes)
AM_CONDITIONAL(have_search_h, test "$ac_cv_header_search_h" = yes)
AM_CONDITIONAL(have_vis_h, test "$ac_cv_header_vis_h" = yes)
dnl Check for functions and libraries
@@ -197,6 +199,10 @@ AC_CHECK_FUNCS([ \
svis \
sysconf \
sysctl \
tdelete \
tfind \
tsearch \
twalk \
uname \
unvis \
vasnprintf \

View File

@@ -32,7 +32,8 @@ check_PROGRAMS = \
parse_reply-test \
parse_time-test \
snprintf-test \
strpftime-test
strpftime-test \
tsearch-test
TESTS = $(check_PROGRAMS)
@@ -58,6 +59,9 @@ strpftime_test_CFLAGS = -DTEST_STRPFTIME
snprintf_test_SOURCES = snprintf-test.c
snprintf_test_LDADD = libtest.la $(LDADD)
snprintf_test_CFLAGS = -DTEST_SNPRINTF
tsearch_test_SOURCES = tsearch-test.c
tsearch_test_LDADD = libtest.la $(LDADD)
tsearch_test_CFLAGS = -DTEST_TSEARCH
resolve_test_SOURCES = resolve-test.c
@@ -107,6 +111,7 @@ libroken_la_SOURCES = \
strerror_r.c \
strpool.c \
timeval.c \
tsearch.c \
tm2time.c \
unvis.c \
verify.c \
@@ -121,6 +126,7 @@ EXTRA_libroken_la_SOURCES = \
glob.hin \
fnmatch.hin \
ifaddrs.hin \
search.hin \
vis.hin
libroken_la_LIBADD = @LTLIBOBJS@ $(LIB_crypt)
@@ -153,6 +159,12 @@ else
ifaddrs_h = ifaddrs.h
endif
if have_search_h
search_h =
else
search_h = search.h
endif
if have_vis_h
vis_h =
else
@@ -160,8 +172,8 @@ vis_h = vis.h
endif
## these are controlled by configure
XHEADERS = $(err_h) $(fnmatch_h) $(glob_h) $(ifaddrs_h) $(vis_h)
CLEANFILES += err.h fnmatch.h glob.h ifaddrs.h vis.h
XHEADERS = $(err_h) $(fnmatch_h) $(glob_h) $(ifaddrs_h) $(search_h) $(vis_h)
CLEANFILES += err.h fnmatch.h glob.h ifaddrs.h search.h vis.h
dist_include_HEADERS = \
base64.h \
@@ -186,7 +198,7 @@ nodist_include_HEADERS = roken.h
rokenincludedir = $(includedir)/roken
nodist_rokeninclude_HEADERS = $(XHEADERS)
man_MANS = getarg.3 parse_time.3 rtbl.3 ecalloc.3
man_MANS = getarg.3 parse_time.3 rtbl.3 ecalloc.3 tsearch.3
SUFFIXES += .hin
.hin.h:

View File

@@ -103,6 +103,7 @@ libroken_la_OBJS = \
$(OBJ)\timegm.obj \
$(OBJ)\timeval.obj \
$(OBJ)\tm2time.obj \
$(OBJ)\tsearch.obj \
$(OBJ)\unvis.obj \
$(OBJ)\verr.obj \
$(OBJ)\verrx.obj \
@@ -155,6 +156,7 @@ INCFILES = \
$(INCDIR)\roken.h \
$(INCDIR)\roken-common.h \
$(INCDIR)\rtbl.h \
$(INCDIR)\search.h \
$(INCDIR)\stdbool.h \
$(INCDIR)\syslog.h \
$(INCDIR)\vis.h \

View File

@@ -1099,6 +1099,18 @@ rk_qsort(void *, size_t, size_t, int (*)(const void *, const void *));
#define rk_random() rand()
#endif
#ifndef HAVE_TDELETE
#define tdelete(a,b,c) rk_tdelete(a,b,c)
#endif
#ifndef HAVE_TFIND
#define tfind(a,b,c) rk_tfind(a,b,c)
#endif
#ifndef HAVE_TSEARCH
#define tsearch(a,b,c) rk_tsearch(a,b,c)
#endif
#ifndef HAVE_TWALK
#define twalk(a,b) rk_twalk(a,b)
#endif
#if defined(__linux__) && defined(SOCK_CLOEXEC) && !defined(SOCKET_WRAPPER_REPLACE) && !defined(__SOCKET_WRAPPER_H__)
#undef socket

42
lib/roken/search.hin Normal file
View File

@@ -0,0 +1,42 @@
/*-
* Written by J.T. Conklin <jtc@netbsd.org>
* Public domain.
*
* $NetBSD: search.h,v 1.12 1999/02/22 10:34:28 christos Exp $
*/
#ifndef _rk_SEARCH_H_
#define _rk_SEARCH_H_ 1
#ifndef ROKEN_LIB_FUNCTION
#ifdef _WIN32
#define ROKEN_LIB_FUNCTION
#define ROKEN_LIB_CALL __cdecl
#else
#define ROKEN_LIB_FUNCTION
#define ROKEN_LIB_CALL
#endif
#endif
#include <sys/cdefs.h>
#include <sys/types.h>
typedef enum {
preorder,
postorder,
endorder,
leaf
} VISIT;
ROKEN_CPP_START
ROKEN_LIB_FUNCTION void * ROKEN_LIB_CALL rk_tdelete(const void * __restrict, void ** __restrict,
int (*)(const void *, const void *));
ROKEN_LIB_FUNCTION void * ROKEN_LIB_CALL rk_tfind(const void *, void * const *,
int (*)(const void *, const void *));
ROKEN_LIB_FUNCTION void * ROKEN_LIB_CALL rk_tsearch(const void *, void **, int (*)(const void *, const void *));
ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL rk_twalk(const void *, void (*)(const void *, VISIT, int));
ROKEN_CPP_END
#endif /* !_rk_SEARCH_H_ */

125
lib/roken/tsearch-test.c Normal file
View File

@@ -0,0 +1,125 @@
/*
* Tree search generalized from Knuth (6.2.2) Algorithm T just like
* the AT&T man page says.
*
* The node_t structure is for internal use only, lint doesn't grok it.
*
* Written by reading the System V Interface Definition, not the code.
*
* Totally public domain.
*/
#include <config.h>
#include "roken.h"
#include "search.h"
struct node {
char *string;
int order;
};
extern void *rk_tdelete(const void * __restrict, void ** __restrict,
int (*)(const void *, const void *));
extern void *rk_tfind(const void *, void * const *,
int (*)(const void *, const void *));
extern void *rk_tsearch(const void *, void **, int (*)(const void *, const void *));
extern void rk_twalk(const void *, void (*)(const void *, VISIT, int));
void *rootnode = NULL;
int numerr = 0;
/*
* This routine compares two nodes, based on an
* alphabetical ordering of the string field.
*/
int
node_compare(const void *node1, const void *node2)
{
return strcmp(((const struct node *) node1)->string,
((const struct node *) node2)->string);
}
static int walkorder = -1;
void
list_node(const void *ptr, VISIT order, int level)
{
const struct node *p = *(const struct node **) ptr;
if (order == postorder || order == leaf) {
walkorder++;
if (p->order != walkorder) {
warnx("sort failed: expected %d next, got %d\n", walkorder,
p->order);
numerr++;
}
}
}
int
main(int argc, char **argv)
{
int numtest = 1;
struct node *t, *p, tests[] = {
{ "", 0 },
{ "ab", 3 },
{ "abc", 4 },
{ "abcdefg", 8 },
{ "abcd", 5 },
{ "a", 2 },
{ "abcdef", 7 },
{ "abcde", 6 },
{ "=", 1 },
{ NULL }
};
for(t = tests; t->string; t++) {
/* Better not be there */
p = (struct node *)rk_tfind((void *)t, (void **)&rootnode,
node_compare);
if (p) {
warnx("erroneous list: found %d\n", p->order);
numerr++;
}
/* Put node into the tree. */
p = (struct node *) rk_tsearch((void *)t, (void **)&rootnode,
node_compare);
if (!p) {
warnx("erroneous list: missing %d\n", t->order);
numerr++;
}
}
rk_twalk(rootnode, list_node);
for(t = tests; t->string; t++) {
/* Better be there */
p = (struct node *) rk_tfind((void *)t, (void **)&rootnode,
node_compare);
if (!p) {
warnx("erroneous list: missing %d\n", t->order);
numerr++;
}
/* pull out node */
(void) rk_tdelete((void *)t, (void **)&rootnode,
node_compare);
/* Better not be there */
p = (struct node *) rk_tfind((void *)t, (void **)&rootnode,
node_compare);
if (p) {
warnx("erroneous list: found %d\n", p->order);
numerr++;
}
}
return numerr;
}

138
lib/roken/tsearch.3 Normal file
View File

@@ -0,0 +1,138 @@
.\" $NetBSD$
.\" Copyright (c) 1997 Todd C. Miller <Todd.Miller@courtesan.com>
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\" 3. The name of the author may not be used to endorse or promote products
.\" derived from this software without specific prior written permission.
.\"
.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
.\" INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
.\" AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
.\" THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
.\" EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
.\" PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
.\" OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
.\" WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
.\" OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
.\" ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
.\" OpenBSD: tsearch.3,v 1.2 1998/06/21 22:13:49 millert Exp
.\" $FreeBSD: src/lib/libc/stdlib/tsearch.3,v 1.15 2006/06/23 13:36:33 keramida Exp $
.\" $DragonFly: src/lib/libc/stdlib/tsearch.c,v 1.6 2005/11/24 17:18:30 swildner Exp $
.\"
.Dd June 15, 1997
.Dt TSEARCH 3
.Os
.Sh NAME
.Nm tsearch ,
.Nm tfind ,
.Nm tdelete ,
.Nm twalk
.Nd manipulate binary search trees
.Sh LIBRARY
.Lb libc
.Sh SYNOPSIS
.In search.h
.Ft void *
.Fn tdelete "const void * restrict key" "void ** restrict rootp" "int (*compar) (const void *, const void *)"
.Ft void *
.Fn tfind "const void *key" "void * const *rootp" "int (*compar) (const void *, const void *)"
.Ft void *
.Fn tsearch "const void *key" "void **rootp" "int (*compar) (const void *, const void *)"
.Ft void
.Fn twalk "const void *root" "void (*action) (const void *, VISIT, int)"
.Sh DESCRIPTION
The
.Fn tdelete ,
.Fn tfind ,
.Fn tsearch ,
and
.Fn twalk
functions manage binary search trees based on algorithms T and D
from Knuth (6.2.2).
The comparison function passed in by
the user has the same style of return values as
.Xr strcmp 3 .
.Pp
The
.Fn tfind
function
searches for the datum matched by the argument
.Fa key
in the binary tree rooted at
.Fa rootp ,
returning a pointer to the datum if it is found and NULL
if it is not.
.Pp
The
.Fn tsearch
function
is identical to
.Fn tfind
except that if no match is found,
.Fa key
is inserted into the tree and a pointer to it is returned.
If
.Fa rootp
points to a NULL value a new binary search tree is created.
.Pp
The
.Fn tdelete
function
deletes a node from the specified binary search tree and returns
a pointer to the parent of the node to be deleted.
It takes the same arguments as
.Fn tfind
and
.Fn tsearch .
If the node to be deleted is the root of the binary search tree,
.Fa rootp
will be adjusted.
.Pp
The
.Fn twalk
function
walks the binary search tree rooted in
.Fa root
and calls the function
.Fa action
on each node.
The
.Fa action
function
is called with three arguments: a pointer to the current node,
a value from the enum
.Sy "typedef enum { preorder, postorder, endorder, leaf } VISIT;"
specifying the traversal type, and a node level (where level
zero is the root of the tree).
.Sh RETURN VALUES
The
.Fn tsearch
function returns NULL if allocation of a new node fails (usually
due to a lack of free memory).
.Pp
The
.Fn tfind ,
.Fn tsearch ,
and
.Fn tdelete
functions
return NULL if
.Fa rootp
is NULL or the datum cannot be found.
.Pp
The
.Fn twalk
function returns no value.
.Sh SEE ALSO
.Xr bsearch 3 ,
.Xr hsearch 3 ,
.Xr lsearch 3

180
lib/roken/tsearch.c Normal file
View File

@@ -0,0 +1,180 @@
/*
* Tree search generalized from Knuth (6.2.2) Algorithm T just like
* the AT&T man page says.
*
* The node_t structure is for internal use only, lint doesn't grok it.
*
* Written by reading the System V Interface Definition, not the code.
*
* Totally public domain.
*
* $NetBSD: tsearch.c,v 1.3 1999/09/16 11:45:37 lukem Exp $
* $NetBSD: twalk.c,v 1.1 1999/02/22 10:33:16 christos Exp $
* $NetBSD: tdelete.c,v 1.2 1999/09/16 11:45:37 lukem Exp $
* $NetBSD: tfind.c,v 1.2 1999/09/16 11:45:37 lukem Exp $
*/
#include <config.h>
#include "roken.h"
#include "search.h"
#include <stdlib.h>
typedef struct node {
char *key;
struct node *llink, *rlink;
} node_t;
#ifndef __DECONST
#define __DECONST(type, var) ((type)(uintptr_t)(const void *)(var))
#endif
/*
* find or insert datum into search tree
*
* Parameters:
* vkey: key to be located
* vrootp: address of tree root
*/
ROKEN_LIB_FUNCTION void *
rk_tsearch(const void *vkey, void **vrootp,
int (*compar)(const void *, const void *))
{
node_t *q;
node_t **rootp = (node_t **)vrootp;
if (rootp == NULL)
return NULL;
while (*rootp != NULL) { /* Knuth's T1: */
int r;
if ((r = (*compar)(vkey, (*rootp)->key)) == 0) /* T2: */
return *rootp; /* we found it! */
rootp = (r < 0) ?
&(*rootp)->llink : /* T3: follow left branch */
&(*rootp)->rlink; /* T4: follow right branch */
}
q = malloc(sizeof(node_t)); /* T5: key not found */
if (q != 0) { /* make new node */
*rootp = q; /* link new node to old */
/* LINTED const castaway ok */
q->key = __DECONST(void *, vkey); /* initialize new node */
q->llink = q->rlink = NULL;
}
return q;
}
/*
* Walk the nodes of a tree
*
* Parameters:
* root: Root of the tree to be walked
*/
static void
trecurse(const node_t *root, void (*action)(const void *, VISIT, int),
int level)
{
if (root->llink == NULL && root->rlink == NULL)
(*action)(root, leaf, level);
else {
(*action)(root, preorder, level);
if (root->llink != NULL)
trecurse(root->llink, action, level + 1);
(*action)(root, postorder, level);
if (root->rlink != NULL)
trecurse(root->rlink, action, level + 1);
(*action)(root, endorder, level);
}
}
/*
* Walk the nodes of a tree
*
* Parameters:
* vroot: Root of the tree to be walked
*/
ROKEN_LIB_FUNCTION void
rk_twalk(const void *vroot,
void (*action)(const void *, VISIT, int))
{
if (vroot != NULL && action != NULL)
trecurse(vroot, action, 0);
}
/*
* delete node with given key
*
* vkey: key to be deleted
* vrootp: address of the root of the tree
* compar: function to carry out node comparisons
*/
ROKEN_LIB_FUNCTION void *
rk_tdelete(const void * __restrict vkey, void ** __restrict vrootp,
int (*compar)(const void *, const void *))
{
node_t **rootp = (node_t **)vrootp;
node_t *p, *q, *r;
int cmp;
if (rootp == NULL || (p = *rootp) == NULL)
return NULL;
while ((cmp = (*compar)(vkey, (*rootp)->key)) != 0) {
p = *rootp;
rootp = (cmp < 0) ?
&(*rootp)->llink : /* follow llink branch */
&(*rootp)->rlink; /* follow rlink branch */
if (*rootp == NULL)
return NULL; /* key not found */
}
r = (*rootp)->rlink; /* D1: */
if ((q = (*rootp)->llink) == NULL) /* Left NULL? */
q = r;
else if (r != NULL) { /* Right link is NULL? */
if (r->llink == NULL) { /* D2: Find successor */
r->llink = q;
q = r;
} else { /* D3: Find NULL link */
for (q = r->llink; q->llink != NULL; q = r->llink)
r = q;
r->llink = q->rlink;
q->llink = (*rootp)->llink;
q->rlink = (*rootp)->rlink;
}
}
free(*rootp); /* D4: Free node */
*rootp = q; /* link parent to new node */
return p;
}
/*
* find a node, or return 0
*
* Parameters:
* vkey: key to be found
* vrootp: address of the tree root
*/
ROKEN_LIB_FUNCTION void *
rk_tfind(const void *vkey, void * const *vrootp,
int (*compar)(const void *, const void *))
{
node_t **rootp = (node_t **)vrootp;
if (rootp == NULL)
return NULL;
while (*rootp != NULL) { /* T1: */
int r;
if ((r = (*compar)(vkey, (*rootp)->key)) == 0) /* T2: */
return *rootp; /* key found */
rootp = (r < 0) ?
&(*rootp)->llink : /* T3: follow left branch */
&(*rootp)->rlink; /* T4: follow right branch */
}
return NULL;
}

View File

@@ -139,6 +139,10 @@ HEIMDAL_ROKEN_1.0 {
rk_timevaladd;
rk_timevalfix;
rk_timevalsub;
rk_tdelete;
rx_tfind;
rk_tsearch;
rk_twalk;
rk_undumpdata;
rk_unvis;
rk_vasnprintf;