ExpandEnvironmentStrings Plattformübergreifend?

Antworten
MmVisual
Beiträge: 1466
Registriert: Fr 10. Okt 2008, 23:54
OS, Lazarus, FPC: Winuxarm (L 3.0 FPC 3.2)
CPU-Target: 32/64Bit

ExpandEnvironmentStrings Plattformübergreifend?

Beitrag von MmVisual »

Hallo,

Ich suche ein"ExpandEnvironmentStrings" das Plattformüberfreifend funktioniert.

Ich möchte in einer INI-Datei Parameter von Pfadangaben haben, der auch Environment Variablen enthalten kann und das soll dann in einen absoluten Pfad gewandelt werden.

Kann mir jemand schreiben, welche Funktion ich verwenden kann um einen UTF8 String zu wandeln und welche Unit ich einbinden muss?

Vielen Dank, Gruß Markus.
EleLa - Elektronik Lagerverwaltung - www.elela.de

Socke
Lazarusforum e. V.
Beiträge: 3158
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: ExpandEnvironmentStrings Plattformübergreifend?

Beitrag von Socke »

MmVisual hat geschrieben:um einen UTF8 String zu wandeln

Das kommt darauf an, was du hast und wo du hin möchtest.
Such mal (bspw. in der Suche auf der Portal-Seite) nach Utf8ToSys, UTF8toAnsi, UTF8ToConsol bzw. umgekehrt. Bei Pfadangaben musst du auch wieder aufpassen, welche Zeichenkodierung dein System erwartet.

Zum ersetzen:
Mit GetEnvironmentVariableCount() erhältst du die Anzahl aller verfügbaren Variablen, die du mit GetEnvironmentString() bzw. GetEnvironmentStringUTF8() auslesen kannst. Da suchst du nach einem Gleichheitszeichen. Der erste Teil ist der Name; hinten steht der Wert (kann möglicherweise leer sein).
Wie die zu ersetzende Zeichenkette aussieht, musst du wissen; Unter Windows ist ein %VARNAME% üblich, unter Linux ein $VARNAME. Ein StringReplace() ersetzt dann die Zeichenkette.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

MmVisual
Beiträge: 1466
Registriert: Fr 10. Okt 2008, 23:54
OS, Lazarus, FPC: Winuxarm (L 3.0 FPC 3.2)
CPU-Target: 32/64Bit

Re: ExpandEnvironmentStrings Plattformübergreifend?

Beitrag von MmVisual »

Ich habe eine INI-Datei mit z.B. den Parameter:

[Programm]
TempDir=C:\Temp

Damit ist es kein Problem.

Nun soll auch folgende Eingabe in der INI Datei möglich sein:

[Programm]
TempDir=%TEMP%\MeinTemp

Bzw. unter Linux:

[Programm]
TempDir=$HOME\MeinTemp

Ich lese mit TIniFile und ReadString diesen Wert ein in eine Variable vom Typ "String". Damit ist das ganze UTF8 Codiert.

Nun möchte ich eine Lazarus-Funktion aufrufen, die mir diese Environment-Variablen extrahiert und ich als String den absoluten Pfad stehen habe, als UTF8.

Wenn andere Funktionen eine andere Codierung benötigen, dann wandle ich das, damit habe ich keine Probleme.
So wie Du beschrieben hast, zu Fuß das machen, wäre zwar möglich, aber ich denke unter Linux ist das ersetzen etwas schwieriger, da man nicht genau weiß wann nun die Variable zu Ende ist, denn es gibt nur eine eindeutige Start-Kennung mit "$". Daher die Frage, gibt es eine fertige Funktion, die ich nur aufrufen brauche, so wie unter Windows?

Vielen Dank für die Infos.

Gruß Markus.
EleLa - Elektronik Lagerverwaltung - www.elela.de

Socke
Lazarusforum e. V.
Beiträge: 3158
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: ExpandEnvironmentStrings Plattformübergreifend?

Beitrag von Socke »

MmVisual hat geschrieben:Ich lese mit TIniFile und ReadString diesen Wert ein in eine Variable vom Typ "String". Damit ist das ganze UTF8 Codiert.

