]> cvs.zerfleddert.de Git - micropolis/blob - src/tk/tktxbtre.c
Fixes for compilation with gcc 15
[micropolis] / src / tk / tktxbtre.c
1 /*
2 * tkTextBTree.c --
3 *
4 * This file contains code that manages the B-tree representation
5 * of text for Tk's text widget. The B-tree holds both the text
6 * and tag information related to the text.
7 *
8 * Copyright 1992 Regents of the University of California
9 * Permission to use, copy, modify, and distribute this
10 * software and its documentation for any purpose and without
11 * fee is hereby granted, provided that this copyright
12 * notice appears in all copies. The University of California
13 * makes no representations about the suitability of this
14 * software for any purpose. It is provided "as is" without
15 * express or implied warranty.
16 */
17
18 #ifndef lint
19 static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkTextBTree.c,v 1.16 92/08/17 09:13:58 ouster Exp $ SPRITE (Berkeley)";
20 #endif /* not lint */
21
22 #include "tkint.h"
23 #include "tkconfig.h"
24 #include "tktext.h"
25
26
27 /*
28 * The data structure below keeps summary information about one tag as part
29 * of the tag information in a node.
30 */
31
32 typedef struct Summary {
33 TkTextTag *tagPtr; /* Handle for tag. */
34 int toggleCount; /* Number of transitions into or
35 * out of this tag that occur in
36 * the subtree rooted at this node. */
37 struct Summary *nextPtr; /* Next in list of all tags for same
38 * node, or NULL if at end of list. */
39 } Summary;
40
41 /*
42 * The data structure below defines a node in the B-tree representing
43 * all of the lines in a text widget.
44 */
45
46 typedef struct Node {
47 struct Node *parentPtr; /* Pointer to parent node, or NULL if
48 * this is the root. */
49 struct Node *nextPtr; /* Next in list of children of the
50 * same parent node, or NULL for end
51 * of list. */
52 Summary *summaryPtr; /* First in malloc-ed list of info
53 * about tags in this subtree (NULL if
54 * no tag info in the subtree). */
55 int level; /* Level of this node in the B-tree.
56 * 0 refers to the bottom of the tree
57 * (children are lines, not nodes). */
58 union { /* First in linked list of children. */
59 struct Node *nodePtr; /* Used if level > 0. */
60 TkTextLine *linePtr; /* Used if level == 0. */
61 } children;
62 int numChildren; /* Number of children of this node. */
63 int numLines; /* Total number of lines (leaves) in
64 * the subtree rooted here. */
65 } Node;
66
67 /*
68 * Upper and lower bounds on how many children a node may have:
69 * rebalance when either of these limits is exceeded. MAX_CHILDREN
70 * should be twice MIN_CHILDREN and MIN_CHILDREN must be >= 2.
71 */
72
73 #define MAX_CHILDREN 12
74 #define MIN_CHILDREN 6
75
76 /*
77 * The data structure below defines an entire B-tree.
78 */
79
80 typedef struct BTree {
81 Node *rootPtr; /* Pointer to root of B-tree. */
82 } BTree;
83
84 /*
85 * The structure below is used to pass information between
86 * TkBTreeGetTags and IncCount:
87 */
88
89 typedef struct TagInfo {
90 int numTags; /* Number of tags for which there
91 * is currently information in
92 * tags and counts. */
93 int arraySize; /* Number of entries allocated for
94 * tags and counts. */
95 TkTextTag **tagPtrs; /* Array of tags seen so far.
96 * Malloc-ed. */
97 int *counts; /* Toggle count (so far) for each
98 * entry in tags. Malloc-ed. */
99 } TagInfo;
100
101 /*
102 * Macro to compute the space needed for a line that holds n non-null
103 * characters:
104 */
105
106 #define LINE_SIZE(n) ((unsigned) (sizeof(TkTextLine) - 3 + (n)))
107
108 /*
109 * Variable that indicates whether to enable consistency checks for
110 * debugging.
111 */
112
113 int tkBTreeDebug = 0;
114
115 /*
116 * Forward declarations for procedures defined in this file:
117 */
118
119 static void AddToggleToLine _ANSI_ARGS_((TkTextLine *linePtr,
120 int index, TkTextTag *tagPtr));
121 static void ChangeNodeToggleCount _ANSI_ARGS_((Node *nodePtr,
122 TkTextTag *tagPtr, int delta));
123 static void CheckNodeConsistency _ANSI_ARGS_((Node *nodePtr));
124 static void DeleteSummaries _ANSI_ARGS_((Summary *tagPtr));
125 static void DestroyNode _ANSI_ARGS_((Node *nodePtr));
126 static void IncCount _ANSI_ARGS_((TkTextTag *tagPtr, int inc,
127 TagInfo *tagInfoPtr));
128 static void Rebalance _ANSI_ARGS_((BTree *treePtr, Node *nodePtr));
129 static void RecomputeNodeCounts _ANSI_ARGS_((Node *nodePtr));
130 \f
131 /*
132 *----------------------------------------------------------------------
133 *
134 * TkBTreeCreate --
135 *
136 * This procedure is called to create a new text B-tree.
137 *
138 * Results:
139 * The return value is a pointer to a new B-tree containing
140 * one line with nothing but a newline character.
141 *
142 * Side effects:
143 * Memory is allocated and initialized.
144 *
145 *----------------------------------------------------------------------
146 */
147
148 TkTextBTree
149 TkBTreeCreate (void)
150 {
151 register BTree *treePtr;
152 register Node *rootPtr;
153 register TkTextLine *linePtr;
154
155 rootPtr = (Node *) ckalloc(sizeof(Node));
156 linePtr = (TkTextLine *) ckalloc(LINE_SIZE(1));
157 rootPtr->parentPtr = NULL;
158 rootPtr->nextPtr = NULL;
159 rootPtr->summaryPtr = NULL;
160 rootPtr->level = 0;
161 rootPtr->children.linePtr = linePtr;
162 rootPtr->numChildren = 1;
163 rootPtr->numLines = 1;
164
165 linePtr->parentPtr = rootPtr;
166 linePtr->nextPtr = NULL;
167 linePtr->annotPtr = NULL;
168 linePtr->numBytes = 1;
169 linePtr->bytes[0] = '\n';
170 linePtr->bytes[1] = 0;
171
172 treePtr = (BTree *) ckalloc(sizeof(BTree));
173 treePtr->rootPtr = rootPtr;
174
175 return (TkTextBTree) treePtr;
176 }
177 \f
178 /*
179 *----------------------------------------------------------------------
180 *
181 * TkBTreeDestroy --
182 *
183 * Delete a B-tree, recycling all of the storage it contains.
184 *
185 * Results:
186 * The tree given by treePtr is deleted. TreePtr should never
187 * again be used.
188 *
189 * Side effects:
190 * Memory is freed.
191 *
192 *----------------------------------------------------------------------
193 */
194
195 void
196 TkBTreeDestroy (
197 TkTextBTree tree /* Pointer to tree to delete. */
198 )
199 {
200 BTree *treePtr = (BTree *) tree;
201
202 DestroyNode(treePtr->rootPtr);
203 ckfree((char *) treePtr);
204 }
205 \f
206 /*
207 *----------------------------------------------------------------------
208 *
209 * DestroyNode --
210 *
211 * This is a recursive utility procedure used during the deletion
212 * of a B-tree.
213 *
214 * Results:
215 * None.
216 *
217 * Side effects:
218 * All the storage for nodePtr and its descendants is freed.
219 *
220 *----------------------------------------------------------------------
221 */
222
223 static void
224 DestroyNode (register Node *nodePtr)
225 {
226 if (nodePtr->level == 0) {
227 register TkTextLine *curPtr, *nextLinePtr;
228 register TkAnnotation *annotPtr, *nextAnnotPtr;
229
230 for (curPtr = nodePtr->children.linePtr; curPtr != NULL; ) {
231 nextLinePtr = curPtr->nextPtr;
232 for (annotPtr = curPtr->annotPtr; annotPtr != NULL; ) {
233 nextAnnotPtr = annotPtr->nextPtr;
234 if (annotPtr->type == TK_ANNOT_TOGGLE) {
235 ckfree((char *) annotPtr);
236 }
237 annotPtr = nextAnnotPtr;
238 }
239 ckfree((char *) curPtr);
240 curPtr = nextLinePtr;
241 }
242 } else {
243 register Node *curPtr, *nextPtr;
244
245 for (curPtr = nodePtr->children.nodePtr; curPtr != NULL; ) {
246 nextPtr = curPtr->nextPtr;
247 DestroyNode(curPtr);
248 curPtr = nextPtr;
249 }
250 }
251 DeleteSummaries(nodePtr->summaryPtr);
252 ckfree((char *) nodePtr);
253 }
254 \f
255 /*
256 *----------------------------------------------------------------------
257 *
258 * DeleteSummaries --
259 *
260 * Free up all of the memory in a list of tag summaries associated
261 * with a node.
262 *
263 * Results:
264 * None.
265 *
266 * Side effects:
267 * Storage is released.
268 *
269 *----------------------------------------------------------------------
270 */
271
272 static void
273 DeleteSummaries (
274 register Summary *summaryPtr /* First in list of node's tag
275 * summaries. */
276 )
277 {
278 register Summary *nextPtr;
279 while (summaryPtr != NULL) {
280 nextPtr = summaryPtr->nextPtr;
281 ckfree((char *) summaryPtr);
282 summaryPtr = nextPtr;
283 }
284 }
285 \f
286 /*
287 *----------------------------------------------------------------------
288 *
289 * TkBTreeInsertChars --
290 *
291 * Insert characters at a given position in a B-tree.
292 *
293 * Results:
294 * None.
295 *
296 * Side effects:
297 * NumBytes characters are added to the B-tree at the given
298 * character position. This can cause the structure of the
299 * B-tree to change.
300 *
301 *----------------------------------------------------------------------
302 */
303
304 void
305 TkBTreeInsertChars (
306 TkTextBTree tree, /* B-tree in which to insert. */
307 register TkTextLine *linePtr, /* Pointer to line in which to
308 * insert. */
309 int ch, /* Index of character before which
310 * to insert. Must not be after
311 * last character in line.*/
312 char *string /* Pointer to bytes to insert (may
313 * contain newlines, must be null-
314 * terminated). */
315 )
316 {
317 BTree *treePtr = (BTree *) tree;
318 register Node *nodePtr;
319 register TkAnnotation *annotPtr;
320 TkTextLine *prevPtr;
321 int newChunkLength; /* # chars in current line being
322 * inserted. */
323 register char *eol; /* Pointer to last character in
324 * current line being inserted. */
325 int changeToLineCount; /* Counts change to total number of
326 * lines in file. */
327 TkAnnotation *afterPtr; /* List of annotations that occur
328 * at or after the insertion point
329 * in the line of the insertion. */
330 int prefixLength, suffixLength, totalLength;
331 register TkTextLine *newPtr;
332
333 /*
334 * Find the line just before the one where the insertion will occur
335 * but with the same parent node (if there is one). This is needed
336 * so we can replace the insertion line with a new one. Remove this
337 * line from the list for its parent, since it's going to be discarded
338 * when we're all done).
339 */
340
341 nodePtr = linePtr->parentPtr;
342 prevPtr = nodePtr->children.linePtr;
343 if (prevPtr == linePtr) {
344 prevPtr = NULL;
345 nodePtr->children.linePtr = linePtr->nextPtr;
346 } else {
347 for ( ; prevPtr->nextPtr != linePtr; prevPtr = prevPtr->nextPtr) {
348 /* Empty loop body. */
349 }
350 prevPtr->nextPtr = linePtr->nextPtr;
351 }
352
353 /*
354 * Break up the annotations for the insertion line into two pieces:
355 * those before the insertion point, and those at or after the insertion
356 * point.
357 */
358
359 afterPtr = NULL;
360 if ((linePtr->annotPtr != NULL) && (linePtr->annotPtr->ch >= ch)) {
361 afterPtr = linePtr->annotPtr;
362 linePtr->annotPtr = NULL;
363 } else {
364 for (annotPtr = linePtr->annotPtr; annotPtr != NULL;
365 annotPtr = annotPtr->nextPtr) {
366 if ((annotPtr->nextPtr != NULL)
367 && (annotPtr->nextPtr->ch >= ch)) {
368 afterPtr = annotPtr->nextPtr;
369 annotPtr->nextPtr = NULL;
370 break;
371 }
372 }
373 }
374
375 /*
376 * Chop the string up into lines and insert each line individually.
377 */
378
379 changeToLineCount = -1;
380 prefixLength = ch;
381 while (1) {
382 for (newChunkLength = 0, eol = string; *eol != 0; eol++) {
383 newChunkLength++;
384 if (*eol == '\n') {
385 break;
386 }
387 }
388
389 /*
390 * Create a new line consisting of up to three parts: a prefix
391 * from linePtr, some material from string, and a suffix from
392 * linePtr.
393 */
394
395 if ((newChunkLength == 0) || (*eol != '\n')) {
396 suffixLength = linePtr->numBytes - ch;
397 } else {
398 suffixLength = 0;
399 }
400 totalLength = prefixLength + newChunkLength + suffixLength;
401 newPtr = (TkTextLine *) ckalloc(LINE_SIZE(totalLength));
402 newPtr->parentPtr = nodePtr;
403 if (prevPtr == NULL) {
404 newPtr->nextPtr = nodePtr->children.linePtr;
405 nodePtr->children.linePtr = newPtr;
406 } else {
407 newPtr->nextPtr = prevPtr->nextPtr;
408 prevPtr->nextPtr = newPtr;
409 }
410 if (linePtr->annotPtr != NULL) {
411 newPtr->annotPtr = linePtr->annotPtr;
412 for (annotPtr = newPtr->annotPtr; annotPtr != NULL;
413 annotPtr = annotPtr->nextPtr) {
414 annotPtr->linePtr = newPtr;
415 }
416 linePtr->annotPtr = NULL;
417 } else {
418 newPtr->annotPtr = NULL;
419 }
420 newPtr->numBytes = totalLength;
421 if (prefixLength != 0) {
422 memcpy((VOID *) newPtr->bytes, (VOID *) linePtr->bytes,
423 prefixLength);
424 }
425 if (newChunkLength != 0) {
426 memcpy((VOID *) (newPtr->bytes + prefixLength), (VOID *) string,
427 newChunkLength);
428 }
429 if (suffixLength != 0) {
430 memcpy((VOID *) (newPtr->bytes + prefixLength + newChunkLength),
431 (VOID *) (linePtr->bytes + ch), suffixLength);
432 }
433 newPtr->bytes[totalLength] = 0;
434 changeToLineCount += 1;
435
436 /*
437 * Quit after the suffix has been output (there is always at least
438 * one character of suffix: the newline). Before jumping out of the
439 * loop, put back the annotations that pertain to the suffix.
440 * Careful! If no newlines were inserted, there could already be
441 * annotations at the beginning of the line; add back to the end.
442 */
443
444 if (suffixLength != 0) {
445 if (newPtr->annotPtr == NULL) {
446 newPtr->annotPtr = afterPtr;
447 } else {
448 for (annotPtr = newPtr->annotPtr; annotPtr->nextPtr != NULL;
449 annotPtr = annotPtr->nextPtr) {
450 /* Empty loop body. */
451 }
452 annotPtr->nextPtr = afterPtr;
453 }
454 for (annotPtr = afterPtr; annotPtr != NULL;
455 annotPtr = annotPtr->nextPtr) {
456 annotPtr->linePtr = newPtr;
457 annotPtr->ch += prefixLength+newChunkLength-ch;
458 }
459 break;
460 }
461
462 /*
463 * Advance to insert the next line chunk.
464 */
465
466 string += newChunkLength;
467 prefixLength = 0;
468 prevPtr = newPtr;
469 }
470
471 /*
472 * Increment the line counts in all the parent nodes of the insertion
473 * point, then rebalance the tree if necessary.
474 */
475
476 for ( ; nodePtr != NULL; nodePtr = nodePtr->parentPtr) {
477 nodePtr->numLines += changeToLineCount;
478 }
479 nodePtr = linePtr->parentPtr;
480 nodePtr->numChildren += changeToLineCount;
481 if (nodePtr->numChildren > MAX_CHILDREN) {
482 Rebalance(treePtr, nodePtr);
483 }
484
485 ckfree((char *) linePtr);
486 if (tkBTreeDebug) {
487 TkBTreeCheck(tree);
488 }
489 }
490 \f
491 /*
492 *----------------------------------------------------------------------
493 *
494 * TkBTreeDeleteChars --
495 *
496 * Delete a range of characters from a B-tree.
497 *
498 * Results:
499 * None.
500 *
501 * Side effects:
502 * Information is deleted from the B-tree. This can cause the
503 * internal structure of the B-tree to change. Note: the two
504 * lines given by line1Ptr and line2Ptr will be replaced with
505 * a single line containing the undeleted parts of the original
506 * lines. This could potentially result in an empty line;
507 * normally the caller should adjust the deletion range to prevent
508 * this sort of behavior.
509 *
510 *----------------------------------------------------------------------
511 */
512
513 void
514 TkBTreeDeleteChars (
515 TkTextBTree tree, /* B-tree in which to delete. */
516 register TkTextLine *line1Ptr, /* Line containing first character
517 * to delete. */
518 int ch1, /* Index within linePtr1 of first
519 * character to delete. */
520 register TkTextLine *line2Ptr, /* Line containing character just
521 * after last one to delete. */
522 int ch2 /* Index within linePtr2 of character
523 * just after last one to delete. */
524 )
525 {
526 BTree *treePtr = (BTree *) tree;
527 TkTextLine *linePtr, *nextPtr, *prevLinePtr;
528 Node *nodePtr, *parentPtr, *nextNodePtr;
529 TkAnnotation *annotPtr, *annotPtr2;
530 int ch;
531 int linesDeleted; /* Counts lines deleted from current
532 * level-0 node. */
533
534 /*
535 * Work through the tree deleting all of the lines between line1Ptr
536 * and line2Ptr (but don't delete line1Ptr or line2Ptr yet). Also
537 * delete any nodes in the B-tree that become empty because of
538 * this process.
539 */
540
541 linePtr = line1Ptr->nextPtr;
542 nodePtr = line1Ptr->parentPtr;
543 if (line1Ptr == line2Ptr) {
544 goto middleLinesDeleted;
545 }
546 while (1) {
547
548 /*
549 * Delete all relevant lines within the same level-0 node.
550 */
551
552 linesDeleted = 0;
553 while ((linePtr != line2Ptr) && (linePtr != NULL)) {
554 /*
555 * Move any annotations in this line to the end of the
556 * deletion range. If both the starting and ending toggle
557 * for a tagged range get moved, they'll cancel each other
558 * automatically and be dropped, which is the right behavior.
559 */
560
561 for (annotPtr = linePtr->annotPtr; annotPtr != NULL;
562 annotPtr = annotPtr2) {
563 if (annotPtr->type == TK_ANNOT_TOGGLE) {
564 AddToggleToLine(line2Ptr, ch2, annotPtr->info.tagPtr);
565 ChangeNodeToggleCount(nodePtr, annotPtr->info.tagPtr, -1);
566 annotPtr2 = annotPtr->nextPtr;
567 ckfree((char *) annotPtr);
568 } else {
569 annotPtr2 = annotPtr->nextPtr;
570 TkBTreeRemoveAnnotation(annotPtr);
571 annotPtr->linePtr = line2Ptr;
572 annotPtr->ch = ch2;
573 TkBTreeAddAnnotation(annotPtr);
574 }
575 }
576 nextPtr = linePtr->nextPtr;
577 ckfree((char *) linePtr);
578 linesDeleted++;
579 linePtr = nextPtr;
580 }
581 if (nodePtr == line1Ptr->parentPtr) {
582 line1Ptr->nextPtr = linePtr;
583 } else {
584 nodePtr->children.linePtr = linePtr;
585 }
586 for (parentPtr = nodePtr; parentPtr != NULL;
587 parentPtr = parentPtr->parentPtr) {
588 parentPtr->numLines -= linesDeleted;
589 }
590 nodePtr->numChildren -= linesDeleted;
591 if (linePtr == line2Ptr) {
592 break;
593 }
594
595 /*
596 * Find the next level-0 node to visit, and its first line (but
597 * remember the current node so we can come back to delete it if
598 * it's empty).
599 */
600
601 nextNodePtr = nodePtr;
602 while (nextNodePtr->nextPtr == NULL) {
603 nextNodePtr = nextNodePtr->parentPtr;
604 }
605 nextNodePtr = nextNodePtr->nextPtr;
606 while (nextNodePtr->level > 0) {
607 nextNodePtr = nextNodePtr->children.nodePtr;
608 }
609 linePtr = nextNodePtr->children.linePtr;
610
611 /*
612 * Now go back to the node we just left and delete it if
613 * it's empty, along with any of its ancestors that are
614 * empty. It may seem funny to go back like this, but it's
615 * simpler to find the next place to visit before modifying
616 * the tree structure.
617 */
618
619 while (nodePtr->numChildren == 0) {
620 parentPtr = nodePtr->parentPtr;
621 if (parentPtr->children.nodePtr == nodePtr) {
622 parentPtr->children.nodePtr = nodePtr->nextPtr;
623 } else {
624 Node *prevPtr;
625
626 for (prevPtr = parentPtr->children.nodePtr;
627 prevPtr->nextPtr != nodePtr;
628 prevPtr = prevPtr->nextPtr) {
629 }
630 prevPtr->nextPtr = nodePtr->nextPtr;
631 }
632 parentPtr->numChildren--;
633 DeleteSummaries(nodePtr->summaryPtr);
634 ckfree((char *) nodePtr);
635 nodePtr = parentPtr;
636 }
637 nodePtr = nextNodePtr;
638 }
639
640 /*
641 * Make a new line that consists of the first part of the first
642 * line of the deletion range and the last part of the last line
643 * of the deletion range.
644 */
645
646 middleLinesDeleted:
647 nodePtr = line1Ptr->parentPtr;
648 linePtr = (TkTextLine *) ckalloc(LINE_SIZE(ch1 + line2Ptr->numBytes - ch2));
649 linePtr->parentPtr = nodePtr;
650 linePtr->nextPtr = line1Ptr->nextPtr;
651 linePtr->annotPtr = NULL;
652 linePtr->numBytes = ch1 + line2Ptr->numBytes - ch2;
653 if (ch1 != 0) {
654 memcpy((VOID *) linePtr->bytes, (VOID *) line1Ptr->bytes, ch1);
655 }
656 strcpy(linePtr->bytes + ch1, line2Ptr->bytes + ch2);
657
658 /*
659 * Process the annotations for the starting and ending lines. Enter
660 * a new annotation on linePtr (the joined line) for each of these
661 * annotations, then delete the originals. The code below is a little
662 * tricky (e.g. the "break" in the first loop) to handle the case where
663 * the starting and ending lines are the same.
664 */
665
666 for (annotPtr = line1Ptr->annotPtr; annotPtr != NULL;
667 annotPtr = line1Ptr->annotPtr) {
668 if (annotPtr->ch <= ch1) {
669 ch = annotPtr->ch;
670 } else {
671 if (line1Ptr == line2Ptr) {
672 break;
673 }
674 ch = ch1;
675 }
676 line1Ptr->annotPtr = annotPtr->nextPtr;
677 if (annotPtr->type == TK_ANNOT_TOGGLE) {
678 AddToggleToLine(linePtr, ch, annotPtr->info.tagPtr);
679 ChangeNodeToggleCount(line1Ptr->parentPtr, annotPtr->info.tagPtr,
680 -1);
681 ckfree((char *) annotPtr);
682 } else {
683 annotPtr->linePtr = linePtr;
684 annotPtr->ch = ch;
685 TkBTreeAddAnnotation(annotPtr);
686 }
687 }
688 for (annotPtr = line2Ptr->annotPtr; annotPtr != NULL;
689 annotPtr = line2Ptr->annotPtr) {
690 if (annotPtr->ch >= ch2) {
691 ch = annotPtr->ch - ch2 + ch1;
692 } else {
693 ch = ch1;
694 }
695 line2Ptr->annotPtr = annotPtr->nextPtr;
696 if (annotPtr->type == TK_ANNOT_TOGGLE) {
697 AddToggleToLine(linePtr, ch, annotPtr->info.tagPtr);
698 ChangeNodeToggleCount(line2Ptr->parentPtr, annotPtr->info.tagPtr,
699 -1);
700 ckfree((char *) annotPtr);
701 } else {
702 annotPtr->linePtr = linePtr;
703 annotPtr->ch = ch;
704 TkBTreeAddAnnotation(annotPtr);
705 }
706 }
707
708 /*
709 * Delete the original starting and stopping lines (don't forget
710 * that the annotations have already been deleted) and insert the
711 * new line in place of line1Ptr.
712 */
713
714 nodePtr = line1Ptr->parentPtr;
715 if (nodePtr->children.linePtr == line1Ptr) {
716 nodePtr->children.linePtr = linePtr;
717 } else {
718 for (prevLinePtr = nodePtr->children.linePtr;
719 prevLinePtr->nextPtr != line1Ptr;
720 prevLinePtr = prevLinePtr->nextPtr) {
721 /* Empty loop body. */
722 }
723 prevLinePtr->nextPtr = linePtr;
724 }
725 ckfree((char *) line1Ptr);
726 nodePtr = line2Ptr->parentPtr;
727 if (line2Ptr != line1Ptr) {
728 if (nodePtr->children.linePtr == line2Ptr) {
729 nodePtr->children.linePtr = line2Ptr->nextPtr;
730 } else {
731 for (prevLinePtr = nodePtr->children.linePtr;
732 prevLinePtr->nextPtr != line2Ptr;
733 prevLinePtr = prevLinePtr->nextPtr) {
734 /* Empty loop body. */
735 }
736 prevLinePtr->nextPtr = line2Ptr->nextPtr;
737 }
738 ckfree((char *) line2Ptr);
739 for (parentPtr = nodePtr; parentPtr != NULL;
740 parentPtr = parentPtr->parentPtr) {
741 parentPtr->numLines--;
742 }
743 nodePtr->numChildren--;
744 }
745
746 /*
747 * Rebalance the tree, starting from each of the endpoints of the
748 * deletion range. This code is a tricky, because the act of
749 * rebalancing the parent of one endpoint can cause the parent of
750 * the other endpoint to be reallocated. The only thing it's safe
751 * to hold onto is a pointer to a line. Thus, rebalance line2Ptr's
752 * parent first, then use linePtr find the second parent to rebalance
753 * second.
754 */
755
756 if (nodePtr != linePtr->parentPtr) {
757 Rebalance(treePtr, nodePtr);
758 }
759 Rebalance(treePtr, linePtr->parentPtr);
760 if (tkBTreeDebug) {
761 TkBTreeCheck(tree);
762 }
763 }
764 \f
765 /*
766 *----------------------------------------------------------------------
767 *
768 * TkBTreeTag --
769 *
770 * Turn a given tag on or off for a given range of characters in
771 * a B-tree of text.
772 *
773 * Results:
774 * None.
775 *
776 * Side effects:
777 * The given tag is added to the given range of characters
778 * in the tree or removed from all those characters, depending
779 * on the "add" argument.
780 *
781 *----------------------------------------------------------------------
782 */
783
784 void
785 TkBTreeTag (
786 TkTextBTree tree, /* B-tree in which to add tag
787 * information. */
788 int line1,
789 int ch1, /* Position of first character to
790 * tag. */
791 int line2,
792 int ch2, /* Position of character just after
793 * last one to tag. */
794 TkTextTag *tagPtr, /* Tag to associate with the range
795 * of characters. */
796 int add /* One means add tag to the given
797 * range of characters; zero means
798 * remove the tag from the range. */
799 )
800 {
801 BTree *treePtr = (BTree *) tree;
802 register TkTextLine *line1Ptr, *line2Ptr;
803 TkTextSearch search;
804 int oldState;
805
806 /*
807 * Find the lines containing the first and last characters to be tagged,
808 * and adjust the starting and stopping locations if they don't already
809 * point within lines. If the range would have started or stopped at the
810 * end of a line, round it up to the beginning of the next line (right
811 * now this restriction keeps the final newline from being tagged).
812 */
813
814 if (line1 < 0) {
815 line1 = 0;
816 ch1 = 0;
817 }
818 line1Ptr = TkBTreeFindLine(tree, line1);
819 if (line1Ptr == NULL) {
820 return;
821 }
822 if (ch1 >= line1Ptr->numBytes) {
823 TkTextLine *nextLinePtr;
824
825 nextLinePtr = TkBTreeNextLine(line1Ptr);
826 if (nextLinePtr == NULL) {
827 return;
828 } else {
829 line1Ptr = nextLinePtr;
830 line1++;
831 ch1 = 0;
832 }
833 }
834 if (line2 < 0) {
835 return;
836 }
837 line2Ptr = TkBTreeFindLine(tree, line2);
838 if (line2Ptr == NULL) {
839 line2Ptr = TkBTreeFindLine(tree, treePtr->rootPtr->numLines-1);
840 ch2 = line2Ptr->numBytes-1;
841 }
842 if (ch2 >= line2Ptr->numBytes) {
843 TkTextLine *nextLinePtr;
844
845 nextLinePtr = TkBTreeNextLine(line2Ptr);
846 if (nextLinePtr == NULL) {
847 ch2 = line2Ptr->numBytes-1;
848 } else {
849 line2Ptr = nextLinePtr;
850 line2++;
851 ch2 = 0;
852 }
853 }
854
855 /*
856 * See if the tag is already present or absent at the start of the
857 * range. If the state doesn't already match what we want then add
858 * a toggle there.
859 */
860
861 oldState = TkBTreeCharTagged(line1Ptr, ch1, tagPtr);
862 if ((add != 0) ^ oldState) {
863 AddToggleToLine(line1Ptr, ch1, tagPtr);
864 }
865
866 /*
867 * Scan the range of characters covered by the change and delete
868 * any existing tag transitions except those on the first and
869 * last characters. Keep track of whether the old state just before
870 * the last character (not including any tags on it) is what we
871 * want now; if not, then add a tag toggle there.
872 */
873
874 TkBTreeStartSearch(tree, line1, ch1+1, line2, ch2, tagPtr, &search);
875 while (TkBTreeNextTag(&search)) {
876 if ((search.linePtr == line2Ptr) && (search.ch1 == ch2)) {
877 break;
878 }
879 oldState ^= 1;
880 AddToggleToLine(search.linePtr, search.ch1, tagPtr);
881 }
882 if ((add != 0) ^ oldState) {
883 AddToggleToLine(line2Ptr, ch2, tagPtr);
884 }
885
886 if (tkBTreeDebug) {
887 TkBTreeCheck(tree);
888 }
889 }
890 \f
891 /*
892 *----------------------------------------------------------------------
893 *
894 * TkBTreeAddAnnotation --
895 *
896 * Given a filled in annotation, this procedure links it into
897 * a B-tree structure so that it will track changes to the B-tree.
898 *
899 * Results:
900 * None.
901 *
902 * Side effects:
903 * AnnotPtr will be linked into its tree. Note: the storage for
904 * annotPtr is assumed to have been malloc'ed by the caller.
905 *
906 *----------------------------------------------------------------------
907 */
908
909 /* ARGSUSED */
910 void
911 TkBTreeAddAnnotation (
912 TkAnnotation *annotPtr /* Pointer to annotation. The caller must
913 * have filled in all the fields except the
914 * "nextPtr" field. The type should NOT be
915 * TK_ANNOT_TOGGLE; these annotations are
916 * managed by the TkBTreeTag procedure. */
917 )
918 {
919 register TkAnnotation *annotPtr2, *prevPtr;
920
921 for (prevPtr = NULL, annotPtr2 = annotPtr->linePtr->annotPtr;
922 annotPtr2 != NULL;
923 prevPtr = annotPtr2, annotPtr2 = annotPtr2->nextPtr) {
924 if (annotPtr2->ch > annotPtr->ch) {
925 break;
926 }
927 }
928 if (prevPtr == NULL) {
929 annotPtr->nextPtr = annotPtr->linePtr->annotPtr;
930 annotPtr->linePtr->annotPtr = annotPtr;
931 } else {
932 annotPtr->nextPtr = prevPtr->nextPtr;
933 prevPtr->nextPtr = annotPtr;
934 }
935 }
936 \f
937 /*
938 *----------------------------------------------------------------------
939 *
940 * TkBTreeRemoveAnnotation --
941 *
942 * This procedure unlinks an annotation from a B-tree so that
943 * the annotation will no longer be managed by the B-tree code.
944 *
945 * Results:
946 * None.
947 *
948 * Side effects:
949 * AnnotPtr will be unlinked from its tree. Note: it is up to the
950 * caller to free the storage for annotPtr, if that is desired.
951 *
952 *----------------------------------------------------------------------
953 */
954
955 /* ARGSUSED */
956 void
957 TkBTreeRemoveAnnotation (
958 TkAnnotation *annotPtr /* Pointer to annotation, which must
959 * have been linked into tree by a previous
960 * call to TkBTreeAddAnnotation. */
961 )
962 {
963 register TkAnnotation *prevPtr;
964
965 if (annotPtr->linePtr->annotPtr == annotPtr) {
966 annotPtr->linePtr->annotPtr = annotPtr->nextPtr;
967 } else {
968 for (prevPtr = annotPtr->linePtr->annotPtr;
969 /* BUG: fixed by dhopkins, prevPtr was null!
970 prevPtr->nextPtr != annotPtr;
971 */
972 (prevPtr != NULL) && (prevPtr->nextPtr != annotPtr);
973 prevPtr = prevPtr->nextPtr) {
974 /* Empty loop body. */
975 }
976 if (prevPtr != NULL) { /* Bullet proofing by dhopkins */
977 prevPtr->nextPtr = annotPtr->nextPtr;
978 }
979 }
980 }
981 \f
982 /*
983 *----------------------------------------------------------------------
984 *
985 * TkBTreeFindLine --
986 *
987 * Find a particular line in a B-tree based on its line number.
988 *
989 * Results:
990 * The return value is a pointer to the line structure for the
991 * line whose index is "line", or NULL if no such line exists.
992 *
993 * Side effects:
994 * None.
995 *
996 *----------------------------------------------------------------------
997 */
998
999 TkTextLine *
1000 TkBTreeFindLine (
1001 TkTextBTree tree, /* B-tree in which to find line. */
1002 int line /* Index of desired line. */
1003 )
1004 {
1005 BTree *treePtr = (BTree *) tree;
1006 register Node *nodePtr;
1007 register TkTextLine *linePtr;
1008 int linesLeft;
1009
1010 nodePtr = treePtr->rootPtr;
1011 linesLeft = line;
1012 if ((line < 0) || (line >= nodePtr->numLines)) {
1013 return NULL;
1014 }
1015
1016 /*
1017 * Work down through levels of the tree until a node is found at
1018 * level 0.
1019 */
1020
1021 while (nodePtr->level != 0) {
1022 for (nodePtr = nodePtr->children.nodePtr;
1023 nodePtr->numLines <= linesLeft;
1024 nodePtr = nodePtr->nextPtr) {
1025 if (nodePtr == NULL) {
1026 panic("TkBTreeFindLine ran out of nodes");
1027 }
1028 linesLeft -= nodePtr->numLines;
1029 }
1030 }
1031
1032 /*
1033 * Work through the lines attached to the level-0 node.
1034 */
1035
1036 for (linePtr = nodePtr->children.linePtr; linesLeft > 0;
1037 linePtr = linePtr->nextPtr) {
1038 if (linePtr == NULL) {
1039 panic("TkBTreeFindLine ran out of lines");
1040 }
1041 linesLeft -= 1;
1042 }
1043 return linePtr;
1044 }
1045 \f
1046 /*
1047 *----------------------------------------------------------------------
1048 *
1049 * TkBTreeNextLine --
1050 *
1051 * Given an existing line in a B-tree, this procedure locates the
1052 * next line in the B-tree. This procedure is used for scanning
1053 * through the B-tree.
1054 *
1055 * Results:
1056 * The return value is a pointer to the line that immediately
1057 * follows linePtr, or NULL if there is no such line.
1058 *
1059 * Side effects:
1060 * None.
1061 *
1062 *----------------------------------------------------------------------
1063 */
1064
1065 TkTextLine *
1066 TkBTreeNextLine (
1067 register TkTextLine *linePtr /* Pointer to existing line in
1068 * B-tree. */
1069 )
1070 {
1071 register Node *nodePtr;
1072
1073 if (linePtr->nextPtr != NULL) {
1074 return linePtr->nextPtr;
1075 }
1076
1077 /*
1078 * This was the last line associated with the particular parent node.
1079 * Search up the tree for the next node, then search down from that
1080 * node to find the first line,
1081 */
1082
1083 for (nodePtr = linePtr->parentPtr; ; nodePtr = nodePtr->parentPtr) {
1084 if (nodePtr->nextPtr != NULL) {
1085 nodePtr = nodePtr->nextPtr;
1086 break;
1087 }
1088 if (nodePtr->parentPtr == NULL) {
1089 return (TkTextLine *) NULL;
1090 }
1091 }
1092 while (nodePtr->level > 0) {
1093 nodePtr = nodePtr->children.nodePtr;
1094 }
1095 return nodePtr->children.linePtr;
1096 }
1097 \f
1098 /*
1099 *----------------------------------------------------------------------
1100 *
1101 * TkBTreeLineIndex --
1102 *
1103 * Given a pointer to a line in a B-tree, return the numerical
1104 * index of that line.
1105 *
1106 * Results:
1107 * The result is the index of linePtr within the tree, where 0
1108 * corresponds to the first line in the tree.
1109 *
1110 * Side effects:
1111 * None.
1112 *
1113 *----------------------------------------------------------------------
1114 */
1115
1116 int
1117 TkBTreeLineIndex (
1118 TkTextLine *linePtr /* Pointer to existing line in
1119 * B-tree. */
1120 )
1121 {
1122 register TkTextLine *linePtr2;
1123 register Node *nodePtr, *parentPtr, *nodePtr2;
1124 int index;
1125
1126 /*
1127 * First count how many lines precede this one in its level-0
1128 * node.
1129 */
1130
1131 nodePtr = linePtr->parentPtr;
1132 index = 0;
1133 for (linePtr2 = nodePtr->children.linePtr; linePtr2 != linePtr;
1134 linePtr2 = linePtr2->nextPtr) {
1135 if (linePtr2 == NULL) {
1136 panic("TkBTreeLineIndex couldn't find line");
1137 }
1138 index += 1;
1139 }
1140
1141 /*
1142 * Now work up through the levels of the tree one at a time,
1143 * counting how many lines are in nodes preceding the current
1144 * node.
1145 */
1146
1147 for (parentPtr = nodePtr->parentPtr ; parentPtr != NULL;
1148 nodePtr = parentPtr, parentPtr = parentPtr->parentPtr) {
1149 for (nodePtr2 = parentPtr->children.nodePtr; nodePtr2 != nodePtr;
1150 nodePtr2 = nodePtr2->nextPtr) {
1151 if (nodePtr2 == NULL) {
1152 panic("TkBTreeLineIndex couldn't find node");
1153 }
1154 index += nodePtr2->numLines;
1155 }
1156 }
1157 return index;
1158 }
1159 \f
1160 /*
1161 *----------------------------------------------------------------------
1162 *
1163 * TkBTreeStartSearch --
1164 *
1165 * This procedure sets up a search for tag transitions involving
1166 * a given tag (or all tags) in a given range of the text.
1167 *
1168 * Results:
1169 * None.
1170 *
1171 * Side effects:
1172 * The information at *searchPtr is set up so that subsequent calls
1173 * to TkBTreeNextTag will return information about the locations of
1174 * tag transitions. Note that TkBTreeNextTag must be called to get
1175 * the first transition.
1176 *
1177 *----------------------------------------------------------------------
1178 */
1179
1180 void
1181 TkBTreeStartSearch (
1182 TkTextBTree tree, /* Tree to search. */
1183 int line1,
1184 int ch1, /* Character position at which to * start search (tags at this position
1185 * will be returned). */
1186 int line2,
1187 int ch2, /* Character position at which to * stop search (tags at this position
1188 * will be returned). */
1189 TkTextTag *tagPtr, /* Tag to search for. NULL means
1190 * search for any tag. */
1191 register TkTextSearch *searchPtr /* Where to store information about
1192 * search's progress. */
1193 )
1194 {
1195 register TkAnnotation *annotPtr;
1196
1197 searchPtr->tree = tree;
1198 if (line1 < 0) {
1199 searchPtr->line1 = 0;
1200 searchPtr->ch1 = 0;
1201 } else {
1202 searchPtr->line1 = line1;
1203 searchPtr->ch1 = ch1;
1204 }
1205 searchPtr->line2 = line2;
1206 searchPtr->ch2 = ch2;
1207 searchPtr->tagPtr = tagPtr;
1208 searchPtr->allTags = (tagPtr == NULL);
1209
1210 searchPtr->linePtr = TkBTreeFindLine(searchPtr->tree, searchPtr->line1);
1211 if (searchPtr->linePtr == NULL) {
1212 searchPtr->line1 = searchPtr->line2;
1213 searchPtr->ch1 = searchPtr->ch2;
1214 searchPtr->annotPtr = NULL;
1215 } else {
1216 for (annotPtr = searchPtr->linePtr->annotPtr;
1217 (annotPtr != NULL) && (annotPtr->ch < ch1);
1218 annotPtr = annotPtr->nextPtr) {
1219 /* Empty loop body. */
1220 }
1221 searchPtr->annotPtr = annotPtr;
1222 }
1223 }
1224 \f
1225 /*
1226 *----------------------------------------------------------------------
1227 *
1228 * TkBTreeNextTag --
1229 *
1230 * Once a tag search has begun, successive calls to this procedure
1231 * return successive tag toggles. Note: it is NOT SAFE to call this
1232 * procedure if characters have been inserted into or deleted from
1233 * the B-tree since the call to TkBTreeStartSearch.
1234 *
1235 * Results:
1236 * The return value is 1 if another toggle was found that met the
1237 * criteria specified in the call to TkBTreeStartSearch. 0 is
1238 * returned if no more matching tag transitions were found.
1239 *
1240 * Side effects:
1241 * Information in *searchPtr is modified to update the state of the
1242 * search and indicate where the next tag toggle is located.
1243 *
1244 *----------------------------------------------------------------------
1245 */
1246
1247 int
1248 TkBTreeNextTag (
1249 register TkTextSearch *searchPtr /* Information about search in
1250 * progress; must have been set up by
1251 * call to TkBTreeStartSearch. */
1252 )
1253 {
1254 register TkAnnotation *annotPtr;
1255 register Node *nodePtr;
1256 register Summary *summaryPtr;
1257
1258 if (searchPtr->linePtr == NULL) {
1259 return 0;
1260 }
1261
1262 /*
1263 * The outermost loop iterates over lines that may potentially contain
1264 * a relevant tag transition, starting from the current line and tag.
1265 */
1266
1267 while (1) {
1268 /*
1269 * See if there are more tags on the current line that are relevant.
1270 */
1271
1272 for (annotPtr = searchPtr->annotPtr; annotPtr != NULL;
1273 annotPtr = annotPtr->nextPtr) {
1274 if ((annotPtr->type == TK_ANNOT_TOGGLE)
1275 && (searchPtr->allTags
1276 || (annotPtr->info.tagPtr == searchPtr->tagPtr))) {
1277 if ((searchPtr->line1 == searchPtr->line2)
1278 && (annotPtr->ch > searchPtr->ch2)) {
1279 goto searchOver;
1280 }
1281 searchPtr->tagPtr = annotPtr->info.tagPtr;
1282 searchPtr->ch1 = annotPtr->ch;
1283 searchPtr->annotPtr = annotPtr->nextPtr;
1284 return 1;
1285 }
1286 }
1287
1288 /*
1289 * See if there are more lines associated with the current parent
1290 * node. If so, go back to the top of the loop to search the next
1291 * one of them.
1292 */
1293
1294 if (searchPtr->line1 >= searchPtr->line2) {
1295 goto searchOver;
1296 }
1297 searchPtr->line1++;
1298 if (searchPtr->linePtr->nextPtr != NULL) {
1299 searchPtr->linePtr = searchPtr->linePtr->nextPtr;
1300 searchPtr->annotPtr = searchPtr->linePtr->annotPtr;
1301 continue;
1302 }
1303
1304 /*
1305 * Search across and up through the B-tree's node hierarchy looking
1306 * for the next node that has a relevant tag transition somewhere in
1307 * its subtree. Be sure to update the current line number as we
1308 * skip over large chunks of lines.
1309 */
1310
1311 nodePtr = searchPtr->linePtr->parentPtr;
1312 while (1) {
1313 while (nodePtr->nextPtr == NULL) {
1314 if (nodePtr->parentPtr == NULL) {
1315 goto searchOver;
1316 }
1317 nodePtr = nodePtr->parentPtr;
1318 }
1319 nodePtr = nodePtr->nextPtr;
1320 for (summaryPtr = nodePtr->summaryPtr; summaryPtr != NULL;
1321 summaryPtr = summaryPtr->nextPtr) {
1322 if ((searchPtr->allTags) ||
1323 (summaryPtr->tagPtr == searchPtr->tagPtr)) {
1324 goto gotNodeWithTag;
1325 }
1326 }
1327 searchPtr->line1 += nodePtr->numLines;
1328 }
1329
1330 /*
1331 * At this point we've found a subtree that has a relevant tag
1332 * transition. Now search down (and across) through that subtree
1333 * to find the first level-0 node that has a relevant tag transition.
1334 */
1335
1336 gotNodeWithTag:
1337 while (nodePtr->level > 0) {
1338 for (nodePtr = nodePtr->children.nodePtr; ;
1339 nodePtr = nodePtr->nextPtr) {
1340 for (summaryPtr = nodePtr->summaryPtr; summaryPtr != NULL;
1341 summaryPtr = summaryPtr->nextPtr) {
1342 if ((searchPtr->allTags)
1343 || (summaryPtr->tagPtr == searchPtr->tagPtr)) {
1344 goto nextChild;
1345 }
1346 }
1347 searchPtr->line1 += nodePtr->numLines;
1348 if (nodePtr->nextPtr == NULL) {
1349 panic("TkBTreeNextTag found incorrect tag summary info.");
1350 }
1351 }
1352 nextChild:
1353 continue;
1354 }
1355
1356 /*
1357 * Now we're down to a level-0 node that contains a line that contains
1358 * a relevant tag transition. Set up line information and go back to
1359 * the beginning of the loop to search through lines.
1360 */
1361
1362 searchPtr->linePtr = nodePtr->children.linePtr;
1363 searchPtr->annotPtr = searchPtr->linePtr->annotPtr;
1364 if (searchPtr->line1 > searchPtr->line2) {
1365 goto searchOver;
1366 }
1367 continue;
1368 }
1369
1370 searchOver:
1371 searchPtr->line1 = searchPtr->line2;
1372 searchPtr->ch1 = searchPtr->ch2;
1373 searchPtr->annotPtr = NULL;
1374 searchPtr->linePtr = NULL;
1375 return 0;
1376 }
1377 \f
1378 /*
1379 *----------------------------------------------------------------------
1380 *
1381 * TkBTreeCheck --
1382 *
1383 * This procedure runs a set of consistency checks over a B-tree
1384 * and panics if any inconsistencies are found.
1385 *
1386 * Results:
1387 * None.
1388 *
1389 * Side effects:
1390 * If a structural defect is found, the procedure panics with an
1391 * error message.
1392 *
1393 *----------------------------------------------------------------------
1394 */
1395
1396 void
1397 TkBTreeCheck (
1398 TkTextBTree tree /* Tree to check. */
1399 )
1400 {
1401 BTree *treePtr = (BTree *) tree;
1402 register Summary *summaryPtr;
1403
1404 /*
1405 * Make sure that overall there is an even count of tag transitions
1406 * for the whole text.
1407 */
1408
1409 for (summaryPtr = treePtr->rootPtr->summaryPtr; summaryPtr != NULL;
1410 summaryPtr = summaryPtr->nextPtr) {
1411 if (summaryPtr->toggleCount & 1) {
1412 panic("TkBTreeCheck found odd toggle count for \"%s\" (%d)",
1413 summaryPtr->tagPtr->name, summaryPtr->toggleCount);
1414 }
1415 }
1416
1417 /*
1418 * Call a recursive procedure to do all of the rest of the checks.
1419 */
1420
1421 CheckNodeConsistency(treePtr->rootPtr);
1422 }
1423 \f
1424 /*
1425 *----------------------------------------------------------------------
1426 *
1427 * Rebalance --
1428 *
1429 * This procedure is called when a node of a B-tree appears to be
1430 * out of balance (too many children, or too few). It rebalances
1431 * that node and all of its ancestors in the tree.
1432 *
1433 * Results:
1434 * None.
1435 *
1436 * Side effects:
1437 * The internal structure of treePtr may change.
1438 *
1439 *----------------------------------------------------------------------
1440 */
1441
1442 static void
1443 Rebalance (
1444 BTree *treePtr, /* Tree that is being rebalanced. */
1445 register Node *nodePtr /* Node that may be out of balance. */
1446 )
1447 {
1448 /*
1449 * Loop over the entire ancestral chain of the node, working up
1450 * through the tree one node at a time until the root node has
1451 * been processed.
1452 */
1453
1454 for ( ; nodePtr != NULL; nodePtr = nodePtr->parentPtr) {
1455 register Node *newPtr, *childPtr;
1456 register TkTextLine *linePtr;
1457 int i;
1458
1459 /*
1460 * Check to see if the node has too many children. If it does,
1461 * then split off all but the first MIN_CHILDREN into a separate
1462 * node following the original one. Then repeat until the
1463 * node has a decent size.
1464 */
1465
1466 if (nodePtr->numChildren > MAX_CHILDREN) {
1467 while (1) {
1468 /*
1469 * If the node being split is the root node, then make a
1470 * new root node above it first.
1471 */
1472
1473 if (nodePtr->parentPtr == NULL) {
1474 newPtr = (Node *) ckalloc(sizeof(Node));
1475 newPtr->parentPtr = NULL;
1476 newPtr->nextPtr = NULL;
1477 newPtr->summaryPtr = NULL;
1478 newPtr->level = nodePtr->level + 1;
1479 newPtr->children.nodePtr = nodePtr;
1480 newPtr->numChildren = 1;
1481 newPtr->numLines = nodePtr->numLines;
1482 RecomputeNodeCounts(newPtr);
1483 treePtr->rootPtr = newPtr;
1484 }
1485 newPtr = (Node *) ckalloc(sizeof(Node));
1486 newPtr->parentPtr = nodePtr->parentPtr;
1487 newPtr->nextPtr = nodePtr->nextPtr;
1488 nodePtr->nextPtr = newPtr;
1489 newPtr->summaryPtr = NULL;
1490 newPtr->level = nodePtr->level;
1491 newPtr->numChildren = nodePtr->numChildren - MIN_CHILDREN;
1492 if (nodePtr->level == 0) {
1493 for (i = MIN_CHILDREN-1,
1494 linePtr = nodePtr->children.linePtr;
1495 i > 0; i--, linePtr = linePtr->nextPtr) {
1496 /* Empty loop body. */
1497 }
1498 newPtr->children.linePtr = linePtr->nextPtr;
1499 linePtr->nextPtr = NULL;
1500 } else {
1501 for (i = MIN_CHILDREN-1,
1502 childPtr = nodePtr->children.nodePtr;
1503 i > 0; i--, childPtr = childPtr->nextPtr) {
1504 /* Empty loop body. */
1505 }
1506 newPtr->children.nodePtr = childPtr->nextPtr;
1507 childPtr->nextPtr = NULL;
1508 }
1509 RecomputeNodeCounts(nodePtr);
1510 nodePtr->parentPtr->numChildren++;
1511 nodePtr = newPtr;
1512 if (nodePtr->numChildren <= MAX_CHILDREN) {
1513 RecomputeNodeCounts(nodePtr);
1514 break;
1515 }
1516 }
1517 }
1518
1519 while (nodePtr->numChildren < MIN_CHILDREN) {
1520 register Node *otherPtr;
1521 Node *halfwayNodePtr = NULL; /* Initialization needed only */
1522 TkTextLine *halfwayLinePtr = NULL; /* to prevent cc warnings. */
1523 int totalChildren, firstChildren, i;
1524
1525 /*
1526 * Too few children for this node. If this is the root,
1527 * it's OK for it to have less than MIN_CHILDREN children
1528 * as long as it's got at least two. If it has only one
1529 * (and isn't at level 0), then chop the root node out of
1530 * the tree and use its child as the new root.
1531 */
1532
1533 if (nodePtr->parentPtr == NULL) {
1534 if ((nodePtr->numChildren == 1) && (nodePtr->level > 0)) {
1535 treePtr->rootPtr = nodePtr->children.nodePtr;
1536 treePtr->rootPtr->parentPtr = NULL;
1537 DeleteSummaries(nodePtr->summaryPtr);
1538 ckfree((char *) nodePtr);
1539 }
1540 return;
1541 }
1542
1543 /*
1544 * Not the root. Make sure that there are siblings to
1545 * balance with.
1546 */
1547
1548 if (nodePtr->parentPtr->numChildren < 2) {
1549 Rebalance(treePtr, nodePtr->parentPtr);
1550 continue;
1551 }
1552
1553 /*
1554 * Find a sibling to borrow from, and arrange for nodePtr to
1555 * be the earlier of the pair.
1556 */
1557
1558 if (nodePtr->nextPtr == NULL) {
1559 for (otherPtr = nodePtr->parentPtr->children.nodePtr;
1560 otherPtr->nextPtr != nodePtr;
1561 otherPtr = otherPtr->nextPtr) {
1562 /* Empty loop body. */
1563 }
1564 nodePtr = otherPtr;
1565 }
1566 otherPtr = nodePtr->nextPtr;
1567
1568 /*
1569 * We're going to either merge the two siblings together
1570 * into one node or redivide the children among them to
1571 * balance their loads. As preparation, join their two
1572 * child lists into a single list and remember the half-way
1573 * point in the list.
1574 */
1575
1576 totalChildren = nodePtr->numChildren + otherPtr->numChildren;
1577 firstChildren = totalChildren/2;
1578 if (nodePtr->children.nodePtr == NULL) {
1579 nodePtr->children = otherPtr->children;
1580 } else if (nodePtr->level == 0) {
1581 register TkTextLine *linePtr;
1582
1583 for (linePtr = nodePtr->children.linePtr, i = 1;
1584 linePtr->nextPtr != NULL;
1585 linePtr = linePtr->nextPtr, i++) {
1586 if (i == firstChildren) {
1587 halfwayLinePtr = linePtr;
1588 }
1589 }
1590 linePtr->nextPtr = otherPtr->children.linePtr;
1591 while (i <= firstChildren) {
1592 halfwayLinePtr = linePtr;
1593 linePtr = linePtr->nextPtr;
1594 i++;
1595 }
1596 } else {
1597 register Node *childPtr;
1598
1599 for (childPtr = nodePtr->children.nodePtr, i = 1;
1600 childPtr->nextPtr != NULL;
1601 childPtr = childPtr->nextPtr, i++) {
1602 if (i <= firstChildren) {
1603 if (i == firstChildren) {
1604 halfwayNodePtr = childPtr;
1605 }
1606 }
1607 }
1608 childPtr->nextPtr = otherPtr->children.nodePtr;
1609 while (i <= firstChildren) {
1610 halfwayNodePtr = childPtr;
1611 childPtr = childPtr->nextPtr;
1612 i++;
1613 }
1614 }
1615
1616 /*
1617 * If the two siblings can simply be merged together, do it.
1618 */
1619
1620 if (totalChildren < MAX_CHILDREN) {
1621 RecomputeNodeCounts(nodePtr);
1622 nodePtr->nextPtr = otherPtr->nextPtr;
1623 nodePtr->parentPtr->numChildren--;
1624 DeleteSummaries(otherPtr->summaryPtr);
1625 ckfree((char *) otherPtr);
1626 continue;
1627 }
1628
1629 /*
1630 * The siblings can't be merged, so just divide their
1631 * children evenly between them.
1632 */
1633
1634 if (nodePtr->level == 0) {
1635 otherPtr->children.linePtr = halfwayLinePtr->nextPtr;
1636 halfwayLinePtr->nextPtr = NULL;
1637 } else {
1638 otherPtr->children.nodePtr = halfwayNodePtr->nextPtr;
1639 halfwayNodePtr->nextPtr = NULL;
1640 }
1641 RecomputeNodeCounts(nodePtr);
1642 RecomputeNodeCounts(otherPtr);
1643 }
1644 }
1645 }
1646 \f
1647 /*
1648 *----------------------------------------------------------------------
1649 *
1650 * RecomputeNodeCounts --
1651 *
1652 * This procedure is called to recompute all the counts in a node
1653 * (tags, child information, etc.) by scaning the information in
1654 * its descendants. This procedure is called during rebalancing
1655 * when a node's child structure has changed.
1656 *
1657 * Results:
1658 * None.
1659 *
1660 * Side effects:
1661 * The tag counts for nodePtr are modified to reflect its current
1662 * child structure, as are its numChildren and numLines fields.
1663 * Also, all of the children's parentPtr fields are made to point
1664 * to nodePtr.
1665 *
1666 *----------------------------------------------------------------------
1667 */
1668
1669 static void
1670 RecomputeNodeCounts (
1671 register Node *nodePtr /* Node whose tag summary information
1672 * must be recomputed. */
1673 )
1674 {
1675 register Summary *summaryPtr, *summaryPtr2;
1676 register Node *childPtr;
1677 register TkTextLine *linePtr;
1678 register TkAnnotation *annotPtr;
1679
1680 /*
1681 * Zero out all the existing counts for the node, but don't delete
1682 * the existing Summary records (most of them will probably be reused).
1683 */
1684
1685 for (summaryPtr = nodePtr->summaryPtr; summaryPtr != NULL;
1686 summaryPtr = summaryPtr->nextPtr) {
1687 summaryPtr->toggleCount = 0;
1688 }
1689 nodePtr->numChildren = 0;
1690 nodePtr->numLines = 0;
1691
1692 /*
1693 * Scan through the children, adding the childrens' tag counts into
1694 * the node's tag counts and adding new Summarys to the node if
1695 * necessary.
1696 */
1697
1698 if (nodePtr->level == 0) {
1699 for (linePtr = nodePtr->children.linePtr; linePtr != NULL;
1700 linePtr = linePtr->nextPtr) {
1701 nodePtr->numChildren++;
1702 nodePtr->numLines++;
1703 linePtr->parentPtr = nodePtr;
1704 for (annotPtr = linePtr->annotPtr; annotPtr != NULL;
1705 annotPtr = annotPtr->nextPtr) {
1706 if (annotPtr->type != TK_ANNOT_TOGGLE) {
1707 continue;
1708 }
1709 for (summaryPtr = nodePtr->summaryPtr; ;
1710 summaryPtr = summaryPtr->nextPtr) {
1711 if (summaryPtr == NULL) {
1712 summaryPtr = (Summary *) ckalloc(sizeof(Summary));
1713 summaryPtr->tagPtr = annotPtr->info.tagPtr;
1714 summaryPtr->toggleCount = 1;
1715 summaryPtr->nextPtr = nodePtr->summaryPtr;
1716 nodePtr->summaryPtr = summaryPtr;
1717 break;
1718 }
1719 if (summaryPtr->tagPtr == annotPtr->info.tagPtr) {
1720 summaryPtr->toggleCount++;
1721 break;
1722 }
1723 }
1724 }
1725 }
1726 } else {
1727 for (childPtr = nodePtr->children.nodePtr; childPtr != NULL;
1728 childPtr = childPtr->nextPtr) {
1729 nodePtr->numChildren++;
1730 nodePtr->numLines += childPtr->numLines;
1731 childPtr->parentPtr = nodePtr;
1732 for (summaryPtr2 = childPtr->summaryPtr; summaryPtr2 != NULL;
1733 summaryPtr2 = summaryPtr2->nextPtr) {
1734 for (summaryPtr = nodePtr->summaryPtr; ;
1735 summaryPtr = summaryPtr->nextPtr) {
1736 if (summaryPtr == NULL) {
1737 summaryPtr = (Summary *) ckalloc(sizeof(Summary));
1738 summaryPtr->tagPtr = summaryPtr2->tagPtr;
1739 summaryPtr->toggleCount = summaryPtr2->toggleCount;
1740 summaryPtr->nextPtr = nodePtr->summaryPtr;
1741 nodePtr->summaryPtr = summaryPtr;
1742 break;
1743 }
1744 if (summaryPtr->tagPtr == summaryPtr2->tagPtr) {
1745 summaryPtr->toggleCount += summaryPtr2->toggleCount;
1746 break;
1747 }
1748 }
1749 }
1750 }
1751 }
1752
1753 /*
1754 * Scan through the node's tag records again and delete any Summary
1755 * records that still have a zero count.
1756 */
1757
1758 summaryPtr2 = NULL;
1759 for (summaryPtr = nodePtr->summaryPtr; summaryPtr != NULL; ) {
1760 if (summaryPtr->toggleCount > 0) {
1761 summaryPtr2 = summaryPtr;
1762 summaryPtr = summaryPtr->nextPtr;
1763 continue;
1764 }
1765 if (summaryPtr2 != NULL) {
1766 summaryPtr2->nextPtr = summaryPtr->nextPtr;
1767 ckfree((char *) summaryPtr);
1768 summaryPtr = summaryPtr2->nextPtr;
1769 } else {
1770 nodePtr->summaryPtr = summaryPtr->nextPtr;
1771 ckfree((char *) summaryPtr);
1772 summaryPtr = nodePtr->summaryPtr;
1773 }
1774 }
1775 }
1776 \f
1777 /*
1778 *----------------------------------------------------------------------
1779 *
1780 * AddToggleToLine --
1781 *
1782 * Insert a tag transition at a particular point in a particular
1783 * line.
1784 *
1785 * Results:
1786 * None.
1787 *
1788 * Side effects:
1789 * LinePtr and all its ancestors in the B-tree stucture are modified
1790 * to indicate the presence of a transition (either on or off) on
1791 * tag at the given place in the given line.
1792 *
1793 *----------------------------------------------------------------------
1794 */
1795
1796 static void
1797 AddToggleToLine (
1798 TkTextLine *linePtr, /* Line within which to add
1799 * transition. */
1800 int index, /* Character before which to
1801 * add transition. */
1802 TkTextTag *tagPtr /* Information about tag. */
1803 )
1804 {
1805 register TkAnnotation *annotPtr, *prevPtr;
1806 int delta = 1;
1807
1808 /*
1809 * Find the position where the toggle should be inserted into
1810 * the array (just after prevPtr), and see if there is already
1811 * a toggle at exactly the point where we're going to insert a
1812 * new toggle. If so then the two toggles cancel; just delete
1813 * the existing toggle.
1814 */
1815
1816 for (prevPtr = NULL, annotPtr = linePtr->annotPtr; annotPtr != NULL;
1817 prevPtr = annotPtr, annotPtr = annotPtr->nextPtr) {
1818 if (annotPtr->ch > index) {
1819 break;
1820 }
1821 if ((annotPtr->type == TK_ANNOT_TOGGLE)
1822 && (annotPtr->ch == index)
1823 && (annotPtr->info.tagPtr == tagPtr)) {
1824 if (prevPtr == NULL) {
1825 linePtr->annotPtr = annotPtr->nextPtr;
1826 } else {
1827 prevPtr->nextPtr = annotPtr->nextPtr;
1828 }
1829 ckfree((char *) annotPtr);
1830 delta = -1;
1831 goto updateNodes;
1832 }
1833 }
1834
1835 /*
1836 * Create a new toggle and insert it into the list.
1837 */
1838
1839 annotPtr = (TkAnnotation *) ckalloc(sizeof(TkAnnotation));
1840 annotPtr->type = TK_ANNOT_TOGGLE;
1841 annotPtr->linePtr = linePtr;
1842 annotPtr->ch = index;
1843 annotPtr->info.tagPtr = tagPtr;
1844 if (prevPtr == NULL) {
1845 annotPtr->nextPtr = linePtr->annotPtr;
1846 linePtr->annotPtr = annotPtr;
1847 } else {
1848 annotPtr->nextPtr = prevPtr->nextPtr;
1849 prevPtr->nextPtr = annotPtr;
1850 }
1851
1852 /*
1853 * Update all the nodes above this line to reflect the change in
1854 * toggle structure.
1855 */
1856
1857 updateNodes:
1858 ChangeNodeToggleCount(linePtr->parentPtr, tagPtr, delta);
1859 }
1860 \f
1861 /*
1862 *----------------------------------------------------------------------
1863 *
1864 * ChangeNodeToggleCount --
1865 *
1866 * This procedure increments or decrements the toggle count for
1867 * a particular tag in a particular node and all its ancestors.
1868 *
1869 * Results:
1870 * None.
1871 *
1872 * Side effects:
1873 * The toggle count for tag is adjusted up or down by "delta" in
1874 * nodePtr.
1875 *
1876 *----------------------------------------------------------------------
1877 */
1878
1879 static void
1880 ChangeNodeToggleCount (
1881 register Node *nodePtr, /* Node whose toggle count for a tag
1882 * must be changed. */
1883 TkTextTag *tagPtr, /* Information about tag. */
1884 int delta /* Amount to add to current toggle
1885 * count for tag (may be negative). */
1886 )
1887 {
1888 register Summary *summaryPtr, *prevPtr;
1889
1890 /*
1891 * Iterate over the node and all of its ancestors.
1892 */
1893
1894 for ( ; nodePtr != NULL; nodePtr = nodePtr->parentPtr) {
1895 /*
1896 * See if there's already an entry for this tag for this node. If so,
1897 * perhaps all we have to do is adjust its count.
1898 */
1899
1900 for (prevPtr = NULL, summaryPtr = nodePtr->summaryPtr;
1901 summaryPtr != NULL;
1902 prevPtr = summaryPtr, summaryPtr = summaryPtr->nextPtr) {
1903 if (summaryPtr->tagPtr != tagPtr) {
1904 continue;
1905 }
1906 summaryPtr->toggleCount += delta;
1907 if (summaryPtr->toggleCount > 0) {
1908 goto nextAncestor;
1909 }
1910 if (summaryPtr->toggleCount < 0) {
1911 panic("ChangeNodeToggleCount: negative toggle count");
1912 }
1913
1914 /*
1915 * Zero count; must remove this tag from the list.
1916 */
1917
1918 if (prevPtr == NULL) {
1919 nodePtr->summaryPtr = summaryPtr->nextPtr;
1920 } else {
1921 prevPtr->nextPtr = summaryPtr->nextPtr;
1922 }
1923 ckfree((char *) summaryPtr);
1924 goto nextAncestor;
1925 }
1926
1927 /*
1928 * This tag isn't in the list. Add a new entry to the list.
1929 */
1930
1931 if (delta < 0) {
1932 panic("ChangeNodeToggleCount: negative delta, no tag entry");
1933 }
1934 summaryPtr = (Summary *) ckalloc(sizeof(Summary));
1935 summaryPtr->tagPtr = tagPtr;
1936 summaryPtr->toggleCount = delta;
1937 summaryPtr->nextPtr = nodePtr->summaryPtr;
1938 nodePtr->summaryPtr = summaryPtr;
1939
1940 nextAncestor:
1941 continue;
1942 }
1943 }
1944 \f
1945 /*
1946 *----------------------------------------------------------------------
1947 *
1948 * TkBTreeCharTagged --
1949 *
1950 * Determine whether a particular character has a particular tag.
1951 *
1952 * Results:
1953 * The return value is 1 if the given tag is in effect at the
1954 * character given by linePtr and ch, and 0 otherwise.
1955 *
1956 * Side effects:
1957 * None.
1958 *
1959 *----------------------------------------------------------------------
1960 */
1961
1962 int
1963 TkBTreeCharTagged (
1964 TkTextLine *linePtr, /* Line containing character of
1965 * interest. */
1966 int ch, /* Index of character in linePtr. */
1967 TkTextTag *tagPtr /* Tag of interest. */
1968 )
1969 {
1970 register Node *nodePtr;
1971 register TkTextLine *siblingLinePtr;
1972 int toggles;
1973
1974 /*
1975 * Count the number of toggles for the tag at the line level (i.e.
1976 * in all the sibling lines that precede this one, plus in this line
1977 * up to the character of interest.
1978 */
1979
1980 toggles = 0;
1981 for (siblingLinePtr = linePtr->parentPtr->children.linePtr; ;
1982 siblingLinePtr = siblingLinePtr->nextPtr) {
1983 register TkAnnotation *annotPtr;
1984
1985 for (annotPtr = siblingLinePtr->annotPtr;
1986 (annotPtr != NULL) && ((siblingLinePtr != linePtr)
1987 || (annotPtr->ch <= ch));
1988 annotPtr = annotPtr->nextPtr) {
1989 if ((annotPtr->type == TK_ANNOT_TOGGLE)
1990 && (annotPtr->info.tagPtr == tagPtr)) {
1991 toggles++;
1992 }
1993 }
1994 if (siblingLinePtr == linePtr) {
1995 break;
1996 }
1997 }
1998
1999 /*
2000 * For each node in the ancestry of this line, count the number of
2001 * toggles of the given tag in siblings that precede that node.
2002 */
2003
2004 for (nodePtr = linePtr->parentPtr; nodePtr->parentPtr != NULL;
2005 nodePtr = nodePtr->parentPtr) {
2006 register Node *siblingPtr;
2007 register Summary *summaryPtr;
2008
2009 for (siblingPtr = nodePtr->parentPtr->children.nodePtr;
2010 siblingPtr != nodePtr; siblingPtr = siblingPtr->nextPtr) {
2011 for (summaryPtr = siblingPtr->summaryPtr; summaryPtr != NULL;
2012 summaryPtr = summaryPtr->nextPtr) {
2013 if (summaryPtr->tagPtr == tagPtr) {
2014 toggles += summaryPtr->toggleCount;
2015 }
2016 }
2017 }
2018 }
2019
2020 /*
2021 * An odd number of toggles means that the tag is present at the
2022 * given point.
2023 */
2024
2025 return toggles & 1;
2026 }
2027 \f
2028 /*
2029 *----------------------------------------------------------------------
2030 *
2031 * TkBTreeGetTags --
2032 *
2033 * Return information about all of the tags that are associated
2034 * with a particular character in a B-tree of text.
2035 *
2036 * Results:
2037 * The return value is a malloc-ed array containing pointers to
2038 * information for each of the tags that is associated with
2039 * the character at the position given by linePtr and ch. The
2040 * word at *numTagsPtr is filled in with the number of pointers
2041 * in the array. It is up to the caller to free the array by
2042 * passing it to free. If there are no tags at the given character
2043 * then a NULL pointer is returned and *numTagsPtr will be set to 0.
2044 *
2045 * Side effects:
2046 * None.
2047 *
2048 *----------------------------------------------------------------------
2049 */
2050
2051 /* ARGSUSED */
2052 TkTextTag **
2053 TkBTreeGetTags (
2054 TkTextBTree tree, /* Tree to check. */
2055 TkTextLine *linePtr, /* Line containing character of interest. */
2056 int ch, /* Index within linePtr of character for
2057 * which tag information is wanted. */
2058 int *numTagsPtr /* Store number of tags found at this
2059 * location. */
2060 )
2061 {
2062 register Node *nodePtr;
2063 register TkTextLine *siblingLinePtr;
2064 int src, dst;
2065 TagInfo tagInfo;
2066 #define NUM_TAG_INFOS 10
2067
2068 tagInfo.numTags = 0;
2069 tagInfo.arraySize = NUM_TAG_INFOS;
2070 tagInfo.tagPtrs = (TkTextTag **) ckalloc((unsigned)
2071 NUM_TAG_INFOS*sizeof(TkTextTag *));
2072 tagInfo.counts = (int *) ckalloc((unsigned)
2073 NUM_TAG_INFOS*sizeof(int));
2074
2075 /*
2076 * Record tag toggles at the line level (i.e. in all the sibling
2077 * lines that precede this one, plus in this line up to the character
2078 * of interest.
2079 */
2080
2081 for (siblingLinePtr = linePtr->parentPtr->children.linePtr; ;
2082 siblingLinePtr = siblingLinePtr->nextPtr) {
2083 register TkAnnotation *annotPtr;
2084
2085 for (annotPtr = siblingLinePtr->annotPtr;
2086 (annotPtr != NULL) && ((siblingLinePtr != linePtr)
2087 || (annotPtr->ch <= ch));
2088 annotPtr = annotPtr->nextPtr) {
2089 if (annotPtr->type == TK_ANNOT_TOGGLE) {
2090 IncCount(annotPtr->info.tagPtr, 1, &tagInfo);
2091 }
2092 }
2093 if (siblingLinePtr == linePtr) {
2094 break;
2095 }
2096 }
2097
2098 /*
2099 * For each node in the ancestry of this line, record tag toggles
2100 * for all siblings that precede that node.
2101 */
2102
2103 for (nodePtr = linePtr->parentPtr; nodePtr->parentPtr != NULL;
2104 nodePtr = nodePtr->parentPtr) {
2105 register Node *siblingPtr;
2106 register Summary *summaryPtr;
2107
2108 for (siblingPtr = nodePtr->parentPtr->children.nodePtr;
2109 siblingPtr != nodePtr; siblingPtr = siblingPtr->nextPtr) {
2110 for (summaryPtr = siblingPtr->summaryPtr; summaryPtr != NULL;
2111 summaryPtr = summaryPtr->nextPtr) {
2112 IncCount(summaryPtr->tagPtr, summaryPtr->toggleCount, &tagInfo);
2113 }
2114 }
2115 }
2116
2117 /*
2118 * Go through the tag information and squash out all of the tags
2119 * that have even toggle counts (these tags exist before the point
2120 * of interest, but not at the desired character itself).
2121 */
2122
2123 for (src = 0, dst = 0; src < tagInfo.numTags; src++) {
2124 if (tagInfo.counts[src] & 1) {
2125 tagInfo.tagPtrs[dst] = tagInfo.tagPtrs[src];
2126 dst++;
2127 }
2128 }
2129 *numTagsPtr = dst;
2130 ckfree((char *) tagInfo.counts);
2131 if (dst == 0) {
2132 ckfree((char *) tagInfo.tagPtrs);
2133 return NULL;
2134 }
2135 return tagInfo.tagPtrs;
2136 }
2137 \f
2138 /*
2139 *----------------------------------------------------------------------
2140 *
2141 * IncCount --
2142 *
2143 * This is a utility procedure used by TkBTreeGetTags. It
2144 * increments the count for a particular tag, adding a new
2145 * entry for that tag if there wasn't one previously.
2146 *
2147 * Results:
2148 * None.
2149 *
2150 * Side effects:
2151 * The information at *tagInfoPtr may be modified, and the arrays
2152 * may be reallocated to make them larger.
2153 *
2154 *----------------------------------------------------------------------
2155 */
2156
2157 static void
2158 IncCount (
2159 TkTextTag *tagPtr, /* Handle for tag. */
2160 int inc, /* Amount by which to increment tag count. */
2161 TagInfo *tagInfoPtr /* Holds cumulative information about tags;
2162 * increment count here. */
2163 )
2164 {
2165 register TkTextTag **tagPtrPtr;
2166 int count;
2167
2168 for (tagPtrPtr = tagInfoPtr->tagPtrs, count = tagInfoPtr->numTags;
2169 count > 0; tagPtrPtr++, count--) {
2170 if (*tagPtrPtr == tagPtr) {
2171 tagInfoPtr->counts[tagInfoPtr->numTags-count] += inc;
2172 return;
2173 }
2174 }
2175
2176 /*
2177 * There isn't currently an entry for this tag, so we have to
2178 * make a new one. If the arrays are full, then enlarge the
2179 * arrays first.
2180 */
2181
2182 if (tagInfoPtr->numTags == tagInfoPtr->arraySize) {
2183 TkTextTag **newTags;
2184 int *newCounts, newSize;
2185
2186 newSize = 2*tagInfoPtr->arraySize;
2187 newTags = (TkTextTag **) ckalloc((unsigned)
2188 (newSize*sizeof(TkTextTag *)));
2189 memcpy((VOID *) newTags, (VOID *) tagInfoPtr->tagPtrs,
2190 tagInfoPtr->arraySize * sizeof(TkTextTag *));
2191 ckfree((char *) tagInfoPtr->tagPtrs);
2192 tagInfoPtr->tagPtrs = newTags;
2193 newCounts = (int *) ckalloc((unsigned) (newSize*sizeof(int)));
2194 memcpy((VOID *) newCounts, (VOID *) tagInfoPtr->counts,
2195 tagInfoPtr->arraySize * sizeof(int));
2196 ckfree((char *) tagInfoPtr->counts);
2197 tagInfoPtr->counts = newCounts;
2198 tagInfoPtr->arraySize = newSize;
2199 }
2200
2201 tagInfoPtr->tagPtrs[tagInfoPtr->numTags] = tagPtr;
2202 tagInfoPtr->counts[tagInfoPtr->numTags] = inc;
2203 tagInfoPtr->numTags++;
2204 }
2205 \f
2206 /*
2207 *----------------------------------------------------------------------
2208 *
2209 * CheckNodeConsistency --
2210 *
2211 * This procedure is called as part of consistency checking for
2212 * B-trees: it checks several aspects of a node and also runs
2213 * checks recursively on the node's children.
2214 *
2215 * Results:
2216 * None.
2217 *
2218 * Side effects:
2219 * If anything suspicious is found in the tree structure, the
2220 * procedure panics.
2221 *
2222 *----------------------------------------------------------------------
2223 */
2224
2225 static void
2226 CheckNodeConsistency (
2227 register Node *nodePtr /* Node whose subtree should be
2228 * checked. */
2229 )
2230 {
2231 register Node *childNodePtr;
2232 register Summary *summaryPtr, *summaryPtr2;
2233 register TkAnnotation *annotPtr;
2234 register TkTextLine *linePtr;
2235 register char *p;
2236 int numChildren, numLines, toggleCount, minChildren, index, numBytes;
2237
2238 if (nodePtr->parentPtr != NULL) {
2239 minChildren = MIN_CHILDREN;
2240 } else if (nodePtr->level > 0) {
2241 minChildren = 2;
2242 } else {
2243 minChildren = 1;
2244 }
2245 if ((nodePtr->numChildren < minChildren)
2246 || (nodePtr->numChildren > MAX_CHILDREN)) {
2247 panic("CheckNodeConsistency found bad child count (%d)",
2248 nodePtr->numChildren);
2249 }
2250
2251 numChildren = 0;
2252 numLines = 0;
2253 if (nodePtr->level == 0) {
2254 for (linePtr = nodePtr->children.linePtr; linePtr != NULL;
2255 linePtr = linePtr->nextPtr) {
2256 if (linePtr->parentPtr != nodePtr) {
2257 panic("CheckNodeConsistency found line that %s",
2258 "didn't point to parent");
2259 }
2260 for (p = linePtr->bytes, numBytes = 0; *p != 0; p++, numBytes++) {
2261 if ((*p == '\n') && (numBytes != linePtr->numBytes-1)) {
2262 panic("CheckNodeConsistency found line with extra newline");
2263 }
2264 }
2265 if (numBytes != linePtr->numBytes) {
2266 panic("CheckNodeConsistency found line with bad numBytes");
2267 }
2268 if (linePtr->bytes[numBytes-1] != '\n') {
2269 panic("CheckNodeConsistency found line with no newline");
2270 }
2271 index = 0;
2272 for (annotPtr = linePtr->annotPtr; annotPtr != NULL;
2273 annotPtr = annotPtr->nextPtr) {
2274 if (annotPtr->ch < index) {
2275 panic("CheckNodeConsistency found %s (%d %d)",
2276 "out-of-order tag indices", index,
2277 annotPtr->ch);
2278 }
2279 index = annotPtr->ch;
2280 if (annotPtr->type == TK_ANNOT_TOGGLE) {
2281 for (summaryPtr = nodePtr->summaryPtr; ;
2282 summaryPtr = summaryPtr->nextPtr) {
2283 if (summaryPtr == NULL) {
2284 panic("CheckNodeConsistency found line %s",
2285 "tag with no node tag: %s",
2286 summaryPtr->tagPtr->name);
2287 }
2288 if (summaryPtr->tagPtr == annotPtr->info.tagPtr) {
2289 break;
2290 }
2291 }
2292 }
2293 }
2294 numChildren++;
2295 numLines++;
2296 }
2297 } else {
2298 for (childNodePtr = nodePtr->children.nodePtr; childNodePtr != NULL;
2299 childNodePtr = childNodePtr->nextPtr) {
2300 CheckNodeConsistency(childNodePtr);
2301 for (summaryPtr = childNodePtr->summaryPtr; summaryPtr != NULL;
2302 summaryPtr = summaryPtr->nextPtr) {
2303 for (summaryPtr2 = nodePtr->summaryPtr; ;
2304 summaryPtr2 = summaryPtr2->nextPtr) {
2305 if (summaryPtr2 == NULL) {
2306 panic("CheckNodeConsistency found %s (%s)",
2307 "node tag with no parent tag",
2308 summaryPtr->tagPtr->name);
2309 }
2310 if (summaryPtr->tagPtr == summaryPtr2->tagPtr) {
2311 break;
2312 }
2313 }
2314 }
2315 numChildren++;
2316 numLines += childNodePtr->numLines;
2317 if (childNodePtr->parentPtr != nodePtr) {
2318 panic("CheckNodeConsistency found node that %s",
2319 "didn't point to parent");
2320 }
2321 if (childNodePtr->level != (nodePtr->level-1)) {
2322 panic("CheckNodeConsistency found level mismatch (%d %d)",
2323 nodePtr->level, childNodePtr->level);
2324 }
2325 }
2326 }
2327 if (numChildren != nodePtr->numChildren) {
2328 panic("CheckNodeConsistency found mismatch in numChildren (%d %d)",
2329 numChildren, nodePtr->numChildren);
2330 }
2331 if (numLines != nodePtr->numLines) {
2332 panic("CheckNodeConsistency found mismatch in numLines (%d %d)",
2333 numLines, nodePtr->numLines);
2334 }
2335
2336 for (summaryPtr = nodePtr->summaryPtr; summaryPtr != NULL;
2337 summaryPtr = summaryPtr->nextPtr) {
2338 toggleCount = 0;
2339 if (nodePtr->level == 0) {
2340 for (linePtr = nodePtr->children.linePtr; linePtr != NULL;
2341 linePtr = linePtr->nextPtr) {
2342 for (annotPtr = linePtr->annotPtr; annotPtr != NULL;
2343 annotPtr = annotPtr->nextPtr) {
2344 if (annotPtr->info.tagPtr == summaryPtr->tagPtr) {
2345 toggleCount++;
2346 }
2347 }
2348 }
2349 } else {
2350 for (childNodePtr = nodePtr->children.nodePtr;
2351 childNodePtr != NULL;
2352 childNodePtr = childNodePtr->nextPtr) {
2353 for (summaryPtr2 = childNodePtr->summaryPtr;
2354 summaryPtr2 != NULL;
2355 summaryPtr2 = summaryPtr2->nextPtr) {
2356 if (summaryPtr2->tagPtr == summaryPtr->tagPtr) {
2357 toggleCount += summaryPtr2->toggleCount;
2358 }
2359 }
2360 }
2361 }
2362 if (toggleCount != summaryPtr->toggleCount) {
2363 panic("CheckNodeConsistency found mismatch in toggleCount (%d %d)",
2364 toggleCount, summaryPtr->toggleCount);
2365 }
2366 for (summaryPtr2 = summaryPtr->nextPtr; summaryPtr2 != NULL;
2367 summaryPtr2 = summaryPtr2->nextPtr) {
2368 if (summaryPtr2->tagPtr == summaryPtr->tagPtr) {
2369 panic("CheckNodeConsistency found duplicated node tag: %s",
2370 summaryPtr->tagPtr->name);
2371 }
2372 }
2373 }
2374 }
2375 \f
2376 /*
2377 *----------------------------------------------------------------------
2378 *
2379 * TkBTreeNumLines --
2380 *
2381 * This procedure returns a count of the number of lines of
2382 * text present in a given B-tree.
2383 *
2384 * Results:
2385 * The return value is a count of the number of lines in tree.
2386 *
2387 * Side effects:
2388 * None.
2389 *
2390 *----------------------------------------------------------------------
2391 */
2392
2393 int
2394 TkBTreeNumLines (
2395 TkTextBTree tree /* Information about tree. */
2396 )
2397 {
2398 BTree *treePtr = (BTree *) tree;
2399 return treePtr->rootPtr->numLines;
2400 }
Impressum, Datenschutz