From 294e8262b05508a0658fb614bb57415456d9442c Mon Sep 17 00:00:00 2001 From: Josh Date: Wed, 20 May 2026 07:41:32 -0400 Subject: [PATCH 1/8] fix(application): correct `--overridelocaldir` hint text in parser Signed-off-by: Josh --- src/gui/application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/application.cpp b/src/gui/application.cpp index f68fb3db2feba..387120515e1d7 100644 --- a/src/gui/application.cpp +++ b/src/gui/application.cpp @@ -967,7 +967,7 @@ void Application::parseOptions(const QStringList &options) if (it.hasNext() && !it.peekNext().startsWith(QLatin1String("--"))) { _overrideLocalDir = it.next(); } else { - showHint("Invalid URL passed to --overridelocaldir"); + showHint("Invalid path passed to --overridelocaldir"); } } else if (option == QStringLiteral("--set-language")) { if (it.hasNext() && !it.peekNext().startsWith(QLatin1String("--"))) { From 8d9884626b43c4f3d20f20a10fa2d7724e6e9e72 Mon Sep 17 00:00:00 2001 From: Josh Date: Wed, 20 May 2026 09:35:16 -0400 Subject: [PATCH 2/8] fix(app): re-organize, clarify, and expand CLI options output Signed-off-by: Josh --- src/gui/application.cpp | 66 ++++++++++++++++++++++++++++------------- 1 file changed, 45 insertions(+), 21 deletions(-) diff --git a/src/gui/application.cpp b/src/gui/application.cpp index 387120515e1d7..caf18b33fb118 100644 --- a/src/gui/application.cpp +++ b/src/gui/application.cpp @@ -70,32 +70,56 @@ namespace { static const char optionsC[] = "Options:\n" + "\n" + "General options:\n" " --help, -h : show this help screen.\n" - " --version, -v : show version information.\n" - " -q --quit : quit the running instance\n" + " --version, -v : show client version/build details.\n" + " --quit, -q : quit the running instance.\n" + " --background : launch the client in the background.\n" + " --reverse : use a reverse layout direction.\n" + "\n" + "Logging options:\n" " --logwindow, -l : open a window to show log output.\n" - " --logfile : write log output to file .\n" - " --logdir : write each sync log output in a new file\n" - " in folder .\n" - " --logexpire : removes logs older than hours.\n" - " (to be used with --logdir)\n" - " --logflush : flush the log file after every write.\n" + " --logfile : write sync logs to the specified filename.\n" + " --logdir : write sync logs for each run to a new file\n" + " in the specified directory.\n" + " --logexpire : delete sync logs older than the specified\n" + " number of hours (to be used with --logdir).\n" + " Default: 24 hours.\n" + " --logflush : flush the sync log file after every write.\n" " --logdebug : also output debug-level messages in the log.\n" - " --confdir : Use the given configuration folder.\n" - " --background : launch the application in the background.\n" - " --overrideserverurl : specify a server URL to use for the force override to be used in the account setup wizard.\n" - " --overridelocaldir : specify a local dir to be used in the account setup wizard.\n" - " --userid : userId (username as on the server) to pass when creating an account via command-line.\n" - " --apppassword : appPassword to pass when creating an account via command-line.\n" - " --set-language : specify a language to use for the client, regardless of the OS language.\n" - " --localdirpath : (optional) path where to create a local sync folder when creating an account via command-line.\n" - " --isvfsenabled : whether to set a VFS or non-VFS folder (1 for 'yes' or 0 for 'no') when creating an account via command-line.\n" - " --remotedirpath : (optional) path to a remote subfolder when creating an account via command-line.\n" - " --serverurl : a server URL to use when creating an account via command-line.\n" + "\n" + "Configuration options:\n" + " --confdir : override the default client configuration\n" + " directory.\n" + " --set-language : use this language for the client, regardless\n" + " of the OS language.\n" + " --debug : enable debug mode and debug-level logging.\n" #if !DISABLE_ACCOUNT_MIGRATION - " --forcelegacyconfigimport : forcefully import account configurations from legacy clients (if available).\n" + " --forcelegacyconfigimport : forcefully import account configurations from\n" + " legacy clients (if available).\n" #endif - " --reverse : use a reverse layout direction.\n"; + "\n" + "Account setup wizard options:\n" + " --overrideserverurl : server URL used by the interactive account\n" + " setup wizard.\n" + " --overridelocaldir : local directory used by the interactive\n" + " account setup wizard.\n" + "\n" + "Non-interactive account provisioning options:\n" + " --serverurl : server URL to use during non-interactive\n" + " account provisioning.\n" + " --userid : server-side user ID to use during\n" + " non-interactive account provisioning.\n" + " --apppassword : server-side app password to use during\n" + " non-interactive account provisioning.\n" + " --localdirpath : local sync directory to use during\n" + " non-interactive account provisioning.\n" + " --remotedirpath : server-side directory to set up for syncing\n" + " during non-interactive account provisioning.\n" + " --isvfsenabled <0|1> : toggle virtual files support (1 = yes,\n" + " 0 = no) during non-interactive account\n" + " provisioning.\n"; QString applicationTrPath() { From 1473eff5cf5c77c22ff0b841e7f2e6ea7760d53d Mon Sep 17 00:00:00 2001 From: Josh Date: Wed, 20 May 2026 09:49:21 -0400 Subject: [PATCH 3/8] fix(app): add missing parameter hint to --set-language Signed-off-by: Josh --- src/gui/application.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gui/application.cpp b/src/gui/application.cpp index caf18b33fb118..8cb969d417de9 100644 --- a/src/gui/application.cpp +++ b/src/gui/application.cpp @@ -996,6 +996,8 @@ void Application::parseOptions(const QStringList &options) } else if (option == QStringLiteral("--set-language")) { if (it.hasNext() && !it.peekNext().startsWith(QLatin1String("--"))) { _setLanguage = it.next(); + } else { + showHint("Invalid language passed to --set-language"); } } #if !DISABLE_ACCOUNT_MIGRATION From 091542eb606c6de6619921940d1be2bc975a0882 Mon Sep 17 00:00:00 2001 From: Josh Date: Wed, 20 May 2026 10:22:23 -0400 Subject: [PATCH 4/8] refactor(app): re-order parseOptions() logic by type Signed-off-by: Josh --- src/gui/application.cpp | 68 ++++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 31 deletions(-) diff --git a/src/gui/application.cpp b/src/gui/application.cpp index 8cb969d417de9..14249cc24b641 100644 --- a/src/gui/application.cpp +++ b/src/gui/application.cpp @@ -904,23 +904,42 @@ void Application::slotParseMessage(const QByteArray &message) void Application::parseOptions(const QStringList &options) { QStringListIterator it(options); - // skip file name; + // skip file name if (it.hasNext()) { it.next(); } bool shouldExit = false; - //parse options; if help or bad option exit + // parse options; if help or bad option exit while (it.hasNext()) { QString option = it.next(); + + // Help / version / exit-related if (option == QLatin1String("--help") || option == QLatin1String("-h")) { setHelp(); break; + } else if (option == QLatin1String("--version") || option == QLatin1String("-v")) { + _versionOnly = true; } else if (option == QLatin1String("--quit") || option == QLatin1String("-q")) { _quitInstance = true; + + // Simple boolean toggles } else if (option == QLatin1String("--logwindow") || option == QLatin1String("-l")) { _showLogWindow = true; + } else if (option == QLatin1String("--logflush")) { + _logFlush = true; + } else if (option == QLatin1String("--logdebug")) { + _logDebug = true; + } else if (option == QLatin1String("--background")) { + _backgroundMode = true; + } else if (option == QLatin1String("--reverse")) { + setLayoutDirection(layoutDirection() == Qt::LeftToRight ? Qt::RightToLeft : Qt::LeftToRight); + } else if (option == QLatin1String("--debug")) { + _logDebug = true; + _debugMode = true; + + // Value options } else if (option == QLatin1String("--logfile")) { if (it.hasNext() && !it.peekNext().startsWith(QLatin1String("--"))) { _logFile = it.next(); @@ -939,10 +958,6 @@ void Application::parseOptions(const QStringList &options) } else { showHint("Log expiration not specified"); } - } else if (option == QLatin1String("--logflush")) { - _logFlush = true; - } else if (option == QLatin1String("--logdebug")) { - _logDebug = true; } else if (option == QLatin1String("--confdir")) { if (it.hasNext() && !it.peekNext().startsWith(QLatin1String("--"))) { QString confDir = it.next(); @@ -952,27 +967,6 @@ void Application::parseOptions(const QStringList &options) } else { showHint("Path for confdir not specified"); } - } else if (option == QLatin1String("--debug")) { - _logDebug = true; - _debugMode = true; - } else if (option == QLatin1String("--background")) { - _backgroundMode = true; - } else if (option == QLatin1String("--version") || option == QLatin1String("-v")) { - _versionOnly = true; - } else if (option == QLatin1String("--reverse")) { - setLayoutDirection(layoutDirection() == Qt::LeftToRight ? Qt::RightToLeft : Qt::LeftToRight); - } else if (option.endsWith(QStringLiteral(APPLICATION_DOTVIRTUALFILE_SUFFIX))) { - // virtual file, open it after the Folder were created (if the app is not terminated) - QTimer::singleShot(0, this, [this, option] { openVirtualFile(option); }); - } else if (option.startsWith(QStringLiteral(APPLICATION_URI_HANDLER_SCHEME "://open"))) { - // see the section Local file editing of the Architecture page of the user documentation - _editFileLocallyUrl = QUrl::fromUserInput(option); - if (!_editFileLocallyUrl.isValid()) { - _editFileLocallyUrl.clear(); - const auto errorParsingLocalFileEditingUrl = QStringLiteral("The supplied url for local file editing '%1' is invalid!").arg(option); - qCInfo(lcApplication) << errorParsingLocalFileEditingUrl; - showHint(errorParsingLocalFileEditingUrl.toStdString()); - } } else if (option == QStringLiteral("--overrideserverurl")) { if (it.hasNext() && !it.peekNext().startsWith(QLatin1String("--"))) { const auto overrideUrl = it.next(); @@ -999,13 +993,25 @@ void Application::parseOptions(const QStringList &options) } else { showHint("Invalid language passed to --set-language"); } - } + + // Special startup handlers + } else if (option.endsWith(QStringLiteral(APPLICATION_DOTVIRTUALFILE_SUFFIX))) { + // virtual file, open it after the Folder were created (if the app is not terminated) + QTimer::singleShot(0, this, [this, option] { openVirtualFile(option); }); + } else if (option.startsWith(QStringLiteral(APPLICATION_URI_HANDLER_SCHEME "://open"))) { + // see the section Local file editing of the Architecture page of the user documentation + _editFileLocallyUrl = QUrl::fromUserInput(option); + if (!_editFileLocallyUrl.isValid()) { + _editFileLocallyUrl.clear(); + const auto errorParsingLocalFileEditingUrl = QStringLiteral("The supplied url for local file editing '%1' is invalid!").arg(option); + qCInfo(lcApplication) << errorParsingLocalFileEditingUrl; + showHint(errorParsingLocalFileEditingUrl.toStdString()); + } #if !DISABLE_ACCOUNT_MIGRATION - else if (option == QStringLiteral("--forcelegacyconfigimport")) { + } else if (option == QStringLiteral("--forcelegacyconfigimport")) { AccountManager::instance()->setForceLegacyImport(true); - } #endif - else { + } else { QString errorMessage; if (!AccountSetupCommandLineManager::instance()->parseCommandlineOption(option, it, errorMessage)) { if (!errorMessage.isEmpty()) { From 0bb252bad4bec53a74cd3c98c8d242fa116ca443 Mon Sep 17 00:00:00 2001 From: Josh Date: Wed, 20 May 2026 10:26:39 -0400 Subject: [PATCH 5/8] refactor(app): drop shouldExit dead code showHint() calls exit itself. Signed-off-by: Josh --- src/gui/application.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/gui/application.cpp b/src/gui/application.cpp index 14249cc24b641..b6bf8db57a5c6 100644 --- a/src/gui/application.cpp +++ b/src/gui/application.cpp @@ -974,7 +974,6 @@ void Application::parseOptions(const QStringList &options) && QUrl::fromUserInput(overrideUrl).isValid(); if (!isUrlValid) { showHint("Invalid URL passed to --overrideserverurl"); - shouldExit = true; } else { _overrideServerUrl = overrideUrl; } @@ -1016,15 +1015,11 @@ void Application::parseOptions(const QStringList &options) if (!AccountSetupCommandLineManager::instance()->parseCommandlineOption(option, it, errorMessage)) { if (!errorMessage.isEmpty()) { showHint(errorMessage.toStdString()); - return; } showHint("Unrecognized option '" + option.toStdString() + "'"); } } } - if (shouldExit) { - std::exit(0); - } } // Helpers for displaying messages. Note that there is no console on Windows. From f5cfe12b818983d984483761dfe076fa8d57e66f Mon Sep 17 00:00:00 2001 From: Josh Date: Wed, 20 May 2026 10:54:09 -0400 Subject: [PATCH 6/8] refactor(app): a tiny bit more parseOptions output polish Signed-off-by: Josh --- src/gui/application.cpp | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/gui/application.cpp b/src/gui/application.cpp index b6bf8db57a5c6..bb2627097db35 100644 --- a/src/gui/application.cpp +++ b/src/gui/application.cpp @@ -909,8 +909,6 @@ void Application::parseOptions(const QStringList &options) it.next(); } - bool shouldExit = false; - // parse options; if help or bad option exit while (it.hasNext()) { QString option = it.next(); @@ -934,6 +932,7 @@ void Application::parseOptions(const QStringList &options) } else if (option == QLatin1String("--background")) { _backgroundMode = true; } else if (option == QLatin1String("--reverse")) { + // FIXME: This is current implemented as a toggle, but help text suggests it should set RTL setLayoutDirection(layoutDirection() == Qt::LeftToRight ? Qt::RightToLeft : Qt::LeftToRight); } else if (option == QLatin1String("--debug")) { _logDebug = true; @@ -954,7 +953,12 @@ void Application::parseOptions(const QStringList &options) } } else if (option == QLatin1String("--logexpire")) { if (it.hasNext() && !it.peekNext().startsWith(QLatin1String("--"))) { - _logExpire = it.next().toInt(); + bool ok = false; + const auto logExpire = it.next().toInt(&ok); + if (!ok) { + showHint("Invalid expiration value passed to --logexpire"); + } + _logExpire = logExpire; } else { showHint("Log expiration not specified"); } @@ -970,27 +974,29 @@ void Application::parseOptions(const QStringList &options) } else if (option == QStringLiteral("--overrideserverurl")) { if (it.hasNext() && !it.peekNext().startsWith(QLatin1String("--"))) { const auto overrideUrl = it.next(); - const auto isUrlValid = (overrideUrl.startsWith(QStringLiteral("http://")) || overrideUrl.startsWith(QStringLiteral("https://"))) + const auto isUrlValid = + (overrideUrl.startsWith(QStringLiteral("http://")) + || overrideUrl.startsWith(QStringLiteral("https://"))) && QUrl::fromUserInput(overrideUrl).isValid(); if (!isUrlValid) { showHint("Invalid URL passed to --overrideserverurl"); - } else { - _overrideServerUrl = overrideUrl; } + _overrideServerUrl = overrideUrl; } else { - showHint("Invalid URL passed to --overrideserverurl"); + showHint("Missing value for --overrideserverurl"); } } else if (option == QStringLiteral("--overridelocaldir")) { if (it.hasNext() && !it.peekNext().startsWith(QLatin1String("--"))) { + // FIXME: validate specified path _overrideLocalDir = it.next(); } else { - showHint("Invalid path passed to --overridelocaldir"); + showHint("Missing value for --overridelocaldir"); } } else if (option == QStringLiteral("--set-language")) { if (it.hasNext() && !it.peekNext().startsWith(QLatin1String("--"))) { _setLanguage = it.next(); } else { - showHint("Invalid language passed to --set-language"); + showHint("Missing value for --set-language"); } // Special startup handlers @@ -1002,7 +1008,8 @@ void Application::parseOptions(const QStringList &options) _editFileLocallyUrl = QUrl::fromUserInput(option); if (!_editFileLocallyUrl.isValid()) { _editFileLocallyUrl.clear(); - const auto errorParsingLocalFileEditingUrl = QStringLiteral("The supplied url for local file editing '%1' is invalid!").arg(option); + const auto errorParsingLocalFileEditingUrl = + QStringLiteral("The supplied url for local file editing '%1' is invalid!").arg(option); qCInfo(lcApplication) << errorParsingLocalFileEditingUrl; showHint(errorParsingLocalFileEditingUrl.toStdString()); } From 3216f1bddfeae0dcb6e18bc8858ec3f73591709f Mon Sep 17 00:00:00 2001 From: Josh Date: Wed, 20 May 2026 11:19:52 -0400 Subject: [PATCH 7/8] refactor(app): add small helpers for parseOptions Signed-off-by: Josh --- src/gui/application.cpp | 67 +++++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 29 deletions(-) diff --git a/src/gui/application.cpp b/src/gui/application.cpp index bb2627097db35..c7da5c5cb0173 100644 --- a/src/gui/application.cpp +++ b/src/gui/application.cpp @@ -909,70 +909,79 @@ void Application::parseOptions(const QStringList &options) it.next(); } + const auto hasNextValue = [&it]() { + return it.hasNext() && !it.peekNext().startsWith(QLatin1String("--")); + }; + + const auto isOption = [](const QString &option, const char *name, const char *shortName = nullptr) { + return option == QLatin1String(name) + || (shortName && option == QLatin1String(shortName)); + }; + // parse options; if help or bad option exit while (it.hasNext()) { QString option = it.next(); // Help / version / exit-related - if (option == QLatin1String("--help") || option == QLatin1String("-h")) { + if (isOption(option, "--help", "-h")) { setHelp(); break; - } else if (option == QLatin1String("--version") || option == QLatin1String("-v")) { + } else if (isOption(option, "--version", "-v")) { _versionOnly = true; - } else if (option == QLatin1String("--quit") || option == QLatin1String("-q")) { + } else if (isOption(option, "--quit", "-q")) { _quitInstance = true; // Simple boolean toggles - } else if (option == QLatin1String("--logwindow") || option == QLatin1String("-l")) { + } else if (isOption(option, "--logwindow", "-l")) { _showLogWindow = true; - } else if (option == QLatin1String("--logflush")) { + } else if (isOption(option, "--logflush")) { _logFlush = true; - } else if (option == QLatin1String("--logdebug")) { + } else if (isOption(option, "--logdebug")) { _logDebug = true; - } else if (option == QLatin1String("--background")) { + } else if (isOption(option, "--background")) { _backgroundMode = true; - } else if (option == QLatin1String("--reverse")) { + } else if (isOption(option, "--reverse")) { // FIXME: This is current implemented as a toggle, but help text suggests it should set RTL setLayoutDirection(layoutDirection() == Qt::LeftToRight ? Qt::RightToLeft : Qt::LeftToRight); - } else if (option == QLatin1String("--debug")) { + } else if (isOption(option, "--debug")) { _logDebug = true; _debugMode = true; // Value options - } else if (option == QLatin1String("--logfile")) { - if (it.hasNext() && !it.peekNext().startsWith(QLatin1String("--"))) { + } else if (isOption(option, "--logfile")) { + if (hasNextValue()) { _logFile = it.next(); } else { - showHint("Log file not specified"); + showHint("Missing value for --logfile"); } - } else if (option == QLatin1String("--logdir")) { - if (it.hasNext() && !it.peekNext().startsWith(QLatin1String("--"))) { + } else if (isOption(option, "--logdir")) { + if (hasNextValue()) { _logDir = it.next(); } else { - showHint("Log dir not specified"); + showHint("Missing value for --logdir"); } - } else if (option == QLatin1String("--logexpire")) { - if (it.hasNext() && !it.peekNext().startsWith(QLatin1String("--"))) { + } else if (isOption(option, "--logexpire")) { + if if (hasNextValue()) { bool ok = false; const auto logExpire = it.next().toInt(&ok); if (!ok) { - showHint("Invalid expiration value passed to --logexpire"); + showHint("Invalid value passed to --logexpire"); } _logExpire = logExpire; } else { - showHint("Log expiration not specified"); + showHint("Missing value for --logexpire"); } - } else if (option == QLatin1String("--confdir")) { - if (it.hasNext() && !it.peekNext().startsWith(QLatin1String("--"))) { + } else if (isOption(option, "--confdir")) { + if (hasNextValue()) { QString confDir = it.next(); if (!ConfigFile::setConfDir(confDir)) { showHint("Invalid path passed to --confdir"); } } else { - showHint("Path for confdir not specified"); + showHint("Missing value for --confdir"); } - } else if (option == QStringLiteral("--overrideserverurl")) { - if (it.hasNext() && !it.peekNext().startsWith(QLatin1String("--"))) { + } else if (isOption(option, "--overrideserverurl")) { + if (hasNextValue()) { const auto overrideUrl = it.next(); const auto isUrlValid = (overrideUrl.startsWith(QStringLiteral("http://")) @@ -985,15 +994,15 @@ void Application::parseOptions(const QStringList &options) } else { showHint("Missing value for --overrideserverurl"); } - } else if (option == QStringLiteral("--overridelocaldir")) { - if (it.hasNext() && !it.peekNext().startsWith(QLatin1String("--"))) { + } else if (isOption(option, "--overridelocaldir")) { + if (hasNextValue()) { // FIXME: validate specified path _overrideLocalDir = it.next(); } else { showHint("Missing value for --overridelocaldir"); } - } else if (option == QStringLiteral("--set-language")) { - if (it.hasNext() && !it.peekNext().startsWith(QLatin1String("--"))) { + } else if (isOption(option, "--set-language")) { + if (hasNextValue()) { _setLanguage = it.next(); } else { showHint("Missing value for --set-language"); @@ -1014,7 +1023,7 @@ void Application::parseOptions(const QStringList &options) showHint(errorParsingLocalFileEditingUrl.toStdString()); } #if !DISABLE_ACCOUNT_MIGRATION - } else if (option == QStringLiteral("--forcelegacyconfigimport")) { + } else if (isOption(option, "--forcelegacyconfigimport")) { AccountManager::instance()->setForceLegacyImport(true); #endif } else { From 7ceba7e7683ae03bf28528738b0bba0432b0ce20 Mon Sep 17 00:00:00 2001 From: Josh Date: Wed, 20 May 2026 11:27:00 -0400 Subject: [PATCH 8/8] chore(app): fixup typo Signed-off-by: Josh --- src/gui/application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/application.cpp b/src/gui/application.cpp index c7da5c5cb0173..52bd7d4f8808e 100644 --- a/src/gui/application.cpp +++ b/src/gui/application.cpp @@ -961,7 +961,7 @@ void Application::parseOptions(const QStringList &options) showHint("Missing value for --logdir"); } } else if (isOption(option, "--logexpire")) { - if if (hasNextValue()) { + if (hasNextValue()) { bool ok = false; const auto logExpire = it.next().toInt(&ok); if (!ok) {