CWD after opening file is home directory. how to change it?

The dedicated forum for PCMan File Manager - http://wiki.lxde.org/en/PCManFM
Locked
botm
Posts: 2
Joined: Wed May 16, 2018 2:57 pm

CWD after opening file is home directory. how to change it?

Post by botm »

Hello,
I have a question about PCManFM,
is it possible to change it so that if a file is opened by double-clicking then the current working directory (CWD) will be the directory where I clicked it and not my home directory?

More detailed:
I have Debian 9 with LXDE and the default file manager is PCManFM.
I observed the following behavior:

If I double-click an executable program I will get a choice to run it. If I do so, then the program will be launched and its CWD will be the directory where I clicked it. And this is expected behavior.
But if I double-click a different file which is not an executable program then it will open the file by launching the command from the appropriate .desktop file and then the program launched this way will have the CWD set to my home directory which is not what would be expected.

Normally, this is not a problem except these two situations where I start a program this way:

1. Windows programs
When I double-click a windows program, the associated command is "/usr/bin/wine %f". So PCManFM launches Wine with the CWD set to my home directory (which is "/b") and Wine runs the Windows program which sees the CWD as "Z:\b". This is problematic because I would expect the Windows program to be run with the CWD set to the directory where I clicked it.

2. PIE executables
In Debian 9 the default format of executable programs is PIE (Position Independent Executable). Unfortunately the MIME system cannot distinguish them from shared objects. Because of this PCManFM also sees these programs as shared objects and if I double-click such a program PCManFM does not give me the choice to run it but instead asks what program to open the file with.
(Actually, I believe this is a bug that it is not possible to run a PIE executable by double clicking. Shouldn't just the executable bit be enough for PCManFM to know what is an executable file and what not? And if not, at least "shared object" + executable bit should be treated as an executable program, I think)
There is a workaround. I can set the command associated with this file type to "xterm -e %f" so clicking the executable file will make PCManFM run xterm which will execute the file. But again with CWD set to my home directory.

So, that's why I'm asking. Is it possible to change this behavior so that If I double-click a file in PCManFM then the program launched this way will have the CWD set to the directory where I clicked and not my home directory?
drooly
Posts: 791
Joined: Mon Apr 08, 2013 6:45 am

Re: CWD after opening file is home directory. how to change it?

Post by drooly »

botm wrote:If I double-click an executable program I will get a choice to run it. If I do so, then the program will be launched and its CWD will be the directory where I clicked it. And this is expected behavior.
But if I double-click a different file which is not an executable program then it will open the file by launching the command from the appropriate .desktop file and then the program launched this way will have the CWD set to my home directory which is not what would be expected.
you are talking about freedesktop specifications: https://freedesktop.org/wiki/Specificat ... entry-spec
what you want exactly may not be possible, but these .desktop files can be written & modified to do all sorts of things, and you can pass the current directory as an argument to the program. if your program does not support that, just wrap it in a script.

btw there's a logical flaw in your thinking; .desktop files are NOT supposed to apply to random executables that aren't in your PATH.
botm
Posts: 2
Joined: Wed May 16, 2018 2:57 pm

Re: CWD after opening file is home directory. how to change it?

Post by botm »

I found a solution for this using .desktop files.
See my post on Debian forums:
http://forums.debian.net/viewtopic.php?p=673989#p673989
bicyclesonthemoon wrote:
debiman wrote:it's not a bug; there's a logical error on op's behalf, using .desktop files for small, self-written C exercises running under wine is not appropriate.
the self written C was only done for:
  • demonstrating the problem with a simple example program
  • testing in which situation the problem occurs and in which not
What I really mean is that the expected behavior is that when I double-click ANY executable file (and not just an example made by me) then the program will be run with the CWD set to the directory where I clicked it (which is where the file is located) but that is not happening in all cases.

With these test programs I was able to verify that this is not working for windows executables run in wine and PIE executables and that this is working for non-PIE executables and executable scripts.

I also asked a question on the pcmanfm-related forum but didn't get the answer I needed.
In the meantime I tried to determine by myself if I can change pcmanfm behavior by some its config and the answer is most likely no.
and tried to see in its source code how it exactly runs programs after clicking and I failed to find it but didn't spend much time on it.

Anyway I did find a solution and it does indeed use .desktop files.
debiman wrote:it is possible to pass the CWD to the program via .desktop file specs, if so desired.
Actually, not really. also that is not a generic solution. Unless I do some tricks.

So I found out that .desktop is my only remaining option.
So there is this the "Path" key which is used exactly for this reason.
Great, so something like

Code: Select all

Path=$PWD
should help.
But it didn't.
There could be 2 reasons:
  1. the PWD variable could be already set to home at this stage
  2. $PWD was not passed as the variable value but literally "$PWD"
I made a simple test program and made it print all its arguments and added $PWD to the "Exec" key and saw that it indeed was passed as just "$PWD". So this approach is useless.

I was told here and on the lxde forum that it is possible to pass the cwd as an argument by .desktop.
Like I already said this is not a generic solution.
On that forum a wrapper was suggested to me. This gave me an idea.
After some thinking I realised that it IS possible to make a generic wrapper.

I wrote this,
runcwd.c:

Code: Select all

//Niezła wymota
//This generic wrapper allows to run a program with specified CWD. First argument is the CWD, second is the program to run, rest are the parameters passed to that program

#include <unistd.h>
#include <stdio.h>

int main(int argc, char *argv[], char *envp[])
{
	int r;
	
	if (argc<2) {
		fprintf(stderr,"%s\n","CWD missing.");
		return 1;
	}
	else if (argc <3) {
		fprintf(stderr,"%s\n","Command missing.");
		return 1;
	}
	
	if (chdir(argv[1]))
		fprintf(stderr,"Chdir to %s failed.\n",argv[1]);
	
	r=execve(argv[2],argv+2,envp);
	
	fprintf(stderr,"Failed to run %s.\n",argv[2]);
	return r;
}
A very simple program which
  • changes the cwd to the first argument
  • runs the program from the second argument
  • passes all next arguments to that program
The only thing left to do was to figure out how to pass the cwd as the first argument. And I found out that I can not do it.
$PWD is not an option but the Exec key has some special field codes. Maybe one of them could be useful?
From the available codes none is the directory.
There used to be a %d code which is exactly this but it is deprecated. I tested it and it does not work. Why remove such a useful option?
So I can not do it this way.
But I could modify it to extract the path from %f.

runcwdi.c:

Code: Select all

//Niezła wymota
//This generic wrapper allows to run a program with specified CWD. First argument is the CWD (must end with "/", anything after it is ignored), second is the program to run, rest are the parameters passed to that program

#include <string.h>
#include <unistd.h>
#include <stdio.h>

int main(int argc, char *argv[], char *envp[])
{
	int r;
	
	if (argc<2) {
		fprintf(stderr,"%s\n","CWD missing.");
		return 1;
	}
	else if (argc <3) {
		fprintf(stderr,"%s\n","Command missing.");
		return 1;
	}
	
	for (r=strlen(argv[1])-1; r>=0; --r)
	{
		if (argv[1][r] == '/')
			break;
		argv[1][r]=0;
	}
	
	if (chdir(argv[1]))
		fprintf(stderr,"Chdir to %s failed.\n",argv[1]);
	
	r=execve(argv[2],argv+2,envp);
	
	fprintf(stderr,"Failed to run %s.\n",argv[2]);
	return r;
}
I modified it to cut everything after the last "/" in the path.

And, finally, it works.
I moved runcwdi to /usr/bin.
This is now my .desktop for windows executables:

Code: Select all

[Desktop Entry]
Type=Application
Name=winecwd
Exec=/usr/bin/runcwdi %f /usr/bin/wine %f
Categories=Other;
NoDisplay=true
MimeType=application/x-ms-dos-executable
Terminal=false
and this is now my .desktop for PIE executables:

Code: Select all

[Desktop Entry]
Type=Application
Name=execcwd
Exec=/usr/bin/runcwdi %f %f
Categories=Other;
NoDisplay=true
MimeType=application/x-sharedlib
Terminal=true
Now everything works like it should.
I'm glad that I made it work.
But I don't like the fact that it was necessary to do such things just to make the system work correctly.
I could solve this for myself after spending effort and time to figure it out and do it (when I could be drawing comics for example) and that's one of the basic functionalities of the OS, something that you would expect to just work by default. But not here.
Also some other people might not be able to fix this for themselves because it's not trivial.

Also, this is not the only problem I have with this system.
Another one is, for example, midi playback.
But that's a topic for another thread (coming soon).
Locked