multi-OS Preferences wrapper

Share your advanced PureBasic knowledge/code with the community.
User avatar
Keya
Addict
Addict
Posts: 1890
Joined: Thu Jun 04, 2015 7:10 am

multi-OS Preferences wrapper

Post by Keya »

just my wrapper of PB's Preference functions (just the string one) for easy save and read of ini-style settings. OS: Windows + Mac + Linux
CFG.PBI:

Code: Select all

Procedure.i FindLastDotInFilename(a.s)
  CompilerIf #PB_Compiler_Unicode = 1
    CompilerError "UNICODE NOT SUPPORTED - FindLastDotInFilename() function"
  CompilerEndIf
  Protected l=Len(a);    using a global variable for 'l' would speed up the procedure
  While l
    l-1
    Select PeekA(@a+1)
      Case '.':  ProcedureReturn l
      Case '/','\': ProcedureReturn 0   ;we've found the folder root before the dot, so there is no dot
    EndSelect
  Wend
  ProcedureReturn 0
EndProcedure

Procedure.s AppPath()  ;IMPORTANT: Always includes a trailing slash (\ in Windows, / in Linux&Mac)
  Static init, sVal.s
  If init = 0
    sVal = GetPathPart(ProgramFilename())
    If Right(sVal,1) <> "\" And Right(sVal,1) <> "/"
      CompilerIf #PB_Compiler_OS = #PB_OS_Windows
        sVal = sVal + "\"
      CompilerElse
        sVal = sVal + "/"
      CompilerEndIf
    EndIf
    init = 1
  EndIf    
  ProcedureReturn sVal
EndProcedure

Procedure.s AppBaseFilename()
  Static init, sVal.s
  Protected aDot.i
  If init = 0
    sVal = ProgramFilename()
    aDot = FindLastDotInFilename(sVal)
    If aDot: sVal = Left(sVal, aDot): EndIf
    init = 1
  EndIf    
  ProcedureReturn sVal
EndProcedure 

;------------------------------------------------------------------

Procedure SaveCFG(sGroup.s, sKey.s, sVal.s)
  Protected sIniFile.s = AppBaseFilename() + ".cfg"
  If OpenPreferences(sIniFile, #PB_Preference_NoSpace | #PB_Preference_GroupSeparator)
  Else
    If CreatePreferences(sIniFile, #PB_Preference_NoSpace | #PB_Preference_GroupSeparator)
    Else
      MessageRequester("Error", "Unable to open settings file " + sIniFile)
      ProcedureReturn
    EndIf
  EndIf
  PreferenceGroup(sGroup)
  WritePreferenceString (sKey, sVal)
  ClosePreferences()
EndProcedure


Procedure.s ReadCFG(sGroup.s, sKey.s)
  Protected sIniFile.s = AppBaseFilename() + ".cfg"
  Protected sResult.s
  If OpenPreferences(sIniFile)
    PreferenceGroup(sGroup)
    sResult = ReadPreferenceString(sKey, "")
    ClosePreferences()
  Else
    MessageRequester("Error", "Unable to open settings file " + sIniFile)
    ProcedureReturn    
  EndIf
  ProcedureReturn sResult
EndProcedure


Procedure.i CFGExists()
  Protected sIniFile.s = AppBaseFilename() + ".cfg"
  If FileSize(sIniFile) > 0: ProcedureReturn 1: EndIf
EndProcedure

Example:

Code: Select all

XIncludeFile("cfg.pbi")

;Save settings
SaveCFG("Settings", "My First Option", "My first value")
SaveCFG("Settings", "My Second Option", "My second value")

;Read setting
Debug("Stored value = " + ReadCFG("Settings", "My First Option"))
Debug("Stored value = " + ReadCFG("Settings", "My Second Option"))
User avatar
ts-soft
Always Here
Always Here
Posts: 5756
Joined: Thu Jun 24, 2004 2:44 pm
Location: Berlin - Germany

Re: multi-OS Preferences wrapper

Post by ts-soft »

I would not write preference in ProgramFiles Dir. You should use on Windows the APPDATA, on Linux the HOMEDIR with . (dot) in front (Hidden).

Greetings Thomas
PureBasic 5.73 | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Old bugs good, new bugs bad! Updates are evil: might fix old bugs and introduce no new ones.
Image
User avatar
Keya
Addict
Addict
Posts: 1890
Joined: Thu Jun 04, 2015 7:10 am

Re: multi-OS Preferences wrapper

Post by Keya »

Thankyou for feedback! My intention for this is a portable app, so it just writes wherever you're running the program from (c:\temp\myapp.exe creates c:\temp\myapp.cfg)
But even as a portable app should I be writing to APPDATA/HOMEDIR instead of application home path?
User avatar
Kukulkan
Addict
Addict
Posts: 1396
Joined: Mon Jun 06, 2005 2:35 pm
Location: germany
Contact:

Re: multi-OS Preferences wrapper

Post by Kukulkan »

Try checking if you are running from a portable device (USB) with read/write access. In this case, write it there, otherwise to %APPDATA%.
Little John
Addict
Addict
Posts: 4791
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Re: multi-OS Preferences wrapper

Post by Little John »

For instance CCleaner, as well as several other programs, look for a special file in the program directory (e.g. named "portable.dat").
If the file is there, it's considered a portable version of the program, and the preferences are written to the program directory, else they are written to a subdirectory of %appdata% (on Windows). I'm also doing it this way with PureBasic for years, and everything works fine.
User avatar
Keya
Addict
Addict
Posts: 1890
Joined: Thu Jun 04, 2015 7:10 am

Re: multi-OS Preferences wrapper

Post by Keya »

Heya guys just getting back to this, Ive put together some code to determine where the location for our applications preferences/ini file

First it checks to see if the preferences file exists in the same folder the app is running from ... perhaps the root of a USB drive, or C:\Program Files\Mycompany\MyApp\, or...
Anyway if it doesnt find it there, it defaults to EnvironVar("APPDATA") for Windows, and EnvironVar("HOME") for Mac + Linux.

Im not so sure at the moment because if it's running from C:\Program Files\xxx\ and the prefs.ini file is there it'll write to that, instead of C:\Documents and Settings\Application Data\xxx\
so maybe a commandline argument will solve that
(also im trying to avoid lowlevel api such to detect if the drive is portable, if possible)

So is this the right way to go??? any feedback positive or negative much appreciated! thankyou

Code: Select all

EnableExplicit

Procedure.s GetPrefsFolder(Prefs.s)  
  Protected CfgPath.s = GetPathPart(ProgramFilename())
  If FileSize(CfgPath + Prefs) = -1          ;Not found in application folder
    CompilerIf #PB_Compiler_OS = #PB_OS_Windows
      CfgPath = GetEnvironmentVariable("APPDATA") + "\MyCompany\MyAppTitle\"
    CompilerElse  ;#PB_OS_Linux and #PB_OS_MacOS
      CfgPath = GetEnvironmentVariable("HOME") + "/.MyCompany/.MyAppTitle/"       ;<- do we need the dots?
    CompilerEndIf
  EndIf
  ProcedureReturn CfgPath
EndProcedure


Debug(GetPrefsFolder("prefs.ini"))

;Typical output:
;WIN:   C:\Documents and Settings\USERNAME\Application Data\MyCompany\MyAppTitle\   (older)
;WIN:   C:\Users\USERNAME\AppData\Roaming\MyCompany\MyAppTitle\                     (newer)
;LNX:   /home/USERNAME/.MyCompany/.MyAppTitle/
;MAC:   /Users/USERNAME/.MyCompany/.MyAppTitle/
Post Reply