Recent

Author Topic: File path to ${HOME}  (Read 4320 times)

cryppyrc

  • Newbie
  • Posts: 1
File path to ${HOME}
« on: May 01, 2023, 08:16:47 am »
I am new to this forum (and new to pascal) so I hope this question is in the appropriate sub section.

I am trying to compile an app for macOS that was written with a focus on Windows users and I don't understand how I need to adjust the code to make it work on Darwin.

If executed on Linux or Windows from CLI, the app creates two folders (wallet and data) in the app directory. On Darwin this obviously does not work because apps have no permission to write to the app bundle. Therefore I am trying to find a way to adjust the paths of the code snippet below to point to the respective user's home directory ${HOME} in Darwin.

Path definition:
Code: Pascal  [Select][+][-]
  1. CONST
  2.   WalletDirectory   = 'wallet'+directoryseparator;  // Wallet folder
  3.   DataDirectory     = 'data'+directoryseparator;
  4.   WalletFileName    = WalletDirectory+'wallet.pkw';  // Wallet keys file
  5.  

I would be very happy for any hints.


simone

  • Hero Member
  • *****
  • Posts: 644
Re: File path to ${HOME}
« Reply #1 on: May 01, 2023, 10:23:49 am »
I have no experience in developing under Mac OS, but about environment variables, maybe this wiki can help:

https://wiki.freepascal.org/Command_line_parameters_and_environment_variables#Environment_variables
« Last Edit: May 01, 2023, 10:27:55 am by simone »
Microsoft Windows 10 64 bit - Lazarus 3.0 FPC 3.2.2 x86_64-win64-win32/win64

KodeZwerg

  • Hero Member
  • *****
  • Posts: 2269
  • Fifty shades of code.
    • Delphi & FreePascal
Re: File path to ${HOME}
« Reply #2 on: May 01, 2023, 11:23:17 am »
Code: Pascal  [Select][+][-]
  1. {$mode objfpc}{$H+}
  2. {$modeswitch objectivec1}
  3.  
  4. ...
  5.  
  6. uses
  7.   CocoaAll; // for NSBundle
  8.  
  9. ...
  10.  
  11. function GetBundlePath: string;
  12. begin
  13.   Result := NSBundle.mainBundle.bundlePath.UTF8String;
  14. end;
Taken from https://wiki.lazarus.freepascal.org/macOS_Programming_Tips
« Last Edit: Tomorrow at 31:76:97 xm by KodeZwerg »

dbannon

  • Hero Member
  • *****
  • Posts: 3192
    • tomboy-ng, a rewrite of the classic Tomboy
Re: File path to ${HOME}
« Reply #3 on: May 01, 2023, 12:42:15 pm »
On the Mac, only files that would be discareded in the even, eg, of an update would be kept in the bundle.  Given we don't see Trev very often these days, here is how I do it ("tomboy-ng" being the name of my app, you will need to set your own !)-

Code: Pascal  [Select][+][-]
  1. function TSett.GetDefaultNoteDir : string;
  2. begin
  3.     {$IFDEF UNIX}
  4.     Result := GetEnvironmentVariable('HOME') + '/.local/share/tomboy-ng/';
  5.     {$ENDIF}
  6.     {$IFDEF DARWIN}
  7.     Result := GetEnvironmentVariable('HOME') + '/Library/Application Support/Tomboy-ng/Notes/';
  8.     {$ENDIF}
  9.     {$IFDEF WINDOWS}
  10.     Result := GetEnvironmentVariable('APPDATA') + '\tomboy-ng\notes\';
  11.     {$ENDIF}
  12. end;

Yes, I know, the UNIX line will be activated in Cocoa too but it will be overwritten by the correct one. There is a historical reason ....

Davo
Lazarus 3, Linux (and reluctantly Win10/11, OSX Monterey)
My Project - https://github.com/tomboy-notes/tomboy-ng and my github - https://github.com/davidbannon

WayneSherman

  • Sr. Member
  • ****
  • Posts: 254
Re: File path to ${HOME}
« Reply #4 on: May 20, 2023, 04:47:34 am »
Quote from: dbannon
Yes, I know, the UNIX line will be activated in Cocoa too but it will be overwritten by the correct one.

Possibly a better way (?) using newer {$IF} and {$ELSEIF} directives:

Code: Pascal  [Select][+][-]
  1. function TSett.GetDefaultNoteDir : string;
  2. begin
  3.   {$IF DEFINED(DARWIN)}
  4.     Result := GetEnvironmentVariable('HOME') + '/Library/Application Support/Tomboy-ng/Notes/';
  5.   {$ELSEIF DEFINED(UNIX)}
  6.     Result := GetEnvironmentVariable('HOME') + '/.local/share/tomboy-ng/';
  7.   {$ELSEIF DEFINED(WINDOWS)}
  8.     Result := GetEnvironmentVariable('APPDATA') + '\tomboy-ng\notes\';
  9.   {$ENDIF}
  10. end;

Reference:
https://wiki.freepascal.org/Conditional_compilation#.24else_and_.24elseif
https://wiki.freepascal.org/$IF

