////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2001-2026 Alexey Kuryakin daqgroup@mail.ru under MIT license //
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// This file is part of the CRW-DAQ project by DaqGroup - component CRWLIB.   //
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Purpose:                                                                   //
// System special folders.                                                    //
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// History:                                                                   //
// 20200914 - derived from GetSpecialFolderPath utility                       //
// 20230616 - Modified for FPC (A.K.)                                         //
////////////////////////////////////////////////////////////////////////////////

unit _crw_spcfld; // Special Folders

{$I _crw_sysdef.inc}

{$I _crw_sysmode.inc}

{$WARN 5023 off : Unit "$1" not used in $2}

interface

uses
 //////////////////////////////////////////////////////
 {$I _crw_uses_first.inc} // NB: MUST BE FIRST USES !!!
 //////////////////////////////////////////////////////
 {$IFDEF WINDOWS} windirs, {$ENDIF}
 {$IFDEF UNIX} baseunix, {$ENDIF}
 sysutils, classes, process,
 _crw_alloc, _crw_str, _crw_environ, _crw_hl, _crw_proc;

 ///////////////////////////////////////////////////////////////////////////////
 // GetSpecialShellFolderPath(CSIDL,sub) returns special folder specified by the
 // given CSIDL with (optional) subdirectory (sub).
 // CSIDL (constant special item ID list) values provides system-independent way
 // to identify special folders used frequently by applications, but which may
 // not have the same name or location on any given system. Some Windows CSIDL's
 // are system specific and has no analogs in Unix, but other CSIDL's has a Unix
 // analogs so this special folders may be used in cross-platform code.
 ///////////////////////////////////////////////////////////////////////////////

function GetSpecialShellFolderPath(CSIDL:Integer; const sub:LongString=''):LongString;

{$IFDEF WINDOWS}
const
 // registry entries for special paths are kept in
 REGSTR_PATH_SPECIAL_FOLDERS = 'Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders';
{$ENDIF WINDOWS}

const // Identify special folders.
 CSIDL_FIRST                   = 0;
 /////////////////////////////// Windows defined.     // Unix mapping:
 CSIDL_DESKTOP                 = 0;                   // CSIDL_XDG_DESKTOP_DIR
 CSIDL_INTERNET                = 1;                   //
 CSIDL_PROGRAMS                = 2;                   //
 CSIDL_CONTROLS                = 3;                   //
 CSIDL_PRINTERS                = 4;                   //
 CSIDL_PERSONAL                = 5;                   // CSIDL_XDG_DOCUMENTS_DIR
 CSIDL_FAVORITES               = 6;                   //
 CSIDL_STARTUP                 = 7;                   //
 CSIDL_RECENT                  = 8;                   //
 CSIDL_SENDTO                  = 9;                   //
 CSIDL_BITBUCKET               = 10;                  //
 CSIDL_STARTMENU               = 11;                  //
 CSIDL_MYDOCUMENTS             = 12;                  // CSIDL_XDG_DOCUMENTS_DIR
 CSIDL_MYMUSIC                 = 13;                  // CSIDL_XDG_MUSIC_DIR
 CSIDL_MYVIDEO                 = 14;                  // CSIDL_XDG_VIDEOS_DIR
 CSIDL_DESKTOPDIRECTORY        = 16;                  // CSIDL_XDG_DESKTOP_DIR
 CSIDL_DRIVES                  = 17;                  //
 CSIDL_NETWORK                 = 18;                  //
 CSIDL_NETHOOD                 = 19;                  //
 CSIDL_FONTS                   = 20;                  //
 CSIDL_TEMPLATES               = 21;                  // CSIDL_XDG_TEMPLATES_DIR
 CSIDL_COMMON_STARTMENU        = 22;                  //
 CSIDL_COMMON_PROGRAMS         = 23;                  //
 CSIDL_COMMON_STARTUP          = 24;                  //
 CSIDL_COMMON_DESKTOPDIRECTORY = 25;                  //
 CSIDL_APPDATA                 = 26;                  // CSIDL_XDG_DATA_HOME
 CSIDL_PRINTHOOD               = 27;                  //
 CSIDL_LOCAL_APPDATA           = 28;                  // CSIDL_XDG_DATA_HOME
 CSIDL_ALTSTARTUP              = 29;                  //
 CSIDL_COMMON_ALTSTARTUP       = 30;                  //
 CSIDL_COMMON_FAVORITES        = 31;                  //
 CSIDL_INTERNET_CACHE          = 32;                  //
 CSIDL_COOKIES                 = 33;                  //
 CSIDL_HISTORY                 = 34;                  //
 CSIDL_COMMON_APPDATA          = 35;                  // CSIDL_XDG_DATA_DIR
 CSIDL_WINDOWS                 = 36;                  //
 CSIDL_SYSTEM                  = 37;                  //
 CSIDL_PROGRAM_FILES           = 38;                  //
 CSIDL_MYPICTURES              = 39;                  // CSIDL_XDG_PICTURES_DIR
 CSIDL_PROFILE                 = 40;                  // CSIDL_XDG_HOME
 CSIDL_SYSTEMX86               = 41;                  //
 CSIDL_PROGRAM_FILESX86        = 42;                  //
 CSIDL_PROGRAM_FILES_COMMON    = 43;                  //
 CSIDL_PROGRAM_FILES_COMMONX86 = 44;                  //
 CSIDL_COMMON_TEMPLATES        = 45;                  //
 CSIDL_COMMON_DOCUMENTS        = 46;                  // CSIDL_XDG_PUBLICSHARE_DIR
 CSIDL_COMMON_ADMINTOOLS       = 47;                  //
 CSIDL_ADMINTOOLS              = 48;                  //
 CSIDL_CONNECTIONS             = 49;                  //
 CSIDL_COMMON_MUSIC            = 53;                  //
 CSIDL_COMMON_PICTURES         = 54;                  //
 CSIDL_COMMON_VIDEO            = 55;                  //
 CSIDL_RESOURCES               = 56;                  //
 CSIDL_RESOURCES_LOCALIZED     = 57;                  //
 CSIDL_COMMON_OEM_LINKS        = 58;                  //
 CSIDL_CDBURN_AREA             = 59;                  //
 CSIDL_COMPUTERSNEARME         = 61;                  //
 /////////////////////////////// Unix XDG defined.    // Windows mapping:
 CSIDL_XDG_HOME                = 62;                  // CSIDL_PROFILE
 CSIDL_XDG_DATA_HOME           = CSIDL_XDG_HOME+1;    // CSIDL_LOCAL_APPDATA
 CSIDL_XDG_CONFIG_HOME         = CSIDL_XDG_HOME+2;    // CSIDL_LOCAL_APPDATA
 CSIDL_XDG_STATE_HOME          = CSIDL_XDG_HOME+3;    // CSIDL_LOCAL_APPDATA
 CSIDL_XDG_CACHE_HOME          = CSIDL_XDG_HOME+4;    // CSIDL_LOCAL_APPDATA
 CSIDL_XDG_DATA_DIRS           = CSIDL_XDG_HOME+5;    //
 CSIDL_XDG_CONFIG_DIRS         = CSIDL_XDG_HOME+6;    //
 CSIDL_XDG_RUNTIME_DIR         = CSIDL_XDG_HOME+7;    // CSIDL_LOCAL_APPDATA
 CSIDL_XDG_DATA_DIR            = CSIDL_XDG_HOME+8;    // CSIDL_COMMON_APPDATA
 CSIDL_XDG_CONFIG_DIR          = CSIDL_XDG_HOME+9;    // CSIDL_COMMON_APPDATA
 CSIDL_XDG_DESKTOP_DIR         = CSIDL_XDG_HOME+10;   // CSIDL_DESKTOP
 CSIDL_XDG_DOWNLOAD_DIR        = CSIDL_XDG_HOME+11;   //
 CSIDL_XDG_TEMPLATES_DIR       = CSIDL_XDG_HOME+12;   // CSIDL_TEMPLATES
 CSIDL_XDG_PUBLICSHARE_DIR     = CSIDL_XDG_HOME+13;   // CSIDL_COMMON_DOCUMENTS
 CSIDL_XDG_DOCUMENTS_DIR       = CSIDL_XDG_HOME+14;   // CSIDL_PERSONAL
 CSIDL_XDG_MUSIC_DIR           = CSIDL_XDG_HOME+15;   // CSIDL_MYMUSIC
 CSIDL_XDG_PICTURES_DIR        = CSIDL_XDG_HOME+16;   // CSIDL_MYPICTURES
 CSIDL_XDG_VIDEOS_DIR          = CSIDL_XDG_HOME+17;   // CSIDL_MYVIDEO
 ///////////////////////////////////////////////////////
 CSIDL_LAST                    = CSIDL_XDG_VIDEOS_DIR;

