Annotation of mstools/samples/sdktools/windiff/section.c, revision 1.1

1.1     ! root        1: 
        !             2: /******************************************************************************\
        !             3: *       This is a part of the Microsoft Source Code Samples. 
        !             4: *       Copyright (C) 1993 Microsoft Corporation.
        !             5: *       All rights reserved. 
        !             6: *       This source code is only intended as a supplement to 
        !             7: *       Microsoft Development Tools and/or WinHelp documentation.
        !             8: *       See these sources for detailed information regarding the 
        !             9: *       Microsoft samples programs.
        !            10: \******************************************************************************/
        !            11: 
        !            12: /****************************** Module Header *******************************
        !            13: * Module Name: SECTION.C
        !            14: *
        !            15: * Manages sections of lines, and lists of sections.
        !            16: *
        !            17: * Functions:
        !            18: *
        !            19: * section_new()
        !            20: * section_delete()
        !            21: * section_match()
        !            22: * section_getfirstline()
        !            23: * section_getlastline()
        !            24: * section_getlink()
        !            25: * section_getcorrespond()
        !            26: * section_getstate()
        !            27: * section_setstate()
        !            28: * section_getlinecount()
        !            29: * section_getleftbasenr()
        !            30: * section_setleftbasenr()
        !            31: * section_getrightbasenr()
        !            32: * section_setrightbasenr()
        !            33: * FindEndOfUnmatched()
        !            34: * NextNonIngnorable()
        !            35: * FinEndOfMatched()
        !            36: * section_makelist()
        !            37: * section_deletelist()
        !            38: * FindFirstWithLink()
        !            39: * section_matchlists()
        !            40: * section_takesection()
        !            41: * section_makecomposite()
        !            42: * AbsorbAnyBlanks()
        !            43: * section_makectree()
        !            44: * section_expandanchor()
        !            45: *
        !            46: * Comments:
        !            47: *
        !            48: * A section is a data type that represents a contiguous block of lines
        !            49: * of the same state (all unmatched, or all matched to a contiguous block of
        !            50: * lines). A section can link up matching lines within the section.
        !            51: *
        !            52: * Section list functions can make and match lists of sections from lists of
        !            53: * lines, and create a composite list by combining sections from two lists
        !            54: * to create a list that 'best represents' the similarities and differences
        !            55: * between the two lists of lines.
        !            56: * 
        !            57: * Assumptions: the lines passed in are on a list (can be traversed with
        !            58: * List_Next() etc. Line numbering using the section_get*basenr()
        !            59: * functions work only if lines are numbered sequentially in ascending order.
        !            60: *
        !            61: ****************************************************************************/
        !            62: 
        !            63: #include <windows.h>
        !            64: #include <stdlib.h>
        !            65: #include <string.h>
        !            66: 
        !            67: #include "gutils.h"
        !            68: #include "tree.h"
        !            69: #include "state.h"
        !            70: #include "windiff.h"
        !            71: #include "wdiffrc.h"
        !            72: #include "list.h"
        !            73: #include "line.h"
        !            74: #include "section.h"
        !            75: 
        !            76: /*
        !            77:  * a section handle (SECTION) is a pointer to one of these structures
        !            78:  */
        !            79: struct section {
        !            80:         LINE first;             /* first line in section */
        !            81:         LINE last;              /* last line in section */
        !            82: 
        !            83:         BOOL bDiscard;          /* true if not alloc-ed on list */
        !            84: 
        !            85:         SECTION link;           /* we match this section */
        !            86:         SECTION correspond;     /* we correspond to this section, but
        !            87:                                  * don't match it
        !            88:                                  */
        !            89: 
        !            90:         int state;              /* compare state for section */
        !            91: 
        !            92:         int leftbase;           /* nr in original left list of first line*/
        !            93:         int rightbase;          /* nr in original right list of first line*/
        !            94: };
        !            95: 
        !            96: /* --- function prototypes ------------------------------------------*/
        !            97: 
        !            98: TREE section_makectree(SECTION sec);
        !            99: BOOL section_expandanchor(SECTION sec1, LINE line1, SECTION sec2, LINE line2);
        !           100: 
        !           101: 
        !           102: 
        !           103: /***************************************************************************
        !           104:  * Function: section_new
        !           105:  *
        !           106:  * Purpose:
        !           107:  *
        !           108:  * Makes a new section, given handles to a first and last line.
        !           109:  *
        !           110:  * A section must be at least one line long. The lines passed in must be
        !           111:  * on a list in order.
        !           112:  *
        !           113:  * If the list parameter is non-null, we will allocate the section struct
        !           114:  * on the list. otherwise we will alloc it from gmem_get(hHeap). We remember
        !           115:  * this in the bDiscard flag for section_delete, so that we only
        !           116:  * hand back to gmem_free memory that we got.
        !           117:  **************************************************************************/
        !           118: SECTION
        !           119: section_new(LINE first, LINE last, LIST list)
        !           120: {
        !           121:         SECTION sec;
        !           122: 
        !           123:         /* alloc the sec and remember where we alloc-ed it */
        !           124:         if (list) {
        !           125:                 sec = (SECTION) List_NewLast(list, sizeof(struct section));
        !           126:                 sec->bDiscard = TRUE;
        !           127:         } else {
        !           128:                 sec = (SECTION) gmem_get(hHeap, sizeof(struct section));
        !           129:                 sec->bDiscard = FALSE;
        !           130:         }
        !           131: 
        !           132:         sec->first = first;
        !           133:         sec->last = last;
        !           134:         sec->link = NULL;
        !           135:         sec->correspond = NULL;
        !           136:         sec->state = 0;
        !           137:         sec->leftbase = 1;
        !           138:         sec->rightbase = 1;
        !           139: 
        !           140:         return(sec);
        !           141: }
        !           142: 
        !           143: /***************************************************************************
        !           144:  * Function: section_delete
        !           145:  *
        !           146:  * Purpose:
        !           147:  *
        !           148:  * Discard a section. Free all associated memory (not the line list).
        !           149:  * Free up the section itself if it was not alloc-ed on a list.
        !           150:  */
        !           151: void
        !           152: section_delete(SECTION section)
        !           153: {
        !           154:         if (section->bDiscard) {
        !           155:                 gmem_free(hHeap, (LPSTR) section, sizeof(struct section));
        !           156:         }
        !           157: }
        !           158: 
        !           159: /***************************************************************************
        !           160:  * Function: section_match
        !           161:  *
        !           162:  * Purpose:
        !           163:  *
        !           164:  * Match up two sections: match all lines that
        !           165:  * are unique and identical between the two sections.
        !           166:  *
        !           167:  * We use a tree of line handles, keyed by the line hash code. We use a
        !           168:  * ctree, which keeps a count for multiple identical keys. This allows
        !           169:  * us to rapidly find lines that are unique within this section.
        !           170:  * We build two of these trees (one for each line list). For each line
        !           171:  * that is unique in both trees, we attempt to link the lines.
        !           172:  *
        !           173:  * We also attempt to link the first and last line of the section.
        !           174:  *
        !           175:  * For each line we successfully link, we spread up and down from
        !           176:  * this anchor point attempting to link lines.
        !           177:  *
        !           178:  * We return true if we linked any lines
        !           179:  *
        !           180:  * This routine may be called more than once on the same list of lines.
        !           181:  * In matching lines we want to find unique, *unmatched* lines: so we only
        !           182:  * insert lines into the ctree if they are currently unlinked.
        !           183:  */
        !           184: BOOL
        !           185: section_match(SECTION sec1, SECTION sec2)
        !           186: {
        !           187:         TREE ctleft, ctright;
        !           188:         LINE line, line2;
        !           189:         BOOL bLinked = FALSE;
        !           190: 
        !           191: 
        !           192:         if ((sec1 == NULL) || (sec2 == NULL)) {
        !           193:                 return(FALSE);
        !           194:         }
        !           195: 
        !           196:         if ((sec1->first == NULL) || (sec2->first == NULL)) {
        !           197:                 return(FALSE);
        !           198:         }
        !           199:         /* ASSERT if first is non-null, so is last */
        !           200: 
        !           201:         /* attempt to link the first line of each file, and
        !           202:          * if matched, expand as long as we keep matching
        !           203:          */
        !           204:         bLinked |= section_expandanchor(sec1, sec1->first, sec2, sec2->first);
        !           205: 
        !           206:         /* attempt to link the last lines of each file and
        !           207:          * expand upwards
        !           208:          */
        !           209:         bLinked |= section_expandanchor(sec1, sec1->last, sec2, sec2->last);
        !           210: 
        !           211: 
        !           212:         /* build a tree of lines, indexed by the line hashcode.
        !           213:          * a ctree will hold only the first value of any given key, but
        !           214:          * it will keep track of the number of items inserted on this key.
        !           215:          * thus we can keep count of the number of times this line
        !           216:          * (or at least this hashcode) appears.
        !           217:          */
        !           218:         ctleft = section_makectree(sec1);
        !           219:         ctright = section_makectree(sec2);
        !           220: 
        !           221:         /* for each unlinked line in one list (doesnt matter which), find if
        !           222:          * appears once only in each list. if so, link, and expand
        !           223:          * the link to link lines before and after the matching line
        !           224:          * as long as they continue to match.
        !           225:          */
        !           226:         for (line = sec1->first; line != NULL; line = List_Next(line)) {
        !           227: 
        !           228:                 if ((line_getlink(line) == NULL) &&
        !           229:                    (ctree_getcount(ctleft, line_gethashcode(line)) == 1) &&
        !           230:                    (ctree_getcount(ctright, line_gethashcode(line)) == 1)) {
        !           231: 
        !           232:                         /* line appears exactly once in each list */
        !           233:                         line2 = * ((LINE FAR *)ctree_find(ctright,
        !           234:                                         line_gethashcode(line)));
        !           235:                         bLinked |= section_expandanchor(sec1, line, sec2, line2);
        !           236:                 }               
        !           237: 
        !           238:                 if (line == sec1->last) {
        !           239:                         break;
        !           240:                 }
        !           241:         }
        !           242: 
        !           243:         /* delete the ctrees */
        !           244:         ctree_delete(ctleft);
        !           245:         ctree_delete(ctright);
        !           246: 
        !           247:         return(bLinked);
        !           248: } /* section_match */
        !           249: 
        !           250: /***************************************************************************
        !           251:  * Function: section_getfirstline
        !           252:  *
        !           253:  * Purpose:
        !           254:  *
        !           255:  * Gets a handle to the first line in this section
        !           256:  */
        !           257: LINE
        !           258: section_getfirstline(SECTION section)
        !           259: {
        !           260:         if (section == NULL) {
        !           261:                 return(NULL);
        !           262:         }
        !           263:         return(section->first);
        !           264: }
        !           265: 
        !           266: /***************************************************************************
        !           267:  * Function: section_getlastline
        !           268:  *
        !           269:  * Purpose:
        !           270:  *
        !           271:  * Returns a handle to the last line in a section
        !           272:  */
        !           273: LINE
        !           274: section_getlastline(SECTION section)
        !           275: {
        !           276:         if (section == NULL) {
        !           277:                 return(NULL);
        !           278:         }
        !           279:         return(section->last);
        !           280: }
        !           281: 
        !           282: /***************************************************************************
        !           283:  * Function: section_getlink
        !           284:  *
        !           285:  * Purpose:
        !           286:  *
        !           287:  * Returns a handle to the linked section, if any. A linked section
        !           288:  * is a section whose lines all match the lines in this section
        !           289:  */
        !           290: SECTION
        !           291: section_getlink(SECTION section)
        !           292: {
        !           293:         if (section == NULL) {
        !           294:                 return NULL;
        !           295:         }
        !           296:         return(section->link);
        !           297: }
        !           298: 
        !           299: /***************************************************************************
        !           300:  * Function: section_getcorrespond
        !           301:  *
        !           302:  * Purpose:
        !           303:  *
        !           304:  * Returns a handle to the corresponding section (a section which
        !           305:  * corresponds in position to this one, but whose lines do not match).
        !           306:  */
        !           307: SECTION
        !           308: section_getcorrespond(SECTION section)
        !           309: {
        !           310:         if (section == NULL) {
        !           311:                 return(NULL);
        !           312:         }
        !           313:         return(section->correspond);    
        !           314: }
        !           315: /***************************************************************************
        !           316:  * Function: section_getstate
        !           317:  *
        !           318:  * Purpose:
        !           319:  *
        !           320:  * Gets the state for this section */
        !           321: int
        !           322: section_getstate(SECTION section)
        !           323: {
        !           324:         if (section == NULL) 
        !           325:                 return(0);
        !           326:         return(section->state);
        !           327: }
        !           328: 
        !           329: /***************************************************************************
        !           330:  * Function: section_setstate
        !           331:  *
        !           332:  * Purpose:
        !           333:  *
        !           334:  * Sets the state for this section */
        !           335: void
        !           336: section_setstate(SECTION section, int state)
        !           337: {
        !           338:         section->state = state;
        !           339: }
        !           340: 
        !           341: /***************************************************************************
        !           342:  * Function: section_getlinecount
        !           343:  *
        !           344:  * Purpose:
        !           345:  *
        !           346:  * Returns the number of lines in the section. Here we assume that
        !           347:  * lines in the section are number sequentially in ascending order, and we
        !           348:  * simply look at the first and last line numbers.
        !           349:  */
        !           350: int
        !           351: section_getlinecount(SECTION section)
        !           352: {
        !           353:         return(line_getlinenr(section->last) -
        !           354:                         line_getlinenr(section->first)) + 1;
        !           355: }
        !           356: 
        !           357: /*
        !           358:  * -- base line numbers --
        !           359:  *
        !           360:  * These functions only apply to sections in the composite list. When creating
        !           361:  * a composite section, we record the line number of the first line in each
        !           362:  * of the two sections we built it from. Thus we can calculate the
        !           363:  * line number of any line in the section in either file it appeared in,
        !           364:  * by adding the index of the line within the section to the base line
        !           365:  * number.
        !           366:  */
        !           367: int
        !           368: section_getleftbasenr(SECTION section)
        !           369: {
        !           370:         return(section->leftbase);
        !           371: }
        !           372: 
        !           373: void
        !           374: section_setleftbasenr(SECTION section, int base)
        !           375: {
        !           376:         section->leftbase = base;
        !           377: }
        !           378: 
        !           379: int
        !           380: section_getrightbasenr(SECTION section)
        !           381: {
        !           382:         return(section->rightbase);
        !           383: }
        !           384: 
        !           385: void
        !           386: section_setrightbasenr(SECTION section, int base)
        !           387: {
        !           388:         section->rightbase = base;
        !           389: }
        !           390: 
        !           391: 
        !           392: /* --- section list functions -------------------------------------*/
        !           393: 
        !           394: /* Theory of handling blank lines:
        !           395: |   
        !           396: |  If ignore_blanks is FALSE then a blank is just another character.
        !           397: |  If it is TRUE then we will normally include unmatched blanks in whatever
        !           398: |  section is surrounding them.  It would be nice if we could arrange to
        !           399: |  never have a section that is only unmatched blanks, but (at least at
        !           400: |  the start of the file) it can happen.
        !           401: |
        !           402: |  Note that there are two DIFFERENT blank handling techniques:
        !           403: |  In the first phase of the comparison when we are just trying to match up
        !           404: |  lines, we skip over blank lines both forwards and backwards from an anchor.
        !           405: |  When we are making real sections for display we only go forwards.
        !           406: |  This results in a possible anomaly at the top of the whole file where
        !           407: |  there could be some blanks which do not match and which can only possibly
        !           408: |  be described as the start of a section.
        !           409: |  For this reason, we label the sections with their state as early as possible
        !           410: |  and go by that rather than by the presence or absence of link fields.
        !           411: |  (It takes some scanning to find a link.  The first line in the section
        !           412: |  could be a blank).
        !           413: */
        !           414: 
        !           415: /***************************************************************************
        !           416:  * Function: FindEndOfUnmatched
        !           417:  *
        !           418:  * Purpose:
        !           419:  *
        !           420:  * Returns a LINE which is the last line in an unmatched section
        !           421:  * containing (probably starting with) Line.
        !           422:  * Note that it does not necessarily make progress.
        !           423:  *
        !           424:  * As noted above, even if blank lines are being ignored, we don't
        !           425:  * mind tagging them onto the end of an already unmatching section.
        !           426:  * This means we carry on until we find the first real link
        !           427:  */
        !           428: LINE FindEndOfUnmatched(LINE line)
        !           429: {
        !           430:         LINE next;
        !           431: 
        !           432:         for (; ; )
        !           433:         {       next = List_Next(line);
        !           434:                 if (next==NULL) return line;
        !           435:                 if (line_getlink(next)!=NULL) return line;
        !           436:                 line = next;
        !           437:         }
        !           438: } /* FindEndOfUnmatched */
        !           439: 
        !           440: 
        !           441: /***************************************************************************
        !           442:  * Function: NextNonIgnorable
        !           443:  *
        !           444:  * Purpose:
        !           445:  *
        !           446:  * An ignorable line is a blank line with no link and ignore_blanks set
        !           447:  *
        !           448:  * Given that line is initially not NULL and not ignorable:
        !           449:  * If line is the last line in the list then return NULL
        !           450:  * Else If ignore_blanks is FALSE then return the next line after line
        !           451:  * else return next line which has a link or which is non-blank.
        !           452:  * If there is no such line then return the last line in the list.
        !           453:  *
        !           454:  * Note that this does always make progress (at the cost of
        !           455:  * sometimes returning NULL).
        !           456:  */
        !           457: LINE NextNonIgnorable(LINE line)
        !           458: {       LINE next;
        !           459: 
        !           460:         next = List_Next(line);
        !           461:         if (next==NULL) return NULL;
        !           462:         for (; ; ) {
        !           463:                 line = next;
        !           464:                 if (  line_getlink(line)!=NULL) return line;
        !           465:                 if (! ignore_blanks)            return line;
        !           466:                 if (! line_isblank(line))       return line;
        !           467:                 next = List_Next(line);
        !           468:                 if (next==NULL) return line;
        !           469:         }
        !           470: } /* NextNonIgnorable */
        !           471: 
        !           472: 
        !           473: /***************************************************************************
        !           474:  * Function: FindEndOfMatched
        !           475:  *
        !           476:  * Purpose:
        !           477:  *
        !           478:  * Given that line is either linked or an ignorable blank:
        !           479:  * Return a LINE which is the last line in a matched section
        !           480:  * containing (probably starting with) line.
        !           481:  * This could mean returning the line we were given.
        !           482:  *
        !           483:  * If the lines linked to are not consecutive then the section ends.
        !           484:  * If blanks are being ignored, then any blank line is deemed
        !           485:  * to match (even if it doesn't match).  In this case we need the
        !           486:  * links of the lines before and after the blanks to be consecutive
        !           487:  * in order to carry on.  There could be blank lines on either or both
        !           488:  * ends of the links.
        !           489:  */
        !           490: LINE FindEndOfMatched(LINE line)
        !           491: {
        !           492:         LINE next;              /* next non-ignored or linked line */
        !           493:         LINE nextlink;          /* next in other file */
        !           494: 
        !           495:         /* The basic algorithm is to set up next and nextlink to point to
        !           496:            candidate lines.  Examine them.  If they are good then step
        !           497:            on to them, else return the line one before.
        !           498:            There are confusion factors associated with the beginning and
        !           499:            end of the file.
        !           500:         */
        !           501: 
        !           502:         /* ASSERT( line is either an ignorable blank or else is linked) */
        !           503: 
        !           504:         /* As a section (at least at the start of the file) might start
        !           505:            with an ignored non-linked blank line, first step over any such
        !           506:         */
        !           507:         if( line_getlink(line)==NULL && line_isblank(line) ) {
        !           508:                 next = NextNonIgnorable(line);
        !           509: 
        !           510:                 /* There are unfortunately 6 cases to deal with
        !           511:                    * marks where next will be. * against eof means next==NULL
        !           512:                    blank(s) refer to ignorable unlinked blanks.
        !           513:                           A         B        C        D        E        F
        !           514:                    line-> xxxxx     xxxxx    xxxxx    xxxxx    xxxxx    xxxxx
        !           515:                          *unlinked  blanks  *linked   blanks  *eof     *blanks
        !           516:                                    *unlinked         *linked            eof
        !           517: 
        !           518:                    next could be:
        !           519:                 
        !           520:                       null - case E => return line
        !           521:                       unlinked ignorable blank - case F => return that blank line
        !           522:                       unlinked other - cases A,B return prev(that unlinked line)
        !           523:                       linked - cases C,D continue from that linked line
        !           524:                 */
        !           525:                 if (next==NULL) return line;
        !           526:                 if (line_getlink(next)==NULL) {
        !           527:                         if (ignore_blanks && line_isblank(next)) {
        !           528:                                 return next;
        !           529:                         }
        !           530:                         return List_Prev(next);
        !           531:                 }
        !           532: 
        !           533:                 line = next;
        !           534:         }
        !           535: 
        !           536:         /* we have stepped over inital blanks and now do have a link */
        !           537: 
        !           538:         for ( ; ; ) {
        !           539: 
        !           540:                 next = NextNonIgnorable(line);
        !           541:                 /* Same 6 cases - basically same again */
        !           542:                 if (next==NULL) return line;
        !           543:                 if (line_getlink(next)==NULL) {
        !           544:                         if (ignore_blanks && line_isblank(next)) {
        !           545:                                 return next;
        !           546:                         }
        !           547:                         return List_Prev(next);
        !           548:                 }
        !           549: 
        !           550:                 nextlink = NextNonIgnorable(line_getlink(line));
        !           551: 
        !           552:                 /* WEAK LOOP INVARIANT
        !           553:                    line is linked.
        !           554:                    next is the next non-ignorable line in this list after line.
        !           555:                    nextlink is the next non-ignorable line after link(line)
        !           556:                                         in the other list (could be NULL etc).
        !           557:                 */
        !           558:                 if (line_getlink(next) != nextlink) return List_Prev(next);
        !           559: 
        !           560:                 line = next;
        !           561:         }
        !           562:         return line;
        !           563: } /* FindEndOfMatched */
        !           564: 
        !           565: 
        !           566: /***************************************************************************
        !           567:  * Function: section_makelist
        !           568:  *
        !           569:  * Purpose:
        !           570:  *
        !           571:  * Make a list of sections by traversing a list of lines. Consecutive
        !           572:  * linked lines that are linked to consecutive lines are put in a single
        !           573:  * section. Blocks of unlinked lines are placed in a section.
        !           574:  * If ignore_blanks is set then we first try to link them as normal.
        !           575:  * but if they won't link then we just skip over them and keep them
        !           576:  * in the same section.
        !           577:  *
        !           578:  * Left must be set TRUE iff the list of lines is a left hand section.
        !           579:  * Returns a handle to a list of sections
        !           580:  */
        !           581: LIST
        !           582: section_makelist(LIST linelist, BOOL left)
        !           583: {
        !           584:         LINE line1, line2;
        !           585:         LIST sections;
        !           586:         BOOL matched;
        !           587:         SECTION sect;
        !           588: 
        !           589:         /* make an empty list of sections */
        !           590:         sections = List_Create();
        !           591: 
        !           592:         /* for each line in the list */
        !           593: 
        !           594:         List_TRAVERSE(linelist, line1) {
        !           595: 
        !           596:                 /* is it linked ? */
        !           597: 
        !           598:                 if( line_getlink(line1) != NULL
        !           599:                   || ( ignore_blanks && line_isblank(line1))
        !           600:                   ) {
        !           601:                         line2 = FindEndOfMatched(line1);
        !           602:                         matched = TRUE;
        !           603:                 } else {
        !           604:                         line2 = FindEndOfUnmatched(line1);
        !           605:                         matched = FALSE;
        !           606:                 }
        !           607: 
        !           608:                 /* create the section and add to list */
        !           609:                 sect = section_new(line1, line2, sections);
        !           610:                 sect->state = (matched ? STATE_SAME
        !           611:                                        : left ? STATE_LEFTONLY
        !           612:                                               : STATE_RIGHTONLY
        !           613:                               );
        !           614: 
        !           615:                 /* advance to end of section (no-op if 1 line section) */
        !           616:                 line1 = line2;
        !           617:         }
        !           618: 
        !           619:         return(sections);
        !           620: } /* section_makelist */
        !           621: 
        !           622: 
        !           623: 
        !           624: /***************************************************************************
        !           625:  * Function: section_deletelist
        !           626:  *
        !           627:  * Purpose:
        !           628:  *
        !           629:  * Delete a list of sections
        !           630:  *
        !           631:  * Sections have no dangling pointers, so all we do is delete the list
        !           632:  */
        !           633: void    
        !           634: section_deletelist(LIST sections)
        !           635: {
        !           636:         List_Destroy(&sections);
        !           637: }
        !           638: 
        !           639: /***************************************************************************
        !           640:  * Function: FindFirstWithLink
        !           641:  *
        !           642:  * Purpose:
        !           643:  *
        !           644:  * Return the first line in the range first..last
        !           645:  * which has a link.  Return last if none of them have a link.
        !           646:  * List_Next must lead from first to last eventually.
        !           647:  * It is legit for last to be NULL.
        !           648:  */
        !           649: LINE FindFirstWithLink(LINE first, LINE last)
        !           650: {       
        !           651:         /* The strategy of including blanks on the ENDS of sections rather
        !           652:            than the start of new sections will mean that this function
        !           653:            usually strikes gold immediately.  A file with a leading
        !           654:            blank section is its raison d'etre.
        !           655:         */
        !           656:         while (line_getlink(first)==NULL && first!=last)
        !           657:                 first = List_Next(first);
        !           658: 
        !           659:         if (line_getlink(first)==NULL) {
        !           660:         }
        !           661:         return first;
        !           662: } /* FindFirstWithLink */
        !           663: 
        !           664: 
        !           665: /***************************************************************************
        !           666:  * Function: section_matchlists
        !           667:  *
        !           668:  * Purpose:
        !           669:  *
        !           670:  * Match up two lists of sections. Establish links between sections
        !           671:  * that match, and establish 'correspondence' between sections that
        !           672:  * are in the same place, but don't match.
        !           673:  *
        !           674:  * For each pair of corresponding sections, we also call section_match
        !           675:  * to try and link up more lines.
        !           676:  *
        !           677:  * We return TRUE if we made any more links between lines, or false
        !           678:  * otherwise.
        !           679:  *
        !           680:  */
        !           681: BOOL
        !           682: section_matchlists(LIST secsleft, LIST secsright)
        !           683: {
        !           684:         BOOL bLinked = FALSE;
        !           685:         SECTION sec1, sec2;
        !           686: 
        !           687:         /* match up linked sections - We know whether a section is
        !           688:            supposed to link from its state, but we don't know what section
        !           689:            it links to.  Also we can have sections which are defined to
        !           690:            be matching but actually contain nothing but ignorable
        !           691:            blank lines
        !           692:         */
        !           693:         
        !           694:         /*  for each linked section try to find the section  linked to it. */
        !           695:         List_TRAVERSE(secsleft, sec1) {
        !           696:                 if (sec1->state==STATE_SAME) {
        !           697:                         LINE FirstWithLink = FindFirstWithLink(sec1->first, sec1->last);
        !           698:                         List_TRAVERSE(secsright, sec2) {
        !           699:                                 if ( sec2->state==STATE_SAME
        !           700:                                    && line_getlink(FirstWithLink)
        !           701:                                         == FindFirstWithLink(sec2->first, sec2->last)) {
        !           702:                                             break;
        !           703:                                 }
        !           704:                         }
        !           705:                         /* sec2 could be NULL if sec1 is all allowable blanks */
        !           706:                         if (sec2!=NULL) {
        !           707:                                 sec1->link = sec2;
        !           708:                                 sec2->link = sec1;
        !           709:                         }
        !           710:                 }
        !           711:         }
        !           712: 
        !           713:         /* go through all unmatched sections. Note that we need to complete
        !           714:          * the link-up of matching sections before this, since we need
        !           715:          * all the links in place for this to work.
        !           716:          */
        !           717: 
        !           718:         List_TRAVERSE(secsleft, sec1) {
        !           719:                 SECTION secTemp;
        !           720: 
        !           721:                 if (sec1->state == STATE_SAME) {
        !           722:                         /* skip the linked sections */
        !           723:                         continue;
        !           724:                 }
        !           725: 
        !           726:                 /* check that the previous and next sections, if
        !           727:                  * they exist, are linked. this should not fail since
        !           728:                  * two consecutive unlinked sections should be made into
        !           729:                  * one section
        !           730:                  */
        !           731:                 secTemp = List_Prev(sec1);
        !           732:                 if (secTemp && secTemp->state!= STATE_SAME) {
        !           733:                         continue;
        !           734:                 }
        !           735:                 secTemp = List_Next(sec1);
        !           736:                 if (secTemp && secTemp->state!= STATE_SAME) {
        !           737:                         continue;
        !           738:                 }
        !           739: 
        !           740:                 /* find the section that corresponds to this - that is, the
        !           741:                  * section following the section linked to our previous section.
        !           742:                  * we could be at beginning or end of list.
        !           743:                  */
        !           744:                 if (List_Prev(sec1) != NULL) {
        !           745:                         SECTION secOther;
        !           746:                         secOther = section_getlink(List_Prev(sec1));
        !           747:                         if (secOther==NULL)
        !           748:                                 continue;
        !           749: 
        !           750:                         sec2 = List_Next(secOther);
        !           751: 
        !           752:                         /* check this section is not linked */
        !           753:                         if ((sec2 == NULL) || (section_getlink(sec2) != NULL)) {
        !           754:                                 continue;
        !           755:                         }
        !           756:                         
        !           757:                         /* check that the section after these are linked
        !           758:                          * to each other (or both are at end of list).
        !           759:                          */
        !           760:                         if (List_Next(sec1) != NULL) {
        !           761: 
        !           762:                                 if (section_getlink(List_Next(sec1)) !=
        !           763:                                     List_Next(sec2)) {
        !           764:                                         continue;
        !           765:                                 }
        !           766:                         } else {
        !           767:                                 if (List_Next(sec2) == NULL) {
        !           768:                                         continue;
        !           769:                                 }
        !           770:                         }
        !           771: 
        !           772:                 } else if (List_Next(sec1) != NULL) {
        !           773:                         SECTION secOther;
        !           774:                         secOther = section_getlink(List_Next(sec1));
        !           775:                         if (secOther==NULL)
        !           776:                                 continue;
        !           777: 
        !           778:                         sec2 = List_Prev(secOther);
        !           779: 
        !           780:                         /* check this section is not linked */
        !           781:                         if ((sec2 == NULL) || (section_getlink(sec2) != NULL)) {
        !           782:                                 continue;
        !           783:                         }
        !           784:                         
        !           785:                         /* check that the section before these are linked
        !           786:                          * to each other (or both are at start of list).
        !           787:                          */
        !           788:                         if (List_Prev(sec1) != NULL) {
        !           789: 
        !           790:                                 if (section_getlink(List_Prev(sec1)) !=
        !           791:                                     List_Prev(sec2)) {
        !           792:                                         continue;
        !           793:                                 }
        !           794:                         } else {
        !           795:                                 if (List_Prev(sec2) == NULL) {
        !           796:                                         continue;
        !           797:                                 }
        !           798:                         }
        !           799:                 } else {
        !           800:                         /* there must be at most one section in each
        !           801:                          * file, and they are unmatched. make these correspond.
        !           802:                          */
        !           803:                         sec2 = List_First(secsright);
        !           804:                 }
        !           805: 
        !           806: 
        !           807:                 /* make the correspondence links
        !           808:                  */
        !           809:                 if ((sec1 != NULL) && (sec2 != NULL)) {
        !           810:                         sec1->correspond = sec2;
        !           811:                         sec2->correspond = sec1;
        !           812:                 }
        !           813: 
        !           814:                 /* attempt to link up lines */
        !           815:                 if (section_match(sec1, sec2)) {
        !           816:                         bLinked = TRUE;
        !           817:                 }
        !           818:         }
        !           819: 
        !           820:         return(bLinked);
        !           821: } /* section_matchlists */
        !           822: 
        !           823: /***************************************************************************
        !           824:  * Function: section_takesection
        !           825:  *
        !           826:  * Purpose:
        !           827:  *
        !           828:  * Add a section to the composite list. Called from make_composites
        !           829:  * to copy a section, add it to the composite list and set the state,
        !           830:  * leftbase and rightbase.   Note that the state could be STATE_SAME
        !           831:  * with a NULL section on the left.  May NOT call with STATE_SAME and
        !           832:  * a NULL right section!
        !           833:  *
        !           834:  */
        !           835: void
        !           836: section_takesection(LIST compo, SECTION left, SECTION right, int state)
        !           837: {
        !           838:         SECTION newsec;
        !           839:         SECTION sec;
        !           840: 
        !           841:         /* select which section is being output, and change the state
        !           842:          * to indicate it has been output
        !           843:          */
        !           844:         switch(state) {
        !           845:         case STATE_SAME:
        !           846:                 /* both the same. we mark both as output, and
        !           847:                  * take the right one.  It is possible that the
        !           848:                  * left one could be NULL (an ignorable blank section)
        !           849:                  */
        !           850:                 if (left!=NULL) left->state = STATE_MARKED;
        !           851:                 right->state = STATE_MARKED;
        !           852:                 sec = right;
        !           853:                 break;
        !           854: 
        !           855:         case STATE_LEFTONLY:
        !           856:         case STATE_MOVEDLEFT:
        !           857:                 sec = left;
        !           858:                 left->state = STATE_MARKED;
        !           859:                 break;
        !           860: 
        !           861:         case STATE_RIGHTONLY:
        !           862:         case STATE_MOVEDRIGHT:
        !           863:                 sec = right;
        !           864:                 right->state = STATE_MARKED;
        !           865:                 break;
        !           866:         }
        !           867: 
        !           868: 
        !           869:         /* create a new section on the list */
        !           870:         newsec = section_new(sec->first, sec->last, compo);
        !           871: 
        !           872:         newsec->state = state;
        !           873: 
        !           874: 
        !           875:         if (left != NULL) {
        !           876:                 newsec->leftbase = line_getlinenr(left->first);
        !           877:         } else {
        !           878:                 newsec->leftbase = 0;
        !           879:         }
        !           880: 
        !           881:         if (right != NULL) {
        !           882:                 newsec->rightbase = line_getlinenr(right->first);
        !           883:         } else {
        !           884:                 newsec->rightbase = 0;
        !           885:         }
        !           886: 
        !           887: } /* section_takesection */
        !           888: 
        !           889: /***************************************************************************
        !           890:  * Function: section_makecomposite
        !           891:  *
        !           892:  * Purpose:
        !           893:  *
        !           894:  * Make a composite list of sections by traversing a list of sections.
        !           895:  *
        !           896:  * Return a handle to a list of sections.
        !           897:  *
        !           898:  * During this, set state, leftbase and rightbase for sections.
        !           899:  *
        !           900:  * Comments:
        !           901:  *
        !           902:  * This function creates a list that corresponds to the 'best' view
        !           903:  * of the differences between the two lists. We place sections from the
        !           904:  * two lists into one composite list. Sections that match each other are only
        !           905:  * inserted once (from the right list). Sections that match, but in different
        !           906:  * positions in the two lists are inserted twice, once in each position, with
        !           907:  * status to indicate this. Unmatched sections are inserted in the correct
        !           908:  * position.
        !           909:  *
        !           910:  * - Take sections from the left list until the section is linked to one not
        !           911:  *   already taken.
        !           912:  * - Then take sections from right until we find a section linked to one not
        !           913:  *   already taken.
        !           914:  * - If the two sections waiting are linked to each other, take them both
        !           915:  *   (once- we take the right one and advance past both).
        !           916:  *
        !           917:  * - Now we have to decide which to take in place and which to declare
        !           918:  *   'moved'. Consider the case where the only change is that the first line
        !           919:  *   has been moved to the end. We should take the first line (as a move),
        !           920:  *   then the bulk of the file (SAME) then the last line (as a move). Hence,
        !           921:  *   in difficult cases, we take the smaller section first, to ensure that
        !           922:  *   the larger section is taken as SAME.
        !           923:  *
        !           924:  *   To indicate which section has been output, we set the state field
        !           925:  *   to STATE_MARKED once we have taken it.   States in left and right
        !           926:  *   lists are of no further interest once we have built the composite.
        !           927:  *
        !           928:  *   Up to this point we have worked off the STATE of a section.  By now
        !           929:  *   all the section links are in place, so we can use them too.
        !           930:  */
        !           931: LIST
        !           932: section_makecomposite(LIST secsleft, LIST secsright)
        !           933: {
        !           934:         SECTION left, right;
        !           935:         LIST compo;
        !           936: 
        !           937:         /* make an empty list for the composite */
        !           938:         compo = List_Create();
        !           939: 
        !           940:         left = List_First(secsleft);
        !           941:         right = List_First(secsright);
        !           942: 
        !           943:         while ( (left != NULL) || (right != NULL)) {
        !           944: 
        !           945:                 if (left == NULL) {
        !           946:                         /* no more in left list - take right section */
        !           947:                         /* is it moved or just unmatched ? */
        !           948:                         if (right->link == NULL) {
        !           949:                                 section_takesection(compo, NULL, right, STATE_RIGHTONLY);
        !           950:                                 right = List_Next(right);
        !           951:                         } else {
        !           952:                                 section_takesection(compo, right->link, right, STATE_MOVEDRIGHT);
        !           953:                                 right = List_Next(right);
        !           954:                         }
        !           955:                 } else if (right == NULL) {
        !           956:                         /* right list empty - must be left next */
        !           957: 
        !           958:                         /* is it moved or just unmatched ? */
        !           959:                         if (left->link == NULL) {
        !           960:                                 section_takesection(compo, left, NULL, STATE_LEFTONLY);
        !           961:                                 left = List_Next(left);
        !           962:                         } else {
        !           963:                                 section_takesection(compo, left, left->link, STATE_MOVEDLEFT);
        !           964:                                 left = List_Next(left);
        !           965:                         }
        !           966: 
        !           967:                 } else if (left->state == STATE_LEFTONLY) {
        !           968:                         /* unlinked section on left */
        !           969:                         section_takesection(compo, left, NULL, STATE_LEFTONLY);
        !           970:                         left = List_Next(left);
        !           971: 
        !           972:                 } else if (left->link==NULL) {
        !           973:                         /* This is an ignorable blank section on the left.
        !           974:                          * We ignore it. (We will take any such from the right)
        !           975:                          */
        !           976:                         left = List_Next(left);
        !           977: 
        !           978:                 } else if (left->link->state==STATE_MARKED) {
        !           979:                         /* left is linked to section that is already taken*/
        !           980:                         section_takesection(compo, left, left->link, STATE_MOVEDLEFT);
        !           981:                         left = List_Next(left);
        !           982: 
        !           983:                 } else  if (right->link == NULL) {
        !           984:                         /* take unlinked section on right
        !           985:                          * Either unmatched or ignorable blanks
        !           986:                          */
        !           987:                         section_takesection(compo, NULL, right, right->state);
        !           988:                         right = List_Next(right);
        !           989:                 
        !           990:                 } else if (right->link->state==STATE_MARKED) {
        !           991:                         /* right is linked to section that's already taken */
        !           992:                         section_takesection(compo, right->link, right, STATE_MOVEDRIGHT);
        !           993:                         right = List_Next(right);
        !           994:                 
        !           995:                 } else if (left->link == right) {
        !           996:                         /* sections match */
        !           997:                         section_takesection(compo, left, right, STATE_SAME);
        !           998:                         right = List_Next(right);
        !           999:                         left = List_Next(left);
        !          1000:                 } else {
        !          1001:                         /* both sections linked to forward sections
        !          1002:                          * decide first based on size of sections
        !          1003:                          * - smallest first as a move so that largest
        !          1004:                          * is an unchanged.
        !          1005:                          */
        !          1006:                         if (section_getlinecount(right) > section_getlinecount(left)) {
        !          1007:                                 section_takesection(compo, left, left->link, STATE_MOVEDLEFT);
        !          1008:                                 left = List_Next(left);
        !          1009:                         } else {
        !          1010:                                 section_takesection(compo, right->link, right, STATE_MOVEDRIGHT);
        !          1011:                                 right = List_Next(right);
        !          1012:                         }
        !          1013:                 }
        !          1014:         }
        !          1015: 
        !          1016:         return(compo);
        !          1017: } /* section_makecomposite */
        !          1018: 
        !          1019: typedef LINE (APIENTRY * MOVEPROC)(LINE);
        !          1020: 
        !          1021: /***************************************************************************
        !          1022:  * Function: AbsorbAnyBlanks
        !          1023:  *
        !          1024:  * Purpose:
        !          1025:  *
        !          1026:  * Update PLINE by making it point to the first non-blank
        !          1027:  * at-or-after from but not after limit.
        !          1028:  * If they are all blank then make it point to limit
        !          1029:  * If from is non-blank then leave it alone.
        !          1030:  * Return TRUE iff PLINE was updated.
        !          1031:  * It is legit for limit to be NULL (meaning end of file).
        !          1032:  */
        !          1033: BOOL AbsorbAnyBlanks(LINE * from, LINE limit, MOVEPROC Move)
        !          1034: {       BOOL progress = FALSE;
        !          1035: 
        !          1036:         while ( (from!=NULL)
        !          1037:               && (line_isblank(*from))
        !          1038:               && (*from!=limit)
        !          1039:               ) {
        !          1040:                 *from = Move(*from);
        !          1041:                 progress = TRUE;
        !          1042:         }
        !          1043:         return progress;
        !          1044: } /* AbsorbAnyBlanks */
        !          1045: 
        !          1046: 
        !          1047: /***************************************************************************
        !          1048:  * Function: section_expandanchor
        !          1049:  *
        !          1050:  * Purpose:
        !          1051:  *
        !          1052:  * Given an anchor point (two lines that we think should match),
        !          1053:  * try to link them, and the lines above and below them for as long
        !          1054:  * as the lines can be linked (are the same, are unlinked).
        !          1055:  *
        !          1056:  * Return TRUE if we make any links.
        !          1057:  *
        !          1058:  */
        !          1059: BOOL
        !          1060: section_expandanchor(SECTION sec1, LINE line1, SECTION sec2, LINE line2)
        !          1061: {
        !          1062:         /* when a line is matched we set bChanges.  If we notice some
        !          1063:          * blank lines, but do NOT link any new non-blank lines, we
        !          1064:          * do NOT set bChanges.  (If we did it would cause a closed
        !          1065:          * loop as they would get noticed again next time.  line_link
        !          1066:          * only returns TRUE if it is a NEW link).
        !          1067:          * At this stage we are only interested in making links, not in
        !          1068:          * the size of the section that results (that fun comes later).
        !          1069:          * therefore trailing blanks at the end of a section are not
        !          1070:          * interesting and we don't look for them.
        !          1071:          */
        !          1072:         BOOL bChanges = FALSE;
        !          1073:         LINE left, right;
        !          1074: 
        !          1075:         /* We handle the section limits by using a sentinel which is one
        !          1076:          * past the end of the section.  (If the section ends at the end
        !          1077:          * of the list then the sentinel is NULL).
        !          1078:          */
        !          1079:         LINE leftend, rightend;
        !          1080:         leftend = List_Next(sec1->last);
        !          1081:         rightend = List_Next(sec2->last);
        !          1082: 
        !          1083:         /* null lines shall not match */
        !          1084:         if ((line1 == NULL) || (line2 == NULL)) {
        !          1085:                 return(FALSE);
        !          1086:         }
        !          1087: 
        !          1088:         /* check all lines forward until fail to link (because null,
        !          1089:          * not matching, or already linked).
        !          1090:          * include the passed in anchor point since this has not
        !          1091:          * yet been linked.
        !          1092:          * If blanks are ignorable then skip over any number of whole
        !          1093:          * blank lines.
        !          1094:          */
        !          1095:         left = line1;
        !          1096:         right = line2;
        !          1097:         for (; ; ) {
        !          1098:                 if (line_link(left, right) ) {
        !          1099: 
        !          1100:                         bChanges = TRUE;
        !          1101:                         left = List_Next(left);
        !          1102:                         right = List_Next(right);
        !          1103:                         if (left==leftend || right==rightend) break;
        !          1104:                 }
        !          1105:                 else if (ignore_blanks){
        !          1106:                         /* even though no match, maybe an ignorable blank? */
        !          1107: 
        !          1108:                         BOOL moved = FALSE;
        !          1109:                         moved |= AbsorbAnyBlanks(&left, leftend, (MOVEPROC)List_Next);
        !          1110:                         moved |= AbsorbAnyBlanks(&right, rightend, (MOVEPROC)List_Next);
        !          1111:                         if (!moved) break; /* it didn't match and we didn't move on */
        !          1112:                         if (left==leftend || right==rightend) break;
        !          1113:                 }
        !          1114:                 else break;
        !          1115:         }
        !          1116: 
        !          1117:         /* check all matches going backwards from anchor point
        !          1118:            but only if it was a real anchor  (could have been
        !          1119:            end-of-section/end-of-file and non-matching).
        !          1120:         */
        !          1121:         if (line_getlink(line1)==NULL) return bChanges;
        !          1122: 
        !          1123:         left = List_Prev(line1);
        !          1124:         right = List_Prev(line2);
        !          1125:         if (left==NULL || right==NULL) return bChanges;
        !          1126: 
        !          1127:         leftend = List_Prev(sec1->first);
        !          1128:         rightend = List_Prev(sec2->first);
        !          1129: 
        !          1130:         for (; ; ) {
        !          1131:                 if (line_link(left, right)) {
        !          1132: 
        !          1133:                         bChanges = TRUE;
        !          1134:                         left = List_Prev(left);
        !          1135:                         right = List_Prev(right);
        !          1136:                         if (left == leftend || right == rightend) break;
        !          1137: 
        !          1138:                 }
        !          1139:                 else if (ignore_blanks){
        !          1140:                         /* even though no match, maybe an ignorable blank? */
        !          1141: 
        !          1142:                         BOOL moved = FALSE;
        !          1143:                         moved |= AbsorbAnyBlanks(&left, leftend, (MOVEPROC)List_Prev);
        !          1144:                         moved |= AbsorbAnyBlanks(&right, rightend, (MOVEPROC)List_Prev);
        !          1145:                         if (!moved) break; /* it didn't match and we didn't move on */
        !          1146:                         if (left==leftend || right==rightend) break;
        !          1147: 
        !          1148:                 }
        !          1149:                 else break;
        !          1150:         }
        !          1151: 
        !          1152:         return(bChanges);
        !          1153: }
        !          1154: 
        !          1155: 
        !          1156: /***************************************************************************
        !          1157:  * Function: section_makectree
        !          1158:  *
        !          1159:  * Purpose:
        !          1160:  *
        !          1161:  * Build a ctree from the lines in the section given
        !          1162:  *
        !          1163:  * Remember that we are only interested in the lines that are
        !          1164:  * not already linked.
        !          1165:  *
        !          1166:  * The value we store in the tree is the handle of the line. the key
        !          1167:  * is the line hash code
        !          1168:  */
        !          1169: TREE
        !          1170: section_makectree(SECTION sec)
        !          1171: {
        !          1172:         TREE tree;
        !          1173:         LINE line;
        !          1174: 
        !          1175:         /* make an empty tree */
        !          1176:         tree = ctree_create(hHeap);
        !          1177: 
        !          1178:         for (line = sec->first; line != NULL; line = List_Next(line)) {
        !          1179:                 if (line_getlink(line) == NULL) {
        !          1180:                         ctree_update(tree, line_gethashcode(line),
        !          1181:                                         &line, sizeof(LINE));
        !          1182:                 }
        !          1183:                 if (line == sec->last) {
        !          1184:                         break;
        !          1185:                 }
        !          1186:         }
        !          1187:         return(tree);
        !          1188: }
        !          1189: 
        !          1190: 

unix.superglobalmegacorp.com

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