Run specific executable with conflicting name within clojure.java.shell - shell

I have two executables named convert.exe in my PATH. One of them is from ImageMagick, the other is from Windows.
user=> (clojure.java.shell/sh "where" "convert")
{:exit 0, :out "E:\\Program Files\\ImageMagick-6.8.3-Q16\\convert.exe\r\nC:\\Windows\\System32\\convert.exe\r\n", :err ""}
I would like to run ImageMagick's convert.exe within clojure.java.shell/sh. Normally, I can do this within PowerShell.
PS E:\Users\bt> convert --version
Version: ImageMagick 6.8.3-8 2013-03-04 Q16 http://www.imagemagick.org
Copyright: Copyright (C) 1999-2013 ImageMagick Studio LLC
Features: DPC OpenMP
Delegates: bzlib fontconfig freetype jng jp2 jpeg lcms lzma pango png ps tiff x xml zlib
However, this does not work within clojure.java.shell/sh.
user=> (clojure.java.shell/sh "convert" "--version" :dir "E:/Users/bt")
{:exit 4, :out "", :err "Invalid drive specification.\r\n"}
Initially, I thought that I needed to move the path to ImageMagick to the front of my PATH so that it would gain precedence over Windows' binary, but that didn't have any effect. Also, I moved it to the end of the PATH just for kicks. That also didn't work.
The only alternative I know that resolves this is to rename either of the executables. But this is undesired because I do not want others to rename their ImageMagick/Windows binaries just to make my code work.
If this information is of any use, opening a new command shell (not PowerShell, but cmd.exe) starts in C:\Windows\System32. I have a feeling that this might affect the choice of executable, but I'm not sure. This only happens if I run cmd.exe from Launchy. If I use the Start Menu, it'll start in my home directory.
Is there any way to do this? I feel like I'm missing something obvious.