function CSIDL_FindByName(Name:LongString; Default:Integer=-1):Integer;
function CSIDL_GetName(CSIDL:Integer; const Prefix:LongString='CSIDL_'):LongString;
function CSIDL_FolderByName(const Name:LongString; const sub:LongString;
               Charset:Integer=-1; const Delim:LongString=''):LongString;
function CSIDL_ListAllAsText(const Prefix:LongString='CSIDL_';
               Charset:Integer=-1; SkipEmpty:Boolean=true):LongString;

{$IFDEF UNIX}
////////////////////////////////////////////////////////////////////////////////
// XDG Base Directory Specification
// https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
////////////////////////////////////////////////////////////////////////////////

 // XDG base directory with environment variable (id),
 // default value (def) and (optional) subdirirectory (sub)
 // id={XDG_DATA_HOME,XDG_CONFIG_HOME,XDG_STATE_HOME,XDG_CACHE_HOME}
function xdg_base_directory(const id,def,sub:LongString):LongString;

 // User home directory like $HOME
function XDG_HOME(const sub:LongString=''):LongString;

 // User data directory like $HOME/.local/share
function XDG_DATA_HOME(const sub:LongString=''):LongString;

 // User config directory like $HOME/.config
function XDG_CONFIG_HOME(const sub:LongString=''):LongString;

 // User state files directory like $HOME/.local/state
function XDG_STATE_HOME(const sub:LongString=''):LongString;

 // User cache files directory like $HOME/.cache
function XDG_CACHE_HOME(const sub:LongString=''):LongString;

 // Shared data search path like /usr/local/share/:/usr/share/
function XDG_DATA_DIRS(const sub:LongString=''):LongString;

 // Shared config search path like /etc/xdg
function XDG_CONFIG_DIRS(const sub:LongString=''):LongString;

 // Shared runtime directory like /run/user/1000
function XDG_RUNTIME_DIR(const sub:LongString):LongString;

 // Shared data directory like /usr/share
function XDG_DATA_DIR(const sub:LongString):LongString;

 // Shared config directory like /etc/xdg
function XDG_CONFIG_DIR(const sub:LongString):LongString;

const // List of XDG user directories names allowed in xdg-user-dir command.
 xdg_user_dir_names='DESKTOP DOWNLOAD TEMPLATES PUBLICSHARE DOCUMENTS MUSIC PICTURES VIDEOS';

 // xdg-user-dir NAME - Find an XDG user dir. NAME is one of xdg_user_dir_names.
function XDG_USER_DIR(NAME:LongString; const sub:LongString=''):LongString; overload;
function XDG_USER_DIR(CSIDL:Integer; const sub:LongString=''):LongString; overload;
{$ENDIF UNIX}

function StrToOem(const AnsiStr:LongString):LongString;
function StrToAnsi(const OemStr:LongString):LongString;

implementation

const Dict:THashList=nil;

procedure InitDictionary;
 procedure Add(CIDL:Integer; Name:LongString);
 var ikey:LongString;
 begin
  Name:=Trim(Name);
  ikey:=IntToStr(CIDL);
  Name:=UpCaseStr(Name);
  if (Pos('CSIDL_',Name)=1)
  then Delete(Name,1,Length('CSIDL_'));
  Dict.KeyedLinks[Name]:=CIDL;
  Dict.KeyedParams[ikey]:=Name;
 end;
