desktop/source/app/app.cxx             |    7 ++++++-
 desktop/source/app/cmdlineargs.hxx     |    7 +++++++
 desktop/source/app/dispatchwatcher.cxx |   13 ++++++++++++-
 desktop/source/app/dispatchwatcher.hxx |   20 +++++++++++++++++++-
 desktop/source/app/officeipcthread.cxx |   12 +++++-------
 desktop/source/app/officeipcthread.hxx |    6 ++++--
 6 files changed, 53 insertions(+), 12 deletions(-)

New commits:
commit 454d8db90e0f5062def3c83930b2c7e88541fed9
Author:     Justin Luth <jl...@mail.com>
AuthorDate: Thu Jun 26 10:12:20 2025 -0400
Commit:     Mike Kaganski <mike.kagan...@collabora.com>
CommitDate: Thu Jul 3 11:47:52 2025 +0200

    tdf#148275: EXIT_FAILURE if any cmdline operation fails
    
    While by no means comprehensive,
    this patch fulfills the bug request
    by returning error status 1 (instead of 0)
    if REQUEST_CONVERSION (and some others) fail for various reasons.
    
    instdir/program/soffice  --convert-to junk  non-existant-file.ods
    echo "status is $?"
    
    -EXIT_FAILURE b/c non-existant-file.ods not found
    -EXIT_FAILURE b/c junk is not a valid filter
    -EXIT_FAILURE if xStorable->storeToURL raises an exception
    
    Change-Id: Ic1d25034fa152cdc4933ce3cca06def204f764a6
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/187075
    Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com>
    Reviewed-by: Justin Luth <jl...@mail.com>
    Tested-by: Jenkins
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/187278
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>

diff --git a/desktop/source/app/app.cxx b/desktop/source/app/app.cxx
index 2461a05fa145..adc254da541e 100644
--- a/desktop/source/app/app.cxx
+++ b/desktop/source/app/app.cxx
@@ -1788,7 +1788,7 @@ int Desktop::doShutdown()
 
         return EXITHELPER_NORMAL_RESTART;
     }
-    return EXIT_SUCCESS;
+    return rCmdLineArgs.GetAllSucceeded() ? EXIT_SUCCESS : EXIT_FAILURE;
 }
 
 IMPL_STATIC_LINK( Desktop, ImplInitFilterHdl, ::ConvertData&, rData, bool )
@@ -2215,8 +2215,13 @@ void Desktop::OpenClients()
         }
 #endif
         // Process request
+        DispatchRequestFlags eFlags = DispatchRequestFlags::NONE;
+        aRequest.mpFlags = &eFlags;
         if ( RequestHandler::ExecuteCmdLineRequests(aRequest, false) )
         {
+            if (eFlags & DispatchRequestFlags::WithError)
+                Desktop::GetCommandLineArgs().SetAllSucceeded(false);
+
             // Don't do anything if we have successfully called terminate at 
desktop:
             return;
         }
diff --git a/desktop/source/app/cmdlineargs.hxx 
b/desktop/source/app/cmdlineargs.hxx
index d5d94abd67f2..2f533e728dd0 100644
--- a/desktop/source/app/cmdlineargs.hxx
+++ b/desktop/source/app/cmdlineargs.hxx
@@ -122,6 +122,9 @@ class CommandLineArgs
 
         void setHeadless() { m_headless = true; }
 
+        bool GetAllSucceeded() const { return m_bAllSucceeded; }
+        void SetAllSucceeded(bool bSet) { m_bAllSucceeded = bSet; }
+
     private:
         void                    ParseCommandLine_Impl( Supplier& supplier );
         void                    InitParamValues();
@@ -182,6 +185,10 @@ class CommandLineArgs
         std::vector< OUString > m_infilter;
         OUString m_language;
         OUString m_pidfile;
+
+        // If any argument fails, this may be set to false so that the app 
exits with EXIT_FAILURE.
+        // NOTE: only items of interest are setting this to false - can be 
extended to more cases...
+        bool m_bAllSucceeded = true;
 };
 
 }
