Source File rx/rx_search.c

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