begin
 if not Assigned(Dict) then begin
  Dict:=NewHashList(false,HashList_DefaultHasher);
  Dict.Master:=@Dict;
  Add(CSIDL_DESKTOP                 , 'CSIDL_DESKTOP');
  Add(CSIDL_INTERNET                , 'CSIDL_INTERNET');
  Add(CSIDL_PROGRAMS                , 'CSIDL_PROGRAMS');
  Add(CSIDL_CONTROLS                , 'CSIDL_CONTROLS');
  Add(CSIDL_PRINTERS                , 'CSIDL_PRINTERS');
  Add(CSIDL_PERSONAL                , 'CSIDL_PERSONAL');
  Add(CSIDL_FAVORITES               , 'CSIDL_FAVORITES');
  Add(CSIDL_STARTUP                 , 'CSIDL_STARTUP');
  Add(CSIDL_RECENT                  , 'CSIDL_RECENT');
  Add(CSIDL_SENDTO                  , 'CSIDL_SENDTO');
  Add(CSIDL_BITBUCKET               , 'CSIDL_BITBUCKET');
  Add(CSIDL_STARTMENU               , 'CSIDL_STARTMENU');
  Add(CSIDL_MYDOCUMENTS             , 'CSIDL_MYDOCUMENTS');
  Add(CSIDL_MYMUSIC                 , 'CSIDL_MYMUSIC');
  Add(CSIDL_MYVIDEO                 , 'CSIDL_MYVIDEO');
  Add(CSIDL_DESKTOPDIRECTORY        , 'CSIDL_DESKTOPDIRECTORY');
  Add(CSIDL_DRIVES                  , 'CSIDL_DRIVES');
  Add(CSIDL_NETWORK                 , 'CSIDL_NETWORK');
  Add(CSIDL_NETHOOD                 , 'CSIDL_NETHOOD');
  Add(CSIDL_FONTS                   , 'CSIDL_FONTS');
  Add(CSIDL_TEMPLATES               , 'CSIDL_TEMPLATES');
  Add(CSIDL_COMMON_STARTMENU        , 'CSIDL_COMMON_STARTMENU');
  Add(CSIDL_COMMON_PROGRAMS         , 'CSIDL_COMMON_PROGRAMS');
  Add(CSIDL_COMMON_STARTUP          , 'CSIDL_COMMON_STARTUP');
  Add(CSIDL_COMMON_DESKTOPDIRECTORY , 'CSIDL_COMMON_DESKTOPDIRECTORY');
  Add(CSIDL_APPDATA                 , 'CSIDL_APPDATA');
  Add(CSIDL_PRINTHOOD               , 'CSIDL_PRINTHOOD');
  Add(CSIDL_LOCAL_APPDATA           , 'CSIDL_LOCAL_APPDATA');
  Add(CSIDL_ALTSTARTUP              , 'CSIDL_ALTSTARTUP');
  Add(CSIDL_COMMON_ALTSTARTUP       , 'CSIDL_COMMON_ALTSTARTUP');
  Add(CSIDL_COMMON_FAVORITES        , 'CSIDL_COMMON_FAVORITES');
  Add(CSIDL_INTERNET_CACHE          , 'CSIDL_INTERNET_CACHE');
  Add(CSIDL_COOKIES                 , 'CSIDL_COOKIES');
  Add(CSIDL_HISTORY                 , 'CSIDL_HISTORY');
  Add(CSIDL_COMMON_APPDATA          , 'CSIDL_COMMON_APPDATA');
  Add(CSIDL_WINDOWS                 , 'CSIDL_WINDOWS');
  Add(CSIDL_SYSTEM                  , 'CSIDL_SYSTEM');
  Add(CSIDL_PROGRAM_FILES           , 'CSIDL_PROGRAM_FILES');
  Add(CSIDL_MYPICTURES              , 'CSIDL_MYPICTURES');
  Add(CSIDL_PROFILE                 , 'CSIDL_PROFILE');
  Add(CSIDL_SYSTEMX86               , 'CSIDL_SYSTEMX86');
  Add(CSIDL_PROGRAM_FILESX86        , 'CSIDL_PROGRAM_FILESX86');
  Add(CSIDL_PROGRAM_FILES_COMMON    , 'CSIDL_PROGRAM_FILES_COMMON');
  Add(CSIDL_PROGRAM_FILES_COMMONX86 , 'CSIDL_PROGRAM_FILES_COMMONX86');
  Add(CSIDL_COMMON_TEMPLATES        , 'CSIDL_COMMON_TEMPLATES');
  Add(CSIDL_COMMON_DOCUMENTS        , 'CSIDL_COMMON_DOCUMENTS');
  Add(CSIDL_COMMON_ADMINTOOLS       , 'CSIDL_COMMON_ADMINTOOLS');
  Add(CSIDL_ADMINTOOLS              , 'CSIDL_ADMINTOOLS');
  Add(CSIDL_CONNECTIONS             , 'CSIDL_CONNECTIONS');
  Add(CSIDL_COMMON_MUSIC            , 'CSIDL_COMMON_MUSIC');
  Add(CSIDL_COMMON_PICTURES         , 'CSIDL_COMMON_PICTURES');
  Add(CSIDL_COMMON_VIDEO            , 'CSIDL_COMMON_VIDEO');
  Add(CSIDL_RESOURCES               , 'CSIDL_RESOURCES');
  Add(CSIDL_RESOURCES_LOCALIZED     , 'CSIDL_RESOURCES_LOCALIZED');
  Add(CSIDL_COMMON_OEM_LINKS        , 'CSIDL_COMMON_OEM_LINKS');
  Add(CSIDL_CDBURN_AREA             , 'CSIDL_CDBURN_AREA');
  Add(CSIDL_COMPUTERSNEARME         , 'CSIDL_COMPUTERSNEARME');
  Add(CSIDL_XDG_HOME                , 'CSIDL_XDG_HOME');
  Add(CSIDL_XDG_DATA_HOME           , 'CSIDL_XDG_DATA_HOME');
  Add(CSIDL_XDG_CONFIG_HOME         , 'CSIDL_XDG_CONFIG_HOME');
  Add(CSIDL_XDG_STATE_HOME          , 'CSIDL_XDG_STATE_HOME');
  Add(CSIDL_XDG_CACHE_HOME          , 'CSIDL_XDG_CACHE_HOME');
  Add(CSIDL_XDG_DATA_DIRS           , 'CSIDL_XDG_DATA_DIRS');
  Add(CSIDL_XDG_CONFIG_DIRS         , 'CSIDL_XDG_CONFIG_DIRS');
  Add(CSIDL_XDG_RUNTIME_DIR         , 'CSIDL_XDG_RUNTIME_DIR');
  Add(CSIDL_XDG_DATA_DIR            , 'CSIDL_XDG_DATA_DIR');
  Add(CSIDL_XDG_CONFIG_DIR          , 'CSIDL_XDG_CONFIG_DIR');
  Add(CSIDL_XDG_DESKTOP_DIR         , 'CSIDL_XDG_DESKTOP_DIR');
  Add(CSIDL_XDG_DOWNLOAD_DIR        , 'CSIDL_XDG_DOWNLOAD_DIR');
  Add(CSIDL_XDG_TEMPLATES_DIR       , 'CSIDL_XDG_TEMPLATES_DIR');
  Add(CSIDL_XDG_PUBLICSHARE_DIR     , 'CSIDL_XDG_PUBLICSHARE_DIR');
  Add(CSIDL_XDG_DOCUMENTS_DIR       , 'CSIDL_XDG_DOCUMENTS_DIR');
  Add(CSIDL_XDG_MUSIC_DIR           , 'CSIDL_XDG_MUSIC_DIR');
  Add(CSIDL_XDG_PICTURES_DIR        , 'CSIDL_XDG_PICTURES_DIR');
  Add(CSIDL_XDG_VIDEOS_DIR          , 'CSIDL_XDG_VIDEOS_DIR');
 end;
