Annotation of micropolis/src/tk/tktxbtre.c, revision 1.1

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.