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