end;

procedure FreeDictionary;
begin
 Kill(Dict);
end;

function CSIDL_GetName(CSIDL:Integer; const Prefix:LongString='CSIDL_'):LongString;
var ikey:LongString;
begin
 Result:='';
 ikey:=IntToStr(CSIDL);
 if Dict.IndexOf(ikey)>=0
 then Result:=Dict.KeyedParams[ikey];
 if (Result<>'') and (Prefix<>'') then Result:=Prefix+Result;
end;

function CSIDL_FindByName(Name:LongString; Default:Integer=-1):Integer;
var BaseName:LongString;
begin
 Result:=Default;
 if HasChars(Name,[' ']) then Name:=StringReplace(Name,' ','',[rfReplaceAll]);
 if (Name<>'') then begin
  if (Pos('CSIDL_',Name)=1)
  then Delete(Name,1,Length('CSIDL_')); BaseName:=Name;
  if (Dict.IndexOf(Name)>=0) then begin Result:=Dict.KeyedLinks[Name]; Exit; end;
  Name:='XDG_'+BaseName;
  if (Dict.IndexOf(Name)>=0) then begin Result:=Dict.KeyedLinks[Name]; Exit; end;
  Name:='XDG_'+BaseName+'_HOME';
  if (Dict.IndexOf(Name)>=0) then begin Result:=Dict.KeyedLinks[Name]; Exit; end;
  Name:='XDG_'+BaseName+'_DIR';
  if (Dict.IndexOf(Name)>=0) then begin Result:=Dict.KeyedLinks[Name]; Exit; end;
  Name:='XDG_'+BaseName+'_DIRS';
  if (Dict.IndexOf(Name)>=0) then begin Result:=Dict.KeyedLinks[Name]; Exit; end;
 end;
end;

