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:

committed by
Simon Wilkinson

parent
3d36172090
commit
2a32bf67f0
@@ -65,6 +65,7 @@ AC_CHECK_HEADERS([\
|
|||||||
poll.h \
|
poll.h \
|
||||||
pwd.h \
|
pwd.h \
|
||||||
rpcsvc/ypclnt.h \
|
rpcsvc/ypclnt.h \
|
||||||
|
search.h \
|
||||||
shadow.h \
|
shadow.h \
|
||||||
stdint.h \
|
stdint.h \
|
||||||
sys/bswap.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_err_h, test "$ac_cv_header_err_h" = yes)
|
||||||
AM_CONDITIONAL(have_ifaddrs_h, test "$ac_cv_header_ifaddrs_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)
|
AM_CONDITIONAL(have_vis_h, test "$ac_cv_header_vis_h" = yes)
|
||||||
|
|
||||||
dnl Check for functions and libraries
|
dnl Check for functions and libraries
|
||||||
@@ -197,6 +199,10 @@ AC_CHECK_FUNCS([ \
|
|||||||
svis \
|
svis \
|
||||||
sysconf \
|
sysconf \
|
||||||
sysctl \
|
sysctl \
|
||||||
|
tdelete \
|
||||||
|
tfind \
|
||||||
|
tsearch \
|
||||||
|
twalk \
|
||||||
uname \
|
uname \
|
||||||
unvis \
|
unvis \
|
||||||
vasnprintf \
|
vasnprintf \
|
||||||
|
@@ -32,7 +32,8 @@ check_PROGRAMS = \
|
|||||||
parse_reply-test \
|
parse_reply-test \
|
||||||
parse_time-test \
|
parse_time-test \
|
||||||
snprintf-test \
|
snprintf-test \
|
||||||
strpftime-test
|
strpftime-test \
|
||||||
|
tsearch-test
|
||||||
|
|
||||||
TESTS = $(check_PROGRAMS)
|
TESTS = $(check_PROGRAMS)
|
||||||
|
|
||||||
@@ -58,6 +59,9 @@ strpftime_test_CFLAGS = -DTEST_STRPFTIME
|
|||||||
snprintf_test_SOURCES = snprintf-test.c
|
snprintf_test_SOURCES = snprintf-test.c
|
||||||
snprintf_test_LDADD = libtest.la $(LDADD)
|
snprintf_test_LDADD = libtest.la $(LDADD)
|
||||||
snprintf_test_CFLAGS = -DTEST_SNPRINTF
|
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
|
resolve_test_SOURCES = resolve-test.c
|
||||||
|
|
||||||
@@ -107,6 +111,7 @@ libroken_la_SOURCES = \
|
|||||||
strerror_r.c \
|
strerror_r.c \
|
||||||
strpool.c \
|
strpool.c \
|
||||||
timeval.c \
|
timeval.c \
|
||||||
|
tsearch.c \
|
||||||
tm2time.c \
|
tm2time.c \
|
||||||
unvis.c \
|
unvis.c \
|
||||||
verify.c \
|
verify.c \
|
||||||
@@ -121,6 +126,7 @@ EXTRA_libroken_la_SOURCES = \
|
|||||||
glob.hin \
|
glob.hin \
|
||||||
fnmatch.hin \
|
fnmatch.hin \
|
||||||
ifaddrs.hin \
|
ifaddrs.hin \
|
||||||
|
search.hin \
|
||||||
vis.hin
|
vis.hin
|
||||||
|
|
||||||
libroken_la_LIBADD = @LTLIBOBJS@ $(LIB_crypt)
|
libroken_la_LIBADD = @LTLIBOBJS@ $(LIB_crypt)
|
||||||
@@ -153,6 +159,12 @@ else
|
|||||||
ifaddrs_h = ifaddrs.h
|
ifaddrs_h = ifaddrs.h
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if have_search_h
|
||||||
|
search_h =
|
||||||
|
else
|
||||||
|
search_h = search.h
|
||||||
|
endif
|
||||||
|
|
||||||
if have_vis_h
|
if have_vis_h
|
||||||
vis_h =
|
vis_h =
|
||||||
else
|
else
|
||||||
@@ -160,8 +172,8 @@ vis_h = vis.h
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
## these are controlled by configure
|
## these are controlled by configure
|
||||||
XHEADERS = $(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 vis.h
|
CLEANFILES += err.h fnmatch.h glob.h ifaddrs.h search.h vis.h
|
||||||
|
|
||||||
dist_include_HEADERS = \
|
dist_include_HEADERS = \
|
||||||
base64.h \
|
base64.h \
|
||||||
@@ -186,7 +198,7 @@ nodist_include_HEADERS = roken.h
|
|||||||
rokenincludedir = $(includedir)/roken
|
rokenincludedir = $(includedir)/roken
|
||||||
nodist_rokeninclude_HEADERS = $(XHEADERS)
|
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
|
SUFFIXES += .hin
|
||||||
.hin.h:
|
.hin.h:
|
||||||
|
@@ -103,6 +103,7 @@ libroken_la_OBJS = \
|
|||||||
$(OBJ)\timegm.obj \
|
$(OBJ)\timegm.obj \
|
||||||
$(OBJ)\timeval.obj \
|
$(OBJ)\timeval.obj \
|
||||||
$(OBJ)\tm2time.obj \
|
$(OBJ)\tm2time.obj \
|
||||||
|
$(OBJ)\tsearch.obj \
|
||||||
$(OBJ)\unvis.obj \
|
$(OBJ)\unvis.obj \
|
||||||
$(OBJ)\verr.obj \
|
$(OBJ)\verr.obj \
|
||||||
$(OBJ)\verrx.obj \
|
$(OBJ)\verrx.obj \
|
||||||
@@ -155,6 +156,7 @@ INCFILES = \
|
|||||||
$(INCDIR)\roken.h \
|
$(INCDIR)\roken.h \
|
||||||
$(INCDIR)\roken-common.h \
|
$(INCDIR)\roken-common.h \
|
||||||
$(INCDIR)\rtbl.h \
|
$(INCDIR)\rtbl.h \
|
||||||
|
$(INCDIR)\search.h \
|
||||||
$(INCDIR)\stdbool.h \
|
$(INCDIR)\stdbool.h \
|
||||||
$(INCDIR)\syslog.h \
|
$(INCDIR)\syslog.h \
|
||||||
$(INCDIR)\vis.h \
|
$(INCDIR)\vis.h \
|
||||||
|
@@ -1099,6 +1099,18 @@ rk_qsort(void *, size_t, size_t, int (*)(const void *, const void *));
|
|||||||
#define rk_random() rand()
|
#define rk_random() rand()
|
||||||
#endif
|
#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__)
|
#if defined(__linux__) && defined(SOCK_CLOEXEC) && !defined(SOCKET_WRAPPER_REPLACE) && !defined(__SOCKET_WRAPPER_H__)
|
||||||
#undef socket
|
#undef socket
|
||||||
|
42
lib/roken/search.hin
Normal file
42
lib/roken/search.hin
Normal 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
125
lib/roken/tsearch-test.c
Normal 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
138
lib/roken/tsearch.3
Normal 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
180
lib/roken/tsearch.c
Normal 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;
|
||||||
|
}
|
@@ -139,6 +139,10 @@ HEIMDAL_ROKEN_1.0 {
|
|||||||
rk_timevaladd;
|
rk_timevaladd;
|
||||||
rk_timevalfix;
|
rk_timevalfix;
|
||||||
rk_timevalsub;
|
rk_timevalsub;
|
||||||
|
rk_tdelete;
|
||||||
|
rx_tfind;
|
||||||
|
rk_tsearch;
|
||||||
|
rk_twalk;
|
||||||
rk_undumpdata;
|
rk_undumpdata;
|
||||||
rk_unvis;
|
rk_unvis;
|
||||||
rk_vasnprintf;
|
rk_vasnprintf;
|
||||||
|
Reference in New Issue
Block a user