1    | /***************************************
2    |   $Revision: 1.35 $
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_DET)) {
335  |     IP_pref_b2a( prefix , buf, IP_PREFSTR_MAX);
336  |     ER_dbg_va(FAC_RX, ASP_RX_SRCH_GEN,
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_DET, "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  |   unsigned 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_DET, "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_GEN,
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  |