function ValidateCSIDL(var CSIDL:Integer):Boolean;
begin
 Result:=true;
 case CSIDL of
  CSIDL_DESKTOP                 : if IsUnix then CSIDL:=CSIDL_XDG_DESKTOP_DIR;
  CSIDL_INTERNET                : ;
  CSIDL_PROGRAMS                : ;
  CSIDL_CONTROLS                : ;
  CSIDL_PRINTERS                : ;
  CSIDL_PERSONAL                : if IsUnix then CSIDL:=CSIDL_XDG_DOCUMENTS_DIR;
  CSIDL_FAVORITES               : ;
  CSIDL_STARTUP                 : ;
  CSIDL_RECENT                  : ;
  CSIDL_SENDTO                  : ;
  CSIDL_BITBUCKET               : ;
  CSIDL_STARTMENU               : ;
  CSIDL_MYDOCUMENTS             : if IsUnix then CSIDL:=CSIDL_XDG_DOCUMENTS_DIR;
  CSIDL_MYMUSIC                 : if IsUnix then CSIDL:=CSIDL_XDG_MUSIC_DIR;
  CSIDL_MYVIDEO                 : if IsUnix then CSIDL:=CSIDL_XDG_VIDEOS_DIR;
  CSIDL_DESKTOPDIRECTORY        : if IsUnix then CSIDL:=CSIDL_XDG_DESKTOP_DIR;
  CSIDL_DRIVES                  : ;
  CSIDL_NETWORK                 : ;
  CSIDL_NETHOOD                 : ;
  CSIDL_FONTS                   : ;
  CSIDL_TEMPLATES               : if IsUnix then CSIDL:=CSIDL_XDG_TEMPLATES_DIR;
  CSIDL_COMMON_STARTMENU        : ;
  CSIDL_COMMON_PROGRAMS         : ;
  CSIDL_COMMON_STARTUP          : ;
  CSIDL_COMMON_DESKTOPDIRECTORY : ;
  CSIDL_APPDATA                 : if IsUnix then CSIDL:=CSIDL_XDG_DATA_HOME;
  CSIDL_PRINTHOOD               : ;
  CSIDL_LOCAL_APPDATA           : if IsUnix then CSIDL:=CSIDL_XDG_DATA_HOME;
  CSIDL_ALTSTARTUP              : ;
  CSIDL_COMMON_ALTSTARTUP       : ;
  CSIDL_COMMON_FAVORITES        : ;
  CSIDL_INTERNET_CACHE          : ;
  CSIDL_COOKIES                 : ;
  CSIDL_HISTORY                 : ;
  CSIDL_COMMON_APPDATA          : if IsUnix then CSIDL:=CSIDL_XDG_DATA_DIR;
  CSIDL_WINDOWS                 : ;
  CSIDL_SYSTEM                  : ;
  CSIDL_PROGRAM_FILES           : ;
  CSIDL_MYPICTURES              : if IsUnix then CSIDL:=CSIDL_XDG_PICTURES_DIR;
  CSIDL_PROFILE                 : if IsUnix then CSIDL:=CSIDL_XDG_HOME;
  CSIDL_SYSTEMX86               : ;
  CSIDL_PROGRAM_FILESX86        : ;
  CSIDL_PROGRAM_FILES_COMMON    : ;
  CSIDL_PROGRAM_FILES_COMMONX86 : ;
  CSIDL_COMMON_TEMPLATES        : ;
  CSIDL_COMMON_DOCUMENTS        : ;
  CSIDL_COMMON_ADMINTOOLS       : ;
  CSIDL_ADMINTOOLS              : ;
  CSIDL_CONNECTIONS             : ;
  CSIDL_COMMON_MUSIC            : ;
  CSIDL_COMMON_PICTURES         : ;
  CSIDL_COMMON_VIDEO            : ;
  CSIDL_RESOURCES               : ;
  CSIDL_RESOURCES_LOCALIZED     : ;
  CSIDL_COMMON_OEM_LINKS        : ;
  CSIDL_CDBURN_AREA             : ;
  CSIDL_COMPUTERSNEARME         : ;
  /////////////////////////////// XDG specific (Unix)
  CSIDL_XDG_HOME                : if IsWindows then CSIDL:=CSIDL_PROFILE;
  CSIDL_XDG_DATA_HOME           : if IsWindows then CSIDL:=CSIDL_LOCAL_APPDATA;
  CSIDL_XDG_CONFIG_HOME         : if IsWindows then CSIDL:=CSIDL_LOCAL_APPDATA;
  CSIDL_XDG_STATE_HOME          : if IsWindows then CSIDL:=CSIDL_LOCAL_APPDATA;
  CSIDL_XDG_CACHE_HOME          : if IsWindows then CSIDL:=CSIDL_LOCAL_APPDATA;
  CSIDL_XDG_DATA_DIRS           : ;
  CSIDL_XDG_CONFIG_DIRS         : ;
  CSIDL_XDG_RUNTIME_DIR         : if IsWindows then CSIDL:=CSIDL_LOCAL_APPDATA;
  CSIDL_XDG_DATA_DIR            : if IsWindows then CSIDL:=CSIDL_COMMON_APPDATA;
  CSIDL_XDG_CONFIG_DIR          : if IsWindows then CSIDL:=CSIDL_COMMON_APPDATA;
  CSIDL_XDG_DESKTOP_DIR         : if IsWindows then CSIDL:=CSIDL_DESKTOP;
  CSIDL_XDG_DOWNLOAD_DIR        : ;
  CSIDL_XDG_TEMPLATES_DIR       : if IsWindows then CSIDL:=CSIDL_TEMPLATES;
  CSIDL_XDG_PUBLICSHARE_DIR     : if IsWindows then CSIDL:=CSIDL_COMMON_DOCUMENTS;
  CSIDL_XDG_DOCUMENTS_DIR       : if IsWindows then CSIDL:=CSIDL_PERSONAL;
  CSIDL_XDG_MUSIC_DIR           : if IsWindows then CSIDL:=CSIDL_MYMUSIC;
  CSIDL_XDG_PICTURES_DIR        : if IsWindows then CSIDL:=CSIDL_MYPICTURES;
  CSIDL_XDG_VIDEOS_DIR          : if IsWindows then CSIDL:=CSIDL_MYVIDEO;
  else Result:=false;
 end;
end;

procedure AddSub(var S:LongString; const sub:LongString);
begin
 if (S<>'') and IsNonEmptyStr(sub) then S:=AddPathDelim(S)+Trim(sub);
end;

{$IFDEF UNIX}
////////////////////////////////////////////////////////////////////////////////
// XDG Base Directory Specification
// https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
////////////////////////////////////////////////////////////////////////////////
function xdg_base_directory(const id,def,sub:LongString):LongString;
var i:Integer; pattern,item:LongString;
begin
 Result:=''; pattern:='';
 if (id<>'') then pattern:=Trim(GetEnv(id));
 if (pattern='') and (def<>'') then pattern:=Trim(ExpEnv(def));
 for i:=1 to WordCount(pattern,[PathSep]) do begin
  item:=Trim(ExtractWord(i,pattern,[PathSep]));
  AddSub(item,sub);
  if (Result='')
  then Result:=item
  else Result:=Result+PathSep+item;
 end;
end;

function XDG_HOME(const sub:LongString=''):LongString;
begin
 Result:=xdg_base_directory('HOME','$HOME',sub);
end;

function XDG_DATA_HOME(const sub:LongString=''):LongString;
begin
 Result:=xdg_base_directory('XDG_DATA_HOME','$HOME/.local/share',sub);
end;

function XDG_CONFIG_HOME(const sub:LongString=''):LongString;
begin
 Result:=xdg_base_directory('XDG_CONFIG_HOME','$HOME/.config',sub);
end;

function XDG_STATE_HOME(const sub:LongString=''):LongString;
begin
 Result:=xdg_base_directory('XDG_STATE_HOME','$HOME/.local/state',sub);
end;

function XDG_CACHE_HOME(const sub:LongString=''):LongString;
begin
 Result:=xdg_base_directory('XDG_CACHE_HOME','$HOME/.cache',sub);
end;

function XDG_DATA_DIRS(const sub:LongString=''):LongString;
begin
 Result:=xdg_base_directory('XDG_DATA_DIRS','/usr/local/share:/usr/share',sub);
end;

function XDG_CONFIG_DIRS(const sub:LongString=''):LongString;
begin
 Result:=xdg_base_directory('XDG_CONFIG_DIRS','/etc/xdg',sub);
