|
|
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.