|
|
1.1 root 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:
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()
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:
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(tree)
197: TkTextBTree tree; /* Pointer to tree to delete. */
198: {
199: BTree *treePtr = (BTree *) tree;
200:
201: DestroyNode(treePtr->rootPtr);
202: ckfree((char *) treePtr);
203: }
204:
205: /*
206: *----------------------------------------------------------------------
207: *
208: * DestroyNode --
209: *
210: * This is a recursive utility procedure used during the deletion
211: * of a B-tree.
212: *
213: * Results:
214: * None.
215: *
216: * Side effects:
217: * All the storage for nodePtr and its descendants is freed.
218: *
219: *----------------------------------------------------------------------
220: */
221:
222: static void
223: DestroyNode(nodePtr)
224: 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:
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(summaryPtr)
274: register Summary *summaryPtr; /* First in list of node's tag
275: * summaries. */
276: {
277: register Summary *nextPtr;
278: while (summaryPtr != NULL) {
279: nextPtr = summaryPtr->nextPtr;
280: ckfree((char *) summaryPtr);
281: summaryPtr = nextPtr;
282: }
283: }
284:
285: /*
286: *----------------------------------------------------------------------
287: *
288: * TkBTreeInsertChars --
289: *
290: * Insert characters at a given position in a B-tree.
291: *
292: * Results:
293: * None.
294: *
295: * Side effects:
296: * NumBytes characters are added to the B-tree at the given
297: * character position. This can cause the structure of the
298: * B-tree to change.
299: *
300: *----------------------------------------------------------------------
301: */
302:
303: void
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
307: * insert. */
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-
313: * terminated). */
314: {
315: BTree *treePtr = (BTree *) tree;
316: register Node *nodePtr;
317: register TkAnnotation *annotPtr;
318: TkTextLine *prevPtr;
319: int newChunkLength; /* # chars in current line being
320: * inserted. */
321: register char *eol; /* Pointer to last character in
322: * current line being inserted. */
323: int changeToLineCount; /* Counts change to total number of
324: * lines in file. */
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;
330:
331: /*
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).
337: */
338:
339: nodePtr = linePtr->parentPtr;
340: prevPtr = nodePtr->children.linePtr;
341: if (prevPtr == linePtr) {
342: prevPtr = NULL;
343: nodePtr->children.linePtr = linePtr->nextPtr;
344: } else {
345: for ( ; prevPtr->nextPtr != linePtr; prevPtr = prevPtr->nextPtr) {
346: /* Empty loop body. */
347: }
348: prevPtr->nextPtr = linePtr->nextPtr;
349: }
350:
351: /*
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
354: * point.
355: */
356:
357: afterPtr = NULL;
358: if ((linePtr->annotPtr != NULL) && (linePtr->annotPtr->ch >= ch)) {
359: afterPtr = linePtr->annotPtr;
360: linePtr->annotPtr = NULL;
361: } else {
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;
368: break;
369: }
370: }
371: }
372:
373: /*
374: * Chop the string up into lines and insert each line individually.
375: */
376:
377: changeToLineCount = -1;
378: prefixLength = ch;
379: while (1) {
380: for (newChunkLength = 0, eol = string; *eol != 0; eol++) {
381: newChunkLength++;
382: if (*eol == '\n') {
383: break;
384: }
385: }
386:
387: /*
388: * Create a new line consisting of up to three parts: a prefix
389: * from linePtr, some material from string, and a suffix from
390: * linePtr.
391: */
392:
393: if ((newChunkLength == 0) || (*eol != '\n')) {
394: suffixLength = linePtr->numBytes - ch;
395: } else {
396: suffixLength = 0;
397: }
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;
404: } else {
405: newPtr->nextPtr = prevPtr->nextPtr;
406: prevPtr->nextPtr = newPtr;
407: }
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;
413: }
414: linePtr->annotPtr = NULL;
415: } else {
416: newPtr->annotPtr = NULL;
417: }
418: newPtr->numBytes = totalLength;
419: if (prefixLength != 0) {
420: memcpy((VOID *) newPtr->bytes, (VOID *) linePtr->bytes,
421: prefixLength);
422: }
423: if (newChunkLength != 0) {
424: memcpy((VOID *) (newPtr->bytes + prefixLength), (VOID *) string,
425: newChunkLength);
426: }
427: if (suffixLength != 0) {
428: memcpy((VOID *) (newPtr->bytes + prefixLength + newChunkLength),
429: (VOID *) (linePtr->bytes + ch), suffixLength);
430: }
431: newPtr->bytes[totalLength] = 0;
432: changeToLineCount += 1;
433:
434: /*
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.
440: */
441:
442: if (suffixLength != 0) {
443: if (newPtr->annotPtr == NULL) {
444: newPtr->annotPtr = afterPtr;
445: } else {
446: for (annotPtr = newPtr->annotPtr; annotPtr->nextPtr != NULL;
447: annotPtr = annotPtr->nextPtr) {
448: /* Empty loop body. */
449: }
450: annotPtr->nextPtr = afterPtr;
451: }
452: for (annotPtr = afterPtr; annotPtr != NULL;
453: annotPtr = annotPtr->nextPtr) {
454: annotPtr->linePtr = newPtr;
455: annotPtr->ch += prefixLength+newChunkLength-ch;
456: }
457: break;
458: }
459:
460: /*
461: * Advance to insert the next line chunk.
462: */
463:
464: string += newChunkLength;
465: prefixLength = 0;
466: prevPtr = newPtr;
467: }
468:
469: /*
470: * Increment the line counts in all the parent nodes of the insertion
471: * point, then rebalance the tree if necessary.
472: */
473:
474: for ( ; nodePtr != NULL; nodePtr = nodePtr->parentPtr) {
475: nodePtr->numLines += changeToLineCount;
476: }
477: nodePtr = linePtr->parentPtr;
478: nodePtr->numChildren += changeToLineCount;
479: if (nodePtr->numChildren > MAX_CHILDREN) {
480: Rebalance(treePtr, nodePtr);
481: }
482:
483: ckfree((char *) linePtr);
484: if (tkBTreeDebug) {
485: TkBTreeCheck(tree);
486: }
487: }
488:
489: /*
490: *----------------------------------------------------------------------
491: *
492: * TkBTreeDeleteChars --
493: *
494: * Delete a range of characters from a B-tree.
495: *
496: * Results:
497: * None.
498: *
499: * Side effects:
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.
507: *
508: *----------------------------------------------------------------------
509: */
510:
511: void
512: TkBTreeDeleteChars(tree, line1Ptr, ch1, line2Ptr, ch2)
513: TkTextBTree tree; /* B-tree in which to delete. */
514: register TkTextLine *line1Ptr; /* Line containing first character
515: * to delete. */
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. */
522: {
523: BTree *treePtr = (BTree *) tree;
524: TkTextLine *linePtr, *nextPtr, *prevLinePtr;
525: Node *nodePtr, *parentPtr, *nextNodePtr;
526: TkAnnotation *annotPtr, *annotPtr2;
527: int ch;
528: int linesDeleted; /* Counts lines deleted from current
529: * level-0 node. */
530:
531: /*
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
535: * this process.
536: */
537:
538: linePtr = line1Ptr->nextPtr;
539: nodePtr = line1Ptr->parentPtr;
540: if (line1Ptr == line2Ptr) {
541: goto middleLinesDeleted;
542: }
543: while (1) {
544:
545: /*
546: * Delete all relevant lines within the same level-0 node.
547: */
548:
549: linesDeleted = 0;
550: while ((linePtr != line2Ptr) && (linePtr != NULL)) {
551: /*
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.
556: */
557:
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);
565: } else {
566: annotPtr2 = annotPtr->nextPtr;
567: TkBTreeRemoveAnnotation(annotPtr);
568: annotPtr->linePtr = line2Ptr;
569: annotPtr->ch = ch2;
570: TkBTreeAddAnnotation(annotPtr);
571: }
572: }
573: nextPtr = linePtr->nextPtr;
574: ckfree((char *) linePtr);
575: linesDeleted++;
576: linePtr = nextPtr;
577: }
578: if (nodePtr == line1Ptr->parentPtr) {
579: line1Ptr->nextPtr = linePtr;
580: } else {
581: nodePtr->children.linePtr = linePtr;
582: }
583: for (parentPtr = nodePtr; parentPtr != NULL;
584: parentPtr = parentPtr->parentPtr) {
585: parentPtr->numLines -= linesDeleted;
586: }
587: nodePtr->numChildren -= linesDeleted;
588: if (linePtr == line2Ptr) {
589: break;
590: }
591:
592: /*
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
595: * it's empty).
596: */
597:
598: nextNodePtr = nodePtr;
599: while (nextNodePtr->nextPtr == NULL) {
600: nextNodePtr = nextNodePtr->parentPtr;
601: }
602: nextNodePtr = nextNodePtr->nextPtr;
603: while (nextNodePtr->level > 0) {
604: nextNodePtr = nextNodePtr->children.nodePtr;
605: }
606: linePtr = nextNodePtr->children.linePtr;
607:
608: /*
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.
614: */
615:
616: while (nodePtr->numChildren == 0) {
617: parentPtr = nodePtr->parentPtr;
618: if (parentPtr->children.nodePtr == nodePtr) {
619: parentPtr->children.nodePtr = nodePtr->nextPtr;
620: } else {
621: Node *prevPtr;
622:
623: for (prevPtr = parentPtr->children.nodePtr;
624: prevPtr->nextPtr != nodePtr;
625: prevPtr = prevPtr->nextPtr) {
626: }
627: prevPtr->nextPtr = nodePtr->nextPtr;
628: }
629: parentPtr->numChildren--;
630: DeleteSummaries(nodePtr->summaryPtr);
631: ckfree((char *) nodePtr);
632: nodePtr = parentPtr;
633: }
634: nodePtr = nextNodePtr;
635: }
636:
637: /*
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.
641: */
642:
643: middleLinesDeleted:
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;
650: if (ch1 != 0) {
651: memcpy((VOID *) linePtr->bytes, (VOID *) line1Ptr->bytes, ch1);
652: }
653: strcpy(linePtr->bytes + ch1, line2Ptr->bytes + ch2);
654:
655: /*
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.
661: */
662:
663: for (annotPtr = line1Ptr->annotPtr; annotPtr != NULL;
664: annotPtr = line1Ptr->annotPtr) {
665: if (annotPtr->ch <= ch1) {
666: ch = annotPtr->ch;
667: } else {
668: if (line1Ptr == line2Ptr) {
669: break;
670: }
671: ch = ch1;
672: }
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,
677: -1);
678: ckfree((char *) annotPtr);
679: } else {
680: annotPtr->linePtr = linePtr;
681: annotPtr->ch = ch;
682: TkBTreeAddAnnotation(annotPtr);
683: }
684: }
685: for (annotPtr = line2Ptr->annotPtr; annotPtr != NULL;
686: annotPtr = line2Ptr->annotPtr) {
687: if (annotPtr->ch >= ch2) {
688: ch = annotPtr->ch - ch2 + ch1;
689: } else {
690: ch = ch1;
691: }
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,
696: -1);
697: ckfree((char *) annotPtr);
698: } else {
699: annotPtr->linePtr = linePtr;
700: annotPtr->ch = ch;
701: TkBTreeAddAnnotation(annotPtr);
702: }
703: }
704:
705: /*
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.
709: */
710:
711: nodePtr = line1Ptr->parentPtr;
712: if (nodePtr->children.linePtr == line1Ptr) {
713: nodePtr->children.linePtr = linePtr;
714: } else {
715: for (prevLinePtr = nodePtr->children.linePtr;
716: prevLinePtr->nextPtr != line1Ptr;
717: prevLinePtr = prevLinePtr->nextPtr) {
718: /* Empty loop body. */
719: }
720: prevLinePtr->nextPtr = linePtr;
721: }
722: ckfree((char *) line1Ptr);
723: nodePtr = line2Ptr->parentPtr;
724: if (line2Ptr != line1Ptr) {
725: if (nodePtr->children.linePtr == line2Ptr) {
726: nodePtr->children.linePtr = line2Ptr->nextPtr;
727: } else {
728: for (prevLinePtr = nodePtr->children.linePtr;
729: prevLinePtr->nextPtr != line2Ptr;
730: prevLinePtr = prevLinePtr->nextPtr) {
731: /* Empty loop body. */
732: }
733: prevLinePtr->nextPtr = line2Ptr->nextPtr;
734: }
735: ckfree((char *) line2Ptr);
736: for (parentPtr = nodePtr; parentPtr != NULL;
737: parentPtr = parentPtr->parentPtr) {
738: parentPtr->numLines--;
739: }
740: nodePtr->numChildren--;
741: }
742:
743: /*
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
750: * second.
751: */
752:
753: if (nodePtr != linePtr->parentPtr) {
754: Rebalance(treePtr, nodePtr);
755: }
756: Rebalance(treePtr, linePtr->parentPtr);
757: if (tkBTreeDebug) {
758: TkBTreeCheck(tree);
759: }
760: }
761:
762: /*
763: *----------------------------------------------------------------------
764: *
765: * TkBTreeTag --
766: *
767: * Turn a given tag on or off for a given range of characters in
768: * a B-tree of text.
769: *
770: * Results:
771: * None.
772: *
773: * Side effects:
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.
777: *
778: *----------------------------------------------------------------------
779: */
780:
781: void
782: TkBTreeTag(tree, line1, ch1, line2, ch2, tagPtr, add)
783: TkTextBTree tree; /* B-tree in which to add tag
784: * information. */
785: int line1, ch1; /* Position of first character to
786: * tag. */
787: int line2, ch2; /* Position of character just after
788: * last one to tag. */
789: TkTextTag *tagPtr; /* Tag to associate with the range
790: * of characters. */
791: int add; /* One means add tag to the given
792: * range of characters; zero means
793: * remove the tag from the range. */
794: {
795: BTree *treePtr = (BTree *) tree;
796: register TkTextLine *line1Ptr, *line2Ptr;
797: TkTextSearch search;
798: int oldState;
799:
800: /*
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).
806: */
807:
808: if (line1 < 0) {
809: line1 = 0;
810: ch1 = 0;
811: }
812: line1Ptr = TkBTreeFindLine(tree, line1);
813: if (line1Ptr == NULL) {
814: return;
815: }
816: if (ch1 >= line1Ptr->numBytes) {
817: TkTextLine *nextLinePtr;
818:
819: nextLinePtr = TkBTreeNextLine(line1Ptr);
820: if (nextLinePtr == NULL) {
821: return;
822: } else {
823: line1Ptr = nextLinePtr;
824: line1++;
825: ch1 = 0;
826: }
827: }
828: if (line2 < 0) {
829: return;
830: }
831: line2Ptr = TkBTreeFindLine(tree, line2);
832: if (line2Ptr == NULL) {
833: line2Ptr = TkBTreeFindLine(tree, treePtr->rootPtr->numLines-1);
834: ch2 = line2Ptr->numBytes-1;
835: }
836: if (ch2 >= line2Ptr->numBytes) {
837: TkTextLine *nextLinePtr;
838:
839: nextLinePtr = TkBTreeNextLine(line2Ptr);
840: if (nextLinePtr == NULL) {
841: ch2 = line2Ptr->numBytes-1;
842: } else {
843: line2Ptr = nextLinePtr;
844: line2++;
845: ch2 = 0;
846: }
847: }
848:
849: /*
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
852: * a toggle there.
853: */
854:
855: oldState = TkBTreeCharTagged(line1Ptr, ch1, tagPtr);
856: if ((add != 0) ^ oldState) {
857: AddToggleToLine(line1Ptr, ch1, tagPtr);
858: }
859:
860: /*
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.
866: */
867:
868: TkBTreeStartSearch(tree, line1, ch1+1, line2, ch2, tagPtr, &search);
869: while (TkBTreeNextTag(&search)) {
870: if ((search.linePtr == line2Ptr) && (search.ch1 == ch2)) {
871: break;
872: }
873: oldState ^= 1;
874: AddToggleToLine(search.linePtr, search.ch1, tagPtr);
875: }
876: if ((add != 0) ^ oldState) {
877: AddToggleToLine(line2Ptr, ch2, tagPtr);
878: }
879:
880: if (tkBTreeDebug) {
881: TkBTreeCheck(tree);
882: }
883: }
884:
885: /*
886: *----------------------------------------------------------------------
887: *
888: * TkBTreeAddAnnotation --
889: *
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.
892: *
893: * Results:
894: * None.
895: *
896: * Side effects:
897: * AnnotPtr will be linked into its tree. Note: the storage for
898: * annotPtr is assumed to have been malloc'ed by the caller.
899: *
900: *----------------------------------------------------------------------
901: */
902:
903: /* ARGSUSED */
904: void
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. */
911: {
912: register TkAnnotation *annotPtr2, *prevPtr;
913:
914: for (prevPtr = NULL, annotPtr2 = annotPtr->linePtr->annotPtr;
915: annotPtr2 != NULL;
916: prevPtr = annotPtr2, annotPtr2 = annotPtr2->nextPtr) {
917: if (annotPtr2->ch > annotPtr->ch) {
918: break;
919: }
920: }
921: if (prevPtr == NULL) {
922: annotPtr->nextPtr = annotPtr->linePtr->annotPtr;
923: annotPtr->linePtr->annotPtr = annotPtr;
924: } else {
925: annotPtr->nextPtr = prevPtr->nextPtr;
926: prevPtr->nextPtr = annotPtr;
927: }
928: }
929:
930: /*
931: *----------------------------------------------------------------------
932: *
933: * TkBTreeRemoveAnnotation --
934: *
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.
937: *
938: * Results:
939: * None.
940: *
941: * Side effects:
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.
944: *
945: *----------------------------------------------------------------------
946: */
947:
948: /* ARGSUSED */
949: void
950: TkBTreeRemoveAnnotation(annotPtr)
951: TkAnnotation *annotPtr; /* Pointer to annotation, which must
952: * have been linked into tree by a previous
953: * call to TkBTreeAddAnnotation. */
954: {
955: register TkAnnotation *prevPtr;
956:
957: if (annotPtr->linePtr->annotPtr == annotPtr) {
958: annotPtr->linePtr->annotPtr = annotPtr->nextPtr;
959: } else {
960: for (prevPtr = annotPtr->linePtr->annotPtr;
961: /* BUG: fixed by dhopkins, prevPtr was null!
962: prevPtr->nextPtr != annotPtr;
963: */
964: (prevPtr != NULL) && (prevPtr->nextPtr != annotPtr);
965: prevPtr = prevPtr->nextPtr) {
966: /* Empty loop body. */
967: }
968: if (prevPtr != NULL) { /* Bullet proofing by dhopkins */
969: prevPtr->nextPtr = annotPtr->nextPtr;
970: }
971: }
972: }
973:
974: /*
975: *----------------------------------------------------------------------
976: *
977: * TkBTreeFindLine --
978: *
979: * Find a particular line in a B-tree based on its line number.
980: *
981: * Results:
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.
984: *
985: * Side effects:
986: * None.
987: *
988: *----------------------------------------------------------------------
989: */
990:
991: TkTextLine *
992: TkBTreeFindLine(tree, line)
993: TkTextBTree tree; /* B-tree in which to find line. */
994: int line; /* Index of desired line. */
995: {
996: BTree *treePtr = (BTree *) tree;
997: register Node *nodePtr;
998: register TkTextLine *linePtr;
999: int linesLeft;
1000:
1001: nodePtr = treePtr->rootPtr;
1002: linesLeft = line;
1003: if ((line < 0) || (line >= nodePtr->numLines)) {
1004: return NULL;
1005: }
1006:
1007: /*
1008: * Work down through levels of the tree until a node is found at
1009: * level 0.
1010: */
1011:
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");
1018: }
1019: linesLeft -= nodePtr->numLines;
1020: }
1021: }
1022:
1023: /*
1024: * Work through the lines attached to the level-0 node.
1025: */
1026:
1027: for (linePtr = nodePtr->children.linePtr; linesLeft > 0;
1028: linePtr = linePtr->nextPtr) {
1029: if (linePtr == NULL) {
1030: panic("TkBTreeFindLine ran out of lines");
1031: }
1032: linesLeft -= 1;
1033: }
1034: return linePtr;
1035: }
1036:
1037: /*
1038: *----------------------------------------------------------------------
1039: *
1040: * TkBTreeNextLine --
1041: *
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.
1045: *
1046: * Results:
1047: * The return value is a pointer to the line that immediately
1048: * follows linePtr, or NULL if there is no such line.
1049: *
1050: * Side effects:
1051: * None.
1052: *
1053: *----------------------------------------------------------------------
1054: */
1055:
1056: TkTextLine *
1057: TkBTreeNextLine(linePtr)
1058: register TkTextLine *linePtr; /* Pointer to existing line in
1059: * B-tree. */
1060: {
1061: register Node *nodePtr;
1062:
1063: if (linePtr->nextPtr != NULL) {
1064: return linePtr->nextPtr;
1065: }
1066:
1067: /*
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,
1071: */
1072:
1073: for (nodePtr = linePtr->parentPtr; ; nodePtr = nodePtr->parentPtr) {
1074: if (nodePtr->nextPtr != NULL) {
1075: nodePtr = nodePtr->nextPtr;
1076: break;
1077: }
1078: if (nodePtr->parentPtr == NULL) {
1079: return (TkTextLine *) NULL;
1080: }
1081: }
1082: while (nodePtr->level > 0) {
1083: nodePtr = nodePtr->children.nodePtr;
1084: }
1085: return nodePtr->children.linePtr;
1086: }
1087:
1088: /*
1089: *----------------------------------------------------------------------
1090: *
1091: * TkBTreeLineIndex --
1092: *
1093: * Given a pointer to a line in a B-tree, return the numerical
1094: * index of that line.
1095: *
1096: * Results:
1097: * The result is the index of linePtr within the tree, where 0
1098: * corresponds to the first line in the tree.
1099: *
1100: * Side effects:
1101: * None.
1102: *
1103: *----------------------------------------------------------------------
1104: */
1105:
1106: int
1107: TkBTreeLineIndex(linePtr)
1108: TkTextLine *linePtr; /* Pointer to existing line in
1109: * B-tree. */
1110: {
1111: register TkTextLine *linePtr2;
1112: register Node *nodePtr, *parentPtr, *nodePtr2;
1113: int index;
1114:
1115: /*
1116: * First count how many lines precede this one in its level-0
1117: * node.
1118: */
1119:
1120: nodePtr = linePtr->parentPtr;
1121: index = 0;
1122: for (linePtr2 = nodePtr->children.linePtr; linePtr2 != linePtr;
1123: linePtr2 = linePtr2->nextPtr) {
1124: if (linePtr2 == NULL) {
1125: panic("TkBTreeLineIndex couldn't find line");
1126: }
1127: index += 1;
1128: }
1129:
1130: /*
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
1133: * node.
1134: */
1135:
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");
1142: }
1143: index += nodePtr2->numLines;
1144: }
1145: }
1146: return index;
1147: }
1148:
1149: /*
1150: *----------------------------------------------------------------------
1151: *
1152: * TkBTreeStartSearch --
1153: *
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.
1156: *
1157: * Results:
1158: * None.
1159: *
1160: * Side effects:
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.
1165: *
1166: *----------------------------------------------------------------------
1167: */
1168:
1169: void
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. */
1180: {
1181: register TkAnnotation *annotPtr;
1182:
1183: searchPtr->tree = tree;
1184: if (line1 < 0) {
1185: searchPtr->line1 = 0;
1186: searchPtr->ch1 = 0;
1187: } else {
1188: searchPtr->line1 = line1;
1189: searchPtr->ch1 = ch1;
1190: }
1191: searchPtr->line2 = line2;
1192: searchPtr->ch2 = ch2;
1193: searchPtr->tagPtr = tagPtr;
1194: searchPtr->allTags = (tagPtr == NULL);
1195:
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;
1201: } else {
1202: for (annotPtr = searchPtr->linePtr->annotPtr;
1203: (annotPtr != NULL) && (annotPtr->ch < ch1);
1204: annotPtr = annotPtr->nextPtr) {
1205: /* Empty loop body. */
1206: }
1207: searchPtr->annotPtr = annotPtr;
1208: }
1209: }
1210:
1211: /*
1212: *----------------------------------------------------------------------
1213: *
1214: * TkBTreeNextTag --
1215: *
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.
1220: *
1221: * Results:
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.
1225: *
1226: * Side effects:
1227: * Information in *searchPtr is modified to update the state of the
1228: * search and indicate where the next tag toggle is located.
1229: *
1230: *----------------------------------------------------------------------
1231: */
1232:
1233: int
1234: TkBTreeNextTag(searchPtr)
1235: register TkTextSearch *searchPtr; /* Information about search in
1236: * progress; must have been set up by
1237: * call to TkBTreeStartSearch. */
1238: {
1239: register TkAnnotation *annotPtr;
1240: register Node *nodePtr;
1241: register Summary *summaryPtr;
1242:
1243: if (searchPtr->linePtr == NULL) {
1244: return 0;
1245: }
1246:
1247: /*
1248: * The outermost loop iterates over lines that may potentially contain
1249: * a relevant tag transition, starting from the current line and tag.
1250: */
1251:
1252: while (1) {
1253: /*
1254: * See if there are more tags on the current line that are relevant.
1255: */
1256:
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)) {
1264: goto searchOver;
1265: }
1266: searchPtr->tagPtr = annotPtr->info.tagPtr;
1267: searchPtr->ch1 = annotPtr->ch;
1268: searchPtr->annotPtr = annotPtr->nextPtr;
1269: return 1;
1270: }
1271: }
1272:
1273: /*
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
1276: * one of them.
1277: */
1278:
1279: if (searchPtr->line1 >= searchPtr->line2) {
1280: goto searchOver;
1281: }
1282: searchPtr->line1++;
1283: if (searchPtr->linePtr->nextPtr != NULL) {
1284: searchPtr->linePtr = searchPtr->linePtr->nextPtr;
1285: searchPtr->annotPtr = searchPtr->linePtr->annotPtr;
1286: continue;
1287: }
1288:
1289: /*
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.
1294: */
1295:
1296: nodePtr = searchPtr->linePtr->parentPtr;
1297: while (1) {
1298: while (nodePtr->nextPtr == NULL) {
1299: if (nodePtr->parentPtr == NULL) {
1300: goto searchOver;
1301: }
1302: nodePtr = nodePtr->parentPtr;
1303: }
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;
1310: }
1311: }
1312: searchPtr->line1 += nodePtr->numLines;
1313: }
1314:
1315: /*
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.
1319: */
1320:
1321: gotNodeWithTag:
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)) {
1329: goto nextChild;
1330: }
1331: }
1332: searchPtr->line1 += nodePtr->numLines;
1333: if (nodePtr->nextPtr == NULL) {
1334: panic("TkBTreeNextTag found incorrect tag summary info.");
1335: }
1336: }
1337: nextChild:
1338: continue;
1339: }
1340:
1341: /*
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.
1345: */
1346:
1347: searchPtr->linePtr = nodePtr->children.linePtr;
1348: searchPtr->annotPtr = searchPtr->linePtr->annotPtr;
1349: if (searchPtr->line1 > searchPtr->line2) {
1350: goto searchOver;
1351: }
1352: continue;
1353: }
1354:
1355: searchOver:
1356: searchPtr->line1 = searchPtr->line2;
1357: searchPtr->ch1 = searchPtr->ch2;
1358: searchPtr->annotPtr = NULL;
1359: searchPtr->linePtr = NULL;
1360: return 0;
1361: }
1362:
1363: /*
1364: *----------------------------------------------------------------------
1365: *
1366: * TkBTreeCheck --
1367: *
1368: * This procedure runs a set of consistency checks over a B-tree
1369: * and panics if any inconsistencies are found.
1370: *
1371: * Results:
1372: * None.
1373: *
1374: * Side effects:
1375: * If a structural defect is found, the procedure panics with an
1376: * error message.
1377: *
1378: *----------------------------------------------------------------------
1379: */
1380:
1381: void
1382: TkBTreeCheck(tree)
1383: TkTextBTree tree; /* Tree to check. */
1384: {
1385: BTree *treePtr = (BTree *) tree;
1386: register Summary *summaryPtr;
1387:
1388: /*
1389: * Make sure that overall there is an even count of tag transitions
1390: * for the whole text.
1391: */
1392:
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);
1398: }
1399: }
1400:
1401: /*
1402: * Call a recursive procedure to do all of the rest of the checks.
1403: */
1404:
1405: CheckNodeConsistency(treePtr->rootPtr);
1406: }
1407:
1408: /*
1409: *----------------------------------------------------------------------
1410: *
1411: * Rebalance --
1412: *
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.
1416: *
1417: * Results:
1418: * None.
1419: *
1420: * Side effects:
1421: * The internal structure of treePtr may change.
1422: *
1423: *----------------------------------------------------------------------
1424: */
1425:
1426: static void
1427: Rebalance(treePtr, nodePtr)
1428: BTree *treePtr; /* Tree that is being rebalanced. */
1429: register Node *nodePtr; /* Node that may be out of balance. */
1430: {
1431: /*
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
1434: * been processed.
1435: */
1436:
1437: for ( ; nodePtr != NULL; nodePtr = nodePtr->parentPtr) {
1438: register Node *newPtr, *childPtr;
1439: register TkTextLine *linePtr;
1440: int i;
1441:
1442: /*
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.
1447: */
1448:
1449: if (nodePtr->numChildren > MAX_CHILDREN) {
1450: while (1) {
1451: /*
1452: * If the node being split is the root node, then make a
1453: * new root node above it first.
1454: */
1455:
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;
1467: }
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. */
1480: }
1481: newPtr->children.linePtr = linePtr->nextPtr;
1482: linePtr->nextPtr = NULL;
1483: } else {
1484: for (i = MIN_CHILDREN-1,
1485: childPtr = nodePtr->children.nodePtr;
1486: i > 0; i--, childPtr = childPtr->nextPtr) {
1487: /* Empty loop body. */
1488: }
1489: newPtr->children.nodePtr = childPtr->nextPtr;
1490: childPtr->nextPtr = NULL;
1491: }
1492: RecomputeNodeCounts(nodePtr);
1493: nodePtr->parentPtr->numChildren++;
1494: nodePtr = newPtr;
1495: if (nodePtr->numChildren <= MAX_CHILDREN) {
1496: RecomputeNodeCounts(nodePtr);
1497: break;
1498: }
1499: }
1500: }
1501:
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;
1507:
1508: /*
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.
1514: */
1515:
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);
1522: }
1523: return;
1524: }
1525:
1526: /*
1527: * Not the root. Make sure that there are siblings to
1528: * balance with.
1529: */
1530:
1531: if (nodePtr->parentPtr->numChildren < 2) {
1532: Rebalance(treePtr, nodePtr->parentPtr);
1533: continue;
1534: }
1535:
1536: /*
1537: * Find a sibling to borrow from, and arrange for nodePtr to
1538: * be the earlier of the pair.
1539: */
1540:
1541: if (nodePtr->nextPtr == NULL) {
1542: for (otherPtr = nodePtr->parentPtr->children.nodePtr;
1543: otherPtr->nextPtr != nodePtr;
1544: otherPtr = otherPtr->nextPtr) {
1545: /* Empty loop body. */
1546: }
1547: nodePtr = otherPtr;
1548: }
1549: otherPtr = nodePtr->nextPtr;
1550:
1551: /*
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.
1557: */
1558:
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;
1565:
1566: for (linePtr = nodePtr->children.linePtr, i = 1;
1567: linePtr->nextPtr != NULL;
1568: linePtr = linePtr->nextPtr, i++) {
1569: if (i == firstChildren) {
1570: halfwayLinePtr = linePtr;
1571: }
1572: }
1573: linePtr->nextPtr = otherPtr->children.linePtr;
1574: while (i <= firstChildren) {
1575: halfwayLinePtr = linePtr;
1576: linePtr = linePtr->nextPtr;
1577: i++;
1578: }
1579: } else {
1580: register Node *childPtr;
1581:
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;
1588: }
1589: }
1590: }
1591: childPtr->nextPtr = otherPtr->children.nodePtr;
1592: while (i <= firstChildren) {
1593: halfwayNodePtr = childPtr;
1594: childPtr = childPtr->nextPtr;
1595: i++;
1596: }
1597: }
1598:
1599: /*
1600: * If the two siblings can simply be merged together, do it.
1601: */
1602:
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);
1609: continue;
1610: }
1611:
1612: /*
1613: * The siblings can't be merged, so just divide their
1614: * children evenly between them.
1615: */
1616:
1617: if (nodePtr->level == 0) {
1618: otherPtr->children.linePtr = halfwayLinePtr->nextPtr;
1619: halfwayLinePtr->nextPtr = NULL;
1620: } else {
1621: otherPtr->children.nodePtr = halfwayNodePtr->nextPtr;
1622: halfwayNodePtr->nextPtr = NULL;
1623: }
1624: RecomputeNodeCounts(nodePtr);
1625: RecomputeNodeCounts(otherPtr);
1626: }
1627: }
1628: }
1629:
1630: /*
1631: *----------------------------------------------------------------------
1632: *
1633: * RecomputeNodeCounts --
1634: *
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.
1639: *
1640: * Results:
1641: * None.
1642: *
1643: * Side effects:
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
1647: * to nodePtr.
1648: *
1649: *----------------------------------------------------------------------
1650: */
1651:
1652: static void
1653: RecomputeNodeCounts(nodePtr)
1654: register Node *nodePtr; /* Node whose tag summary information
1655: * must be recomputed. */
1656: {
1657: register Summary *summaryPtr, *summaryPtr2;
1658: register Node *childPtr;
1659: register TkTextLine *linePtr;
1660: register TkAnnotation *annotPtr;
1661:
1662: /*
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).
1665: */
1666:
1667: for (summaryPtr = nodePtr->summaryPtr; summaryPtr != NULL;
1668: summaryPtr = summaryPtr->nextPtr) {
1669: summaryPtr->toggleCount = 0;
1670: }
1671: nodePtr->numChildren = 0;
1672: nodePtr->numLines = 0;
1673:
1674: /*
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
1677: * necessary.
1678: */
1679:
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) {
1689: continue;
1690: }
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;
1699: break;
1700: }
1701: if (summaryPtr->tagPtr == annotPtr->info.tagPtr) {
1702: summaryPtr->toggleCount++;
1703: break;
1704: }
1705: }
1706: }
1707: }
1708: } else {
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;
1724: break;
1725: }
1726: if (summaryPtr->tagPtr == summaryPtr2->tagPtr) {
1727: summaryPtr->toggleCount += summaryPtr2->toggleCount;
1728: break;
1729: }
1730: }
1731: }
1732: }
1733: }
1734:
1735: /*
1736: * Scan through the node's tag records again and delete any Summary
1737: * records that still have a zero count.
1738: */
1739:
1740: summaryPtr2 = NULL;
1741: for (summaryPtr = nodePtr->summaryPtr; summaryPtr != NULL; ) {
1742: if (summaryPtr->toggleCount > 0) {
1743: summaryPtr2 = summaryPtr;
1744: summaryPtr = summaryPtr->nextPtr;
1745: continue;
1746: }
1747: if (summaryPtr2 != NULL) {
1748: summaryPtr2->nextPtr = summaryPtr->nextPtr;
1749: ckfree((char *) summaryPtr);
1750: summaryPtr = summaryPtr2->nextPtr;
1751: } else {
1752: nodePtr->summaryPtr = summaryPtr->nextPtr;
1753: ckfree((char *) summaryPtr);
1754: summaryPtr = nodePtr->summaryPtr;
1755: }
1756: }
1757: }
1758:
1759: /*
1760: *----------------------------------------------------------------------
1761: *
1762: * AddToggleToLine --
1763: *
1764: * Insert a tag transition at a particular point in a particular
1765: * line.
1766: *
1767: * Results:
1768: * None.
1769: *
1770: * Side effects:
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.
1774: *
1775: *----------------------------------------------------------------------
1776: */
1777:
1778: static void
1779: AddToggleToLine(linePtr, index, tagPtr)
1780: TkTextLine *linePtr; /* Line within which to add
1781: * transition. */
1782: int index; /* Character before which to
1783: * add transition. */
1784: TkTextTag *tagPtr; /* Information about tag. */
1785: {
1786: register TkAnnotation *annotPtr, *prevPtr;
1787: int delta = 1;
1788:
1789: /*
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.
1795: */
1796:
1797: for (prevPtr = NULL, annotPtr = linePtr->annotPtr; annotPtr != NULL;
1798: prevPtr = annotPtr, annotPtr = annotPtr->nextPtr) {
1799: if (annotPtr->ch > index) {
1800: break;
1801: }
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;
1807: } else {
1808: prevPtr->nextPtr = annotPtr->nextPtr;
1809: }
1810: ckfree((char *) annotPtr);
1811: delta = -1;
1812: goto updateNodes;
1813: }
1814: }
1815:
1816: /*
1817: * Create a new toggle and insert it into the list.
1818: */
1819:
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;
1828: } else {
1829: annotPtr->nextPtr = prevPtr->nextPtr;
1830: prevPtr->nextPtr = annotPtr;
1831: }
1832:
1833: /*
1834: * Update all the nodes above this line to reflect the change in
1835: * toggle structure.
1836: */
1837:
1838: updateNodes:
1839: ChangeNodeToggleCount(linePtr->parentPtr, tagPtr, delta);
1840: }
1841:
1842: /*
1843: *----------------------------------------------------------------------
1844: *
1845: * ChangeNodeToggleCount --
1846: *
1847: * This procedure increments or decrements the toggle count for
1848: * a particular tag in a particular node and all its ancestors.
1849: *
1850: * Results:
1851: * None.
1852: *
1853: * Side effects:
1854: * The toggle count for tag is adjusted up or down by "delta" in
1855: * nodePtr.
1856: *
1857: *----------------------------------------------------------------------
1858: */
1859:
1860: static void
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). */
1867: {
1868: register Summary *summaryPtr, *prevPtr;
1869:
1870: /*
1871: * Iterate over the node and all of its ancestors.
1872: */
1873:
1874: for ( ; nodePtr != NULL; nodePtr = nodePtr->parentPtr) {
1875: /*
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.
1878: */
1879:
1880: for (prevPtr = NULL, summaryPtr = nodePtr->summaryPtr;
1881: summaryPtr != NULL;
1882: prevPtr = summaryPtr, summaryPtr = summaryPtr->nextPtr) {
1883: if (summaryPtr->tagPtr != tagPtr) {
1884: continue;
1885: }
1886: summaryPtr->toggleCount += delta;
1887: if (summaryPtr->toggleCount > 0) {
1888: goto nextAncestor;
1889: }
1890: if (summaryPtr->toggleCount < 0) {
1891: panic("ChangeNodeToggleCount: negative toggle count");
1892: }
1893:
1894: /*
1895: * Zero count; must remove this tag from the list.
1896: */
1897:
1898: if (prevPtr == NULL) {
1899: nodePtr->summaryPtr = summaryPtr->nextPtr;
1900: } else {
1901: prevPtr->nextPtr = summaryPtr->nextPtr;
1902: }
1903: ckfree((char *) summaryPtr);
1904: goto nextAncestor;
1905: }
1906:
1907: /*
1908: * This tag isn't in the list. Add a new entry to the list.
1909: */
1910:
1911: if (delta < 0) {
1912: panic("ChangeNodeToggleCount: negative delta, no tag entry");
1913: }
1914: summaryPtr = (Summary *) ckalloc(sizeof(Summary));
1915: summaryPtr->tagPtr = tagPtr;
1916: summaryPtr->toggleCount = delta;
1917: summaryPtr->nextPtr = nodePtr->summaryPtr;
1918: nodePtr->summaryPtr = summaryPtr;
1919:
1920: nextAncestor:
1921: continue;
1922: }
1923: }
1924:
1925: /*
1926: *----------------------------------------------------------------------
1927: *
1928: * TkBTreeCharTagged --
1929: *
1930: * Determine whether a particular character has a particular tag.
1931: *
1932: * Results:
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.
1935: *
1936: * Side effects:
1937: * None.
1938: *
1939: *----------------------------------------------------------------------
1940: */
1941:
1942: int
1943: TkBTreeCharTagged(linePtr, ch, tagPtr)
1944: TkTextLine *linePtr; /* Line containing character of
1945: * interest. */
1946: int ch; /* Index of character in linePtr. */
1947: TkTextTag *tagPtr; /* Tag of interest. */
1948: {
1949: register Node *nodePtr;
1950: register TkTextLine *siblingLinePtr;
1951: int toggles;
1952:
1953: /*
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.
1957: */
1958:
1959: toggles = 0;
1960: for (siblingLinePtr = linePtr->parentPtr->children.linePtr; ;
1961: siblingLinePtr = siblingLinePtr->nextPtr) {
1962: register TkAnnotation *annotPtr;
1963:
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)) {
1970: toggles++;
1971: }
1972: }
1973: if (siblingLinePtr == linePtr) {
1974: break;
1975: }
1976: }
1977:
1978: /*
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.
1981: */
1982:
1983: for (nodePtr = linePtr->parentPtr; nodePtr->parentPtr != NULL;
1984: nodePtr = nodePtr->parentPtr) {
1985: register Node *siblingPtr;
1986: register Summary *summaryPtr;
1987:
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;
1994: }
1995: }
1996: }
1997: }
1998:
1999: /*
2000: * An odd number of toggles means that the tag is present at the
2001: * given point.
2002: */
2003:
2004: return toggles & 1;
2005: }
2006:
2007: /*
2008: *----------------------------------------------------------------------
2009: *
2010: * TkBTreeGetTags --
2011: *
2012: * Return information about all of the tags that are associated
2013: * with a particular character in a B-tree of text.
2014: *
2015: * Results:
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.
2023: *
2024: * Side effects:
2025: * None.
2026: *
2027: *----------------------------------------------------------------------
2028: */
2029:
2030: /* ARGSUSED */
2031: TkTextTag **
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
2038: * location. */
2039: {
2040: register Node *nodePtr;
2041: register TkTextLine *siblingLinePtr;
2042: int src, dst;
2043: TagInfo tagInfo;
2044: #define NUM_TAG_INFOS 10
2045:
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));
2052:
2053: /*
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
2056: * of interest.
2057: */
2058:
2059: for (siblingLinePtr = linePtr->parentPtr->children.linePtr; ;
2060: siblingLinePtr = siblingLinePtr->nextPtr) {
2061: register TkAnnotation *annotPtr;
2062:
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);
2069: }
2070: }
2071: if (siblingLinePtr == linePtr) {
2072: break;
2073: }
2074: }
2075:
2076: /*
2077: * For each node in the ancestry of this line, record tag toggles
2078: * for all siblings that precede that node.
2079: */
2080:
2081: for (nodePtr = linePtr->parentPtr; nodePtr->parentPtr != NULL;
2082: nodePtr = nodePtr->parentPtr) {
2083: register Node *siblingPtr;
2084: register Summary *summaryPtr;
2085:
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);
2091: }
2092: }
2093: }
2094:
2095: /*
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).
2099: */
2100:
2101: for (src = 0, dst = 0; src < tagInfo.numTags; src++) {
2102: if (tagInfo.counts[src] & 1) {
2103: tagInfo.tagPtrs[dst] = tagInfo.tagPtrs[src];
2104: dst++;
2105: }
2106: }
2107: *numTagsPtr = dst;
2108: ckfree((char *) tagInfo.counts);
2109: if (dst == 0) {
2110: ckfree((char *) tagInfo.tagPtrs);
2111: return NULL;
2112: }
2113: return tagInfo.tagPtrs;
2114: }
2115:
2116: /*
2117: *----------------------------------------------------------------------
2118: *
2119: * IncCount --
2120: *
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.
2124: *
2125: * Results:
2126: * None.
2127: *
2128: * Side effects:
2129: * The information at *tagInfoPtr may be modified, and the arrays
2130: * may be reallocated to make them larger.
2131: *
2132: *----------------------------------------------------------------------
2133: */
2134:
2135: static void
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. */
2141: {
2142: register TkTextTag **tagPtrPtr;
2143: int count;
2144:
2145: for (tagPtrPtr = tagInfoPtr->tagPtrs, count = tagInfoPtr->numTags;
2146: count > 0; tagPtrPtr++, count--) {
2147: if (*tagPtrPtr == tagPtr) {
2148: tagInfoPtr->counts[tagInfoPtr->numTags-count] += inc;
2149: return;
2150: }
2151: }
2152:
2153: /*
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
2156: * arrays first.
2157: */
2158:
2159: if (tagInfoPtr->numTags == tagInfoPtr->arraySize) {
2160: TkTextTag **newTags;
2161: int *newCounts, newSize;
2162:
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;
2176: }
2177:
2178: tagInfoPtr->tagPtrs[tagInfoPtr->numTags] = tagPtr;
2179: tagInfoPtr->counts[tagInfoPtr->numTags] = inc;
2180: tagInfoPtr->numTags++;
2181: }
2182:
2183: /*
2184: *----------------------------------------------------------------------
2185: *
2186: * CheckNodeConsistency --
2187: *
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.
2191: *
2192: * Results:
2193: * None.
2194: *
2195: * Side effects:
2196: * If anything suspicious is found in the tree structure, the
2197: * procedure panics.
2198: *
2199: *----------------------------------------------------------------------
2200: */
2201:
2202: static void
2203: CheckNodeConsistency(nodePtr)
2204: register Node *nodePtr; /* Node whose subtree should be
2205: * checked. */
2206: {
2207: register Node *childNodePtr;
2208: register Summary *summaryPtr, *summaryPtr2;
2209: register TkAnnotation *annotPtr;
2210: register TkTextLine *linePtr;
2211: register char *p;
2212: int numChildren, numLines, toggleCount, minChildren, index, numBytes;
2213:
2214: if (nodePtr->parentPtr != NULL) {
2215: minChildren = MIN_CHILDREN;
2216: } else if (nodePtr->level > 0) {
2217: minChildren = 2;
2218: } else {
2219: minChildren = 1;
2220: }
2221: if ((nodePtr->numChildren < minChildren)
2222: || (nodePtr->numChildren > MAX_CHILDREN)) {
2223: panic("CheckNodeConsistency found bad child count (%d)",
2224: nodePtr->numChildren);
2225: }
2226:
2227: numChildren = 0;
2228: numLines = 0;
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");
2235: }
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");
2239: }
2240: }
2241: if (numBytes != linePtr->numBytes) {
2242: panic("CheckNodeConsistency found line with bad numBytes");
2243: }
2244: if (linePtr->bytes[numBytes-1] != '\n') {
2245: panic("CheckNodeConsistency found line with no newline");
2246: }
2247: index = 0;
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,
2253: annotPtr->ch);
2254: }
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);
2263: }
2264: if (summaryPtr->tagPtr == annotPtr->info.tagPtr) {
2265: break;
2266: }
2267: }
2268: }
2269: }
2270: numChildren++;
2271: numLines++;
2272: }
2273: } else {
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);
2285: }
2286: if (summaryPtr->tagPtr == summaryPtr2->tagPtr) {
2287: break;
2288: }
2289: }
2290: }
2291: numChildren++;
2292: numLines += childNodePtr->numLines;
2293: if (childNodePtr->parentPtr != nodePtr) {
2294: panic("CheckNodeConsistency found node that %s",
2295: "didn't point to parent");
2296: }
2297: if (childNodePtr->level != (nodePtr->level-1)) {
2298: panic("CheckNodeConsistency found level mismatch (%d %d)",
2299: nodePtr->level, childNodePtr->level);
2300: }
2301: }
2302: }
2303: if (numChildren != nodePtr->numChildren) {
2304: panic("CheckNodeConsistency found mismatch in numChildren (%d %d)",
2305: numChildren, nodePtr->numChildren);
2306: }
2307: if (numLines != nodePtr->numLines) {
2308: panic("CheckNodeConsistency found mismatch in numLines (%d %d)",
2309: numLines, nodePtr->numLines);
2310: }
2311:
2312: for (summaryPtr = nodePtr->summaryPtr; summaryPtr != NULL;
2313: summaryPtr = summaryPtr->nextPtr) {
2314: toggleCount = 0;
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) {
2321: toggleCount++;
2322: }
2323: }
2324: }
2325: } else {
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;
2334: }
2335: }
2336: }
2337: }
2338: if (toggleCount != summaryPtr->toggleCount) {
2339: panic("CheckNodeConsistency found mismatch in toggleCount (%d %d)",
2340: toggleCount, summaryPtr->toggleCount);
2341: }
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);
2347: }
2348: }
2349: }
2350: }
2351:
2352: /*
2353: *----------------------------------------------------------------------
2354: *
2355: * TkBTreeNumLines --
2356: *
2357: * This procedure returns a count of the number of lines of
2358: * text present in a given B-tree.
2359: *
2360: * Results:
2361: * The return value is a count of the number of lines in tree.
2362: *
2363: * Side effects:
2364: * None.
2365: *
2366: *----------------------------------------------------------------------
2367: */
2368:
2369: int
2370: TkBTreeNumLines(tree)
2371: TkTextBTree tree; /* Information about tree. */
2372: {
2373: BTree *treePtr = (BTree *) tree;
2374: return treePtr->rootPtr->numLines;
2375: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.