|
|
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(§ions); ! 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:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.