diff --git a/desktop/source/app/dispatchwatcher.cxx 
b/desktop/source/app/dispatchwatcher.cxx
index faf830ffae3c..39bd64db53f7 100644
--- a/desktop/source/app/dispatchwatcher.cxx
+++ b/desktop/source/app/dispatchwatcher.cxx
@@ -334,7 +334,9 @@ DispatchWatcher::~DispatchWatcher()
 }
 
 
-bool DispatchWatcher::executeDispatchRequests( const 
std::vector<DispatchRequest>& aDispatchRequestsList, bool bNoTerminate )
+bool DispatchWatcher::executeDispatchRequests(
+    const std::vector<DispatchRequest>& aDispatchRequestsList, bool 
bNoTerminate,
+    DispatchRequestFlags* pFlags)
 {
     Reference< XDesktop2 > xDesktop = css::frame::Desktop::create( 
::comphelper::getProcessComponentContext() );
 
@@ -636,6 +638,8 @@ bool DispatchWatcher::executeDispatchRequests( const 
std::vector<DispatchRequest
                             if (aFilter.isEmpty())
                             {
                                 std::cerr << "Error: no export filter" << 
std::endl;
+                                if (pFlags)
+                                    *pFlags |= DispatchRequestFlags::WithError;
                             }
                             else
                             {
@@ -720,6 +724,8 @@ bool DispatchWatcher::executeDispatchRequests( const 
std::vector<DispatchRequest
                                     if (!rException.Message.isEmpty())
                                         std::cerr << " (" << 
rException.Message << ")";
                                     std::cerr << std::endl;
+                                    if (pFlags)
+                                        *pFlags |= 
DispatchRequestFlags::WithError;
                                 }
 
                                 if (fileForCat && fileForCat->IsValid())
@@ -768,6 +774,8 @@ bool DispatchWatcher::executeDispatchRequests( const 
std::vector<DispatchRequest
                 else
                 {
                     std::cerr << "Error: source file could not be loaded" << 
std::endl;
+                    if (pFlags)
+                        *pFlags |= DispatchRequestFlags::WithError;
                 }
 
                 // remove the document
@@ -815,6 +823,9 @@ bool DispatchWatcher::executeDispatchRequests( const 
std::vector<DispatchRequest
         }
     }
 
+    if (pFlags)
+        *pFlags |= DispatchRequestFlags::Finished;
+
     bool bEmpty = (m_nRequestCount == 0);
 
     // No more asynchronous requests?
diff --git a/desktop/source/app/dispatchwatcher.hxx 
b/desktop/source/app/dispatchwatcher.hxx
index 9a970cf82dce..a6b53a74d2a1 100644
--- a/desktop/source/app/dispatchwatcher.hxx
+++ b/desktop/source/app/dispatchwatcher.hxx
@@ -28,6 +28,23 @@
 namespace desktop
 {
 
+enum class DispatchRequestFlags
+{
+    NONE = 0x0000,
+    Finished = 0x0001,
+    WithError = 0x0002, // one or more requests reported an error condition
+};
+
+}
+
+namespace o3tl
+{
+    template<> struct typed_flags<desktop::DispatchRequestFlags> : 
is_typed_flags<desktop::DispatchRequestFlags, 0x3> {};
+}
+
+namespace desktop
+{
+
 /*
     Class for controls dispatching of command URL through office command line. 
There
     are "dangerous" command URLs, that can result in a running office without 
UI. To prevent
@@ -74,7 +91,8 @@ class DispatchWatcher : public ::cppu::WeakImplHelper< 
css::frame::XDispatchResu
         virtual void SAL_CALL dispatchFinished( const 
css::frame::DispatchResultEvent& aEvent ) override;
 
         // execute new dispatch request
-        bool executeDispatchRequests( const std::vector<DispatchRequest>& 
aDispatches, bool bNoTerminate );
+        bool executeDispatchRequests(const std::vector<DispatchRequest>& 
aDispatches,
+                                     bool bNoTerminate, DispatchRequestFlags* 
pFlags);
 
     private:
 
diff --git a/desktop/source/app/officeipcthread.cxx 
b/desktop/source/app/officeipcthread.cxx
index 4029546fd666..6a18a6e6fb48 100644
--- a/desktop/source/app/officeipcthread.cxx
+++ b/desktop/source/app/officeipcthread.cxx
@@ -26,7 +26,6 @@
 #include <app.hxx>
 #include "officeipcthread.hxx"
 #include "cmdlineargs.hxx"
-#include "dispatchwatcher.hxx"
 #include <com/sun/star/frame/TerminationVetoException.hpp>
 #include <salhelper/thread.hxx>
 #include <sal/log.hxx>
@@ -970,8 +969,8 @@ bool IpcThread::process(OString const & arguments, bool * 
waitProcessed) {
             aCmdLineArgs->getCwdUrl()));
         m_handler->cProcessed.reset();
         pRequest->pcProcessed = &m_handler->cProcessed;
-        m_handler->mbSuccess = false;
-        pRequest->mpbSuccess = &m_handler->mbSuccess;
+        m_handler->mFlags = DispatchRequestFlags::NONE;
+        pRequest->mpFlags = &m_handler->mFlags;
 
         // Print requests are not dependent on the --invisible cmdline 
argument as they are
         // loaded with the "hidden" flag! So they are always checked.
@@ -1175,7 +1174,7 @@ void PipeIpcThread::execute()
             if (waitProcessed)
             {
                 m_handler->cProcessed.wait();
-                bSuccess = m_handler->mbSuccess;
+                bSuccess = static_cast<bool>(m_handler->mFlags & 
DispatchRequestFlags::Finished);
             }
             if (bSuccess)
             {
@@ -1344,9 +1343,8 @@ bool RequestHandler::ExecuteCmdLineRequests(
         aGuard.clear();
 
         // Execute dispatch requests
-        bShutdown = dispatchWatcher->executeDispatchRequests( aTempList, 
noTerminate);
-        if (aRequest.mpbSuccess)
-            *aRequest.mpbSuccess = true; // signal that we have actually 
succeeded
+        bShutdown
+            = dispatchWatcher->executeDispatchRequests(aTempList, noTerminate, 
aRequest.mpFlags);
     }
 
     return bShutdown;
diff --git a/desktop/source/app/officeipcthread.hxx 
b/desktop/source/app/officeipcthread.hxx
index fc95b42668bc..3f7f4c00d602 100644
--- a/desktop/source/app/officeipcthread.hxx
+++ b/desktop/source/app/officeipcthread.hxx
@@ -34,6 +34,8 @@
 #include <osl/conditn.hxx>
 #include <optional>
 
+#include "dispatchwatcher.hxx"
+
 namespace desktop
 {
 
@@ -63,7 +65,7 @@ struct ProcessDocumentsRequest
     OUString aStartListParams;
     std::vector< OUString > aInFilter;
     ::osl::Condition *pcProcessed;  // pointer condition to be set when the 
request has been processed
-    bool* mpbSuccess = nullptr; // pointer to boolean receiving if the 
processing was successful
+    DispatchRequestFlags* mpFlags = nullptr; // pointer to 
DispatchRequestFlags receiving if the processing was successful
     bool bTextCat; // boolean flag indicating whether to dump text content to 
console
     bool bScriptCat; // boolean flag indicating whether to dump script content 
to console
 };
@@ -92,7 +94,7 @@ class RequestHandler: public salhelper::SimpleReferenceObject
     /* condition to be set when the request has been processed */
     ::osl::Condition cProcessed;
     /* receives if the processing was successful (may be false e.g. when 
shutting down) */
-    bool mbSuccess = false;
+    DispatchRequestFlags mFlags = DispatchRequestFlags::NONE;
 
     /* condition to be set when the main event loop is ready
        otherwise an error dialogs event loop could eat away

Reply via email to