I can reproduce your results. Whatever the reason is, it's buried somewhere with Java's Runtime.exec which receives the PATH, but maybe it adds the Windows System dir in front?
If you want to understand it thoroughly, this is where you should continue investigation.
See the call to it in sh:
(let [...
proc (.exec (Runtime/getRuntime)
(into-array cmd)
(as-env-string (:env opts))
(as-file (:dir opts)))]
It does work if you give an absolute path to the executable, however. So, here are two options how you can solve the problem:
If it is safe to assume that the IM convert.exe is the first on the PATH, just execute "where" as you showed in your question, take the first path from the :out which is returned, append convert.exe to it and use the result as program to sh.
If you cannot assume a correct PATH, ask the user where Image Magick is installed and store the program path somewhere. Prepend this to the program path.

Can you try giving the absolute PATH for the executable?
like,
user=> (clojure.java.shell/sh "E:\\Program Files\\ImageMagick-6.8.3-Q16\\convert.exe" "--version" :dir "E:/Users/bt")
OR
user=> (clojure.java.shell/sh "E:\\Progra~1\\ImageMagick-6.8.3-Q16\\convert.exe" "--version" :dir "E:/Users/bt")

Related

How are WSL POSIX paths converted to UNC for Windows native applications?

I found out that if I execute a Windows native program (PE) from WSL2, accessing a POSIX path magically works.
For example, I can access /dev/random if I execute my program from WSL bash, but if I execute the same program from CMD (command-prompt), I cannot.
I must understand the mechanism which allows this! :)
The test program is fairly simple:
#include <stdio.h>
int main(int argc, char *argv[], char *envp[]) {
printf("%p\n", fopen("/dev/urandom", "r"));
return 0;
}
If I execute this from inside the WSL instance, it succeeds opening the device.
If I execute this via CMD, however, it fails.
When I look at API mon, I can see that the open("/dev/urandom", "r") is converted to CreateFileA("\\wsl.localhost\Ubuntu\dev\urandom", ...).
First question: What component is doing this conversion?
If I replace the fopen with CreateFile it fails... so it must be something in the stdio functions.
Second question: How does it know what WSL instance is the parent?
I saw no API query, no environment to give me a hint. The only abnormality I can see is the opening \\wsl.localhost\Ubuntu\tmp during process startup.
Third question: Does this survive nested within process tree?
When I execute cmd.exe from inside WSL, then execute my test program, it fails.
However, I wrote my own native Windows program that executes my test program and the test program succeeds, so this behavior does survive process tree.
Can anyone explain the mechanism that allows this magic to work? What API? What component is doing the transition? Where is the context stored? How is it queried? How does it knows what distro to lookup?
I tried to ask this at Microsoft discussion[1] and got no response, so I am hopping someone here may be able to provide a hint.
[1] https://github.com/microsoft/WSL/discussions/8212
Short summary. I believe:
/init handles the conversion of the working directory that gets passed to the Windows executable.
When a path starts with a directory separator character (e.g. / or \), fopen considers it to be relative to the root of the volume of the working directory.
For example:
If you execute your code from /home/<username>
... then the working directory will be \\wsl.localhost\Ubuntu\home\<username>.
... the "volume" (share name in this case) will be \\wsl.localhost\Ubuntu\
... so /dev/random is opened as \\wsl.localhost\Ubuntu\dev\random.
Try this, however:
cd /mnt/c (or any location inside that mount)
Call your program via /full/path/to/the.exe.
The fopen fails in my testing (and I assume will for you as well), because ...
... the working directory that gets passed in is C:\ (or a subdirectory thereof).
... thus the volume name is also C:\.
... and fopen attempts to open C:\dev\random, which doesn't exist.
More detail:
What component is doing this conversion?
That part is (I believe) fairly easy to answer, although not definitively. As mentioned in this answer, when you launch a Windows executable in WSL, it uses a handler registered with binfmt_misc (see cat /proc/sys/fs/binfmt_misc/WSLInterop) to call the WSL /init.
Unfortunately, WSL's /init is closed source, and so it is difficult to get full insight into what is happening with the launch process. But I think we can safely say that the handler (/init) is going to be the component that converts the path before the Windows process receives it.
One interesting thing to note is that the wslpath command is mapped to that same binary via symlink. When called with the name wslpath, the /init binary will do OS path conversions. For example:
wslpath -w /dev/random
# \\wsl.localhost\Ubuntu\dev\random
But here's the real question ...
So we know that /init knows how to convert the path, but exactly what does it convert when launching a Windows binary? That's a bit tricky, but I think we can surmise that what gets converted is the path of the current working directory.
Try these simple experiments:
$ cd /home
$ wslpath -w .
\\wsl.localhost\Ubuntu\home
$ powershell.exe -c "Get-Location"
Path
----
Microsoft.PowerShell.Core\FileSystem::\\wsl.localhost\Ubuntu\home
$ cd /dev
$ wslpath -w .
\\wsl.localhost\Ubuntu\dev
$ powershell.exe -c "Get-Location"
Path
----
Microsoft.PowerShell.Core\FileSystem::\\wsl.localhost\Ubuntu\dev
$ cd /mnt/c
$ wslpath -w .
C:\
$ powershell.exe -c "Get-Location"
Path
----
C:\
And another question
So here's my question -- When did the Windows API get smart about concatenating UNC working directories and paths that start with a directory separator? I can find no documentation on that behavior, but it obviously works. And it's not specific to WSL. I observed the same concatenation behavior when using a UNC working directory for a regular network share.
Even more curious is that .NET's path handling is not this smart about UNC concatenation. From the doc, the behavior we observe with fopen is expected for DOS paths, but for UNC:
UNC paths must always be fully qualified. They can include relative directory segments (. and ..), but these must be part of a fully qualified path. You can use relative paths only by mapping a UNC path to a drive letter.
And I was able to confirm that behavior in PowerShell with a simple Get-Content.
Back to our regularly scheduled ...
But that aside, you don't even need your sample code to demonstrate this. You can see the same behavior by calling notepad.exe from within WSL:
$ cd /etc
$ notepad.exe /home/<username>/testfile.txt
# Creates or opens the proper file using \\wsl.localhost\Ubuntu\home\<username>\testfile.txt
$ cd /mnt/c/Users
$ notepad.exe /home/<username>/testfile.txt
# Results in "The system cannot find the path specified", because it is really attempting to open C:\home\<username>/testfile.txt, and the `home` directory (likely) doesn't exist at that path.
And your other related questions:
How does it know what WSL instance is the parent?
In case it's not clear by now, I think it's safe to say that the WSL /init knows what WSL instance you are in since it is "orchestrating" the whole thing anyway.
Does this survive nested within process tree?
As long as one process doesn't change the working directory of the next process in the tree, yes. However, CMD doesn't understand UNC paths, so, if it's in the process chain, your program will fail.

Imagemagick error using overlapcrop on Window with Cygwin

I am using Imageimagick to crop arieal images in equal sizes.
Searching Google imagemagick tutorials led me to Fred Weinhaus scripts tutorial which I followed. When I am passing the command on bash or cmd based on syntax given in this website (bash /fullpathto/scriptname.sh with arguments /fullpathto/inputimage /fullpathto/outputimage)
I am getting error
$ overlapcrop -s 128 -o 50% -m matrix -M -L \
-R 'F:\bash\top_potsdam_2_10_RGB' 'F:\bash\o.jpg'
error Invalid Parameter - F:\bash\top_potsdam_2_10_RGB
FILE F:\bash\top_potsdam_2_10_RGB DOES NOT EXIST OR IS NOT AN ORDINARY FILE, NOT READABLE OR HAS ZERO SIZE
Even if we set the directory in the path, we get the same error:
$ overlapcrop -s 128 -o 50% -m matrix -M -L -R top_potsdam_2_10_RGB.png o.png
--Screenshots of imagemagick --help and convert --help
error I am getting
Windows is useless when it comes to quoting, so maybe try escaping the % sign by adding a second one or a caret (^) in front of it.
Also, try removing the F: from the paths and try putting the files in the current directory in case the slashes are causing errors.
Finally, you may have your PATH set incorrectly so that when the script executes the convert command it finds the Windows utility that converts filesystems to NTFS rather than the ImageMagick utility that converts images.
Try running:
convert /?
and seeing if you get an error/help message from Windows CONVERT.EXE or something from ImageMagick. If you get the Windows one, your PATH is incorrect and you need to put the directory where you installed ImageMagick ahead (in front of) C:\WINDOWS\System32 or wherever Windows CONVERT.EXE lives and restart your Command Prompt window.

