/* Copyright (c) 2014, David Anderson All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 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. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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. */ /* Testing tsearch etc. tsearch [-adds] [-std] -byvalue inputfile ... If one or more inputfile is listed then 'standard' tests are not done. If -std is given then (even with inputfile) standard tests are done. If -adds is given then extra tdump output is generated from one of the test driver functions. If -showa is given then extra output is generated identifying some some add/delete actions. If -byvalue is given then the tests are run using values not pointes. Run like this it is impossible to differentiate whether dwarf_tsearch() adds a new tree entry or just finds an existing one. In the right circumstances this approach is useful in that it is a bit faster than the default. See applybyvalue() and applybypointer. For timing tests, you probably want to compile with -DFULL_SPEED_RUN */ #include #include #include #include #include #include "dwarf_tsearch.h" /* These defines rename the call targets to Unix standard names (or to nothing where there is no standard version). */ #ifdef LIBC_TSEARCH #define _GNU_SOURCE /* for tdestroy */ #define __USE_GNU /* tdestroy */ #include #define dwarf_tsearch(a,b,c) tsearch(a,b,c) #define dwarf_tfind(a,b,c) tfind(a,b,c) #define dwarf_tdelete(a,b,c) tdelete(a,b,c) #define dwarf_twalk(a,b) twalk(a,b) #define dwarf_tdestroy(a,b) tdestroy(a,b) #define dwarf_tdump(a,c,d) #define dwarf_initialize_search_hash(a,b,c) #define DW_VISIT VISIT #define dwarf_preorder preorder #define dwarf_postorder postorder #define dwarf_endorder endorder #define dwarf_leaf leaf #endif /* LIBC_TSEARCH */ /* The struct is trivially usable to implement a set or map (mapping an integer to a string). The following struct is the example basis because that is the capability I wanted to use. tsearch has no idea what data is involved, only the comparison function mt_compare_func() and the free function mt_free_func() (passed in to tsearch calls) know what data is involved. Making tsearch very flexible indeed. Obviously the use of a struct (and this particular struct) is arbitrary, it is just an example. */ struct example_tentry { unsigned mt_key; /* When using this as a set of mt_key the mt_name field is set to 0 (NULL). */ char * mt_name; }; /* used to hold test data */ struct myacts { char action_; unsigned addr_; }; /* Another example of tree content is a simple value. Since the tree contains a pointer for each object we save, we can only directly save a value that fits in a pointer. */ typedef unsigned VALTYPE; enum insertorder { increasing, decreasing, balanced }; static int applybypointer(struct myacts *m, const char *msg, int hideactions, int printwalk, int dumpeverystage); static int applybyvalue(struct myacts *m, const char *msg, int hideactions, int printwalk, int dumpeverystage); int(*applyby)(struct myacts *m, const char *msg, int hideactions, int printwalk, int dumpeverystage); static const int increaseorder[] = {1,2,3,4,5,6}; static const int decreaseorder[] = {6,5,4,3,2,1}; /* The following a pseudo-random order. */ static const int balanceorder[] = {3,6,2,5,4,1}; /* From real code exposing a bug: */ static struct myacts sequence1[] = { {'a', 0x33c8}, {'a', 0x34d8}, {'a', 0x35c8}, {'a', 0x3640}, {'a', 0x3820}, {'a', 0x38d0}, {'a', 0x3958}, {'a', 0x39e8}, {'a', 0x3a78}, {'a', 0x3b08}, {'a', 0x3b98}, {'a', 0x3c28}, {'a', 0x3cb8}, {'d', 0x3c28}, {'a', 0x3d48}, {'d', 0x3cb8}, {'a', 0x3dd8}, {'d', 0x3d48}, {'a', 0x3e68}, {'d', 0x3dd8}, {'a', 0x3ef8}, {'a', 0x3f88}, {'d', 0x3e68}, {'a', 0x4018}, {'d', 0x3ef8}, {0,0} }; static struct myacts sequence2[] = { {'a', 0x931d2e0}, {'a', 0x931d340}, {'a', 0x931d378}, {'a', 0x931d3b8}, {'a', 0x931d0b0}, {'a', 0x931f7f8}, {'d', 0x931f7f8}, {'d', 0x931d378}, {'a', 0x931d378}, {'a', 0x931f7f8}, {'d', 0x931f7f8}, {'a', 0x931f7f8}, {'d', 0x931f7f8}, {'a', 0x931f9a8}, {'a', 0x931f9f0}, {'a', 0x931fa38}, {'a', 0x93224c0}, {'a', 0x93224f0}, {'a', 0x9322538}, {'a', 0x9322568}, {'a', 0x93225b0}, {'a', 0x93225e0}, {'a', 0x9322628}, {'a', 0x9322658}, {'a', 0x93226a0}, {'a', 0x93226d0}, {'d', 0x93224c0}, {'d', 0x9322538}, {'d', 0x93225b0}, {'d', 0x9322628}, {'d', 0x93226a0}, {'a', 0x931f918}, {'d', 0x931f918}, {'a', 0x931f918}, {0,0} }; /* This test is meant to ensure we can add a value with key of zero. */ static struct myacts sequence3[] = { {'a', 0x0}, {'a', 0xffffffff}, {'a', 0xffff}, {0,0} }; static struct myacts sequential64[] = { {'a', 1}, {'a', 2}, {'a', 3}, {'a', 4}, {'a', 5}, {'a', 6}, {'a', 7}, {'a', 8}, {'a', 9}, {'a', 10}, {'a', 11}, {'a', 12}, {'a', 13}, {'a', 14}, {'a', 15}, {'a', 16}, {'a', 17}, {'a', 18}, {'a', 19}, {'a', 20}, {'a', 21}, {'a', 22}, {'a', 23}, {'a', 24}, {'a', 25}, {'a', 26}, {'a', 27}, {'a', 28}, {'a', 29}, {'a', 30}, {'a', 31}, {'a', 32}, {'a', 33}, {'a', 34}, {'a', 35}, {'a', 36}, {'a', 37}, {'a', 38}, {'a', 39}, {'a', 40}, {'a', 41}, {'a', 42}, {'a', 43}, {'a', 44}, {'a', 45}, {'a', 46}, {'a', 47}, {'a', 48}, {'a', 49}, {'a', 50}, {'a', 51}, {'a', 52}, {'a', 53}, {'a', 54}, {'a', 55}, {'a', 56}, {'a', 57}, {'a', 58}, {'a', 59}, {'a', 60}, {'a', 61}, {'a', 62}, {'a', 63}, {'a', 64}, {0,0} }; static int runstandardtests = 1; static int g_hideactions = 1; /* showallactions is for debugging tree add/delete and should almost always be zero. */ static int g_showallactions = 0; static char filetest1name[2000]; static struct myacts *filetest1 = 0; static char filetest2name[2000]; static struct myacts *filetest2 = 0; static char filetest3name[2000]; static struct myacts *filetest3 = 0; static char filetest4name[2000]; static struct myacts *filetest4 = 0; static int get_record_id(enum insertorder ord,int indx) { int i = 0; switch(ord) { case increasing: i = increaseorder[indx]; break; case decreasing: i = decreaseorder[indx]; break; case balanced: i = balanceorder[indx]; break; default: printf("FAIL, internal error in test code\n"); exit(1); } return i; } /* We allow a NULL name so this struct acts sort of like a set and sort of like a map. */ static struct example_tentry * make_example_tentry(unsigned k,char *name) { struct example_tentry *mt = (struct example_tentry *)calloc(sizeof(struct example_tentry),1); if(!mt) { printf("calloc fail\n"); exit(1); } mt->mt_key = k; if(name) { mt->mt_name = strdup(name); } return mt; } static void mt_free_func(void *mt_data) { struct example_tentry *m = mt_data; if(!m) { return; } free(m->mt_name); free(mt_data); return; } #ifdef HASHSEARCH static DW_TSHASHTYPE mt_hashfunc(const void *keyp) { /* our key here is particularly simple. */ const struct example_tentry *ml = keyp; return ml->mt_key; } #endif /* HASHSEARCH */ static void printlevel(int level) { int len = 0; int targetlen = 4 + level; int shownlen = 0; char number[10]; len = snprintf(number,sizeof(number),"<%d>",level); printf("%s",number); shownlen = len; while(shownlen < targetlen) { putchar(' '); ++shownlen; } } static int mt_compare_func(const void *l, const void *r) { const struct example_tentry *ml = l; const struct example_tentry *mr = r; if(ml->mt_key < mr->mt_key) { return -1; } if(ml->mt_key > mr->mt_key) { return 1; } return 0; } static void walk_entry(const void *mt_data,DW_VISIT x,int level) { const struct example_tentry *m = *(const struct example_tentry **)mt_data; printlevel(level); printf("Walk on node %s %u %s \n", x == dwarf_preorder?"preorder": x == dwarf_postorder?"postorder": x == dwarf_endorder?"endorder": x == dwarf_leaf?"leaf": "unknown", m->mt_key,m->mt_name); return; } static void value_only_walk_entry(const void *data,DW_VISIT x,int level) { VALTYPE val = (VALTYPE)data; printlevel(level); printf("Walk on node %s 0x%lu\n", x == dwarf_preorder?"preorder": x == dwarf_postorder?"postorder": x == dwarf_endorder?"endorder": x == dwarf_leaf?"leaf": "unknown", (unsigned long)val); return; } #ifndef LIBC_TSEARCH static char * mt_keyprint(const void *v) { static char buf[50]; const struct example_tentry *mt = (const struct example_tentry *)v; buf[0] = 0; snprintf(buf,sizeof(buf),"0x%08x",(unsigned)mt->mt_key); return buf; } static char * value_keyprint(const void *v) { VALTYPE val = (VALTYPE)v; static char buf[50]; buf[0] = 0; snprintf(buf,sizeof(buf),"0x%08lx",(unsigned long)val); return buf; } #endif /* LIBC_TSEARCH */ static int insertrecsbypointer(int max, void **tree, const enum insertorder order) { int indx = 0; for(indx = 0 ; indx < max ; ++indx) { int i = 0; int k = 0; char kbuf[40]; char dbuf[60]; struct example_tentry *mt = 0; struct example_tentry *retval = 0; i = get_record_id(order,indx); snprintf(kbuf,sizeof(kbuf),"%u",i); strcpy(dbuf," data for "); strcat(dbuf,kbuf); printf("insertrec %d\n",i); /* Do it twice so we have test the case where tsearch adds and one where it finds an existing record. */ for (k = 0; k < 2 ;++k) { mt = make_example_tentry(i,dbuf); errno = 0; /* tsearch adds an entry if its not present already. */ retval = dwarf_tsearch(mt,tree, mt_compare_func ); if(retval == 0) { printf("FAIL ENOMEM in search on %d, give up insertrecsbypointer\n",i); exit(1); } else { struct example_tentry *re = 0; re = *(struct example_tentry **)retval; if(re != mt) { if(!k) { printf("FAIL found existing an error %u\n",i); mt_free_func(mt); return 1; } else { printf("found existing ok %u\n",i); } /* Prevents data leak: mt was already present. */ mt_free_func(mt); } else { if(!k) { printf("insert new ok %u\n",i); } else { printf("FAIL new found but expected existing %u\n",i); } /* New entry mt was added. */ } } } } return 0; } static int findrecsbypointer(int max,int findexpected, const void **tree, const enum insertorder order) { int indx = 0; for(indx = 0 ; indx < max ; ++indx) { char kbuf[40]; char dbuf[60]; dbuf[0] = 0; struct example_tentry *mt = 0; struct example_tentry *retval = 0; int i = 0; i = get_record_id(order,indx); snprintf(kbuf,sizeof(kbuf),"%u",i); mt = make_example_tentry(i,dbuf); printf("findrec %d\n",i); retval = dwarf_tfind(mt,(void *const*)tree,mt_compare_func); if(!retval) { if(indx < findexpected) { mt_free_func(mt); printf("FAIL tfind on %s is FAILURE\n",kbuf); return 1; } else { printf("Not found with tfind on %s is ok\n",kbuf); } } else { printf("found ok %u\n",i); if(indx >= findexpected) { mt_free_func(mt); printf("FAIL: found with tfind on %s is FAILURE\n",kbuf); return 1; } else { printf("Found with tfind on %s is ok\n",kbuf); } } mt_free_func(mt); } return 0; } /* The dodump flag is so we can distinguish binarysearch actions from the binarysearch with eppinger mods easily in the test output. The difference is slight and not significant, but the difference is what we look for when we look. */ static int delrecsbypointer(int max,int findexpected, void **tree,const enum insertorder order,int dodump) { int indx = 0; for (indx = 0; indx < max; indx++) { struct example_tentry *mt = 0; struct example_tentry *re3 = 0; void *r = 0; int i = 0; i = get_record_id(order,indx); printf("delrec %d\n",i); mt = make_example_tentry(i,0); r = dwarf_tfind(mt,(void *const*)tree,mt_compare_func); if (r) { /* This is what tdelete will delete. tdelete just removes the reference from the tree, it does not actually delete the memory for the entry itself. In fact there is no way to know for sure what was done just given the return from tdelete. You just just assume the delete worked and use the tfind result to delete your contents if you want to.*/ re3 = *(struct example_tentry **)r; if(indx < findexpected) { ; } else { mt_free_func(mt); printf("FAIL delrecsbypointer should not have found record to delete for %d\n",i); return 1; } r = dwarf_tdelete(mt,tree,mt_compare_func); if (! *tree) { printf("tree itself now empty\n"); } /* We don't want the 'test' node left around. */ if(r) { /* If the node deleted was root, r is really the new root, not the parent. Or r is non-null but bogus. (so don't print). */ printf("tdelete returned parent or something.\n"); } else { printf("tdelete returned NULL, tree now empty.\n"); #ifdef HASHSEARCH printf("Only really means some hash chain is now empty.\n"); #endif /* HASHSEARCH */ } mt_free_func(mt); mt_free_func(re3); } else { if(indx >= findexpected) { ; } else { mt_free_func(mt); printf("FAIL delrecsbypointer should have found record to delete for %d\n",i); return 1; } /* There is no node like this to delete. */ /* We don't want the 'test' node left around. */ mt_free_func(mt); } if (dodump) { dwarf_tdump( *tree,mt_keyprint,"In Delrecs"); } dwarf_twalk( *tree,walk_entry); } return 0; } /* mt must point to data in static storage for this to make any sense. Malloc()ed data or unique static data for this instance mt points at. For example, if there was an immobile array and make_example_tentry() somehow selected a unique entry. */ static int insertonebypointer(void **tree, unsigned long addr,int ct) { struct example_tentry *mt = 0; void *retval = 0; mt = make_example_tentry(addr,0); /* tsearch adds an entry if its not present already. */ retval = dwarf_tsearch(mt,tree, mt_compare_func ); if(retval == 0) { printf("FAIL ENOMEM in search on rec %d adr 0x%lu," " error in insertonebypointer\n", ct,(unsigned long)addr); exit(1); } else { struct example_tentry *re = 0; re = *(struct example_tentry **)retval; if(re != mt) { /* Found existing, error. */ printf("insertonebypointer rec %d addr %lu 0x%lx found record" " preexisting, error\n", ct, (unsigned long)addr, (unsigned long)addr); mt_free_func(mt); return 1; } else { /* inserted new entry, make sure present. */ #ifndef FULL_SPEED_RUN struct example_tentry *mt2 = make_example_tentry(addr,0); retval = dwarf_tfind(mt2,tree,mt_compare_func); mt_free_func(mt2); if(!retval) { printf("insertonebypointer record %d addr 0x%lu " "failed to add as desired," " error\n", ct,(unsigned long)addr); return 1; } #endif /* FULL_SPEED_RUN */ } } return 0; } /* For tfind and tdelete one can use static data and take its address for mt instead of using malloc/free. */ static int deleteonebypointer(void **tree, unsigned addr,int ct) { struct example_tentry *mt = 0; struct example_tentry *re3 = 0; void *r = 0; int err=0; mt = make_example_tentry(addr,0); r = dwarf_tfind(mt,(void *const*)tree,mt_compare_func); if (r) { re3 = *(struct example_tentry **)r; dwarf_tdelete(mt,tree,mt_compare_func); mt_free_func(mt); mt_free_func(re3); } else { printf("deleteonebypointer could not find rec %d ! error! addr" " 0x%lx\n", ct,(unsigned long)addr); mt_free_func(mt); err = 1; } return err; } #ifdef HASHSEARCH /* Only needed for hash based search in a tsearch style. */ #define INITTREE(x,y) x = dwarf_initialize_search_hash(&(x),(y),0) #else #define INITTREE(x,y) #endif /* HASHSEARCH */ static const char * describe_action(char a) { static const char* ad = "add "; static const char* de = "delete "; static const char* un = "unknown"; switch(a) { case 'a': return ad; case 'd': return de; } return un; } static int applybypointer(struct myacts *m, const char *msg, int hideactions, int printwalk, int dumpeverystage) { unsigned ct = 1; void *treesq1 = 0; int errcount = 0; INITTREE(treesq1,mt_hashfunc); printf("special sequence %s\n",msg); for(; m->action_ != 0; m++,ct++) { if(!hideactions) { printf("Action %2u: %s 0x%x val 0x%x\n",ct, describe_action(m->action_), m->action_,m->addr_); } if(m->action_ == 'a') { errcount += insertonebypointer(&treesq1,m->addr_,ct); continue; } if(m->action_ == 'd') { errcount += deleteonebypointer(&treesq1,m->addr_,ct); continue; } printf("Fail applybypointer, bad action %s entry %d.\n",msg,ct); return 1; } dwarf_tdestroy(treesq1,mt_free_func); return errcount; } #ifdef HASHSEARCH static DW_TSHASHTYPE value_hashfunc(const void *keyp) { VALTYPE up = (VALTYPE )keyp; return up; } #endif /* HASHFUNC */ static int value_compare_func(const void *l, const void *r) { VALTYPE lp = (VALTYPE)l; VALTYPE rp = (VALTYPE)r; if(lp < rp) { return -1; } if(lp > rp) { return 1; } return 0; } /* Nothing to free for the 'value' example. */ static void value_node_free(void *valp) { } static int insertbyvalue(void **tree, VALTYPE addr,int ct) { void *retval = 0; /* tsearch adds an entry if its not present already. */ /* Since in this test we do not malloc anything there is no free needed either. Instead we just let tsearch store the value in the pointer in the tree. */ VALTYPE newval = addr; retval = dwarf_tsearch((void *)newval,tree, value_compare_func ); if(retval == 0) { printf("FAIL ENOMEM in search on item %d, value %lu, " "error in insertbyvalue\n", ct, (unsigned long)newval); exit(1); } else { /* Since we insert a value there is no possible distinction to be made between newly-inserted and found-in-tree. */ #ifndef FULL_SPEED_RUN { /* For debugging. */ VALTYPE mt2 = addr; retval = dwarf_tfind((void *)mt2,tree,value_compare_func); if(!retval) { printf("insertone record %d value 0x%lu failed to add" " as desired, error\n", ct,(unsigned long)mt2); return 1; } } #endif /*FULL_SPEED_RUN */ } return 0; } static int deletebyvalue(void **tree, unsigned addr,int ct) { void *r = 0; int err=0; VALTYPE newval = addr; /* We are not mallocing, so nothing to free he tree holds simple values for us. */ r = dwarf_tfind((void *)newval,(void *const*)tree,value_compare_func); if (r) { void *r2 = dwarf_tdelete((void *)newval,tree,value_compare_func); if(r2) { /* tdelete returned parent */ } else { /* tdelete returned NULL, tree now empty */ } } else { printf("deletebyvalue action %d could not find rec! error!" " addr 0x%x\n", addr,ct); err = 1; } return err; } /* This demonstrates using a simple integer as the value saved, as itself, not a pointer, per-se. The various flags are not important except in that they can help in case bugs still exist. */ static int applybyvalue(struct myacts *m, const char *msg, int hideactions, int printwalk, int dumpeverystage) { unsigned ct = 1; void *treesq1 = 0; int errcount = 0; INITTREE(treesq1,value_hashfunc); printf("special sequence %s\n",msg); for(; m->action_ != 0; m++,ct++) { if(!hideactions) { printf("Action %2u: %s 0x%x val 0x%x\n",ct, describe_action(m->action_), m->action_,m->addr_); } if(m->action_ == 'a') { errcount += insertbyvalue(&treesq1,m->addr_,ct); if(ct == 0) { printf("Add done. action# %2d value 0x%x\n", ct,m->addr_); dwarf_tdump(treesq1,value_keyprint,"first sequence2 added"); } else if(dumpeverystage) { dwarf_tdump(treesq1,value_keyprint,"after add"); } continue; } if(m->action_ == 'd') { errcount += deletebyvalue(&treesq1,m->addr_,ct); if(dumpeverystage) { printf("Delete done. action# %2d value 0x%x\n", ct,m->addr_); dwarf_tdump(treesq1,value_keyprint,"after delete"); } continue; } printf("Fail applybyvalue, bad action %s entry %d.\n",msg,ct); return 1; } if(printwalk) { printf("Twalk start, simple value \n"); dwarf_twalk(treesq1,value_only_walk_entry); printf("Twalk end, simple value\n"); dwarf_tdump(treesq1,value_keyprint,"tdump simple value from applybyvalue"); } dwarf_tdestroy(treesq1,value_node_free); return errcount; } static int standard_tests(void) { void *tree1 = 0; int errcount = 0; if(applyby == applybypointer) { printf("Test with increasing input\n"); INITTREE(tree1,mt_hashfunc); errcount += insertrecsbypointer(3,&tree1,increasing); errcount += findrecsbypointer(6,3,(const void **)&tree1,increasing); dwarf_twalk(tree1,walk_entry); dwarf_tdump(tree1,mt_keyprint,"Dump Tree from increasing input"); errcount += delrecsbypointer(6,3,&tree1,increasing,0); #ifdef HASHSEARCH dwarf_tdestroy(tree1,mt_free_func); tree1 = 0; #endif if(tree1) { printf("FAIL: delrecsbypointer of increasing did not empty the tree.\n"); exit(1); } printf("Test twalk with empty tree\n"); dwarf_twalk(tree1,walk_entry); INITTREE(tree1,mt_hashfunc); printf("Insert decreasing, try tdestroy\n"); errcount += insertrecsbypointer(6,&tree1,decreasing); dwarf_twalk(tree1,walk_entry); dwarf_tdestroy(tree1,mt_free_func); tree1 = 0; INITTREE(tree1,mt_hashfunc); printf("Now test with decreasing input and test twalk and tdelete\n"); errcount += insertrecsbypointer(5,&tree1,decreasing); errcount += findrecsbypointer(6,5,(const void **)&tree1,decreasing); dwarf_twalk(tree1,walk_entry); dwarf_tdump(tree1,mt_keyprint,"Dump Tree from decreasing input"); errcount += delrecsbypointer(6,5,&tree1,decreasing,0); #ifdef HASHSEARCH dwarf_tdestroy(tree1,mt_free_func); tree1 = 0; #endif if(tree1) { printf("FAIL: delrecsbypointer of decreasing did not empty the tree.\n"); exit(1); } INITTREE(tree1,mt_hashfunc); printf("Now test with balanced input and test twalk and tdelete\n"); errcount += insertrecsbypointer(4,&tree1,balanced); errcount += findrecsbypointer(6,4,(const void **)&tree1,balanced); dwarf_twalk(tree1,walk_entry); dwarf_tdump(tree1,mt_keyprint,"Dump Tree from balanced input"); errcount += delrecsbypointer(6,4,&tree1,balanced,1); #ifdef HASHSEARCH dwarf_tdestroy(tree1,mt_free_func); tree1 = 0; #endif if(tree1) { printf("FAIL: delrecsbypointer of balanced did not empty the tree.\n"); exit(1); } dwarf_twalk(tree1,walk_entry); if (errcount > 0) { printf("FAIL tsearch test.\n"); exit(1); } errcount += applyby(&sequence1[0],"Sequence 1", g_hideactions,0,0); errcount += applyby(&sequence2[0],"Sequence 2, a", g_hideactions,0,0); } else { errcount += applyby(&sequence2[0],"Sequence 2, b", g_hideactions,0,0); errcount += applyby(&sequence3[0],"Sequence 3", g_hideactions,0,0); errcount += applyby(&sequential64[0],"Sequential 64", g_hideactions,1,0); } return errcount; } static int getaddr(const char *in, unsigned long *addrout) { unsigned long int res = 0; errno = 0; res = strtoul(in,0,0); if(errno) { return 1; } *addrout = res; return 0; } /* Valid input lines start with # (the rest of the line ignored) or a 12345 or d 0x12345 meaning add a tree record or delete one, respectively. Where the value is the key. Leading spaces on a line are not allowed. Only a single space after the 'a' or 'd' and before the value is allowed. */ static int build_filetest(struct myacts **tout, char *pathout, const char *filename,FILE *f) { char buffer[500]; char * buf = &buffer[0]; size_t bufsize = sizeof(buffer); size_t filelen = 0; size_t ct = 0; int done = 0; size_t ixout = 0; struct myacts *recordacts = 0; while(!done) { ssize_t charsread = 0; bufsize = sizeof(buffer); charsread = getline(&buf,&bufsize,f); if(charsread < 0) { done = 1; break; } ++filelen; } rewind(f); /* Leave zeroed entry (at least one) at the end. */ recordacts = calloc(sizeof(struct myacts),filelen+2); *tout = recordacts; strcpy(pathout,filename); done = 0; for(ct = 0; !done && ( ct < filelen); ++ct) { ssize_t charsread = 0; charsread = getline(&buf,&bufsize,f); if(charsread < 0) { done = 1; break; } if(buf[0] == '#') { continue; } if(buf[0] == 'a' && buf[1] == ' ') { int readaddrfail = 0; unsigned long addr = 0; recordacts[ixout].action_ = 'a'; readaddrfail = getaddr(&buf[2],&addr); if(readaddrfail) { fprintf(stderr,"Improper value input, line %lu of file %s\n" "%s\n", (unsigned long)ct,filename,buf); return 1; } recordacts[ixout].addr_ = addr; } else if(buf[0] == 'd' && buf[1] == ' ') { int readaddrfail = 0; unsigned long addr = 0; recordacts[ixout].action_ = 'd'; readaddrfail = getaddr(&buf[2],&addr); if(readaddrfail) { fprintf(stderr,"Improper value input, line %lu of file %s\n" "%s\n", (unsigned long)ct,filename,buf); return 1; } recordacts[ixout].addr_ = addr; } else { fprintf(stderr,"Improper input, line %lu of file %s\n" "%s\n", (unsigned long)ct,filename,buf); return 1; } ixout++; } return 0; } static int fill_in_filetest(const char *filename) { FILE *f = 0; int errcount = 0; f = fopen(filename,"r"); if (!f) { fprintf(stderr,"Open of %s failed",filename); return 1; } if(!filetest1) { errcount += build_filetest(&filetest1,filetest1name,filename,f); } else if(!filetest2) { errcount += build_filetest(&filetest2,filetest2name,filename,f); } else if(!filetest3) { errcount += build_filetest(&filetest3,filetest3name,filename,f); } else if(!filetest4) { errcount += build_filetest(&filetest4,filetest4name,filename,f); } else { printf("Exceeded limit on input files. %s ignored\n",filename); errcount = 1; } fclose(f); return errcount; } static void print_usage(const char *a, const char *b,const char *app) { fprintf(stderr,"%s : %s\n",a,b); fprintf(stderr,"run as\n"); fprintf(stderr," %s [-std] [samplefile]...\n",app); fprintf(stderr,"By default runs standard tests\n"); fprintf(stderr,"with pathnames, standard tests are not run\n"); fprintf(stderr,"unless -std passed in as first arg.\n"); exit(1); } static void readargs(int argc, char **argv) { int ix = 0; int notedstd = 0; int defaultstd = 1; if(argc < 2) { /* No arguments, take defaults. */ return; } for(ix = 1; ix