Blame tsearch/tsearch.c

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
}