]>
git.zerfleddert.de Git - micropolis/blob - src/tk/tktxbtre.c
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.
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.
19 static char rcsid
[] = "$Header: /user6/ouster/wish/RCS/tkTextBTree.c,v 1.16 92/08/17 09:13:58 ouster Exp $ SPRITE (Berkeley)";
28 * The data structure below keeps summary information about one tag as part
29 * of the tag information in a node.
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. */
42 * The data structure below defines a node in the B-tree representing
43 * all of the lines in a text widget.
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
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. */
62 int numChildren
; /* Number of children of this node. */
63 int numLines
; /* Total number of lines (leaves) in
64 * the subtree rooted here. */
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.
73 #define MAX_CHILDREN 12
74 #define MIN_CHILDREN 6
77 * The data structure below defines an entire B-tree.
80 typedef struct BTree
{
81 Node
*rootPtr
; /* Pointer to root of B-tree. */
85 * The structure below is used to pass information between
86 * TkBTreeGetTags and IncCount:
89 typedef struct TagInfo
{
90 int numTags
; /* Number of tags for which there
91 * is currently information in
93 int arraySize
; /* Number of entries allocated for
95 TkTextTag
**tagPtrs
; /* Array of tags seen so far.
97 int *counts
; /* Toggle count (so far) for each
98 * entry in tags. Malloc-ed. */
102 * Macro to compute the space needed for a line that holds n non-null
106 #define LINE_SIZE(n) ((unsigned) (sizeof(TkTextLine) - 3 + (n)))
109 * Variable that indicates whether to enable consistency checks for
113 int tkBTreeDebug
= 0;
116 * Forward declarations for procedures defined in this file:
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
));
132 *----------------------------------------------------------------------
136 * This procedure is called to create a new text B-tree.
139 * The return value is a pointer to a new B-tree containing
140 * one line with nothing but a newline character.
143 * Memory is allocated and initialized.
145 *----------------------------------------------------------------------
151 register BTree
*treePtr
;
152 register Node
*rootPtr
;
153 register TkTextLine
*linePtr
;
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
;
161 rootPtr
->children
.linePtr
= linePtr
;
162 rootPtr
->numChildren
= 1;
163 rootPtr
->numLines
= 1;
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;
172 treePtr
= (BTree
*) ckalloc(sizeof(BTree
));
173 treePtr
->rootPtr
= rootPtr
;
175 return (TkTextBTree
) treePtr
;
179 *----------------------------------------------------------------------
183 * Delete a B-tree, recycling all of the storage it contains.
186 * The tree given by treePtr is deleted. TreePtr should never
192 *----------------------------------------------------------------------
197 TkTextBTree tree
; /* Pointer to tree to delete. */
199 BTree
*treePtr
= (BTree
*) tree
;
201 DestroyNode(treePtr
->rootPtr
);
202 ckfree((char *) treePtr
);
206 *----------------------------------------------------------------------
210 * This is a recursive utility procedure used during the deletion
217 * All the storage for nodePtr and its descendants is freed.
219 *----------------------------------------------------------------------
224 register Node
*nodePtr
;
226 if (nodePtr
->level
== 0) {
227 register TkTextLine
*curPtr
, *nextLinePtr
;
228 register TkAnnotation
*annotPtr
, *nextAnnotPtr
;
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
);
237 annotPtr
= nextAnnotPtr
;
239 ckfree((char *) curPtr
);
240 curPtr
= nextLinePtr
;
243 register Node
*curPtr
, *nextPtr
;
245 for (curPtr
= nodePtr
->children
.nodePtr
; curPtr
!= NULL
; ) {
246 nextPtr
= curPtr
->nextPtr
;
251 DeleteSummaries(nodePtr
->summaryPtr
);
252 ckfree((char *) nodePtr
);
256 *----------------------------------------------------------------------
260 * Free up all of the memory in a list of tag summaries associated
267 * Storage is released.
269 *----------------------------------------------------------------------
273 DeleteSummaries(summaryPtr
)
274 register Summary
*summaryPtr
; /* First in list of node's tag
277 register Summary
*nextPtr
;
278 while (summaryPtr
!= NULL
) {
279 nextPtr
= summaryPtr
->nextPtr
;
280 ckfree((char *) summaryPtr
);
281 summaryPtr
= nextPtr
;
286 *----------------------------------------------------------------------
288 * TkBTreeInsertChars --
290 * Insert characters at a given position in a B-tree.
296 * NumBytes characters are added to the B-tree at the given
297 * character position. This can cause the structure of the
300 *----------------------------------------------------------------------
304 TkBTreeInsertChars(tree
, linePtr
, ch
, string
)
305 TkTextBTree tree
; /* B-tree in which to insert. */
306 register TkTextLine
*linePtr
; /* Pointer to line in which to
308 int ch
; /* Index of character before which
309 * to insert. Must not be after
310 * last character in line.*/
311 char *string
; /* Pointer to bytes to insert (may
312 * contain newlines, must be null-
315 BTree
*treePtr
= (BTree
*) tree
;
316 register Node
*nodePtr
;
317 register TkAnnotation
*annotPtr
;
319 int newChunkLength
; /* # chars in current line being
321 register char *eol
; /* Pointer to last character in
322 * current line being inserted. */
323 int changeToLineCount
; /* Counts change to total number of
325 TkAnnotation
*afterPtr
; /* List of annotations that occur
326 * at or after the insertion point
327 * in the line of the insertion. */
328 int prefixLength
, suffixLength
, totalLength
;
329 register TkTextLine
*newPtr
;
332 * Find the line just before the one where the insertion will occur
333 * but with the same parent node (if there is one). This is needed
334 * so we can replace the insertion line with a new one. Remove this
335 * line from the list for its parent, since it's going to be discarded
336 * when we're all done).
339 nodePtr
= linePtr
->parentPtr
;
340 prevPtr
= nodePtr
->children
.linePtr
;
341 if (prevPtr
== linePtr
) {
343 nodePtr
->children
.linePtr
= linePtr
->nextPtr
;
345 for ( ; prevPtr
->nextPtr
!= linePtr
; prevPtr
= prevPtr
->nextPtr
) {
346 /* Empty loop body. */
348 prevPtr
->nextPtr
= linePtr
->nextPtr
;
352 * Break up the annotations for the insertion line into two pieces:
353 * those before the insertion point, and those at or after the insertion
358 if ((linePtr
->annotPtr
!= NULL
) && (linePtr
->annotPtr
->ch
>= ch
)) {
359 afterPtr
= linePtr
->annotPtr
;
360 linePtr
->annotPtr
= NULL
;
362 for (annotPtr
= linePtr
->annotPtr
; annotPtr
!= NULL
;
363 annotPtr
= annotPtr
->nextPtr
) {
364 if ((annotPtr
->nextPtr
!= NULL
)
365 && (annotPtr
->nextPtr
->ch
>= ch
)) {
366 afterPtr
= annotPtr
->nextPtr
;
367 annotPtr
->nextPtr
= NULL
;
374 * Chop the string up into lines and insert each line individually.
377 changeToLineCount
= -1;
380 for (newChunkLength
= 0, eol
= string
; *eol
!= 0; eol
++) {
388 * Create a new line consisting of up to three parts: a prefix
389 * from linePtr, some material from string, and a suffix from
393 if ((newChunkLength
== 0) || (*eol
!= '\n')) {
394 suffixLength
= linePtr
->numBytes
- ch
;
398 totalLength
= prefixLength
+ newChunkLength
+ suffixLength
;
399 newPtr
= (TkTextLine
*) ckalloc(LINE_SIZE(totalLength
));
400 newPtr
->parentPtr
= nodePtr
;
401 if (prevPtr
== NULL
) {
402 newPtr
->nextPtr
= nodePtr
->children
.linePtr
;
403 nodePtr
->children
.linePtr
= newPtr
;
405 newPtr
->nextPtr
= prevPtr
->nextPtr
;
406 prevPtr
->nextPtr
= newPtr
;
408 if (linePtr
->annotPtr
!= NULL
) {
409 newPtr
->annotPtr
= linePtr
->annotPtr
;
410 for (annotPtr
= newPtr
->annotPtr
; annotPtr
!= NULL
;
411 annotPtr
= annotPtr
->nextPtr
) {
412 annotPtr
->linePtr
= newPtr
;
414 linePtr
->annotPtr
= NULL
;
416 newPtr
->annotPtr
= NULL
;
418 newPtr
->numBytes
= totalLength
;
419 if (prefixLength
!= 0) {
420 memcpy((VOID
*) newPtr
->bytes
, (VOID
*) linePtr
->bytes
,
423 if (newChunkLength
!= 0) {
424 memcpy((VOID
*) (newPtr
->bytes
+ prefixLength
), (VOID
*) string
,
427 if (suffixLength
!= 0) {
428 memcpy((VOID
*) (newPtr
->bytes
+ prefixLength
+ newChunkLength
),
429 (VOID
*) (linePtr
->bytes
+ ch
), suffixLength
);
431 newPtr
->bytes
[totalLength
] = 0;
432 changeToLineCount
+= 1;
435 * Quit after the suffix has been output (there is always at least
436 * one character of suffix: the newline). Before jumping out of the
437 * loop, put back the annotations that pertain to the suffix.
438 * Careful! If no newlines were inserted, there could already be
439 * annotations at the beginning of the line; add back to the end.
442 if (suffixLength
!= 0) {
443 if (newPtr
->annotPtr
== NULL
) {
444 newPtr
->annotPtr
= afterPtr
;
446 for (annotPtr
= newPtr
->annotPtr
; annotPtr
->nextPtr
!= NULL
;
447 annotPtr
= annotPtr
->nextPtr
) {
448 /* Empty loop body. */
450 annotPtr
->nextPtr
= afterPtr
;
452 for (annotPtr
= afterPtr
; annotPtr
!= NULL
;
453 annotPtr
= annotPtr
->nextPtr
) {
454 annotPtr
->linePtr
= newPtr
;
455 annotPtr
->ch
+= prefixLength
+newChunkLength
-ch
;
461 * Advance to insert the next line chunk.
464 string
+= newChunkLength
;
470 * Increment the line counts in all the parent nodes of the insertion
471 * point, then rebalance the tree if necessary.
474 for ( ; nodePtr
!= NULL
; nodePtr
= nodePtr
->parentPtr
) {
475 nodePtr
->numLines
+= changeToLineCount
;
477 nodePtr
= linePtr
->parentPtr
;
478 nodePtr
->numChildren
+= changeToLineCount
;
479 if (nodePtr
->numChildren
> MAX_CHILDREN
) {
480 Rebalance(treePtr
, nodePtr
);
483 ckfree((char *) linePtr
);
490 *----------------------------------------------------------------------
492 * TkBTreeDeleteChars --
494 * Delete a range of characters from a B-tree.
500 * Information is deleted from the B-tree. This can cause the
501 * internal structure of the B-tree to change. Note: the two
502 * lines given by line1Ptr and line2Ptr will be replaced with
503 * a single line containing the undeleted parts of the original
504 * lines. This could potentially result in an empty line;
505 * normally the caller should adjust the deletion range to prevent
506 * this sort of behavior.
508 *----------------------------------------------------------------------
512 TkBTreeDeleteChars(tree
, line1Ptr
, ch1
, line2Ptr
, ch2
)
513 TkTextBTree tree
; /* B-tree in which to delete. */
514 register TkTextLine
*line1Ptr
; /* Line containing first character
516 int ch1
; /* Index within linePtr1 of first
517 * character to delete. */
518 register TkTextLine
*line2Ptr
; /* Line containing character just
519 * after last one to delete. */
520 int ch2
; /* Index within linePtr2 of character
521 * just after last one to delete. */
523 BTree
*treePtr
= (BTree
*) tree
;
524 TkTextLine
*linePtr
, *nextPtr
, *prevLinePtr
;
525 Node
*nodePtr
, *parentPtr
, *nextNodePtr
;
526 TkAnnotation
*annotPtr
, *annotPtr2
;
528 int linesDeleted
; /* Counts lines deleted from current
532 * Work through the tree deleting all of the lines between line1Ptr
533 * and line2Ptr (but don't delete line1Ptr or line2Ptr yet). Also
534 * delete any nodes in the B-tree that become empty because of
538 linePtr
= line1Ptr
->nextPtr
;
539 nodePtr
= line1Ptr
->parentPtr
;
540 if (line1Ptr
== line2Ptr
) {
541 goto middleLinesDeleted
;
546 * Delete all relevant lines within the same level-0 node.
550 while ((linePtr
!= line2Ptr
) && (linePtr
!= NULL
)) {
552 * Move any annotations in this line to the end of the
553 * deletion range. If both the starting and ending toggle
554 * for a tagged range get moved, they'll cancel each other
555 * automatically and be dropped, which is the right behavior.
558 for (annotPtr
= linePtr
->annotPtr
; annotPtr
!= NULL
;
559 annotPtr
= annotPtr2
) {
560 if (annotPtr
->type
== TK_ANNOT_TOGGLE
) {
561 AddToggleToLine(line2Ptr
, ch2
, annotPtr
->info
.tagPtr
);
562 ChangeNodeToggleCount(nodePtr
, annotPtr
->info
.tagPtr
, -1);
563 annotPtr2
= annotPtr
->nextPtr
;
564 ckfree((char *) annotPtr
);
566 annotPtr2
= annotPtr
->nextPtr
;
567 TkBTreeRemoveAnnotation(annotPtr
);
568 annotPtr
->linePtr
= line2Ptr
;
570 TkBTreeAddAnnotation(annotPtr
);
573 nextPtr
= linePtr
->nextPtr
;
574 ckfree((char *) linePtr
);
578 if (nodePtr
== line1Ptr
->parentPtr
) {
579 line1Ptr
->nextPtr
= linePtr
;
581 nodePtr
->children
.linePtr
= linePtr
;
583 for (parentPtr
= nodePtr
; parentPtr
!= NULL
;
584 parentPtr
= parentPtr
->parentPtr
) {
585 parentPtr
->numLines
-= linesDeleted
;
587 nodePtr
->numChildren
-= linesDeleted
;
588 if (linePtr
== line2Ptr
) {
593 * Find the next level-0 node to visit, and its first line (but
594 * remember the current node so we can come back to delete it if
598 nextNodePtr
= nodePtr
;
599 while (nextNodePtr
->nextPtr
== NULL
) {
600 nextNodePtr
= nextNodePtr
->parentPtr
;
602 nextNodePtr
= nextNodePtr
->nextPtr
;
603 while (nextNodePtr
->level
> 0) {
604 nextNodePtr
= nextNodePtr
->children
.nodePtr
;
606 linePtr
= nextNodePtr
->children
.linePtr
;
609 * Now go back to the node we just left and delete it if
610 * it's empty, along with any of its ancestors that are
611 * empty. It may seem funny to go back like this, but it's
612 * simpler to find the next place to visit before modifying
613 * the tree structure.
616 while (nodePtr
->numChildren
== 0) {
617 parentPtr
= nodePtr
->parentPtr
;
618 if (parentPtr
->children
.nodePtr
== nodePtr
) {
619 parentPtr
->children
.nodePtr
= nodePtr
->nextPtr
;
623 for (prevPtr
= parentPtr
->children
.nodePtr
;
624 prevPtr
->nextPtr
!= nodePtr
;
625 prevPtr
= prevPtr
->nextPtr
) {
627 prevPtr
->nextPtr
= nodePtr
->nextPtr
;
629 parentPtr
->numChildren
--;
630 DeleteSummaries(nodePtr
->summaryPtr
);
631 ckfree((char *) nodePtr
);
634 nodePtr
= nextNodePtr
;
638 * Make a new line that consists of the first part of the first
639 * line of the deletion range and the last part of the last line
640 * of the deletion range.
644 nodePtr
= line1Ptr
->parentPtr
;
645 linePtr
= (TkTextLine
*) ckalloc(LINE_SIZE(ch1
+ line2Ptr
->numBytes
- ch2
));
646 linePtr
->parentPtr
= nodePtr
;
647 linePtr
->nextPtr
= line1Ptr
->nextPtr
;
648 linePtr
->annotPtr
= NULL
;
649 linePtr
->numBytes
= ch1
+ line2Ptr
->numBytes
- ch2
;
651 memcpy((VOID
*) linePtr
->bytes
, (VOID
*) line1Ptr
->bytes
, ch1
);
653 strcpy(linePtr
->bytes
+ ch1
, line2Ptr
->bytes
+ ch2
);
656 * Process the annotations for the starting and ending lines. Enter
657 * a new annotation on linePtr (the joined line) for each of these
658 * annotations, then delete the originals. The code below is a little
659 * tricky (e.g. the "break" in the first loop) to handle the case where
660 * the starting and ending lines are the same.
663 for (annotPtr
= line1Ptr
->annotPtr
; annotPtr
!= NULL
;
664 annotPtr
= line1Ptr
->annotPtr
) {
665 if (annotPtr
->ch
<= ch1
) {
668 if (line1Ptr
== line2Ptr
) {
673 line1Ptr
->annotPtr
= annotPtr
->nextPtr
;
674 if (annotPtr
->type
== TK_ANNOT_TOGGLE
) {
675 AddToggleToLine(linePtr
, ch
, annotPtr
->info
.tagPtr
);
676 ChangeNodeToggleCount(line1Ptr
->parentPtr
, annotPtr
->info
.tagPtr
,
678 ckfree((char *) annotPtr
);
680 annotPtr
->linePtr
= linePtr
;
682 TkBTreeAddAnnotation(annotPtr
);
685 for (annotPtr
= line2Ptr
->annotPtr
; annotPtr
!= NULL
;
686 annotPtr
= line2Ptr
->annotPtr
) {
687 if (annotPtr
->ch
>= ch2
) {
688 ch
= annotPtr
->ch
- ch2
+ ch1
;
692 line2Ptr
->annotPtr
= annotPtr
->nextPtr
;
693 if (annotPtr
->type
== TK_ANNOT_TOGGLE
) {
694 AddToggleToLine(linePtr
, ch
, annotPtr
->info
.tagPtr
);
695 ChangeNodeToggleCount(line2Ptr
->parentPtr
, annotPtr
->info
.tagPtr
,
697 ckfree((char *) annotPtr
);
699 annotPtr
->linePtr
= linePtr
;
701 TkBTreeAddAnnotation(annotPtr
);
706 * Delete the original starting and stopping lines (don't forget
707 * that the annotations have already been deleted) and insert the
708 * new line in place of line1Ptr.
711 nodePtr
= line1Ptr
->parentPtr
;
712 if (nodePtr
->children
.linePtr
== line1Ptr
) {
713 nodePtr
->children
.linePtr
= linePtr
;
715 for (prevLinePtr
= nodePtr
->children
.linePtr
;
716 prevLinePtr
->nextPtr
!= line1Ptr
;
717 prevLinePtr
= prevLinePtr
->nextPtr
) {
718 /* Empty loop body. */
720 prevLinePtr
->nextPtr
= linePtr
;
722 ckfree((char *) line1Ptr
);
723 nodePtr
= line2Ptr
->parentPtr
;
724 if (line2Ptr
!= line1Ptr
) {
725 if (nodePtr
->children
.linePtr
== line2Ptr
) {
726 nodePtr
->children
.linePtr
= line2Ptr
->nextPtr
;
728 for (prevLinePtr
= nodePtr
->children
.linePtr
;
729 prevLinePtr
->nextPtr
!= line2Ptr
;
730 prevLinePtr
= prevLinePtr
->nextPtr
) {
731 /* Empty loop body. */
733 prevLinePtr
->nextPtr
= line2Ptr
->nextPtr
;
735 ckfree((char *) line2Ptr
);
736 for (parentPtr
= nodePtr
; parentPtr
!= NULL
;
737 parentPtr
= parentPtr
->parentPtr
) {
738 parentPtr
->numLines
--;
740 nodePtr
->numChildren
--;
744 * Rebalance the tree, starting from each of the endpoints of the
745 * deletion range. This code is a tricky, because the act of
746 * rebalancing the parent of one endpoint can cause the parent of
747 * the other endpoint to be reallocated. The only thing it's safe
748 * to hold onto is a pointer to a line. Thus, rebalance line2Ptr's
749 * parent first, then use linePtr find the second parent to rebalance
753 if (nodePtr
!= linePtr
->parentPtr
) {
754 Rebalance(treePtr
, nodePtr
);
756 Rebalance(treePtr
, linePtr
->parentPtr
);
763 *----------------------------------------------------------------------
767 * Turn a given tag on or off for a given range of characters in
774 * The given tag is added to the given range of characters
775 * in the tree or removed from all those characters, depending
776 * on the "add" argument.
778 *----------------------------------------------------------------------
782 TkBTreeTag(tree
, line1
, ch1
, line2
, ch2
, tagPtr
, add
)
783 TkTextBTree tree
; /* B-tree in which to add tag
785 int line1
, ch1
; /* Position of first character to
787 int line2
, ch2
; /* Position of character just after
788 * last one to tag. */
789 TkTextTag
*tagPtr
; /* Tag to associate with the range
791 int add
; /* One means add tag to the given
792 * range of characters; zero means
793 * remove the tag from the range. */
795 BTree
*treePtr
= (BTree
*) tree
;
796 register TkTextLine
*line1Ptr
, *line2Ptr
;
801 * Find the lines containing the first and last characters to be tagged,
802 * and adjust the starting and stopping locations if they don't already
803 * point within lines. If the range would have started or stopped at the
804 * end of a line, round it up to the beginning of the next line (right
805 * now this restriction keeps the final newline from being tagged).
812 line1Ptr
= TkBTreeFindLine(tree
, line1
);
813 if (line1Ptr
== NULL
) {
816 if (ch1
>= line1Ptr
->numBytes
) {
817 TkTextLine
*nextLinePtr
;
819 nextLinePtr
= TkBTreeNextLine(line1Ptr
);
820 if (nextLinePtr
== NULL
) {
823 line1Ptr
= nextLinePtr
;
831 line2Ptr
= TkBTreeFindLine(tree
, line2
);
832 if (line2Ptr
== NULL
) {
833 line2Ptr
= TkBTreeFindLine(tree
, treePtr
->rootPtr
->numLines
-1);
834 ch2
= line2Ptr
->numBytes
-1;
836 if (ch2
>= line2Ptr
->numBytes
) {
837 TkTextLine
*nextLinePtr
;
839 nextLinePtr
= TkBTreeNextLine(line2Ptr
);
840 if (nextLinePtr
== NULL
) {
841 ch2
= line2Ptr
->numBytes
-1;
843 line2Ptr
= nextLinePtr
;
850 * See if the tag is already present or absent at the start of the
851 * range. If the state doesn't already match what we want then add
855 oldState
= TkBTreeCharTagged(line1Ptr
, ch1
, tagPtr
);
856 if ((add
!= 0) ^ oldState
) {
857 AddToggleToLine(line1Ptr
, ch1
, tagPtr
);
861 * Scan the range of characters covered by the change and delete
862 * any existing tag transitions except those on the first and
863 * last characters. Keep track of whether the old state just before
864 * the last character (not including any tags on it) is what we
865 * want now; if not, then add a tag toggle there.
868 TkBTreeStartSearch(tree
, line1
, ch1
+1, line2
, ch2
, tagPtr
, &search
);
869 while (TkBTreeNextTag(&search
)) {
870 if ((search
.linePtr
== line2Ptr
) && (search
.ch1
== ch2
)) {
874 AddToggleToLine(search
.linePtr
, search
.ch1
, tagPtr
);
876 if ((add
!= 0) ^ oldState
) {
877 AddToggleToLine(line2Ptr
, ch2
, tagPtr
);
886 *----------------------------------------------------------------------
888 * TkBTreeAddAnnotation --
890 * Given a filled in annotation, this procedure links it into
891 * a B-tree structure so that it will track changes to the B-tree.
897 * AnnotPtr will be linked into its tree. Note: the storage for
898 * annotPtr is assumed to have been malloc'ed by the caller.
900 *----------------------------------------------------------------------
905 TkBTreeAddAnnotation(annotPtr
)
906 TkAnnotation
*annotPtr
; /* Pointer to annotation. The caller must
907 * have filled in all the fields except the
908 * "nextPtr" field. The type should NOT be
909 * TK_ANNOT_TOGGLE; these annotations are
910 * managed by the TkBTreeTag procedure. */
912 register TkAnnotation
*annotPtr2
, *prevPtr
;
914 for (prevPtr
= NULL
, annotPtr2
= annotPtr
->linePtr
->annotPtr
;
916 prevPtr
= annotPtr2
, annotPtr2
= annotPtr2
->nextPtr
) {
917 if (annotPtr2
->ch
> annotPtr
->ch
) {
921 if (prevPtr
== NULL
) {
922 annotPtr
->nextPtr
= annotPtr
->linePtr
->annotPtr
;
923 annotPtr
->linePtr
->annotPtr
= annotPtr
;
925 annotPtr
->nextPtr
= prevPtr
->nextPtr
;
926 prevPtr
->nextPtr
= annotPtr
;
931 *----------------------------------------------------------------------
933 * TkBTreeRemoveAnnotation --
935 * This procedure unlinks an annotation from a B-tree so that
936 * the annotation will no longer be managed by the B-tree code.
942 * AnnotPtr will be unlinked from its tree. Note: it is up to the
943 * caller to free the storage for annotPtr, if that is desired.
945 *----------------------------------------------------------------------
950 TkBTreeRemoveAnnotation(annotPtr
)
951 TkAnnotation
*annotPtr
; /* Pointer to annotation, which must
952 * have been linked into tree by a previous
953 * call to TkBTreeAddAnnotation. */
955 register TkAnnotation
*prevPtr
;
957 if (annotPtr
->linePtr
->annotPtr
== annotPtr
) {
958 annotPtr
->linePtr
->annotPtr
= annotPtr
->nextPtr
;
960 for (prevPtr
= annotPtr
->linePtr
->annotPtr
;
961 /* BUG: fixed by dhopkins, prevPtr was null!
962 prevPtr->nextPtr != annotPtr;
964 (prevPtr
!= NULL
) && (prevPtr
->nextPtr
!= annotPtr
);
965 prevPtr
= prevPtr
->nextPtr
) {
966 /* Empty loop body. */
968 if (prevPtr
!= NULL
) { /* Bullet proofing by dhopkins */
969 prevPtr
->nextPtr
= annotPtr
->nextPtr
;
975 *----------------------------------------------------------------------
979 * Find a particular line in a B-tree based on its line number.
982 * The return value is a pointer to the line structure for the
983 * line whose index is "line", or NULL if no such line exists.
988 *----------------------------------------------------------------------
992 TkBTreeFindLine(tree
, line
)
993 TkTextBTree tree
; /* B-tree in which to find line. */
994 int line
; /* Index of desired line. */
996 BTree
*treePtr
= (BTree
*) tree
;
997 register Node
*nodePtr
;
998 register TkTextLine
*linePtr
;
1001 nodePtr
= treePtr
->rootPtr
;
1003 if ((line
< 0) || (line
>= nodePtr
->numLines
)) {
1008 * Work down through levels of the tree until a node is found at
1012 while (nodePtr
->level
!= 0) {
1013 for (nodePtr
= nodePtr
->children
.nodePtr
;
1014 nodePtr
->numLines
<= linesLeft
;
1015 nodePtr
= nodePtr
->nextPtr
) {
1016 if (nodePtr
== NULL
) {
1017 panic("TkBTreeFindLine ran out of nodes");
1019 linesLeft
-= nodePtr
->numLines
;
1024 * Work through the lines attached to the level-0 node.
1027 for (linePtr
= nodePtr
->children
.linePtr
; linesLeft
> 0;
1028 linePtr
= linePtr
->nextPtr
) {
1029 if (linePtr
== NULL
) {
1030 panic("TkBTreeFindLine ran out of lines");
1038 *----------------------------------------------------------------------
1040 * TkBTreeNextLine --
1042 * Given an existing line in a B-tree, this procedure locates the
1043 * next line in the B-tree. This procedure is used for scanning
1044 * through the B-tree.
1047 * The return value is a pointer to the line that immediately
1048 * follows linePtr, or NULL if there is no such line.
1053 *----------------------------------------------------------------------
1057 TkBTreeNextLine(linePtr
)
1058 register TkTextLine
*linePtr
; /* Pointer to existing line in
1061 register Node
*nodePtr
;
1063 if (linePtr
->nextPtr
!= NULL
) {
1064 return linePtr
->nextPtr
;
1068 * This was the last line associated with the particular parent node.
1069 * Search up the tree for the next node, then search down from that
1070 * node to find the first line,
1073 for (nodePtr
= linePtr
->parentPtr
; ; nodePtr
= nodePtr
->parentPtr
) {
1074 if (nodePtr
->nextPtr
!= NULL
) {
1075 nodePtr
= nodePtr
->nextPtr
;
1078 if (nodePtr
->parentPtr
== NULL
) {
1079 return (TkTextLine
*) NULL
;
1082 while (nodePtr
->level
> 0) {
1083 nodePtr
= nodePtr
->children
.nodePtr
;
1085 return nodePtr
->children
.linePtr
;
1089 *----------------------------------------------------------------------
1091 * TkBTreeLineIndex --
1093 * Given a pointer to a line in a B-tree, return the numerical
1094 * index of that line.
1097 * The result is the index of linePtr within the tree, where 0
1098 * corresponds to the first line in the tree.
1103 *----------------------------------------------------------------------
1107 TkBTreeLineIndex(linePtr
)
1108 TkTextLine
*linePtr
; /* Pointer to existing line in
1111 register TkTextLine
*linePtr2
;
1112 register Node
*nodePtr
, *parentPtr
, *nodePtr2
;
1116 * First count how many lines precede this one in its level-0
1120 nodePtr
= linePtr
->parentPtr
;
1122 for (linePtr2
= nodePtr
->children
.linePtr
; linePtr2
!= linePtr
;
1123 linePtr2
= linePtr2
->nextPtr
) {
1124 if (linePtr2
== NULL
) {
1125 panic("TkBTreeLineIndex couldn't find line");
1131 * Now work up through the levels of the tree one at a time,
1132 * counting how many lines are in nodes preceding the current
1136 for (parentPtr
= nodePtr
->parentPtr
; parentPtr
!= NULL
;
1137 nodePtr
= parentPtr
, parentPtr
= parentPtr
->parentPtr
) {
1138 for (nodePtr2
= parentPtr
->children
.nodePtr
; nodePtr2
!= nodePtr
;
1139 nodePtr2
= nodePtr2
->nextPtr
) {
1140 if (nodePtr2
== NULL
) {
1141 panic("TkBTreeLineIndex couldn't find node");
1143 index
+= nodePtr2
->numLines
;
1150 *----------------------------------------------------------------------
1152 * TkBTreeStartSearch --
1154 * This procedure sets up a search for tag transitions involving
1155 * a given tag (or all tags) in a given range of the text.
1161 * The information at *searchPtr is set up so that subsequent calls
1162 * to TkBTreeNextTag will return information about the locations of
1163 * tag transitions. Note that TkBTreeNextTag must be called to get
1164 * the first transition.
1166 *----------------------------------------------------------------------
1170 TkBTreeStartSearch(tree
, line1
, ch1
, line2
, ch2
, tagPtr
, searchPtr
)
1171 TkTextBTree tree
; /* Tree to search. */
1172 int line1
, ch1
; /* Character position at which to * start search (tags at this position
1173 * will be returned). */
1174 int line2
, ch2
; /* Character position at which to * stop search (tags at this position
1175 * will be returned). */
1176 TkTextTag
*tagPtr
; /* Tag to search for. NULL means
1177 * search for any tag. */
1178 register TkTextSearch
*searchPtr
; /* Where to store information about
1179 * search's progress. */
1181 register TkAnnotation
*annotPtr
;
1183 searchPtr
->tree
= tree
;
1185 searchPtr
->line1
= 0;
1188 searchPtr
->line1
= line1
;
1189 searchPtr
->ch1
= ch1
;
1191 searchPtr
->line2
= line2
;
1192 searchPtr
->ch2
= ch2
;
1193 searchPtr
->tagPtr
= tagPtr
;
1194 searchPtr
->allTags
= (tagPtr
== NULL
);
1196 searchPtr
->linePtr
= TkBTreeFindLine(searchPtr
->tree
, searchPtr
->line1
);
1197 if (searchPtr
->linePtr
== NULL
) {
1198 searchPtr
->line1
= searchPtr
->line2
;
1199 searchPtr
->ch1
= searchPtr
->ch2
;
1200 searchPtr
->annotPtr
= NULL
;
1202 for (annotPtr
= searchPtr
->linePtr
->annotPtr
;
1203 (annotPtr
!= NULL
) && (annotPtr
->ch
< ch1
);
1204 annotPtr
= annotPtr
->nextPtr
) {
1205 /* Empty loop body. */
1207 searchPtr
->annotPtr
= annotPtr
;
1212 *----------------------------------------------------------------------
1216 * Once a tag search has begun, successive calls to this procedure
1217 * return successive tag toggles. Note: it is NOT SAFE to call this
1218 * procedure if characters have been inserted into or deleted from
1219 * the B-tree since the call to TkBTreeStartSearch.
1222 * The return value is 1 if another toggle was found that met the
1223 * criteria specified in the call to TkBTreeStartSearch. 0 is
1224 * returned if no more matching tag transitions were found.
1227 * Information in *searchPtr is modified to update the state of the
1228 * search and indicate where the next tag toggle is located.
1230 *----------------------------------------------------------------------
1234 TkBTreeNextTag(searchPtr
)
1235 register TkTextSearch
*searchPtr
; /* Information about search in
1236 * progress; must have been set up by
1237 * call to TkBTreeStartSearch. */
1239 register TkAnnotation
*annotPtr
;
1240 register Node
*nodePtr
;
1241 register Summary
*summaryPtr
;
1243 if (searchPtr
->linePtr
== NULL
) {
1248 * The outermost loop iterates over lines that may potentially contain
1249 * a relevant tag transition, starting from the current line and tag.
1254 * See if there are more tags on the current line that are relevant.
1257 for (annotPtr
= searchPtr
->annotPtr
; annotPtr
!= NULL
;
1258 annotPtr
= annotPtr
->nextPtr
) {
1259 if ((annotPtr
->type
== TK_ANNOT_TOGGLE
)
1260 && (searchPtr
->allTags
1261 || (annotPtr
->info
.tagPtr
== searchPtr
->tagPtr
))) {
1262 if ((searchPtr
->line1
== searchPtr
->line2
)
1263 && (annotPtr
->ch
> searchPtr
->ch2
)) {
1266 searchPtr
->tagPtr
= annotPtr
->info
.tagPtr
;
1267 searchPtr
->ch1
= annotPtr
->ch
;
1268 searchPtr
->annotPtr
= annotPtr
->nextPtr
;
1274 * See if there are more lines associated with the current parent
1275 * node. If so, go back to the top of the loop to search the next
1279 if (searchPtr
->line1
>= searchPtr
->line2
) {
1283 if (searchPtr
->linePtr
->nextPtr
!= NULL
) {
1284 searchPtr
->linePtr
= searchPtr
->linePtr
->nextPtr
;
1285 searchPtr
->annotPtr
= searchPtr
->linePtr
->annotPtr
;
1290 * Search across and up through the B-tree's node hierarchy looking
1291 * for the next node that has a relevant tag transition somewhere in
1292 * its subtree. Be sure to update the current line number as we
1293 * skip over large chunks of lines.
1296 nodePtr
= searchPtr
->linePtr
->parentPtr
;
1298 while (nodePtr
->nextPtr
== NULL
) {
1299 if (nodePtr
->parentPtr
== NULL
) {
1302 nodePtr
= nodePtr
->parentPtr
;
1304 nodePtr
= nodePtr
->nextPtr
;
1305 for (summaryPtr
= nodePtr
->summaryPtr
; summaryPtr
!= NULL
;
1306 summaryPtr
= summaryPtr
->nextPtr
) {
1307 if ((searchPtr
->allTags
) ||
1308 (summaryPtr
->tagPtr
== searchPtr
->tagPtr
)) {
1309 goto gotNodeWithTag
;
1312 searchPtr
->line1
+= nodePtr
->numLines
;
1316 * At this point we've found a subtree that has a relevant tag
1317 * transition. Now search down (and across) through that subtree
1318 * to find the first level-0 node that has a relevant tag transition.
1322 while (nodePtr
->level
> 0) {
1323 for (nodePtr
= nodePtr
->children
.nodePtr
; ;
1324 nodePtr
= nodePtr
->nextPtr
) {
1325 for (summaryPtr
= nodePtr
->summaryPtr
; summaryPtr
!= NULL
;
1326 summaryPtr
= summaryPtr
->nextPtr
) {
1327 if ((searchPtr
->allTags
)
1328 || (summaryPtr
->tagPtr
== searchPtr
->tagPtr
)) {
1332 searchPtr
->line1
+= nodePtr
->numLines
;
1333 if (nodePtr
->nextPtr
== NULL
) {
1334 panic("TkBTreeNextTag found incorrect tag summary info.");
1342 * Now we're down to a level-0 node that contains a line that contains
1343 * a relevant tag transition. Set up line information and go back to
1344 * the beginning of the loop to search through lines.
1347 searchPtr
->linePtr
= nodePtr
->children
.linePtr
;
1348 searchPtr
->annotPtr
= searchPtr
->linePtr
->annotPtr
;
1349 if (searchPtr
->line1
> searchPtr
->line2
) {
1356 searchPtr
->line1
= searchPtr
->line2
;
1357 searchPtr
->ch1
= searchPtr
->ch2
;
1358 searchPtr
->annotPtr
= NULL
;
1359 searchPtr
->linePtr
= NULL
;
1364 *----------------------------------------------------------------------
1368 * This procedure runs a set of consistency checks over a B-tree
1369 * and panics if any inconsistencies are found.
1375 * If a structural defect is found, the procedure panics with an
1378 *----------------------------------------------------------------------
1383 TkTextBTree tree
; /* Tree to check. */
1385 BTree
*treePtr
= (BTree
*) tree
;
1386 register Summary
*summaryPtr
;
1389 * Make sure that overall there is an even count of tag transitions
1390 * for the whole text.
1393 for (summaryPtr
= treePtr
->rootPtr
->summaryPtr
; summaryPtr
!= NULL
;
1394 summaryPtr
= summaryPtr
->nextPtr
) {
1395 if (summaryPtr
->toggleCount
& 1) {
1396 panic("TkBTreeCheck found odd toggle count for \"%s\" (%d)",
1397 summaryPtr
->tagPtr
->name
, summaryPtr
->toggleCount
);
1402 * Call a recursive procedure to do all of the rest of the checks.
1405 CheckNodeConsistency(treePtr
->rootPtr
);
1409 *----------------------------------------------------------------------
1413 * This procedure is called when a node of a B-tree appears to be
1414 * out of balance (too many children, or too few). It rebalances
1415 * that node and all of its ancestors in the tree.
1421 * The internal structure of treePtr may change.
1423 *----------------------------------------------------------------------
1427 Rebalance(treePtr
, nodePtr
)
1428 BTree
*treePtr
; /* Tree that is being rebalanced. */
1429 register Node
*nodePtr
; /* Node that may be out of balance. */
1432 * Loop over the entire ancestral chain of the node, working up
1433 * through the tree one node at a time until the root node has
1437 for ( ; nodePtr
!= NULL
; nodePtr
= nodePtr
->parentPtr
) {
1438 register Node
*newPtr
, *childPtr
;
1439 register TkTextLine
*linePtr
;
1443 * Check to see if the node has too many children. If it does,
1444 * then split off all but the first MIN_CHILDREN into a separate
1445 * node following the original one. Then repeat until the
1446 * node has a decent size.
1449 if (nodePtr
->numChildren
> MAX_CHILDREN
) {
1452 * If the node being split is the root node, then make a
1453 * new root node above it first.
1456 if (nodePtr
->parentPtr
== NULL
) {
1457 newPtr
= (Node
*) ckalloc(sizeof(Node
));
1458 newPtr
->parentPtr
= NULL
;
1459 newPtr
->nextPtr
= NULL
;
1460 newPtr
->summaryPtr
= NULL
;
1461 newPtr
->level
= nodePtr
->level
+ 1;
1462 newPtr
->children
.nodePtr
= nodePtr
;
1463 newPtr
->numChildren
= 1;
1464 newPtr
->numLines
= nodePtr
->numLines
;
1465 RecomputeNodeCounts(newPtr
);
1466 treePtr
->rootPtr
= newPtr
;
1468 newPtr
= (Node
*) ckalloc(sizeof(Node
));
1469 newPtr
->parentPtr
= nodePtr
->parentPtr
;
1470 newPtr
->nextPtr
= nodePtr
->nextPtr
;
1471 nodePtr
->nextPtr
= newPtr
;
1472 newPtr
->summaryPtr
= NULL
;
1473 newPtr
->level
= nodePtr
->level
;
1474 newPtr
->numChildren
= nodePtr
->numChildren
- MIN_CHILDREN
;
1475 if (nodePtr
->level
== 0) {
1476 for (i
= MIN_CHILDREN
-1,
1477 linePtr
= nodePtr
->children
.linePtr
;
1478 i
> 0; i
--, linePtr
= linePtr
->nextPtr
) {
1479 /* Empty loop body. */
1481 newPtr
->children
.linePtr
= linePtr
->nextPtr
;
1482 linePtr
->nextPtr
= NULL
;
1484 for (i
= MIN_CHILDREN
-1,
1485 childPtr
= nodePtr
->children
.nodePtr
;
1486 i
> 0; i
--, childPtr
= childPtr
->nextPtr
) {
1487 /* Empty loop body. */
1489 newPtr
->children
.nodePtr
= childPtr
->nextPtr
;
1490 childPtr
->nextPtr
= NULL
;
1492 RecomputeNodeCounts(nodePtr
);
1493 nodePtr
->parentPtr
->numChildren
++;
1495 if (nodePtr
->numChildren
<= MAX_CHILDREN
) {
1496 RecomputeNodeCounts(nodePtr
);
1502 while (nodePtr
->numChildren
< MIN_CHILDREN
) {
1503 register Node
*otherPtr
;
1504 Node
*halfwayNodePtr
= NULL
; /* Initialization needed only */
1505 TkTextLine
*halfwayLinePtr
= NULL
; /* to prevent cc warnings. */
1506 int totalChildren
, firstChildren
, i
;
1509 * Too few children for this node. If this is the root,
1510 * it's OK for it to have less than MIN_CHILDREN children
1511 * as long as it's got at least two. If it has only one
1512 * (and isn't at level 0), then chop the root node out of
1513 * the tree and use its child as the new root.
1516 if (nodePtr
->parentPtr
== NULL
) {
1517 if ((nodePtr
->numChildren
== 1) && (nodePtr
->level
> 0)) {
1518 treePtr
->rootPtr
= nodePtr
->children
.nodePtr
;
1519 treePtr
->rootPtr
->parentPtr
= NULL
;
1520 DeleteSummaries(nodePtr
->summaryPtr
);
1521 ckfree((char *) nodePtr
);
1527 * Not the root. Make sure that there are siblings to
1531 if (nodePtr
->parentPtr
->numChildren
< 2) {
1532 Rebalance(treePtr
, nodePtr
->parentPtr
);
1537 * Find a sibling to borrow from, and arrange for nodePtr to
1538 * be the earlier of the pair.
1541 if (nodePtr
->nextPtr
== NULL
) {
1542 for (otherPtr
= nodePtr
->parentPtr
->children
.nodePtr
;
1543 otherPtr
->nextPtr
!= nodePtr
;
1544 otherPtr
= otherPtr
->nextPtr
) {
1545 /* Empty loop body. */
1549 otherPtr
= nodePtr
->nextPtr
;
1552 * We're going to either merge the two siblings together
1553 * into one node or redivide the children among them to
1554 * balance their loads. As preparation, join their two
1555 * child lists into a single list and remember the half-way
1556 * point in the list.
1559 totalChildren
= nodePtr
->numChildren
+ otherPtr
->numChildren
;
1560 firstChildren
= totalChildren
/2;
1561 if (nodePtr
->children
.nodePtr
== NULL
) {
1562 nodePtr
->children
= otherPtr
->children
;
1563 } else if (nodePtr
->level
== 0) {
1564 register TkTextLine
*linePtr
;
1566 for (linePtr
= nodePtr
->children
.linePtr
, i
= 1;
1567 linePtr
->nextPtr
!= NULL
;
1568 linePtr
= linePtr
->nextPtr
, i
++) {
1569 if (i
== firstChildren
) {
1570 halfwayLinePtr
= linePtr
;
1573 linePtr
->nextPtr
= otherPtr
->children
.linePtr
;
1574 while (i
<= firstChildren
) {
1575 halfwayLinePtr
= linePtr
;
1576 linePtr
= linePtr
->nextPtr
;
1580 register Node
*childPtr
;
1582 for (childPtr
= nodePtr
->children
.nodePtr
, i
= 1;
1583 childPtr
->nextPtr
!= NULL
;
1584 childPtr
= childPtr
->nextPtr
, i
++) {
1585 if (i
<= firstChildren
) {
1586 if (i
== firstChildren
) {
1587 halfwayNodePtr
= childPtr
;
1591 childPtr
->nextPtr
= otherPtr
->children
.nodePtr
;
1592 while (i
<= firstChildren
) {
1593 halfwayNodePtr
= childPtr
;
1594 childPtr
= childPtr
->nextPtr
;
1600 * If the two siblings can simply be merged together, do it.
1603 if (totalChildren
< MAX_CHILDREN
) {
1604 RecomputeNodeCounts(nodePtr
);
1605 nodePtr
->nextPtr
= otherPtr
->nextPtr
;
1606 nodePtr
->parentPtr
->numChildren
--;
1607 DeleteSummaries(otherPtr
->summaryPtr
);
1608 ckfree((char *) otherPtr
);
1613 * The siblings can't be merged, so just divide their
1614 * children evenly between them.
1617 if (nodePtr
->level
== 0) {
1618 otherPtr
->children
.linePtr
= halfwayLinePtr
->nextPtr
;
1619 halfwayLinePtr
->nextPtr
= NULL
;
1621 otherPtr
->children
.nodePtr
= halfwayNodePtr
->nextPtr
;
1622 halfwayNodePtr
->nextPtr
= NULL
;
1624 RecomputeNodeCounts(nodePtr
);
1625 RecomputeNodeCounts(otherPtr
);
1631 *----------------------------------------------------------------------
1633 * RecomputeNodeCounts --
1635 * This procedure is called to recompute all the counts in a node
1636 * (tags, child information, etc.) by scaning the information in
1637 * its descendants. This procedure is called during rebalancing
1638 * when a node's child structure has changed.
1644 * The tag counts for nodePtr are modified to reflect its current
1645 * child structure, as are its numChildren and numLines fields.
1646 * Also, all of the children's parentPtr fields are made to point
1649 *----------------------------------------------------------------------
1653 RecomputeNodeCounts(nodePtr
)
1654 register Node
*nodePtr
; /* Node whose tag summary information
1655 * must be recomputed. */
1657 register Summary
*summaryPtr
, *summaryPtr2
;
1658 register Node
*childPtr
;
1659 register TkTextLine
*linePtr
;
1660 register TkAnnotation
*annotPtr
;
1663 * Zero out all the existing counts for the node, but don't delete
1664 * the existing Summary records (most of them will probably be reused).
1667 for (summaryPtr
= nodePtr
->summaryPtr
; summaryPtr
!= NULL
;
1668 summaryPtr
= summaryPtr
->nextPtr
) {
1669 summaryPtr
->toggleCount
= 0;
1671 nodePtr
->numChildren
= 0;
1672 nodePtr
->numLines
= 0;
1675 * Scan through the children, adding the childrens' tag counts into
1676 * the node's tag counts and adding new Summarys to the node if
1680 if (nodePtr
->level
== 0) {
1681 for (linePtr
= nodePtr
->children
.linePtr
; linePtr
!= NULL
;
1682 linePtr
= linePtr
->nextPtr
) {
1683 nodePtr
->numChildren
++;
1684 nodePtr
->numLines
++;
1685 linePtr
->parentPtr
= nodePtr
;
1686 for (annotPtr
= linePtr
->annotPtr
; annotPtr
!= NULL
;
1687 annotPtr
= annotPtr
->nextPtr
) {
1688 if (annotPtr
->type
!= TK_ANNOT_TOGGLE
) {
1691 for (summaryPtr
= nodePtr
->summaryPtr
; ;
1692 summaryPtr
= summaryPtr
->nextPtr
) {
1693 if (summaryPtr
== NULL
) {
1694 summaryPtr
= (Summary
*) ckalloc(sizeof(Summary
));
1695 summaryPtr
->tagPtr
= annotPtr
->info
.tagPtr
;
1696 summaryPtr
->toggleCount
= 1;
1697 summaryPtr
->nextPtr
= nodePtr
->summaryPtr
;
1698 nodePtr
->summaryPtr
= summaryPtr
;
1701 if (summaryPtr
->tagPtr
== annotPtr
->info
.tagPtr
) {
1702 summaryPtr
->toggleCount
++;
1709 for (childPtr
= nodePtr
->children
.nodePtr
; childPtr
!= NULL
;
1710 childPtr
= childPtr
->nextPtr
) {
1711 nodePtr
->numChildren
++;
1712 nodePtr
->numLines
+= childPtr
->numLines
;
1713 childPtr
->parentPtr
= nodePtr
;
1714 for (summaryPtr2
= childPtr
->summaryPtr
; summaryPtr2
!= NULL
;
1715 summaryPtr2
= summaryPtr2
->nextPtr
) {
1716 for (summaryPtr
= nodePtr
->summaryPtr
; ;
1717 summaryPtr
= summaryPtr
->nextPtr
) {
1718 if (summaryPtr
== NULL
) {
1719 summaryPtr
= (Summary
*) ckalloc(sizeof(Summary
));
1720 summaryPtr
->tagPtr
= summaryPtr2
->tagPtr
;
1721 summaryPtr
->toggleCount
= summaryPtr2
->toggleCount
;
1722 summaryPtr
->nextPtr
= nodePtr
->summaryPtr
;
1723 nodePtr
->summaryPtr
= summaryPtr
;
1726 if (summaryPtr
->tagPtr
== summaryPtr2
->tagPtr
) {
1727 summaryPtr
->toggleCount
+= summaryPtr2
->toggleCount
;
1736 * Scan through the node's tag records again and delete any Summary
1737 * records that still have a zero count.
1741 for (summaryPtr
= nodePtr
->summaryPtr
; summaryPtr
!= NULL
; ) {
1742 if (summaryPtr
->toggleCount
> 0) {
1743 summaryPtr2
= summaryPtr
;
1744 summaryPtr
= summaryPtr
->nextPtr
;
1747 if (summaryPtr2
!= NULL
) {
1748 summaryPtr2
->nextPtr
= summaryPtr
->nextPtr
;
1749 ckfree((char *) summaryPtr
);
1750 summaryPtr
= summaryPtr2
->nextPtr
;
1752 nodePtr
->summaryPtr
= summaryPtr
->nextPtr
;
1753 ckfree((char *) summaryPtr
);
1754 summaryPtr
= nodePtr
->summaryPtr
;
1760 *----------------------------------------------------------------------
1762 * AddToggleToLine --
1764 * Insert a tag transition at a particular point in a particular
1771 * LinePtr and all its ancestors in the B-tree stucture are modified
1772 * to indicate the presence of a transition (either on or off) on
1773 * tag at the given place in the given line.
1775 *----------------------------------------------------------------------
1779 AddToggleToLine(linePtr
, index
, tagPtr
)
1780 TkTextLine
*linePtr
; /* Line within which to add
1782 int index
; /* Character before which to
1783 * add transition. */
1784 TkTextTag
*tagPtr
; /* Information about tag. */
1786 register TkAnnotation
*annotPtr
, *prevPtr
;
1790 * Find the position where the toggle should be inserted into
1791 * the array (just after prevPtr), and see if there is already
1792 * a toggle at exactly the point where we're going to insert a
1793 * new toggle. If so then the two toggles cancel; just delete
1794 * the existing toggle.
1797 for (prevPtr
= NULL
, annotPtr
= linePtr
->annotPtr
; annotPtr
!= NULL
;
1798 prevPtr
= annotPtr
, annotPtr
= annotPtr
->nextPtr
) {
1799 if (annotPtr
->ch
> index
) {
1802 if ((annotPtr
->type
== TK_ANNOT_TOGGLE
)
1803 && (annotPtr
->ch
== index
)
1804 && (annotPtr
->info
.tagPtr
== tagPtr
)) {
1805 if (prevPtr
== NULL
) {
1806 linePtr
->annotPtr
= annotPtr
->nextPtr
;
1808 prevPtr
->nextPtr
= annotPtr
->nextPtr
;
1810 ckfree((char *) annotPtr
);
1817 * Create a new toggle and insert it into the list.
1820 annotPtr
= (TkAnnotation
*) ckalloc(sizeof(TkAnnotation
));
1821 annotPtr
->type
= TK_ANNOT_TOGGLE
;
1822 annotPtr
->linePtr
= linePtr
;
1823 annotPtr
->ch
= index
;
1824 annotPtr
->info
.tagPtr
= tagPtr
;
1825 if (prevPtr
== NULL
) {
1826 annotPtr
->nextPtr
= linePtr
->annotPtr
;
1827 linePtr
->annotPtr
= annotPtr
;
1829 annotPtr
->nextPtr
= prevPtr
->nextPtr
;
1830 prevPtr
->nextPtr
= annotPtr
;
1834 * Update all the nodes above this line to reflect the change in
1839 ChangeNodeToggleCount(linePtr
->parentPtr
, tagPtr
, delta
);
1843 *----------------------------------------------------------------------
1845 * ChangeNodeToggleCount --
1847 * This procedure increments or decrements the toggle count for
1848 * a particular tag in a particular node and all its ancestors.
1854 * The toggle count for tag is adjusted up or down by "delta" in
1857 *----------------------------------------------------------------------
1861 ChangeNodeToggleCount(nodePtr
, tagPtr
, delta
)
1862 register Node
*nodePtr
; /* Node whose toggle count for a tag
1863 * must be changed. */
1864 TkTextTag
*tagPtr
; /* Information about tag. */
1865 int delta
; /* Amount to add to current toggle
1866 * count for tag (may be negative). */
1868 register Summary
*summaryPtr
, *prevPtr
;
1871 * Iterate over the node and all of its ancestors.
1874 for ( ; nodePtr
!= NULL
; nodePtr
= nodePtr
->parentPtr
) {
1876 * See if there's already an entry for this tag for this node. If so,
1877 * perhaps all we have to do is adjust its count.
1880 for (prevPtr
= NULL
, summaryPtr
= nodePtr
->summaryPtr
;
1882 prevPtr
= summaryPtr
, summaryPtr
= summaryPtr
->nextPtr
) {
1883 if (summaryPtr
->tagPtr
!= tagPtr
) {
1886 summaryPtr
->toggleCount
+= delta
;
1887 if (summaryPtr
->toggleCount
> 0) {
1890 if (summaryPtr
->toggleCount
< 0) {
1891 panic("ChangeNodeToggleCount: negative toggle count");
1895 * Zero count; must remove this tag from the list.
1898 if (prevPtr
== NULL
) {
1899 nodePtr
->summaryPtr
= summaryPtr
->nextPtr
;
1901 prevPtr
->nextPtr
= summaryPtr
->nextPtr
;
1903 ckfree((char *) summaryPtr
);
1908 * This tag isn't in the list. Add a new entry to the list.
1912 panic("ChangeNodeToggleCount: negative delta, no tag entry");
1914 summaryPtr
= (Summary
*) ckalloc(sizeof(Summary
));
1915 summaryPtr
->tagPtr
= tagPtr
;
1916 summaryPtr
->toggleCount
= delta
;
1917 summaryPtr
->nextPtr
= nodePtr
->summaryPtr
;
1918 nodePtr
->summaryPtr
= summaryPtr
;
1926 *----------------------------------------------------------------------
1928 * TkBTreeCharTagged --
1930 * Determine whether a particular character has a particular tag.
1933 * The return value is 1 if the given tag is in effect at the
1934 * character given by linePtr and ch, and 0 otherwise.
1939 *----------------------------------------------------------------------
1943 TkBTreeCharTagged(linePtr
, ch
, tagPtr
)
1944 TkTextLine
*linePtr
; /* Line containing character of
1946 int ch
; /* Index of character in linePtr. */
1947 TkTextTag
*tagPtr
; /* Tag of interest. */
1949 register Node
*nodePtr
;
1950 register TkTextLine
*siblingLinePtr
;
1954 * Count the number of toggles for the tag at the line level (i.e.
1955 * in all the sibling lines that precede this one, plus in this line
1956 * up to the character of interest.
1960 for (siblingLinePtr
= linePtr
->parentPtr
->children
.linePtr
; ;
1961 siblingLinePtr
= siblingLinePtr
->nextPtr
) {
1962 register TkAnnotation
*annotPtr
;
1964 for (annotPtr
= siblingLinePtr
->annotPtr
;
1965 (annotPtr
!= NULL
) && ((siblingLinePtr
!= linePtr
)
1966 || (annotPtr
->ch
<= ch
));
1967 annotPtr
= annotPtr
->nextPtr
) {
1968 if ((annotPtr
->type
== TK_ANNOT_TOGGLE
)
1969 && (annotPtr
->info
.tagPtr
== tagPtr
)) {
1973 if (siblingLinePtr
== linePtr
) {
1979 * For each node in the ancestry of this line, count the number of
1980 * toggles of the given tag in siblings that precede that node.
1983 for (nodePtr
= linePtr
->parentPtr
; nodePtr
->parentPtr
!= NULL
;
1984 nodePtr
= nodePtr
->parentPtr
) {
1985 register Node
*siblingPtr
;
1986 register Summary
*summaryPtr
;
1988 for (siblingPtr
= nodePtr
->parentPtr
->children
.nodePtr
;
1989 siblingPtr
!= nodePtr
; siblingPtr
= siblingPtr
->nextPtr
) {
1990 for (summaryPtr
= siblingPtr
->summaryPtr
; summaryPtr
!= NULL
;
1991 summaryPtr
= summaryPtr
->nextPtr
) {
1992 if (summaryPtr
->tagPtr
== tagPtr
) {
1993 toggles
+= summaryPtr
->toggleCount
;
2000 * An odd number of toggles means that the tag is present at the
2008 *----------------------------------------------------------------------
2012 * Return information about all of the tags that are associated
2013 * with a particular character in a B-tree of text.
2016 * The return value is a malloc-ed array containing pointers to
2017 * information for each of the tags that is associated with
2018 * the character at the position given by linePtr and ch. The
2019 * word at *numTagsPtr is filled in with the number of pointers
2020 * in the array. It is up to the caller to free the array by
2021 * passing it to free. If there are no tags at the given character
2022 * then a NULL pointer is returned and *numTagsPtr will be set to 0.
2027 *----------------------------------------------------------------------
2032 TkBTreeGetTags(tree
, linePtr
, ch
, numTagsPtr
)
2033 TkTextBTree tree
; /* Tree to check. */
2034 TkTextLine
*linePtr
; /* Line containing character of interest. */
2035 int ch
; /* Index within linePtr of character for
2036 * which tag information is wanted. */
2037 int *numTagsPtr
; /* Store number of tags found at this
2040 register Node
*nodePtr
;
2041 register TkTextLine
*siblingLinePtr
;
2044 #define NUM_TAG_INFOS 10
2046 tagInfo
.numTags
= 0;
2047 tagInfo
.arraySize
= NUM_TAG_INFOS
;
2048 tagInfo
.tagPtrs
= (TkTextTag
**) ckalloc((unsigned)
2049 NUM_TAG_INFOS
*sizeof(TkTextTag
*));
2050 tagInfo
.counts
= (int *) ckalloc((unsigned)
2051 NUM_TAG_INFOS
*sizeof(int));
2054 * Record tag toggles at the line level (i.e. in all the sibling
2055 * lines that precede this one, plus in this line up to the character
2059 for (siblingLinePtr
= linePtr
->parentPtr
->children
.linePtr
; ;
2060 siblingLinePtr
= siblingLinePtr
->nextPtr
) {
2061 register TkAnnotation
*annotPtr
;
2063 for (annotPtr
= siblingLinePtr
->annotPtr
;
2064 (annotPtr
!= NULL
) && ((siblingLinePtr
!= linePtr
)
2065 || (annotPtr
->ch
<= ch
));
2066 annotPtr
= annotPtr
->nextPtr
) {
2067 if (annotPtr
->type
== TK_ANNOT_TOGGLE
) {
2068 IncCount(annotPtr
->info
.tagPtr
, 1, &tagInfo
);
2071 if (siblingLinePtr
== linePtr
) {
2077 * For each node in the ancestry of this line, record tag toggles
2078 * for all siblings that precede that node.
2081 for (nodePtr
= linePtr
->parentPtr
; nodePtr
->parentPtr
!= NULL
;
2082 nodePtr
= nodePtr
->parentPtr
) {
2083 register Node
*siblingPtr
;
2084 register Summary
*summaryPtr
;
2086 for (siblingPtr
= nodePtr
->parentPtr
->children
.nodePtr
;
2087 siblingPtr
!= nodePtr
; siblingPtr
= siblingPtr
->nextPtr
) {
2088 for (summaryPtr
= siblingPtr
->summaryPtr
; summaryPtr
!= NULL
;
2089 summaryPtr
= summaryPtr
->nextPtr
) {
2090 IncCount(summaryPtr
->tagPtr
, summaryPtr
->toggleCount
, &tagInfo
);
2096 * Go through the tag information and squash out all of the tags
2097 * that have even toggle counts (these tags exist before the point
2098 * of interest, but not at the desired character itself).
2101 for (src
= 0, dst
= 0; src
< tagInfo
.numTags
; src
++) {
2102 if (tagInfo
.counts
[src
] & 1) {
2103 tagInfo
.tagPtrs
[dst
] = tagInfo
.tagPtrs
[src
];
2108 ckfree((char *) tagInfo
.counts
);
2110 ckfree((char *) tagInfo
.tagPtrs
);
2113 return tagInfo
.tagPtrs
;
2117 *----------------------------------------------------------------------
2121 * This is a utility procedure used by TkBTreeGetTags. It
2122 * increments the count for a particular tag, adding a new
2123 * entry for that tag if there wasn't one previously.
2129 * The information at *tagInfoPtr may be modified, and the arrays
2130 * may be reallocated to make them larger.
2132 *----------------------------------------------------------------------
2136 IncCount(tagPtr
, inc
, tagInfoPtr
)
2137 TkTextTag
*tagPtr
; /* Handle for tag. */
2138 int inc
; /* Amount by which to increment tag count. */
2139 TagInfo
*tagInfoPtr
; /* Holds cumulative information about tags;
2140 * increment count here. */
2142 register TkTextTag
**tagPtrPtr
;
2145 for (tagPtrPtr
= tagInfoPtr
->tagPtrs
, count
= tagInfoPtr
->numTags
;
2146 count
> 0; tagPtrPtr
++, count
--) {
2147 if (*tagPtrPtr
== tagPtr
) {
2148 tagInfoPtr
->counts
[tagInfoPtr
->numTags
-count
] += inc
;
2154 * There isn't currently an entry for this tag, so we have to
2155 * make a new one. If the arrays are full, then enlarge the
2159 if (tagInfoPtr
->numTags
== tagInfoPtr
->arraySize
) {
2160 TkTextTag
**newTags
;
2161 int *newCounts
, newSize
;
2163 newSize
= 2*tagInfoPtr
->arraySize
;
2164 newTags
= (TkTextTag
**) ckalloc((unsigned)
2165 (newSize
*sizeof(TkTextTag
*)));
2166 memcpy((VOID
*) newTags
, (VOID
*) tagInfoPtr
->tagPtrs
,
2167 tagInfoPtr
->arraySize
* sizeof(TkTextTag
*));
2168 ckfree((char *) tagInfoPtr
->tagPtrs
);
2169 tagInfoPtr
->tagPtrs
= newTags
;
2170 newCounts
= (int *) ckalloc((unsigned) (newSize
*sizeof(int)));
2171 memcpy((VOID
*) newCounts
, (VOID
*) tagInfoPtr
->counts
,
2172 tagInfoPtr
->arraySize
* sizeof(int));
2173 ckfree((char *) tagInfoPtr
->counts
);
2174 tagInfoPtr
->counts
= newCounts
;
2175 tagInfoPtr
->arraySize
= newSize
;
2178 tagInfoPtr
->tagPtrs
[tagInfoPtr
->numTags
] = tagPtr
;
2179 tagInfoPtr
->counts
[tagInfoPtr
->numTags
] = inc
;
2180 tagInfoPtr
->numTags
++;
2184 *----------------------------------------------------------------------
2186 * CheckNodeConsistency --
2188 * This procedure is called as part of consistency checking for
2189 * B-trees: it checks several aspects of a node and also runs
2190 * checks recursively on the node's children.
2196 * If anything suspicious is found in the tree structure, the
2199 *----------------------------------------------------------------------
2203 CheckNodeConsistency(nodePtr
)
2204 register Node
*nodePtr
; /* Node whose subtree should be
2207 register Node
*childNodePtr
;
2208 register Summary
*summaryPtr
, *summaryPtr2
;
2209 register TkAnnotation
*annotPtr
;
2210 register TkTextLine
*linePtr
;
2212 int numChildren
, numLines
, toggleCount
, minChildren
, index
, numBytes
;
2214 if (nodePtr
->parentPtr
!= NULL
) {
2215 minChildren
= MIN_CHILDREN
;
2216 } else if (nodePtr
->level
> 0) {
2221 if ((nodePtr
->numChildren
< minChildren
)
2222 || (nodePtr
->numChildren
> MAX_CHILDREN
)) {
2223 panic("CheckNodeConsistency found bad child count (%d)",
2224 nodePtr
->numChildren
);
2229 if (nodePtr
->level
== 0) {
2230 for (linePtr
= nodePtr
->children
.linePtr
; linePtr
!= NULL
;
2231 linePtr
= linePtr
->nextPtr
) {
2232 if (linePtr
->parentPtr
!= nodePtr
) {
2233 panic("CheckNodeConsistency found line that %s",
2234 "didn't point to parent");
2236 for (p
= linePtr
->bytes
, numBytes
= 0; *p
!= 0; p
++, numBytes
++) {
2237 if ((*p
== '\n') && (numBytes
!= linePtr
->numBytes
-1)) {
2238 panic("CheckNodeConsistency found line with extra newline");
2241 if (numBytes
!= linePtr
->numBytes
) {
2242 panic("CheckNodeConsistency found line with bad numBytes");
2244 if (linePtr
->bytes
[numBytes
-1] != '\n') {
2245 panic("CheckNodeConsistency found line with no newline");
2248 for (annotPtr
= linePtr
->annotPtr
; annotPtr
!= NULL
;
2249 annotPtr
= annotPtr
->nextPtr
) {
2250 if (annotPtr
->ch
< index
) {
2251 panic("CheckNodeConsistency found %s (%d %d)",
2252 "out-of-order tag indices", index
,
2255 index
= annotPtr
->ch
;
2256 if (annotPtr
->type
== TK_ANNOT_TOGGLE
) {
2257 for (summaryPtr
= nodePtr
->summaryPtr
; ;
2258 summaryPtr
= summaryPtr
->nextPtr
) {
2259 if (summaryPtr
== NULL
) {
2260 panic("CheckNodeConsistency found line %s",
2261 "tag with no node tag: %s",
2262 summaryPtr
->tagPtr
->name
);
2264 if (summaryPtr
->tagPtr
== annotPtr
->info
.tagPtr
) {
2274 for (childNodePtr
= nodePtr
->children
.nodePtr
; childNodePtr
!= NULL
;
2275 childNodePtr
= childNodePtr
->nextPtr
) {
2276 CheckNodeConsistency(childNodePtr
);
2277 for (summaryPtr
= childNodePtr
->summaryPtr
; summaryPtr
!= NULL
;
2278 summaryPtr
= summaryPtr
->nextPtr
) {
2279 for (summaryPtr2
= nodePtr
->summaryPtr
; ;
2280 summaryPtr2
= summaryPtr2
->nextPtr
) {
2281 if (summaryPtr2
== NULL
) {
2282 panic("CheckNodeConsistency found %s (%s)",
2283 "node tag with no parent tag",
2284 summaryPtr
->tagPtr
->name
);
2286 if (summaryPtr
->tagPtr
== summaryPtr2
->tagPtr
) {
2292 numLines
+= childNodePtr
->numLines
;
2293 if (childNodePtr
->parentPtr
!= nodePtr
) {
2294 panic("CheckNodeConsistency found node that %s",
2295 "didn't point to parent");
2297 if (childNodePtr
->level
!= (nodePtr
->level
-1)) {
2298 panic("CheckNodeConsistency found level mismatch (%d %d)",
2299 nodePtr
->level
, childNodePtr
->level
);
2303 if (numChildren
!= nodePtr
->numChildren
) {
2304 panic("CheckNodeConsistency found mismatch in numChildren (%d %d)",
2305 numChildren
, nodePtr
->numChildren
);
2307 if (numLines
!= nodePtr
->numLines
) {
2308 panic("CheckNodeConsistency found mismatch in numLines (%d %d)",
2309 numLines
, nodePtr
->numLines
);
2312 for (summaryPtr
= nodePtr
->summaryPtr
; summaryPtr
!= NULL
;
2313 summaryPtr
= summaryPtr
->nextPtr
) {
2315 if (nodePtr
->level
== 0) {
2316 for (linePtr
= nodePtr
->children
.linePtr
; linePtr
!= NULL
;
2317 linePtr
= linePtr
->nextPtr
) {
2318 for (annotPtr
= linePtr
->annotPtr
; annotPtr
!= NULL
;
2319 annotPtr
= annotPtr
->nextPtr
) {
2320 if (annotPtr
->info
.tagPtr
== summaryPtr
->tagPtr
) {
2326 for (childNodePtr
= nodePtr
->children
.nodePtr
;
2327 childNodePtr
!= NULL
;
2328 childNodePtr
= childNodePtr
->nextPtr
) {
2329 for (summaryPtr2
= childNodePtr
->summaryPtr
;
2330 summaryPtr2
!= NULL
;
2331 summaryPtr2
= summaryPtr2
->nextPtr
) {
2332 if (summaryPtr2
->tagPtr
== summaryPtr
->tagPtr
) {
2333 toggleCount
+= summaryPtr2
->toggleCount
;
2338 if (toggleCount
!= summaryPtr
->toggleCount
) {
2339 panic("CheckNodeConsistency found mismatch in toggleCount (%d %d)",
2340 toggleCount
, summaryPtr
->toggleCount
);
2342 for (summaryPtr2
= summaryPtr
->nextPtr
; summaryPtr2
!= NULL
;
2343 summaryPtr2
= summaryPtr2
->nextPtr
) {
2344 if (summaryPtr2
->tagPtr
== summaryPtr
->tagPtr
) {
2345 panic("CheckNodeConsistency found duplicated node tag: %s",
2346 summaryPtr
->tagPtr
->name
);
2353 *----------------------------------------------------------------------
2355 * TkBTreeNumLines --
2357 * This procedure returns a count of the number of lines of
2358 * text present in a given B-tree.
2361 * The return value is a count of the number of lines in tree.
2366 *----------------------------------------------------------------------
2370 TkBTreeNumLines(tree
)
2371 TkTextBTree tree
; /* Information about tree. */
2373 BTree
*treePtr
= (BTree
*) tree
;
2374 return treePtr
->rootPtr
->numLines
;