How do I create a simple Octave distributable without installing Octave - octave

The Octave documentation on this subject is both intimidating and sparse.
I did not know where else to document the solution I found, so I am posting here. I apologize if that's inappropriate, but I want to help the next guy.
The following solution is for a simple windows distributable.
Use Case:
A solution is developed in Octave 3.2.4, and needs to be distributed to end-users with few computer skills. Installing and explaining Octave is impossible, the solution must be "one-click" or "brain-dead-simple."
Known Issues:
imread fails in 3.2.4 because file_in_path.m is wrong. You will need to update the file file_in_path.m to the following (just replace it):
function name=file_in_path(p,file)
idx=[1 findstr(p,pathsep) length(p)+1];
for i=1:length(idx)-1
if idx(i+1)-idx(i)<=1
dir=strcat(pwd,"/");
else
dir=p(idx(i)+1:idx(i+1)-1);
end
name = fullfile(dir, file);
fid = fopen(name,"r");
if fid >= 0
fclose(fid);
return
end
end
fid = fopen(file,"r");
if fid >= 0,
fclose(fid);
name=file;
return
end
name=[];

Solution: Create a distributable exe using mkoctfile, and package this exe with the core Octave files, and other .oct and .m files as necessary.
Step 1: Create a stand-alone executable.
You can see code that works here:
http://www.gnu.org/software/octave/doc/interpreter/Standalone-Programs.html
Particularly the file "embedded.cc".
I have simplified that file as follows:
#include <iostream>
#include <octave/oct.h>
#include <octave/octave.h>
#include <octave/parse.h>
int
main (int argc, char *argvc[])
{
string_vector argv (2);
argv(0) = "embedded";
argv(1) = "-q";
octave_main (2, argv.c_str_vec(), 1);
octave_value_list in = octave_value (argvc[1]);
octave_value_list out = feval ("your_custom_m_file", in);
if (!error_state && out.length () > 0)
{
}
else
{
std::cout << "invalid\n";
}
return 0;
}
Build this file with the command
mkoctfile --link-stand-alone embedded.cc -o embedded
It may throw warnings, but as long as it throws no errors, you should be fine. The file embedded.exe will be built, and can be run. The only issue is that it will lack all the goodies that make octave awesome. You will have to provide those.
Step 2: Create a distribution folder
You will need to create a copy of many of the Octave files. I suggest a directory specifically for this. At a minimum, you will need a copy of all or most of the DLLs in \bin. Additionally, place your distributable executable in this directory.
Step 3: Other files whack-a-mole
You will now need to find out what other files will be necessary to run your .m script. You can simplify this step by copying \oct\i686-pc-mingw32*.oct and \share\octave\3.2.4\m\*\*.m to the distribution directory, although this will be overkill, and will not actually prevent the whack-a-mole step.
Now, you must play whack-a-mole or the time-honored tradition of "where my includes be at, yo?"
Open a cmd prompt and navigate to your distribution folder.
Get rid of any useful PATH strings. Your customers won't have them.
Attempt to run the program embedded.exe. You will get an error such as the following:
embedded.exe
error: `max' undefined near line 83 column 22
error: evaluating argument list element number 1
error: evaluating argument list element number 1
error: called from:
error: T:\sms\Development\research\c2\disttest\strcat.m at line 83, column 3
error: T:\sms\Development\research\c2\disttest\file_in_path.m at line 5, column 10
error: T:\sms\Development\research\c2\disttest\imread.m at line 50, column 6
A Search in your Octave installation for "max". It will either be a .oct or a .m file. In this case, it is a .oct file, max.oct. Copy it to your distribution directory.
B You search for something obvious like "min", and get no results. This is because the Loadable Function "min" is in the .oct file "max.oct". Make a copy of max.oct, and rename it to min.oct. It will work now. How do you know where the functions are? I'm not sure. Most of them are in obvious places like "max.oct" for min, and "fft2.oct" for "ifft2.oct". Good luck with all that.
Repeat until your executable runs.

Just to add that if you want to run a script instead of an m function, then the line of the embedded.cc:
octave_value_list out = feval ("your_custom_m_file", in);
should be:
octave_value_list out = feval ("your_custom_m_script");
Also use 'which' to find where the missing functions are packed. For example for the min function:
octave:22> which min
min is a function from the file C:\Octave\Octave3.6.2_gcc4.6.2\lib\octave\3.6.2\oct\i686-pc-mingw32\max.oct

Something I found when linking my custom m file into an Octave standalone:
Needed #include <octave/toplev.h>
Replace return 0; (as above) with clean_up_and_exit(0);
Without these steps my program repeatedly crashed on exit.

Run mkoctfile --link-stand-alone embedded.cc -o embedded
from the octave solution and not from a batch file.
Just saved you half day (-;

In the above solution in bullet 4 B:
B You search for something obvious like "min", and get no results.
This is because the Loadable Function "min" is in the .oct file
"max.oct". Make a copy of max.oct, and rename it to min.oct. It will
work now.
This might not work if some function is being called from #folder function.m and also to avoid unnecessary duplicated files, just add the following code somewhere in your m file outside #folder
autoload ("min", "max.oct");
Likewise, it can be removed via
autoload ("min", "max.oct", "remove");
Ensure that the path to max.oct is provided here.
The above understanding is based on a file PKG_ADD and PKG_DEL in the communications package located at \Octave-4.0.1\lib\octave\packages\communications-1.2.1\i686-w64-mingw32-api-v50+\

Check out Stratego Octave Compiler.
(I've not tested it yet, but plan to do so in the next few days.)

I had that very same requirement (one-click, brain-dead-simple), so I made a setup that contained only curl.exe, the batch file below, an exe which was a .bat in disguise (simply calling the batch file below) and the .vbs script below (not writen by me). And of course my m-file.
This will download Octave 4.2.1 as a portable program (32 bit, otherwise we'dd have to download again if the system turns out to be 32 bit), unpack using the vbs script, move the contents to the same folder as the batch file and run it in GUI mode. Every next time the same script is called, it will only check if octave.bat is still there.
Of course this results in a huge waste of disk space, downloading the 280MB zip, which unpacks to over 1GB (which I make even worse by not deleting the zip afterwards), and you're stuck with a cmd window that is not easy to hide.
But it does offer the simplest solution I could find. It is also less likely to break in the future (either with an update of your own, or an update from Octave). Some glorious day, mkoktfile will actually be easy to use and will solve dependencies on its own, but until that day this remains the least headache-inducing solution I could find. And aspirins are more expensive than someone else's disk space.
::this file will test if the octave portable is downloaded and unpacked
#ECHO OFF
SET my_m_file=your_mfile.m
SET name_of_this_script=run_me.bat
::if the file exists, skip to the actual running.
IF EXIST "octave.bat" goto OctaveIsExtracted
IF EXIST "octave-4.2.1-w32.zip" goto OctaveIsDownloaded
ECHO The runtime (Octave portable 4.2.1) will now be downloaded.
ECHO This may take a long time, as it is about 280MB.
ECHO .
ECHO If this download restarts multiple times, you can manually download the octave-4.2.1-w32.zip from the GNU website. Make sure to unpack the contents.
::if this errors, you can uncomment the line with archive.org (which doesn't report total size during download)
curl http://ftp.gnu.org/gnu/octave/windows/octave-4.2.1-w32.zip > octave-4.2.1-w32.zip
::curl http://web.archive.org/web/20170827205614/https://ftp.gnu.org/gnu/octave/windows/octave-4.2.1-w32.zip > octave-4.2.1-w32.zip
:OctaveIsDownloaded
::check to see if the file size is the correct size to assume a successful download
::if the file size is incorrect, delete the file, restart this script to attempt a new download
::file size should be 293570269 bytes
call :filesize octave-4.2.1-w32.zip
IF /I "%size%" GEQ "293560000" goto OctaveIsDownloadedSuccessfully
del octave-4.2.1-w32.zip
::start new instance and exit and release this one
start %name_of_this_script%
exit
:OctaveIsDownloadedSuccessfully
IF EXIST "octave.bat" goto OctaveIsExtracted
::unzip and move those contents to the current folder
ECHO Unzipping octave portable, this may take a moment.
cscript //B j_unzip.vbs octave-4.2.1-w32.zip
SET src_folder=octave-4.2.1
SET tar_folder=%cd%
for /f %%a IN ('dir "%src_folder%" /b') do move %src_folder%\%%a %tar_folder%
pause
:OctaveIsExtracted
octave.bat %my_m_file%
goto :eof
:filesize
set size=%~z1
exit /b 0
And j_unzip.vbs
' j_unzip.vbs
'
' UnZip a file script
'
' By Justin Godden 2010
'
' It's a mess, I know!!!
'
' Dim ArgObj, var1, var2
Set ArgObj = WScript.Arguments
If (Wscript.Arguments.Count > 0) Then
var1 = ArgObj(0)
Else
var1 = ""
End if
If var1 = "" then
strFileZIP = "example.zip"
Else
strFileZIP = var1
End if
'The location of the zip file.
REM Set WshShell = CreateObject("Wscript.Shell")
REM CurDir = WshShell.ExpandEnvironmentStrings("%%cd%%")
Dim sCurPath
sCurPath = CreateObject("Scripting.FileSystemObject").GetAbsolutePathName(".")
strZipFile = sCurPath & "\" & strFileZIP
'The folder the contents should be extracted to.
outFolder = sCurPath
'original line: outFolder = sCurPath & "\"
WScript.Echo ( "Extracting file " & strFileZIP)
Set objShell = CreateObject( "Shell.Application" )
Set objSource = objShell.NameSpace(strZipFile).Items()
Set objTarget = objShell.NameSpace(outFolder)
intOptions = 256
objTarget.CopyHere objSource, intOptions
WScript.Echo ( "Extracted." )

Related

octave mkdir fails after recursive rmdir

I have a code that creates a subfolder but first removes the subfolder if it already existed. I am using Octave3.6.4_gcc4.6.2 for MinGW on a Win7 pro machine. I noticed that mkdir fails if the subfolder existed and contained several files. It seems like rmdir has not completed in the background before the next lines of code are executed. Below is a sample of the test code.
parentDir = 'C:\Temp\rmDir';
childDir = fullfile(parentDir, 'output');
if (exist(childDir, 'dir') ~= 0)
[status] = rmdir(childDir, 's');
disp(status);
end;
[status] = mkdir(parentDir, 'output');
disp(status);
disp(exist(childDir, 'dir'));
Below is the Octave result for when the subfolder does not exist. This works as expected.
octave:5> testrmdir
1
7
Below is the Octave result for when a subfolder exists and is empty. This works as expected.
octave:6> testrmdir
1
1
7
Below is the Octave result for when a subfolder exists and contains 3 PNG files with a total size of 349 KB. Status is 1 for both mkdir and rmdir. However, the exist function reports that the folder does not exist. I confirm from windows explorer that the subfolder is deleted. My guess is that when mkdir executes, the files are still being deleted by the prior rmdir function. So mkdir reports success because the subfolder has not been deleted by rmdir yet. However, by the time exist is executed rmdir has completed and so the subfolder no longer exists.
octave:7> testrmdir
1
1
0
I tried different file types with the following results:
2 PNG files, 232 KB total - pass
4 PNG files, 465 KB total - fail
3 PNG files, 349 KB total - fail
3 csv files, 518 KB total - pass
5 csv files, 777 KB total - fail
The behavior is the same when I run Octave from the command line. I have used the same code on MATLAB in the past without any noticeable issues. For now, I had to switch to Octave for test automation on a different machine.
Does this make sense? Any suggestions on how to make this code work regardless of the subfolder contents or size?
Not sure if this is important, but I have the following setting in the resource file: confirm_recursive_rmdir(false).
I changed the if statement to a while loop and this fixed the problem (i.e. all I did was replace "if" with "while"). Then I added a counter in the while loop and saw that rmdir was successful on the first iteration. Therefore, I cannot explain why the code does not work with an if statement. See expanded code with new counter below. But like I said, the code also works if I simply replace "if" in the original code with "while".
parentDir = 'C:\Temp\rmDir';
childDir = fullfile(parentDir, 'output');
count = 0;
while (exist(childDir, 'dir') ~= 0)
%if (exist(childDir, 'dir') ~= 0)
count++
[status] = rmdir(childDir, 's');
disp(status);
disp(count);
end;
[status] = mkdir(parentDir, 'output');
disp(status);
disp(exist(childDir, 'dir'));

using a variable to identify file in 'print -dpdf file_name'

I am trying to use a formatted string to identify the file location when using 'print -dpdf file_name' to write a plot (or figure) to a file.
I've tried:
k=1;
file_name = sprintf("\'/home/user/directory to use/file%3.3i.pdf\'",k);
print -dpdf file_name;
but that only gets me a figure written to ~/file_name.pdf which is not what I want. I've tried several other approaches but I cannot find an approach that causes the the third term (file_name, in this example) to be evaluated. I have not found any other printing function that will allow me to perform a formatted write (the '-dpdf' option) of a plot (or figure) to a file.
I need the single quotes because the path name to the location where I want to write the file contains spaces. (I'm working on a Linux box running Fedora 24 updated daily.)
If I compute the file name using the line above, then cut and paste it into the print statement, everything works exactly as I wish it to. I've tried using
k=1;
file_name = sprintf("\'/home/user/directory to use/file%3.3i.pdf\'",k);
print ("-dpdf", '/home/user/directory to use/file001.pdf');
But simply switching to a different form of print statement doesn't solve the problem,although now I get an error message:
GPL Ghostscript 9.16: **** Could not open the file '/home/user/directory to use/file001.pdf' .
**** Unable to open the initial device, quitting.
warning: broken pipe
if you use foo a b this is the same as foo ("a", "b"). In your case you called print ("-dpdf", "file_name")
k = 1;
file_name = sprintf ("/home/user/directory to use/file%3.3i.pdf", k);
print ("-dpdf", file_name);
Observe:
>> k=1;
>> file_name = sprintf ('/home/tasos/Desktop/a folder with spaces in it/this is file number %3.3i.pdf', k)
file_name = /home/tasos/Desktop/a folder with spaces in it/this is file number 001.pdf
>> plot (1 : 10);
>> print (gcf, file_name, '-dpdf')
Tadaaa!
So yeah, no single quotes needed. The reason single quotes work when you're "typing it by hand" is because you're literally creating the string on the spot with the single quotes.
Having said that, it's generally a good idea when generating absolute paths to use the fullfile command instead. Have a look at it.
Tasos Papastylianou #TasosPapastylianou provided great help. My problem is now solved.

SSIS copy all files into a variable destination via execute process task

I have read several posts that have given examples of similar processes.
I have put a lot of time trying to make this work so i would really like to get this to work so I can put all this time into the "I learned something Category"
But if its just not possible... I guess I will learn that at least.
I am tring to copy all files from a folder to another folder using ssis 2008r2.
-using execute process task
-using C:\Windows\System32\cmd.exe in the executable field
-the destination folder is a variable I want to create using the starttime variable - which is working
-then i want to copy all the working files into that folder.
-this dest folder uses the start time down to the second so i can capture each attempted run with minimal errors or overwrites
With so many attempts and failures i have dumbed it down to using straight string variables to try to just move all the files in the simplest way that still uses a variable.
the variables I am currently using in my mutiple failed attempts are...
[user::testtofolder] = p:\Utilities\SWUMOps\Archive\20160720-0921-03-SAWSImports
[user::testtofolder1] = p:\\Utilities\\SWUMOps\\Archive\\20160720-0921-03-SAWSImports
[user::testtofolder2] = p:\\Utilities\\SWUMOps\\Archive\\20160720-0921-03-SAWSImports
The below are some of the attempts I have tried in the arguments field
/c copy /y p:\utilities\swumops\backend\saws p:\utilities\swumops\archive\20160720-0921-03-sawsimports -- works (no variables)
/c copy /y p:\utilities\swumops\backend\saws + #[user::testtofolder] -- fails
"/c copy /y \"p:\utilities\swumops\backend\saws\" + #[user::testtofolder] -- fails
"/c copy /y \"p:\utilities\swumops\backend\saws\"" + #[user::testtofolder] -- fails
"/c copy /y \"p:\utilities\swumops\backend\saws \"" + #[user::testtofolder] -- fails
"/c copy /y \"p:\\utilities\\swumops\\backend\\saws \"" + #[user::testtofolder] --fails
/c copy /y p:\\utilities\\swumops\\backend\\saws + #[user::testtofolder] --fails
"/c copy /y \"p:\\utilities\\swumops\\backend\\saws \" + #[user::testtofolder] --fails feel this is the closest
"/c copy /y \"p:\\utilities\\swumops\\backend\\saws \" + #[user::testtofolder1] -- fails
"/c copy /y \"p:\\utilities\\swumops\\backend\\saws \" + #[user::testtofolder2] -- fails
the frustration thing about this is I can't(don't know how to) see what is truly passed to cmd because the window flashes to fast and the errors only read the line that is in the argument field not the past string. the errors look like this...
Error: 0xC0029151 at Copy files to archive, Execute Process Task: In Executing "C:\Windows\System32\cmd.exe" ""/c copy /y \"p:\utilities\swumops\backend\saws \" + #[user::testtofolder]" at "", The process exit code was "1" while the expected was "0".
Hope I put enough details. Let me know if you need more.
Thank you
In SSIS File System task should be a much easier way to do your task. I am not sure how come you need to use command line to move/copy the file.
For your question, I believe you need to use xcopy.exe instead of cmd.exe. The executable should be "C:\Windows\System32\xcopy.exe", and arguments should be looked like
" /y p:\utilities\swumops\backend\saws " + #[user::testtofolder]
Hope it helps.
Thanks for the attention, It helped me look elsewhere for my problem. I have not used the expressions area of a task before. iNoob. Everywhere I researched said place the formatted line IN the arguments. So that's where I have been placing it, directly in the arguments line... Not in the expressions window by adding a line for arguments. The first simple test worked. So I think I will be ok now.
I appreciate y'alls time it got me to where I needed to be!

How to get SSIS to wait for a file to exist and/or become available

Scenario:
Package#1 creates a flat file that contains multiple messages (one per line) that is dropped in an external system's "INPUT" folder. The file is picked up by the external system and processed and the responses are written in the same format to a file in the "OUTPUT" folder. The file starts to be written while the external system is still processing messages, so it is written as foo.rsppro. When processing is complete and all response messages are written it is renamed foo.rsp.
I need to pick up that file once it is complete (i.e. after the rename) and process using Package#2, which will start immediatly following Package#1. When Package#2 starts, the external system could be in three states:
Processing the first message not yet complete and no response file written yet, in which case I need to wait for foo.rsppro to be written, then renamed to foo.rsp
Processing in progress and foo.rsppro written, in which case I need
to wait for foo.rsppro to be renamed to foo.rsp
Processing completed, foo.rsppro has been written and been renamed to foo.rsp, in which case I just need to process foo.rsp.
I have tried:
using a file in use task but that errors if the expected file isn't present when the task begins (i.e. errors for scenario 1 and 2)
using a file watcher task but that appears to ignore file renames by design, so will never handle scenario 1 or 2
Aside from building a script task, is there a custom task that will handle all three scenarios?
Edit: SSIS 2008 R2
only a script task can help in your case.
consider using FileSystemWatcher within the script if possible or have an application/windows service which can monitor file system using FileSystemWatcher and invoke your packages when the event is triggered.
humm, it seems that you can solve it by using a for each loop container on the output folder and set it to read only .rsp files. That would deal with your .rsp files.
how can scenario 1 and 2 happens if package 2 will only run after package1 is finish? As I understand, package1 renames the file so it will only end when all the files are processes and renamed
EDIT:
ok, no worry, there is a solution for everything.
How about, you create a variable on package1 called #TotalNumberOfFiles with the total number of files to be processed, then you use package one to call pacakge2 (not sure if you are doing this already, but if not is very simple, just use a execute pacakge task) and on package2 you create a "parent package variable" (this is very simple too in case you have never done it) and package 2 just start processing when there are #TotalNumberOfFiles files on the output folder with the .rsp extension?
EDIT2:
I dont know jf there is a command to get that, maybe google it, but if you dont find out you can add a foreachloop container pointing to the output directory and do something like this on a script task:
Public Sub Main()
Dts.Variables("User::filesCount").Value = Dts.Variables("User::FilesCount").Value + 1
Dts.TaskResult = ScriptResults.Success
End Sub
after it finishes counting, just compare with TotalNumberOfFiles. If equal, move to the next task, else sleep for a while and count again
Final code used as follows. Basically loops through until either the file is found or the max specified number of attempts is hit.
Imports System.Threading is required for Thread.sleep. It may not be the most processor efficient method but this is 100% dedicated hardware and the packages is are running in serial.
'loop until number of required attempts is hit or file is found
Do Until iCounter = iAttempts Or bFileFound = True
'Check if the file exists
If File.Exists(sFilename) Then
'Switch bFileFound to true
bFileFound = True
'Report that file has been found to VERIFY_Input_File_Exists_INT variable
Dts.Variables("VERIFY_Input_File_Exists_INT").Value = True
Dts.Events.FireInformation(1, "DEBUG:", sFilename & " found successfully.", "", 0, False)
Else
'sleep for specified time
Thread.Sleep(iInterval * 1000)
Dts.Events.FireInformation(1, "DEBUG:", sFilename & " not found successfully. Sleeping for " & iInterval & "* 1000", "", 0, False)
End If
'increment counter
iCounter = iCounter + 1
Loop

Case-insensitive diffs in Mercurial

I'm using Mercurial (specifically TortoiseHg on Windows) to do version control of VBA code. Anybody who's tried this knows that VBA changes the case of every variable throughout a project whenever any declaration of that variable is changed anywhere in the project (regardless of scope). It makes version control a nightmare.
I would like to ignore case changes in my source code when performing diffs. What is the easiest way to do this? (some option for diff that I'm missing, an external diff utility, something else?)
NOTE: I am not talking about dealing with 'case-insensitive filenames' (yes, I'm talking to you Google...)
You can do that when diffing for your on-screen consumption using the ExtDiff Extension.
[extensions]
hgext.extdiff =
[extdiff]
# add new command that runs GNU diff(1) in case-insensitive mode
cmd.mydiff = diff
opts.mydiff = -i
Then you'd run hg mydiff from the command line. That, of course, requires you have a diff binary installed be it gnu's or other.
However, that's not going to be as helpful as you might like because internally, of course, Mercurial can't ignore case -- it's taking the cryptographic hash of the file contents, and those don't allow for wiggle room. So if you get this set up you'll do hg mydiff, and see no changes, and then do hg commit and see changes all over the place.
So you can make this work on-screen, but not fundamentally.
One option would be to find a visual basic code-cleaner, similar to indent for C-like languages, that normalizes variable case and run that in a mercurial commit hook. Then at least all the code going into source control will be consistent and you can diff across revisions accurately.
If you are okay with having your code in all lower-case, say, then you could employ the encode/decode hooks for this. It would work like this:
[encode]
*.vba = tr A-Z a-z
This will encode the file content in lower-case whenever you do a commit. The diffs are also computed based on the encoded (repository) version of the files.
Consider a file that contains
hello
Changing it in your working copy to
Hello World
will give a diff of
% hg diff
diff --git a/a.txt b/a.txt
--- a/a.txt
+++ b/a.txt
## -1,1 +1,1 ##
-hello
+hello world
Notice how the capital "H" and "W" has been ignored.
I don't really know anything about VBA code, so I'm not 100% sure this solution works for you. But I hope it can be a starting point.
One drawback is that you'll need to set this encode rule for all your repositories. The reposettings extension can help you here.
Here's the solution I have settled on. It is far from ideal, but better than the other alternatives I've considered.
I created an Autohotkey script that does the following:
reverts MS Access files in a repository with detected changes (to .orig files)
reads in the .orig file (the one with the changes)
reads in the existing file (the one already in the repository)
converts the text of both files to lower case
compares the lower case contents of the files
if the files still differ, the .orig file is restored so it may be committed to the repository
if the files are the same (i.e., they differ only in case, the .orig file is deleted because we don't care about those changes)
For files that have actual changes that we care about, I still see the case changes that were made as well. If that results in a lot of noise, I open the file in a comparison tool that allows case-insensitive compares (e.g., kdiff).
It's not a perfect solution, but it removes about 90% of the frustration for me.
Here's my script. Note that the script includes another Autohotkey script, ConsoleApp.ahk, which provides a function named, ConsoleApp_RunWait(). This is a 3rd party script that no longer works very well with 64-bit AHK, so I'm not including it as part of my answer. Any AHK function that executes a command line and returns the output as a string will suffice.
; This script checks an MS Access source directory and reverts all files whose only modifications are to the
; case of the characters within the file.
#Include %A_ScriptDir%\ConsoleApp.ahk
#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
SendMode Input ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory.
; Allow for custom path to hg (support for moving to TortoiseHg 2.0)
IniRead hg, %A_ScriptDir%\LocalSettings\Settings.cfg, TortoiseHg, hg_path, hg
if 0 < 1 ; The left side of a non-expression if-statement is always the name of a variable.
{
MsgBox Usage:`n`HgIgnoreCase DirectoryWithFilesToScrub
ExitApp
}
SrcDir = %1%
StringReplace SrcDir, SrcDir, ", , All
StringRight test, SrcDir, 1 ; add trailing slash if necessary
ifnotequal test, \
SrcDir = %SrcDir%\
RestoreOriginals(SrcDir)
RevertCaseChangeModifiedFiles(SrcDir)
RevertCaseChangeModifiedFiles(SrcDir) {
global hg
includes = -I "*.form" -I "*.bas" -I "*.report" -I "*.table"
cmdline = %hg% revert --all %includes%
;Don't revert items that have been removed completely
Loop 3
{
Result := ConsoleApp_RunWait(hg . " status -nrd " . includes, SrcDir)
If (Result)
Break
}
Loop parse, Result, `n, `r
{
if (A_LoopField)
cmdline = %cmdline% -X "%A_LoopField%"
}
Result =
;msgbox %cmdline%
;revert all modified forms, reports, and code modules
Loop 3
{
Result := ConsoleApp_RunWait(cmdline, SrcDir)
If (Result)
Break
}
;MsgBox %Result%
Loop parse, Result, `n, `r
{
StringLeft FileStatus, A_LoopField, 9
If (FileStatus = "reverting")
{
StringMid FName, A_LoopField, 11
FullPath = %SrcDir%%FName%
ToolTip Checking %FullPath%
RestoreIfNotEqual(FullPath, FullPath . ".orig")
}
}
ToolTip
}
RestoreIfNotEqual(FName, FNameOrig) {
FileRead File1, %FName%
FileRead File2, %FNameOrig%
StringLower File1, File1
StringLower File2, File2
;MsgBox %FName%`n%FNameOrig%
If (File1 = File2)
FileDelete %FNameOrig%
Else
FileMove %FNameOrig%, %FName%, 1
}
RestoreOriginals(SrcDir) {
Loop %SrcDir%*.orig
{
;MsgBox %A_LoopFileLongPath%`n%NewName%
NewName := SubStr(A_LoopFileLongPath, 1, -5)
FileMove %A_LoopFileLongPath%, %NewName%, 1
}
while FileExist(SrcDir . "*.orig")
Sleep 10
}