xref: /aosp_15_r20/external/e2fsprogs/lib/support/dict.c (revision 6a54128f25917bfc36a8a6e9d722c04a0b4641b6)
1 /*
2  * Dictionary Abstract Data Type
3  * Copyright (C) 1997 Kaz Kylheku <[email protected]>
4  *
5  * Free Software License:
6  *
7  * All rights are reserved by the author, with the following exceptions:
8  * Permission is granted to freely reproduce and distribute this software,
9  * possibly in exchange for a fee, provided that this copyright notice appears
10  * intact. Permission is also granted to adapt this software to produce
11  * derivative works, as long as the modified versions carry this copyright
12  * notice and additional notices stating that the work has been modified.
13  * This source code may be translated into executable form and incorporated
14  * into proprietary software; there is no requirement for such software to
15  * contain a copyright notice related to this source.
16  *
17  * $Id: dict.c,v 1.40.2.7 2000/11/13 01:36:44 kaz Exp $
18  * $Name: kazlib_1_20 $
19  * The work has been modified.
20  */
21 
22 #define DICT_NODEBUG
23 
24 #ifdef __GNUC__
25 #define EXT2FS_ATTR(x) __attribute__(x)
26 #else
27 #define EXT2FS_ATTR(x)
28 #endif
29 
30 #include "config.h"
31 #include <stdlib.h>
32 #include <stddef.h>
33 #ifdef DICT_NODEBUG
34 #define dict_assert(x)
35 #else
36 #include <assert.h>
37 #define dict_assert(x) assert(x)
38 #endif
39 #define DICT_IMPLEMENTATION
40 #include "dict.h"
41 
42 #ifdef KAZLIB_RCSID
43 static const char rcsid[] = "$Id: dict.c,v 1.40.2.7 2000/11/13 01:36:44 kaz Exp $";
44 #endif
45 
46 /*
47  * These macros provide short convenient names for structure members,
48  * which are embellished with dict_ prefixes so that they are
49  * properly confined to the documented namespace. It's legal for a
50  * program which uses dict to define, for instance, a macro called ``parent''.
51  * Such a macro would interfere with the dnode_t struct definition.
52  * In general, highly portable and reusable C modules which expose their
53  * structures need to confine structure member names to well-defined spaces.
54  * The resulting identifiers aren't necessarily convenient to use, nor
55  * readable, in the implementation, however!
56  */
57 
58 #define left dict_left
59 #define right dict_right
60 #define parent dict_parent
61 #define color dict_color
62 #define key dict_key
63 #define data dict_data
64 
65 #define nilnode dict_nilnode
66 #define nodecount dict_nodecount
67 #define maxcount dict_maxcount
68 #define compare dict_compare
69 #define allocnode dict_allocnode
70 #define freenode dict_freenode
71 #define context dict_context
72 #define dupes dict_dupes
73 
74 #define dictptr dict_dictptr
75 
76 #define dict_root(D) ((D)->nilnode.left)
77 #define dict_nil(D) (&(D)->nilnode)
78 #define DICT_DEPTH_MAX 64
79 
80 static dnode_t *dnode_alloc(void *context);
81 static void dnode_free(dnode_t *node, void *context);
82 
83 /*
84  * Perform a ``left rotation'' adjustment on the tree.  The given node P and
85  * its right child C are rearranged so that the P instead becomes the left
86  * child of C.   The left subtree of C is inherited as the new right subtree
87  * for P.  The ordering of the keys within the tree is thus preserved.
88  */
89 
rotate_left(dnode_t * upper)90 static void rotate_left(dnode_t *upper)
91 {
92     dnode_t *lower, *lowleft, *upparent;
93 
94     lower = upper->right;
95     upper->right = lowleft = lower->left;
96     lowleft->parent = upper;
97 
98     lower->parent = upparent = upper->parent;
99 
100     /* don't need to check for root node here because root->parent is
101        the sentinel nil node, and root->parent->left points back to root */
102 
103     if (upper == upparent->left) {
104 	upparent->left = lower;
105     } else {
106 	dict_assert (upper == upparent->right);
107 	upparent->right = lower;
108     }
109 
110     lower->left = upper;
111     upper->parent = lower;
112 }
113 
114 /*
115  * This operation is the ``mirror'' image of rotate_left. It is
116  * the same procedure, but with left and right interchanged.
117  */
118 
rotate_right(dnode_t * upper)119 static void rotate_right(dnode_t *upper)
120 {
121     dnode_t *lower, *lowright, *upparent;
122 
123     lower = upper->left;
124     upper->left = lowright = lower->right;
125     lowright->parent = upper;
126 
127     lower->parent = upparent = upper->parent;
128 
129     if (upper == upparent->right) {
130 	upparent->right = lower;
131     } else {
132 	dict_assert (upper == upparent->left);
133 	upparent->left = lower;
134     }
135 
136     lower->right = upper;
137     upper->parent = lower;
138 }
139 
140 /*
141  * Do a postorder traversal of the tree rooted at the specified
142  * node and free everything under it.  Used by dict_free().
143  */
144 
free_nodes(dict_t * dict,dnode_t * node,dnode_t * nil)145 static void free_nodes(dict_t *dict, dnode_t *node, dnode_t *nil)
146 {
147     if (node == nil)
148 	return;
149     free_nodes(dict, node->left, nil);
150     free_nodes(dict, node->right, nil);
151     dict->freenode(node, dict->context);
152 }
153 
154 /*
155  * This procedure performs a verification that the given subtree is a binary
156  * search tree. It performs an inorder traversal of the tree using the
157  * dict_next() successor function, verifying that the key of each node is
158  * strictly lower than that of its successor, if duplicates are not allowed,
159  * or lower or equal if duplicates are allowed.  This function is used for
160  * debugging purposes.
161  */
162 #ifndef DICT_NODEBUG
verify_bintree(dict_t * dict)163 static int verify_bintree(dict_t *dict)
164 {
165     dnode_t *first, *next;
166 
167     first = dict_first(dict);
168 
169     if (dict->dupes) {
170 	while (first && (next = dict_next(dict, first))) {
171 	    if (dict->compare(first->key, next->key) > 0)
172 		return 0;
173 	    first = next;
174 	}
175     } else {
176 	while (first && (next = dict_next(dict, first))) {
177 	    if (dict->compare(first->key, next->key) >= 0)
178 		return 0;
179 	    first = next;
180 	}
181     }
182     return 1;
183 }
184 
185 /*
186  * This function recursively verifies that the given binary subtree satisfies
187  * three of the red black properties. It checks that every red node has only
188  * black children. It makes sure that each node is either red or black. And it
189  * checks that every path has the same count of black nodes from root to leaf.
190  * It returns the blackheight of the given subtree; this allows blackheights to
191  * be computed recursively and compared for left and right siblings for
192  * mismatches. It does not check for every nil node being black, because there
193  * is only one sentinel nil node. The return value of this function is the
194  * black height of the subtree rooted at the node ``root'', or zero if the
195  * subtree is not red-black.
196  */
197 
verify_redblack(dnode_t * nil,dnode_t * root)198 static unsigned int verify_redblack(dnode_t *nil, dnode_t *root)
199 {
200     unsigned height_left, height_right;
201 
202     if (root != nil) {
203 	height_left = verify_redblack(nil, root->left);
204 	height_right = verify_redblack(nil, root->right);
205 	if (height_left == 0 || height_right == 0)
206 	    return 0;
207 	if (height_left != height_right)
208 	    return 0;
209 	if (root->color == dnode_red) {
210 	    if (root->left->color != dnode_black)
211 		return 0;
212 	    if (root->right->color != dnode_black)
213 		return 0;
214 	    return height_left;
215 	}
216 	if (root->color != dnode_black)
217 	    return 0;
218 	return height_left + 1;
219     }
220     return 1;
221 }
222 
223 /*
224  * Compute the actual count of nodes by traversing the tree and
225  * return it. This could be compared against the stored count to
226  * detect a mismatch.
227  */
228 
verify_node_count(dnode_t * nil,dnode_t * root)229 static dictcount_t verify_node_count(dnode_t *nil, dnode_t *root)
230 {
231     if (root == nil)
232 	return 0;
233     else
234 	return 1 + verify_node_count(nil, root->left)
235 	    + verify_node_count(nil, root->right);
236 }
237 #endif
238 
239 /*
240  * Verify that the tree contains the given node. This is done by
241  * traversing all of the nodes and comparing their pointers to the
242  * given pointer. Returns 1 if the node is found, otherwise
243  * returns zero. It is intended for debugging purposes.
244  */
245 
verify_dict_has_node(dnode_t * nil,dnode_t * root,dnode_t * node)246 static int verify_dict_has_node(dnode_t *nil, dnode_t *root, dnode_t *node)
247 {
248     if (root != nil) {
249 	return root == node
250 		|| verify_dict_has_node(nil, root->left, node)
251 		|| verify_dict_has_node(nil, root->right, node);
252     }
253     return 0;
254 }
255 
256 
257 #ifdef E2FSCK_NOTUSED
258 /*
259  * Dynamically allocate and initialize a dictionary object.
260  */
261 
dict_create(dictcount_t maxcount,dict_comp_t comp)262 dict_t *dict_create(dictcount_t maxcount, dict_comp_t comp)
263 {
264     dict_t *new = malloc(sizeof *new);
265 
266     if (new) {
267 	new->compare = comp;
268 	new->allocnode = dnode_alloc;
269 	new->freenode = dnode_free;
270 	new->context = NULL;
271 	new->cmp_ctx = NULL;
272 	new->nodecount = 0;
273 	new->maxcount = maxcount;
274 	new->nilnode.left = &new->nilnode;
275 	new->nilnode.right = &new->nilnode;
276 	new->nilnode.parent = &new->nilnode;
277 	new->nilnode.color = dnode_black;
278 	new->dupes = 0;
279     }
280     return new;
281 }
282 #endif /* E2FSCK_NOTUSED */
283 
284 /*
285  * Select a different set of node allocator routines.
286  */
287 
dict_set_allocator(dict_t * dict,dnode_alloc_t al,dnode_free_t fr,void * context)288 void dict_set_allocator(dict_t *dict, dnode_alloc_t al,
289 	dnode_free_t fr, void *context)
290 {
291     dict_assert (dict_count(dict) == 0);
292     dict_assert ((al == NULL && fr == NULL) || (al != NULL && fr != NULL));
293 
294     dict->allocnode = al ? al : dnode_alloc;
295     dict->freenode = fr ? fr : dnode_free;
296     dict->context = context;
297 }
298 
dict_set_cmp_context(dict_t * dict,const void * cmp_ctx)299 void dict_set_cmp_context(dict_t *dict, const void *cmp_ctx)
300 {
301     dict_assert (!dict->cmp_ctx);
302     dict_assert (dict_count(dict) == 0);
303 
304     dict->cmp_ctx = cmp_ctx;
305 }
306 
307 #ifdef E2FSCK_NOTUSED
308 /*
309  * Free a dynamically allocated dictionary object. Removing the nodes
310  * from the tree before deleting it is required.
311  */
312 
dict_destroy(dict_t * dict)313 void dict_destroy(dict_t *dict)
314 {
315     dict_assert (dict_isempty(dict));
316     free(dict);
317 }
318 #endif
319 
320 /*
321  * Free all the nodes in the dictionary by using the dictionary's
322  * installed free routine. The dictionary is emptied.
323  */
324 
dict_free_nodes(dict_t * dict)325 void dict_free_nodes(dict_t *dict)
326 {
327     dnode_t *nil = dict_nil(dict), *root = dict_root(dict);
328     free_nodes(dict, root, nil);
329     dict->nodecount = 0;
330     dict->nilnode.left = &dict->nilnode;
331     dict->nilnode.right = &dict->nilnode;
332 }
333 
334 #ifdef E2FSCK_NOTUSED
335 /*
336  * Obsolescent function, equivalent to dict_free_nodes
337  */
dict_free(dict_t * dict)338 void dict_free(dict_t *dict)
339 {
340 #ifdef KAZLIB_OBSOLESCENT_DEBUG
341     dict_assert ("call to obsolescent function dict_free()" && 0);
342 #endif
343     dict_free_nodes(dict);
344 }
345 #endif
346 
347 /*
348  * Initialize a user-supplied dictionary object.
349  */
350 
dict_init(dict_t * dict,dictcount_t maxcount,dict_comp_t comp)351 dict_t *dict_init(dict_t *dict, dictcount_t maxcount, dict_comp_t comp)
352 {
353     dict->compare = comp;
354     dict->allocnode = dnode_alloc;
355     dict->freenode = dnode_free;
356     dict->context = NULL;
357     dict->nodecount = 0;
358     dict->maxcount = maxcount;
359     dict->nilnode.left = &dict->nilnode;
360     dict->nilnode.right = &dict->nilnode;
361     dict->nilnode.parent = &dict->nilnode;
362     dict->nilnode.color = dnode_black;
363     dict->dupes = 0;
364     return dict;
365 }
366 
367 #ifdef E2FSCK_NOTUSED
368 /*
369  * Initialize a dictionary in the likeness of another dictionary
370  */
371 
dict_init_like(dict_t * dict,const dict_t * template)372 void dict_init_like(dict_t *dict, const dict_t *template)
373 {
374     dict->compare = template->compare;
375     dict->allocnode = template->allocnode;
376     dict->freenode = template->freenode;
377     dict->context = template->context;
378     dict->nodecount = 0;
379     dict->maxcount = template->maxcount;
380     dict->nilnode.left = &dict->nilnode;
381     dict->nilnode.right = &dict->nilnode;
382     dict->nilnode.parent = &dict->nilnode;
383     dict->nilnode.color = dnode_black;
384     dict->dupes = template->dupes;
385 
386     dict_assert (dict_similar(dict, template));
387 }
388 
389 /*
390  * Remove all nodes from the dictionary (without freeing them in any way).
391  */
392 
dict_clear(dict_t * dict)393 static void dict_clear(dict_t *dict)
394 {
395     dict->nodecount = 0;
396     dict->nilnode.left = &dict->nilnode;
397     dict->nilnode.right = &dict->nilnode;
398     dict->nilnode.parent = &dict->nilnode;
399     dict_assert (dict->nilnode.color == dnode_black);
400 }
401 #endif /* E2FSCK_NOTUSED */
402 
403 
404 /*
405  * Verify the integrity of the dictionary structure.  This is provided for
406  * debugging purposes, and should be placed in assert statements.   Just because
407  * this function succeeds doesn't mean that the tree is not corrupt. Certain
408  * corruptions in the tree may simply cause undefined behavior.
409  */
410 #ifndef DICT_NODEBUG
dict_verify(dict_t * dict)411 int dict_verify(dict_t *dict)
412 {
413     dnode_t *nil = dict_nil(dict), *root = dict_root(dict);
414 
415     /* check that the sentinel node and root node are black */
416     if (root->color != dnode_black)
417 	return 0;
418     if (nil->color != dnode_black)
419 	return 0;
420     if (nil->right != nil)
421 	return 0;
422     /* nil->left is the root node; check that its parent pointer is nil */
423     if (nil->left->parent != nil)
424 	return 0;
425     /* perform a weak test that the tree is a binary search tree */
426     if (!verify_bintree(dict))
427 	return 0;
428     /* verify that the tree is a red-black tree */
429     if (!verify_redblack(nil, root))
430 	return 0;
431     if (verify_node_count(nil, root) != dict_count(dict))
432 	return 0;
433     return 1;
434 }
435 #endif /* DICT_NODEBUG */
436 
437 #ifdef E2FSCK_NOTUSED
438 /*
439  * Determine whether two dictionaries are similar: have the same comparison and
440  * allocator functions, and same status as to whether duplicates are allowed.
441  */
dict_similar(const dict_t * left,const dict_t * right)442 int dict_similar(const dict_t *left, const dict_t *right)
443 {
444     if (left->compare != right->compare)
445 	return 0;
446 
447     if (left->allocnode != right->allocnode)
448 	return 0;
449 
450     if (left->freenode != right->freenode)
451 	return 0;
452 
453     if (left->context != right->context)
454 	return 0;
455 
456     if (left->dupes != right->dupes)
457 	return 0;
458 
459     return 1;
460 }
461 #endif /* E2FSCK_NOTUSED */
462 
463 /*
464  * Locate a node in the dictionary having the given key.
465  * If the node is not found, a null a pointer is returned (rather than
466  * a pointer that dictionary's nil sentinel node), otherwise a pointer to the
467  * located node is returned.
468  */
469 
dict_lookup(dict_t * dict,const void * key)470 dnode_t *dict_lookup(dict_t *dict, const void *key)
471 {
472     dnode_t *root = dict_root(dict);
473     dnode_t *nil = dict_nil(dict);
474     dnode_t *saved;
475     int result;
476 
477     /* simple binary search adapted for trees that contain duplicate keys */
478 
479     while (root != nil) {
480 	result = dict->compare(dict->cmp_ctx, key, root->key);
481 	if (result < 0)
482 	    root = root->left;
483 	else if (result > 0)
484 	    root = root->right;
485 	else {
486 	    if (!dict->dupes) {	/* no duplicates, return match		*/
487 		return root;
488 	    } else {		/* could be dupes, find leftmost one	*/
489 		do {
490 		    saved = root;
491 		    root = root->left;
492 		    while (root != nil
493 			   && dict->compare(dict->cmp_ctx, key, root->key))
494 			root = root->right;
495 		} while (root != nil);
496 		return saved;
497 	    }
498 	}
499     }
500 
501     return NULL;
502 }
503 
504 #ifdef E2FSCK_NOTUSED
505 /*
506  * Look for the node corresponding to the lowest key that is equal to or
507  * greater than the given key.  If there is no such node, return null.
508  */
509 
dict_lower_bound(dict_t * dict,const void * key)510 dnode_t *dict_lower_bound(dict_t *dict, const void *key)
511 {
512     dnode_t *root = dict_root(dict);
513     dnode_t *nil = dict_nil(dict);
514     dnode_t *tentative = 0;
515 
516     while (root != nil) {
517 	int result = dict->compare(dict->cmp_ctx, key, root->key);
518 
519 	if (result > 0) {
520 	    root = root->right;
521 	} else if (result < 0) {
522 	    tentative = root;
523 	    root = root->left;
524 	} else {
525 	    if (!dict->dupes) {
526 	    	return root;
527 	    } else {
528 		tentative = root;
529 		root = root->left;
530 	    }
531 	}
532     }
533 
534     return tentative;
535 }
536 
537 /*
538  * Look for the node corresponding to the greatest key that is equal to or
539  * lower than the given key.  If there is no such node, return null.
540  */
541 
dict_upper_bound(dict_t * dict,const void * key)542 dnode_t *dict_upper_bound(dict_t *dict, const void *key)
543 {
544     dnode_t *root = dict_root(dict);
545     dnode_t *nil = dict_nil(dict);
546     dnode_t *tentative = 0;
547 
548     while (root != nil) {
549 	int result = dict->compare(dict->cmp_ctx, key, root->key);
550 
551 	if (result < 0) {
552 	    root = root->left;
553 	} else if (result > 0) {
554 	    tentative = root;
555 	    root = root->right;
556 	} else {
557 	    if (!dict->dupes) {
558 	    	return root;
559 	    } else {
560 		tentative = root;
561 		root = root->right;
562 	    }
563 	}
564     }
565 
566     return tentative;
567 }
568 #endif
569 
570 /*
571  * Insert a node into the dictionary. The node should have been
572  * initialized with a data field. All other fields are ignored.
573  * The behavior is undefined if the user attempts to insert into
574  * a dictionary that is already full (for which the dict_isfull()
575  * function returns true).
576  */
577 
dict_insert(dict_t * dict,dnode_t * node,const void * key)578 void dict_insert(dict_t *dict, dnode_t *node, const void *key)
579 {
580     dnode_t *where = dict_root(dict), *nil = dict_nil(dict);
581     dnode_t *parent = nil, *uncle, *grandpa;
582     int result = -1;
583 
584     node->key = key;
585 
586     dict_assert (!dict_isfull(dict));
587     dict_assert (!dict_contains(dict, node));
588     dict_assert (!dnode_is_in_a_dict(node));
589 
590     /* basic binary tree insert */
591 
592     while (where != nil) {
593 	parent = where;
594 	result = dict->compare(dict->cmp_ctx, key, where->key);
595 	/* trap attempts at duplicate key insertion unless it's explicitly allowed */
596 	dict_assert (dict->dupes || result != 0);
597 	if (result < 0)
598 	    where = where->left;
599 	else
600 	    where = where->right;
601     }
602 
603     dict_assert (where == nil);
604 
605     if (result < 0)
606 	parent->left = node;
607     else
608 	parent->right = node;
609 
610     node->parent = parent;
611     node->left = nil;
612     node->right = nil;
613 
614     dict->nodecount++;
615 
616     /* red black adjustments */
617 
618     node->color = dnode_red;
619 
620     while (parent->color == dnode_red) {
621 	grandpa = parent->parent;
622 	if (parent == grandpa->left) {
623 	    uncle = grandpa->right;
624 	    if (uncle->color == dnode_red) {	/* red parent, red uncle */
625 		parent->color = dnode_black;
626 		uncle->color = dnode_black;
627 		grandpa->color = dnode_red;
628 		node = grandpa;
629 		parent = grandpa->parent;
630 	    } else {				/* red parent, black uncle */
631 	    	if (node == parent->right) {
632 		    rotate_left(parent);
633 		    parent = node;
634 		    dict_assert (grandpa == parent->parent);
635 		    /* rotation between parent and child preserves grandpa */
636 		}
637 		parent->color = dnode_black;
638 		grandpa->color = dnode_red;
639 		rotate_right(grandpa);
640 		break;
641 	    }
642 	} else { 	/* symmetric cases: parent == parent->parent->right */
643 	    uncle = grandpa->left;
644 	    if (uncle->color == dnode_red) {
645 		parent->color = dnode_black;
646 		uncle->color = dnode_black;
647 		grandpa->color = dnode_red;
648 		node = grandpa;
649 		parent = grandpa->parent;
650 	    } else {
651 	    	if (node == parent->left) {
652 		    rotate_right(parent);
653 		    parent = node;
654 		    dict_assert (grandpa == parent->parent);
655 		}
656 		parent->color = dnode_black;
657 		grandpa->color = dnode_red;
658 		rotate_left(grandpa);
659 		break;
660 	    }
661 	}
662     }
663 
664     dict_root(dict)->color = dnode_black;
665 
666     dict_assert (dict_verify(dict));
667 }
668 
669 #ifdef E2FSCK_NOTUSED
670 /*
671  * Delete the given node from the dictionary. If the given node does not belong
672  * to the given dictionary, undefined behavior results.  A pointer to the
673  * deleted node is returned.
674  */
675 
dict_delete(dict_t * dict,dnode_t * delete)676 dnode_t *dict_delete(dict_t *dict, dnode_t *delete)
677 {
678     dnode_t *nil = dict_nil(dict), *child, *delparent = delete->parent;
679 
680     /* basic deletion */
681 
682     dict_assert (!dict_isempty(dict));
683     dict_assert (dict_contains(dict, delete));
684 
685     /*
686      * If the node being deleted has two children, then we replace it with its
687      * successor (i.e. the leftmost node in the right subtree.) By doing this,
688      * we avoid the traditional algorithm under which the successor's key and
689      * value *only* move to the deleted node and the successor is spliced out
690      * from the tree. We cannot use this approach because the user may hold
691      * pointers to the successor, or nodes may be inextricably tied to some
692      * other structures by way of embedding, etc. So we must splice out the
693      * node we are given, not some other node, and must not move contents from
694      * one node to another behind the user's back.
695      */
696 
697     if (delete->left != nil && delete->right != nil) {
698 	dnode_t *next = dict_next(dict, delete);
699 	dnode_t *nextparent = next->parent;
700 	dnode_color_t nextcolor = next->color;
701 
702 	dict_assert (next != nil);
703 	dict_assert (next->parent != nil);
704 	dict_assert (next->left == nil);
705 
706 	/*
707 	 * First, splice out the successor from the tree completely, by
708 	 * moving up its right child into its place.
709 	 */
710 
711 	child = next->right;
712 	child->parent = nextparent;
713 
714 	if (nextparent->left == next) {
715 	    nextparent->left = child;
716 	} else {
717 	    dict_assert (nextparent->right == next);
718 	    nextparent->right = child;
719 	}
720 
721 	/*
722 	 * Now that the successor has been extricated from the tree, install it
723 	 * in place of the node that we want deleted.
724 	 */
725 
726 	next->parent = delparent;
727 	next->left = delete->left;
728 	next->right = delete->right;
729 	next->left->parent = next;
730 	next->right->parent = next;
731 	next->color = delete->color;
732 	delete->color = nextcolor;
733 
734 	if (delparent->left == delete) {
735 	    delparent->left = next;
736 	} else {
737 	    dict_assert (delparent->right == delete);
738 	    delparent->right = next;
739 	}
740 
741     } else {
742 	dict_assert (delete != nil);
743 	dict_assert (delete->left == nil || delete->right == nil);
744 
745 	child = (delete->left != nil) ? delete->left : delete->right;
746 
747 	child->parent = delparent = delete->parent;
748 
749 	if (delete == delparent->left) {
750 	    delparent->left = child;
751 	} else {
752 	    dict_assert (delete == delparent->right);
753 	    delparent->right = child;
754 	}
755     }
756 
757     delete->parent = NULL;
758     delete->right = NULL;
759     delete->left = NULL;
760 
761     dict->nodecount--;
762 
763     dict_assert (verify_bintree(dict));
764 
765     /* red-black adjustments */
766 
767     if (delete->color == dnode_black) {
768 	dnode_t *parent, *sister;
769 
770 	dict_root(dict)->color = dnode_red;
771 
772 	while (child->color == dnode_black) {
773 	    parent = child->parent;
774 	    if (child == parent->left) {
775 		sister = parent->right;
776 		dict_assert (sister != nil);
777 		if (sister->color == dnode_red) {
778 		    sister->color = dnode_black;
779 		    parent->color = dnode_red;
780 		    rotate_left(parent);
781 		    sister = parent->right;
782 		    dict_assert (sister != nil);
783 		}
784 		if (sister->left->color == dnode_black
785 			&& sister->right->color == dnode_black) {
786 		    sister->color = dnode_red;
787 		    child = parent;
788 		} else {
789 		    if (sister->right->color == dnode_black) {
790 			dict_assert (sister->left->color == dnode_red);
791 			sister->left->color = dnode_black;
792 			sister->color = dnode_red;
793 			rotate_right(sister);
794 			sister = parent->right;
795 			dict_assert (sister != nil);
796 		    }
797 		    sister->color = parent->color;
798 		    sister->right->color = dnode_black;
799 		    parent->color = dnode_black;
800 		    rotate_left(parent);
801 		    break;
802 		}
803 	    } else {	/* symmetric case: child == child->parent->right */
804 		dict_assert (child == parent->right);
805 		sister = parent->left;
806 		dict_assert (sister != nil);
807 		if (sister->color == dnode_red) {
808 		    sister->color = dnode_black;
809 		    parent->color = dnode_red;
810 		    rotate_right(parent);
811 		    sister = parent->left;
812 		    dict_assert (sister != nil);
813 		}
814 		if (sister->right->color == dnode_black
815 			&& sister->left->color == dnode_black) {
816 		    sister->color = dnode_red;
817 		    child = parent;
818 		} else {
819 		    if (sister->left->color == dnode_black) {
820 			dict_assert (sister->right->color == dnode_red);
821 			sister->right->color = dnode_black;
822 			sister->color = dnode_red;
823 			rotate_left(sister);
824 			sister = parent->left;
825 			dict_assert (sister != nil);
826 		    }
827 		    sister->color = parent->color;
828 		    sister->left->color = dnode_black;
829 		    parent->color = dnode_black;
830 		    rotate_right(parent);
831 		    break;
832 		}
833 	    }
834 	}
835 
836 	child->color = dnode_black;
837 	dict_root(dict)->color = dnode_black;
838     }
839 
840     dict_assert (dict_verify(dict));
841 
842     return delete;
843 }
844 #endif /* E2FSCK_NOTUSED */
845 
846 /*
847  * Allocate a node using the dictionary's allocator routine, give it
848  * the data item.
849  */
850 
dict_alloc_insert(dict_t * dict,const void * key,void * data)851 int dict_alloc_insert(dict_t *dict, const void *key, void *data)
852 {
853     dnode_t *node = dict->allocnode(dict->context);
854 
855     if (node) {
856 	dnode_init(node, data);
857 	dict_insert(dict, node, key);
858 	return 1;
859     }
860     return 0;
861 }
862 
863 #ifdef E2FSCK_NOTUSED
dict_delete_free(dict_t * dict,dnode_t * node)864 void dict_delete_free(dict_t *dict, dnode_t *node)
865 {
866     dict_delete(dict, node);
867     dict->freenode(node, dict->context);
868 }
869 #endif
870 
871 /*
872  * Return the node with the lowest (leftmost) key. If the dictionary is empty
873  * (that is, dict_isempty(dict) returns 1) a null pointer is returned.
874  */
875 
dict_first(dict_t * dict)876 dnode_t *dict_first(dict_t *dict)
877 {
878     dnode_t *nil = dict_nil(dict), *root = dict_root(dict), *left;
879 
880     if (root != nil)
881 	while ((left = root->left) != nil)
882 	    root = left;
883 
884     return (root == nil) ? NULL : root;
885 }
886 
887 /*
888  * Return the node with the highest (rightmost) key. If the dictionary is empty
889  * (that is, dict_isempty(dict) returns 1) a null pointer is returned.
890  */
891 
dict_last(dict_t * dict)892 dnode_t *dict_last(dict_t *dict)
893 {
894     dnode_t *nil = dict_nil(dict), *root = dict_root(dict), *right;
895 
896     if (root != nil)
897 	while ((right = root->right) != nil)
898 	    root = right;
899 
900     return (root == nil) ? NULL : root;
901 }
902 
903 /*
904  * Return the given node's successor node---the node which has the
905  * next key in the the left to right ordering. If the node has
906  * no successor, a null pointer is returned rather than a pointer to
907  * the nil node.
908  */
909 
dict_next(dict_t * dict,dnode_t * curr)910 dnode_t *dict_next(dict_t *dict, dnode_t *curr)
911 {
912     dnode_t *nil = dict_nil(dict), *parent, *left;
913 
914     if (curr->right != nil) {
915 	curr = curr->right;
916 	while ((left = curr->left) != nil)
917 	    curr = left;
918 	return curr;
919     }
920 
921     parent = curr->parent;
922 
923     while (parent != nil && curr == parent->right) {
924 	curr = parent;
925 	parent = curr->parent;
926     }
927 
928     return (parent == nil) ? NULL : parent;
929 }
930 
931 /*
932  * Return the given node's predecessor, in the key order.
933  * The nil sentinel node is returned if there is no predecessor.
934  */
935 
dict_prev(dict_t * dict,dnode_t * curr)936 dnode_t *dict_prev(dict_t *dict, dnode_t *curr)
937 {
938     dnode_t *nil = dict_nil(dict), *parent, *right;
939 
940     if (curr->left != nil) {
941 	curr = curr->left;
942 	while ((right = curr->right) != nil)
943 	    curr = right;
944 	return curr;
945     }
946 
947     parent = curr->parent;
948 
949     while (parent != nil && curr == parent->left) {
950 	curr = parent;
951 	parent = curr->parent;
952     }
953 
954     return (parent == nil) ? NULL : parent;
955 }
956 
dict_allow_dupes(dict_t * dict)957 void dict_allow_dupes(dict_t *dict)
958 {
959     dict->dupes = 1;
960 }
961 
962 #undef dict_count
963 #undef dict_isempty
964 #undef dict_isfull
965 #undef dnode_get
966 #undef dnode_put
967 #undef dnode_getkey
968 
dict_count(dict_t * dict)969 dictcount_t dict_count(dict_t *dict)
970 {
971     return dict->nodecount;
972 }
973 
dict_isempty(dict_t * dict)974 int dict_isempty(dict_t *dict)
975 {
976     return dict->nodecount == 0;
977 }
978 
dict_isfull(dict_t * dict)979 int dict_isfull(dict_t *dict)
980 {
981     return dict->nodecount == dict->maxcount;
982 }
983 
dict_contains(dict_t * dict,dnode_t * node)984 int dict_contains(dict_t *dict, dnode_t *node)
985 {
986     return verify_dict_has_node(dict_nil(dict), dict_root(dict), node);
987 }
988 
dnode_alloc(void * context EXT2FS_ATTR ((unused)))989 static dnode_t *dnode_alloc(void *context EXT2FS_ATTR((unused)))
990 {
991     return malloc(sizeof *dnode_alloc(NULL));
992 }
993 
dnode_free(dnode_t * node,void * context EXT2FS_ATTR ((unused)))994 static void dnode_free(dnode_t *node, void *context EXT2FS_ATTR((unused)))
995 {
996     free(node);
997 }
998 
dnode_create(void * data)999 dnode_t *dnode_create(void *data)
1000 {
1001     dnode_t *new = malloc(sizeof *new);
1002     if (new) {
1003 	new->data = data;
1004 	new->parent = NULL;
1005 	new->left = NULL;
1006 	new->right = NULL;
1007     }
1008     return new;
1009 }
1010 
dnode_init(dnode_t * dnode,void * data)1011 dnode_t *dnode_init(dnode_t *dnode, void *data)
1012 {
1013     dnode->data = data;
1014     dnode->parent = NULL;
1015     dnode->left = NULL;
1016     dnode->right = NULL;
1017     return dnode;
1018 }
1019 
dnode_destroy(dnode_t * dnode)1020 void dnode_destroy(dnode_t *dnode)
1021 {
1022     dict_assert (!dnode_is_in_a_dict(dnode));
1023     free(dnode);
1024 }
1025 
dnode_get(dnode_t * dnode)1026 void *dnode_get(dnode_t *dnode)
1027 {
1028     return dnode->data;
1029 }
1030 
dnode_getkey(dnode_t * dnode)1031 const void *dnode_getkey(dnode_t *dnode)
1032 {
1033     return dnode->key;
1034 }
1035 
1036 #ifdef E2FSCK_NOTUSED
dnode_put(dnode_t * dnode,void * data)1037 void dnode_put(dnode_t *dnode, void *data)
1038 {
1039     dnode->data = data;
1040 }
1041 #endif
1042 
1043 #ifndef DICT_NODEBUG
dnode_is_in_a_dict(dnode_t * dnode)1044 int dnode_is_in_a_dict(dnode_t *dnode)
1045 {
1046     return (dnode->parent && dnode->left && dnode->right);
1047 }
1048 #endif
1049 
1050 #ifdef E2FSCK_NOTUSED
dict_process(dict_t * dict,void * context,dnode_process_t function)1051 void dict_process(dict_t *dict, void *context, dnode_process_t function)
1052 {
1053     dnode_t *node = dict_first(dict), *next;
1054 
1055     while (node != NULL) {
1056 	/* check for callback function deleting	*/
1057 	/* the next node from under us		*/
1058 	dict_assert (dict_contains(dict, node));
1059 	next = dict_next(dict, node);
1060 	function(dict, node, context);
1061 	node = next;
1062     }
1063 }
1064 
load_begin_internal(dict_load_t * load,dict_t * dict)1065 static void load_begin_internal(dict_load_t *load, dict_t *dict)
1066 {
1067     load->dictptr = dict;
1068     load->nilnode.left = &load->nilnode;
1069     load->nilnode.right = &load->nilnode;
1070 }
1071 
dict_load_begin(dict_load_t * load,dict_t * dict)1072 void dict_load_begin(dict_load_t *load, dict_t *dict)
1073 {
1074     dict_assert (dict_isempty(dict));
1075     load_begin_internal(load, dict);
1076 }
1077 
dict_load_next(dict_load_t * load,dnode_t * newnode,const void * key)1078 void dict_load_next(dict_load_t *load, dnode_t *newnode, const void *key)
1079 {
1080     dict_t *dict = load->dictptr;
1081     dnode_t *nil = &load->nilnode;
1082 
1083     dict_assert (!dnode_is_in_a_dict(newnode));
1084     dict_assert (dict->nodecount < dict->maxcount);
1085 
1086 #ifndef DICT_NODEBUG
1087     if (dict->nodecount > 0) {
1088 	if (dict->dupes)
1089 	    dict_assert (dict->compare(nil->left->key, key) <= 0);
1090 	else
1091 	    dict_assert (dict->compare(nil->left->key, key) < 0);
1092     }
1093 #endif
1094 
1095     newnode->key = key;
1096     nil->right->left = newnode;
1097     nil->right = newnode;
1098     newnode->left = nil;
1099     dict->nodecount++;
1100 }
1101 
dict_load_end(dict_load_t * load)1102 void dict_load_end(dict_load_t *load)
1103 {
1104     dict_t *dict = load->dictptr;
1105     dnode_t *tree[DICT_DEPTH_MAX] = { 0 };
1106     dnode_t *curr, *dictnil = dict_nil(dict), *loadnil = &load->nilnode, *next;
1107     dnode_t *complete = 0;
1108     dictcount_t fullcount = DICTCOUNT_T_MAX, nodecount = dict->nodecount;
1109     dictcount_t botrowcount;
1110     unsigned baselevel = 0, level = 0, i;
1111 
1112     dict_assert (dnode_red == 0 && dnode_black == 1);
1113 
1114     while (fullcount >= nodecount && fullcount)
1115 	fullcount >>= 1;
1116 
1117     botrowcount = nodecount - fullcount;
1118 
1119     for (curr = loadnil->left; curr != loadnil; curr = next) {
1120 	next = curr->left;
1121 
1122 	if (complete == NULL && botrowcount-- == 0) {
1123 	    dict_assert (baselevel == 0);
1124 	    dict_assert (level == 0);
1125 	    baselevel = level = 1;
1126 	    complete = tree[0];
1127 
1128 	    if (complete != 0) {
1129 		tree[0] = 0;
1130 		complete->right = dictnil;
1131 		while (tree[level] != 0) {
1132 		    tree[level]->right = complete;
1133 		    complete->parent = tree[level];
1134 		    complete = tree[level];
1135 		    tree[level++] = 0;
1136 		}
1137 	    }
1138 	}
1139 
1140 	if (complete == NULL) {
1141 	    curr->left = dictnil;
1142 	    curr->right = dictnil;
1143 	    curr->color = level % 2;
1144 	    complete = curr;
1145 
1146 	    dict_assert (level == baselevel);
1147 	    while (tree[level] != 0) {
1148 		tree[level]->right = complete;
1149 		complete->parent = tree[level];
1150 		complete = tree[level];
1151 		tree[level++] = 0;
1152 	    }
1153 	} else {
1154 	    curr->left = complete;
1155 	    curr->color = (level + 1) % 2;
1156 	    complete->parent = curr;
1157 	    tree[level] = curr;
1158 	    complete = 0;
1159 	    level = baselevel;
1160 	}
1161     }
1162 
1163     if (complete == NULL)
1164 	complete = dictnil;
1165 
1166     for (i = 0; i < DICT_DEPTH_MAX; i++) {
1167 	if (tree[i] != 0) {
1168 	    tree[i]->right = complete;
1169 	    complete->parent = tree[i];
1170 	    complete = tree[i];
1171 	}
1172     }
1173 
1174     dictnil->color = dnode_black;
1175     dictnil->right = dictnil;
1176     complete->parent = dictnil;
1177     complete->color = dnode_black;
1178     dict_root(dict) = complete;
1179 
1180     dict_assert (dict_verify(dict));
1181 }
1182 
dict_merge(dict_t * dest,dict_t * source)1183 void dict_merge(dict_t *dest, dict_t *source)
1184 {
1185     dict_load_t load;
1186     dnode_t *leftnode = dict_first(dest), *rightnode = dict_first(source);
1187 
1188     dict_assert (dict_similar(dest, source));
1189 
1190     if (source == dest)
1191 	return;
1192 
1193     dest->nodecount = 0;
1194     load_begin_internal(&load, dest);
1195 
1196     for (;;) {
1197 	if (leftnode != NULL && rightnode != NULL) {
1198 	    if (dest->compare(leftnode->key, rightnode->key) < 0)
1199 		goto copyleft;
1200 	    else
1201 		goto copyright;
1202 	} else if (leftnode != NULL) {
1203 	    goto copyleft;
1204 	} else if (rightnode != NULL) {
1205 	    goto copyright;
1206 	} else {
1207 	    dict_assert (leftnode == NULL && rightnode == NULL);
1208 	    break;
1209 	}
1210 
1211     copyleft:
1212 	{
1213 	    dnode_t *next = dict_next(dest, leftnode);
1214 #ifndef DICT_NODEBUG
1215 	    leftnode->left = NULL;	/* suppress assertion in dict_load_next */
1216 #endif
1217 	    dict_load_next(&load, leftnode, leftnode->key);
1218 	    leftnode = next;
1219 	    continue;
1220 	}
1221 
1222     copyright:
1223 	{
1224 	    dnode_t *next = dict_next(source, rightnode);
1225 #ifndef DICT_NODEBUG
1226 	    rightnode->left = NULL;
1227 #endif
1228 	    dict_load_next(&load, rightnode, rightnode->key);
1229 	    rightnode = next;
1230 	    continue;
1231 	}
1232     }
1233 
1234     dict_clear(source);
1235     dict_load_end(&load);
1236 }
1237 #endif /* E2FSCK_NOTUSED */
1238 
1239 #ifdef KAZLIB_TEST_MAIN
1240 
1241 #include <stdio.h>
1242 #include <string.h>
1243 #include <ctype.h>
1244 #include <stdarg.h>
1245 
1246 typedef char input_t[256];
1247 
tokenize(char * string,...)1248 static int tokenize(char *string, ...)
1249 {
1250     char **tokptr;
1251     va_list arglist;
1252     int tokcount = 0;
1253 
1254     va_start(arglist, string);
1255     tokptr = va_arg(arglist, char **);
1256     while (tokptr) {
1257 	while (*string && isspace((unsigned char) *string))
1258 	    string++;
1259 	if (!*string)
1260 	    break;
1261 	*tokptr = string;
1262 	while (*string && !isspace((unsigned char) *string))
1263 	    string++;
1264 	tokptr = va_arg(arglist, char **);
1265 	tokcount++;
1266 	if (!*string)
1267 	    break;
1268 	*string++ = 0;
1269     }
1270     va_end(arglist);
1271 
1272     return tokcount;
1273 }
1274 
comparef(const void * cmp_ctx,const void * key1,const void * key2)1275 static int comparef(const void *cmp_ctx, const void *key1, const void *key2)
1276 {
1277     return strcmp(key1, key2);
1278 }
1279 
dupstring(char * str)1280 static char *dupstring(char *str)
1281 {
1282     int sz = strlen(str) + 1;
1283     char *new = malloc(sz);
1284     if (new)
1285 	memcpy(new, str, sz);
1286     return new;
1287 }
1288 
new_node(void * c)1289 static dnode_t *new_node(void *c)
1290 {
1291     static dnode_t few[5];
1292     static int count;
1293 
1294     if (count < 5)
1295 	return few + count++;
1296 
1297     return NULL;
1298 }
1299 
del_node(dnode_t * n,void * c)1300 static void del_node(dnode_t *n, void *c)
1301 {
1302 }
1303 
1304 static int prompt = 0;
1305 
construct(dict_t * d)1306 static void construct(dict_t *d)
1307 {
1308     input_t in;
1309     int done = 0;
1310     dict_load_t dl;
1311     dnode_t *dn;
1312     char *tok1, *tok2, *val;
1313     const char *key;
1314     char *help =
1315 	"p                      turn prompt on\n"
1316 	"q                      finish construction\n"
1317 	"a <key> <val>          add new entry\n";
1318 
1319     if (!dict_isempty(d))
1320 	puts("warning: dictionary not empty!");
1321 
1322     dict_load_begin(&dl, d);
1323 
1324     while (!done) {
1325 	if (prompt)
1326 	    putchar('>');
1327 	fflush(stdout);
1328 
1329 	if (!fgets(in, sizeof(input_t), stdin))
1330 	    break;
1331 
1332 	switch (in[0]) {
1333 	    case '?':
1334 		puts(help);
1335 		break;
1336 	    case 'p':
1337 		prompt = 1;
1338 		break;
1339 	    case 'q':
1340 		done = 1;
1341 		break;
1342 	    case 'a':
1343 		if (tokenize(in+1, &tok1, &tok2, (char **) 0) != 2) {
1344 		    puts("what?");
1345 		    break;
1346 		}
1347 		key = dupstring(tok1);
1348 		val = dupstring(tok2);
1349 		dn = dnode_create(val);
1350 
1351 		if (!key || !val || !dn) {
1352 		    puts("out of memory");
1353 		    free((void *) key);
1354 		    free(val);
1355 		    if (dn)
1356 			dnode_destroy(dn);
1357 		}
1358 
1359 		dict_load_next(&dl, dn, key);
1360 		break;
1361 	    default:
1362 		putchar('?');
1363 		putchar('\n');
1364 		break;
1365 	}
1366     }
1367 
1368     dict_load_end(&dl);
1369 }
1370 
main(void)1371 int main(void)
1372 {
1373     input_t in;
1374     dict_t darray[10];
1375     dict_t *d = &darray[0];
1376     dnode_t *dn;
1377     int i;
1378     char *tok1, *tok2, *val;
1379     const char *key;
1380 
1381     char *help =
1382 	"a <key> <val>          add value to dictionary\n"
1383 	"d <key>                delete value from dictionary\n"
1384 	"l <key>                lookup value in dictionary\n"
1385 	"( <key>                lookup lower bound\n"
1386 	") <key>                lookup upper bound\n"
1387 	"# <num>                switch to alternate dictionary (0-9)\n"
1388 	"j <num> <num>          merge two dictionaries\n"
1389 	"f                      free the whole dictionary\n"
1390 	"k                      allow duplicate keys\n"
1391 	"c                      show number of entries\n"
1392 	"t                      dump whole dictionary in sort order\n"
1393 	"m                      make dictionary out of sorted items\n"
1394 	"p                      turn prompt on\n"
1395 	"s                      switch to non-functioning allocator\n"
1396 	"q                      quit";
1397 
1398     for (i = 0; i < sizeof darray / sizeof *darray; i++)
1399 	dict_init(&darray[i], DICTCOUNT_T_MAX, comparef);
1400 
1401     for (;;) {
1402 	if (prompt)
1403 	    putchar('>');
1404 	fflush(stdout);
1405 
1406 	if (!fgets(in, sizeof(input_t), stdin))
1407 	    break;
1408 
1409 	switch(in[0]) {
1410 	    case '?':
1411 		puts(help);
1412 		break;
1413 	    case 'a':
1414 		if (tokenize(in+1, &tok1, &tok2, (char **) 0) != 2) {
1415 		    puts("what?");
1416 		    break;
1417 		}
1418 		key = dupstring(tok1);
1419 		val = dupstring(tok2);
1420 
1421 		if (!key || !val) {
1422 		    puts("out of memory");
1423 		    free((void *) key);
1424 		    free(val);
1425 		}
1426 
1427 		if (!dict_alloc_insert(d, key, val)) {
1428 		    puts("dict_alloc_insert failed");
1429 		    free((void *) key);
1430 		    free(val);
1431 		    break;
1432 		}
1433 		break;
1434 	    case 'd':
1435 		if (tokenize(in+1, &tok1, (char **) 0) != 1) {
1436 		    puts("what?");
1437 		    break;
1438 		}
1439 		dn = dict_lookup(d, tok1);
1440 		if (!dn) {
1441 		    puts("dict_lookup failed");
1442 		    break;
1443 		}
1444 		val = dnode_get(dn);
1445 		key = dnode_getkey(dn);
1446 		dict_delete_free(d, dn);
1447 
1448 		free(val);
1449 		free((void *) key);
1450 		break;
1451 	    case 'f':
1452 		dict_free(d);
1453 		break;
1454 	    case 'l':
1455 	    case '(':
1456 	    case ')':
1457 		if (tokenize(in+1, &tok1, (char **) 0) != 1) {
1458 		    puts("what?");
1459 		    break;
1460 		}
1461 		dn = 0;
1462 		switch (in[0]) {
1463 		case 'l':
1464 		    dn = dict_lookup(d, tok1);
1465 		    break;
1466 		case '(':
1467 		    dn = dict_lower_bound(d, tok1);
1468 		    break;
1469 		case ')':
1470 		    dn = dict_upper_bound(d, tok1);
1471 		    break;
1472 		}
1473 		if (!dn) {
1474 		    puts("lookup failed");
1475 		    break;
1476 		}
1477 		val = dnode_get(dn);
1478 		puts(val);
1479 		break;
1480 	    case 'm':
1481 		construct(d);
1482 		break;
1483 	    case 'k':
1484 		dict_allow_dupes(d);
1485 		break;
1486 	    case 'c':
1487 		printf("%lu\n", (unsigned long) dict_count(d));
1488 		break;
1489 	    case 't':
1490 		for (dn = dict_first(d); dn; dn = dict_next(d, dn)) {
1491 		    printf("%s\t%s\n", (char *) dnode_getkey(dn),
1492 			    (char *) dnode_get(dn));
1493 		}
1494 		break;
1495 	    case 'q':
1496 		exit(0);
1497 		break;
1498 	    case '\0':
1499 		break;
1500 	    case 'p':
1501 		prompt = 1;
1502 		break;
1503 	    case 's':
1504 		dict_set_allocator(d, new_node, del_node, NULL);
1505 		break;
1506 	    case '#':
1507 		if (tokenize(in+1, &tok1, (char **) 0) != 1) {
1508 		    puts("what?");
1509 		    break;
1510 		} else {
1511 		    int dictnum = atoi(tok1);
1512 		    if (dictnum < 0 || dictnum > 9) {
1513 			puts("invalid number");
1514 			break;
1515 		    }
1516 		    d = &darray[dictnum];
1517 		}
1518 		break;
1519 	    case 'j':
1520 		if (tokenize(in+1, &tok1, &tok2, (char **) 0) != 2) {
1521 		    puts("what?");
1522 		    break;
1523 		} else {
1524 		    int dict1 = atoi(tok1), dict2 = atoi(tok2);
1525 		    if (dict1 < 0 || dict1 > 9 || dict2 < 0 || dict2 > 9) {
1526 			puts("invalid number");
1527 			break;
1528 		    }
1529 		    dict_merge(&darray[dict1], &darray[dict2]);
1530 		}
1531 		break;
1532 	    default:
1533 		putchar('?');
1534 		putchar('\n');
1535 		break;
1536 	}
1537     }
1538 
1539     return 0;
1540 }
1541 
1542 #endif
1543