SECTION.C
上传用户:bangxh
上传日期:2007-01-31
资源大小:42235k
文件大小:42k
源码类别:

Windows编程

开发平台:

Visual C++

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