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