colo

  • New Member
  • *
  • Posts: 48
Re: File path to ${HOME}
« Reply #5 on: May 20, 2023, 09:42:37 am »
Sorry for raising this maybe slightly off-topic remark, but since I've seen something along the lines of

Code: [Select]
GetEnvironmentVariable('HOME')

to determine a user's home directory on GNU/Linux and other UNIX-like OSes quite often, I would like to remind everyone that it's not a given this environment variable exists or is set to the proper value. Most interactive shell sessions will have it set, but not all commonly encountered (and even less so, all possible) execution environments will have it. Even getuserdir seems to get it wrong, at least per its docs, and if you call it with an empty HOME value in the env, it will return "/tmp", which is its own can of worms entirely.

Only getpwnam(3) or getpwuid(3) from the host's C library will always yield the right thing (even in the face of more exotic circumstances, like the user database not living in /etc/passwd due to nsswitch, for example), which is what you should call from any fpc code to get this right. Unfortunately, it escapes me how to do that, since the only reference I could find is to the deprecated libc stuff in https://www.freepascal.org/daily/packages/libc/index-8.html#SECTIONG - but if anyone else can chime in, I'd be eager to learn! :)

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11990
  • FPC developer.
Re: File path to ${HOME}
« Reply #6 on: May 20, 2023, 02:05:48 pm »
Maybe the "users" unit as mentioned in the libc article as alternative ?

https://wiki.freepascal.org/libc_unit

colo

  • New Member
  • *
  • Posts: 48
Re: File path to ${HOME}
« Reply #7 on: May 21, 2023, 11:34:10 am »
Yes! Excellent, this led me to the right thing :) The procedures buried in the related `pwd` module call `getpwnam` and friends, as it should be.

For demonstration purposes (I'm on GNU/Linux here, with a boring passwd/shadow-based NSS user and group database), consider this program:
Code: [Select]
program userhomedir(output);
uses sysutils, baseunix, pwd;
var
    pwrec: PPasswd;
begin
 { Sometimes wrong, don't use }
 writeLn('GetUserDir: ' + GetUserDir);

 { Correct way to determine the current user's home directory }
 pwrec := fpgetpwuid(FpGetUid);
 writeLn('FpGetPwUid: ' + pwrec^.pw_dir );
end.

The struct that contains pw_dir also carries other useful per-account data, as documented here.


Demo shell session output:
Code: [Select]
$ fpc userhomedir.pas
Free Pascal Compiler version 3.2.2 [2023/05/07] for x86_64
Copyright (c) 1993-2021 by Florian Klaempfl and others
Target OS: Linux for x86-64
Compiling userhomedir.pas
Linking userhomedir
12 lines compiled, 0.1 sec

# "happy" path
$ ./userhomedir
GetUserDir: /home/colo/
FpGetPwUid: /home/colo

# where GetUserDir gets it wrong
$ HOME= ./userhomedir
GetUserDir: /tmp/
FpGetPwUid: /home/colo


A soon as a program that uses GetUserDir ends up being run as a system service, chances are its notion of home will be off. So I would encourage anyone to use the `pwd` module instead, and if the value of HOME is to be accounted for, maybe handle this in custom code.
« Last Edit: May 21, 2023, 11:39:15 am by colo »

Raskaton

  • New Member
  • *
  • Posts: 13
Re: File path to ${HOME}
« Reply #8 on: July 03, 2023, 09:58:56 pm »
Maybe just use:
ExpandFileName https://lazarus-ccr.sourceforge.io/docs/rtl/sysutils/expandfilename.html
Code: Pascal  [Select][+][-]
  1. uses SysUtils;
  2.  
  3. ExpandFileName('.');  //app directory
  4. ExpandFileName('~/');  //home absolute path like: /home/username/  or /root/
  5. ExpandFileName('~/.local/');  // /home/username/.local
  6. ExpandFileName('~/..');  // /home/ or  /
  7.  
Never use it by myself  ;)

Upd. Test it in Linux (KDE)
Code: Pascal  [Select][+][-]
  1.   writeln('ExpandFileName(''.'') = ', ExpandFileName('.'));
  2.   writeln('ExpandFileName(''~/'') = ', ExpandFileName('~/'));
  3.   writeln('ExpandFileName(''~/.local/'') = ', ExpandFileName('~/.local/'));
  4.   writeln('ExpandFileName(''~/..'') = ', ExpandFileName('~/..'));
  5.  
Code: Pascal  [Select][+][-]
  1. ExpandFileName('.') = /home/raskaton/Projects/Lazarus-Projects/TestConsoleApp
  2. ExpandFileName('~/') = /home/raskaton/
  3. ExpandFileName('~/.local/') = /home/raskaton/.local/
  4. ExpandFileName('~/..') = /home
  5.  
wrong slashes also converted
Code: Pascal  [Select][+][-]
  1. ExpandFileName('~\..') = /home
  2. ExpandFileName('~\..\') = /home/
  3.  
« Last Edit: July 03, 2023, 10:12:37 pm by Raskaton »

 

TinyPortal © 2005-2018