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  |