Das ist nur UTF8-kodiert, wenn du das in UTF-8 hineinschreibst; TIniFile oder der Typ String wandelt da überhaupt nichts um.

MmVisual hat geschrieben:So wie Du beschrieben hast, zu Fuß das machen, wäre zwar möglich, aber ich denke unter Linux ist das ersetzen etwas schwieriger, da man nicht genau weiß wann nun die Variable zu Ende ist, denn es gibt nur eine eindeutige Start-Kennung mit "$". Daher die Frage, gibt es eine fertige Funktion, die ich nur aufrufen brauche, so wie unter Windows?

Soweit ich weiß, gibts keine fertige Funktion. Bei StringReplace() ist es aber auch egal, ob die Variable überhaupt im String vorkommt oder nicht. Wenn du nur bestimmte Umgebungsvariablen ersetzen möchtest, ist das auch nicht so zeitraubend.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

MmVisual
Beiträge: 1466
Registriert: Fr 10. Okt 2008, 23:54
OS, Lazarus, FPC: Winuxarm (L 3.0 FPC 3.2)
CPU-Target: 32/64Bit

Re: ExpandEnvironmentStrings Plattformübergreifend?

Beitrag von MmVisual »

Ich kann mit vostellen, dass wenn eine Systemvariable $VARAA und eine zweite $VARAAA heißt und im String der INI das steht: "$VARAAAAAA", welche Variable ist dan richtig? $VARAA oder $VARAAA?

Also ich weiß jetzt nicht nach welcher Regel Linux das Handhabt. Oder bleiben solche Dinge dem Zufall überlassen?
EleLa - Elektronik Lagerverwaltung - www.elela.de

Socke
Lazarusforum e. V.
Beiträge: 3158
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: ExpandEnvironmentStrings Plattformübergreifend?

Beitrag von Socke »

MmVisual hat geschrieben:Ich kann mit vostellen, dass wenn eine Systemvariable $VARAA und eine zweite $VARAAA heißt und im String der INI das steht: "$VARAAAAAA", welche Variable ist dan richtig? $VARAA oder $VARAAA?

Also ich weiß jetzt nicht nach welcher Regel Linux das Handhabt. Oder bleiben solche Dinge dem Zufall überlassen?

Die Variable geht immer bis zum nächsten Trennzeichen; wobei ich jetzt nicht genau weiß, was der POSIX-Standard vorschreibt.
Also bei "$VARAAAA/abc" wäre die Variable also $VARAAAA; daher musst du immer die längste Variable zuerst ersetzen. Alternativ könnte man auch darüber streiten, wie man denn $GDM_LANG und $GDMSESSION auseinander hält -- beide beginnen schließlich mit $GD...

Gegenfrage: Welche Variablen mit diesen Namen kennst du/hast du auf deinem System?
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

MmVisual
Beiträge: 1466
Registriert: Fr 10. Okt 2008, 23:54
OS, Lazarus, FPC: Winuxarm (L 3.0 FPC 3.2)
CPU-Target: 32/64Bit

Re: ExpandEnvironmentStrings Plattformübergreifend?

Beitrag von MmVisual »

Gegenantwort: Ich bin Windowsprogrammierer und habe von Linux nur sehr begrenzt Ahnung. Nur kompilliere ich meine Exe auch unter Linux und habe ein paar Kleinigkeiten dafür angepasst.
Nun möchte jemand gerne ein .deb Paket schnüren und dazu waren ein paar INI-Einträge erforderlich, die auf z.B. $HOME verweisen. Aber was die breite Masse der User so anstellt kann ich schlecht abschätzen. Daher wollte ich erst mal abschätzen/nachfragen wie das unter Linux denn richtig geht.

Erst mal die längste Variable nehmen, ich denke das ist ein gangbarer Weg und auch für jedermann verständlich dokumentierbar. Wenn das Ergebnis demjenigen dann nicht gefällt, so muss er eben eine andere Environment-Variable machen.

Falls jemand doch eine Linux-Funktion kennt, dann gerne noch posten.

