
{
  Strings2 : routine di gestione stringhe tipo 'C' (PChar),
             gestione messaggi,
             help,
             common dialog,
             profiling,
             file .INI

  Versione 2.0A
}
unit Strings2;

{$V-}
{$IFDEF Final}
{$D-,I-,L-,R-,S-}
{$ELSE}
{$D+,I+,L+,R+,S+}
{$ENDIF}

interface

uses
  WinDos,
  Strings,
  OWindows;

{--------------------------------------------------------- Scansione stringhe }

function StrRTrimm(Str : PChar) : PChar;
  {- Elimina gli spazi alla fine della stringa Str.}

function StrLTrimm(Str : PChar) : PChar;
  {- Elimina gli spazi all'inizio della stringa Str.}

function StrToken(var Str : PChar; Delim : char) : PChar;
  {- Restituisce la parte di Str fino al primo carattere Delim. Modifica Str
     in modo che punti al primo carattere dopo Delim. Elimina gli spazi.}

function StrPasNil(P : PChar) : string;

{------------------------------------------------- Conversione numero/stringa }

function StrToInt(Str : PChar) : longint;
  {- Restituisce il valore intero rappresentato dalla stringa decimale Str.}

function StrToIntDef(Str : PChar; DefV : longint) : longint;
  {- Restituisce il valore intero rappresentato dalla stringa decimale Str.
     Se Str è nil o è vuota o non è corretta viene restituito DefV.}

function StrToReal(Str : PChar) : real;
  {- Restituisce il valore reale rappresentato dalla stringa decimale Str.}

function RealToStr(V : real; NDigits : integer; Dest : PChar) : PChar;
  {- In Dest la reappresentazione decimale di V con NDigits cifre dopo
     la virgola. Cifre '0' non significative vengono eliminate.}

function IntToStr(V : longint; Dest : PChar) : PChar;
  {- In Dest la rappresentazione decimale di V. Restituisce Dest.}

{--------------------------------------------------------- Gestione nomi file }

function GetFileName(FilePath : PChar) : PChar;
  {- Restituisce il nome del file FilePath.}

function GetExtension(FilePath : PChar) : PChar;
  {- Restituisce l'estensione (compreso il punto) del file FilePath.}

function HasWildCards(FilePath : PChar) : boolean;
  {- True se FilePath contiene wildcard: '*' o '?'.}

function HasPath(FilePath : PChar): boolean;
  {- True se FilePath contiene il nome di una directory e/o di un drive.}

function PutExtension(FilePath,NewExt : PChar) : PChar;
  {- Sostituisce l'estensione di FilePath con NewExt. FilePath deve
     avere il posto per mettere l'estensione. Se NewExt=nil l'estensione
     di FilePath viene eliminata.}

{--------------------------------------------------- Gestione file di profile }

function GetPrivateProfileFlag(Sec,Key : PChar; Default : boolean; FileName : PChar) : boolean;

function GetPrivateProfileLong(Sec,Key : PChar; Default : longint; FileName : PChar) : longint;

function GetPrivateProfileReal(Sec,Key : PChar; Default : real; FileName : PChar) : real;

function GetPrivateProfileText(Sec,Key : PChar;
                               Default,Dest : PChar; DestSize : integer;
                               FileName : PChar) : integer;

procedure WritePrivateProfileFlag(Sec,Key : PChar; V : boolean; FileName : PChar);

procedure WritePrivateProfileInt(Sec,Key : PChar; V : longint; FileName : PChar);

procedure WritePrivateProfileReal(Sec,Key : PChar; V : real; NDigits : integer; FileName : PChar);

procedure WritePrivateProfileText(Sec,Key : PChar; V : PChar; FileName : PChar);

{-------------------------------------------------------------- Gestione help }

procedure SetHelpFileName(FN : PChar);
  {- Setta il nome del file contenente l'help per il programma.}

function ExecHelp(Command : word; Data : longint) : boolean;
  {- Richiama l'help con comando Command e parametro Data.}

procedure CloseHelp;
  {- Chiude l'help associato al programma.}

{------------------------------------------------------------------ Profiling }

procedure StartProfile;
  {- Inizia il conteggio del tempo trascorso.}

procedure EndProfile;
  {- Visualizza il tempo trascorso dall'ultima StartProfile.}

{------------------------------------- Caricamento e visualizzazione messaggi }

const
  MParent = 65535;     {Valore di TitleH per avere come titolo la caption
                        di AParent.}

function MsgPtr(MsgH : word) : PChar;
  {- Restituisce il messaggio di codice MsgH. Il messaggio non può essere lungo più
     di 80 caratteri.}

function MessageBoxH(AParent : PWindowsObject; MsgH,TitleH : word; BoxType : word) : integer;
  {- Visualizza un message box con caption di codice TitleH e messaggio di codice MsgH.
     BoxType è il tipo del message box da creare. Viene restituito il codice restituito dal
     message box.}

procedure ErrorMsg(AParent : PWindowsObject; MsgH,TitleH : word);
  {- Visualizza il messaggio di codice MsgH in un message box con caption di codice
     TitleH.}

procedure ParErrorMsg(AParent : PWindowsObject; MsgH,TitleH : word; ParS : PChar);
  {- Visualizza il messaggio di codice MsgH con parametro ParS ('%s' nella
     messaggio di errore) in un message box con caption di codice TitleH.}

procedure ParParErrorMsg(AParent : PWindowsObject; MsgH,TitleH : word; ParS1,ParS2 : PChar);
  {- Visualizza il messaggio di codice MsgH con parametri ParS1 e ParS2 ('%s' nella
     messaggio di errore) in un message box con caption di codice TitleH.}

procedure ErrorMsgStr(AParent : PWindowsObject; Msg : PChar; TitleH : word);
  {- Visualizza il messaggio Msg in un message box.}

procedure Panic(N : integer);
  {- Errore fatale non previsto: visualizza 'This can't happen' e interrompe
     l'esecuzione del programma.}

procedure Warning(N : integer);
  {- Errore non fatale non previsto: visualizza 'Warning' e prosegue.}

{------------------------------------------- Richiamo common dialog }

function GetOpenFName(AParent : PWindowsObject;
                      FName : PChar;
                      MCaption,MDesc : word;
                      MustExist : boolean) : boolean;

function GetSaveFName(AParent : PWindowsObject;
                      FName : PChar;
                      MCaption,MDesc : word) : boolean;

implementation {==============================================================}

uses
  WinTypes,
  WinProcs,
  CommDlg,
  Arit;

{--------------------------------------------------------- Scansione stringhe }

function StrRTrimm(Str : PChar) : PChar;
var
  EndS : PChar;
begin
  if Str = nil then StrRTrimm := nil
  else begin
    EndS := StrEnd(Str)-1;
    while (EndS >= Str) and (EndS^ = ' ') do dec(EndS);
    inc(EndS);
    EndS^ := #0;
    StrRTrimm := Str;
  end;
end; { StrRTrimm }

function StrLTrimm(Str : PChar) : PChar;
begin
  if Str = nil then StrLTrimm := nil
  else begin
    while (Str^ = ' ') do inc(Str);
    StrLTrimm := Str;
  end;
end; { StrLTrimm }

function StrToken(var Str : PChar; Delim : char) : PChar;
var
  DelimS : PChar;
begin
  if Str = nil then StrToken := nil
  else begin
    DelimS := StrScan(Str,Delim);
    if DelimS = nil then DelimS := StrEnd(Str)-1
    else DelimS^ := #0;
    StrToken := StrRTrimm(StrLTrimm(Str));
    Str := DelimS+1;
  end;
end; { StrToken }

function StrPasNil(P : PChar) : string;
begin
  if (P <> nil) then StrPasNil := StrPas(P)
  else StrPasNil := '';
end;  { StrPasNil }

{------------------------------------------------- Conversione numero/stringa }

function StrToInt(Str : PChar) : longint;
begin
  StrToInt := StrToIntDef(Str,-32768)
end; { StrToInt }

function StrToIntDef(Str : PChar; DefV : longint) : longint;
var
  V : longint;
  Code : integer;
begin
  if Str = nil then StrToIntDef := DefV
  else begin
    val(Str,V,Code);
    if Code <> 0 then V := DefV;
    StrToIntDef := V;
  end;
end; { StrToIntDef }

function StrToReal(Str : PChar) : real;
var
  V : real;
  Code : integer;
begin
  val(Str,V,Code);
  if Code <> 0 then V := -32768;
  StrToReal := V;
end; { StrToReal }

function RealToStr(V : real; NDigits : integer; Dest : PChar) : PChar;
var
  Buffer : array[0..20] of char;
  P : PChar;
begin
  Str(V:1:NDigits,Buffer);
  P := Buffer+StrLen(Buffer);
  while PChar(P-1)^ = '0' do dec(P);
  if PChar(P-1)^ = '.' then dec(P);
  P^ := #0;
  StrCopy(Dest,Buffer);
  RealToStr := Dest;
end; { RealToStr }

function IntToStr(V : longint; Dest : PChar) : PChar;
var
  Buffer : array[0..12] of char;
begin
  Str(V,Buffer);
  StrCopy(Dest,Buffer);
  IntToStr := Dest;
end; { IntToStr }

{--------------------------------------------------------- Gestione nomi file }

function GetFileName(FilePath : PChar) : PChar;
var
  P: PChar;
begin
  P := StrRScan(FilePath,'\');
  if P = nil then P := StrRScan(FilePath,':');
  if P = nil then GetFileName := FilePath else GetFileName := P + 1;
end; { GetFileName }

function GetExtension(FilePath : PChar) : PChar;
var
  P: PChar;
begin
  P := StrScan(GetFileName(FilePath),'.');
  if P = nil then GetExtension := StrEnd(FilePath)
  else GetExtension := P;
end; { GetExtension }

function HasWildCards(FilePath : PChar) : boolean;
begin
  HasWildCards := (StrScan(FilePath,'*') <> nil) or
                  (StrScan(FilePath,'?') <> nil);
end; { HasWildCards }

function HasPath(FilePath : PChar): boolean;
begin
  HasPath := (StrRScan(FilePath,'\') <> nil) or
             (StrRScan(FilePath,':') <> nil);
end;  { HasPath }

function PutExtension(FilePath,NewExt : PChar) : PChar;
var
  P : PChar;
begin
  P := GetExtension(FilePath);
  if (NewExt = nil) or
     (StrLen(NewExt) = 0) or
     (StrLen(NewExt) = 1) and (NewExt[0] = '.') then P^ := #0
  else begin
    if NewExt[0] <> '.' then begin
      P^ := '.';
      inc(P);
    end;
    StrLCopy(P,NewExt,4);
  end;
  PutExtension := FilePath;
end; { PutExtension }

{--------------------------------------------------- Gestione file di profile }

function GetPrivateProfileFlag(Sec,Key : PChar; Default : boolean; FileName : PChar) : boolean;
var
  Buffer : array[0..6] of char;
begin
  GetPrivateProfileString(Sec,Key,'',Buffer,SizeOf(Buffer),FileName);
  if StrLen(Buffer) = 0 then GetPrivateProfileFlag := Default
  else begin
    StrLower(Buffer);
    if (StrComp(Buffer,'0') = 0) or
       (StrComp(Buffer,'false') = 0) or
       (StrComp(Buffer,'off') = 0) then GetPrivateProfileFlag := false
    else if (StrComp(Buffer,'1') = 0) or
       (StrComp(Buffer,'true') = 0) or
       (StrComp(Buffer,'on') = 0) then GetPrivateProfileFlag := true
    else GetPrivateProfileFlag := Default;
  end;
end; { GetPrivateProfileFlag }

function GetPrivateProfileLong(Sec,Key : PChar; Default : longint; FileName : PChar) : longint;
var
  Buffer : array[0..20] of char;
  Code : integer;
  V : longint;
begin
  GetPrivateProfileString(Sec,Key,'',Buffer,SizeOf(Buffer),FileName);
  if StrLen(Buffer) = 0 then GetPrivateProfileLong := Default
  else begin
    Val(Buffer,V,Code);
    if Code <> 0 then GetPrivateProfileLong := Default
    else GetPrivateProfileLong := V;
  end;
end; { GetPrivateProfileLong }

function GetPrivateProfileReal(Sec,Key : PChar; Default : real; FileName : PChar) : real;
var
  Buffer : array[0..20] of char;
  Code : integer;
  V : real;
begin
  GetPrivateProfileString(Sec,Key,'',Buffer,SizeOf(Buffer),FileName);
  if StrLen(Buffer) = 0 then GetPrivateProfileReal := Default
  else begin
    Val(Buffer,V,Code);
    if Code <> 0 then GetPrivateProfileReal := Default
    else GetPrivateProfileReal := V;
  end;
end; { GetPrivateProfileReal }

function GetPrivateProfileText(Sec,Key : PChar;
                               Default,Dest : PChar; DestSize : integer;
                               FileName : PChar) : integer;
var
  BufPtr,Source : PChar;
  BufSize : integer;
  NumBuf : array[0..3] of char;
begin
  BufSize := DestSize*4;
  GetMem(BufPtr,BufSize);
  GetPrivateProfileString(Sec,Key,'',BufPtr,BufSize,FileName);
  if StrLen(BufPtr) = 0 then StrLCopy(Dest,Default,DestSize-1)
  else begin
    Source := BufPtr;
    while Source^ <> #0 do begin
      if Source^ <> '#' then begin
        Dest^ := Source^;
        inc(Source);
      end else begin
        inc(Source);
        Dest^ := chr(Min(255,StrToIntDef(StrLCopy(NumBuf,Source,3),ord(' '))));
        inc(Source,StrLen(NumBuf));
      end;
      inc(Dest);
    end;
    Dest^ := #0;
  end;
  GetPrivateProfileText := StrLen(Dest);
  FreeMem(BufPtr,BufSize);
end; { GetPrivateProfileText }

procedure WritePrivateProfileFlag(Sec,Key : PChar; V : boolean; FileName : PChar);
const
  ZU : array[false..true] of PChar = ('0','1');
begin
  WritePrivateProfileString(Sec,Key,ZU[V],FileName);
end; { WritePrivateProfileFlag }

procedure WritePrivateProfileInt(Sec,Key : PChar; V : longint; FileName : PChar);
var
  Buffer : array[0..12] of char;
begin
  WVSPrintF(Buffer,'%ld',V);
  WritePrivateProfileString(Sec,Key,Buffer,FileName);
end; { WritePrivateProfileInt }

procedure WritePrivateProfileReal(Sec,Key : PChar; V : real; NDigits : integer; FileName : PChar);
var
  Buffer : array[0..20] of char;
begin
  Str(V:1:NDigits,Buffer);
  WritePrivateProfileString(Sec,Key,Buffer,FileName);
end; { WritePrivateProfileReal }

procedure WritePrivateProfileText(Sec,Key : PChar; V : PChar; FileName : PChar);
var
  BufPtr,Dest : PChar;
  BufSize,I : integer;
begin
  if (V = nil) or (StrLen(V) = 0) then
    WritePrivateProfileString(Sec,Key,V,FileName)
  else begin
    BufSize := StrLen(V)*4+1;
    GetMem(BufPtr,BufSize);
    Dest := BufPtr;
    while V^ <> #0 do begin
      if not (V^ in [#1..#31,'#']) then begin
        Dest^ := V^;
        inc(Dest);
      end else begin
        I := ord(V^);
        WVSPrintF(Dest,'#%03d',I);
        Dest := StrEnd(Dest);
      end;
      inc(V);
    end;
    Dest^ := #0;
    WritePrivateProfileString(Sec,Key,BufPtr,FileName);
    FreeMem(BufPtr,BufSize);
  end;
end; { WritePrivateProfileText }

{-------------------------------------------------------------- Gestione help }

var
  HelpFile : array[0..fsPathName] of char;

procedure SetHelpFileName(FN : PChar);
begin
  StrLCopy(HelpFile,FN,fsPathName);
end; { SetHelpFileName }

function ExecHelp(Command : word; Data : longint) : boolean;
begin
  ExecHelp := not WinHelp(Application^.MainWindow^.HWindow,HelpFile,Command,Data);
end; { ExecHelp }

procedure CloseHelp;
begin
  ExecHelp(Help_Quit,0);
end; { CloseHelp }

{------------------------------------------------------------------ Profiling }

var
  Time : longint;

procedure StartProfile;
begin
  Time := GetCurrentTime;
end; { StartProfile }

procedure EndProfile;
var
  Buffer : array[0..80] of char;
  Value : longint;
begin
  Value := GetCurrentTime-Time;
  WVSPrintF(Buffer,'Tempo impiegato: %ldms',Value);
  MessageBox(GetFocus,Buffer,'',mb_Ok);
end; { EndProfile }

{------------------------------------- Caricamento e visualizzazione messaggi }

const
  MaxTitleLen = 50;   {Lunghezza massima dei titoli dei message box.}
  MaxMsgLen   = 80;   {Lunghezza massima dei messaggi dei message box
                       e dei messaggi caricati con la MsgPtr.}

var
  MsgBuffer : array[0..MaxMsgLen] of char;

function MsgPtr(MsgH : word) : PChar;
begin
  LoadString(hInstance,MsgH,MsgBuffer,SizeOf(MsgBuffer));
  MsgPtr := @MsgBuffer;
end; { MsgPtr }

function MessageBoxH(AParent : PWindowsObject; MsgH,TitleH : word; BoxType : word) : integer;
var
  Msg   : array[0..MaxMsgLen] of char;
  Title : array[0..MaxTitleLen] of char;
begin
  if (TitleH = MParent) and (AParent <> nil) then
    GetWindowText(AParent^.HWindow,Title,SizeOf(Title))
  else
    LoadString(hInstance,TitleH,Title,SizeOf(Title));
  LoadString(hInstance,MsgH,Msg,SizeOf(Msg));
  if AParent = nil then
    MessageBoxH := MessageBox(GetFocus,Msg,Title,BoxType)
  else
    MessageBoxH := MessageBox(AParent^.HWindow,Msg,Title,BoxType);
end; { MessageBoxH }

procedure ErrorMsg(AParent : PWindowsObject; MsgH,TitleH : word);
begin
  MessageBoxH(AParent,MsgH,TitleH,mb_IconExclamation);
end; { ErrorMsg }

procedure ParErrorMsg(AParent : PWindowsObject; MsgH,TitleH : word; ParS : PChar);
var
  Title : array[0..MaxTitleLen] of char;
  Msg,Buffer : array[0..MaxMsgLen] of char;
begin
  if (TitleH = MParent) and (AParent <> nil) then
    GetWindowText(AParent^.HWindow,Title,SizeOf(Title))
  else
    LoadString(hInstance,TitleH,Title,SizeOf(Title));
  LoadString(hInstance,MsgH,Msg,SizeOf(Msg));
  if ParS = nil then ParS := '';
  WVSPrintf(Buffer,Msg,ParS);
  if AParent = nil then
    MessageBox(GetFocus,Buffer,Title,mb_IconExclamation)
  else
    MessageBox(AParent^.HWindow,Buffer,Title,mb_IconExclamation);
end; { ParErrorMsg }

procedure ParParErrorMsg(AParent : PWindowsObject; MsgH,TitleH : word; ParS1,ParS2 : PChar);
var
  Title : array[0..MaxTitleLen] of char;
  Msg,Buffer : array[0..MaxMsgLen] of char;
  ParBuffer : array[1..2] of PChar;
begin
  if (TitleH = MParent) and (AParent <> nil) then
    GetWindowText(AParent^.HWindow,Title,SizeOf(Title))
  else
    LoadString(hInstance,TitleH,Title,SizeOf(Title));
  LoadString(hInstance,MsgH,Msg,SizeOf(Msg));
  if Pars1 <> nil then ParBuffer[1] := ParS1
  else ParBuffer[1] := '';
  if Pars2 <> nil then ParBuffer[2] := ParS2
  else ParBuffer[2] := '';
  WVSPrintf(Buffer,Msg,ParBuffer);
  if AParent = nil then
    MessageBox(GetFocus,Buffer,Title,mb_IconExclamation)
  else
    MessageBox(AParent^.HWindow,Buffer,Title,mb_IconExclamation);
end; { ParParErrorMsg }

procedure ErrorMsgStr(AParent : PWindowsObject; Msg : PChar; TitleH : word);
begin
  if AParent = nil then
    MessageBox(GetFocus,Msg,MsgPtr(TitleH),mb_IconExclamation)
  else
    MessageBox(AParent^.HWindow,Msg,MsgPtr(TitleH),mb_IconExclamation);
end; { ErrorMsgStr }

procedure Panic(N : integer);
var
  MsgBuffer : array[0..32] of char;
begin
  WVSPrintF(MsgBuffer,'This can''t happen (%d)',N);
  MessageBox(GetFocus,MsgBuffer,'Fatal error',mb_IconExclamation);
  halt(1);
end; { Panic }

procedure Warning(N : integer);
var
  MsgBuffer : array[0..32] of char;
begin
  WVSPrintF(MsgBuffer,'This shouldn''t happen (%d)',N);
  MessageBox(GetFocus,MsgBuffer,'Warning',mb_IconExclamation);
end; { Warning }

{----------------------------------------------------- Richiamo common dialog }

function GetOpenFName(AParent : PWindowsObject;
                      FName : PChar;
                      MCaption,MDesc : word;
                      MustExist : boolean) : boolean;
var
  OpenFileName : TOpenFileName;
  DefExt : array[0..fsExtension] of char;
  Filter : array[0..fsExtension+84] of char;
begin
  FillChar(OpenFileName,SizeOf(TOpenFileName),#0);
  StrCopy(DefExt,GetExtension(FName)+1);
  FillChar(Filter,SizeOf(Filter),#0);
  StrCopy(Filter,MsgPtr(MDesc));
  StrCopy(Filter+StrLen(Filter)+1,'*.');
  StrCat(Filter+StrLen(Filter)+1,DefExt);
  with OpenFileName do begin
    hInstance     := HInstance;
    hwndOwner     := AParent^.HWindow;
    lpstrDefExt   := DefExt;
    lpstrFile     := FName;
    lpstrFilter   := Filter;
    if MCaption <> 0 then lpstrTitle:= MsgPtr(MCaption);
    if MustExist then Flags := ofn_FileMustExist;
    Flags         := Flags or ofn_HideReadOnly;
    lStructSize   := sizeof(TOpenFileName);
    nFilterIndex  := 1;       {Index into Filter String in lpstrFilter}
    nMaxFile      := fsPathName+1;
  end;
  GetOpenFName := GetOpenFileName(OpenFileName); 
  AnsiLower(Fname);
end; { GetOpenFName }

function GetSaveFName(AParent : PWindowsObject;
                      FName : PChar;
                      MCaption,MDesc : word) : boolean;
var
  OpenFileName : TOpenFileName;
  DefExt : array[0..fsExtension] of char;
  Filter : array[0..fsExtension+84] of char;
begin
  FillChar(OpenFileName,SizeOf(TOpenFileName),#0);
  StrCopy(DefExt,GetExtension(FName)+1);
  FillChar(Filter,SizeOf(Filter),#0);
  StrCopy(Filter,MsgPtr(MDesc));
  StrCopy(Filter+StrLen(Filter)+1,'*.');
  StrCat(Filter+StrLen(Filter)+1,DefExt);
  with OpenFileName do begin
    hInstance     := HInstance;
    hwndOwner     := AParent^.HWindow;
    lpstrDefExt   := DefExt;
    lpstrFile     := FName;
    lpstrFilter   := Filter;
    if MCaption <> 0 then lpstrTitle:= MsgPtr(MCaption);
    Flags         := ofn_OverWritePrompt or ofn_HideReadOnly;
    lStructSize   := sizeof(TOpenFileName);
    nFilterIndex  := 1;       {Index into Filter String in lpstrFilter}
    nMaxFile      := fsPathName+1;
  end;
  GetSaveFName := GetSaveFileName(OpenFileName); 
  AnsiLower(FName);
end; { GetSaveFName }

{----------------------------------------------------------------------- Main }

end. { unit Strings2 }