Source File modules/rx/rx_search.c

    1    | /***************************************
    2    |   $Revision: 1.33 $
    3    |
    4    |   Radix tree (rx).  rx_search.c - functions to search nodes of the tree
    5    |
    6    |   Status: NOT REVUED, TESTED, COMPLETE
    7    |
    8    |   Design and implementation by: Marek Bukowy
    9    |
    10   |   ******************/ /******************
    11   |   Copyright (c) 1999                              RIPE NCC
    12   |
    13   |   All Rights Reserved
    14   |
    15   |   Permission to use, copy, modify, and distribute this software and its
    16   |   documentation for any purpose and without fee is hereby granted,
    17   |   provided that the above copyright notice appear in all copies and that
    18   |   both that copyright notice and this permission notice appear in
    19   |   supporting documentation, and that the name of the author not be
    20   |   used in advertising or publicity pertaining to distribution of the
    21   |   software without specific, written prior permission.
    22   |
    23   |   THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
    24   |   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
    25   |   AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
    26   |   DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
    27   |   AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
    28   |   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
    29   |   ***************************************/
    30   |
    31   |
    32   | #include <erroutines.h>
    33   | #include <rxroutines.h>
    34   | #include <stubs.h>
    35   |
    36   | #include "iproutines.h"
    37   |
    38   | /***************************************************************************/
    39   |
    40   | /*++++++++++++++
    41   | Descends the given tree following the last prefix bit to get [past]
    42   | the node with the given prefix.
    43   | It fills up a stack of COPIES of nodes, including glue nodes.
    44   |
    45   | Then it also sets the number of elements on the stack:
    46   | set maxdepth to the position where a next one would be written
    47   | ( = last + 1, or number of nodes pushed)
    48   |
    49   |    The dmodes:
    50   |
    51   |    RX_STK_QUERY_NOGLUE = (search exact/less spec) stop when
    52   |                          * the current prefix length >= newprefix length
    53   |                          * the current prefix does not match anymore
    54   |                          * do not add glue nodes
    55   |
    56   |    RX_STK_QUERY_ALLNOD = as above, except that the glue and data nodes are
    57   |                          treated equally (i.e. glue nodes are not skipped)
    58   |
    59   |    RX_STK_CREAT        = descend until the next non-glue node past the one found
    60   |                          in exact mode (for creation)
    61   |
    62   | ++++++++++++++*/
    63   |
    64   | er_ret_t
    65   | rx_build_stack(rx_nodcpy_t    stack[],
    66   |                int            *maxdepth,
    67   |                rx_tree_t      *tree,
    68   |                ip_prefix_t    *newpref,
    69   |                rx_stk_mt      dmode
    70   |                )
    71   | {
    72   |   register rx_node_t *curnode;
    73   |   register int link, quit_now=0;
    74   |   register int tracedet = ER_is_traced( FAC_RX, ASP_RX_STKBLD_DET);
    75   |   char bbf[IP_PREFSTR_MAX];
    76   |
    77   |   if( ER_is_traced( FAC_RX, ASP_RX_STKBLD_GEN)) {
    78   |     IP_pref_b2a( newpref , bbf, IP_PREFSTR_MAX);
    79   |     ER_dbg_va(FAC_RX, ASP_RX_STKBLD_GEN,
    80   |             "rx_build_stack: searching for %s in mode %d", bbf, dmode);
    81   |   }
    82   |
    83   |   *maxdepth = 0;
    84   |
    85   |   if ( tree -> num_nodes == 0) {
    86   |     /* The tree was empty. */
    87   |     return RX_OK;
    88   |   }
    89   |
    90   |   curnode = tree->top_ptr;
    91   |   /* this works for RAM, for SQL one would have to call a 'getsqlnode' here*/
    92   |
    93   |   /* OK, there is at least one node. Descend the tree */
    94   |   /* as long as the correct bit length is not exceeded*/
    95   |   /* or a glue is being found (take the last non-glue node then) */
    96   |   /* or you run out of nodes in the direction of descending*/
    97   |
    98   |   do {
    99   |     /* check at the current node, where the one we look for would fit*/
    100  |     /* (the second argument of IP_addr_bit_get starts with 0,*/
    101  |     /* so this effectively looks at the bit next to the last significant bit*/
    102  |     /* of the current node*/
    103  |
    104  |     link = IP_addr_bit_get( & newpref->ip, curnode->prefix.bits );
    105  |
    106  |     /* check conditions for leaving the loop    */
    107  |     if(curnode->child_ptr[link] == NULL) {
    108  |       /* end of branch. quit after adding the current node to the stack*/
    109  |       /* (or before - subject to bit test in QUERY mode)*/
    110  |       quit_now = 1;
    111  |     }
    112  |     else {
    113  |     /* check the node.
    114  |        BIG DIFFERENCE between the modes:
    115  |        in CREAT we don't mind the stack to go too deep,
    116  |        in QUERY it can lead to false answers
    117  |        (e.g. a /24 is found for a /23 query).
    118  |
    119  |        So this must be "peeled off the stack" later in the search routine,
    120  |        if both types of stack are to work properly with query searches.
    121  |     */
    122  |
    123  |
    124  |       if( curnode->prefix.bits > newpref->bits ) {
    125  |         /* deep enough.*/
    126  |         quit_now = 2;
    127  |       }
    128  |
    129  |       if(dmode == RX_STK_CREAT && curnode->glue) {
    130  |         /* mode: creation. */
    131  |         /* Cancel quitting if glue -- in CREAT mode the stack building */
    132  |         /* should stop at the next real (non-glue) node.*/
    133  |         /* ("next" meaning following link #0)*/
    134  |         quit_now = 0;
    135  |       }
    136  |     }
    137  |
    138  |     /* now that the conditions for leaving the loop after the node is
    139  |        added on the stack, see if we shouldn't leave the loop BEFOREHAND */
    140  |
    141  |     /* In query mode, we should quit as soon as we see a mismatch */
    142  |
    143  |     if(dmode != RX_STK_CREAT
    144  |        && 0 != IP_addr_cmp(&curnode->prefix.ip, &newpref->ip,
    145  |                            curnode->prefix.bits) ) {
    146  |         /*QUIT NOW! (but add this node)*/
    147  |       quit_now = 4;
    148  |     }
    149  |
    150  |     /* push the current node on the stack. RAM only.*/
    151  |     /* */
    152  |     /* (unless quit_now is 64 which means do NOT copy the current node.*/
    153  |     /**/
    154  |     /* In CREAT and QUERY_ALLNOD modes, push everything. */
    155  |     /* In QUERY_NOGLUE mode, only non-glues.*/
    156  |
    157  |     if( /* quit_now < 64 &&           disabled as 64 is not in use right now */
    158  |        (dmode != RX_STK_QUERY_NOGLUE || curnode->glue == 0 )) {
    159  | 	memcpy( & stack[*maxdepth].cpy, curnode, sizeof(rx_node_t));
    160  | 	stack[*maxdepth].srcptr = curnode;
    161  | 	stack[*maxdepth].srckey = SQ_NOKEY;
    162  | 	stack[*maxdepth].tree = tree;
    163  | 	(*maxdepth)++;
    164  |     }
    165  |
    166  |     /* make debug info.*/
    167  |
    168  |     if( tracedet ) {
    169  |       IP_pref_b2a( & curnode->prefix , bbf, IP_PREFSTR_MAX );
    170  |       ER_dbg_va(FAC_RX, ASP_RX_STKBLD_DET,
    171  |                 "rx_build_stack: %s%d at %s%s (stk len: %d)",
    172  |                 quit_now ? "stop/" : "link ",
    173  |                 quit_now ? quit_now : link,
    174  |                 bbf, ( curnode->glue ) ? " ++glue++" : "",
    175  |                 *maxdepth  );
    176  |     }
    177  |
    178  |     curnode = curnode -> child_ptr[link];
    179  |
    180  |   } while( !quit_now );
    181  |
    182  |   return RX_OK;
    183  | }
    184  |
    185  | /***************************************************************************/
    186  | /*+++++++++
    187  |    helper for the nod_search routine:
    188  |
    189  |    allocate a new node copy struct, copy the struct and add to nodlist
    190  | ++++++++++*/
    191  |
    192  | static
    193  | er_ret_t
    194  | rx_nod_append( GList **nodlist, rx_nodcpy_t *element)
    195  | {
    196  |   rx_nodcpy_t *newcpy;
    197  |   er_ret_t err;
    198  |
    199  |   if( (err=wr_calloc( (void **) & newcpy, 1, sizeof(rx_nodcpy_t))) != UT_OK) {
    200  |     return err; /*    die;*/
    201  |   }
    202  |   memcpy(newcpy, element, sizeof(rx_nodcpy_t));
    203  |   (*nodlist) = g_list_prepend( *nodlist, newcpy );
    204  |
    205  |   return RX_OK;
    206  | }
    207  |
    208  |
    209  |
    210  |
    211  | /***************************************************************************/
    212  |
    213  | /*+++++++++++
    214  |   helper for MORE specific lookup in rx_nod_search
    215  |
    216  |   adds a node to the list of answers.
    217  | +++++++++++*/
    218  |
    219  | static
    220  | er_ret_t
    221  | rx_walk_hook_addnode(rx_node_t *node, int level, int nodecounter,
    222  |                      void *userptr)
    223  | {
    224  |   rx_nodcpy_t nodcpy;
    225  |   hook_addnode_userdat_t *userdat = userptr;
    226  |
    227  |
    228  |   /* do not append glue nodes*/
    229  |   if( node->glue == 1 ) return RX_OK;
    230  |
    231  |   /* in RAM mode, do not copy the node.*/
    232  |   /*  memcpy( &nodcpy.cpy, node, sizeof(rx_node_t));*/
    233  |
    234  |   /* XXX reset to 0 to avoid warnings from workshop: but it
    235  |      slows things down! */
    236  |   memset( &nodcpy.cpy, 0, sizeof(rx_node_t));
    237  |
    238  |   nodcpy.srcptr = node;
    239  |   nodcpy.srckey = SQ_NOKEY;
    240  |   nodcpy.tree = userdat->tree;
    241  |
    242  |   return rx_nod_append( userdat->nodlist, &nodcpy);
    243  | }
    244  |
    245  |
    246  | /***************************************************************************/
    247  |
    248  | /*+++++++++++
    249  |   helper for DBLS lookup in rx_nod_search
    250  |
    251  |   adds a node to the list of answers.
    252  | +++++++++++*/
    253  |
    254  | static
    255  | er_ret_t
    256  | rx_walk_hook_adddoubles(rx_node_t *node, int level, int nodecounter,
    257  |                         void *userptr)
    258  | {
    259  |   rx_nodcpy_t nodcpy;
    260  |   hook_addnode_userdat_t  *userdat = userptr;
    261  |   int leaves = g_list_length(node->leaves_ptr);
    262  |   char buf[1024];
    263  |
    264  |   /* do not append glue nodes*/
    265  |   if( node->glue == 1 ) return RX_OK;
    266  |
    267  |
    268  |   /* add only nodes with more than 1 dataleaf*/
    269  |   if( leaves < 2 ) return RX_OK;
    270  |
    271  |   if( ER_is_traced( FAC_RX, ASP_RX_SRCH_DET)) {
    272  |     rx_nod_print(node, buf, 1024);
    273  |     ER_dbg_va(FAC_RX, ASP_RX_SRCH_DET,
    274  |               "rx_walk_hook_adddoubles: %30s, %d leaves", buf, leaves);
    275  |   }
    276  |
    277  |   /*  memcpy( &nodcpy.cpy, node, sizeof(rx_node_t));*/
    278  |   nodcpy.srcptr = node;
    279  |   nodcpy.srckey = SQ_NOKEY;
    280  |   nodcpy.tree = userdat->tree;
    281  |
    282  |   return rx_nod_append( userdat->nodlist, &nodcpy);
    283  | }
    284  |
    285  |
    286  | /***************************************************************************/
    287  | er_ret_t
    288  | rx_nod_search (
    289  |                rx_srch_mt  search_mode,
    290  |                int         par_a,
    291  |                int         par_b,
    292  |                /* see rx_asc_search() for explanation */
    293  |                rx_tree_t  *tree,           /* tree ptr*/
    294  |                ip_prefix_t  *prefix,          /* binary prefix*/
    295  |
    296  |                rx_nodcpy_t stack[],         /* stack==array of node_copies*/
    297  |                int         stackcount,      /* number of element on the stack,*/
    298  |                                             /* can come from a creat stack!*/
    299  |
    300  |                GList       **nodlist,       /* answers go here*/
    301  |                int         max_count        /* max # of answers*/
    302  |                )
    303  |      /*
    304  |         searches the stack for a given prefix, finds *nodes* in the stack
    305  |         and appends *copies of the nodes* to the nodlist;
    306  |
    307  |         finds
    308  |         0 or 1 nodes for exact search
    309  |         0 or 1 nodes for exless (0 if no less specific node found)
    310  |         any number (incl. 0) for {more|less}^n-m specific
    311  |
    312  |        returns errcode.
    313  |
    314  |
    315  |      */
    316  | {
    317  |   char buf[1024];
    318  |   int sps = stackcount-1;       /* stack position.*/
    319  |   int depthcounter=0;
    320  |   er_ret_t err=RX_OK;
    321  |   int i;
    322  |   hook_addnode_userdat_t datstr;
    323  |   er_ret_t (*hook_function)();  /* pointer to the walk_hook function*/
    324  |                                 /* (see MORE spec lookup)*/
    325  |
    326  |   /* structure for carrying data to walk_tree hook functions, used only
    327  |      in MORE, DBLS and RANG search modes
    328  |   */
    329  |   datstr.nodlist = nodlist;
    330  |   datstr.tree    = tree;
    331  |   datstr.prefix  = prefix;
    332  |
    333  |
    334  |   if( ER_is_traced( FAC_RX, ASP_RX_SRCH_BOT)) {
    335  |     IP_pref_b2a( prefix , buf, IP_PREFSTR_MAX);
    336  |     ER_dbg_va(FAC_RX, ASP_RX_SRCH_BOT,
    337  |               "rx_nod_search: searching for %s in mode %d (%s)",
    338  | 	      buf, search_mode, RX_text_srch_mode(search_mode) );
    339  |   }
    340  |
    341  |   /* in non-CREAT modes, glue nodes are skipped anyway.
    342  |      (they should normally not be there if the stack was created in
    343  |      the STK_QUERY mode, but it's possible to use a CREAT stack too).
    344  |
    345  |      It's also possible that the stack is too deep.
    346  |      So, truncate the stack to the last non-glue node
    347  |      of the length <= search term.
    348  |      otherwise a /24 would be returned for a /23 query.
    349  |
    350  |      For LESS SPECIFIC searches one has to peel off entries
    351  |      whose prefixes do not contain the search term,
    352  |   */
    353  |
    354  |   if( search_mode != RX_SRCH_CREAT ) {
    355  |
    356  |     while( sps >= 0 ) {
    357  |       char *reason = NULL;
    358  |
    359  |       if( stack[sps].cpy.prefix.bits > prefix->bits ) {            /* too deep*/
    360  | 	reason = "2deep";
    361  |       }
    362  |       else if( 0 != IP_addr_cmp(& stack[sps].cpy.prefix.ip, &prefix->ip,
    363  | 				stack[sps].cpy.prefix.bits) ) {   /* mismatch */
    364  | 	reason = "mismatch";
    365  |       }
    366  |       else if ( search_mode != RX_SRCH_MORE && search_mode != RX_SRCH_DBLS
    367  | 		&& search_mode != RX_SRCH_RANG && stack[sps].cpy.glue == 1 ) {                    /* is glue*/
    368  | 	reason = "glue";
    369  |       }
    370  | #if 0
    371  |       /* mhm. it can't be limited here, must be done in RP */
    372  |       else if ( search_mode == RX_SRCH_LESS && par_a == 1
    373  | 		&& stack[sps].cpy.prefix.bits == prefix->bits ) {  /* too deep*/
    374  | 	reason = "2deep4less";
    375  |       }
    376  | #endif
    377  |
    378  |       else {
    379  |
    380  | 	break;  /* stop peeling off */
    381  |       }
    382  |
    383  |       if( ER_is_traced( FAC_RX, ASP_RX_SRCH_DET)) {
    384  | 	rx_nod_print( & stack[sps].cpy , buf, IP_PREFSTR_MAX);
    385  | 	ER_dbg_va(FAC_RX, ASP_RX_SRCH_DET,
    386  | 		  "rx_nod_search: peeling off %d: %s (%s)", sps, buf, reason);
    387  |       }
    388  |       sps--;
    389  |     }
    390  |   }
    391  |
    392  |   /* nothing left on the stack. Sorry.*/
    393  |   /* we allow that for more spec search -- this means*/
    394  |   /* that the search term is a shorter prefix than the one*/
    395  |   /* in the top node. Possibly it's 0/0 which is valid for more spec search.*/
    396  |
    397  |   if( search_mode != RX_SRCH_MORE && search_mode != RX_SRCH_DBLS
    398  |       && sps < 0 ) {
    399  |     return RX_OK;
    400  |   }
    401  |
    402  |   switch(search_mode) {
    403  |   case RX_SRCH_EXACT:
    404  |   case RX_SRCH_CREAT:
    405  |     /* go up the tree (stack) and exit when the proper prefix is found.*/
    406  |     /* For RX_SRCH_EXACT skip glue nodes, for RX_SRCH_CREAT take all.*/
    407  |     /* They may contain a valid prefix, so watch out.*/
    408  |
    409  |     while(sps >= 0) {
    410  |
    411  |       if( ER_is_traced( FAC_RX, ASP_RX_SRCH_DET)) {
    412  |         rx_nod_print(& stack[sps].cpy, buf, 1024);
    413  |         ER_dbg_va(FAC_RX, ASP_RX_SRCH_DET,
    414  |                   "rx_nod_search: position %d: %s", sps, buf);
    415  |       }
    416  |
    417  |       if ( search_mode == RX_SRCH_EXACT
    418  |            && stack[sps].cpy.glue ) {
    419  |         die;
    420  |       }
    421  |
    422  |       if ( memcmp( & stack[sps].cpy.prefix,
    423  |                    prefix,
    424  |                    sizeof(ip_prefix_t)) == 0 ) {
    425  |         /* FOUND!!*/
    426  |         /* add to the nodlist.*/
    427  |
    428  |         if( (err=rx_nod_append( nodlist, & stack[sps])) != RX_OK ) {
    429  |           return err;
    430  |         }
    431  |
    432  |         ER_dbg_va(FAC_RX, ASP_RX_SRCH_BOT, "rx_nod_search: found!");
    433  |         break;
    434  |       }
    435  |       sps--;
    436  |     }
    437  |     break;
    438  |
    439  |   case RX_SRCH_EXLESS:
    440  |     /* just fetch the last element off the stack (if any). */
    441  |     /* Must be non-glue for EXLESS.*/
    442  |
    443  |     if( sps >= 0 ) {
    444  |       rx_nod_append( nodlist, & stack[sps]);
    445  |     }
    446  |
    447  |     /* else : nothing found.*/
    448  |     /* For EXLESS: check if the stack contains only non-glue nodes.*/
    449  |     /* If it contains a glue, it means it was created in the CREAT mode,*/
    450  |     /* which renders the above algorithm absolutely useless. Then crash,*/
    451  |     /* this is a programmer's error.*/
    452  |
    453  |     while( sps >= 0 ) {
    454  |       if( stack[sps].cpy.glue ) {
    455  |         die;
    456  |       }
    457  |       sps--;
    458  |     }
    459  |
    460  |     break;
    461  |
    462  |   case RX_SRCH_LESS:
    463  |     while( sps >= 0 && depthcounter < par_a ) {
    464  |       if( stack[sps].cpy.glue == 0 ) {
    465  |         rx_nod_append( nodlist, & stack[sps]);
    466  |         depthcounter++;
    467  |       }
    468  |       sps--;
    469  |     }
    470  |     break;
    471  |
    472  |   case RX_SRCH_MORE:
    473  |   case RX_SRCH_DBLS:   /* special (debug?) mode : find nodes with multiple*/
    474  |                        /* data leaves. Much like more specific, except that*/
    475  |                        /* most nodes will be skipped.*/
    476  |                        /* The difference is in calling another hook function*/
    477  |     hook_function = ( search_mode == RX_SRCH_MORE )
    478  |       ? rx_walk_hook_addnode
    479  |       : rx_walk_hook_adddoubles;
    480  |
    481  |     /* the result of a more spec search should NOT contain the object exactly*/
    482  |     /* matching the query, even if it exists in the database. So two walks are */
    483  |     /* performed, one for each child (if it exists). */
    484  |     /* MEMORY IMPLEMENTATION ONLY FOR THE MOMENT*/
    485  |
    486  |     /* start from the top node if the searched prefix is between the
    487  |        top node and the first node on the stack (i.e. the first node is
    488  |        contained within the search term) */
    489  |
    490  |     /* COVERS THE CASE 0.0.0.0/0 */
    491  |     /* or any other prefix that the tree might be set to represent,*/
    492  |     /* but there is no actual object for it (not even glue)*/
    493  |
    494  |     if( sps < 0 ) {
    495  |       if( tree->num_nodes > 0         /* there is any node in the tree */
    496  | 	  && 0 == IP_addr_cmp( & prefix->ip,
    497  | 			       & stack[0].cpy.prefix.ip,
    498  | 			       prefix->bits) ) {     /* addr match */
    499  | 	rx_walk_tree( tree->top_ptr, hook_function,
    500  | 		      /* RX_WALK_REVERS | */ RX_WALK_SKPGLU, /* skip glue nodes while counting*/
    501  | 		      par_a, /* display this many levels */
    502  | 		      0, 0, &datstr, &err);
    503  | 	if( err != RX_OK ) {
    504  | 	  return err;
    505  | 	}
    506  |       }
    507  |     } /* if nothing on stack */
    508  |     else {
    509  |
    510  |       /* walk from this node if it matches the query prefix and is
    511  |        long enough (if it is shorter, then it will harvest too many
    512  |        results
    513  |       */
    514  |       if(  prefix->bits <= stack[sps].srcptr->prefix.bits
    515  | 	   && 0 == IP_addr_cmp( & stack[sps].srcptr->prefix.ip,
    516  | 				& prefix->ip,
    517  | 				prefix->bits) ) {
    518  | 	rx_walk_tree( stack[sps].srcptr,  hook_function,
    519  | 		      /* RX_WALK_REVERS | */ RX_WALK_SKPGLU, /* skip glue nodes while counting*/
    520  | 		      par_a, /* display up to this max length*/
    521  | 		      0, 0, &datstr, &err);
    522  | 	if( err != RX_OK ) {
    523  | 	  return err;
    524  | 	}
    525  |       }
    526  |       else {
    527  | 	/* or walk the child nodes otherwise (still check the address) */
    528  |
    529  | 	for( i = 1; i >= 0; i--) {
    530  | 	  if( stack[sps].cpy.child_ptr[i] != NULL ) {
    531  | 	    if( ER_is_traced( FAC_RX, ASP_RX_SRCH_DET)) {
    532  | 	      IP_pref_b2a(& stack[sps].cpy.child_ptr[i]->prefix, buf, 1023);
    533  | 	    }
    534  |
    535  | 	    if( 0 == IP_addr_cmp( & stack[sps].cpy.child_ptr[i]->prefix.ip,
    536  | 				  & prefix->ip,
    537  | 				  prefix->bits) ) {
    538  |
    539  | 	      ER_dbg_va(FAC_RX, ASP_RX_SRCH_DET,
    540  | 			"rx_nod_search: digging child %d: %s", i, buf);
    541  |
    542  | 	      rx_walk_tree( stack[sps].cpy.child_ptr[i],  hook_function,
    543  | 			    /* RX_WALK_REVERS | */ RX_WALK_SKPGLU, /* skip glue nodes while counting*/
    544  | 			    par_a, /* display this many levels */
    545  | 			    0, 0, &datstr, &err);
    546  | 	      if( err != RX_OK ) {
    547  | 		return err;
    548  | 	      }
    549  |           }
    550  | 	    else {
    551  | 	      ER_dbg_va(FAC_RX, ASP_RX_SRCH_DET,
    552  | 			"rx_nod_search: prefix mismatch with child %d: %s",
    553  | 			i, buf);
    554  | 	    }
    555  | 	  }
    556  | 	}
    557  |       } /* if node does not match, dig child nodes */
    558  |
    559  |     }
    560  |     break;
    561  |
    562  |   case RX_SRCH_RANG:
    563  |     /* OK, start from the node at the end of the stack (exless match including
    564  |        glue nodes) then
    565  |
    566  |
    567  |        if its prefix length is
    568  |          OK -> found! descend from here as long as the prefixes are in range
    569  |          shorter -> apparently there is even no such glue node. come back down
    570  |                     one step
    571  |
    572  |     */
    573  |
    574  |     i = sps;               /* go up the tree (down the stack) */
    575  |                            /* until too far (one node too much, after >= )*/
    576  |     while( i >= 0 && stack[i].cpy.prefix.bits >= prefix->bits ) {
    577  |       i--;
    578  |     }
    579  |
    580  |     /* look where you are:*/
    581  |
    582  |     if( i < 0 )          /* it was the top object, but its prefix was too long*/
    583  |       i=0;               /* take the top object as the base*/
    584  |     else
    585  |       i++;               /* went one too much, now come back one step*/
    586  |
    587  |
    588  |     rx_walk_tree( stack[i].srcptr,  rx_walk_hook_addnode,
    589  |                   RX_WALK_PRFLEN, /* skip glue nodes while counting*/
    590  |                   par_a, /* display up to this max length*/
    591  |                   0, 0, &datstr, &err);
    592  |     if( err != RX_OK ) {
    593  |       return err;
    594  |     }
    595  |
    596  |     break;
    597  |
    598  |     /* return RX_NOYETI;*/
    599  |     /*not implemented*/
    600  |     /*    die; */
    601  |   default:
    602  |     die; /* are you nuts??*/
    603  |   }
    604  |
    605  |   return err;
    606  |
    607  | }
    608  |
    609  |
    610  |
    611  | /*****************************************************************************/
    612  |
    613  | /*+++++++++++++
    614  |   builds a stack for this prefix, finds *nodes* in the stack
    615  |   and appends *copies of the data leaves* to the LL of answers;
    616  |
    617  |   sorts by SQL object keys and uniq's the data
    618  |
    619  |   finds:
    620  |   0 or 1 nodes for exact search
    621  |   0 or 1 nodes for exless (0 if no less specific node found)
    622  |   any number (incl. 0) for {more|less}-n specific
    623  |
    624  |   then copies the nodes/dataleaves to the answer structs and appends them
    625  |   to the given LL. So, effectively, the number of answers can be
    626  |   anything from 0 to infinity, because objects may be duplicate
    627  |   even at the same node.
    628  |
    629  |   returns errcode.
    630  |
    631  |   algorithm:
    632  |
    633  |   builds stack[MAXBIT (==128)];
    634  |
    635  |   if( more/less-depth && par_a == 0)
    636  |
    637  |   run rx_nod_search, then
    638  |
    639  |   if(more spec) rx_nod_walk(maxdepth=n, append_to_LL() );
    640  |   if(less spec) do { append(LL, stack[i]) } while(i-- && n--);
    641  |   otherwise just set LL
    642  |
    643  |
    644  |   The routine provides _at_least_ max_count answers.
    645  |   It will *try* to stop after max_count as soon as possible
    646  |   - but it's the higher level routine that should do the final cut.
    647  | +++++++++++++++*/
    648  |
    649  | er_ret_t
    650  | RX_bin_search (
    651  |                rx_srch_mt  search_mode,
    652  |                int         par_a,
    653  |                int         par_b,
    654  |                rx_tree_t  *tree,           /* tree ptr*/
    655  |                ip_prefix_t *prefix,         /* binary prefix*/
    656  |                GList       **datleaves,    /* data leaves go here*/
    657  |                int         max_count
    658  |                )
    659  |
    660  | {
    661  |   rx_nodcpy_t  stack[128];
    662  |   int k;
    663  |   int stkcnt, resnum = 0, maxleaves;
    664  |   GList  *nodlist = NULL, *nitem;
    665  |   rx_node_t *curnode;
    666  |   rx_nodcpy_t *curcpy;
    667  |   rx_datref_t *datref;
    668  |   rx_stk_mt     dmode;
    669  |
    670  |   /* more specific node search may start from a glue node, */
    671  |   /* for all others the stack should not contain glues.*/
    672  |
    673  |   dmode = ( search_mode == RX_SRCH_MORE
    674  |             || search_mode == RX_SRCH_DBLS
    675  |             || search_mode == RX_SRCH_RANG )
    676  |     ? RX_STK_QUERY_ALLNOD
    677  |     : RX_STK_QUERY_NOGLUE;
    678  |
    679  |   rx_build_stack(stack, &stkcnt, tree, prefix, dmode);
    680  |
    681  |   rx_nod_search( search_mode, par_a, par_b, tree, prefix,
    682  |                  stack, stkcnt, &nodlist, 1000);
    683  |
    684  |   ER_dbg_va(FAC_RX, ASP_RX_SRCH_BOT, "RX_bin_search: processing nodes");
    685  |
    686  |   for( nitem = g_list_first(nodlist);
    687  |        nitem != NULL;
    688  |        nitem = g_list_next(nitem)) {
    689  |
    690  |     resnum++;
    691  |     curcpy = nitem->data;
    692  |
    693  |     /*
    694  |       if memory mode includes RAM:
    695  |       * do not expect copies of nodes in the list received from bin_search.
    696  |       * iterate through data leaves with g_list_nth_data.
    697  |       */
    698  |
    699  |     curnode = curcpy->srcptr;
    700  |
    701  |     /*    rx_nod_print( curnode, buf, 1024 );*/
    702  |
    703  |     maxleaves = g_list_length(curnode->leaves_ptr);
    704  |     /*    fprintf(stderr,"###node %d, %d dataleaves attached:", i, maxleaves);*/
    705  |
    706  |     /* iterate through dataleafs attached to this node*/
    707  |     for(k=0; k<maxleaves; k++) {
    708  |       rx_dataleaf_t *leafptr = g_list_nth_data(curnode->leaves_ptr, k);
    709  |
    710  |       /*
    711  | 	 check the conditions to add the leaf:
    712  |
    713  | 	 XXX never add composed inetnum for exact prefix search
    714  | 	 (but do for exact range search...) - must be solved in upper layer.
    715  |
    716  |       */
    717  |
    718  |
    719  |       /* add*/
    720  |
    721  |       dieif( wr_calloc( (void **) &datref,
    722  | 			sizeof(rx_datref_t), 1) != UT_OK);
    723  |       datref->leafptr = leafptr;
    724  |       /* srckey and excluded fields are initialised to 0 by calloc */
    725  |
    726  |       *datleaves = g_list_prepend(*datleaves, datref);
    727  |     }
    728  |   }
    729  |
    730  |   wr_clear_list( &nodlist );
    731  |
    732  |   ER_dbg_va(FAC_RX, ASP_RX_SRCH_BOT,
    733  |             "RX_bin_search: found %d nodes", resnum);
    734  |
    735  |
    736  |   /* the LL of answers (*datleaves) contains pointers to answer structs,
    737  |      that SHOULD BE NORMALIZED HERE (==with no redundant entries)
    738  |   */
    739  |
    740  | return RX_OK;
    741  | }
    742  |