Danke, Gruß Markus.
EleLa - Elektronik Lagerverwaltung - www.elela.de

MmVisual
Beiträge: 1466
Registriert: Fr 10. Okt 2008, 23:54
OS, Lazarus, FPC: Winuxarm (L 3.0 FPC 3.2)
CPU-Target: 32/64Bit

Re: ExpandEnvironmentStrings Plattformübergreifend?

Beitrag von MmVisual »

Ich habe mal was geschrieben. Habe ich was vergessen, oder könnte man es besser (einfacher/effizienter) machen?

Diese Funktion erkennt Windows und Linux-Variablen. Allerdings habe ich das unter Linux jetzt noch nicht getestet.

Code: Alles auswählen

< neuer Code im nächsten Postiong von mir >


Gruß Markus

PS: Kann der Code jemand nach FPC/Sysutils.pp einpflegen?
Zuletzt geändert von MmVisual am Di 1. Mär 2011, 07:07, insgesamt 1-mal geändert.
EleLa - Elektronik Lagerverwaltung - www.elela.de

Socke
Lazarusforum e. V.
Beiträge: 3158
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: ExpandEnvironmentStrings Plattformübergreifend?

Beitrag von Socke »

Das aufspalten am Gleichheitszeichen kann die StringList alleine machen (Names, Values und NameValueSeparator).

Man könnte annehmen, dass auf einer Plattform entweder %...% oder $... als Variablenname verwendet wird; das würde einiges an Effizienz bringen, wenn nicht immer nach beiden gesucht werden muss.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

MmVisual
Beiträge: 1466
Registriert: Fr 10. Okt 2008, 23:54
OS, Lazarus, FPC: Winuxarm (L 3.0 FPC 3.2)
CPU-Target: 32/64Bit

Re: ExpandEnvironmentStrings Plattformübergreifend?

Beitrag von MmVisual »

Hab ich rein gemacht:

Code: Alles auswählen

Function ExpandEnvironmentString(s: String): String;
Var i, k, m: Integer;
    sl: TStringList;
    s2: String;
    b, bP, bD: Boolean;
Begin
  Result := s;
  bP := Pos('%', s) > 0;
  bD := Pos('$', s) > 0;
  If Not(bP Or bD) Then // Nichts zu konvertieren
    Exit;
  sl := TStringList.Create;
  k := GetEnvironmentVariableCount - 1;
  For i := 0 To k Do // Alle Variablen in eine Liste merken
  Begin
    s2 := GetEnvironmentStringUTF8(i);
    If Length(s2) >= 2 Then
      If s2[1] <> '=' Then
      Begin
        If bD Then
        Begin
          m := Pos('=',s2) - 1;
          sl.AddObject(s2, TObject(m));
        End Else sl.Add(s2);
      end;
  end;
 
  If bD Then // Nur bei Env-String mit '$' drin.
  Repeat // Tauschen-Listen-Einträge, so dass die längsten Env-Strings zu Anfang stehen
    b := True;
    For i := 0 To sl.Count - 2 Do
    Begin
      If Integer(sl.Objects[i]) < Integer(sl.Objects[i + 1]) Then
      Begin
        b := False;
        sl.Exchange(i, i + 1);
      end;
    end;
  Until b;
 
  m := 0;
  Repeat // Durchlaufe so oft bis alle Env-Variablen aufgelöst sind
    For i := 0 To sl.Count - 1 Do
    Begin
      If bP Then
        Result := StringReplace(Result, '%' + sl.Names[i] + '%', sl.ValueFromIndex[i], [rfReplaceAll, rfIgnoreCase]);
      If bD Then
        Result := StringReplace(Result, '$' + sl.Names[i], sl.ValueFromIndex[i], [rfReplaceAll]);
    end;
    Inc(m);
    If m > sl.Count - 2 Then break; // Max Durchläufe
    b := True; // Teste auf weitere Env-Werte im String
    If bP Then
    Begin
      i := Pos('%', Result);
      If i > 0 Then b := PosEx('%', Result, i + 1) <= 0;
    end;
    If bD And b Then b := Pos('$', Result) <= 0;
  Until b;
  sl.Free;
