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

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: WINDIFF.C
  12. *
  13. * File and directory comparisions.
  14. *
  15. * Functions:
  16. *
  17. * windiff_UI()
  18. * WinMain()
  19. * windiff_usage()
  20. * Poll()
  21. * DoResize()
  22. * AboutBox()
  23. * DoPrint()
  24. * FindNextChange()
  25. * FindPrevChange()
  26. * WriteProfileInt()
  27. * ToOutline()
  28. * ToMoved()
  29. * do_editfile()
  30. * do_editthread()
  31. * SetStatus()
  32. * SetNames()
  33. * IsBusy()
  34. * BusyError()
  35. * StateToColour()
  36. * SetSelection()
  37. * do_gethdr()
  38. * do_getprops()
  39. * do_getdata()
  40. * SvrClose()
  41. * TableServer()
  42. * wd_dirdialog()
  43. * wd_copy()
  44. * InitApplication()
  45. * InitInstance()
  46. * CreateTools()
  47. * DeleteTools()
  48. * MainWndProc()
  49. * SetBusy()
  50. * SetNotBusy()
  51. * SetSelection()
  52. * SetButtonText()
  53. * ToExpand()
  54. * ParseArgs()
  55. * wd_initial()
  56. *
  57. * Comments:
  58. *
  59. * Compare two directories (including all files and subdirs). Look for names
  60. * that are present in both (report all that are not). For files that
  61. * are present in both, produce a line-by-line comparison of the differences
  62. * between the two files (if any).
  63. *
  64. * Overview of Windiff internals - the whole program.
  65. *
  66. * Windiff is built from several modules (a "module" has a .h file
  67. * which describes its interface and a .c file which implements it).
  68. * Apart from THIS comment which tries to give an overview of the whole
  69. * scheme of things, each module is as self-contained as possible.
  70. * This is enforced by the use of opaque data types.  Modules cannot
  71. * see each others' internal data structures.  Modules are abstract
  72. * data types.  The term "Module" (from Modula2) and "Class" (from C++)
  73. * are used synonymously.
  74. *
  75. *    Windiff  - main program - parse arguments, put up main window,
  76. *               handle input, calling other modules as needed
  77. *               invoke table class to create the main display and
  78. *               service callbacks from the table class.
  79. *               Contains global flags for options (e.g. ignore_blanks)
  80. *    list     - (in gutils) a generalised LIST of anything data type
  81. *               has full set of operations for insert, delete, join etc.
  82. *    line     - a LINE is a numbered line of text.  Information is kept to
  83. *               allow fast comparisons of LINEs.  A LINE can hold a
  84. *               link to another LINE.  The links are used to connect
  85. *               lines in one file to matching lines in the other file.
  86. *    file     - a FILEDATA represents a file as a file name in the form
  87. *               of a DIRITEM and a LIST of LINEs
  88. *    scandir  - a DIRITEM represents information about a file.  (for
  89. *               instance its name, whether it has a local copy).
  90. *    compitem - a COMPITEM is a pair of files together with information
  91. *               on how they compare in the form of a breakdown of the
  92. *               files into a LIST of matching or non-matching sections.
  93. *               Either file can be absent.  This module contains the
  94. *               file "contrast" algorithm used for the actual comparison
  95. *    tree       (in gutils) A binary tree.  Important because it is what
  96. *               gives the file comparison its speed as it makes it
  97. *               an "N log N" algorithm rather than "N squared"
  98. *    complist - a COMPLIST is the master data structure.  It has a DIRLIST
  99. *               of the left hand files, a DIRLIST of the right hand files
  100. *               and a LIST of COMPITEMs. The left and right hand DIRLISTs
  101. *               are working data used to produce the COMPLIST.  The LIST
  102. *               is displayed as the outline table.  Any given COMPITEM can
  103. *               be displayed as an expanded item.
  104. *    section  - a SECTION is a section of a file (first line, last line)
  105. *               and information as to what it matches in the other file.
  106. *    bar.c    - the picture down the left of the screen
  107. *               has a WNDPROC.  
  108. *    view     - Although the COMPLIST is the master state, it doesn't do
  109. *               all the work itself.  The data is actually displayed by
  110. *               the table class which is highly generalised.  View
  111. *               owns a COMPLIST (and therefore calls upon the functions
  112. *               in complist to fill it and interrogate it) and calls
  113. *               upon (and is called back by) the functions in table to
  114. *               actually display it.  Read about table in gutils.h
  115. *    table.c    (in gutils) a highly generalised system for displaying
  116. *               data in rows and columns.  The interface is in gutils.h.
  117. *    status.c   (in gutils) the status line at the top. See gutils.h
  118. *************************************************************************
  119. *
  120. * Overview of this file:
  121. *
  122. *   We create a table window (gutils.dll) to show the files and the
  123. *   results of their comparisons. We create a COMPLIST object representing
  124. *   a list of files and their differences, and a VIEW object to map between
  125. *   the rows of the table window and the COMPLIST.
  126. *
  127. *   This module is responsible for creating and managing the main window,
  128. *   placing the child windows (table, status window etc) within it, and
  129. *   handling all menu items. We maintain global option flags set by
  130. *   menu commands.
  131. *
  132. *   Creating a COMPLIST creates a list of unmatched files, and of matching
  133. *   files that are compared with each other (these are COMPITEMS).
  134. *   The VIEW provides a mapping between rows on the screen, and items in
  135. *   the COMPLIST.
  136. *
  137. *   This version tries to maintain a responsive user interface by
  138. *   creating worker threads to do long jobs.  This potentially creates
  139. *   conflicts between the threads as they will both want to update common
  140. *   variables (for instance the UI thread may be changing the options to
  141. *   exclude identical files while the worker thread is adding in the
  142. *   results of new comparisons).  Critical sections are used to manage
  143. *   the conflicts.
  144. *
  145. *   The Edit options invoke an editor on a separate thread.  This allows
  146. *   us to repaint our window and thereby allow the user to refer back to
  147. *   what he saw before invoking the editor.  When he's finished editing,
  148. *   we would of course like to refresh things and if this is still on the
  149. *   separate thread it might clash. We avoid this clash by POSTing ourselves
  150. *   a (WM_COMMAND, IDM_UPDATE) message.
  151. *
  152. ****************************************************************************/
  153. #include <windows.h>
  154. #include <shellapi.h>
  155. #include <stdlib.h>
  156. #include <commdlg.h>
  157. #include <string.h>
  158. #include "gutils.h"
  159. #include "table.h"
  160. #include "list.h"
  161. #include "scandir.h"            /* needed for file.h     */
  162. #include "file.h"               /* needed for compitem.h */
  163. #include "compitem.h"           /* needed for view.h     */
  164. #include "complist.h"
  165. #include "view.h"
  166. #include "state.h"
  167. #include "windiff.h"
  168. #include "wdiffrc.h"
  169. /*--constants and data types--------------------------------------------*/
  170. int Version = 2;
  171. int SubVersion = 01;
  172. /* When we print the current table, we pass this id as the table id
  173.  * When we are queried for the properties of this table, we know they
  174.  * want the printing properties for the current view. We use this to
  175.  * select different fonts and colours for the printer.
  176.  */
  177. #define TABID_PRINTER   1
  178. /*
  179.  * structure containing args passed to worker thread in initial
  180.  * case (executing command line instructions). 
  181.  */
  182. typedef struct {
  183.         LPSTR first;
  184.         LPSTR second;
  185.         LPSTR savelist;
  186.         UINT saveopts;
  187.         VIEW view;
  188.         BOOL fDeep;
  189. } THREADARGS, FAR * PTHREADARGS;
  190. /* Structure containing all the arguments we'd like to give to do_editfile
  191.    Need a structure because CreateThread only allows for one argument.
  192. */
  193. typedef struct {
  194.         VIEW view;
  195.         int option;
  196.         int selection;
  197. } EDITARGS, FAR * PEDITARGS;
  198. /*---- colour scheme------------------------------- */
  199. /* outline */
  200. DWORD rgb_outlinehi = RGB(255, 0, 0);   /* hilighted files in outline mode  */
  201. /* expand view */
  202. DWORD rgb_leftfore =   RGB(  0,   0,   0);         /* foregrnd for left lines */
  203. DWORD rgb_leftback  =  RGB(255,   0,   0);         /* backgrnd for left lines */
  204. DWORD rgb_rightfore =  RGB(  0,   0,   0);         /* foregrnd for right lines*/
  205. DWORD rgb_rightback =  RGB(255, 255,   0);         /* backgrnd for right lines*/
  206. /* moved lines */
  207. DWORD rgb_mleftfore =  RGB(  0,   0, 128);         /* foregrnd for moved-left */
  208. DWORD rgb_mleftback =  RGB(255,   0,   0);         /* backgrnd for moved-left */
  209. DWORD rgb_mrightfore = RGB(  0,   0, 255);         /* foregrnd for moved-right*/
  210. DWORD rgb_mrightback = RGB(255, 255,   0);         /* backgrnd for moved-right*/
  211. /* bar window */
  212. DWORD rgb_barleft =    RGB(255,   0,   0);         /* bar sections in left only  */
  213. DWORD rgb_barright =   RGB(255, 255,   0);         /* bar sections in right only */
  214. DWORD rgb_barcurrent = RGB(  0,   0, 255);         /* current pos markers in bar */
  215. /* module static data -------------------------------------------------*/
  216. /* current value of window title */
  217. char AppTitle[256];
  218. HWND hwndClient;        /* main window */
  219. HWND hwndRCD;           /* table window */
  220. HWND hwndStatus;        /* status bar across top */
  221. HWND hwndBar;           /* graphic of sections as vertical bars */
  222. HACCEL haccel;
  223. /* The status bar told us it should be this high. Rest of client area
  224.  * goes to the hwndBar and hwndRCD.
  225.  */
  226. int status_height;
  227. HINSTANCE hInst;   /* handle to current app instance */
  228. HMENU hMenu;    /* handle to menu for hwndClient */
  229. int nMinMax = SW_SHOWNORMAL;         /* default state of window normal */
  230. /* The message sent to us as a callback by the table window needs to be
  231.  * registered - table_msgcode is the result of the RegisterMessage call
  232.  */
  233. UINT table_msgcode;
  234. /* True if we are currently doing some scan or comparison.
  235.  * Must get critical section before checking/changing this (call
  236.  * SetBusy.
  237.  */
  238. BOOL fBusy = FALSE;
  239. int     selection       =       -1;     /* selected row in table*/
  240. /* Options for DisplayMode field indicating what is currently shown.
  241.  * We use this to know whether or not to show the graphic bar window.
  242.  */
  243. #define MODE_NULL       0       /* nothing displayed */
  244. #define MODE_OUTLINE    1       /* a list of files displayed */
  245. #define MODE_EXPAND     2       /* view is expanded view of one file */
  246. int DisplayMode = MODE_NULL;    /* indicates whether we are in expand mode */
  247. VIEW current_view = NULL;
  248. /* command line parameters */
  249. extern int __argc;
  250. extern char ** __argv;
  251. BOOL bAbort = FALSE;    /* set to request abort of current operation */
  252. char editor_cmdline[256] = "notepad %p";  /* editor cmdline */
  253.                           /* slick version is "s %p -#%l" */
  254. /* app-wide global data --------------------------------------------- */
  255. /* Handle returned from gmem_init - we use this for all memory allocations */
  256. HANDLE hHeap;
  257. /* Current state of menu options */
  258. int line_numbers = IDM_LNRS;
  259. int expand_mode = IDM_BOTHFILES;
  260. int outline_include = INCLUDE_LEFTONLY|INCLUDE_RIGHTONLY|INCLUDE_SAME|INCLUDE_DIFFER;
  261. BOOL ignore_blanks = TRUE;
  262. BOOL picture_mode = TRUE;
  263. /* function prototypes ---------------------------------------------*/
  264. BOOL InitApplication(HINSTANCE hInstance);
  265. BOOL InitInstance(HINSTANCE hInstance, int nCmdShow);
  266. void CreateTools(void);
  267. void DeleteTools(void);
  268. long APIENTRY MainWndProc(HWND hWnd, UINT message, UINT wParam, LONG lParam);
  269. BOOL SetBusy(void);
  270. void SetNotBusy(void);
  271. void SetSelection(long rownr);
  272. void SetButtonText(LPSTR cmd);
  273. BOOL ToExpand(HWND hwnd);
  274. void ParseArgs(int argc, char ** argv);
  275. DWORD wd_initial(LPVOID arg);
  276. static HANDLE ghThread = NULL;
  277. static DWORD gdwMainThreadId;     /* threadid of main (user interface) thread
  278.                                      initialised in winmain(), thereafter constant.
  279.                                      See windiff_UI()
  280.                                   */
  281. /***************************************************************************
  282.  * Function: windiff_UI
  283.  *
  284.  * Purpose:
  285.  *
  286.  * If you are about to put up a dialog box or in fact process input in any way
  287.  * on any thread other than the main thread - or if you MIGHT be on a thread other
  288.  * than the main thread, then you must call this function with TRUE before doing
  289.  * it and with FALSE immediately afterwards.  Otherwise you will get one of a
  290.  * number of flavours of not-very-responsiveness
  291.  */
  292. void windiff_UI(BOOL bAttach)
  293. {
  294.         DWORD dwThreadId = GetCurrentThreadId();
  295.         if (dwThreadId==gdwMainThreadId) return;
  296.         if (bAttach) GetDesktopWindow();
  297.         AttachThreadInput(dwThreadId, gdwMainThreadId, bAttach);
  298. } /* windiff_UI */
  299. /***************************************************************************
  300.  * Function: WinMain
  301.  *
  302.  * Purpose:
  303.  *
  304.  * Main entry point. Register window classes, create windows,
  305.  * parse command line arguments and then perform a message loop
  306.  */
  307. int WINAPI
  308. WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
  309. {
  310.         MSG msg;
  311.         gdwMainThreadId = GetCurrentThreadId();
  312.         /* create any pens/brushes etc and read in profile defaults */
  313.         CreateTools();
  314.         /* init window class unless other instances running */
  315.         if (!hPrevInstance)
  316.             if (!InitApplication(hInstance))
  317.                 return(FALSE);
  318.         /* init this instance - create all the windows */
  319.         if (!InitInstance(hInstance, nCmdShow))
  320.             return(FALSE);
  321.         ParseArgs(__argc, __argv);
  322.         /* message loop */
  323.         while(GetMessage(&msg, NULL, 0, 0)) {
  324.                 if (!TranslateAccelerator(hwndClient, haccel, &msg)) {
  325.                     TranslateMessage(&msg);
  326.                     DispatchMessage(&msg);
  327.                 }
  328.         }
  329.         return (msg.wParam);
  330. }
  331. /***************************************************************************
  332.  * Function: InitApplication
  333.  *
  334.  * Purpose:
  335.  *
  336.  * Register window class for the main window and the bar window.
  337.  */
  338. BOOL
  339. InitApplication(HINSTANCE hInstance)
  340. {
  341.         WNDCLASS    wc;
  342.         BOOL resp;
  343.         /* register the bar window class */
  344.         InitBarClass(hInstance);
  345.         wc.style = 0;
  346.         wc.lpfnWndProc = MainWndProc;
  347.         wc.cbClsExtra = 0;
  348.         wc.cbWndExtra = 0;
  349.         wc.hInstance = hInstance;
  350.         wc.hIcon = LoadIcon(hInstance, "WinDiff");
  351.         wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  352.         wc.hbrBackground = NULL;
  353.         wc.lpszClassName =  "WinDiffViewerClass";
  354.         wc.lpszMenuName = NULL;
  355.         resp = RegisterClass(&wc);
  356.         return(resp);
  357. }
  358. /***************************************************************************
  359.  * Function: InitInstance
  360.  *
  361.  * Purpose:
  362.  *
  363.  * Create and show the windows
  364.  */
  365. BOOL
  366. InitInstance(HINSTANCE hInstance, int nCmdShow)
  367. {
  368.         RECT rect;
  369.         HANDLE hstatus;
  370.         int bar_width;
  371.         RECT childrc;
  372.         hInst = hInstance;
  373.         /* initialise a heap. we use this one heap throughout
  374.          * the app. for all memory requirements
  375.          */
  376.         hHeap = gmem_init();
  377.         /* initialise the list package */
  378.         List_Init();
  379.         hMenu = LoadMenu(hInstance, "WinDiffMenu");
  380.         haccel = LoadAccelerators(hInstance, "WinDiffAccel");
  381.         /* create the main window */
  382.         hwndClient = CreateWindow("WinDiffViewerClass",
  383.                             "WinDiff",
  384.                             WS_OVERLAPPEDWINDOW,
  385.                             CW_USEDEFAULT,
  386.                             CW_USEDEFAULT,
  387.                             CW_USEDEFAULT,
  388.                             CW_USEDEFAULT,
  389.                             NULL,
  390.                             hMenu,
  391.                             hInstance,
  392.                             NULL
  393.                 );
  394.         if (!hwndClient) {
  395.             return(FALSE);
  396.         }
  397.         /* create 3 child windows, one status, one table and one bar
  398.          * Initially, the bar window is hidden and covered by the table.
  399.          */
  400.         /* create a status bar window as
  401.          * a child of the main window.
  402.          */
  403.         /* build a status struct for two labels and an abort button */
  404.         hstatus = StatusAlloc(3);
  405.         StatusAddItem(hstatus, 0, SF_STATIC, SF_LEFT|SF_VAR|SF_SZMIN, IDL_STATLAB, 14, NULL);
  406.         StatusAddItem(hstatus, 1, SF_BUTTON, SF_RIGHT|SF_RAISE, IDM_ABORT, 8,
  407.                 LoadRcString(IDS_EXIT));
  408.         StatusAddItem(hstatus, 2, SF_STATIC, SF_LOWER|SF_LEFT|SF_VAR,
  409.                         IDL_NAMES, 60, NULL);
  410.         /* ask the status bar how high it should be for the controls
  411.          * we have chosen, and save this value for re-sizing.
  412.          */
  413.         status_height = StatusHeight(hstatus);
  414.         /* create a window of this height */
  415.         GetClientRect(hwndClient, &rect);
  416.         childrc = rect;
  417.         childrc.bottom = status_height;
  418.         hwndStatus = StatusCreate(hInst, hwndClient, IDC_STATUS, &childrc,
  419.                         hstatus);
  420.         /* layout constants are stated as percentages of the window width */
  421.         bar_width = (rect.right - rect.left) * BAR_WIN_WIDTH / 100;
  422.         /* create the table class covering all the remaining part of
  423.          * the main window
  424.          */
  425.         hwndRCD = CreateWindow(TableClassName,
  426.                         NULL,
  427.                         WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL,
  428.                         0,
  429.                         status_height,
  430.                         (int)(rect.right - rect.left),
  431.                         (int)(rect.bottom - status_height),
  432.                         hwndClient,
  433.                         (HANDLE) IDC_RCDISP1,
  434.                         hInst,
  435.                         NULL);
  436.         /* create a bar window as a child of the main window.
  437.          * this window remains hidden until we switch into MODE_EXPAND
  438.          */
  439.         hwndBar = CreateWindow("BarClass",
  440.                         NULL,
  441.                         WS_CHILD | WS_VISIBLE,
  442.                         0,
  443.                         status_height,
  444.                         bar_width,
  445.                         (int)(rect.bottom - status_height),
  446.                         hwndClient,
  447.                         (HANDLE) IDC_BAR,
  448.                         hInst,
  449.                         NULL);
  450.         /* nMinMax indicates whether we are to be minimised on startup,
  451.          * on command line parameters
  452.          */
  453.         ShowWindow(hwndBar, SW_HIDE);
  454.         if (GetProfileInt(APPNAME, "OutlineSaved", 0))
  455.         {
  456.                 WINDOWPLACEMENT wp;
  457.                 /* restore the previous expanded size and position */
  458.                 wp.length                  = sizeof( WINDOWPLACEMENT );
  459.                 wp.flags                   = 0;
  460.                 wp.showCmd                 = GetProfileInt( APPNAME, "OutlineShowCmd",
  461.                                                             SW_SHOWNORMAL);
  462.                 wp.ptMaxPosition.x         = GetProfileInt( APPNAME, "OutlineMaxX",       0);
  463.                 wp.ptMaxPosition.y         = GetProfileInt( APPNAME, "OutlineMaxY",       0);
  464.                 wp.rcNormalPosition.left   = (int)GetProfileInt( APPNAME, "OutlineNormLeft",  (UINT)(-1));
  465.                 wp.rcNormalPosition.top    = (int)GetProfileInt( APPNAME, "OutlineNormTop",   (UINT)(-1));
  466.                 wp.rcNormalPosition.right  = (int)GetProfileInt( APPNAME, "OutlineNormRight", (UINT)(-1));
  467.                 wp.rcNormalPosition.bottom = (int)GetProfileInt( APPNAME, "OutlineNormBottom",(UINT)(-1));
  468.                 SetWindowPlacement(hwndClient,&wp);
  469.         }
  470.         else ShowWindow(hwndClient, nMinMax);
  471.         UpdateWindow(hwndClient);
  472.         /* initialise busy flag and status line to show we are idle
  473.          * (ie not comparing or scanning)
  474.          */
  475.         SetNotBusy();
  476.         return(TRUE);
  477. } /* InitInstance */
  478. /***************************************************************************
  479.  * Function: windiff_usage
  480.  *
  481.  * Purpose:
  482.  *
  483.  * Complain to command line users about poor syntax,
  484.  * will be replaced by proper help file.
  485.  */
  486. void
  487. windiff_usage(LPSTR msg)
  488. {
  489.         int retval;
  490.         TCHAR szBuf[MAX_PATH];
  491.         if (msg==NULL)
  492.                 msg = LoadRcString(IDS_USAGE_STR);
  493.         LoadString(hInst, IDS_WINDIFF_USAGE, szBuf, sizeof(szBuf));
  494.         retval = MessageBox(hwndClient,
  495.                 msg,
  496.                 szBuf, MB_ICONINFORMATION|MB_OKCANCEL);
  497.         if (retval == IDCANCEL) {
  498.                 exit(1);
  499.         }
  500. }
  501. /***************************************************************************
  502.  * Function: ParseArgs
  503.  *
  504.  * Purpose:
  505.  *
  506.  * Parse command line arguments
  507.  *
  508.  * The user can give one or two paths. If only one, we assume the second
  509.  * is '.' for the current directory. If one of the two paths is a directory
  510.  * and the other a file, we compare a file of the same name in the two dirs.
  511.  *
  512.  * The command -s filename causes the outline list to be written to a file
  513.  * and then the program exits. -s{slrd} filename allows selection of which
  514.  * files are written out; by default, we assume -sld for files left and different.
  515.  *
  516.  * -T means tree.  Go deep.
  517.  *
  518.  * The default is Deep, -L overrides and implies shallow, -T overrides -L
  519.  */
  520. void
  521. ParseArgs(int argc, char ** argv)
  522. {
  523.         int i;
  524.         LPSTR chp;
  525.         PTHREADARGS ta;
  526.         DWORD threadid;
  527.         /* thread args can't be on the stack since the stack will change
  528.          * before the thread completes execution
  529.          */
  530.         ta = (PTHREADARGS) gmem_get(hHeap, sizeof(THREADARGS));
  531.         ta->first = NULL;
  532.         ta->second = NULL;
  533.         ta->savelist = NULL;
  534.         ta->saveopts = 0;
  535.         ta->fDeep = FALSE;  /* No -T option seen yet */
  536.         for (i = 1; i < argc; i++) {
  537.                 /* is this an option ? */
  538.                 if ((argv[i][0] == '-') || (argv[i][0] == '/')) {
  539.                         switch(argv[i][1]) {
  540.                         case 's':
  541.                         case 'S':
  542.                                 /* read letters for the save option: s,l,r,d */
  543.                                 for(chp = &argv[i][2]; *chp != ''; chp++) {
  544.                                         switch(*chp) {
  545.                                         case 's':
  546.                                         case 'S':
  547.                                                 ta->saveopts |= INCLUDE_SAME;
  548.                                                 break;
  549.                                         case 'l':
  550.                                         case 'L':
  551.                                                 ta->saveopts |= INCLUDE_LEFTONLY;
  552.                                                 break;
  553.                                         case 'r':
  554.                                         case 'R':
  555.                                                 ta->saveopts |= INCLUDE_RIGHTONLY;
  556.                                                 break;
  557.                                         case 'd':
  558.                                         case 'D':
  559.                                                 ta->saveopts |= INCLUDE_DIFFER;
  560.                                                 break;
  561.                                         default:
  562.                                                 windiff_usage(NULL);
  563.                                                 return;
  564.                                         }
  565.                                 }
  566.                                 if (ta->saveopts == 0) {
  567.                                         /* default to left and differ */
  568.                                         ta->saveopts = (INCLUDE_LEFTONLY) | (INCLUDE_DIFFER);
  569.                                 }
  570.                                 ta->savelist = argv[++i];
  571.                                 break;
  572.                         case 't':
  573.                         case 'T':
  574.                                 ta->fDeep = TRUE;
  575.                                 break;
  576.                         default:
  577.                                 windiff_usage(NULL);
  578.                                 return;
  579.                         }
  580.                 } else {
  581.                         if (ta->first == NULL) {
  582.                                 ta->first = argv[i];
  583.                         } else {
  584.                                 ta->second = argv[i];
  585.                         }
  586.                 }
  587.         }
  588.         /* set the correct depth */
  589.         if (ta->fDeep)
  590.                 ;                       /* explicitly set -- leave it alone */
  591.         else ta->fDeep = TRUE;          /* global default */
  592.         /* any paths to scan ? */
  593.         if (ta->first == NULL) {
  594.                 return;
  595.         }
  596.         if (ta->second == NULL) {
  597.                 ta->second = ".";
  598.         }
  599.         SetBusy();
  600.         /* minimise the window if -s flag given */
  601.         if (ta->savelist != NULL) {
  602.                 ShowWindow(hwndClient, SW_MINIMIZE);
  603.         }
  604.         /* make an empty view */
  605.         current_view = view_new(hwndRCD);
  606.         DisplayMode = MODE_OUTLINE;
  607.         ta->view = current_view;
  608.         /* attempt to create a worker thread */
  609.         ghThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)wd_initial, (LPVOID) ta,
  610.                         0, &threadid);
  611.         if (ghThread == NULL)
  612.         {
  613.                 wd_initial( (LPVOID) ta);
  614.         }
  615. } /* ParseArgs */
  616. /***************************************************************************
  617.  * Function: CreateTools
  618.  *
  619.  * Purpose:
  620.  *
  621.  * Create any pens/brushes, and read defaults
  622.  * from the profile file for menu settings etc.
  623.  */
  624. void
  625. CreateTools(void)
  626. {
  627.         /* standard message that table class sends us for
  628.          * notifications and queries.
  629.          */
  630.         table_msgcode = RegisterWindowMessage(TableMessage);
  631.         line_numbers = GetProfileInt(APPNAME, "LineNumbers", line_numbers);
  632.         outline_include = GetProfileInt(APPNAME, "FileInclude", outline_include);
  633.         ignore_blanks = GetProfileInt(APPNAME, "Blanks", ignore_blanks);
  634.         picture_mode = GetProfileInt(APPNAME, "Picture", picture_mode);
  635.         GetProfileString(APPNAME, "Editor", editor_cmdline, (LPTSTR)editor_cmdline,
  636.                         sizeof(editor_cmdline));
  637.         InitializeCriticalSection(&CSWindiff);
  638. }
  639. /***************************************************************************
  640.  * Function: DeleteTools
  641.  *
  642.  * Purpose:
  643.  *
  644.  * Delete any pens or brushes that were created in CreateTools 
  645.  */
  646. void
  647. DeleteTools(void)
  648. {
  649.         DeleteCriticalSection(&CSWindiff);
  650. }
  651. /***************************************************************************
  652.  * Function:
  653.  *
  654.  * Purpose:
  655.  *
  656.  * Check whether we have had an abort request (IDM_ABORT), and
  657.  * return TRUE if abort requested, otherwise FALSE
  658.  */
  659. BOOL
  660. Poll(void)
  661. {
  662.     return(bAbort);
  663. }
  664. /***************************************************************************
  665.  * Function: DoResize
  666.  *
  667.  * Purpose:
  668.  *
  669.  * Position child windows on a resize of the main window
  670.  */
  671. void
  672. DoResize(HWND hWnd)
  673. {
  674.         RECT rc;
  675.         int bar_width;
  676.         GetClientRect(hWnd, &rc);
  677.         MoveWindow(hwndStatus, 0, 0, rc.right - rc.left, status_height, TRUE);
  678.         bar_width = (rc.right - rc.left) * BAR_WIN_WIDTH / 100;
  679.         /* bar window is hidden unless in expand mode */
  680.         if ((DisplayMode == MODE_EXPAND) && (picture_mode)) {
  681.                 ShowWindow(hwndBar, SW_SHOW);
  682.                 MoveWindow(hwndBar, 0, status_height,
  683.                         bar_width, rc.bottom - status_height, TRUE);
  684.                 MoveWindow(hwndRCD, bar_width, status_height,
  685.                         (rc.right - rc.left) - bar_width,
  686.                         rc.bottom - status_height, TRUE);
  687.         } else {
  688.                 MoveWindow(hwndRCD, 0, status_height, (rc.right - rc.left),
  689.                         rc.bottom - status_height, TRUE);
  690.                 ShowWindow(hwndBar, SW_HIDE);
  691.         }
  692. }
  693. /***************************************************************************
  694.  * Function: AboutBox
  695.  *
  696.  * Purpose:
  697.  *
  698.  * Standard processing for About box.
  699.  */
  700. int APIENTRY
  701. AboutBox(HWND hDlg, unsigned message, UINT wParam, LONG lParam)
  702. {
  703.         char ch[256];
  704.         switch (message) {
  705.         case WM_INITDIALOG:
  706.                 wsprintf((LPTSTR)ch, "%d.%02d", Version, SubVersion);
  707.                 SetDlgItemText(hDlg, IDD_VERSION, ch);
  708.                 return(TRUE);
  709.         case WM_COMMAND:
  710.                 switch (GET_WM_COMMAND_ID(wParam, lParam)) {
  711.                 case IDOK:
  712.                         EndDialog(hDlg, 0);
  713.                         return(TRUE);
  714.                 }
  715.                 break;
  716.         }
  717.         return(FALSE);
  718. }
  719. /* -- menu commands ---------------------------------------------------*/
  720. /***************************************************************************
  721.  * Function: DoPrint
  722.  *
  723.  * Purpose:
  724.  *
  725.  * Print the current view 
  726.  */
  727. void
  728. DoPrint(void)
  729. {
  730.         Title head, foot;
  731.         PrintContext context;
  732.         TCHAR szPage[50];
  733.         /* print context contains the header and footer. Use the
  734.          * default margins and printer selection
  735.          */
  736.         /* we set the table id to be TABID_PRINTER. When the table calls
  737.          * back to get text and properties, we use this to indicate
  738.          * that the table refered to is the 'current_view', but in print
  739.          * mode, and thus we will use different colours/fonts.
  740.          */
  741.         context.head = &head;
  742.         context.foot = &foot;
  743.         context.margin = NULL;
  744.         context.pd = NULL;
  745.         context.id = TABID_PRINTER;
  746.         /* header is filenames or just WinDiff if no names known*/
  747.         if (strlen(AppTitle) > 0) {
  748.                 head.ptext = AppTitle;
  749.         } else {
  750.                 head.ptext = "WinDiff";
  751.         }
  752.         /* header is centred, footer is right-aligned and
  753.          * consists of the page number
  754.          */
  755.         head.props.valid = P_ALIGN;
  756.         head.props.alignment = P_CENTRE;
  757.         lstrcpy(szPage,LoadRcString(IDS_PAGE));
  758.         foot.ptext = (LPSTR)szPage;
  759.         foot.props.valid = P_ALIGN;
  760.         foot.props.alignment = P_RIGHT;
  761.         SendMessage(hwndRCD, TM_PRINT, 0, (DWORD) (LPSTR) &context);
  762. }
  763. /***************************************************************************
  764.  * Function: FindNextChange
  765.  *
  766.  * Purpose:
  767.  *
  768.  * Find the next line in the current view that is
  769.  * not STATE_SAME. Start from the current selection, if valid, or
  770.  * from the top of the window if no selection.
  771.  *
  772.  */
  773. BOOL
  774. FindNextChange(void)
  775. {
  776.         long row;
  777.         /* start from the selection or top of the window if no selection */
  778.         if (selection >= 0) {
  779.                 row = selection;
  780.         } else {
  781.                 row = (int) SendMessage(hwndRCD, TM_TOPROW, FALSE, 0);
  782.         }
  783.         /* find the next 'interesting' line */
  784.         row = view_findchange(current_view, row, TRUE);
  785.         if (row >= 0) {
  786.                 SetSelection(row);
  787.                 return(TRUE);
  788.         } else {
  789.                 windiff_UI(TRUE);
  790.                 MessageBox(hwndClient, LoadRcString(IDS_NO_MORE_CHANGES), "Windiff",
  791.                         MB_ICONINFORMATION|MB_OK);
  792.                 windiff_UI(FALSE);
  793.                 return(FALSE);
  794.         }
  795. }
  796. /***************************************************************************
  797.  * Function: FindPrevChange
  798.  *
  799.  * Purpose:
  800.  *
  801.  * Find the previous line in the current view that is not STATE_SAME
  802.  */
  803. BOOL
  804. FindPrevChange(void)
  805. {
  806.         long row;
  807.         /* start from the selection or top of window if no selection */
  808.         if (selection >= 0) {
  809.                 row = selection;
  810.         } else {
  811.                 row = (int) SendMessage(hwndRCD, TM_TOPROW, FALSE, 0);
  812.         }
  813.         /* find the previous 'interesting' line */
  814.         row = view_findchange(current_view, row, FALSE);
  815.         if (row >= 0) {
  816.                 SetSelection(row);
  817.                 return(TRUE);
  818.         } else {
  819.                 windiff_UI(TRUE);
  820.                 MessageBox(hwndClient, LoadRcString(IDS_NO_PREV_CHANGES), "Windiff",
  821.                         MB_ICONINFORMATION|MB_OK);
  822.                 windiff_UI(FALSE);
  823.                 return(FALSE);
  824.         }
  825. }
  826. /***************************************************************************
  827.  * Function: WriteProfileInt
  828.  *
  829.  */
  830.  
  831. BOOL WriteProfileInt(LPSTR AppName, LPSTR Key, int Int)
  832. {       char Str[40];
  833.         wsprintf((LPTSTR)Str, "%d", Int);
  834.         return WriteProfileString(AppName, Key, Str);
  835. } /* WriteProfileInt */
  836. /***************************************************************************
  837.  * Function: ToExpand
  838.  *
  839.  * Purpose:
  840.  *
  841.  * Switch to expand view of the selected line 
  842.  */
  843. BOOL
  844. ToExpand(HWND hwnd)
  845. {
  846.         if (selection < 0) {
  847.                 return(FALSE);
  848.         }
  849.         if (!view_isexpanded(current_view)) {
  850.                 /* save the current outline size and position */
  851.                 WINDOWPLACEMENT wp;
  852.                 if (GetWindowPlacement(hwndClient,&wp)) {
  853.                         WriteProfileInt(APPNAME, "OutlineShowCmd", wp.showCmd);
  854.                         WriteProfileInt(APPNAME, "OutlineMaxX", wp.ptMaxPosition.x);
  855.                         WriteProfileInt(APPNAME, "OutlineMaxY", wp.ptMaxPosition.y);
  856.                         WriteProfileInt(APPNAME, "OutlineNormLeft", wp.rcNormalPosition.left);
  857.                         WriteProfileInt(APPNAME, "OutlineNormTop", wp.rcNormalPosition.top);
  858.                         WriteProfileInt(APPNAME, "OutlineNormRight", wp.rcNormalPosition.right);
  859.                         WriteProfileInt(APPNAME, "OutlineNormBottom", wp.rcNormalPosition.bottom);
  860.                         WriteProfileInt(APPNAME, "OutlineSaved", 1);
  861.                 }
  862.                 /* restore the previous expanded size and position, if any */
  863.                 if (GetProfileInt(APPNAME, "ExpandedSaved", 0)) {
  864.                         wp.flags                   = 0;
  865.                         wp.showCmd
  866.                                 = GetProfileInt( APPNAME, "ExpandShowCmd"
  867.                                                , SW_SHOWMAXIMIZED);
  868.                         wp.ptMaxPosition.x
  869.                                 = GetProfileInt( APPNAME, "ExpandMaxX", 0);
  870.                         wp.ptMaxPosition.y
  871.                                 = GetProfileInt( APPNAME, "ExpandMaxY", 0);
  872.                         wp.rcNormalPosition.left
  873.                                 = GetProfileInt( APPNAME, "ExpandNormLeft"
  874.                                                , wp.rcNormalPosition.left);
  875.                         wp.rcNormalPosition.top
  876.                                 = GetProfileInt( APPNAME, "ExpandNormTop"
  877.                                                , wp.rcNormalPosition.top);
  878.                         wp.rcNormalPosition.right
  879.                                 = GetProfileInt( APPNAME, "ExpandNormRight"
  880.                                                , wp.rcNormalPosition.right);
  881.                         wp.rcNormalPosition.bottom
  882.                                 = GetProfileInt( APPNAME, "ExpandNormBottom"
  883.                                                , wp.rcNormalPosition.bottom);
  884.                         SetWindowPlacement(hwndClient,&wp);
  885.                 }
  886.                 else ShowWindow(hwndClient, SW_SHOWMAXIMIZED);
  887.         }
  888.         /*change the view mapping to expand mode */
  889.         if (view_expand(current_view, selection)) {
  890.                 /* ok - we now have an expanded view - change status
  891.                  * to show this
  892.                  */
  893.                 DisplayMode = MODE_EXPAND;
  894.                 /* resize to show the graphic bar picture */
  895.                 DoResize(hwndClient);
  896.                 /* change button,status text-if we are not still busy*/
  897.                 if (!fBusy) {
  898.                         TCHAR szBuf[10];
  899.                         lstrcpy(szBuf,LoadRcString(IDS_OUTLINE));
  900.                         /* the status field when we are expanded shows the
  901.                          * tag field (normally the file name) for the
  902.                          * item we are expanding
  903.                          */
  904.                         SetStatus(view_getcurrenttag(current_view) );
  905.                         SetButtonText(szBuf);
  906.                 }
  907.                 return(TRUE);
  908.         }
  909.         return(FALSE);
  910. } /* ToExpand */
  911. /***************************************************************************
  912.  * Function: ToOutline
  913.  *
  914.  * Purpose:
  915.  *
  916.  * Switch back to outline view - showing just the list of file names.
  917.  */
  918. void
  919. ToOutline(HWND hwnd)
  920. {
  921.         if (view_isexpanded(current_view)) {
  922.                 /* save the current expanded size and position */
  923.                 WINDOWPLACEMENT wp;
  924.                 if (GetWindowPlacement(hwndClient,&wp)) {
  925.                         WriteProfileInt(APPNAME, "ExpandShowCmd", wp.showCmd);
  926.                         WriteProfileInt(APPNAME, "ExpandMaxX", wp.ptMaxPosition.x);
  927.                         WriteProfileInt(APPNAME, "ExpandMaxY", wp.ptMaxPosition.y);
  928.                         WriteProfileInt(APPNAME, "ExpandNormLeft", wp.rcNormalPosition.left);
  929.                         WriteProfileInt(APPNAME, "ExpandNormTop", wp.rcNormalPosition.top);
  930.                         WriteProfileInt(APPNAME, "ExpandNormRight", wp.rcNormalPosition.right);
  931.                         WriteProfileInt(APPNAME, "ExpandNormBottom", wp.rcNormalPosition.bottom);
  932.                         WriteProfileInt(APPNAME, "ExpandedSaved", 1);
  933.                 }
  934.                 /* restore the previous expanded size and position, if any */
  935.                 if (GetProfileInt(APPNAME, "OutlineSaved", 0))  {
  936.                         wp.flags = 0;
  937.                         wp.showCmd
  938.                                 = GetProfileInt( APPNAME, "OutlineShowCmd"
  939.                                                , SW_SHOWNORMAL);
  940.                         wp.ptMaxPosition.x
  941.                                 = GetProfileInt( APPNAME, "OutlineMaxX", 0);
  942.                         wp.ptMaxPosition.y
  943.                                 = GetProfileInt( APPNAME, "OutlineMaxY", 0);
  944.                         wp.rcNormalPosition.left
  945.                                 = GetProfileInt( APPNAME, "OutlineNormLeft"
  946.                                                , wp.rcNormalPosition.left);
  947.                         wp.rcNormalPosition.top
  948.                                 = GetProfileInt( APPNAME, "OutlineNormTop"
  949.                                                , wp.rcNormalPosition.top);
  950.                         wp.rcNormalPosition.right
  951.                                 = GetProfileInt( APPNAME, "OutlineNormRight"
  952.                                                , wp.rcNormalPosition.right);
  953.                         wp.rcNormalPosition.bottom
  954.                                 = GetProfileInt( APPNAME, "OutlineNormBottom"
  955.                                                , wp.rcNormalPosition.bottom);
  956.                         SetWindowPlacement(hwndClient,&wp);
  957.                 }
  958.                 ShowWindow(hwndClient, SW_SHOWNORMAL);
  959.         }
  960.         DisplayMode = MODE_OUTLINE;
  961.         /* switch mapping back to outline view */
  962.         view_outline(current_view);
  963.         /* hide bar window and resize to cover */
  964.         DoResize(hwndClient);
  965.         /* change label on button */
  966.         if (!fBusy) {
  967.                 TCHAR szBuf[8];
  968.                 lstrcpy(szBuf,LoadRcString(IDS_EXPAND));
  969.                 SetButtonText(szBuf);
  970.                 SetStatus(NULL);
  971.         }
  972. } /* ToOutline */
  973. /***************************************************************************
  974.  * Function: ToMoved
  975.  *
  976.  * Purpose:
  977.  *
  978.  * If the user clicks on a MOVED line in expand mode, we jump to the
  979.  * other line. We return TRUE if this was possible,  or FALSE otherwise.
  980.  */
  981. BOOL
  982. ToMoved(HWND hwnd)
  983. {
  984.         BOOL bIsLeft;
  985.         int linenr, state;
  986.         long i, total;
  987.         if (DisplayMode != MODE_EXPAND) {
  988.                 return(FALSE);
  989.         }
  990.         if (selection < 0) {
  991.                 return(FALSE);
  992.         }
  993.         state = view_getstate(current_view, selection);
  994.         if (state == STATE_MOVEDLEFT) {
  995.                 bIsLeft = TRUE;
  996.                 /* get the linenr of the other copy */
  997.                 linenr = abs(view_getlinenr_right(current_view, selection));
  998.         } else if (state == STATE_MOVEDRIGHT) {
  999.                 bIsLeft = FALSE;
  1000.                 /* get the linenr of the other copy */
  1001.                 linenr = abs(view_getlinenr_left(current_view, selection));
  1002.         } else {
  1003.                 /* not a moved line - so we can't find another copy */
  1004.                 return(FALSE);
  1005.         }
  1006.         /* search the view for this line nr */
  1007.         total = view_getrowcount(current_view);
  1008.         for (i = 0; i < total; i++) {
  1009.                 if (bIsLeft) {
  1010.                         if (linenr == view_getlinenr_right(current_view, i)) {
  1011.                                 /* found it */
  1012.                                 SetSelection(i);
  1013.                                 return(TRUE);
  1014.                         }
  1015.                 } else {
  1016.                         if (linenr == view_getlinenr_left(current_view, i)) {
  1017.                                 SetSelection(i);
  1018.                                 return(TRUE);
  1019.                         }
  1020.                 }
  1021.         }
  1022.         return(FALSE);
  1023. }
  1024. /***************************************************************************
  1025.  * Function: do_editfile
  1026.  *
  1027.  * Purpose:
  1028.  *
  1029.  * Launch an editor on the current file (the file we are expanding, or
  1030.  * in outline mode the selected row. Option allows selection of the
  1031.  * left file, the right file or the composite view of this item.
  1032.  * pe points to a packet of parameters that must be freed before returning.
  1033.  * The return value is meaningless (just to conform to CreateThread).
  1034.  */
  1035. LONG
  1036. do_editfile(PEDITARGS pe)
  1037. {
  1038.         VIEW view = pe->view;
  1039.         int option = pe->option;
  1040.         int selection = pe->selection;
  1041.         COMPITEM item;
  1042.         LPSTR fname;
  1043.         char cmdline[256];
  1044.         int currentline;
  1045.         char * pOut = cmdline;
  1046.         char * pIn = editor_cmdline;
  1047.         STARTUPINFO si;
  1048.         PROCESS_INFORMATION pi;
  1049.         item = view_getitem(view, selection);
  1050.         if (item == NULL) {
  1051.                 return -1;
  1052.         }
  1053.         fname = compitem_getfilename(item, option);
  1054.         if ( 0 == fname )
  1055.         {
  1056.             windiff_UI(TRUE);
  1057.             MessageBox(hwndClient, LoadRcString(IDS_FILE_DOESNT_EXIST),
  1058.                        "Windiff", MB_ICONSTOP|MB_OK);
  1059.             windiff_UI(FALSE);
  1060.             goto error;
  1061.         }
  1062.        switch ( option )
  1063.         {
  1064.         case CI_LEFT:
  1065.             currentline = view_getlinenr_left( view,
  1066.                                                selection > 0 ? selection : 1);
  1067.             break;
  1068.         case CI_RIGHT:
  1069.             currentline = view_getlinenr_right( view,
  1070.                                                 selection > 0 ? selection : 1);
  1071.             break;
  1072.         default:
  1073.             currentline = 1;
  1074.             break;
  1075.         }
  1076.         while( *pIn )
  1077.         {
  1078.             switch( *pIn )
  1079.             {
  1080.             case '%':
  1081.                 pIn++;
  1082.                 switch ( *pIn )
  1083.                 {
  1084.                 case 'p':
  1085.                     lstrcpy( (LPTSTR)pOut, fname );
  1086.                     while ( *pOut )
  1087.                         pOut++;
  1088.                     break;
  1089.                 case 'l':
  1090.                     _ltoa( currentline, pOut, 10 );
  1091.                     while ( *pOut )
  1092.                         pOut++;
  1093.                     break;
  1094.                 default:
  1095.                     if (IsDBCSLeadByte(*pIn) && *(pIn+1)) {
  1096.                         *pOut++ = *pIn++;
  1097.                     }
  1098.                     *pOut++ = *pIn;
  1099.                     break;
  1100.                 }
  1101.                 pIn++;
  1102.                 break;
  1103.             default:
  1104.                 if (IsDBCSLeadByte(*pIn) && *(pIn+1)) {
  1105.                     *pOut++ = *pIn++;
  1106.                 }
  1107.                 *pOut++ = *pIn++;
  1108.                 break;
  1109.             }
  1110.         }
  1111.         /* Launch the process and waits for it to complete */
  1112.         si.cb = sizeof(STARTUPINFO);
  1113.         si.lpReserved = NULL;
  1114.         si.lpReserved2 = NULL;
  1115.         si.cbReserved2 = 0;
  1116.         si.lpTitle = (LPSTR)cmdline; 
  1117.         si.lpDesktop = (LPTSTR)NULL;
  1118.         si.dwFlags = STARTF_FORCEONFEEDBACK;
  1119.         if (!CreateProcess(NULL,
  1120.                         cmdline,
  1121.                         NULL,
  1122.                         NULL,
  1123.                         FALSE,
  1124.                         NORMAL_PRIORITY_CLASS,
  1125.                         NULL,
  1126.                         (LPTSTR)NULL,
  1127.                         &si,
  1128.                         &pi)) {
  1129.                 windiff_UI(TRUE);
  1130.                 MessageBox(hwndClient, LoadRcString(IDS_FAILED_TO_LAUNCH_EDT),
  1131.                         "Windiff", MB_ICONSTOP|MB_OK);
  1132.                 windiff_UI(FALSE);
  1133.                 goto error;
  1134.         }
  1135.         /* wait for completion. */
  1136.         WaitForSingleObject(pi.hProcess, INFINITE);
  1137.         /* close process and thread handles */
  1138.         CloseHandle(pi.hThread);
  1139.         CloseHandle(pi.hProcess);
  1140.         /* finished with the filename. deletes it if it was a temp. */
  1141.         compitem_freefilename(item, option, fname);
  1142.         /*
  1143.          * refresh cached view always .  A common trick is to edit the
  1144.          * composite file and then save it as a new left or right file.
  1145.          * Equally the user can edit the left and save as a new right.
  1146.          */
  1147.         /* We want to force both files to be re-read, but it's not a terribly
  1148.          * good idea to throw the lines away on this thread.  Someone might
  1149.          * be reading them on another thread!
  1150.          */
  1151.         /* file_discardlines(compitem_getleftfile(item)) */
  1152.         /* file_discardlines(compitem_getrightfile(item)) */
  1153.         /* force the compare to be re-done */
  1154.         PostMessage(hwndClient, WM_COMMAND, IDM_UPDATE, (LONG)item);
  1155. error:
  1156.         gmem_free(hHeap, (LPSTR) pe, sizeof(EDITARGS));
  1157.         return 0;
  1158. } /* do_editfile */
  1159. /***************************************************************************
  1160.  * Function: do_editthread
  1161.  *
  1162.  * Purpose:
  1163.  *
  1164.  * Launch an editor on a separate thread.  It will actually get a separate
  1165.  * process, but we want our own thread in this process.  This thread will
  1166.  * wait until it's finished and then order up a refresh of the UI.
  1167.  * Need to give it its parameters as a gmem allocated packet because
  1168.  * it IS on a separate thread.
  1169.  */
  1170. void do_editthread(VIEW view, int option)
  1171. {
  1172.         PEDITARGS pe;
  1173.         HANDLE thread;
  1174.         DWORD threadid;
  1175.         pe = (PEDITARGS) gmem_get(hHeap, sizeof(EDITARGS));
  1176.         pe->view = view;
  1177.         pe->option = option;
  1178.         pe->selection = selection;
  1179.         thread = CreateThread( NULL
  1180.                              , 0
  1181.                              , (LPTHREAD_START_ROUTINE)do_editfile
  1182.                              , (LPVOID) pe
  1183.                              , 0
  1184.                              , &threadid
  1185.                              );
  1186.         if (thread == NULL)
  1187.         {
  1188.                 /* The createthread failed, do without the extra thread - just
  1189.                  * call the function synchronously
  1190.                  */
  1191.                  do_editfile(pe);
  1192.         }
  1193.         else CloseHandle(thread);
  1194. } /* do_editthread */
  1195. /* status bar and busy flags --------------------------------------------*/
  1196. /***************************************************************************
  1197.  * Function: SetButtonText
  1198.  *
  1199.  * Purpose:
  1200.  *
  1201.  * Set the Text on the statusbar button to reflect the current state 
  1202.  */
  1203. void
  1204. SetButtonText(LPSTR cmd)
  1205. {
  1206.         SendMessage(hwndStatus, SM_SETTEXT, IDM_ABORT, (DWORD) cmd);
  1207. }
  1208. /***************************************************************************
  1209.  * Function: SetStatus
  1210.  *
  1211.  * Purpose:
  1212.  *
  1213.  * Set the status field (left-hand part) of the status bar. 
  1214.  */
  1215. void
  1216. SetStatus(LPSTR cmd)
  1217. {
  1218.         SendMessage(hwndStatus, SM_SETTEXT, IDL_STATLAB, (DWORD) cmd);
  1219. }
  1220. /***************************************************************************
  1221.  * Function: SetNames
  1222.  *
  1223.  * Purpose:
  1224.  *
  1225.  * Set the names field - the central box in the status bar 
  1226.  */
  1227. void
  1228. SetNames(LPSTR names)
  1229. {
  1230.         SendMessage(hwndStatus, SM_SETTEXT, IDL_NAMES, (DWORD) names);
  1231.         if (names == NULL) {
  1232.                 AppTitle[0] = '';
  1233.         } else {
  1234.                 strncpy(AppTitle, names, sizeof(AppTitle));
  1235.         }
  1236. }
  1237. /***************************************************************************
  1238.  * Function: SetBusy
  1239.  *
  1240.  * Purpose:
  1241.  *
  1242.  * If we are not already busy, set the busy flag.
  1243.  *
  1244.  * Enter critical section first.
  1245.  */
  1246. BOOL
  1247. SetBusy(void)
  1248. {
  1249.         HMENU hmenu;
  1250.         WDEnter();
  1251.         if (fBusy) {
  1252.                 WDLeave();
  1253.                 return(FALSE);
  1254.         }
  1255.         fBusy = TRUE;
  1256.         SetStatus(LoadRcString(IDS_COMPARING));
  1257.         /* status also on window text, so that you can see even from
  1258.          * the icon when the scan has finished
  1259.          */
  1260.         SetWindowText(hwndClient, LoadRcString(IDS_SCANNING));
  1261.         /* disable appropriate parts of menu */
  1262.         hmenu = GetMenu(hwndClient);
  1263.         EnableMenuItem(hmenu, IDM_FILE,MF_DISABLED|MF_GRAYED|MF_BYCOMMAND);
  1264.         EnableMenuItem(hmenu, IDM_DIR,MF_DISABLED|MF_GRAYED|MF_BYCOMMAND);
  1265.         EnableMenuItem(hmenu, IDM_PRINT,MF_DISABLED|MF_GRAYED|MF_BYCOMMAND);
  1266.         /* enable abort only when busy */
  1267.         EnableMenuItem(hmenu, IDM_ABORT,MF_ENABLED|MF_BYCOMMAND);
  1268.         SetButtonText(LoadRcString(IDS_ABORT));  /* leave DisplayMode unchanged */
  1269.         WDLeave();
  1270.         return(TRUE);
  1271. } /* SetBusy */
  1272. /***************************************************************************
  1273.  * Function: SetNotBusy
  1274.  *
  1275.  * Purpose:
  1276.  *
  1277.  * This function can be called from the worker thread.
  1278.  * Thus we must not cause any SendMessage calls to windows
  1279.  * owned by the main thread while holding the CritSec or we
  1280.  * could cause deadlock.
  1281.  *
  1282.  * The critsec is only needed to protect the fBusy flag - so
  1283.  * clear the busy flag last, and only get the crit sec as needed.
  1284.  */
  1285. void
  1286. SetNotBusy(void)
  1287. {
  1288.         HMENU hmenu;
  1289.         /* reset button and status bar (clearing out busy flags) */
  1290.         if (current_view == NULL) {
  1291.                 SetButtonText(LoadRcString(IDS_EXIT));
  1292.                 SetStatus(NULL);
  1293.                 DisplayMode = MODE_NULL;
  1294.         } else if (view_isexpanded(current_view)) {
  1295.                 TCHAR szBuf[10];
  1296.                 lstrcpy(szBuf,LoadRcString(IDS_OUTLINE));
  1297.                 SetButtonText(szBuf);
  1298.                 SetStatus(view_getcurrenttag(current_view) );
  1299.                 DisplayMode = MODE_EXPAND;
  1300.         } else {
  1301.                 TCHAR szBuf[8];
  1302.                 lstrcpy(szBuf,LoadRcString(IDS_EXPAND));
  1303.                 SetButtonText(szBuf);
  1304.                 SetStatus(NULL);
  1305.                 DisplayMode = MODE_OUTLINE;
  1306.         }
  1307.         SetWindowText(hwndClient, "WinDiff");
  1308.         /* re-enable appropriate parts of menu */
  1309.         hmenu = GetMenu(hwndClient);
  1310.         EnableMenuItem(hmenu, IDM_FILE,MF_ENABLED|MF_BYCOMMAND);
  1311.         EnableMenuItem(hmenu, IDM_DIR,MF_ENABLED|MF_BYCOMMAND);
  1312.         EnableMenuItem(hmenu, IDM_PRINT,MF_ENABLED|MF_BYCOMMAND);
  1313.         /* disable abort now no longer busy */
  1314.         EnableMenuItem(hmenu, IDM_ABORT,MF_DISABLED|MF_GRAYED|MF_BYCOMMAND);
  1315.         /* clear the busy flag, protected by critical section */
  1316.         WDEnter();
  1317.         fBusy = FALSE;
  1318.         bAbort = FALSE;
  1319.         if (ghThread!=NULL){
  1320.             CloseHandle(ghThread);
  1321.             ghThread = NULL;
  1322.         }
  1323.         WDLeave();
  1324. } /* SetNotBusy */
  1325. /***************************************************************************
  1326.  * Function: IsBusy
  1327.  *
  1328.  * Purpose:
  1329.  *
  1330.  * Checks whether or not crit sec is open
  1331.  */
  1332. BOOL
  1333. IsBusy()
  1334. {
  1335.         BOOL bOK;
  1336.         WDEnter();
  1337.         bOK = fBusy;
  1338.         WDLeave();
  1339.         return(bOK);
  1340. } /* IsBusy */
  1341. /***************************************************************************
  1342.  * Function: BusyError
  1343.  *
  1344.  * Purpose:
  1345.  *
  1346.  * Puts up message box that system is busy.
  1347.  */
  1348. void
  1349. BusyError(void)
  1350. {
  1351.         windiff_UI(TRUE);
  1352.         MessageBox(hwndClient,
  1353.                 LoadRcString(IDS_PLEASE_WAIT),
  1354.                 "WinDiff", MB_OK|MB_ICONSTOP);
  1355.         windiff_UI(FALSE);
  1356. } /* BusyError */
  1357. /* --- colour scheme --------------------------------------------------- */
  1358. /***************************************************************************
  1359.  * Function: StateToColour
  1360.  *
  1361.  * Purpose:
  1362.  *
  1363.  * Map the state given into a foreground and a background colour
  1364.  * for states that are highlighted. Return P_FCOLOUR if the foreground
  1365.  * colour (put in *foreground) is to be used, return P_FCOLOUR|P_BCOLOUR if
  1366.  * both *foreground and *background are to be used, or 0 if the default
  1367.  * colours are to be used.
  1368.  */
  1369. UINT
  1370. StateToColour(int state, int col, DWORD FAR * foreground, DWORD FAR * background)
  1371. {
  1372.         switch (state) {
  1373.         case STATE_DIFFER:
  1374.                 /* files that differ are picked out in a foreground highlight,
  1375.                  * with the default background
  1376.                  */
  1377.                 *foreground = rgb_outlinehi;
  1378.                 return(P_FCOLOUR);
  1379.         case STATE_LEFTONLY:
  1380.                 /* lines only in the left file */
  1381.                 *foreground = rgb_leftfore;
  1382.                 *background = rgb_leftback;
  1383.                 return(P_FCOLOUR|P_BCOLOUR);
  1384.         case STATE_RIGHTONLY:
  1385.                 /* lines only in the right file */
  1386.                 *foreground = rgb_rightfore;
  1387.                 *background = rgb_rightback;
  1388.                 return(P_FCOLOUR|P_BCOLOUR);
  1389.         case STATE_MOVEDLEFT:
  1390.                 /* displaced lines in both files - left file version */
  1391.                 *foreground = rgb_mleftfore;
  1392.                 *background = rgb_mleftback;
  1393.                 return(P_FCOLOUR|P_BCOLOUR);
  1394.         case STATE_MOVEDRIGHT:
  1395.                 /* displaced lines in both files - right file version */
  1396.                 *foreground = rgb_mrightfore;
  1397.                 *background = rgb_mrightback;
  1398.                 return(P_FCOLOUR|P_BCOLOUR);
  1399.         default:
  1400.                 /* no highlighting - default colours */
  1401.                 return(0);
  1402.         }
  1403. }
  1404. /* table window communication routines ---------------------------------*/
  1405. /***************************************************************************
  1406.  * Function: SetSelection
  1407.  *
  1408.  * Purpose:
  1409.  *
  1410.  * Set a given row as the selected row in the table window 
  1411.  */
  1412. void
  1413. SetSelection(long rownr)
  1414. {
  1415.         TableSelection select;
  1416.         select.startrow = rownr;
  1417.         select.startcell = 0;
  1418.         select.nrows = 1;
  1419.         select.ncells = 1;
  1420.         SendMessage(hwndRCD, TM_SELECT, 0, (long) (LPSTR)&select);
  1421. }
  1422. /***************************************************************************
  1423.  * Function: do_gethdr
  1424.  *
  1425.  * Purpose:
  1426.  *
  1427.  * Handle table class call back to get nr of rows and columns,
  1428.  * and properties for the whole table.
  1429.  * The 'table id' is either TABID_PRINTER - meaning we are
  1430.  * printing the current_view, or it is the view to
  1431.  * use for row/column nr information
  1432.  */
  1433. long
  1434. do_gethdr(HWND hwnd, lpTableHdr phdr)
  1435. {
  1436.         VIEW view;
  1437.         BOOL bIsPrinter = FALSE;
  1438.         if (phdr->id == TABID_PRINTER) {
  1439.                 view = current_view;
  1440.                 bIsPrinter = TRUE;
  1441.         } else {
  1442.                 view = (VIEW) phdr->id;
  1443.         }
  1444.         if (view == NULL) {
  1445.                 return(FALSE);
  1446.         }
  1447.         phdr->nrows = view_getrowcount(view);
  1448.         /*  three columns: line nr, tag and rest of line */
  1449.         /*
  1450.          * if IDM_NONRS (no line numbers) is selected, suppress the
  1451.          * line-nr column entirely to save screen space
  1452.          */
  1453.         if (line_numbers == IDM_NONRS) {
  1454.                 phdr->ncols = 2;
  1455.                 phdr->fixedcols = 0;
  1456.         } else {
  1457.                 phdr->ncols = 3;
  1458.                 phdr->fixedcols = 1;
  1459.         }
  1460.         phdr->fixedrows = 0;
  1461.         phdr->fixedselectable = FALSE;
  1462.         phdr->hseparator = TRUE;
  1463.         phdr->vseparator = TRUE;
  1464.         phdr->selectmode = TM_ROW | TM_SINGLE;
  1465.         /*
  1466.          * find if we are in expand mode - ask for the item we are expanding.
  1467.          */
  1468.         if (view_isexpanded(view) == TRUE) {
  1469.                 /* use focus rect as selection mode in expand mode
  1470.                  * so as not to interfere with background colours.
  1471.                  */
  1472.                 phdr->selectmode |= TM_FOCUS;
  1473.         } else {
  1474.                 /* use default solid inversion when possible as it is clearer.*/
  1475.                 phdr->selectmode |= TM_SOLID;
  1476.         }
  1477.         /* please send TQ_SCROLL notifications when the table is scrolled */
  1478.         phdr->sendscroll = TRUE;
  1479.         phdr->props.valid = 0;
  1480.         return TRUE;
  1481. }
  1482. /***************************************************************************
  1483.  * Function: do_getprops
  1484.  *
  1485.  * Purpose:
  1486.  *
  1487.  * Respond to table callback asking for the size and properties
  1488.  * of each column. Table id is either TABID_PRINTER (meaning the
  1489.  * current_view, for printing) or it is the view to be used.
  1490.  */
  1491. long
  1492. do_getprops(HWND hwnd, lpColPropsList propslist)
  1493. {
  1494.         int i, cell;
  1495.         BOOL bIsPrinter = FALSE;
  1496.         VIEW view;
  1497.         if (propslist->id == TABID_PRINTER) {
  1498.                 view = current_view;
  1499.                 bIsPrinter = TRUE;
  1500.         } else {
  1501.                 view = (VIEW) propslist->id;
  1502.         }
  1503.         if (view == NULL) {
  1504.                 return(FALSE);
  1505.         }
  1506.         /* The table inteface is slightly confused here. we are not
  1507.          * guaranteed which columns we are being asked about, so instead
  1508.          * of just setting each column cols[0], cols[1] etc, we need
  1509.          * to loop through, looking at each column in the table and
  1510.          * seeing which it is.
  1511.          */
  1512.         for (i = 0; i < propslist->ncols; i++) {
  1513.                 cell = i + propslist->startcol;
  1514.                 propslist->plist[i].props.valid = 0;
  1515.                 /* for all column widths, add on 1 for the NULL char. */
  1516.                 /*
  1517.                  * skip the line nr column if IDM_NONRS
  1518.                  */
  1519.                 if (line_numbers == IDM_NONRS) {
  1520.                         cell++;
  1521.                 }
  1522.                 if (cell == 0) {
  1523.                         /* properties for line nr column */
  1524.                         propslist->plist[i].nchars = view_getwidth(view, 0)+1;
  1525.                         propslist->plist[i].props.valid |= P_ALIGN;
  1526.                         propslist->plist[i].props.alignment = P_CENTRE;
  1527.                 } else if (cell == 1) {
  1528.                         /* properties for tag field */
  1529.                         propslist->plist[i].nchars = view_getwidth(view, 1)+1;
  1530.                         propslist->plist[i].props.valid |= P_ALIGN;
  1531.                         propslist->plist[i].props.alignment = P_LEFT;
  1532.                 } else {
  1533.                         /* properties for main text column -
  1534.                          * use a fixed font unless printing (if
  1535.                          * printing, best to use the default font, because
  1536.                          * of resolution differences.
  1537.                          * add on 8 chars to the width to ensure that
  1538.                          * the width of lines beginning with tabs
  1539.                          * works out ok
  1540.                          */
  1541.                         propslist->plist[i].nchars = view_getwidth(view, 2)+1;
  1542.                         propslist->plist[i].props.valid |= P_ALIGN;
  1543.                         propslist->plist[i].props.alignment = P_LEFT;
  1544.                         if (!bIsPrinter) {
  1545.                                 propslist->plist[i].props.valid |= P_FONT;
  1546.                                 propslist->plist[i].props.hFont =
  1547.                                         GetStockObject(SYSTEM_FIXED_FONT);
  1548.                         }
  1549.                 }
  1550.         }
  1551.         return (TRUE);
  1552. }
  1553. /***************************************************************************
  1554.  * Function: do_getdata
  1555.  *
  1556.  * Purpose:
  1557.  *
  1558.  * Respond to a table callback asking for the contents of individual cells.
  1559.  * table id is either TABID_PRINTER, or it is a pointer to the view
  1560.  * to use for data. If going to the printer, don't set the
  1561.  * colours (stick to black and white).
  1562.  */
  1563. long
  1564. do_getdata(HWND hwnd, lpCellDataList cdlist)
  1565. {
  1566.         int start, endcell, col, i;
  1567.         lpCellData cd;
  1568.         VIEW view;
  1569.         LPSTR textp;
  1570.         BOOL bIsPrinter = FALSE;
  1571.         if (cdlist->id == TABID_PRINTER) {
  1572.                 view = current_view;
  1573.                 bIsPrinter = TRUE;
  1574.         } else {
  1575.                 view = (VIEW) cdlist->id;
  1576.         }
  1577.         start = cdlist->startcell;
  1578.         endcell = cdlist->ncells + start;
  1579.         if (cdlist->row >= view_getrowcount(view)) {
  1580.                 return(FALSE);
  1581.         }
  1582.         for (i = start; i < endcell; i++) {
  1583.                 cd = &cdlist->plist[i - start];
  1584.                 /* skip the line number column if IDM_NONRS */
  1585.                 if (line_numbers == IDM_NONRS) {
  1586.                         col = i+1;
  1587.                 } else {
  1588.                         col = i;
  1589.                 }
  1590.                 /* set colour of text to mark out
  1591.                  * lines that are changed, if not printer - for the
  1592.                  * printer everything should stay in the default colours
  1593.                  */
  1594.                 if (!bIsPrinter) {
  1595.                         /* convert the state of the requested row into a
  1596.                          * colour scheme. returns P_FCOLOUR and/or
  1597.                          * P_BCOLOUR if it sets either of the colours
  1598.                          */
  1599.                         cd->props.valid |=
  1600.                             StateToColour(view_getstate(view, cdlist->row), col,
  1601.                                         &cd->props.forecolour,
  1602.                                         &cd->props.backcolour);
  1603.                 }
  1604.                 textp = view_gettext(view, cdlist->row, col);
  1605.                 if (cd->nchars != 0) {
  1606.                         if (textp == NULL) {
  1607.                                 cd->ptext[0] = '';
  1608.                         } else {
  1609.                                 strncpy(cd->ptext, textp, cd->nchars -1);
  1610.                                 cd->ptext[cd->nchars - 1] = '';
  1611.                         }
  1612.                 }
  1613.         }
  1614.         return(TRUE);
  1615. }
  1616. /***************************************************************************
  1617.  * Function: SvrClose
  1618.  *
  1619.  * Purpose:
  1620.  *
  1621.  * Table window has finished with this view. It can be deleted.
  1622.  */
  1623. void
  1624. SvrClose(void)
  1625. {
  1626.         view_delete(current_view);
  1627.         current_view = NULL;
  1628.         /* hide picture - only visible when we are in MODE_EXPAND */
  1629.         DisplayMode = MODE_NULL;
  1630.         DoResize(hwndClient);
  1631.         /* if we already busy when closing this view (ie
  1632.          * we are in the process of starting a new scan,
  1633.          * then leave the status bar alone, otherwise
  1634.          * we should clean up the state of the status bar
  1635.          */
  1636.         if (!fBusy) {
  1637.                 SetButtonText(LoadRcString(IDS_EXIT));
  1638.                 SetNames(NULL);
  1639.                 SetStatus(NULL);
  1640.         }
  1641. } /* SvrClose */
  1642. /***************************************************************************
  1643.  * Function: TableServer
  1644.  *
  1645.  * Purpose:
  1646.  *
  1647.  * Handle callbacks and notifications from the table class 
  1648.  */
  1649. long
  1650. TableServer(HWND hwnd, UINT cmd, long lParam)
  1651. {
  1652.         lpTableHdr phdr;
  1653.         lpColPropsList proplist;
  1654.         lpCellDataList cdlist;
  1655.         lpTableSelection pselect;
  1656.         switch(cmd) {
  1657.         case TQ_GETSIZE:
  1658.                 /* get the nr of rows and cols in this table */
  1659.                 phdr = (lpTableHdr) lParam;
  1660.                 return(do_gethdr(hwnd, phdr));
  1661.         case TQ_GETCOLPROPS:
  1662.                 /* get the size and properties of each column */
  1663.                 proplist = (lpColPropsList) lParam;
  1664.                 return (do_getprops(hwnd, proplist));
  1665.         case TQ_GETDATA:
  1666.                 /* get the contents of individual cells */
  1667.                 cdlist = (lpCellDataList) lParam;
  1668.                 return (do_getdata(hwnd, cdlist));
  1669.         case TQ_SELECT:
  1670.                 /* selection has changed */
  1671.         case TQ_ENTER:
  1672.                 /* user has double-clicked or pressed enter */
  1673.                 pselect = (lpTableSelection) lParam;
  1674.                 /* store location for use in later search (IDM_FCHANGE) */
  1675.                 if (pselect->nrows < 1) {
  1676.                         selection = -1;
  1677.                 } else {
  1678.                         selection = (int) pselect->startrow;
  1679.                         if (cmd == TQ_ENTER) {
  1680.                                 /* try to expand this row */
  1681.                                 if (!ToExpand(hwnd)) {
  1682.                                         /* expand failed - maybe this
  1683.                                          * is a moved line- show the other
  1684.                                          * copy
  1685.                                          */
  1686.                                         ToMoved(hwnd);
  1687.                                 }
  1688.                         }
  1689.                 }
  1690.                 break;
  1691.         case TQ_CLOSE:
  1692.                 /* close this table - table class no longer needs data*/
  1693.                 SvrClose();
  1694.                 break;
  1695.         case TQ_SCROLL:
  1696.                 /* notification that the rows visible in the window
  1697.                  * have changed -change the current position lines in
  1698.                  * the graphic bar view (the sections picture)
  1699.                  */
  1700.                 if (picture_mode) {
  1701.                         BarDrawPosition(hwndBar, NULL, TRUE);
  1702.                 }
  1703.                 break;
  1704.         default:
  1705.                 return(FALSE);
  1706.         }
  1707.         return(TRUE);
  1708. }
  1709. /***************************************************************************
  1710.  * Function: wd_initial
  1711.  *
  1712.  * Purpose:
  1713.  *
  1714.  * Called on worker thread (not UI thread) to handle the work
  1715.  * requested on the command line. 
  1716.  * arg is a pointer to a THREADARGS block allocated from gmem_get(hHeap). This
  1717.  * needs to be freed before exiting.
  1718.  */
  1719. DWORD
  1720. wd_initial(LPVOID arg)
  1721. {
  1722.         PTHREADARGS pta = (PTHREADARGS) arg;
  1723.         COMPLIST cl;
  1724.         /* build a complist from these args,
  1725.          * and register with the view we have made
  1726.          */
  1727.         cl = complist_args(pta->first, pta->second, pta->view, pta->fDeep);
  1728.         if (cl == NULL) {
  1729.                 view_close(pta->view);
  1730.                 gmem_free(hHeap, (LPSTR) pta, sizeof(THREADARGS));
  1731.                 SetNotBusy();
  1732.                 return 0;
  1733.         }
  1734.         /* if savelist was selected, write out the list and exit */
  1735.         if(pta->savelist != NULL) {
  1736.                 complist_savelist(cl, pta->savelist, pta->saveopts);
  1737.                 gmem_free(hHeap, (LPSTR) pta, sizeof(THREADARGS));
  1738.                 SetNotBusy();
  1739.                 exit(0);
  1740.         }
  1741.         /* if there was only one file, expand it */
  1742.         if (view_getrowcount(pta->view) == 1) {
  1743.                 SetSelection(0);
  1744.                 ToExpand(hwndClient);
  1745.         }
  1746.         gmem_free(hHeap, (LPSTR) pta, sizeof(THREADARGS));
  1747.         SetNotBusy();
  1748.         return(0);
  1749. } /* wd_initial */
  1750. /***************************************************************************
  1751.  * Function: wd_dirdialog
  1752.  *
  1753.  * Purpose:
  1754.  *
  1755.  * Called on worker thread (not UI thread) to handle a Dir request
  1756.  */
  1757. DWORD
  1758. wd_dirdialog(LPVOID arg)
  1759. {
  1760.         VIEW view = (VIEW) arg;
  1761.         /* make a COMPLIST using the directory dialog,
  1762.          * and notify the view
  1763.          */
  1764.         if (complist_dirdialog(view) == NULL) {
  1765.                 view_close(view);
  1766.         }
  1767.         /* all done! */
  1768.         SetNotBusy();
  1769.         return(0);
  1770. }
  1771. /***************************************************************************
  1772.  * Function: wd_copy
  1773.  *
  1774.  * Purpose:
  1775.  *
  1776.  * Called on worker thread to do a copy-files operation
  1777.  */
  1778. DWORD
  1779. wd_copy(LPVOID arg)
  1780. {
  1781.         VIEW view = (VIEW) arg;
  1782.         complist_copyfiles(view_getcomplist(view), NULL, 0);
  1783.         SetNotBusy();
  1784.         return(0);
  1785. }
  1786. /***************************************************************************
  1787.  * Function: MainWndProc
  1788.  *
  1789.  * Purpose:
  1790.  *
  1791.  * Window processing for main window
  1792.  */
  1793. long APIENTRY
  1794. MainWndProc(HWND hWnd, UINT message, UINT wParam, LONG lParam)
  1795. {
  1796.         char str[32];
  1797.         long ret;
  1798.         DWORD threadid;
  1799.         switch(message) {
  1800.         case WM_CREATE:
  1801.                 /* initialise menu options to default/saved
  1802.                  * option settings
  1803.                  */
  1804.                 CheckMenuItem(hMenu, IDM_INCSAME,
  1805.                       (outline_include & INCLUDE_SAME) ?
  1806.                                 MF_CHECKED:MF_UNCHECKED);
  1807.                 CheckMenuItem(hMenu, IDM_INCLEFT,
  1808.                       (outline_include & INCLUDE_LEFTONLY) ?
  1809.                                 MF_CHECKED:MF_UNCHECKED);
  1810.                 CheckMenuItem(hMenu, IDM_INCRIGHT,
  1811.                       (outline_include & INCLUDE_RIGHTONLY) ?
  1812.                                 MF_CHECKED:MF_UNCHECKED);
  1813.                 CheckMenuItem(hMenu, IDM_INCDIFFER,
  1814.                       (outline_include & INCLUDE_DIFFER) ?
  1815.                                 MF_CHECKED:MF_UNCHECKED);
  1816.                 CheckMenuItem(hMenu, line_numbers, MF_CHECKED);
  1817.                 CheckMenuItem(hMenu, expand_mode, MF_CHECKED);
  1818.                 CheckMenuItem(hMenu, IDM_IGNBLANKS,
  1819.                         ignore_blanks ? MF_CHECKED : MF_UNCHECKED);
  1820.                 CheckMenuItem(hMenu, IDM_PICTURE,
  1821.                         picture_mode ? MF_CHECKED : MF_UNCHECKED);
  1822.                 /* nothing currently displayed */
  1823.                 DisplayMode = MODE_NULL;
  1824.                 break;
  1825.         case WM_COMMAND:
  1826.                 switch (GET_WM_COMMAND_ID(wParam, lParam)) {
  1827.                 case IDM_EXIT:
  1828.                         if (ghThread!=NULL) {
  1829.                                 extern CRITICAL_SECTION CSView;
  1830.                                 /* Stop any other thread from allocating things that we
  1831.                                    want to free!  See the threads DOGMA at the top
  1832.                                    of this file.
  1833.                                 */
  1834.                                 /* Because the thread that we are about to kill might be in
  1835.                                    a critical section, we first grab them both.  It is
  1836.                                    essential that anyone else who ever does this, does
  1837.                                    so in the same order!
  1838.                                 */
  1839.                                 WDEnter();
  1840.                                 EnterCriticalSection(&CSView);
  1841.                                 TerminateThread(ghThread, 31);
  1842.                                 CloseHandle(ghThread);
  1843.                                 ghThread = NULL;
  1844.                                 LeaveCriticalSection(&CSView);
  1845.                                 WDLeave();
  1846.                         }
  1847.                         if (!view_isexpanded(current_view)) {
  1848.                                 /* save the current outline size and position */
  1849.                                 WINDOWPLACEMENT wp;
  1850.                                 if (GetWindowPlacement(hwndClient,&wp)) {
  1851.                                         WriteProfileInt(APPNAME, "OutlineShowCmd", wp.showCmd);
  1852.                                         WriteProfileInt(APPNAME, "OutlineMaxX", wp.ptMaxPosition.x);
  1853.                                         WriteProfileInt(APPNAME, "OutlineMaxY", wp.ptMaxPosition.y);
  1854.                                         WriteProfileInt(APPNAME, "OutlineNormLeft", wp.rcNormalPosition.left);
  1855.                                         WriteProfileInt(APPNAME, "OutlineNormTop", wp.rcNormalPosition.top);
  1856.                                         WriteProfileInt(APPNAME, "OutlineNormRight", wp.rcNormalPosition.right);
  1857.                                         WriteProfileInt(APPNAME, "OutlineNormBottom", wp.rcNormalPosition.bottom);
  1858.                                         WriteProfileInt(APPNAME, "OutlineSaved", 1);
  1859.                                 }
  1860.                         } else {
  1861.                                 /* save the current expanded size and position */
  1862.                                 WINDOWPLACEMENT wp;
  1863.                                 if (GetWindowPlacement(hwndClient,&wp)) {
  1864.                                         WriteProfileInt(APPNAME, "ExpandShowCmd", wp.showCmd);
  1865.                                         WriteProfileInt(APPNAME, "ExpandMaxX", wp.ptMaxPosition.x);
  1866.                                         WriteProfileInt(APPNAME, "ExpandMaxY", wp.ptMaxPosition.y);
  1867.                                         WriteProfileInt(APPNAME, "ExpandNormLeft", wp.rcNormalPosition.left);
  1868.                                         WriteProfileInt(APPNAME, "ExpandNormTop", wp.rcNormalPosition.top);
  1869.                                         WriteProfileInt(APPNAME, "ExpandNormRight", wp.rcNormalPosition.right);
  1870.                                         WriteProfileInt(APPNAME, "ExpandNormBottom", wp.rcNormalPosition.bottom);
  1871.                                         WriteProfileInt(APPNAME, "ExpandedSaved", 1);
  1872.                                 }
  1873.                         }
  1874.                         DestroyWindow(hWnd);
  1875.                         break;
  1876.                 case IDM_ABORT:
  1877.                         /* abort menu item, or status bar button.
  1878.                          * the status bar button text gives the appropriate
  1879.                          * action depending on our state - abort, outline
  1880.                          * or expand. But the command sent is always
  1881.                          * IDM_ABORT. Thus we need to check the state
  1882.                          * to see what to do. If we are busy, set the abort
  1883.                          * flag. If there is nothing to view,
  1884.                          * exit, otherwise switch outline<->expand
  1885.                          */
  1886.                         if (IsBusy()) {
  1887.                                 bAbort = TRUE;
  1888.                                 SetStatus(LoadRcString(IDS_ABORT_PENDING));
  1889.                         } else if (DisplayMode == MODE_NULL) {
  1890.                                 DestroyWindow(hWnd);
  1891.                         } else if (DisplayMode == MODE_EXPAND) {
  1892.                                 ToOutline(hWnd);
  1893.                         } else {
  1894.                                 ToExpand(hWnd);
  1895.                         }
  1896.                         break;
  1897.                 case IDM_FILE:
  1898.                         /* select two files and compare them */
  1899.                         if (SetBusy()) {
  1900.                                /* close the current view */
  1901.                                 view_close(current_view);
  1902.                                 /* make a new empty view */
  1903.                                 current_view = view_new(hwndRCD);
  1904.                                 /* make a COMPLIST using the files dialog,
  1905.                                  * and notify the view
  1906.                                  */
  1907.                                 if (complist_filedialog(current_view) == NULL) {
  1908.                                         view_close(current_view);
  1909.                                 }
  1910.                                 /* all done! */
  1911.                                 SetNotBusy();
  1912.                         } else {
  1913.                                 BusyError();
  1914.                         }
  1915.                         break;
  1916.                 case IDM_DIR:
  1917.                         /* read two directory names, scan them and
  1918.                          * compare all the files and subdirs.
  1919.                          */
  1920.                         if (SetBusy()) {
  1921.                                 /* close the current view */
  1922.                                 view_close(current_view);
  1923.                                 /* make a new empty view */
  1924.                                 current_view = view_new(hwndRCD);
  1925.                                 ghThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)wd_dirdialog,
  1926.                                         (LPVOID) current_view, 0, &threadid);
  1927.                                 if (ghThread == NULL)
  1928.                                 {
  1929.                                         wd_dirdialog( (LPVOID) current_view);
  1930.                                 }
  1931.                         } else {
  1932.                                 BusyError();
  1933.                         }
  1934.                         break;
  1935.                 case IDM_CLOSE:
  1936.                         /* close the output list -
  1937.                          * discard all results so far
  1938.                          */
  1939.                         if (!IsBusy()) {
  1940.                                 view_close(current_view);
  1941.                         }
  1942.                         break;
  1943.                 case IDM_PRINT:
  1944.                         /* print the current view -
  1945.                          * either the outline list of filenames,
  1946.                          * or the currently expanded file.
  1947.                          */
  1948.                         if (!IsBusy()) {
  1949.                                 DoPrint();
  1950.                         } else {
  1951.                                 BusyError();
  1952.                         }
  1953.                         break;
  1954.                 case IDM_TIME:
  1955.                         /* show time it took */
  1956.                         {       char msg[50];
  1957.                                 DWORD tim;
  1958.                                 if (IsBusy()) {
  1959.                                          BusyError();
  1960.                                 }
  1961.                                 else{
  1962.                                         tim = complist_querytime();
  1963.                                         wsprintf((LPTSTR)msg, LoadRcString(IDS_SECONDS), tim/1000, tim%1000);
  1964.                                 }
  1965.                         }
  1966.                         break;
  1967.                 case IDM_SAVELIST:
  1968.                         /* allow user to save list of same/different files
  1969.                          * to a text file. dialog box to give filename
  1970.                          * and select which types of file to include
  1971.                          */
  1972.                         complist_savelist(view_getcomplist(current_view), NULL, 0);
  1973.                         break;
  1974.                 case IDM_COPYFILES:
  1975.                         /*
  1976.                          * copy files that are same/different to a new
  1977.                          * root directory. dialog box allows user
  1978.                          * to select new root and inclusion options
  1979.                          */
  1980.                         if (current_view == NULL) {
  1981.                                 MessageBox(hWnd,
  1982.                                     LoadRcString(IDS_CREATE_DIFF_LIST),
  1983.                                     "WinDiff", MB_OK|MB_ICONSTOP);
  1984.                                 break;
  1985.                         }
  1986.                         if (SetBusy()) {
  1987.                                 ghThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)wd_copy,
  1988.                                         (LPVOID) current_view, 0, &threadid);
  1989.                                 if (ghThread == NULL)
  1990.                                 {
  1991.                                         wd_copy( (LPVOID) current_view);
  1992.                                 }
  1993.                         } else {
  1994.                                 BusyError();
  1995.                         }
  1996.                         break;
  1997.                 case IDM_ABOUT:
  1998.                         DialogBox( hInst, "About", hWnd, (DLGPROC)AboutBox);
  1999.                         break;
  2000.                 /* launch an editor on the current item - left, right or
  2001.                  * composite view
  2002.                  */
  2003.                 case IDM_EDITLEFT:
  2004.                         do_editthread(current_view, CI_LEFT);
  2005.                         break;
  2006.                 case IDM_EDITRIGHT:
  2007.                         do_editthread(current_view, CI_RIGHT);
  2008.                         break;
  2009.                 case IDM_EDITCOMP:
  2010.                         do_editthread(current_view, CI_COMP);
  2011.                         break;
  2012.                 /* allow customisation of the editor command line */
  2013.                 case IDM_SETEDIT:
  2014.                         if (StringInput(editor_cmdline, sizeof(editor_cmdline),
  2015.                                         LoadRcString(IDS_ENTER_EDT_CMD_LINE),
  2016.                                         "Windiff", editor_cmdline))  {
  2017.                                 WriteProfileString(APPNAME, "Editor",
  2018.                                         (LPCSTR)editor_cmdline);
  2019.                         }
  2020.                         break;
  2021.                 case IDM_LNRS:
  2022.                 case IDM_RNRS:
  2023.                 case IDM_NONRS:
  2024.                         /* option selects whether the line nrs displayed
  2025.                          * in expand mode are the line nrs in the left
  2026.                          * file, the right file or none
  2027.                          */
  2028.                         CheckMenuItem(GetMenu(hWnd),
  2029.                                 line_numbers, MF_UNCHECKED);
  2030.                         line_numbers = GET_WM_COMMAND_ID(wParam, lParam);
  2031.                         CheckMenuItem(GetMenu(hWnd), line_numbers, MF_CHECKED);
  2032.                         wsprintf((LPTSTR)str, "%d", line_numbers);
  2033.                         WriteProfileString(APPNAME, "LineNumbers", str);
  2034.                         /* change the display to show the line nr style
  2035.                          * chosen
  2036.                          */
  2037.                         view_changeviewoptions(current_view);
  2038.                         break;
  2039.                 /*
  2040.                  * options selecting which files to include in the
  2041.                  * outline listing, based on their state
  2042.                  */
  2043.                 case IDM_INCLEFT:
  2044.                         /* toggle flag in outline_include options */
  2045.                         outline_include ^= INCLUDE_LEFTONLY;
  2046.                         /* check/uncheck as necessary */
  2047.                         CheckMenuItem(hMenu, IDM_INCLEFT,
  2048.                               (outline_include & INCLUDE_LEFTONLY) ?
  2049.                                         MF_CHECKED:MF_UNCHECKED);
  2050.                         wsprintf((LPTSTR)str, "%d", outline_include);
  2051.                         WriteProfileString(APPNAME, "FileInclude", str);
  2052.                         view_changeviewoptions(current_view);
  2053.                         break;
  2054.                 case IDM_INCRIGHT:
  2055.                         outline_include ^= INCLUDE_RIGHTONLY;
  2056.                         CheckMenuItem(hMenu, IDM_INCRIGHT,
  2057.                               (outline_include & INCLUDE_RIGHTONLY) ?
  2058.                                         MF_CHECKED:MF_UNCHECKED);
  2059.                         wsprintf((LPTSTR)str, "%d", outline_include);
  2060.                         WriteProfileString(APPNAME, "FileInclude", str);
  2061.                         view_changeviewoptions(current_view);
  2062.                         break;
  2063.                 case IDM_INCSAME:
  2064.                         outline_include ^= INCLUDE_SAME;
  2065.                         CheckMenuItem(hMenu, IDM_INCSAME,
  2066.                               (outline_include & INCLUDE_SAME) ?
  2067.                                         MF_CHECKED:MF_UNCHECKED);
  2068.                         wsprintf((LPTSTR)str, "%d", outline_include);
  2069.                         WriteProfileString(APPNAME, "FileInclude", str);
  2070.                         view_changeviewoptions(current_view);
  2071.                         break;
  2072.                 case IDM_INCDIFFER:
  2073.                         outline_include ^= INCLUDE_DIFFER;
  2074.                         CheckMenuItem(hMenu, IDM_INCDIFFER,
  2075.                               (outline_include & INCLUDE_DIFFER) ?
  2076.                                         MF_CHECKED:MF_UNCHECKED);
  2077.                         wsprintf((LPTSTR)str, "%d", outline_include);
  2078.                         WriteProfileString(APPNAME, "FileInclude", str);
  2079.                         view_changeviewoptions(current_view);
  2080.                         break;
  2081.                 case IDM_UPDATE:
  2082.                         /* update the display.  Options or files may have changed */
  2083.                         /* discard lines  (thereby forcing re-read).
  2084.                          */
  2085.                         file_discardlines(compitem_getleftfile( (COMPITEM)lParam) );
  2086.                         file_discardlines(compitem_getrightfile( (COMPITEM)lParam) );
  2087.                         view_changediffoptions(current_view);
  2088.                         /* force repaint of bar window */
  2089.                         InvalidateRect(hwndBar, NULL, TRUE);
  2090.                         break;
  2091.                 case IDM_LONLY:
  2092.                 case IDM_RONLY:
  2093.                 case IDM_BOTHFILES:
  2094.                         /* option selects whether the expanded file
  2095.                          * show is the combined file, or just one
  2096.                          * or other of the input files.
  2097.                          *
  2098.                          * if we are not in expand mode, this also
  2099.                          * causes us to expand the selection
  2100.                          */
  2101.                         CheckMenuItem(GetMenu(hWnd), expand_mode, MF_UNCHECKED);
  2102.                         expand_mode = GET_WM_COMMAND_ID(wParam, lParam);
  2103.                         CheckMenuItem(GetMenu(hWnd), expand_mode, MF_CHECKED);
  2104.                         /* change the current view to show only the lines
  2105.                          * of the selected type.
  2106.                          */
  2107.                         if (DisplayMode == MODE_OUTLINE) {
  2108.                                 ToExpand(hWnd);
  2109.                         } else {
  2110.                                 view_changeviewoptions(current_view);
  2111.                         }
  2112.                         break;
  2113.                 case IDM_IGNBLANKS:
  2114.                         /* if selected, ignore all spaces and tabs on
  2115.                          * comparison - expand view only: outline view
  2116.                          * will still show that 'text files differ'
  2117.                          */
  2118.                         ignore_blanks = !ignore_blanks;
  2119.                         CheckMenuItem(hMenu, IDM_IGNBLANKS,
  2120.                                 ignore_blanks? MF_CHECKED:MF_UNCHECKED);
  2121.                         wsprintf((LPTSTR)str, "%d", ignore_blanks);
  2122.                         WriteProfileString(APPNAME, "Blanks", str);
  2123.                         /* invalidate all diffs since we have
  2124.                          * changed diff options, and re-do and display the
  2125.                          * current diff if we are in expand mode.
  2126.                          */
  2127.                         view_changediffoptions(current_view);
  2128.                         /* force repaint of bar window */
  2129.                         InvalidateRect(hwndBar, NULL, TRUE);
  2130.                         break;
  2131.                 case IDM_PICTURE:
  2132.                         /* do we show the bar picture in expand mode ? */
  2133.                         picture_mode = !picture_mode;
  2134.                         CheckMenuItem(hMenu, IDM_PICTURE,
  2135.                                 picture_mode? MF_CHECKED:MF_UNCHECKED);
  2136.                         wsprintf((LPTSTR)str, "%d", picture_mode);
  2137.                         WriteProfileString(APPNAME, "Picture", str);
  2138.                         DoResize(hWnd);
  2139.                         break;
  2140.                 case IDM_EXPAND:
  2141.                         /* show the expanded view of the
  2142.                          * selected file
  2143.                          */
  2144.                         if (current_view != NULL) {
  2145.                                 ToExpand(hWnd);
  2146.                         }
  2147.                         break;
  2148.                 case IDM_OUTLINE:
  2149.                         /* return to the outline view (list of filenames) */
  2150.                         ToOutline(hWnd);
  2151.                         break;
  2152.                 case IDM_FCHANGE:
  2153.                         /* find the next line in the current view
  2154.                          * that is not the same in both files -
  2155.                          * in outline view, finds the next filename that
  2156.                          * is not identical
  2157.                          */
  2158.                         FindNextChange();
  2159.                         break;
  2160.                 case IDM_FPCHANGE:
  2161.                         /* same as IDM_FCHANGE, but going backwards from
  2162.                          * current position
  2163.                          */
  2164.                         FindPrevChange();
  2165.                         break;
  2166.                 }
  2167.                 break;
  2168.         case WM_SIZE:
  2169.                 DoResize(hWnd);
  2170.                 break;
  2171.         case WM_SETFOCUS:
  2172.                 /* set the focus on the table class so it can process
  2173.                  * page-up /pagedown keys etc.
  2174.                  */
  2175.                 SetFocus(hwndRCD);
  2176.                 break;
  2177.         case WM_KEYDOWN:
  2178.                 /* although the table window has the focus, he passes
  2179.                  * back to us any keys he doesn't understand
  2180.                  * We handle escape here to mean 'return to outline view'
  2181.                  */
  2182.                 if (wParam == VK_ESCAPE) {
  2183.                         ToOutline(hWnd);
  2184.                 }
  2185.                 break;
  2186.         case WM_CLOSE:
  2187.                 /* don't allow close when busy - process this message in
  2188.                  * order to ensure this
  2189.                  */
  2190.                 if (IsBusy()) {
  2191.                         return(TRUE);
  2192.                 } else {
  2193.                         return(DefWindowProc(hWnd, message, wParam, lParam));
  2194.                 }
  2195.                 break;
  2196.         case WM_DESTROY:
  2197.                 DeleteTools();
  2198.                 PostQuitMessage(0);
  2199.                 break;
  2200.         case TM_CURRENTVIEW:
  2201.                 /* allow other people such as the bar window to query the
  2202.                  * current view
  2203.                  */
  2204.                 return((DWORD) current_view);
  2205.         default:
  2206.                 /* handle registered table messages */
  2207.                 if (message == table_msgcode) {
  2208.                         ret = TableServer(hWnd, wParam, lParam);
  2209.                         return(ret);
  2210.                 }
  2211.                 return(DefWindowProc(hWnd, message, wParam, lParam));
  2212.         }
  2213.         return(0);
  2214. }
  2215. /***************************************************************************
  2216.  * Function: My_mbschr
  2217.  *
  2218.  * Purpose:
  2219.  *
  2220.  * DBCS version of strchr
  2221.  *
  2222.  */
  2223. unsigned char * _CRTAPI1 My_mbschr(
  2224.     unsigned char *psz, unsigned short uiSep)
  2225. {
  2226.     while (*psz != '' && *psz != uiSep) {
  2227.         psz = CharNext(psz);
  2228.     }
  2229.     return *psz == uiSep ? psz : NULL;
  2230. }
  2231. /***************************************************************************
  2232.  * Function: My_mbsncpy
  2233.  *
  2234.  * Purpose:
  2235.  *
  2236.  * DBCS version of strncpy
  2237.  *
  2238.  */
  2239. unsigned char * _CRTAPI1 My_mbsncpy(
  2240. unsigned char *psz1, const unsigned char *psz2, size_t Length)
  2241. {
  2242.         int nLen = (int)Length;
  2243. unsigned char *pszSv = psz1;
  2244. while (0 < nLen) {
  2245. if (*psz2 == '') {
  2246. *psz1++ = '';
  2247. nLen--;
  2248. } else if (IsDBCSLeadByte(*psz2)) {
  2249. if (nLen == 1) {
  2250. *psz1 = '';
  2251. } else {
  2252. *psz1++ = *psz2++;
  2253. *psz1++ = *psz2++;
  2254. }
  2255. nLen -= 2;
  2256. } else {
  2257. *psz1++ = *psz2++;
  2258. nLen--;
  2259. }
  2260. }
  2261. return pszSv;
  2262. }
  2263. /***************************************************************************
  2264.  * Function: My_mbsrchr
  2265.  *
  2266.  * Purpose:
  2267.  *
  2268.  * DBCS version of strrchr
  2269.  *
  2270.  */
  2271. unsigned char * _CRTAPI1 My_mbsrchr(
  2272.     unsigned char *psz, unsigned short uiSep)
  2273. {
  2274.     unsigned char *pszHead;
  2275.     pszHead = psz;
  2276.     while (*psz != '') {
  2277.         psz++;
  2278.     }
  2279.     if (uiSep == '') {
  2280.         return psz;
  2281.     }
  2282.     while (psz > pszHead) {
  2283.         psz = CharPrev(pszHead, psz);
  2284.         if (*psz == uiSep) {
  2285.             break;
  2286.         }
  2287.     }
  2288.     return *psz == uiSep ? psz : NULL;
  2289. }
  2290. /***************************************************************************
  2291.  * Function: My_mbsncmp
  2292.  *
  2293.  * Purpose:
  2294.  *
  2295.  * DBCS version of strncmp
  2296.  * If 'nLen' splits a DBC, this function compares the DBC's 2nd byte also.
  2297.  *
  2298.  */
  2299. int _CRTAPI1 My_mbsncmp(
  2300.     const unsigned char *psz1, const unsigned char *psz2, size_t nLen)
  2301. {
  2302.     int Length = (int)nLen;
  2303.     while (0 < Length) {
  2304.         if ('' == *psz1 || '' == *psz2) {
  2305.             return *psz1 - *psz2;
  2306.         }
  2307.         if (IsDBCSLeadByte(*psz1) || IsDBCSLeadByte(*psz2)) {
  2308.             if (*psz1 != *psz2 || *(psz1+1) != *(psz2+1)) {
  2309.                 return *psz1 - *psz2;
  2310.             }
  2311.             psz1 += 2;
  2312.             psz2 += 2;
  2313.             Length -= 2;
  2314.         } else {
  2315.             if (*psz1 != *psz2) {
  2316.                 return *psz1 - *psz2;
  2317.             }
  2318.             psz1++;
  2319.             psz2++;
  2320.             Length--;
  2321.         }
  2322.     }
  2323.     return 0;
  2324. }