How to get the parent directory path of a path in FreePascal/Lazarus? - pascal

I have a path to a directory saved as a string, and wanted to know how I can easily and robustly extract the parent directory from this string?
I tried to see if there is some method for this in FileUtil and SysUtils, but haven't found anything so far.

The simplest way is to find the last path delimiter character and trim the source string. BTW there are some alternative:
program Project1;
uses
sysutils;
var
sExe: string;
sParent: string;
sParentProper: string;
begin
sExe := ExtractFilePath(ParamStr(0)); // Get executable directory
Writeln(sExe);
sParent := IncludeTrailingPathDelimiter(sExe) + '..' + PathDelim; // Make parent path for executable
Writeln(sParent);
sParentProper := ExpandFileName(sParent); // Get absolute path based on relative path
WriteLn(sParentProper);
Readln;
end.
And output is:
C:\Users\nd\AppData\Local\Temp\
C:\Users\nd\AppData\Local\Temp\..\
C:\Users\nd\AppData\Local\
So using this technique the proper way is ExpandFileName(IncludeTrailingPathDelimiter(sBasePath) + '..')
PS: We are using only sysutils unit so it is pure FPC solution and it is not require any LCL libraries.

An even simpler way to do this would be:
parentDirPath := ExtractFilePath(ExcludeTrailingPathDelimiter(thePath));
This works on the three major platforms (Linux, Mac OS X and Windows), and thePath may refer to a file or a folder.

Well, I of course figured it out only after asking about it here:
parentDirPath := FileUtil.ExtractFileNameOnly(
FileUtil.ChompPathDelim(theSubDirPath));
... will do!
The FileUtil.ChompPathDelim() part is necessary to "fool" FPC to believe the top folder is a "file" (so no trailing slash allowed).

Related

filepath Base on windows not working after cross compilation (Golang)

If I have a path on linux/OSX such as filepath := /Users/jamesbond/some/where/file.txt
then I can do
name := path.Base(filepath)
and name will be file.txt
however on windows this does not work. The returned value of name is still /Users/jamesbond/some/where/file.txt
I guess I can see why, as the function base has
for len(path) > 0 && path[len(path)-1] == '/' {
path = path[0 : len(path)-1]
}
i.e its clearly really only setup for linux/OSX style file paths.
I am cross compiling from OSX for windows. I guess that the library on windows has the corect / in the SDK, but how can I handle this regardless of OS? Is there a way to convert or something before, then use Base function?
What would be the recommended way to write this code once and it work on all OSes?

How find shortcut name by program name?

Is there some way to find the name of a shortcut (present on desktop) through the associated program name?
Ex:
Filename:
C:\Program Files\Mozilla Firefox\firefox.exe
and result in:
C:\Users\Public\Desktop\Firefox.lnk
I found something near to this, but is made the opposite way (returns the associated program name by shortcut name).
The application knows nothing about shortcuts that are created to point to it, so this isn't possible. You'd have to iterate every file in the user's Desktop folder looking for shortcut files, open them using IShellLink, and look to see if they launched the application you're looking to find. Here's an example of doing so. You'll need to add ShellAPI to your uses clause. FileName is the fully qualified name of the shortcut file.
function GetLinkPath(const FileName: WideString): String;
var
ShellLink: IShellLink;
Path: array[0..MAX_PATH] of Char;
begin
Result := '';
ShellLink := CreateComObject(CLSID_ShellLink) as IShellLink;
if (ShellLink as IPersistFile).Load(PWideChar(FileName), STGM_READ) = 0 then
begin
if ShellLink.GetPath(Path, MAX_PATH, nil, SLGP_SHORTPATH) = 0 then
Result := Path;
end;
end;

Using paths in different packages convenient way

I have a program in which I use a lot "../" which is to go one level up
in the file system and run some process on the directory with specific name. I have a command line tool in Go.
I have 3 questions
there is nicer way to do it instead of “../“
is there a const with which I can use instead of “/“
if 2 is not available should I create “constants“ under that internal package to share the “/“ between packages since I need it in
many place (from diff packages...)
example
dir.zip("../"+tmpDirName, "../"+m.Id+".zip", "../"+tmpDirName)
Set a variable, and use that everywhere:
path := "../"
or
path := ".." + string(os.PathSeparator)
then later:
dir.zip(path+tmpDirName, path+m.Id+".zip", path+tmpDirName)
This makes it very easy to change the path in the future, via a command line option, configuration, or just editing the value.
Yes. os.PathSeparator is the OS-specific path separator for the current architecture.
n/a
declare a global const somewhere, but I would just use ".." everywhere
os.PathSeparator
use filepath.Join("..", someDir, someFilename)

How can this code generate a file with a filename containing a colon on Windows?

I see the pascal code below on another forum. How can this code be possible?
Doesn't windows allow user to create a filename with colon?
However, this code only work when you create a file with name contains colon in root directory of drive (Ex: D:, C:, E:, etc). And when the file is created, it's completely invisible.
uses crt, sysutils;
var
f, f1: file of char;
c:char;
begin
clrscr;
assign(f, 'D:\src\payload.exe');
reset(f);
assign(f1, 'D:\:malware.exe');
rewrite(f1);
while not eof(f) do
begin
read(f, c);
write(f1, c);
end;
close(f1);
close(f);
executeprocess('D:\:malware.exe', ''); //here
readln;
erase(f1);
end.
You can compile the code above with free pascal
fpc [filename].pas
Thank you.
EDIT:
For more detail:
You can execute D:\:malware.exe from CreateProcess (WinAPI)
You can't execute D:\:malware.exe from command line, path, etc
I use process explorer to find D:\:malware.exe path/contain folder. However, when I pressed explore button, it takes me to %UserProfile%
It only work for D:\:malware.exe, D:\\malware.exe, D:\/malware.exe
It works because it is possible*. You can name files all kinds of horrid things, regardless of proper naming convention.
*Yes, I know MSDN lists colons as “reserved”. That is not the same as forbidden or impossible. It is only the same as “don’t do it”.

How to get the parent folder of a Windows user's profile path using C++

I am trying get the parent folder of a Windows user's profile path. But I couldn't find any "parameter" to get this using SHGetSpecialFolderPath, so far I am using CSIDL_PROFILE.
Expected Path:
Win7 - "C:\Users"
Windows XP - "C:\Documents and Settings"
For most purposes other than displaying the path to a user, it should work to append "\\.." (or "..\\" if it ends with a backslash) to the path in question.
With the shell libary version 6.0 you have the CSIDL_PROFILES (not to be confused with CSIDL_PROFILE) which gives you what you want. This value was removed (see here), you have to use your own workaround.
On any prior version you'll have to implement your own workaround, such as looking for the possible path separator(s), i.e. \ and / on Windows, and terminate the string at the last one. A simple version of this could use strrchr (or wcsrchr) to locate the backslash and then, assuming the string is writable, terminate the string at that location.
Example:
char* path;
// Retrieve the path at this point, e.g. "C:\\Users\\username"
char* lastSlash = strrchr(path, '\\');
if(!lastSlash)
lastSlash = strrchr(path, '/');
if(lastSlash)
*lastSlash = 0;
Or of course GetProfilesDirectory (that eluded me) which you pointed out in a comment to this answer.

Resources