end;


Noch die Änderung: Die Windows-Codierung mit %..% wird mit "rfIgnoreCase" geprüft.

Edit: Habe das grad mit Linux getestet, klappt prima! Es steht somit nichts mehr im Wege das in die FPC-Sourcen mit auf zu nehmen.
EleLa - Elektronik Lagerverwaltung - www.elela.de

Socke
Lazarusforum e. V.
Beiträge: 3158
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: ExpandEnvironmentStrings Plattformübergreifend?

Beitrag von Socke »

MmVisual hat geschrieben:Edit: Habe das grad mit Linux getestet, klappt prima! Es steht somit nichts mehr im Wege das in die FPC-Sourcen mit auf zu nehmen.

Ich habe mir mal erlaubt, das ganze hinsichtlich der Lesbarkeit zu optimieren:

Code: Alles auswählen

{$IFDEF windows}
const
  ENV_PREFIX = '%';
  ENV_POSTFIX = '%';
{$ELSE}
const
  ENV_PREFIX ='$';
  ENV_POSTFIX = '';
{$ENDIF}
 
function ExpandEnvironmentStrings(const s: String; IgnoreCase: Boolean = True): String;
var
  envs: TStringList;
  aEnvValue: String;
  aEnvName: String;
  flags: TReplaceFlags;
  i: Integer;
begin
  Result := s;
  if Pos(ENV_PREFIX, Result) = 0 then exit;
 
  if IgnoreCase then
    flags := [rfReplaceAll, rfIgnoreCase]
  else
    flags := [rfReplaceAll];
 
  envs := TStringList.Create;
  envs.Sorted := ENV_POSTFIX = '';
  for i := GetEnvironmentVariableCount - 1  downto 0 do
    envs.Add(GetEnvironmentString(i));
 
  for i := envs.Count - 1 downto 0 do
  begin
    envs.GetNameValue(i, aEnvName, aEnvValue);
    Result := StringReplace(Result, ENV_PREFIX+aEnvName+ENV_POSTFIX, aEnvValue, flags);
    if Pos(ENV_PREFIX, Result) = 0 then break;
  end;
 
  envs.Destroy;
end;
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

MmVisual
Beiträge: 1466
Registriert: Fr 10. Okt 2008, 23:54
OS, Lazarus, FPC: Winuxarm (L 3.0 FPC 3.2)
CPU-Target: 32/64Bit

Re: ExpandEnvironmentStrings Plattformübergreifend?

Beitrag von MmVisual »

Hallo Socke,

Vielen Dank, sieht gut aus.

Was ist es möglich, dass in einer ENV Variable eine weitere mit drin ist?

%WERT1% = Hallo%WERT2%gugus
%WERT2% = Mein

Dann würde im Ergebnis immer noch %WERT2% drin stehen, da meine For-Schleife noch in ein Repeat gekapselt ist werden solche Verschachtelungen abgefangen. Bei Dir jetzt nicht.

Gruß Markus

PS: Ich bräuchte eine Funktion, bei der ein und die selbe INI-Datei mit den ENV-Werten unter Linux und Windows gleichermassen funktioniert, daher frage ich in meiner Routine beides einfach ab. Die Sortierung ist bei Dir eleganter gelöst.
EleLa - Elektronik Lagerverwaltung - www.elela.de

Socke
Lazarusforum e. V.
Beiträge: 3158
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: ExpandEnvironmentStrings Plattformübergreifend?

Beitrag von Socke »

MmVisual hat geschrieben:Was ist es möglich, dass in einer ENV Variable eine weitere mit drin ist?

%WERT1% = Hallo%WERT2%gugus
%WERT2% = Mein

Dann würde im Ergebnis immer noch %WERT2% drin stehen, da meine For-Schleife noch in ein Repeat gekapselt ist werden solche Verschachtelungen abgefangen. Bei Dir jetzt nicht.

Wenn du für die rekursiven Variablen zuständig bist, bist du auch dafür zuständig die Rekursion wieder aufzulösen. Zumal du dir damit auch ganz einfach Endlosschleifen einhandeln kannst.

