using System;
using System.IO;
using System.Text;
using Deploymentcenter.Client.Models;
namespace Deploymentcenter.Client
{
///
/// Loest die Zielpfade einer setup.json plattformrichtig auf.
///
/// Ohne das schrieb der Installer ausschliesslich ins
/// Installationsverzeichnis. Eine Anwendung, die sich unter Linux richtig
/// verhaelt, liest ihre Konfiguration aber aus $XDG_CONFIG_HOME und unter
/// Windows aus %APPDATA% - schon deshalb, weil /opt/<app> fuer den
/// Dienstbenutzer typischerweise nicht schreibbar ist. Der Installer legte
/// die Datei also dorthin, wo die Anwendung nie nachsieht.
///
public static class SetupPaths
{
///
/// Ermittelt den vollstaendigen Pfad eines Ziels.
///
/// Ziel aus der setup.json.
/// Installationsverzeichnis der Anwendung.
public static string Resolve(SetupTarget target, string installDir)
{
string file = Expand(target?.File ?? string.Empty);
// Ein absoluter Pfad ist bereits die Antwort - etwa nachdem
// ${XDG_CONFIG_HOME} ersetzt wurde.
if (Path.IsPathRooted(file))
{
return Path.GetFullPath(file);
}
string root = BaseDirectory(target?.Location, installDir);
return Path.GetFullPath(Path.Combine(root, file.Replace('/', Path.DirectorySeparatorChar)));
}
///
/// Wurzelverzeichnis fuer eine Ortsangabe.
///
/// install (Vorgabe) - das Installationsverzeichnis
/// config - %APPDATA% bzw. $XDG_CONFIG_HOME (~/.config)
/// data - %LOCALAPPDATA% bzw. $XDG_DATA_HOME (~/.local/share)
/// home - Benutzerverzeichnis
///
public static string BaseDirectory(string? location, string installDir)
{
switch ((location ?? string.Empty).Trim().ToLowerInvariant())
{
case "config":
return ConfigDirectory();
case "data":
return DataDirectory();
case "home":
return HomeDirectory();
case "":
case "install":
return installDir;
default:
// Eine unbekannte Angabe ins Installationsverzeichnis
// umzubiegen ist die harmlosere Auslegung: dort landet die
// Datei sichtbar statt an einem geratenen Ort.
return installDir;
}
}
public static string ConfigDirectory()
{
if (OperatingSystemHelpers.IsWindows())
{
string appData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
return string.IsNullOrWhiteSpace(appData)
? Path.Combine(HomeDirectory(), "AppData", "Roaming")
: appData;
}
string xdg = Environment.GetEnvironmentVariable("XDG_CONFIG_HOME") ?? string.Empty;
return string.IsNullOrWhiteSpace(xdg)
? Path.Combine(HomeDirectory(), ".config")
: xdg;
}
public static string DataDirectory()
{
if (OperatingSystemHelpers.IsWindows())
{
string localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
return string.IsNullOrWhiteSpace(localAppData)
? Path.Combine(HomeDirectory(), "AppData", "Local")
: localAppData;
}
string xdg = Environment.GetEnvironmentVariable("XDG_DATA_HOME") ?? string.Empty;
return string.IsNullOrWhiteSpace(xdg)
? Path.Combine(HomeDirectory(), ".local", "share")
: xdg;
}
public static string HomeDirectory()
{
string home = Environment.GetEnvironmentVariable("HOME") ?? string.Empty;
if (string.IsNullOrWhiteSpace(home))
{
home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
}
return string.IsNullOrWhiteSpace(home) ? Directory.GetCurrentDirectory() : home;
}
///
/// Ersetzt Umgebungsvariablen in beiden gebraeuchlichen Schreibweisen:
/// ${NAME} und %NAME%. Unbekannte Namen bleiben stehen -
/// eine leere Ersetzung wuerde stillschweigend einen falschen Pfad
/// ergeben, ein stehengebliebener Platzhalter faellt dagegen auf.
///
public static string Expand(string value)
{
if (string.IsNullOrEmpty(value))
return string.Empty;
var builder = new StringBuilder(value.Length);
for (int i = 0; i < value.Length; i++)
{
// ${NAME}
if (value[i] == '$' && i + 1 < value.Length && value[i + 1] == '{')
{
int close = value.IndexOf('}', i + 2);
if (close > i + 2)
{
string name = value.Substring(i + 2, close - i - 2);
string? resolved = Lookup(name);
if (resolved != null)
{
builder.Append(resolved);
i = close;
continue;
}
}
}
// %NAME%
if (value[i] == '%')
{
int close = value.IndexOf('%', i + 1);
if (close > i + 1)
{
string name = value.Substring(i + 1, close - i - 1);
string? resolved = Lookup(name);
if (resolved != null)
{
builder.Append(resolved);
i = close;
continue;
}
}
}
builder.Append(value[i]);
}
return builder.ToString();
}
///
/// Schlaegt einen Namen nach. Neben echten Umgebungsvariablen werden
/// die XDG-Namen auch dann beantwortet, wenn sie nicht gesetzt sind -
/// sonst muesste jede setup.json den Rueckfall auf ~/.config selbst
/// ausformulieren.
///
private static string? Lookup(string name)
{
string value = Environment.GetEnvironmentVariable(name) ?? string.Empty;
if (!string.IsNullOrWhiteSpace(value))
return value;
switch (name.ToUpperInvariant())
{
case "XDG_CONFIG_HOME": return ConfigDirectory();
case "XDG_DATA_HOME": return DataDirectory();
case "HOME":
case "USERPROFILE": return HomeDirectory();
case "APPDATA": return ConfigDirectory();
case "LOCALAPPDATA": return DataDirectory();
default: return null;
}
}
}
}