end;

function XDG_RUNTIME_DIR(const sub:LongString):LongString;
begin
 Result:=xdg_base_directory('XDG_RUNTIME_DIR','/run/user/'+IntToStr(FpGetuid),sub);
end;

function XDG_DATA_DIR(const sub:LongString):LongString;
begin
 Result:=xdg_base_directory('','/usr/share',sub);
end;

function XDG_CONFIG_DIR(const sub:LongString):LongString;
begin
 Result:=xdg_base_directory('','/etc/xdg',sub);
end;

////////////////////////////////////////////////////////////////////////////////
// $ cat ${XDG_CONFIG_HOME:-~/.config}/user-dirs.dirs
// XDG_DESKTOP_DIR="$HOME/Desktop"
// XDG_DOWNLOAD_DIR="$HOME/Загрузки"
// XDG_TEMPLATES_DIR="$HOME/Шаблоны"
// XDG_PUBLICSHARE_DIR="$HOME/Общедоступные"
// XDG_DOCUMENTS_DIR="$HOME/Документы"
// XDG_MUSIC_DIR="$HOME/Музыка"
// XDG_PICTURES_DIR="$HOME/Изображения"
// XDG_VIDEOS_DIR="$HOME/Видео"
////////////////////////////////////////////////////////////////////////////////
function read_xdg_user_dirs:Integer;
var Lines:TStringList; FName,s,id,sn,sv:LongString; i,p:Integer;
begin
 Result:=0;
 FName:=XDG_CONFIG_HOME('user-dirs.dirs');
 try
  Lines:=TStringList.Create;
  try
   if FileExists(Fname) then begin
    Lines.Text:=ReadTextLinesFromFile(FName);
    for i:=0 to Lines.Count-1 do begin
     s:=Lines[i]; p:=Pos('#',s);
     if (p>0) then s:=Copy(s,1,p-1);
     s:=Trim(s); if (s='') then continue;
     p:=Pos('=',s); if (p<2) then continue;
     sn:=Trim(Copy(s,1,p-1)); if (sn='') then continue;
     id:=sn; if SameText(LeftStr(id,4),'XDG_') then Delete(id,1,4);
     if SameText(RightStr(id,4),'_DIR') then id:=Copy(id,1,Length(id)-4);
     if (WordIndex(id,xdg_user_dir_names,JustSpaces)=0) then continue;
     sv:=Trim(Copy(s,p+1,Length(s)-p)); if (sv='') then continue;
     if (StrFetch(sv,1)=QuoteMark) then sv:=AnsiDequotedStr(sv,QuoteMark);
     if (StrFetch(sv,1)=Apostrophe) then sv:=AnsiDequotedStr(sv,Apostrophe);
     sv:=ExpEnv(sv); if (sv='') then continue;
     if SetEnv(sn,sv) then Inc(Result);
    end;
   end;
   if (Result=0) then
   for i:=1 to WordCount(xdg_user_dir_names,JustSpaces) do begin
    id:=UpCaseStr(ExtractWord(i,xdg_user_dir_names,JustSpaces));
    if RunCommandInDirIdle('','xdg-user-dir '+id,sv,s) then sv:=Trim(sv) else sv:='';
    if (sv='') then continue; sn:='XDG_'+id+'_DIR';
    if SetEnv(sn,sv) then Inc(Result);
   end;
  finally
   Lines.Free;
  end;
 except
  on E:Exception do BugReport(E,nil,'read_xdg_user_dirs');
 end;
end;

function XDG_USER_DIR(NAME:LongString; const sub:LongString=''):LongString;
var ID:LongString;
begin
 Result:='';
 if NAME='' then Exit;
 NAME:=UpCaseStr(NAME);
 ID:='XDG_'+NAME+'_DIR';
 Result:=Trim(GetEnv(ID));
 if (Result='') then
 if (read_xdg_user_dirs>0)
 then Result:=Trim(GetEnv(ID));
 AddSub(Result,sub);
end;

function XDG_USER_DIR(CSIDL:Integer; const sub:LongString=''):LongString;
begin
 Result:='';
 case CSIDL of
  CSIDL_FONTS:               begin Result:=XDG_DATA_DIR('fonts'); AddSub(Result,sub); end;
  CSIDL_XDG_HOME:            Result:=XDG_HOME(Trim(sub));
  CSIDL_XDG_DATA_HOME:       Result:=XDG_DATA_HOME(Trim(sub));
  CSIDL_XDG_CONFIG_HOME:     Result:=XDG_CONFIG_HOME(Trim(sub));
  CSIDL_XDG_STATE_HOME:      Result:=XDG_STATE_HOME(Trim(sub));
  CSIDL_XDG_CACHE_HOME:      Result:=XDG_CACHE_HOME(Trim(sub));
  CSIDL_XDG_DATA_DIRS:       Result:=XDG_DATA_DIRS(Trim(sub));
  CSIDL_XDG_CONFIG_DIRS:     Result:=XDG_CONFIG_DIRS(Trim(sub));
  CSIDL_XDG_RUNTIME_DIR:     Result:=XDG_RUNTIME_DIR(Trim(sub));
  CSIDL_XDG_DATA_DIR:        Result:=XDG_DATA_DIR(Trim(sub));
  CSIDL_XDG_CONFIG_DIR:      Result:=XDG_CONFIG_DIR(Trim(sub));
  CSIDL_XDG_DESKTOP_DIR:     Result:=XDG_USER_DIR('DESKTOP',Trim(sub));
  CSIDL_XDG_DOWNLOAD_DIR:    Result:=XDG_USER_DIR('DOWNLOAD',Trim(sub));
  CSIDL_XDG_TEMPLATES_DIR:   Result:=XDG_USER_DIR('TEMPLATES',Trim(sub));
  CSIDL_XDG_PUBLICSHARE_DIR: Result:=XDG_USER_DIR('PUBLICSHARE',Trim(sub));
  CSIDL_XDG_DOCUMENTS_DIR:   Result:=XDG_USER_DIR('DOCUMENTS',Trim(sub));
  CSIDL_XDG_MUSIC_DIR:       Result:=XDG_USER_DIR('MUSIC',Trim(sub));
  CSIDL_XDG_PICTURES_DIR:    Result:=XDG_USER_DIR('PICTURES',Trim(sub));
  CSIDL_XDG_VIDEOS_DIR:      Result:=XDG_USER_DIR('VIDEOS',Trim(sub));
 end;
