//////////////////////////////////////////////////////////////////////////////// // // IIIIIII SSSSSS // II SS InstallShield (R) // II SSSSSS (c) 1996-1997, InstallShield Software Corporation // II SS (c) 1990-1996, InstallShield Corporation // IIIIIII SSSSSS All Rights Reserved. // // // This code is generated as a starting setup template. You should // modify it to provide all necessary steps for your setup. // // // File Name: Setup.rul // // Description: InstallShield script // // Comments: This template script performs a basic setup on a // Windows 95 or Windows NT 4.0 platform. With minor // modifications, this template can be adapted to create // new, customized setups. // //////////////////////////////////////////////////////////////////////////////// // Include header file #include "ifx.h" ////////////////////// string defines //////////////////////////// #define UNINST_LOGFILE_NAME "Uninst.isu" //////////////////// installation declarations /////////////////// // ----- DLL prototypes ----- // your DLL prototypes // ---- script prototypes ----- // generated prototype ShowDialogs(); prototype MoveFileData(); prototype HandleMoveDataError( NUMBER ); prototype ProcessBeforeDataMove(); prototype ProcessAfterDataMove(); prototype SetupRegistry(); prototype SetupFolders(); prototype CleanUpInstall(); prototype SetupInstall(); prototype SetupScreen(); prototype CheckListScreen(); prototype CheckRequirements(); prototype DialogShowSdWelcome(); prototype DialogShowSdShowInfoList(); prototype DialogShowSdAskDestPath(); prototype DialogShowSdSetupType(); prototype DialogShowSdComponentDialog2(); prototype DialogShowSdSelectFolder(); prototype DialogShowSdFinishReboot(); // your prototypes prototype MyParsePath ( STRING ); prototype ForwardSlashify ( STRING, BYREF STRING ); prototype CreateExecPerlScript ( STRING, STRING ); prototype DropSuffix ( STRING, BYREF STRING ); prototype BaseName ( STRING, BYREF STRING ); // ----- global variables ------ // generated BOOL bWinNT, bIsShellExplorer, bInstallAborted, bIs32BitSetup; STRING svDir; STRING svName, svCompany, svSerial; STRING szAppPath; STRING svSetupType; LIST listPath; // your global variables //STRING szRawCpp STRING szPerlPath; STRING szBinDir, szBinDirUnslashed, szLibDir, szLibDirUnslashed, szDataDir, szLibExecDir; /////////////////////////////////////////////////////////////////////////////// // // MAIN PROGRAM // // The setup begins here by hiding the visible setup // window. This is done to allow all the titles, images, etc. to // be established before showing the main window. The following // logic then performs the setup in a series of steps. // /////////////////////////////////////////////////////////////////////////////// program Disable( BACKGROUND ); CheckRequirements(); SetupInstall(); SetupScreen(); if (ShowDialogs()<0) goto end_install; if (ProcessBeforeDataMove()<0) goto end_install; if (MoveFileData()<0) goto end_install; if (ProcessAfterDataMove()<0) goto end_install; if (SetupRegistry()<0) goto end_install; if (SetupFolders()<0) goto end_install; CheckListScreen(); end_install: CleanUpInstall(); // If an unrecoverable error occurred, clean up the partial installation. // Otherwise, exit normally. if (bInstallAborted) then abort; endif; endprogram /////////////////////////////////////////////////////////////////////////////// // // // Function: ShowDialogs // // // // Purpose: This function manages the display and navigation // // the standard dialogs that exist in a setup. // // // /////////////////////////////////////////////////////////////////////////////// function ShowDialogs() NUMBER nResult; begin Dlg_Start: // beginning of dialogs label Dlg_SdWelcome: nResult = DialogShowSdWelcome(); if (nResult = BACK) goto Dlg_Start; Dlg_SdShowInfoList: nResult = DialogShowSdShowInfoList(); if (nResult = BACK) goto Dlg_SdWelcome; Dlg_SdAskDestPath: nResult = DialogShowSdAskDestPath(); if (nResult = BACK) goto Dlg_SdShowInfoList; Dlg_SdSetupType: nResult = DialogShowSdSetupType(); if (nResult = BACK) goto Dlg_SdAskDestPath; Dlg_SdComponentDialog2: if ((nResult = BACK) && (svSetupType != "Custom") && (svSetupType != "")) then goto Dlg_SdSetupType; endif; nResult = DialogShowSdComponentDialog2(); if (nResult = BACK) goto Dlg_SdSetupType; Dlg_SdSelectFolder: //nResult = DialogShowSdSelectFolder(); //if (nResult = BACK) goto Dlg_SdComponentDialog2; return 0; end; /////////////////////////////////////////////////////////////////////////////// // // // Function: ProcessBeforeDataMove // // // // Purpose: This function performs any necessary operations prior to the // // actual data move operation. // // // /////////////////////////////////////////////////////////////////////////////// function ProcessBeforeDataMove() STRING svLogFile; NUMBER nResult; begin InstallationInfo( @COMPANY_NAME, @PRODUCT_NAME, @PRODUCT_VERSION, @PRODUCT_KEY ); svLogFile = UNINST_LOGFILE_NAME; nResult = DeinstallStart( svDir, svLogFile, @UNINST_KEY, 0 ); if (nResult < 0) then MessageBox( @ERROR_UNINSTSETUP, WARNING ); endif; szAppPath = TARGETDIR; // TODO : if your application .exe is in a subdir of TARGETDIR then add subdir if ((bIs32BitSetup) && (bIsShellExplorer)) then RegDBSetItem( REGDB_APPPATH, szAppPath ); RegDBSetItem( REGDB_APPPATH_DEFAULT, szAppPath ^ @PRODUCT_KEY ); RegDBSetItem( REGDB_UNINSTALL_NAME, @UNINST_DISPLAY_NAME ); endif; // TODO : update any items you want to process before moving the data // ComponentSetTarget( MEDIA, "", TARGETDIR ^ "\\doc" ); return 0; end; /////////////////////////////////////////////////////////////////////////////// // // // Function: MoveFileData // // // // Purpose: This function handles the data movement for // // the setup. // // // /////////////////////////////////////////////////////////////////////////////// function MoveFileData() NUMBER nResult, nDisk; begin nDisk = 1; SetStatusWindow( 0, "" ); Disable( DIALOGCACHE ); Enable( STATUS ); StatusUpdate( ON, 100 ); nResult = ComponentMoveData( MEDIA, nDisk, 0 ); HandleMoveDataError( nResult ); Disable( STATUS ); return nResult; end; /////////////////////////////////////////////////////////////////////////////// // // // Function: HandleMoveDataError // // // // Purpose: This function handles the error (if any) during the move data // // operation. // // // /////////////////////////////////////////////////////////////////////////////// function HandleMoveDataError( nResult ) STRING szErrMsg, svComponent , svFileGroup , svFile; begin svComponent = ""; svFileGroup = ""; svFile = ""; switch (nResult) case 0: return 0; default: ComponentError ( MEDIA , svComponent , svFileGroup , svFile , nResult ); szErrMsg = @ERROR_MOVEDATA + "\n\n" + @ERROR_COMPONENT + " " + svComponent + "\n" + @ERROR_FILEGROUP + " " + svFileGroup + "\n" + @ERROR_FILE + " " + svFile; SprintfBox( SEVERE, @TITLE_CAPTIONBAR, szErrMsg, nResult ); bInstallAborted = TRUE; return nResult; endswitch; end; /////////////////////////////////////////////////////////////////////////////// // // // Function: ProcessAfterDataMove // // // // Purpose: This function performs any necessary operations needed after // // all data has been moved. // // // /////////////////////////////////////////////////////////////////////////////// function ProcessAfterDataMove() STRING szPath, szGcc, szGccDir; STRING szTemp, szRes, szPathEntry, szDrive; NUMBER nvSize,nvType, nResult, nPos, nSuccess; LIST listDirs; begin RegDBSetDefaultRoot ( HKEY_LOCAL_MACHINE ); RegDBGetKeyValueEx ( "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment" , "path" , nvType, szPath , nvSize ); // Spin through path looking for perl.exe. // Our task is made considerably harder since the // the free version of InstallShield doesn't support // functions such as StrGetTokens() or ParsePath(). // TODO: Convert this back to IS Prof Edn MyParsePath ( szPath ); nResult = ListGetFirstString ( listPath, szPathEntry); szPathEntry = ""; szPerlPath=""; while ( nResult != END_OF_LIST ) if ( FindFile (szPathEntry, "perl.exe", szRes ) = 0 ) then szPerlPath = szPathEntry + "/" + szRes; nResult = END_OF_LIST; else nResult = ListGetNextString ( listPath, szPathEntry ); endif; endwhile; if ( StrCompare ( szPerlPath, "" ) = 0 ) then // If not found in global env. block, look in the // user-specific part. RegDBSetDefaultRoot ( HKEY_CURRENT_USER ); RegDBGetKeyValueEx ( "Environment", "path", nvType, szPath, nvSize); MyParsePath ( szPath ); nResult = ListGetFirstString ( listPath, szPathEntry); szPathEntry = ""; szPerlPath=""; while ( nResult != END_OF_LIST ) if ( FindFile (szPathEntry, "perl.exe", szRes ) = 0 ) then szPerlPath = szPathEntry + "/" + szRes; nResult = END_OF_LIST; else nResult = ListGetNextString ( listPath, szPathEntry ); endif; endwhile; endif; if ( StrCompare ( szPerlPath, "" ) = 0 ) then // Default it to /bin/perl MessageBox ("Unable to find perl in your PATH. Not to worry, this installer includes a version that should be useable." + "When the installer has finished, please copy it from the bin/ directory of the ghc installation to /bin/perl", INFORMATION); szPerlPath = "/bin/perl.exe"; else // strip initial drive spec. GetDir ( szPerlPath, szTemp); ForwardSlashify ( szTemp, szPerlPath ); endif; DropSuffix (szPerlPath, szTemp); szPerlPath = szTemp; ListDestroy (listPath); return 0; end; /////////////////////////////////////////////////////////////////////////////// // // // Function: SetupRegistry // // // // Purpose: This function makes the registry entries for this setup. // // // /////////////////////////////////////////////////////////////////////////////// function SetupRegistry() NUMBER nResult,nWays,i; STRING szProjectDir, szProjectVersionDir, szTargetDir; STRING szSyslib, szWay, szSyslibsDir; STRING szLib, szSyslibsKey; //LIST syslib_list, way_list; begin // TODO : Add all your registry entry keys here // // nResult = CreateRegistrySet( "" ); // By now, we will have had the following Registry // entries generated (see ProcessBeforeDataMove() ): // // HK_L_M\Software\Glasgow University\GHC\ // // For GHC, we store and use the install info inside // HK_L_M\Software\Haskell\GHC (== %ROOT%) // So, we perform the following tasks here: // // * Checks to see if %ROOT% is defined. // If not, creates it. // * Checks for %ROOT%\Version is defined. // * Create %ROOT%\. // * Create %ROOT%\\libdir // (This key will be given a value later.) // * Create %ROOT%\syslib\ and fill it in // with the syslibs that has been installed. // * %ROOT%\syslib\name\ // is added for each kind (e.g., seq, conc) // syslib we're installing. // When we eventually reach the end, the registry // should be ready for use by the installed app. // Check to see if we've already installed // a version of GHC on this box.. RegDBSetDefaultRoot ( HKEY_LOCAL_MACHINE ); szProjectDir = @HASKELL_REG_ROOT ^ @PRODUCT_NAME; // check whether we've already got the Registry sub-tree we're // about to add to. If not, then we create it // level-by-level, so as to ensure that the uninstaller // can clean up after us. if ( RegDBKeyExist ( @HASKELL_REG_ROOT ) < 0) then RegDBCreateKeyEx (@HASKELL_REG_ROOT, ""); endif; if ( RegDBKeyExist ( szProjectDir ) < 0 ) then RegDBCreateKeyEx ( szProjectDir, ""); endif; // Note: we overwrite any existing value. RegDBSetKeyValueEx( szProjectDir, "Version", REGDB_STRING, @PRODUCT_KEY, -1); szProjectVersionDir = szProjectDir ^ @PRODUCT_KEY; if ( RegDBKeyExist ( szProjectVersionDir ) < 0) then RegDBCreateKeyEx ( szProjectVersionDir, ""); endif; // Here starts the GHC specific part ForwardSlashify (TARGETDIR, szTargetDir); // fill in the all-important path to where the archives // and interface files have been parked. szLibDir = szTargetDir + "/lib"; szLibDirUnslashed = TARGETDIR ^ "\\lib"; RegDBSetKeyValueEx ( szProjectVersionDir, "libdir", REGDB_STRING, szLibDir , -1); // libexecdir szLibExecDir = szTargetDir + "/lib"; RegDBSetKeyValueEx ( szProjectVersionDir, "libexecdir", REGDB_STRING, szLibExecDir , -1); // bindir szBinDir = szTargetDir + "/bin"; szBinDirUnslashed = TARGETDIR ^ "\\bin"; RegDBSetKeyValueEx ( szProjectVersionDir, "bindir", REGDB_STRING, szBinDir , -1); return 0; end; /////////////////////////////////////////////////////////////////////////////// // // Function: SetupFolders // // Purpose: This function creates all the folders and shortcuts for the // setup. This includes program groups and items for Windows 3.1. // /////////////////////////////////////////////////////////////////////////////// function SetupFolders() NUMBER nResult; begin // TODO : Add all your folder (program group) along with shortcuts (program items) // // // CreateProgramFolder, AddFolderIcon.... // CreateExecPerlScript ( szBinDirUnslashed, "ghc-" + @PRODUCT_VERSION ); CreateExecPerlScript ( szBinDirUnslashed, "stat2resid" ); CreateExecPerlScript ( szLibDirUnslashed, "hscpp" ); CreateExecPerlScript ( szLibDirUnslashed, "mkdependHS" ); VarSave (SRCTARGETDIR); SRCDIR = szBinDirUnslashed; TARGETDIR = szBinDirUnslashed; CopyFile( "ghc-" + @PRODUCT_VERSION, "ghc"); VarRestore (SRCTARGETDIR); nResult = CreateShellObjects( "" ); return nResult; end; /////////////////////////////////////////////////////////////////////////////// // // // Function: CleanUpInstall // // // // Purpose: This cleans up the setup. Anything that should // // be released or deleted at the end of the setup should // // be done here. // // // /////////////////////////////////////////////////////////////////////////////// function CleanUpInstall() begin if (bInstallAborted) then return 0; endif; DialogShowSdFinishReboot(); if (BATCH_INSTALL) then // ensure locked files are properly written CommitSharedFiles(0); endif; return 0; end; /////////////////////////////////////////////////////////////////////////////// // // // Function: SetupInstall // // // // Purpose: This will setup the installation. Any general initialization // // needed for the installation should be performed here. // // // /////////////////////////////////////////////////////////////////////////////// function SetupInstall() begin Enable( CORECOMPONENTHANDLING ); bInstallAborted = FALSE; GetDisk(WINDIR, svDir); if (bIs32BitSetup) then svDir = svDir + "\\" ^ @PRODUCT_NAME_SHORT ^ @PRODUCT_KEY; else // We're (=>ghc) 32 through and through, but for the sake of // completenes. svDir = svDir + "\\" ^ @PRODUCT_NAME_SHORT ^ @PRODUCT_NAME16; endif; TARGETDIR = svDir; SdProductName( @PRODUCT_NAME ); Enable( DIALOGCACHE ); return 0; end; /////////////////////////////////////////////////////////////////////////////// // // // Function: SetupScreen // // // // Purpose: This function establishes the screen look. This includes // // colors, fonts, and text to be displayed. // // // /////////////////////////////////////////////////////////////////////////////// function SetupScreen() begin SetColor ( BACKGROUND, BLUE ); Enable( FULLWINDOWMODE ); Enable( INDVFILESTATUS ); SetTitle( @TITLE_MAIN, 24, WHITE ); SetTitle( @TITLE_CAPTIONBAR, 0, BACKGROUNDCAPTION ); // Caption bar text. Enable( BACKGROUND ); Delay( 1 ); end; /////////////////////////////////////////////////////////////////////////////// // // // Function: CheckRequirements // // // // Purpose: This function checks all minimum requirements for the // // application being installed. If any fail, then the user // // is informed and the setup is terminated. // // // /////////////////////////////////////////////////////////////////////////////// function CheckRequirements() NUMBER nvDx, nvDy, nvResult; STRING svResult; begin bWinNT = FALSE; bIsShellExplorer = FALSE; // Check screen resolution. GetExtents( nvDx, nvDy ); if (nvDy < 480) then MessageBox( @ERROR_VGARESOLUTION, WARNING ); abort; endif; // set 'setup' operation mode bIs32BitSetup = TRUE; GetSystemInfo( ISTYPE, nvResult, svResult ); if (nvResult = 16) then bIs32BitSetup = FALSE; // running 16-bit setup return 0; // no additional information required endif; // --- 32-bit testing after this point --- // Determine the target system's operating system. GetSystemInfo( OS, nvResult, svResult ); if (nvResult = IS_WINDOWSNT) then // Running Windows NT. bWinNT = TRUE; // Check to see if the shell being used is EXPLORER shell. if (GetSystemInfo( OSMAJOR, nvResult, svResult ) = 0) then if (nvResult >= 4) then bIsShellExplorer = TRUE; endif; endif; elseif (nvResult = IS_WINDOWS95 ) then bIsShellExplorer = TRUE; endif; end; /////////////////////////////////////////////////////////////////////////////// // // // Function: DialogShowSdWelcome // // // // Purpose: This function handles the standard welcome dialog. // // // // // /////////////////////////////////////////////////////////////////////////////// function DialogShowSdWelcome() NUMBER nResult; STRING szTitle, szMsg; begin szTitle = ""; szMsg = ""; nResult = SdWelcome( szTitle, szMsg ); return nResult; end; /////////////////////////////////////////////////////////////////////////////// // // // Function: DialogShowSdShowInfoList // // // // Purpose: This function displays the general information list dialog. // // // // // /////////////////////////////////////////////////////////////////////////////// function DialogShowSdShowInfoList() NUMBER nResult; LIST list; STRING szTitle, szMsg, szFile; begin szFile = SUPPORTDIR ^ "announce"; list = ListCreate( STRINGLIST ); ListReadFromFile( list, szFile ); szTitle = ""; szMsg = " "; nResult = SdShowInfoList( szTitle, szMsg, list ); ListDestroy( list ); return nResult; end; /////////////////////////////////////////////////////////////////////////////// // // // Function: DialogShowSdAskDestPath // // // // Purpose: This function asks the user for the destination directory. // // // /////////////////////////////////////////////////////////////////////////////// function DialogShowSdAskDestPath() NUMBER nResult; STRING szTitle, szMsg; begin szTitle = ""; szMsg = "WARNING! The path must not contain spaces."; nResult = SdAskDestPath( szTitle, szMsg, svDir, 0 ); TARGETDIR = svDir; return nResult; end; /////////////////////////////////////////////////////////////////////////////// // // // Function: DialogShowSdSetupType // // // // Purpose: This function displays the standard setup type dialog. // // // /////////////////////////////////////////////////////////////////////////////// function DialogShowSdSetupType() NUMBER nResult, nType; STRING szTitle, szMsg; begin switch (svSetupType) case "Typical": nType = TYPICAL; case "Custom": nType = CUSTOM; case "Compact": nType = COMPACT; case "": svSetupType = "Typical"; nType = TYPICAL; endswitch; szTitle = ""; szMsg = ""; nResult = SetupType( szTitle, szMsg, "", nType, 0 ); switch (nResult) case COMPACT: svSetupType = "Compact"; case TYPICAL: svSetupType = "Typical"; case CUSTOM: svSetupType = "Custom"; endswitch; return nResult; end; /////////////////////////////////////////////////////////////////////////////// // // // Function: DialogShowSdComponentDialog2 // // // // Purpose: This function displays the custom component dialog. // // // // // /////////////////////////////////////////////////////////////////////////////// function DialogShowSdComponentDialog2() NUMBER nResult; STRING szTitle, szMsg; begin if ((svSetupType != "Custom") && (svSetupType != "")) then return 0; endif; szTitle = ""; szMsg = ""; nResult = SdComponentDialog2( szTitle, szMsg, svDir, "" ); return nResult; end; /////////////////////////////////////////////////////////////////////////////// // // // Function: CheckListScreen // // // // Purpose: Show a check-list of post-install user actions. // // // /////////////////////////////////////////////////////////////////////////////// function CheckListScreen() BOOL bDone; NUMBER nCmdValue; begin EzDefineDialog("CHECKLIST", "", "", 30001); bDone = FALSE; while (bDone=FALSE) nCmdValue = WaitOnDialog("CHECKLIST"); switch (nCmdValue) case DLG_INIT: // Process the Next button. case SD_PBUT_CONTINUE: bDone = TRUE; // Process the Cancel button. case SD_PBUT_EXITSETUP: bDone = TRUE; // Process the close dialog box button. case DLG_CLOSE: bDone = TRUE; // Process dialog box errors. case DLG_ERR: MessageBox("Internal dialog box error", SEVERE); bDone = TRUE; endswitch; endwhile; // Identify the end of dialog box processing. EndDialog("CHECKLIST"); // Free the dialog box and list from memory. ReleaseDialog("CHECKLIST"); end; /////////////////////////////////////////////////////////////////////////////// // // // Function: DialogShowSdFinishReboot // // // // Purpose: This function will show the last dialog of the product. // // It will allow the user to reboot and/or show some readme text. // // // /////////////////////////////////////////////////////////////////////////////// function DialogShowSdFinishReboot() NUMBER nResult, nDefOptions; STRING szTitle, szMsg1, szMsg2, szOption1, szOption2; NUMBER bOpt1, bOpt2; begin if (!BATCH_INSTALL) then bOpt1 = FALSE; bOpt2 = FALSE; szMsg1 = ""; szMsg2 = ""; szOption1 = ""; szOption2 = ""; szTitle = "Installation is now complete."; nResult = SdFinish( szTitle, szMsg1, szMsg2, szOption1, szOption2, bOpt1, bOpt2 ); return 0; else nDefOptions = SYS_BOOTMACHINE; szTitle = ""; szMsg1 = ""; szMsg2 = ""; nResult = SdFinishReboot( szTitle, szMsg1, nDefOptions, szMsg2, 0 ); return nResult; endif; end; function MyParsePath(szPath) STRING szTmp,szPth; NUMBER nPos; begin szPth = szPath; listPath = ListCreate( STRINGLIST ); // Man, all I want is map. Please? :-) nPos = StrFind ( szPth, ";"); while ( nPos > 0 ) StrSub ( szTmp, szPth, 0, nPos); ListAddString ( listPath, szTmp, AFTER ); StrSub ( szTmp, szPth, nPos + 1, StrLength ( szPth) - nPos ); szPth = szTmp; nPos = StrFind ( szPth, ";" ); endwhile; return 0; end; function ForwardSlashify ( szStr , theRes ) NUMBER nPos; STRING szTemp, szRes; begin // Tortuous piece of code to convert backslashes into // forward ones. nPos = StrFind ( szStr, "\\"); szRes=""; while ( nPos >= 0 ) StrSub ( szTemp, szStr, 0, nPos); szRes = szRes + szTemp + "/"; StrSub ( szTemp, szStr, nPos + 1, StrLength ( szStr) - nPos ); szStr = szTemp; nPos = StrFind ( szStr, "\\" ); endwhile; StrSub ( szTemp, szStr, 0, StrLength (szStr)); szRes = szRes + szTemp; theRes = szRes; return 0; end; function CreateExecPerlScript ( szPath, szFileName ) NUMBER nResult, writeHandle, readHandle; STRING szLine; begin VarSave (SRCTARGETDIR); SRCDIR = szPath; TARGETDIR = szPath; DeleteFile ( szFileName + ".bak"); RenameFile ( szFileName, szFileName + ".bak"); VarRestore (SRCTARGETDIR); OpenFileMode (FILE_MODE_APPEND); if ( CreateFile ( writeHandle, szPath, szFileName ) < 0 ) then MessageBox ("CreateFile " + szPath ^ szFileName + " failed", INFORMATION); endif; // append WriteLine (writeHandle, "#!" + szPerlPath ); WriteLine (writeHandle, "$libdir='" + szLibDir + "';"); WriteLine (writeHandle, "$bindir='" + szBinDir + "';"); WriteLine (writeHandle, "$libexecdir='" + szLibExecDir + "';"); WriteLine (writeHandle, "$datadir='" + szDataDir + "';"); WriteLine (writeHandle, "$SED='sed';"); WriteLine (writeHandle, "$TMPDIR='C:/TEMP';"); WriteLine (writeHandle, "$RAWCPP='gcc -E';"); // For the benefit of mkdependHS, which doesn't get this prepended WriteLine (writeHandle, "$INSTALLING=1;"); OpenFileMode (FILE_MODE_NORMAL); if ( OpenFile ( readHandle, szPath, szFileName + ".bak") < 0 ) then MessageBox ("OpenFile " + szPath ^ szFileName + ".bak failed", INFORMATION); endif; // copy the template over. nResult = GetLine ( readHandle, szLine); while ( nResult >= 0 ) WriteLine ( writeHandle, szLine); nResult = GetLine ( readHandle, szLine); endwhile; if ( CloseFile( readHandle ) < 0 ) then MessageBox ( "CloseFile " + szPath ^ szFileName + ".bak failed", INFORMATION); endif; if ( CloseFile( writeHandle ) < 0 ) then MessageBox ( "CloseFile " + szPath ^ szFileName + " failed", INFORMATION); endif; // There's no way to set the 'x' bit using // SetFileInfo(), but luckily it is not needed to run #! scripts // under cygwin. SetFileInfo ( szPath ^ szFileName, FILE_ATTRIBUTE, FILE_ATTR_NORMAL, ""); // Delete the .bak file DeleteFile ( szPath ^ szFileName + ".bak"); return 0; end; function DropSuffix ( szInp, szOut ) NUMBER nResult, nLen; STRING szTemp, szTemp2; begin nLen = 0; szTemp2 = szInp; nResult = StrFind ( szTemp2 , "."); while ( nResult >= 0 ) nLen = nLen + nResult; StrSub ( szTemp, szTemp2, nResult + 1, StrLength ( szTemp2) - nResult ); szTemp2 = szTemp; nResult = StrFind ( szTemp2, "." ); if ( nResult >= 0 ) then nLen = nLen + 1; // incl the previous . if there's more. endif; endwhile; StrSub ( szOut, szInp, 0, nLen); return 0; end;