From e2b2cece97cf5e2185832e8f7ed74b7ddea53eeb Mon Sep 17 00:00:00 2001 From: Leo Meyer Date: Wed, 14 Jan 2026 16:29:45 +0100 Subject: [PATCH 1/4] Add /config= cmd line argument for using a specified configuration XML file from the AppData folder --- NAPS2.Lib/Config/Naps2Config.cs | 6 +++++- NAPS2.Lib/EtoForms/Ui/DesktopForm.cs | 2 ++ NAPS2.Lib/Modules/CommonModule.cs | 16 +++++++++++++++- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/NAPS2.Lib/Config/Naps2Config.cs b/NAPS2.Lib/Config/Naps2Config.cs index 73cfae19cd..8722961635 100644 --- a/NAPS2.Lib/Config/Naps2Config.cs +++ b/NAPS2.Lib/Config/Naps2Config.cs @@ -25,7 +25,7 @@ public static Naps2Config Stub() => ConfigScope.Memory(), ConfigScope.Defaults(InternalDefaults.GetCommonConfig())); - public Naps2Config(string appConfigPath, string userConfigPath) + public Naps2Config(string appConfigPath, string userConfigPath, string? userConfigName = null) { AppLocked = ConfigScope.File(appConfigPath, new ConfigSerializer(ConfigReadMode.LockedOnly, ConfigRootName.AppConfig), @@ -33,12 +33,14 @@ public Naps2Config(string appConfigPath, string userConfigPath) Run = ConfigScope.Memory(); User = ConfigScope.File(userConfigPath, new ConfigSerializer(ConfigReadMode.All, ConfigRootName.UserConfig), ConfigScopeMode.ReadWrite); + UserConfigName = userConfigName; AppDefault = ConfigScope.File(appConfigPath, new ConfigSerializer(ConfigReadMode.DefaultOnly, ConfigRootName.AppConfig), ConfigScopeMode.ReadOnly); InternalDefault = ConfigScope.Defaults(InternalDefaults.GetCommonConfig()); Scopes = new[] { AppLocked, Run, User, AppDefault, InternalDefault }; + UserConfigName = userConfigName; } public Naps2Config(ConfigScope appLocked, ConfigScope run, @@ -87,4 +89,6 @@ public Naps2Config WithTransaction(params TransactionConfigScope[] public ConfigScope User { get; } public ConfigScope AppDefault { get; } public ConfigScope InternalDefault { get; } + + public string? UserConfigName { get; } } \ No newline at end of file diff --git a/NAPS2.Lib/EtoForms/Ui/DesktopForm.cs b/NAPS2.Lib/EtoForms/Ui/DesktopForm.cs index 1ad1a5a578..e3f6359aac 100644 --- a/NAPS2.Lib/EtoForms/Ui/DesktopForm.cs +++ b/NAPS2.Lib/EtoForms/Ui/DesktopForm.cs @@ -575,6 +575,8 @@ public void EditWithAppChanged() protected virtual void UpdateTitle(ScanProfile? defaultProfile) { Title = string.Format(UiStrings.Naps2TitleFormat, defaultProfile?.DisplayName ?? UiStrings.Naps2FullName); + if (Config.UserConfigName != null) + Title += " (" + Config.UserConfigName + ")"; } private void ListViewMouseWheel(object? sender, MouseEventArgs e) diff --git a/NAPS2.Lib/Modules/CommonModule.cs b/NAPS2.Lib/Modules/CommonModule.cs index 91af8e51ac..aa29e57235 100644 --- a/NAPS2.Lib/Modules/CommonModule.cs +++ b/NAPS2.Lib/Modules/CommonModule.cs @@ -39,9 +39,23 @@ protected override void Load(ContainerBuilder builder) builder.RegisterType().AsSelf(); // Config + string configFileName = "config"; + string? configName = null; // Default: standard config file + // Config file suffix specified via command line argument? + string? configFileOption = Environment.GetCommandLineArgs() + .FirstOrDefault(s => s.ToLower().StartsWith("/config=")); + if (configFileOption != null) + { + configName = configFileOption.Substring("/config=".Length); + if (configName != "") + configFileName += "_" + configName; + } + configFileName += ".xml"; + // TODO: Make this a usable path on Mac/Linux var config = new Naps2Config(Path.Combine(Paths.Executable, "appsettings.xml"), - Path.Combine(Paths.AppData, "config.xml")); + Path.Combine(Paths.AppData, configFileName), + configName); // Remember optional config name builder.RegisterInstance(config); builder.RegisterBuildCallback(ctx => { From 1ac1c0b04afd088c8b51e1cc909b854106a2a1ce Mon Sep 17 00:00:00 2001 From: Leo Meyer Date: Thu, 15 Jan 2026 14:22:59 +0100 Subject: [PATCH 2/4] Trim /config= specification; ignore if it is empty or contains invalid file name characters --- NAPS2.Lib/Modules/CommonModule.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/NAPS2.Lib/Modules/CommonModule.cs b/NAPS2.Lib/Modules/CommonModule.cs index aa29e57235..7bc56f76f7 100644 --- a/NAPS2.Lib/Modules/CommonModule.cs +++ b/NAPS2.Lib/Modules/CommonModule.cs @@ -46,9 +46,14 @@ protected override void Load(ContainerBuilder builder) .FirstOrDefault(s => s.ToLower().StartsWith("/config=")); if (configFileOption != null) { - configName = configFileOption.Substring("/config=".Length); + configName = configFileOption.Substring("/config=".Length).Trim(); if (configName != "") - configFileName += "_" + configName; + { + if (configName.ContainsAny(Path.GetInvalidFileNameChars())) + configName = null; + else + configFileName += "_" + configName; + } } configFileName += ".xml"; From 6a1255255df7ca50b3a02e490806aba71543fd92 Mon Sep 17 00:00:00 2001 From: Leo Meyer Date: Thu, 15 Jan 2026 14:30:44 +0100 Subject: [PATCH 3/4] Fix: Avoid using ContainsAny (not available on Linux) --- NAPS2.Lib/Modules/CommonModule.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NAPS2.Lib/Modules/CommonModule.cs b/NAPS2.Lib/Modules/CommonModule.cs index 7bc56f76f7..78476c6be1 100644 --- a/NAPS2.Lib/Modules/CommonModule.cs +++ b/NAPS2.Lib/Modules/CommonModule.cs @@ -49,7 +49,7 @@ protected override void Load(ContainerBuilder builder) configName = configFileOption.Substring("/config=".Length).Trim(); if (configName != "") { - if (configName.ContainsAny(Path.GetInvalidFileNameChars())) + if (Path.GetInvalidFileNameChars().Any(configName.Contains)) configName = null; else configFileName += "_" + configName; From 8f410f17bd7ea130b6dd02fbf0f8a1a5fa6fb190 Mon Sep 17 00:00:00 2001 From: Leo Meyer Date: Thu, 15 Jan 2026 17:06:15 +0100 Subject: [PATCH 4/4] Correctly ignore whitespace-only config names --- NAPS2.Lib/Modules/CommonModule.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/NAPS2.Lib/Modules/CommonModule.cs b/NAPS2.Lib/Modules/CommonModule.cs index 78476c6be1..787179ae37 100644 --- a/NAPS2.Lib/Modules/CommonModule.cs +++ b/NAPS2.Lib/Modules/CommonModule.cs @@ -47,7 +47,9 @@ protected override void Load(ContainerBuilder builder) if (configFileOption != null) { configName = configFileOption.Substring("/config=".Length).Trim(); - if (configName != "") + if (configName == "") + configName = null; + else { if (Path.GetInvalidFileNameChars().Any(configName.Contains)) configName = null;