MmVisual hat geschrieben:PS: Ich bräuchte eine Funktion, bei der ein und die selbe INI-Datei mit den ENV-Werten unter Linux und Windows gleichermassen funktioniert, daher frage ich in meiner Routine beides einfach ab.

Umgebungsvariablen sind per Definition abhängig von der Umgebung. Um plattformunabhängig zu bleiben, wirst du vorher eine Normalisierung durchführen müssen.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

MmVisual
Beiträge: 1466
Registriert: Fr 10. Okt 2008, 23:54
OS, Lazarus, FPC: Winuxarm (L 3.0 FPC 3.2)
CPU-Target: 32/64Bit

Re: ExpandEnvironmentStrings Plattformübergreifend?

Beitrag von MmVisual »

> Zumal du dir damit auch ganz einfach Endlosschleifen einhandeln kannst.

Ja, deshalb hab ich einen Zähler drin, wenn das so aussieht dass es Endlos wird, dann BREAK; :mrgreen:
(Den Zähler könnte ich auch optimieren indem ich den letzten String vergleiche, bei keiner Änderung gabs nichts zu konvertieren, bzw. die % $ Variable existiert nicht im System)

Um das Lazarus-Tauglich zu machen und in die FPC Sourcen auf zu nehmen ist deine Variante sicher besser geeignet :wink:

Meine Exe kompiliere ich unter Linux und Windows. Ich kann die SQLite Datenbank sowie die .so und .dll sowie die zwei EXE auf einen USB Stick kopieren und habe dann einen Stick, bei dem die Datenbank/das Programm sowohl unter Linux als auch unter Windows auf jedem Rechner ohne installation funktioniert.
Ist eine coole Sache.
Hier hab ich das mal vorgestellt:
viewtopic.php?f=11&t=4107
Ich nutze das auch als Warenlager, daher sollte das Programm möglichst Kriesentauglich sein, also problemlos mit USB Stick funktionieren können.

Edit: Hier ist meine geänderte Linux/Windows Kombi Variante, angepasst mit deinen besserungen:

Code: Alles auswählen

Function ExpandEnvironmentString(s: String): String;
Var i, k: Integer;
    sl: TStringList;
    s2: String;
    bP, bD: Boolean;
Begin
  Result := s;
  bP := Pos('%', s) > 0;
  bD := Pos('$', s) > 0;
  If Not(bP Or bD) Then Exit; // Nichts zu konvertieren
  sl := TStringList.Create;
  sl.Sorted := bD;
  For i := GetEnvironmentVariableCount - 1 DownTo 0 Do // Alle Variablen in eine Liste merken
  Begin
    s2 := GetEnvironmentStringUTF8(i);
    If Length(s2) >= 2 Then
      If s2[1] <> '=' Then // Bei Windows kommt sowas auch vor, daher wegoptimieren
        sl.Add(s2);
  end;
 
  k := 0;
  Repeat // Durchlaufe so oft bis alle Env-Variablen aufgelöst sind
    s2 := Result;
    For i := sl.Count - 1 DownTo 0 Do
    Begin
      If bP Then
      Begin
        Result := StringReplace(Result, '%' + sl.Names[i] + '%', sl.ValueFromIndex[i], [rfReplaceAll, rfIgnoreCase]);
        If Pos('%', Result) = 0 Then bP := False;
      end;
      If bD Then
      Begin
        Result := StringReplace(Result, '$' + sl.Names[i], sl.ValueFromIndex[i], [rfReplaceAll]);
        If Pos('$', Result) = 0 Then bD := False;
      end;
      If Not(bP Or bD) Then Break;
    end;
    If Not(bP Or bD) Then Break;
    If SameText(s2, Result) Then Break; // % $ nicht konvertierbar, da im System nicht vorhanden
    Inc(k);
    If k > sl.Count - 2 Then Break; // Max Durchläufe
  Until True;
  sl.Free;
end;
EleLa - Elektronik Lagerverwaltung - www.elela.de

Antworten