home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / sdktools / windiff / section.c < prev    next >
C/C++ Source or Header  |  1997-10-05  |  42KB  |  1,191 lines

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