|
|
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: COMPITEM.C
14: *
15: * Module which does the comparison between two files.
16: *
17: * Functions:
18: *
19: * ci_copytext()
20: * ci_makecomposite()
21: * ci_compare()
22: * ci_onesection()
23: * compitem_new()
24: * compitem_delete()
25: * compitem_discardsections()
26: * compitem_getcomposite()
27: * compitem_getleftsections()
28: * compitem_getrightsections()
29: * compitem_getleftfile()
30: * compitem_getrightfile()
31: * compitem_getstate()
32: * compitem_gettext_tag()
33: * compitem_gettext_result()
34: * compitem_getfilename()
35: * compitem_frefilename()
36: *
37: * Comments:
38: *
39: * This module uses the structure compitem which is a data type that knows
40: * about two files, and can compare them. The result of the comparison
41: * is a list of sections for each file, and a composite list of sections
42: * representing the comparison of the two files.
43: *
44: * A compitem has a state (one of the integer values defined in state.h)
45: * representing the result of the comparison. It can also be
46: * queried for the text result (text equivalent of the state) as well
47: * as the tag - or title for this compitem (usually a text string containing
48: * the name(s) of the files being compared).
49: *
50: * A compitem will supply a composite section list even if the files are
51: * the same, or if there is only one file. The composite section list will
52: * only be built (and the files read in) when the compitem_getcomposite()
53: * call is made (and not at compitem_new time).
54: *
55: *
56: ****************************************************************************/
57:
58: #include <windows.h>
59: #include <stdlib.h>
60: #include <string.h>
61:
62: #include "gutils.h"
63: #include "state.h"
64: #include "windiff.h"
65: #include "wdiffrc.h"
66: #include "list.h"
67: #include "line.h"
68: #include "scandir.h"
69: #include "file.h"
70: #include "section.h"
71: #include "compitem.h"
72:
73:
74: struct compitem {
75:
76: FILEDATA left; /* handle for left-hand file */
77: FILEDATA right; /* handle for right-hand file */
78:
79: LIST secs_composite; /* list of sections (composite file)*/
80: LIST secs_left; /* list of sections (left file) */
81: LIST secs_right; /* list of sections (right file) */
82:
83: int state; /* compitem state - result of compare */
84: BOOL bDiscard; /* true if not alloc-ed on list */
85: LPSTR tag; /* text for tag (title of compitem) */
86: LPSTR result; /* text equivalent of state */
87:
88: };
89:
90:
91: LPSTR ci_copytext(LPSTR in);
92: void ci_makecomposite(COMPITEM ci);
93: void ci_compare(COMPITEM ci);
94:
95:
96: /***************************************************************************
97: * Function: compitem_new
98: *
99: * Purpose:
100: *
101: * Returns a handle to a new compitem - given the filenames for the
102: * left and right files to be compared. Either left or right or neither
103: * (but not both) may be null. In this case we set the state accordingly.
104: *
105: * The parameters are handles to DIRITEM objects: these allow us to get the
106: * the name of the file relative to the compare roots (needed for the tag)
107: * and the absolute name of the file (needed for opening the file).
108: *
109: * Comments:
110: *
111: * If the list parameter is not null, the List_New* functions are used to
112: * allocate memory for the compitem. We remember this (in the bDiscard flag)
113: * so we do not delete the compitem if it was allocated on the list.
114: *
115: * If the list parameter is null, the memory
116: * for the compitem is allocated from the gmem_* heap initialised by the app.
117: *
118: ****************************************************************************/
119: COMPITEM
120: compitem_new(DIRITEM leftname, DIRITEM rightname, LIST list, BOOL fExact)
121: {
122: COMPITEM ci;
123: LPSTR str1, str2;
124: char buf[2*MAX_PATH+20];
125:
126:
127: /*
128: * Allocate the memory for the compitem, either at the end of the
129: * list or in the gmem_* heap.
130: */
131: if (list == NULL) {
132: /* no list passed */
133: ci = (COMPITEM) gmem_get(hHeap, sizeof(struct compitem));
134: memset(ci, 0, sizeof(struct compitem));
135: ci->bDiscard = TRUE;
136: } else {
137: /* add to end of list */
138: ci = (COMPITEM) List_NewLast(list, sizeof(struct compitem));
139: ci->bDiscard = FALSE;
140: }
141:
142: ci->secs_composite = NULL;
143: ci->secs_left = NULL;
144: ci->secs_right = NULL;
145:
146: /*
147: * Make a filedata for each of the files that are non-null.
148: * Filedata objects are responsible for reading the file and
149: * accessing the lines in it. Don't read in the files until we need to.
150: */
151: if (leftname != NULL) {
152: ci->left = file_new(leftname, FALSE);
153: if (ci->left == NULL) {
154: return(NULL);
155: }
156: } else {
157: ci->left = NULL;
158: }
159: if ( rightname != NULL) {
160: ci->right = file_new(rightname, FALSE);
161: if (ci->right == NULL) {
162: return(NULL);
163: }
164: } else {
165: ci->right = NULL;
166: }
167:
168:
169: /*
170: * See if we have one or two files, and set the state accordingly
171: */
172: if ( ! ci->left && !ci->right) {
173: /* two NULL files - this is wrong */
174: return(NULL);
175: }
176:
177:
178: /* Set the tag (title field) for this item. If the
179: * two files have names that match, we use just that name -
180: * otherwise we use both names separated by a colon 'left : right'.
181: *
182: * In both cases, use the names relative to compare root (the
183: * names will certainly be different if we compare the abs paths)
184: */
185: str1 = dir_getrelname(leftname);
186: str2 = dir_getrelname(rightname);
187:
188: /* If only one file - set name to that */
189: if (ci->left == NULL) {
190: ci->tag = ci_copytext(str2);
191: } else if (ci->right == NULL) {
192: ci->tag = ci_copytext(str1);
193: } else {
194: if (lstrcmpi(str1, str2) == 0) {
195: ci->tag = ci_copytext(str2);
196: } else {
197: wsprintf(buf, "%s : %s", str1, str2);
198: ci->tag = ci_copytext(buf);
199: }
200: }
201:
202: dir_freerelname(leftname, str1);
203: dir_freerelname(leftname, str2);
204:
205:
206: if (ci->left == NULL) {
207: str1 = dir_getroot_item(rightname);
208: wsprintf(buf, "only in %s", str1);
209: dir_freeroot_item(rightname, str1);
210:
211: ci->result = ci_copytext(buf);
212: ci->state = STATE_FILERIGHTONLY;
213: } else if (ci->right == NULL) {
214: str1 = dir_getroot_item(leftname);
215: wsprintf(buf, "only in %s", str1);
216: dir_freeroot_item(leftname, str1);
217:
218: ci->result = ci_copytext(buf);
219: ci->state = STATE_FILELEFTONLY;
220: } else {
221: /* two files - are they the same ? compare
222: * the file sizes
223: */
224:
225:
226: if (dir_getfilesize(leftname) != dir_getfilesize(rightname))
227: {
228: ci->state = STATE_DIFFER;
229: ci->result = ci_copytext("different sizes");
230: } else if (!fExact){
231: ci->result = ci_copytext("same size");
232: ci->state = STATE_SAME;
233: } else {
234: ci->result = ci_copytext("identical");
235: ci->state = STATE_SAME;
236: }
237: }
238:
239:
240: #if FALSE
241: if (dir_getfilesize(leftname) == dir_getfilesize(rightname)) {
242: if ( !fExact )
243: {
244: ci->result = ci_copytext("same size etc.");
245: ci->state = STATE_SAME;
246: } else {
247: ci->result = ci_copytext("files differ");
248: ci->state = STATE_DIFFER;
249: ci->result = ci_copytext("files differ");
250: }
251: } else {
252: ci->result = ci_copytext("files differ");
253: ci->state = STATE_DIFFER;
254: }
255: }
256: #endif
257:
258: /*
259: * Building the section lists and composite lists can wait
260: * until needed.
261: */
262: return(ci);
263: } /* compitem_new */
264:
265: /***************************************************************************
266: * Function: compitem_delete
267: *
268: * Purpose:
269: *
270: * Deletes a compitem and free all associated data.
271: *
272: * Comments:
273: *
274: * If the ci->bDiscard flag is set, the compitem was alloc-ed on a list,
275: * and should not be discarded (the list itself will be deleted).
276: *
277: * The DIRDATA we were passed are not deleted. the filedata, lines
278: * and sections are.
279: ***************************************************************************/
280: void
281: compitem_delete(COMPITEM ci)
282: {
283: if (ci == NULL) {
284: return;
285: }
286:
287: compitem_discardsections(ci);
288:
289: /* Delete the two filedatas (and associated line lists) */
290: file_delete(ci->left);
291: file_delete(ci->right);
292:
293: /* text we allocated */
294: gmem_free(hHeap, ci->tag, lstrlen(ci->tag) + 1);
295: gmem_free(hHeap, ci->result, lstrlen(ci->result) + 1);
296:
297: /* Free the compitem struct itself if not alloced on a list */
298: if (ci->bDiscard) {
299: gmem_free(hHeap, (LPSTR) ci, sizeof(struct compitem));
300: }
301: }
302:
303:
304: /*
305: /***************************************************************************
306: * Function: compitem_discardsections
307: *
308: * Purpose:
309: *
310: * To discard sections - throw away cached information relating to the
311: * comparison (but not the files if they are read into memory). This
312: * is used to force a re-compare if changes in the comparison options
313: * are made
314: *
315: ***************************************************************************/
316: void
317: compitem_discardsections(COMPITEM ci)
318: {
319: /* delete the lists of sections we built */
320: if (ci == NULL) {
321: return;
322: }
323: if (ci->secs_composite) {
324: section_deletelist(ci->secs_composite);
325: ci->secs_composite = NULL;
326: }
327: if (ci->secs_left) {
328: section_deletelist(ci->secs_left);
329: ci->secs_left = NULL;
330: }
331: if (ci->secs_right) {
332: section_deletelist(ci->secs_right);
333: ci->secs_right = NULL;
334: }
335:
336: /* reset the line lists to throw away cached hash codes and links */
337: if (ci->left != NULL) {
338: file_reset(ci->left);
339: }
340: if (ci->right != NULL) {
341: file_reset(ci->right);
342: }
343:
344: }
345:
346: /***************************************************************************
347: * Function: compitem_getcomposite
348: *
349: * Purpose:
350: *
351: * To get the handle for the composite section list
352: *
353: ***************************************************************************/
354: LIST
355: compitem_getcomposite(COMPITEM ci)
356: {
357: if (ci == NULL) {
358: return NULL;
359: }
360: /*
361: * do the comparison if we haven't already done it
362: */
363: if (ci->secs_composite == NULL) {
364: ci_makecomposite(ci);
365: }
366:
367: return(ci->secs_composite);
368: }
369:
370: /***************************************************************************
371: * Function: compitem_getleftsections
372: *
373: * Purpose:
374: *
375: * To get the handle for the list of sections in the left file
376: *
377: ***************************************************************************/
378: LIST
379: compitem_getleftsections(COMPITEM ci)
380: {
381: if (ci == NULL) {
382: return NULL;
383: }
384: /*
385: * do the comparison if we haven't already done it
386: */
387: if (ci->secs_composite == NULL) {
388: ci_makecomposite(ci);
389: }
390:
391: return(ci->secs_left);
392: }
393:
394: /***************************************************************************
395: * Function: compitem_getrightsections
396: *
397: * Purpose:
398: *
399: * To get the handle for the list of sections in the right file
400: *
401: ***************************************************************************/
402: LIST
403: compitem_getrightsections(COMPITEM ci)
404: {
405: if (ci == NULL) {
406: return NULL;
407: }
408: /*
409: * do the comparison if we haven't already done it
410: */
411: if (ci->secs_composite == NULL) {
412: ci_makecomposite(ci);
413: }
414:
415: return(ci->secs_right);
416: }
417:
418: /***************************************************************************
419: * Function: compitem_getleftfile
420: *
421: * Purpose:
422: *
423: * To get the handle to the left file itself
424: *
425: ***************************************************************************/
426: FILEDATA
427: compitem_getleftfile(COMPITEM ci)
428: {
429: if (ci == NULL) {
430: return(NULL);
431: }
432: return(ci->left);
433: }
434:
435: /***************************************************************************
436: * Function: compitem_getrightfile
437: *
438: * Purpose:
439: *
440: * To get the handle to the right file itself
441: *
442: ***************************************************************************/
443: FILEDATA
444: compitem_getrightfile(COMPITEM ci)
445: {
446: if (ci == NULL) {
447: return(NULL);
448: }
449: return(ci->right);
450: }
451:
452: /***************************************************************************
453: * Function: compitem_getstate
454: *
455: * Purpose:
456: *
457: * To get the state (compare result) of this compitem
458: *
459: ***************************************************************************/
460: int
461: compitem_getstate(COMPITEM ci)
462: {
463: if (ci == NULL) {
464: return(0);
465: }
466: return(ci->state);
467: }
468:
469: /***************************************************************************
470: * Function: compitem_gettext_tag
471: *
472: * Purpose:
473: *
474: * To get the tag (text for the compitem title)
475: *
476: ***************************************************************************/
477: LPSTR
478: compitem_gettext_tag(COMPITEM ci)
479: {
480: if (ci == NULL) {
481: return(NULL);
482: }
483: return(ci->tag);
484: }
485:
486: /***************************************************************************
487: * Function: compitem_gettext_result
488: *
489: * Purpose:
490: *
491: * To get the result text (text equiv of state)
492: *
493: ***************************************************************************/
494: LPSTR
495: compitem_gettext_result(COMPITEM ci)
496: {
497: if (ci == NULL) {
498: return(NULL);
499: }
500: return(ci->result);
501: }
502:
503: /***************************************************************************
504: * Function: compitem_getfilename
505: *
506: * Purpose:
507: *
508: * To return the name of the file associated with this compitem. The option
509: * argument (one of CI_LEFT, CI_RIGHT, CI_COMP) indicates which file
510: * is required.
511: *
512: * Comments:
513: *
514: * CI_LEFT and CI_RIGHT just result in calls to dir_getopenname to get
515: * an open-able filename.
516: *
517: * For CI_COMP, we create a temporary file, write out all the text in the
518: * composite section list to this file, and then pass the name of the
519: * temporary file to the caller. This file will be deleted on
520: * the call to compitem_freefilename().
521: *
522: ***************************************************************************/
523: LPSTR
524: compitem_getfilename(COMPITEM item, int option)
525: {
526: LPSTR fname;
527: LINE line;
528: LPSTR tag, text;
529: SECTION sec;
530: OFSTRUCT os;
531: int fh;
532:
533: if (item == NULL) {
534: return(NULL);
535: }
536:
537: switch(option) {
538: case CI_LEFT:
539: if (item->left != NULL) {
540: return(dir_getopenname(file_getdiritem(item->left)));
541: } else {
542: return(NULL);
543: }
544:
545: case CI_RIGHT:
546: if (item->right != NULL) {
547: return(dir_getopenname(file_getdiritem(item->right)));
548: } else {
549: return(NULL);
550: }
551:
552: case CI_COMP:
553:
554: /* caller has asked for the filename of the composite file.
555: * we need to create a temporary file and write the
556: * lines in the composite section list out to it.
557: */
558: fname = gmem_get(hHeap, MAX_PATH);
559: GetTempPath(MAX_PATH, fname);
560: GetTempFileName(fname, "wdf", 0, fname);
561:
562: fh = OpenFile(fname, &os, OF_READWRITE|OF_SHARE_DENY_NONE);
563: if (fh < 0) {
564: MessageBox(NULL, "Cannot open temp file", "Error", MB_OK | MB_ICONSTOP);
565: return(NULL);
566: }
567:
568: /* make sure the composite list has been built */
569:
570: if (item->secs_composite == NULL) {
571: ci_makecomposite(item);
572: }
573:
574: /* write out every line in every section on the composite
575: * list to the temp file.
576: */
577: List_TRAVERSE(item->secs_composite, sec) {
578:
579: /* get the tag field based on the section state*/
580: switch(section_getstate(sec)) {
581: case STATE_SAME:
582: tag = " ";
583: break;
584:
585: case STATE_LEFTONLY:
586: tag = " <! ";
587: break;
588: case STATE_RIGHTONLY:
589: tag = " !> ";
590: break;
591:
592: case STATE_MOVEDLEFT:
593: tag = " <- ";
594: break;
595:
596: case STATE_MOVEDRIGHT:
597: tag = " -> ";
598: break;
599: }
600:
601: /* write out each line in this section.
602: * non-standard traverse of list as we only
603: * want to go from section first to section last
604: * inclusive.
605: */
606: for (line = section_getfirstline(sec);
607: line != NULL;
608: line = List_Next(line) ) {
609:
610: text = line_gettext(line);
611:
612: /* write out to file */
613: _lwrite(fh, tag, lstrlen(tag));
614: _lwrite(fh, text, lstrlen(text));
615:
616: if (line == section_getlastline(sec)) {
617: break;
618: }
619: }
620: }
621:
622: /* now close the file and return its name */
623: _lclose(fh);
624: return(fname);
625:
626:
627: default:
628: MessageBox(NULL, "Bad argument", "Error", MB_OK | MB_ICONSTOP);
629: return(NULL);
630: }
631: }
632:
633: /***************************************************************************
634: * Function: compitem_freefilename
635: *
636: * Purpose:
637: *
638: * Free memory created by a call to compitem_getfilename. If a temporary
639: * file was created, this may cause it to be deleted. The option argument must
640: * be the same as passed to the original compitem_getfilename call.
641: *
642: * If we created a temporary file for CI_COMP, then delete it; otherwise,
643: * just pass the name to dir_freeopenname.
644: *
645: ***************************************************************************/
646: void
647: compitem_freefilename(COMPITEM item, int option, LPSTR filename)
648: {
649: OFSTRUCT os;
650:
651:
652: if ((item == NULL) || (filename == NULL)) {
653: return;
654: }
655:
656: switch(option) {
657:
658: case CI_LEFT:
659: dir_freeopenname(file_getdiritem(item->left), filename);
660: break;
661:
662: case CI_RIGHT:
663: dir_freeopenname(file_getdiritem(item->right), filename);
664: break;
665:
666: case CI_COMP:
667:
668: /* this is a temporary file we created. Delete it. */
669: OpenFile(filename, &os, OF_DELETE);
670:
671: gmem_free(hHeap, filename, MAX_PATH);
672: break;
673: }
674: }
675:
676:
677: /***************************************************************************
678: * Function: ci_copytext
679: *
680: * Purpose:
681: *
682: * To alloc a buffer large enough for the text string and copy the text into
683: * it and return a pointer to the string.
684: *
685: ***************************************************************************/
686: LPSTR
687: ci_copytext(LPSTR in)
688: {
689: LPSTR out;
690:
691: if (in == NULL) {
692: out = gmem_get(hHeap, 1);
693: out[0] = '\0';
694: } else {
695: out = gmem_get(hHeap, lstrlen(in) + 1);
696: lstrcpy(out, in);
697: }
698: return(out);
699: }
700:
701: /***************************************************************************
702: * Function: ci_onesection
703: *
704: * Purpose:
705: *
706: * To make a list containing a single section from the whole list of lines
707: *
708: ***************************************************************************/
709: LIST
710: ci_onesection(FILEDATA file)
711: {
712: LIST lines;
713: LIST sections;
714: SECTION section;
715:
716: lines = file_getlinelist(file);
717:
718: /* create a null list */
719: sections = List_Create();
720:
721: /* tell the section to create itself on the end of this list. */
722: section = section_new(List_First(lines), List_Last(lines), sections);
723: section_setstate(section, STATE_SAME);
724:
725:
726: return(sections);
727: }
728:
729:
730:
731: /***************************************************************************
732: * Function: ci_makecomposite
733: *
734: * Purpose:
735: *
736: * Compare the two files and build the composite list. This function is
737: * called whenever we need one of the section lists and only does the
738: * comparison if the composite list does not already exist.
739: *
740: ***************************************************************************/
741: void
742: ci_makecomposite(COMPITEM ci)
743: {
744: if (ci->secs_composite != NULL) {
745: return;
746: }
747:
748: /* if there is only one file, make a single item list
749: * of sections
750: */
751: if (ci->left == NULL) {
752: ci->secs_left = NULL;
753: ci->secs_right = ci_onesection(ci->right);
754:
755: /* make a second list, not a pointer to the first
756: * or we will get confused when deleting
757: */
758: ci->secs_composite = ci_onesection(ci->right);
759: return;
760: } else if (ci->right == NULL) {
761: ci->secs_right = NULL;
762: ci->secs_left = ci_onesection(ci->left);
763:
764: /* make a second list, not a pointer to the first
765: * or we will get confused when deleting
766: */
767: ci->secs_composite = ci_onesection(ci->left);
768: return;
769: }
770:
771: /* we have two files - we need to compare them fully */
772: ci_compare(ci);
773: }
774:
775: /***************************************************************************
776: * Function: ci_compare
777: *
778: * Purpose:
779: *
780: * Compare files and build a composite list.
781: *
782: * Comments:
783: *
784: * Comparison method:
785: *
786: * 0 Break each file into lines and hash each line. Lines which
787: * don't match can be rapidly eliminated by comparing the hash code.
788: *
789: * Store the hash codes in a binary search tree that
790: * will give for each hash code the number of times that it
791: * occurred in each file and one of the lines where it occurred
792: * in each file. The tree is used to rapidly find the partner
793: * of a line which occurs exactly once in each file.
794: *
795: * 1 Make a section covering the whole file (for both)
796: * and link unique lines between these sections (i.e. link lines
797: * which occur exactly once in each file as they must match each other).
798: * These are referred to as anchor points.
799: *
800: * 2 Build section lists for both files by traversing line lists and
801: * making a section for each set of adjacent lines that are unmatched
802: * and for each set of adjacent lines that match a set of adjacent
803: * lines in the other file. In making a section we start from a
804: * known matching line and work both forwards and backwards through
805: * the file including lines which match, whether they are unique or not.
806: *
807: * 3 Establish links between sections that match
808: * and also between sections that don't match but do
809: * correspond (by position in file between matching sections)
810: *
811: * 4 For each section pair that don't match but do correspond,
812: * link up matching lines unique within that section. (i.e. do
813: * the whole algorithm again on just this section).
814: *
815: * There may be some lines which occur many times over in each file.
816: * As these occurrences are matched up, so the number left to match
817: * reduces, and may reach one in each file. At this point these two
818: * can be matched. Therefore we...
819: *
820: * Repeat steps 0-4 until no more new links are added, but (especially
821: * in step 0) we only bother with lines which have not yet been matched.
822: * This means that a line which has only one unmatched instance in each
823: * file gets a count of one and so is a new anchor point.
824: *
825: * Finally build a composite list from the two lists of sections.
826: *
827: ***************************************************************************/
828: void
829: ci_compare(COMPITEM ci)
830: {
831: LIST lines_left, lines_right;
832: SECTION whole_left, whole_right;
833: BOOL bChanges;
834:
835: /* get the list of lines for each file */
836: lines_left = file_getlinelist(ci->left);
837: lines_right = file_getlinelist(ci->right);
838:
839: if ((lines_left == NULL) || (lines_right == NULL)) {
840: ci->secs_left = NULL;
841: ci->secs_right = NULL;
842: ci->secs_composite = NULL;
843: return;
844: }
845:
846: do {
847:
848: /* we have made no changes so far this time round the
849: * loop
850: */
851: bChanges = FALSE;
852:
853: /* make a section covering the whole file */
854: whole_left = section_new(List_First(lines_left),
855: List_Last(lines_left), NULL);
856:
857: whole_right = section_new(List_First(lines_right),
858: List_Last(lines_right), NULL);
859:
860: /* link up matching unique lines between these sections */
861: if (section_match(whole_left, whole_right)) {
862: bChanges = TRUE;
863: }
864:
865: /* delete the two temp sections */
866: section_delete(whole_left);
867: section_delete(whole_right);
868:
869: /* discard previous section lists if made */
870: if (ci->secs_left) {
871: section_deletelist(ci->secs_left);
872: ci->secs_left = NULL;
873: }
874: if (ci->secs_right) {
875: section_deletelist(ci->secs_right);
876: ci->secs_right = NULL;
877: }
878: /* build new section lists for both files */
879: ci->secs_left = section_makelist(lines_left, TRUE);
880: ci->secs_right = section_makelist(lines_right, FALSE);
881:
882: /* match up sections - make links and corresponds between
883: * sections. Attempts to section_match corresponding
884: * sections that are not matched. returns true if any
885: * further links were made
886: */
887: if (section_matchlists(ci->secs_left, ci->secs_right)) {
888: bChanges = TRUE;
889: }
890:
891: /* repeat as long as we keep adding new links */
892:
893: } while (bChanges);
894:
895: /* all possible lines linked, and section lists made .
896: * combine the two section lists to get a view of the
897: * whole comparison - the composite section list. This also
898: * sets the state of each section in the composite list.
899: */
900: ci->secs_composite = section_makecomposite(ci->secs_left, ci->secs_right);
901:
902: }
903:
904:
905:
906:
907:
908:
909:
910:
911:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.