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

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