end;
{$ENDIF UNIX}

{$IFDEF WINDOWS}
function GetSpecialShellFolderPath(CSIDL:Integer; const sub:LongString=''):LongString;
begin
 if ValidateCSIDL(CSIDL)
 then Result:=GetWindowsSpecialDir(CSIDL)
 else Result:='';
 AddSub(Result,sub);
end;
{$ENDIF WINDOWS}

{$IFDEF UNIX}
function GetSpecialShellFolderPath(CSIDL:Integer; const sub:LongString=''):LongString;
begin
 if ValidateCSIDL(CSIDL)
 then Result:=XDG_USER_DIR(CSIDL,sub)
 else Result:='';
end;
{$ENDIF UNIX}

function StrToOem(const AnsiStr:LongString):LongString;
begin
 Result:=AnsiStr;
 {$IFDEF WINDOWS}
 if (Result<>'') then UniqueString(Result);
 if (Result<>'') then CharToOem(PChar(AnsiStr),PChar(Result));
 {$ENDIF WINDOWS}
end;

function StrToAnsi(const OemStr:LongString):LongString;
begin
 Result:=OemStr;
 {$IFDEF WINDOWS}
 if (Result<>'') then UniqueString(Result);
 if (Result<>'') then OemToChar(PChar(OemStr),PChar(Result));
 {$ENDIF WINDOWS}
end;

function CSIDL_ListAllAsText(const Prefix:LongString='CSIDL_';
         Charset:Integer=-1; SkipEmpty:Boolean=true):LongString;
var CSIDL:Integer; Item,Folder:LongString;
begin
 Result:='';
 for CSIDL:=CSIDL_FIRST to CSIDL_LAST do begin
  Item:=CSIDL_GetName(CSIDL,Prefix); if Length(Item)=0 then Continue;
  Folder:=GetSpecialShellFolderPath(CSIDL);
  if (Folder='') and SkipEmpty then Continue;
  Result:=Result+Item+'='+Folder+EOL;
 end;
 {$IFDEF WINDOWS}
 if (Charset=-1) then Charset:=DEFAULT_CHARSET;
 if (Charset=OEM_CHARSET) and (Length(Result)>0) then Result:=StrToOem(Result);
 if (Charset=ANSI_CHARSET) and (Length(Result)>0) then Result:=StrToAnsi(Result);
 {$ENDIF WINDOWS}
end;

function CSIDL_FolderByName(const Name:LongString; const sub:LongString;
               Charset:Integer=-1; const Delim:LongString=''):LongString;
var CSIDL:Integer;
begin
 Result:='';
 if Name<>'' then begin
  CSIDL:=CSIDL_FindByName(Name);
  if (CSIDL>=0) then Result:=GetSpecialShellFolderPath(CSIDL);
  if (Result<>'') and (Delim<>'') then Result:=Result+Delim;
 end;
 {$IFDEF WINDOWS}
 if (Charset=-1) then Charset:=DEFAULT_CHARSET;
 if (Charset=OEM_CHARSET) and (Length(Result)>0) then Result:=StrToOem(Result);
 if (Charset=ANSI_CHARSET) and (Length(Result)>0) then Result:=StrToAnsi(Result);
 {$ENDIF WINDOWS}
end;

