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

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

////////////////////////////////////////////////////////////////////////////////
// Purpose:                                                                   //
// getspecialfolderpath returns Shell special folders path.                   //
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// History:                                                                   //
// 2016xxxx - Created by A.K.                                                 //
// 20230617 - Modified for FPC (A.K.)                                         //
////////////////////////////////////////////////////////////////////////////////

program getspecialfolderpath;

{$I _crw_sysdef.inc}

{$I _crw_sysmode.inc}

{$IFNDEF FPC}{$APPTYPE CONSOLE}{$ENDIF}

{$WARN 5028 off : Local $1 "$2" is not used}

{$R getspecialfolderpath.res}

uses
 //////////////////////////////////////////////////////
 {$I _crw_uses_first.inc} // NB: MUST BE FIRST USES !!!
 //////////////////////////////////////////////////////
 sysutils, classes,
 _crw_alloc, _crw_fio, _crw_spcfld;

const
 stdin                     = 0;                // Identify stdin
 stdout                    = 1;                // Identify stdout
 stderr                    = 2;                // Identify stderr
 NoPrefix     : Boolean    = False;            // No CSIDL_ prefix
 SkipEmpty    : Boolean    = True;             // Do not list empty
 ErrorsWrite  : Integer    = 0;                // Count of errors file write
 ErrorsFails  : Integer    = 0;                // Count of errors failure
 ErrorsTotal  : Integer    = 0;                // Count of errors total
 ecSuccess                 = 0;                // Exit code Success
 ecFailure                 = 1;                // Exit code Failure
 {$IFNDEF WINDOWS}
 DEFAULT_CHARSET           = 0;                // Simulate WinApi
 OEM_CHARSET               = 2;                // Simulate WinApi
 {$ENDIF WINDOWS}
 Charset      : Integer    = OEM_CHARSET;      // DEFAULT/ANSI/OEM

procedure FixError(var Errors:Integer);
begin
 inc(Errors);
 inc(ErrorsTotal);
end;

procedure Print(n:Integer; const S:LongString);
var h:THandle;
begin
 if Length(S)>0 then
 try
  h:=0;
  case n of
   stdout: h:=GetStdHandle(STD_OUTPUT_HANDLE);
   stderr: h:=GetStdHandle(STD_ERROR_HANDLE);
  end;
  if (h<>0) then if FileWrite(h,PChar(S)^,Length(S))<0  then FixError(ErrorsWrite);
 except
  FixError(ErrorsFails);
 end;
end;

procedure ShowHelp;
const sAbout =
         'getspecialfolderpath version 1.0'+EOL
        +'Copyright (c) 2016-2023 Alexey Kuryakin daqgroup@mail.ru'+EOL
        +'Get path of special folders by constant string ID (CSIDL).'+EOL
        +'Usage: getspecialfolderpath [-opt] [args] ...'+EOL
        +' [-opt] is option, of -short or --long style'+EOL
        +' [args] is arguments, list of CSIDL of special folders to get'+EOL
        +'        CSIDL (constant special ID list) is a number or name,'+EOL
        +'        which uses to identify special folders in the system.'+EOL
        +'        For example: FONTS or CSIDL_FONTS means FONTS folder.'+EOL
        +' Next options and arguments is allowed:'+EOL
        +' getspecialfolderpath -h              - Same as --help.'+EOL
        +' getspecialfolderpath --help          - Show help and exit.'+EOL
        +' getspecialfolderpath -a              - Same as --ansi.'+EOL
        +' getspecialfolderpath --ansi          - Output as ANSI text.'+EOL
        +' getspecialfolderpath -o              - Same as --oem.'+EOL
        +' getspecialfolderpath --oem           - Output as OEM text, by default.'+EOL
        +' getspecialfolderpath -l              - Same as --list.'+EOL
        +' getspecialfolderpath --list          - List all known CSIDL and folder values.'+EOL
        +' getspecialfolderpath csidl           - get special folder by CSIDL identifier.'+EOL
        +' getspecialfolderpath                 - without args, list all defined folders.'+EOL
        +'Exit code:'+EOL
        +' 0 - Success, special folder(s) found.'+EOL
        +' 1 - Failure, some CSIDL(s) is unknown or folder(s) not defined.'+EOL
        +'Examples:'+EOL
        +' getspecialfolderpath                 - List all defined special folders.'+EOL
        +' getspecialfolderpath -l              - List all known CSIDL and folders.'+EOL
        +' getspecialfolderpath -h              - Show help.'+EOL
        +' getspecialfolderpath 0               - Get current user DESKTOP folder by CSIDL value.'+EOL
        +' getspecialfolderpath fonts           - Get system FONTS folder, like c:\Windows\Fonts.'+EOL
        +' getspecialfolderpath CSIDL_FONTS     - Get system FONTS folder, like c:\Windows\Fonts.'+EOL
        +' getspecialfolderpath -a DOCUMENTS    - Get current user DOCUMENTS folder as ANSI text.'+EOL
        +' etc'+EOL;
begin
 Print(stderr,sAbout);
end;

function CheckOpt(const ShortOpt,LongOpt,Param:LongString; var Arg:LongString):Boolean;
begin
 Result:=False; Arg:='';
 if (ShortOpt<>'') and (Pos(ShortOpt,Param)=1) then begin
  Arg:=Param; Delete(Arg,1,Length(ShortOpt));
  Result:=True;
  Exit;
 end;
 if (LongOpt<>'') and (Pos(LongOpt,Param)=1) then begin
  Arg:=Param; Delete(Arg,1,Length(LongOpt));
  Result:=True;
  Exit;
 end;
end;

function Prefix:LongString;
begin
 if NoPrefix then Result:='' else Result:='CSIDL_';
end;

var
 NArgs:Integer=0;
 Param:Integer=1;
 ParamArg:LongString='';
 Folder:LongString='';

begin
 try
  Param:=1;
  while (Param<=ParamCount) do begin
   if CheckOpt('-o','--oem',ParamStr(Param),ParamArg) then Charset:=OEM_CHARSET else
   if CheckOpt('-a','--ansi',ParamStr(Param),ParamArg) then Charset:=DEFAULT_CHARSET else
   if CheckOpt('-l','--list',ParamStr(Param),ParamArg) then begin
    Print(stdout,CSIDL_ListAllAsText(Prefix,Charset,False));
    inc(NArgs);
   end else
   if CheckOpt('-h','--help',ParamStr(Param),ParamArg) then begin
    ShowHelp;
    Exit;
   end else
   if Length(ParamStr(Param))>0 then begin
    if CSIDL_FindByName(ParamStr(Param))=-1 then begin
     Print(stderr,'Error: unknown CSIDL '+ParamStr(Param)+EOL);
     FixError(ErrorsFails);
    end else begin
     Folder:=CSIDL_FolderByName(ParamStr(Param),'',Charset);
     if Length(Folder)>0 then Print(stdout,Folder+EOL) else begin
      Print(stderr,'Error: undefined CSIDL '+ParamStr(Param)+EOL);
      FixError(ErrorsFails);
     end;
    end;
    inc(NArgs);
   end;
   inc(Param);
  end;
  if (NArgs=0) then Print(stdout,CSIDL_ListAllAsText(Prefix,Charset,SkipEmpty));
 except
  on E:Exception do begin
   Print(stderr,'Error: '+E.ClassName+' '+E.Message+EOL);
   FixError(ErrorsFails);
  end;
 end;
 if (ErrorsTotal>0) then ExitCode:=ecFailure;
end.
