1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) University of Cambridge 1995 - 2015 */
6 /* See the file NOTICE for conditions of use and distribution. */
8 /* Functions for maintaining binary balanced trees and some associated
17 /*************************************************
18 * Add entry to non-recipients tree *
19 *************************************************/
21 /* Duplicates are just discarded.
30 tree_add_nonrecipient(const uschar *s)
32 rmark rpoint = store_mark();
33 tree_node *node = store_get(sizeof(tree_node) + Ustrlen(s), is_tainted(s));
34 Ustrcpy(node->name, s);
35 node->data.ptr = NULL;
36 if (!tree_insertnode(&tree_nonrecipients, node)) store_reset(rpoint);
41 /*************************************************
42 * Add entry to duplicates tree *
43 *************************************************/
45 /* Duplicates are just discarded.
49 addr the address is is a duplicate of
55 tree_add_duplicate(const uschar *s, address_item *addr)
57 rmark rpoint = store_mark();
58 tree_node *node = store_get(sizeof(tree_node) + Ustrlen(s), is_tainted(s));
59 Ustrcpy(node->name, s);
60 node->data.ptr = addr;
61 if (!tree_insertnode(&tree_duplicates, node)) store_reset(rpoint);
66 /*************************************************
67 * Add entry to unusable addresses tree *
68 *************************************************/
70 /* Duplicates are simply discarded.
72 Argument: the host item
77 tree_add_unusable(const host_item *h)
79 rmark rpoint = store_mark();
82 sprintf(CS s, "T:%.200s:%s", h->name, h->address);
83 node = store_get(sizeof(tree_node) + Ustrlen(s),
84 is_tainted(h->name) || is_tainted(h->address));
85 Ustrcpy(node->name, s);
86 node->data.val = h->why;
87 if (h->status == hstatus_unusable_expired) node->data.val += 256;
88 if (!tree_insertnode(&tree_unusable, node)) store_reset(rpoint);
93 /*************************************************
94 * Write a tree in re-readable form *
95 *************************************************/
97 /* This function writes out a tree in a form in which it can
98 easily be re-read. It is used for writing out the non-recipients
99 tree onto the spool, for retrieval at the next retry time.
101 The format is as follows:
103 . If the tree is empty, write one line containing XX.
105 . Otherwise, each node is written, preceded by two letters
106 (Y/N) indicating whether it has left or right children.
108 . The left subtree (if any) then follows, then the right subtree.
110 First, there's an internal recursive subroutine.
120 write_tree(tree_node *p, FILE *f)
122 fprintf(f, "%c%c %s\n",
123 (p->left == NULL)? 'N':'Y', (p->right == NULL)? 'N':'Y', p->name);
124 if (p->left != NULL) write_tree(p->left, f);
125 if (p->right != NULL) write_tree(p->right, f);
128 /* This is the top-level function, with the same arguments. */
131 tree_write(tree_node *p, FILE *f)
145 /***********************************************************
146 * Binary Balanced Tree Management Routines *
147 ***********************************************************/
149 /* This set of routines maintains a balanced binary tree using
150 the algorithm given in Knuth Vol 3 page 455.
152 The routines make use of uschar * pointers as byte pointers,
153 so as to be able to do arithmetic on them, since ANSI Standard
154 C does not permit additions and subtractions on void pointers. */
157 /*************************************************
158 * Flags and Parameters *
159 *************************************************/
161 #define tree_lbal 1 /* left subtree is longer */
162 #define tree_rbal 2 /* right subtree is longer */
163 #define tree_bmask 3 /* mask for flipping bits */
166 /*************************************************
167 * Insert a new node into a tree *
168 *************************************************/
170 /* The node->name field must (obviously) be set, but the other
171 fields need not be initialized.
174 treebase pointer to the root of the tree
175 node the note to insert, with name field set
177 Returns: TRUE if node inserted; FALSE if not (duplicate)
181 tree_insertnode(tree_node **treebase, tree_node *node)
183 tree_node *p = *treebase;
184 tree_node **q, *r, *s, **t;
187 node->left = node->right = NULL;
190 /* Deal with an empty tree */
198 /* The tree is not empty. While finding the insertion point,
199 q points to the pointer to p, and t points to the pointer to
200 the potential re-balancing point. */
205 /* Loop to search tree for place to insert new node */
209 int c = Ustrcmp(node->name, p->name);
210 if (c == 0) return FALSE; /* Duplicate node encountered */
212 /* Deal with climbing down the tree, exiting from the loop
213 when we reach a leaf. */
215 q = (c > 0)? &(p->right) : &(p->left);
217 if (p == NULL) break;
219 /* Save the address of the pointer to the last node en route
220 which has a non-zero balance factor. */
222 if (p->balance != 0) t = q;
225 /* When the above loop completes, q points to the pointer to NULL;
226 that is the place at which the new node must be inserted. */
230 /* Set up s as the potential re-balancing point, and r as the
231 next node after it along the route. */
234 r = (Ustrcmp(node->name, s->name) > 0)? s->right : s->left;
236 /* Adjust balance factors along the route from s to node. */
242 if (Ustrcmp(node->name, p->name) < 0)
244 p->balance = tree_lbal;
249 p->balance = tree_rbal;
254 /* Now the World-Famous Balancing Act */
256 a = (Ustrcmp(node->name, s->name) < 0)? tree_lbal : tree_rbal;
258 if (s->balance == 0) s->balance = (uschar)a; /* The tree has grown higher */
259 else if (s->balance != (uschar)a) s->balance = 0; /* It's become more balanced */
260 else /* It's got out of balance */
262 /* Perform a single rotation */
264 if (r->balance == (uschar)a)
281 /* Perform a double rotation There was an occasion when the balancing
282 factors were screwed up by a bug in the code that reads a tree from
283 the spool. In case this ever happens again, check for changing p to NULL
284 and don't do it. It is better to have an unbalanced tree than a crash. */
290 if (r->left == NULL) return TRUE; /* Bail out if tree corrupt */
299 if (r->right == NULL) return TRUE; /* Bail out if tree corrupt */
307 s->balance = (p->balance == (uschar)a)? (uschar)(a^tree_bmask) : 0;
308 r->balance = (p->balance == (uschar)(a^tree_bmask))? (uschar)a : 0;
312 /* Finishing touch */
317 return TRUE; /* Successful insertion */
322 /*************************************************
323 * Search tree for node by name *
324 *************************************************/
329 name key to search for
331 Returns: pointer to node, or NULL if not found
335 tree_search(tree_node *p, const uschar *name)
339 int c = Ustrcmp(name, p->name);
340 if (c == 0) return p;
341 p = c < 0 ? p->left : p->right;
348 /*************************************************
349 * Walk tree recursively and execute function *
350 *************************************************/
355 f function to execute for each name-value-pair
356 ctx context data for f
360 tree_walk(tree_node *p, void (*f)(uschar*, uschar*, void*), void *ctx)
363 f(p->name, p->data.ptr, ctx);
364 tree_walk(p->left, f, ctx);
365 tree_walk(p->right, f, ctx);
370 /* Add a node to a tree, ignoring possibility of duplicates */
373 tree_add_var(uschar * name, uschar * val, void * ctx)
375 tree_node ** root = ctx;
376 tree_node * node = store_get(sizeof(tree_node) + Ustrlen(name), is_tainted(name));
377 Ustrcpy(node->name, name);
378 node->data.ptr = val;
379 (void) tree_insertnode(root, node);
382 /* Duplicate a tree */
385 tree_dup(tree_node ** dstp, tree_node * src)
387 tree_walk(src, tree_add_var, dstp);