is there a way to specify custom init file on emacs startup?

I've tried runemacs --user d:\path\to\init\.emacs but it gives
Error (initialization): Invalid user name d:\path\to\init\.emacs
I mean custom file in custom location like on my pendrive etc. and not in my default locations in user folder. I try to create a cmd files with corresponding inits for different configuration.
I am guessing that your end goal is to run Emacs from a USB drive which you can carry around. There are two ways to have Emacs start with a different init file:
(1) the -q --load way
This is simply to run
emacs -q --load path-to-your-init.el
which starts Emacs without any initialization, then loads path-to-your-init.el
Gotchas of this way: in this case, Emacs doesn't really consider the el file to be an init file, in fact, Emacs hasn't gone through initialization at all, which means the following post-initialization steps are skipped:
enabling packages, that is, evaluating (package-initialize)
running hook after-init-hook, that is, evaluating (run-hooks after-init-hook)
You may have to add post-initialization stuff in your alternate init file.
Also, Customize interface won't let you save settings if Emacs is started with -q option.
(2) the HOME way
Create my-emacs.bat with contents:
set HOME=%~dp0
C:\path-to-your\Emacs\bin\runemacs.exe --xrm "emacs.Background: light green"
When you click on that bat file, Emacs creates folder named .emacs.d not in your usual user directory, but in where the bat file is. You can put your own init.el in that .emacs.d folder. With this Emacs, evaluating (find-file "~/.emacs.d/init.el") opens your own portable init.el and not the init file in your usual user directory. You may want to delete the --xrm "emacs.Background: light green" part, I include that because I always forget which Emacs is which.
Gotchas: this Emacs has its own package directory in its own .emacs.d directory. That may or may not be what you want. If you want to share the package directory, you can change package-user-dir or create a symbolic link to the to-be-shared package directory using Link Shell Extension.
Somethings to consider for running Emacs on a USB drive:
Emacs outsources some of its features to external tools. Grepping and comparison are outsourced to grep and diff, so you probably have installed at least GnuWin32 Grep and GnuWin32 DiffUtils on your system. And part of spell checking is outsourced to aspell, which is also a separate install. Encryption is outsourced to GPG, so Gpg4Win another separate install. For your portable Emacs to have all those features, all those external tools should be on your USB drive as well. Fortunately, GnuWin32 and aspell are portable-friendly. I don't know if GPG is. The dependency to external grep may be eliminated if you manage to make versions of lgrep and rgrep that only depend on eshell's own grep, but it seems nobody has done it, so making such lgrep/rgrep is to boldly go where no man has gone before.
You can get help on all of the emacs options by typing emacs --help on the command line. That help shows that --user expect the name of a user, not the path to an elisp file.
To load a lisp file at startup you can use the --load option:
$ emacs --help
...
--load, -l FILE load Emacs Lisp FILE using the load function
...
If you want to prevent your default init file from loading, you can use this option:
$ emacs --help
...
--no-init-file, -q load neither ~/.emacs nor default.el
...

Is there a way to save a file in vim in Windows without marking it executable?

Heading says it all really. Using Windows 7 and latest stable gvim, whenever I save (:w) a file it's marked executable. I'm doing cross-platform development and it'd be nice if this didn't happen.
#sceptics: The flag of the files are indeed set as executable. Do a ls -al before and after re-saving the file to observe the issue. (install cygwin, or may be other *nix emulations)
#OP: the question have been raised several times in the past. I don't remember the conclusion on the subject. You should search vim mailing-lists archives (vim_use and vim_dev).
May be you can try to add an hook to your RCS (if it supports that) to proceed to a chmod -x on file extensions that does not correspond to an executable (*.h, *.cpp, *.vim, ...), or on files that do not contain a shebang (unlike perl, I don't know if python source files may contain a shebang)

How do I set/clear the Windows archive bit with cygwin's find/chmod?

Yes, I know, the archive bit is evil.
That being said, is there support for querying it with 'find', and modifying it with 'chmod'?
My googling has turned up nothing......
As already mentioned by Jed, you can use attrib both to query and to set the archive bit. You must however remember to use the cygpath tool to convert between cygwin style file names and DOS style names, as required by attrib.
If you convert the output of find with cygpath, invoke attrib for each file name and use egrep to check for lines starting with A (regexp '^A'), you should be able to search for files with the archive bit set.
When I used cygwin, I made sure it had access to the Windows tools as well. In that case, you can use attrib to at least set or clear the archive bit for you.
To list files with the archive bit set you can use dir /A:A, which you can accomplish by doing CMD /c or something similar.
I don't think you're going to find the ability to do this in Unix tools.

Resources