////////////////////////////////////////////////////////////////////////////////
// Sample for Windows10/x32:
////////////////////////////////////////////////////////////////////////////////
// CSIDL_DESKTOP=C:\Users\main\Desktop
// CSIDL_PROGRAMS=C:\Users\main\AppData\Roaming\Microsoft\Windows\Start Menu\Programs
// CSIDL_PERSONAL=C:\Users\main\Documents
// CSIDL_FAVORITES=C:\Users\main\Favorites
// CSIDL_STARTUP=C:\Users\main\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup
// CSIDL_RECENT=C:\Users\main\AppData\Roaming\Microsoft\Windows\Recent
// CSIDL_SENDTO=C:\Users\main\AppData\Roaming\Microsoft\Windows\SendTo
// CSIDL_STARTMENU=C:\Users\main\AppData\Roaming\Microsoft\Windows\Start Menu
// CSIDL_MYMUSIC=C:\Users\main\Music
// CSIDL_MYVIDEO=C:\Users\main\Videos
// CSIDL_DESKTOPDIRECTORY=C:\Users\main\Desktop
// CSIDL_NETHOOD=C:\Users\main\AppData\Roaming\Microsoft\Windows\Network Shortcuts
// CSIDL_FONTS=C:\Windows\Fonts
// CSIDL_TEMPLATES=C:\Users\main\AppData\Roaming\Microsoft\Windows\Templates
// CSIDL_COMMON_STARTMENU=C:\ProgramData\Microsoft\Windows\Start Menu
// CSIDL_COMMON_PROGRAMS=C:\ProgramData\Microsoft\Windows\Start Menu\Programs
// CSIDL_COMMON_STARTUP=C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup
// CSIDL_COMMON_DESKTOPDIRECTORY=C:\Users\Public\Desktop
// CSIDL_APPDATA=C:\Users\main\AppData\Roaming
// CSIDL_PRINTHOOD=C:\Users\main\AppData\Roaming\Microsoft\Windows\Printer Shortcuts
// CSIDL_LOCAL_APPDATA=C:\Users\main\AppData\Local
// CSIDL_ALTSTARTUP=C:\Users\main\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup
// CSIDL_COMMON_ALTSTARTUP=C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup
// CSIDL_COMMON_FAVORITES=C:\Users\main\Favorites
// CSIDL_INTERNET_CACHE=C:\Users\main\AppData\Local\Microsoft\Windows\INetCache
// CSIDL_COOKIES=C:\Users\main\AppData\Local\Microsoft\Windows\INetCookies
// CSIDL_HISTORY=C:\Users\main\AppData\Local\Microsoft\Windows\History
// CSIDL_COMMON_APPDATA=C:\ProgramData
// CSIDL_WINDOWS=C:\Windows
// CSIDL_SYSTEM=C:\Windows\system32
// CSIDL_PROGRAM_FILES=C:\Program Files
// CSIDL_MYPICTURES=C:\Users\main\Pictures
// CSIDL_PROFILE=C:\Users\main
// CSIDL_SYSTEMX86=C:\Windows\system32
// CSIDL_PROGRAM_FILESX86=C:\Program Files
// CSIDL_PROGRAM_FILES_COMMON=C:\Program Files\Common Files
// CSIDL_PROGRAM_FILES_COMMONX86=C:\Program Files\Common Files
// CSIDL_COMMON_TEMPLATES=C:\ProgramData\Microsoft\Windows\Templates
// CSIDL_COMMON_DOCUMENTS=C:\Users\Public\Documents
// CSIDL_COMMON_ADMINTOOLS=C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Administrative Tools
// CSIDL_ADMINTOOLS=C:\Users\main\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Administrative Tools
// CSIDL_COMMON_MUSIC=C:\Users\Public\Music
// CSIDL_COMMON_PICTURES=C:\Users\Public\Pictures
// CSIDL_COMMON_VIDEO=C:\Users\Public\Videos
// CSIDL_RESOURCES=C:\Windows\resources
// CSIDL_CDBURN_AREA=C:\Users\main\AppData\Local\Microsoft\Windows\Burn\Burn
////////////////////////////////////////////////////////////////////////////////
// Sample for WinXp/x32:
////////////////////////////////////////////////////////////////////////////////
// CSIDL_DESKTOP=C:\Documents and Settings\main\Рабочий стол
// CSIDL_PROGRAMS=C:\Documents and Settings\main\Главное меню\Программы
// CSIDL_PERSONAL=C:\Documents and Settings\main\Мои документы
// CSIDL_FAVORITES=C:\Documents and Settings\main\Избранное
// CSIDL_STARTUP=C:\Documents and Settings\main\Главное меню\Программы\Автозагрузка
// CSIDL_RECENT=C:\Documents and Settings\main\Recent
// CSIDL_SENDTO=C:\Documents and Settings\main\SendTo
// CSIDL_STARTMENU=C:\Documents and Settings\main\Главное меню
// CSIDL_MYMUSIC=C:\Documents and Settings\main\Мои документы\Моя музыка
// CSIDL_MYVIDEO=C:\Documents and Settings\main\Мои документы\Мои видеозаписи
// CSIDL_DESKTOPDIRECTORY=C:\Documents and Settings\main\Рабочий стол
// CSIDL_NETHOOD=C:\Documents and Settings\main\NetHood
// CSIDL_FONTS=C:\WINDOWS\Fonts
// CSIDL_TEMPLATES=C:\Documents and Settings\main\Шаблоны
// CSIDL_COMMON_STARTMENU=C:\Documents and Settings\All Users\Главное меню
// CSIDL_COMMON_PROGRAMS=C:\Documents and Settings\All Users\Главное меню\Программы
// CSIDL_COMMON_STARTUP=C:\Documents and Settings\All Users\Главное меню\Программы\Автозагрузка
// CSIDL_COMMON_DESKTOPDIRECTORY=C:\Documents and Settings\All Users\Рабочий стол
// CSIDL_APPDATA=C:\Documents and Settings\main\Application Data
// CSIDL_PRINTHOOD=C:\Documents and Settings\main\PrintHood
// CSIDL_LOCAL_APPDATA=C:\Documents and Settings\main\Local Settings\Application Data
// CSIDL_COMMON_FAVORITES=C:\Documents and Settings\All Users\Избранное
// CSIDL_INTERNET_CACHE=C:\Documents and Settings\main\Local Settings\Temporary Internet Files
// CSIDL_COOKIES=C:\Documents and Settings\main\Cookies
// CSIDL_HISTORY=C:\Documents and Settings\main\Local Settings\History
// CSIDL_COMMON_APPDATA=C:\Documents and Settings\All Users\Application Data
// CSIDL_WINDOWS=C:\WINDOWS
// CSIDL_SYSTEM=C:\WINDOWS\system32
// CSIDL_PROGRAM_FILES=C:\Program Files
// CSIDL_MYPICTURES=C:\Documents and Settings\main\Мои документы\Мои рисунки
// CSIDL_PROFILE=C:\Documents and Settings\main
// CSIDL_SYSTEMX86=C:\WINDOWS\system32
// CSIDL_PROGRAM_FILES_COMMON=C:\Program Files\Common Files
// CSIDL_COMMON_TEMPLATES=C:\Documents and Settings\All Users\Шаблоны
// CSIDL_COMMON_DOCUMENTS=C:\Documents and Settings\All Users\Документы
// CSIDL_COMMON_ADMINTOOLS=C:\Documents and Settings\All Users\Главное меню\Программы\Администрирование
// CSIDL_ADMINTOOLS=C:\Documents and Settings\main\Главное меню\Программы\Администрирование
// CSIDL_COMMON_MUSIC=C:\Documents and Settings\All Users\Документы\Моя музыка
// CSIDL_COMMON_PICTURES=C:\Documents and Settings\All Users\Документы\Мои рисунки
// CSIDL_COMMON_VIDEO=C:\Documents and Settings\All Users\Документы\Мои видеозаписи
// CSIDL_RESOURCES=C:\WINDOWS\resources
// CSIDL_RESOURCES_LOCALIZED=C:\WINDOWS\resources\0419
// CSIDL_CDBURN_AREA=C:\Documents and Settings\main\Local Settings\Application Data\Microsoft\CD Burning
////////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////
// Unit initialization and finalization
///////////////////////////////////////

procedure Init_crw_spcfld;
begin
 InitDictionary;
end;

procedure Free_crw_spcfld;
begin
 FreeDictionary;
end;

initialization

 Init_crw_spcfld;

finalization

 Free_crw_spcfld;

end.

//////////////
// END OF FILE
//////////////

