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

  1.  
  2. /******************************************************************************\
  3. *       This is a part of the Microsoft Source Code Samples. 
  4. *       Copyright (C) 1993-1997 Microsoft Corporation.
  5. *       All rights reserved. 
  6. *       This source code is only intended as a supplement to 
  7. *       Microsoft Development Tools and/or WinHelp documentation.
  8. *       See these sources for detailed information regarding the 
  9. *       Microsoft samples programs.
  10. \******************************************************************************/
  11.  
  12. /****************************** Module Header *******************************
  13. * Module Name: file.c
  14. *
  15. * This file contains the high level routines that begin opening
  16. * and saving files.
  17. *
  18. * Functions:
  19. *    Open()
  20. *    BuildFilterString()
  21. *    DoWeSave()
  22. *    Save()
  23. *    OpenCmdLineFile()
  24. *    FileInPath()
  25. *    ShowFileStatus()
  26. *    DifferentDirs()
  27. *    HasPath()
  28. *    WriteDWordPad()
  29. *    BuildDefSaveName()
  30. *    WriteTheFile()
  31. *    FormTempFileName()
  32. *    FileCat()
  33. *
  34. * Comments:
  35. *
  36. ****************************************************************************/
  37.  
  38. #include "dlgedit.h"
  39. #include "dlgfuncs.h"
  40. #include "dlgextrn.h"
  41. #include "dialogs.h"
  42.  
  43. #include <wchar.h>
  44.  
  45. #include <commdlg.h>
  46.  
  47.  
  48. /*
  49.  * File types.
  50.  */
  51. #define FILE_RES    0               // Resource (.RES) file.
  52. #define FILE_DLG    1               // Dialog (.DLG) file.
  53. #define FILE_INC    2               // Include (.H) file.
  54.  
  55. STATICFN VOID BuildDefSaveName(INT FileType, LPTSTR pszFullFileName,
  56.     LPTSTR pszFileName, LPTSTR pszOtherFullFileName, LPTSTR pszOtherFileName,
  57.     LPTSTR pszFullFileNameBuffer, INT cchBuffer);
  58. STATICFN BOOL WriteTheFile(LPTSTR pszFile, INT fmt);
  59. STATICFN VOID FormTempFileName(LPTSTR pszBaseName,  LPTSTR pszBuffer);
  60. STATICFN VOID FileCat(LPTSTR pchName, LPTSTR pchCat, BOOL fChop);
  61.  
  62.  
  63.  
  64. /************************************************************************
  65. * Open
  66. *
  67. * Handles opening of resource and include files.
  68. * Saves current dialog in the resource.
  69. * Might put up a message box.
  70. * Cancels moves.
  71. * Changes szFullResFile, pszResFile, szFullIncludeFile, pszIncludeFile
  72. * Puts up dialog boxes.
  73. * Restores dialog box from resource.
  74. * Sets changed flags.
  75. *
  76. * Arguments:
  77. *   INT FileType - FILE_RESOURCE or FILE_INCLUDE.
  78. *
  79. * Returns:
  80. *   TRUE if successful, FALSE if not.
  81. *
  82. ************************************************************************/
  83.  
  84. BOOL Open(
  85.     INT FileType)
  86. {
  87.     BOOL fSuccess;
  88.     BOOL fGotName;
  89.     OPENFILENAME ofn;
  90.     TCHAR szNewFileName[CCHMAXPATH];
  91.     TCHAR szInitialDir[CCHMAXPATH];
  92.     TCHAR szFilter[CCHTEXTMAX];
  93.     INT idPrevDlg;
  94.  
  95.     /*
  96.      * Cancel any outstanding selection(s).
  97.      */
  98.     CancelSelection(TRUE);
  99.  
  100.     /*
  101.      * Put current dialog back into the resource buffer.
  102.      */
  103.     if (!SynchDialogResource())
  104.         return FALSE;
  105.  
  106.     /*
  107.      * Begin setting up the globals and the open file dialog structure.
  108.      */
  109.     fSuccess = FALSE;
  110.     *szNewFileName = CHAR_NULL;
  111.  
  112.     /*
  113.      * Build up the filter string.
  114.      */
  115.     BuildFilterString(FileType, szFilter);
  116.  
  117.     ofn.lStructSize = sizeof(ofn);
  118.     ofn.hwndOwner = ghwndMain;
  119.     ofn.hInstance = NULL;
  120.     ofn.lpstrFilter = szFilter;
  121.     ofn.lpstrCustomFilter = NULL;
  122.     ofn.nMaxCustFilter = 0;
  123.     ofn.nFilterIndex = 1;
  124.     ofn.lpstrFile = szNewFileName;
  125.     ofn.nMaxFile = CCHMAXPATH;
  126.     ofn.lpstrFileTitle = NULL;
  127.     ofn.nMaxFileTitle = 0;
  128.  
  129.     if (FileType == FILE_INCLUDE) {
  130.         /*
  131.          * If there is a res file, set the default include file
  132.          * name to open to be the basename of the res file with
  133.          * a .H extension, if such a file exists.  We use szInitialDir
  134.          * here as a temporary buffer.
  135.          */
  136.         if (pszResFile) {
  137.             lstrcpy(szInitialDir, szFullResFile);
  138.             FileCat(szInitialDir, ids(IDS_DOTH), TRUE);
  139.             if (GetFileAttributes(szInitialDir) != -1) {
  140.                 lstrcpy(szNewFileName, pszResFile);
  141.                 FileCat(szNewFileName, ids(IDS_DOTH), TRUE);
  142.             }
  143.         }
  144.  
  145.         ofn.lpstrTitle = ids(IDS_INCOPENTITLE);
  146.         ofn.lpstrDefExt = ids(IDS_INCEXT);
  147.     }
  148.     else {
  149.         ofn.lpstrTitle = ids(IDS_RESOPENTITLE);
  150.         ofn.lpstrDefExt = ids(IDS_RESEXT);
  151.     }
  152.  
  153.     /*
  154.      * If they have already opened one res file, start looking for
  155.      * any new files to open in the same directory.  Otherwise, just
  156.      * default to the current directory.
  157.      */
  158.     if (pszResFile) {
  159.         lstrcpy(szInitialDir, szFullResFile);
  160.         *FileInPath(szInitialDir) = CHAR_NULL;
  161.         ofn.lpstrInitialDir = szInitialDir;
  162.     }
  163.     else {
  164.         ofn.lpstrInitialDir = NULL;
  165.     }
  166.  
  167.     ofn.Flags = OFN_HIDEREADONLY | OFN_SHOWHELP | OFN_FILEMUSTEXIST;
  168.     ofn.lCustData = 0;
  169.     ofn.lpfnHook = NULL;
  170.     ofn.lpTemplateName = NULL;
  171.  
  172.     /*
  173.      * Fire off the dialog box to open the file.
  174.      */
  175.     EnteringDialog((FileType == FILE_INCLUDE) ?
  176.             DID_COMMONFILEOPENINCLUDE : DID_COMMONFILEOPENRES,
  177.             &idPrevDlg, TRUE);
  178.     fGotName = GetOpenFileName(&ofn);
  179.     EnteringDialog(idPrevDlg, NULL, FALSE);
  180.     if (fGotName) {
  181.         if (FileType == FILE_INCLUDE) {
  182.             if (OpenIncludeFile(szNewFileName)) {
  183.                 /*
  184.                  * Since we just loaded a new include file, we mark the
  185.                  * resource as changed so that the .RES and .DLG files
  186.                  * will be written out with the proper name in the
  187.                  * DLGINCLUDE statement.
  188.                  */
  189.                 gfResChged = TRUE;
  190.                 fSuccess = TRUE;
  191.             }
  192.         }
  193.         else {
  194.             if (OpenResFile(szNewFileName))
  195.                 fSuccess = TRUE;
  196.         }
  197.     }
  198.  
  199.     ShowFileStatus(TRUE);
  200.  
  201.     return fSuccess;
  202. }
  203.  
  204.  
  205.  
  206. /************************************************************************
  207. * BuildFilterString
  208. *
  209. * This function creates a filter string to be passed into the
  210. * standard file open and save dialogs.  This will be something like:
  211. * "Resource (*.res)\0*.res\0\0"
  212. *
  213. * Arguments:
  214. *   INT FileType      - Flags for type of file, FILE_INCLUDE, FILE_RESOURCE
  215. *                       or FILE_DLL.
  216. *   LPTSTR pszFilter  - Where to return the filter string.
  217. *
  218. ************************************************************************/
  219.  
  220. VOID BuildFilterString(
  221.     INT FileType,
  222.     LPTSTR pszFilter)
  223. {
  224.     INT idsFileSpecName;
  225.     INT idsFileSpec;
  226.     LPTSTR psz;
  227.  
  228.     if (FileType & FILE_INCLUDE) {
  229.         idsFileSpecName = IDS_DEFINCFILESPECNAME;
  230.         idsFileSpec = IDS_DEFINCFILESPEC;
  231.     }
  232.     else if (FileType & FILE_RESOURCE) {
  233.         idsFileSpecName = IDS_DEFRESFILESPECNAME;
  234.         idsFileSpec = IDS_DEFRESFILESPEC;
  235.     }
  236.     else { // Must be a DLL.
  237.         idsFileSpecName = IDS_DEFDLLFILESPECNAME;
  238.         idsFileSpec = IDS_DEFDLLFILESPEC;
  239.     }
  240.  
  241.     /*
  242.      * Build up the filter string.  This will be something like:
  243.      * "Resource (*.res)\0*.res\0\0"
  244.      */
  245.     psz = (LPTSTR)WriteSz(pszFilter, ids(idsFileSpecName));
  246.     psz = (LPTSTR)WriteSz(psz, ids(idsFileSpec));
  247.     *psz = CHAR_NULL;
  248. }
  249.  
  250.  
  251.  
  252. /************************************************************************
  253. * OpenCmdLineFile
  254. *
  255. * Handles opening of the resource file specified on the command line.
  256. *
  257. * Arguments:
  258. *   LPTSTR - pointer to the file name string
  259. *
  260. ************************************************************************/
  261.  
  262. VOID OpenCmdLineFile(
  263.     LPTSTR pszFileName)
  264. {
  265.     TCHAR szFullPath[CCHMAXPATH];
  266.     LPTSTR pszOnlyFileName;
  267.  
  268.     if (SearchPath(L".", pszFileName, ids(IDS_DOTRES), CCHMAXPATH,
  269.             szFullPath, &pszOnlyFileName) == -1) {
  270.         Message(MSG_CANTOPENRES, pszFileName);
  271.     }
  272.     else {
  273.         OpenResFile(szFullPath);
  274.     }
  275. }
  276.  
  277.  
  278.  
  279. /************************************************************************
  280. * DoWeSave
  281. *
  282. * This function checks to see if the include file or the resource file
  283. * needs to be saved.  It first checks the changed flags and if TRUE,
  284. * asks the user if they want to save the file.  If they say yes, it
  285. * calls Save to do the actual work.
  286. *
  287. * Arguments:
  288. *     INT rgbFlags = FILE_RESOURCE or FILE_INCLUDE (but not both).
  289. *
  290. * Returns:
  291. *     IDYES    - The user wanted to save the file AND the save
  292. *                was successful, or the file has not been changed.
  293. *     IDNO     - The file had been changed but the user did not
  294. *                want it saved.
  295. *     IDCANCEL - The file had been changed, and either the user wanted
  296. *                it saved and the save failed, or they specified that
  297. *                they wanted the operation cancelled.
  298. *
  299. ************************************************************************/
  300.  
  301. INT DoWeSave(
  302.     INT rgbFlags)
  303. {
  304.     LPTSTR pszFile;
  305.     INT MsgCode;
  306.     BOOL fChanged;
  307.     INT nRet = IDYES;
  308.  
  309.     /*
  310.      * First set variables for current case.
  311.      */
  312.     if (rgbFlags & FILE_RESOURCE) {
  313.         fChanged = gfResChged;
  314.         MsgCode = MSG_CLOSING;
  315.         pszFile = pszResFile ? pszResFile : ids(IDS_UNTITLED);
  316.     }
  317.     else {
  318.         fChanged = gfIncChged;
  319.         MsgCode = MSG_INCLCLOSING;
  320.         pszFile = pszIncludeFile ? pszIncludeFile : ids(IDS_UNTITLED);
  321.     }
  322.  
  323.     if (fChanged) {
  324.         nRet = Message(MsgCode, pszFile);
  325.         if (nRet == IDYES) {
  326.             if (!Save(FILE_NOSHOW | rgbFlags))
  327.                 nRet = IDCANCEL;
  328.         }
  329.     }
  330.  
  331.     return nRet;
  332. }
  333.  
  334.  
  335.  
  336. /************************************************************************
  337. * Save
  338. *
  339. * Handles all saving of files based on menu choice.  Does a
  340. * CancelSelection and a SynchDialogResource.
  341. *
  342. * Arguments:
  343. *     INT rgbFlags - Can include FILE_SHOW, FILE_INCLUDE, FILE_SAVEAS.
  344. *
  345. * Returns:
  346. *     TRUE if the file was saved, FALSE if not.
  347. *
  348. ************************************************************************/
  349.  
  350. BOOL Save(
  351.     INT rgbFlags)
  352. {
  353.     OPENFILENAME ofn;
  354.     BOOL fGotName;
  355.     LPTSTR pszFileName;
  356.     LPTSTR pszFileNameDlg;
  357.     LPTSTR pszFullFileName;
  358.     BOOL fSuccess = FALSE;
  359.     TCHAR szInitialDir[CCHMAXPATH];
  360.     TCHAR szSaveFileName[CCHMAXPATH];
  361.     TCHAR szSaveFileNameDlg[CCHMAXPATH];
  362.     TCHAR szFilter[CCHTEXTMAX];
  363.     INT idPrevDlg;
  364.  
  365.     /*
  366.      * Put current dialog back into the resource buffer.
  367.      */
  368.     if (!SynchDialogResource())
  369.         return FALSE;
  370.  
  371.     /*
  372.      * If the file being saved has not been named, force a "Save As".
  373.      */
  374.     if ((rgbFlags & FILE_INCLUDE) ? !pszIncludeFile : !pszResFile)
  375.         rgbFlags |= FILE_SAVEAS;
  376.  
  377.     if (rgbFlags & FILE_SAVEAS) {
  378.         ofn.lStructSize = sizeof(ofn);
  379.         ofn.hwndOwner = ghwndMain;
  380.         ofn.hInstance = NULL;
  381.  
  382.         /*
  383.          * Build up the filter string.
  384.          */
  385.         BuildFilterString(rgbFlags, szFilter);
  386.         ofn.lpstrFilter = szFilter;
  387.         ofn.lpstrCustomFilter = NULL;
  388.         ofn.nMaxCustFilter = 0;
  389.         ofn.nFilterIndex = 1;
  390.  
  391.         ofn.lpstrFile = szSaveFileName;
  392.         ofn.nMaxFile = CCHMAXPATH;
  393.         ofn.lpstrFileTitle = NULL;
  394.         ofn.nMaxFileTitle = 0;
  395.  
  396.         if (rgbFlags & FILE_INCLUDE) {
  397.             ofn.lpstrTitle = ids(IDS_INCSAVETITLE);
  398.             ofn.lpstrDefExt = ids(IDS_INCEXT);
  399.             BuildDefSaveName(FILE_INCLUDE,
  400.                     szFullIncludeFile, pszIncludeFile,
  401.                     szFullResFile, pszResFile,
  402.                     szInitialDir, CCHMAXPATH);
  403.         }
  404.         else {
  405.             ofn.lpstrTitle = ids(IDS_RESSAVETITLE);
  406.             ofn.lpstrDefExt = ids(IDS_RESEXT);
  407.             BuildDefSaveName(FILE_RESOURCE,
  408.                     szFullResFile, pszResFile,
  409.                     szFullIncludeFile, pszIncludeFile,
  410.                     szInitialDir, CCHMAXPATH);
  411.         }
  412.  
  413.         /*
  414.          * At this point, szInitialDir contains the full path to
  415.          * the suggested save file name.  Find the end of the path,
  416.          * copy just the filename to the file name buffer and cut
  417.          * the filename portion off the initial directory buffer.
  418.          */
  419.         pszFileName = FileInPath(szInitialDir);
  420.         lstrcpy(szSaveFileName, pszFileName);
  421.         *pszFileName = CHAR_NULL;
  422.         ofn.lpstrInitialDir = szInitialDir;
  423.  
  424.         ofn.Flags = OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY | OFN_SHOWHELP;
  425.         ofn.lCustData = 0;
  426.         ofn.lpfnHook = NULL;
  427.         ofn.lpTemplateName = NULL;
  428.  
  429.         /*
  430.          * Fire off the dialog box to get the file name to use.
  431.          */
  432.         EnteringDialog((rgbFlags & FILE_INCLUDE) ?
  433.                 DID_COMMONFILESAVEINCLUDE : DID_COMMONFILESAVERES,
  434.                 &idPrevDlg, TRUE);
  435.         fGotName = GetSaveFileName(&ofn);
  436.         EnteringDialog(idPrevDlg, NULL, FALSE);
  437.         if (fGotName) {
  438.             pszFullFileName = szSaveFileName;
  439.             pszFileName = FileInPath(szSaveFileName);
  440.             fSuccess = TRUE;
  441.         }
  442.     }
  443.     else {
  444.         if (rgbFlags & FILE_INCLUDE) {
  445.             pszFileName = pszIncludeFile;
  446.             pszFullFileName = szFullIncludeFile;
  447.         }
  448.         else {
  449.             pszFileName = pszResFile;
  450.             pszFullFileName = szFullResFile;
  451.         }
  452.  
  453.         fSuccess = TRUE;
  454.     }
  455.  
  456.     if (fSuccess) {
  457.         if (rgbFlags & FILE_INCLUDE) {
  458.             /*
  459.              * Save include file.
  460.              */
  461.             if (!WriteTheFile(pszFullFileName, FILE_INC)) {
  462.                 Message(MSG_CANTCREATE, pszFileName);
  463.                 fSuccess = FALSE;
  464.             }
  465.         }
  466.         else {
  467.             /*
  468.              * Form the same name as the .res file but with
  469.              * a .dlg extension.
  470.              */
  471.             lstrcpy(szSaveFileNameDlg, pszFullFileName);
  472.             pszFileNameDlg = FileInPath(szSaveFileNameDlg);
  473.             FileCat(pszFileNameDlg, ids(IDS_DOTDLG), TRUE);
  474.  
  475.             /*
  476.              * Save .RES file, then the .DLG file.  It is done
  477.              * in this order so that makes wil notice that the
  478.              * .dlg file has a newer time stamp than the .res
  479.              * and will cause the .res to be rebuilt.  This
  480.              * could be necessary to pick up other changes
  481.              * in the resources in a project.
  482.              */
  483.             if (!WriteTheFile(pszFullFileName, FILE_RES)) {
  484.                 Message(MSG_CANTCREATE, pszFileName);
  485.                 fSuccess = FALSE;
  486.             }
  487.             else if (!WriteTheFile(szSaveFileNameDlg, FILE_DLG)) {
  488.                 Message(MSG_CANTCREATE, pszFileNameDlg);
  489.                 fSuccess = FALSE;
  490.             }
  491.             else {
  492.                 /*
  493.                  * Successfully saved both files.  Update our
  494.                  * globals.
  495.                  */
  496.                 lstrcpy(szFullResFile, pszFullFileName);
  497.                 pszResFile = FileInPath(szFullResFile);
  498.                 gfResChged = FALSE;
  499.             }
  500.         }
  501.     }
  502.  
  503.     ShowFileStatus(TRUE);
  504.  
  505.     return fSuccess;
  506. }
  507.  
  508.  
  509.  
  510. /************************************************************************
  511. * BuildDefSaveName
  512. *
  513. * This function takes the filenames of the current resource and include
  514. * files and builds the default filename that will be shown in the
  515. * "Save As" dialog.  If the current file is still untitled, it will
  516. * attempt to pick a default name based on the other files name.
  517. *
  518. * To use, pass in the file type (FILE_RESOURCE or FILE_INCLUDE) and
  519. * give the current file name and full file name of both the current
  520. * file you are building, and the other type of file.  The following
  521. * rules will be followed, in order:
  522. *
  523. *   1. If the file name is valid (not NULL) and it is either the
  524. *      include file we are naming or it is the res file but there
  525. *      is no include file, it will copy the full file name to the
  526. *      output buffer.
  527. *
  528. *   2. If the other file name is valid, it will take this name, add the
  529. *      appropriate extension and copy it to the output buffer.
  530. *
  531. *   3. If neither of the file names are valid (they are BOTH untitled),
  532. *      it will assume the current directory and make a default file
  533. *      name with the appropriate extension.
  534. *
  535. * Rule 1 is a little complicated, but it's purpose is to make it so
  536. * that if a default res file name is being requested, and they changed
  537. * the directory and/or name for the include file that was just saved,
  538. * the default directory and name for the res file will be the same
  539. * directory and base name as the new include file directory and name.
  540. *
  541. * Arguments:
  542. *   INT FileType                 - Either FILE_RESOURE or FILE_INCLUDE.
  543. *   LPTSTR pszFullFileName       - The full file name.  This will only
  544. *                                  be used if pszFileName is not NULL.
  545. *   LPTSTR pszFileName           - File name to use, or NULL if it is
  546. *                                  currently untitled.
  547. *   LPTSTR pszOtherFullFileName  - Full file name of the other file.  Only
  548. *                                  considered valid if pszOtherFileName is
  549. *                                  not NULL.
  550. *   LPTSTR pszOtherFileName      - File name of the other file, or NULL if
  551. *                                  it is untitled.
  552. *   LPTSTR pszFullFileNameBuffer - Where to put the full file name.
  553. *   INT cchBuffer                - Size of the buffer in characters.
  554. *
  555. ************************************************************************/
  556.  
  557. STATICFN VOID BuildDefSaveName(
  558.     INT FileType,
  559.     LPTSTR pszFullFileName,
  560.     LPTSTR pszFileName,
  561.     LPTSTR pszOtherFullFileName,
  562.     LPTSTR pszOtherFileName,
  563.     LPTSTR pszFullFileNameBuffer,
  564.     INT cchBuffer)
  565. {
  566.     TCHAR szBuffer[CCHMAXPATH];
  567.  
  568.     if (pszFileName && (FileType == FILE_INCLUDE || !pszOtherFileName)) {
  569.         /*
  570.          * Simple case.  The file already has a title.
  571.          */
  572.         lstrcpy(pszFullFileNameBuffer, pszFullFileName);
  573.     }
  574.     else if (pszOtherFileName) {
  575.         /*
  576.          * Copy the other files name and add the proper extension.
  577.          */
  578.         lstrcpy(pszFullFileNameBuffer, pszOtherFullFileName);
  579.         FileCat(pszFullFileNameBuffer,
  580.                 (FileType == FILE_INCLUDE) ? ids(IDS_DOTH) :
  581.                 ids(IDS_DOTRES), TRUE);
  582.     }
  583.     else {
  584.         /*
  585.          * Pick a default name in the current directory and
  586.          * add the proper extension.
  587.          */
  588.         lstrcpy(szBuffer, ids(IDS_DEFSAVENAME));
  589.         FileCat(szBuffer,
  590.                 (FileType == FILE_INCLUDE) ? ids(IDS_DOTH) :
  591.                 ids(IDS_DOTRES), TRUE);
  592.         GetFullPathName(szBuffer, cchBuffer, pszFullFileNameBuffer, NULL);
  593.     }
  594. }
  595.  
  596.  
  597.  
  598. /************************************************************************
  599. * WriteTheFile
  600. *
  601. * This function accepts a pointer to a resource buffer and a format
  602. * type.  It writes the buffer out in the appropriate format.  It
  603. * gets the file name from pszFile, adding the appropriate extension
  604. * for the type of file.  The file is first written to a temporary file
  605. * then the old file is removed and finally the new file is renamed.
  606. *
  607. * Arguments:
  608. *   LPTSTR pszFile  - The name to save to.
  609. *   INT fmt         - format to write the buffer out in,
  610. *                     FILE_RES, FILE_INC or FILE_DLG.
  611. *
  612. * Returns:
  613. *     TRUE => File successfully written.
  614. *     FALSE => Failure in writing file.
  615. *
  616. ************************************************************************/
  617.  
  618. STATICFN BOOL WriteTheFile(
  619.     LPTSTR pszFile,
  620.     INT fmt)
  621. {
  622.     TCHAR szTempFile[CCHMAXPATH]; /* Used for temporary filename            */
  623.     TCHAR szSrcFile[CCHMAXPATH];  /* Source file with proper extension      */
  624.     HANDLE hfWrite;
  625.     HCURSOR hcurSave;
  626.     BOOL fSuccess = FALSE;
  627.     WORD idsExt;
  628.  
  629.     hcurSave = SetCursor(hcurWait);
  630.  
  631.     switch (fmt) {
  632.         case FILE_RES:
  633.             idsExt = IDS_DOTRES;
  634.             break;
  635.  
  636.         case FILE_DLG:
  637.             idsExt = IDS_DOTDLG;
  638.             break;
  639.  
  640.         case FILE_INC:
  641.             idsExt = IDS_DOTH;
  642.             break;
  643.     }
  644.  
  645.     /*
  646.      * Append appropriate file name extension.
  647.      */
  648.     lstrcpy(szSrcFile, pszFile);
  649.     FileCat(szSrcFile, ids(idsExt), fmt == FILE_DLG ? TRUE : FALSE);
  650.  
  651.     /*
  652.      * Generate appropriate temporary file name in the same directory.
  653.      * It is done in the same directory so that a simple rename can
  654.      * be done later.
  655.      */
  656.     FormTempFileName(szSrcFile, szTempFile);
  657.  
  658.     if ((hfWrite = CreateFile(szTempFile, GENERIC_READ | GENERIC_WRITE,
  659.             FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
  660.             NULL)) == (HANDLE)-1)
  661.         goto Exit;
  662.  
  663.     switch (fmt) {
  664.         case FILE_RES:
  665.             if (!WriteRes(hfWrite, szSrcFile))
  666.                 goto CloseAndExit;
  667.  
  668.             break;
  669.  
  670.         case FILE_DLG:
  671.             if (!WriteDlg(hfWrite, szSrcFile))
  672.                 goto CloseAndExit;
  673.  
  674.             break;
  675.  
  676.         case FILE_INC:
  677.             if (!WriteInc(hfWrite))
  678.                 goto CloseAndExit;
  679.  
  680.             break;
  681.     }
  682.  
  683.     CloseHandle((HANDLE)hfWrite);
  684.     DeleteFile(szSrcFile);
  685.     if (!MoveFile(szTempFile, szSrcFile)) {
  686.         DeleteFile(szTempFile);
  687.         goto Exit;
  688.     }
  689.  
  690.     fSuccess = TRUE;
  691.  
  692.     /*
  693.      * If we just wrote to the include file, read it to get the new
  694.      * file offsets, etc.
  695.      */
  696.     if (fmt == FILE_INC) {
  697.         if (!OpenIncludeFile(szSrcFile))
  698.             fSuccess = FALSE;
  699.     }
  700.  
  701. Exit:
  702.     SetCursor(hcurSave);
  703.     return fSuccess;
  704.  
  705. CloseAndExit:
  706.     CloseHandle(hfWrite);
  707.     DeleteFile(szTempFile);
  708.     SetCursor(hcurSave);
  709.     return fSuccess;
  710. }
  711.  
  712.  
  713.  
  714. /************************************************************************
  715. * FormTempFileName
  716. *
  717. * This function forms a temporary file name in the provided string.
  718. * The provided string is assumed to have been filled with a filename
  719. * that includes a path.  The temp file will be created in the same
  720. * directory as the file that is currently in the string.
  721. *
  722. * Arguments:
  723. *   LPTSTR pszBaseName - The base name (a filename that includes a path).
  724. *   LPTSTR pszBuffer   - Where to return the
  725. *
  726. ************************************************************************/
  727.  
  728. STATICFN VOID FormTempFileName(
  729.     LPTSTR pszBaseName,
  730.     LPTSTR pszBuffer)
  731. {
  732.     TCHAR szBuffer[CCHMAXPATH];
  733.     LPTSTR psz;
  734.  
  735.     /*
  736.      * Cut the base file name down to just the path portion.
  737.      */
  738.     lstrcpy(szBuffer, pszBaseName);
  739.     psz = FileInPath(szBuffer);
  740.     psz--;
  741.     *psz = TEXT('\0');
  742.  
  743.     /*
  744.      * Create a temporary file in the same directory.
  745.      */
  746.     GetTempFileName(szBuffer, L"dlg", 0, pszBuffer);
  747. }
  748.  
  749.  
  750.  
  751. /************************************************************************
  752. * FileInPath
  753. *
  754. * This function takes a path and returns a pointer to the file name
  755. * portion of it.  For instance, it will return a pointer to
  756. * "abc.res" if it is given the following path: "c:\windows\abc.res".
  757. *
  758. * Arguments:
  759. *   LPTSTR pszPath - Path to look through.
  760. *
  761. ************************************************************************/
  762.  
  763. LPTSTR FileInPath(
  764.     LPTSTR pszPath)
  765. {
  766.     LPTSTR psz;
  767.  
  768.     psz = pszPath + lstrlen(pszPath);
  769.     while (psz > pszPath) {
  770.         psz--;
  771.         if (*psz == CHAR_BACKSLASH || *psz == CHAR_COLON) {
  772.             psz++;
  773.             break;
  774.         }
  775.     }
  776.  
  777.     return psz;
  778. }
  779.  
  780.  
  781.  
  782. /************************************************************************
  783. * FileCat
  784. *
  785. * This function puts the extension pchCat on the file spec pch.
  786. * If fChop, this is done regardless of whether pch has an extension
  787. * or not (replacing the old extension).  Otherwise, pchCat is added
  788. * only if there is no extension on the spec pch.
  789. *
  790. * Arguments:
  791. *     LPTSTR pch        - The file spec to "cat" the extension to.
  792. *     LPTSTR pchCat     - The extension to "cat" on to pch,
  793. *                         including the '.'
  794. *
  795. ************************************************************************/
  796.  
  797. STATICFN VOID FileCat(
  798.     LPTSTR pchName,
  799.     LPTSTR pchCat,
  800.     BOOL fChop)
  801. {
  802.     LPTSTR pch;
  803.  
  804.     pch = pchName + lstrlen(pchName);
  805.     pch--;
  806.  
  807.     /* back up to '.' or '\\' */
  808.     while (*pch != CHAR_DOT) {
  809.         if (*pch == CHAR_BACKSLASH || pch <= pchName) {
  810.             /* no extension, add one */
  811.             lstrcat(pchName, pchCat);
  812.             return;
  813.         }
  814.  
  815.         pch--;
  816.     }
  817.  
  818.     if (fChop)
  819.         lstrcpy(pch, pchCat);
  820. }
  821.  
  822.  
  823.  
  824. /************************************************************************
  825. * ShowFileStatus
  826. *
  827. * This function displays the title of the Dialog Editor, along with
  828. * the file names for the RES and H files with asterisks if they have
  829. * changed.  It displays this information only if one of these items
  830. * has changed or if fForce is TRUE.
  831. *
  832. * Arguments:
  833. *   BOOL fForce - TRUE if the title should be updated even if the value
  834. *                 of gfResChged or gfIncChged has not changed since the
  835. *                 last call.  This function should be called with fForce
  836. *                 equal to TRUE if it is known that one of the file names
  837. *                 has just been changed.
  838. *
  839. ************************************************************************/
  840.  
  841. VOID ShowFileStatus(
  842.     BOOL fForce)
  843. {
  844.     static BOOL fResChgedSave = FALSE;
  845.     static BOOL fIncChgedSave = FALSE;
  846.     TCHAR szTitle[CCHTEXTMAX];
  847.  
  848.     if (gfResChged != fResChgedSave || gfIncChged != fIncChgedSave ||
  849.             fForce) {
  850.         lstrcpy(szTitle, ids(IDS_DLGEDIT));
  851.         lstrcat(szTitle, L" - ");
  852.         lstrcat(szTitle, pszResFile ? pszResFile : ids(IDS_UNTITLED));
  853.         if (gfResChged)
  854.             lstrcat(szTitle, L"*");
  855.  
  856.         lstrcat(szTitle, L", ");
  857.         lstrcat(szTitle, pszIncludeFile ? pszIncludeFile : ids(IDS_UNTITLED));
  858.         if (gfIncChged)
  859.             lstrcat(szTitle, L"*");
  860.  
  861.         SetWindowText(ghwndMain, szTitle);
  862.  
  863.         fResChgedSave = gfResChged;
  864.         fIncChgedSave = gfIncChged;
  865.     }
  866. }
  867.  
  868.  
  869.  
  870. /************************************************************************
  871. * DifferentDirs
  872. *
  873. * This function returns TRUE if the given full paths are to files
  874. * that are in different directories.
  875. *
  876. * Arguments:
  877. *   LPTSTR pszPath1 - First path.
  878. *   LPTSTR pszPath2 - Second path.
  879. *
  880. ************************************************************************/
  881.  
  882. BOOL DifferentDirs(
  883.     LPTSTR pszPath1,
  884.     LPTSTR pszPath2)
  885. {
  886.     INT nLen1;
  887.     INT nLen2;
  888.     LPTSTR pszFile1;
  889.     LPTSTR pszFile2;
  890.  
  891.     pszFile1 = FileInPath(pszPath1);
  892.     pszFile2 = FileInPath(pszPath2);
  893.  
  894.     nLen1 = lstrlen(pszPath1) - lstrlen(pszFile1);
  895.     nLen2 = lstrlen(pszPath2) - lstrlen(pszFile2);
  896.     if (nLen1 != nLen2 || _wcsnicmp(pszPath1, pszPath2, nLen1) != 0)
  897.         return TRUE;
  898.     else
  899.         return FALSE;
  900. }
  901.  
  902.  
  903.  
  904. /************************************************************************
  905. * HasPath
  906. *
  907. * This function returns TRUE if the given filespec includes a path
  908. * specification.  It returns false if it is a filename without a
  909. * path.
  910. *
  911. * A filespec is considered to have a path if a backslash character (\)
  912. * is found in it.
  913. *
  914. * Arguments:
  915. *   LPTSTR pszFileSpec - File spec to check.
  916. *
  917. ************************************************************************/
  918.  
  919. BOOL HasPath(
  920.     LPTSTR pszFileSpec)
  921. {
  922.     LPTSTR psz;
  923.  
  924.     for (psz = pszFileSpec; *psz; psz = CharNext(psz))
  925.         if (*psz == CHAR_BACKSLASH)
  926.             return TRUE;
  927.     return FALSE;
  928. }
  929.  
  930.  
  931.  
  932. /************************************************************************
  933. * WriteDWordPad
  934. *
  935. * This function writes nulls to the specified file until it is
  936. * dword aligned.  If the file is already dword aligned, nothing
  937. * will be written.
  938. *
  939. * Arguments:
  940. *   HANDLE hf    - The file to write to.
  941. *   DWORD cbFile - Where the file pointer is at in the file.
  942. *
  943. * Returns:
  944. *   TRUE if successful, FALSE otherwise.
  945. *
  946. ************************************************************************/
  947.  
  948. BOOL WriteDWordPad(
  949.     HANDLE hf,
  950.     DWORD cbFile)
  951. {
  952.     static BYTE Buf[3] = {0, 0, 0};
  953.     WORD cb;
  954.  
  955.     cb = (WORD)((4 - (((WORD)cbFile) & 3)) % 4);
  956.     if (cb) {
  957.         if (_lwrite((HFILE)hf, (LPSTR)Buf, cb) == -1)
  958.             return FALSE;
  959.     }
  960.  
  961.     return TRUE;
  962. }
  963.