Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / desktop / source / app / cmdlineargs.cxx
blob381147cd534eda1326d085867212803cdfa94c10
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <config_features.h>
22 #if HAVE_FEATURE_MACOSX_SANDBOX
23 #include <premac.h>
24 #include <Foundation/Foundation.h>
25 #include <postmac.h>
26 #endif
28 #include "cmdlineargs.hxx"
29 #include <tools/stream.hxx>
30 #include <vcl/svapp.hxx>
31 #include <rtl/uri.hxx>
32 #include <rtl/ustring.hxx>
33 #include <rtl/process.h>
34 #include <comphelper/lok.hxx>
35 #include <comphelper/processfactory.hxx>
36 #include <com/sun/star/uri/ExternalUriReferenceTranslator.hpp>
37 #include <unotools/bootstrap.hxx>
39 #include <svl/documentlockfile.hxx>
41 #include <rtl/strbuf.hxx>
42 #include <osl/file.hxx>
43 #include <sal/log.hxx>
45 using namespace com::sun::star::lang;
46 using namespace com::sun::star::uri;
47 using namespace com::sun::star::uno;
49 namespace desktop
52 namespace {
54 OUString translateExternalUris(OUString const & input) {
55 OUString t(
56 css::uri::ExternalUriReferenceTranslator::create(
57 comphelper::getProcessComponentContext())->
58 translateToInternal(input));
59 return t.isEmpty() ? input : t;
62 std::vector< OUString > translateExternalUris(
63 std::vector< OUString > const & input)
65 std::vector< OUString > t;
66 t.reserve(input.size());
67 for (auto const& elem : input)
69 t.push_back(translateExternalUris(elem));
71 return t;
74 class ExtCommandLineSupplier: public CommandLineArgs::Supplier {
75 public:
76 explicit ExtCommandLineSupplier():
77 m_count(
78 comphelper::LibreOfficeKit::isActive()
79 ? 0 : rtl_getAppCommandArgCount()),
80 m_index(0)
82 OUString url;
83 if (utl::Bootstrap::getProcessWorkingDir(url)) {
84 m_cwdUrl.reset(url);
88 virtual boost::optional< OUString > getCwdUrl() override { return m_cwdUrl; }
90 virtual bool next(OUString * argument) override {
91 OSL_ASSERT(argument != nullptr);
92 if (m_index < m_count) {
93 rtl_getAppCommandArg(m_index++, &argument->pData);
94 return true;
95 } else {
96 return false;
100 private:
101 boost::optional< OUString > m_cwdUrl;
102 sal_uInt32 m_count;
103 sal_uInt32 m_index;
106 enum class CommandLineEvent {
107 Open, Print, View, Start, PrintTo,
108 ForceOpen, ForceNew, Conversion, BatchPrint
111 // Office URI Schemes: see https://msdn.microsoft.com/en-us/library/dn906146
112 // This functions checks if the arg is an Office URI.
113 // If applicable, it updates arg to inner URI.
114 // If no event argument is explicitly set in command line,
115 // then it returns updated command line event,
116 // according to Office URI command.
117 CommandLineEvent CheckOfficeURI(/* in,out */ OUString& arg, CommandLineEvent curEvt)
119 // 1. Strip the scheme name
120 OUString rest1;
121 bool isOfficeURI = ( arg.startsWithIgnoreAsciiCase("vnd.libreoffice.command:", &rest1) // Proposed extended schema
122 || arg.startsWithIgnoreAsciiCase("ms-word:", &rest1)
123 || arg.startsWithIgnoreAsciiCase("ms-powerpoint:", &rest1)
124 || arg.startsWithIgnoreAsciiCase("ms-excel:", &rest1)
125 || arg.startsWithIgnoreAsciiCase("ms-visio:", &rest1)
126 || arg.startsWithIgnoreAsciiCase("ms-access:", &rest1));
127 if (!isOfficeURI)
128 return curEvt;
130 OUString rest2;
131 long nURIlen = -1;
133 // URL might be encoded
134 OUString decoded_rest = rest1.replaceAll("%7C", "|").replaceAll("%7c", "|");
136 // 2. Discriminate by command name (incl. 1st command argument descriptor)
137 // Extract URI: everything up to possible next argument
138 if (decoded_rest.startsWith("ofv|u|", &rest2))
140 // Open for view - override only in default mode
141 if (curEvt == CommandLineEvent::Open)
142 curEvt = CommandLineEvent::View;
143 nURIlen = rest2.indexOf("|");
145 else if (decoded_rest.startsWith("ofe|u|", &rest2))
147 // Open for editing - override only in default mode
148 if (curEvt == CommandLineEvent::Open)
149 curEvt = CommandLineEvent::ForceOpen;
150 nURIlen = rest2.indexOf("|");
152 else if (decoded_rest.startsWith("nft|u|", &rest2))
154 // New from template - override only in default mode
155 if (curEvt == CommandLineEvent::Open)
156 curEvt = CommandLineEvent::ForceNew;
157 nURIlen = rest2.indexOf("|");
158 // TODO: process optional second argument (default save-to location)
159 // For now, we just ignore it
161 else
163 // Abbreviated scheme: <scheme-name>:URI
164 // "ofv|u|" implied
165 // override only in default mode
166 if (curEvt == CommandLineEvent::Open)
167 curEvt = CommandLineEvent::View;
168 rest2 = rest1;
170 if (nURIlen < 0)
171 nURIlen = rest2.getLength();
172 arg = rest2.copy(0, nURIlen);
173 return curEvt;
176 // Skip single newline (be it *NIX LF, MacOS CR, of Win CRLF)
177 // Changes the offset, and returns true if moved
178 bool SkipNewline(const char* & pStr)
180 if ((*pStr != '\r') && (*pStr != '\n'))
181 return false;
182 if (*pStr == '\r')
183 ++pStr;
184 if (*pStr == '\n')
185 ++pStr;
186 return true;
189 // Web query: http://support.microsoft.com/kb/157482
190 CommandLineEvent CheckWebQuery(/* in,out */ OUString& arg, CommandLineEvent curEvt)
192 // Only handle files with extension .iqy
193 if (!arg.endsWithIgnoreAsciiCase(".iqy"))
194 return curEvt;
196 static osl::Mutex aMutex;
197 osl::MutexGuard aGuard(aMutex);
201 OUString sFileURL;
202 // Cannot use translateExternalUris yet, because process service factory is not yet available
203 if (osl::FileBase::getFileURLFromSystemPath(arg, sFileURL) != osl::FileBase::RC::E_None)
204 return curEvt;
205 SvFileStream stream(sFileURL, StreamMode::READ);
207 const sal_Int32 nBufLen = 32000;
208 char sBuffer[nBufLen];
209 size_t nRead = stream.ReadBytes(sBuffer, nBufLen);
210 if (nRead < 8) // WEB\n1\n...
211 return curEvt;
213 const char* pPos = sBuffer;
214 if (strncmp(pPos, "WEB", 3) != 0)
215 return curEvt;
216 pPos += 3;
217 if (!SkipNewline(pPos))
218 return curEvt;
219 if (*pPos != '1')
220 return curEvt;
221 ++pPos;
222 if (!SkipNewline(pPos))
223 return curEvt;
225 OStringBuffer aResult(static_cast<unsigned int>(nRead));
228 const char* pPos1 = pPos;
229 const char* pEnd = sBuffer + nRead;
230 while ((pPos1 < pEnd) && (*pPos1 != '\r') && (*pPos1 != '\n'))
231 ++pPos1;
232 aResult.append(pPos, pPos1 - pPos);
233 if (pPos1 < pEnd) // newline
234 break;
235 pPos = sBuffer;
236 } while ((nRead = stream.ReadBytes(sBuffer, nBufLen)) > 0);
238 stream.Close();
240 arg = OStringToOUString(aResult.makeStringAndClear(), osl_getThreadTextEncoding());
241 return CommandLineEvent::ForceNew;
243 catch (...)
245 SAL_WARN("desktop.app", "An error processing Web Query file: " << arg);
248 return curEvt;
251 } // namespace
253 CommandLineArgs::Supplier::Exception::Exception() {}
255 CommandLineArgs::Supplier::Exception::Exception(Exception const &) {}
257 CommandLineArgs::Supplier::Exception::~Exception() {}
259 CommandLineArgs::Supplier::Exception &
260 CommandLineArgs::Supplier::Exception::operator =(Exception const &)
261 { return *this; }
263 CommandLineArgs::Supplier::~Supplier() {}
265 // initialize class with command line parameters from process environment
266 CommandLineArgs::CommandLineArgs()
268 InitParamValues();
269 ExtCommandLineSupplier s;
270 ParseCommandLine_Impl( s );
273 CommandLineArgs::CommandLineArgs( Supplier& supplier )
275 InitParamValues();
276 ParseCommandLine_Impl( supplier );
279 void CommandLineArgs::ParseCommandLine_Impl( Supplier& supplier )
281 m_cwdUrl = supplier.getCwdUrl();
282 CommandLineEvent eCurrentEvent = CommandLineEvent::Open;
284 for (;;)
286 OUString aArg;
287 if ( !supplier.next( &aArg ) )
289 break;
292 if ( !aArg.isEmpty() )
294 m_bEmpty = false;
295 OUString oArg;
296 OUString oDeprecatedArg;
297 if (!aArg.startsWith("--", &oArg) && aArg.startsWith("-", &oArg)
298 && aArg.getLength() > 2) // -h, -?, -n, -o, -p are still valid
300 oDeprecatedArg = aArg; // save here, since aArg can change later
303 OUString rest;
304 if ( oArg == "minimized" )
306 m_minimized = true;
308 else if ( oArg == "invisible" )
310 m_invisible = true;
312 else if ( oArg == "norestore" )
314 m_norestore = true;
316 else if ( oArg == "nodefault" )
318 m_nodefault = true;
320 else if ( oArg == "headless" )
322 setHeadless();
324 else if ( oArg == "eventtesting" )
326 m_eventtesting = true;
328 else if ( oArg == "safe-mode" )
330 m_safemode = true;
332 else if ( oArg == "cat" )
334 m_textcat = true;
335 m_conversionparams = "txt:Text";
336 eCurrentEvent = CommandLineEvent::Conversion;
337 setHeadless();
339 else if ( oArg == "script-cat" )
341 m_scriptcat = true;
342 eCurrentEvent = CommandLineEvent::Conversion;
343 setHeadless();
345 else if ( oArg == "quickstart" )
347 #if defined(ENABLE_QUICKSTART_APPLET)
348 m_quickstart = true;
349 #endif
350 m_noquickstart = false;
352 else if ( oArg == "quickstart=no" )
354 m_noquickstart = true;
355 m_quickstart = false;
357 else if ( oArg == "terminate_after_init" )
359 m_terminateafterinit = true;
361 else if ( oArg == "nofirststartwizard" )
363 // Do nothing, accept only for backward compatibility
365 else if ( oArg == "nologo" )
367 m_nologo = true;
369 #if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
370 else if ( oArg == "nolockcheck" )
372 m_nolockcheck = true;
374 #endif
375 else if ( oArg == "help" || aArg == "-h" || aArg == "-?" )
377 m_help = true;
379 else if ( oArg == "helpwriter" )
381 m_helpwriter = true;
383 else if ( oArg == "helpcalc" )
385 m_helpcalc = true;
387 else if ( oArg == "helpdraw" )
389 m_helpdraw = true;
391 else if ( oArg == "helpimpress" )
393 m_helpimpress = true;
395 else if ( oArg == "helpbase" )
397 m_helpbase = true;
399 else if ( oArg == "helpbasic" )
401 m_helpbasic = true;
403 else if ( oArg == "helpmath" )
405 m_helpmath = true;
407 else if ( oArg == "protector" )
409 // Not relevant for us here, but can be used in unit tests.
410 // Usually unit tests would not end up here, but e.g. the
411 // LOK Tiled Rendering tests end up running a full soffice
412 // process, and we can't bail on the use of --protector.
414 // We specifically need to consume the following 2 arguments
415 // for --protector
416 if ((!supplier.next(&aArg) || !supplier.next(&aArg)) && m_unknown.isEmpty())
417 m_unknown = "--protector must be followed by two arguments";
419 else if ( oArg == "version" )
421 m_version = true;
423 else if ( oArg.startsWith("splash-pipe=") )
425 m_splashpipe = true;
427 #ifdef MACOSX
428 /* #i84053# ignore -psn on Mac
429 Platform dependent #ifdef here is ugly, however this is currently
430 the only platform dependent parameter. Should more appear
431 we should find a better solution
433 else if ( aArg.startsWith("-psn") )
435 oDeprecatedArg.clear();
437 #endif
438 #if HAVE_FEATURE_MACOSX_SANDBOX
439 else if ( oArg == "nstemporarydirectory" )
441 printf("%s\n", [NSTemporaryDirectory() UTF8String]);
442 exit(0);
444 #endif
445 #ifdef _WIN32
446 /* fdo#57203 ignore -Embedding on Windows
447 when LibreOffice is launched by COM+
449 else if ( oArg == "Embedding" )
451 oDeprecatedArg.clear();
453 #endif
454 else if ( oArg.startsWith("infilter=", &rest))
456 m_infilter.push_back(rest);
458 else if ( oArg.startsWith("accept=", &rest))
460 m_accept.push_back(rest);
462 else if ( oArg.startsWith("unaccept=", &rest))
464 m_unaccept.push_back(rest);
466 else if ( oArg.startsWith("language=", &rest))
468 m_language = rest;
470 else if ( oArg.startsWith("pidfile=", &rest))
472 m_pidfile = rest;
474 else if ( oArg == "writer" )
476 m_writer = true;
477 m_bDocumentArgs = true;
479 else if ( oArg == "calc" )
481 m_calc = true;
482 m_bDocumentArgs = true;
484 else if ( oArg == "draw" )
486 m_draw = true;
487 m_bDocumentArgs = true;
489 else if ( oArg == "impress" )
491 m_impress = true;
492 m_bDocumentArgs = true;
494 else if ( oArg == "base" )
496 m_base = true;
497 m_bDocumentArgs = true;
499 else if ( oArg == "global" )
501 m_global = true;
502 m_bDocumentArgs = true;
504 else if ( oArg == "math" )
506 m_math = true;
507 m_bDocumentArgs = true;
509 else if ( oArg == "web" )
511 m_web = true;
512 m_bDocumentArgs = true;
514 else if ( aArg == "-n" )
516 // force new documents based on the following documents
517 eCurrentEvent = CommandLineEvent::ForceNew;
519 else if ( aArg == "-o" )
521 // force open documents regardless if they are templates or not
522 eCurrentEvent = CommandLineEvent::ForceOpen;
524 else if ( oArg == "pt" )
526 // Print to special printer
527 eCurrentEvent = CommandLineEvent::PrintTo;
528 // first argument after "-pt" must be the printer name
529 if (supplier.next(&aArg))
530 m_printername = aArg;
531 else if (m_unknown.isEmpty())
532 m_unknown = "--pt must be followed by printername";
534 else if ( aArg == "-p" )
536 // Print to default printer
537 eCurrentEvent = CommandLineEvent::Print;
539 else if ( oArg == "view")
541 // open in viewmode
542 eCurrentEvent = CommandLineEvent::View;
544 else if ( oArg == "show" )
546 // open in viewmode
547 eCurrentEvent = CommandLineEvent::Start;
549 else if ( oArg == "display" )
551 // The command line argument following --display should
552 // always be treated as the argument of --display.
553 // --display and its argument are handled "out of line"
554 // in Unix-only desktop/unx/source/splashx.c and vcl/unx/*,
555 // and just ignored here
556 (void)supplier.next(&aArg);
558 else if ( oArg == "convert-to" )
560 eCurrentEvent = CommandLineEvent::Conversion;
561 // first argument must be the params
562 if (supplier.next(&aArg))
564 m_conversionparams = aArg;
565 // It doesn't make sense to use convert-to without headless.
566 setHeadless();
568 else if (m_unknown.isEmpty())
569 m_unknown = "--convert-to must be followed by output_file_extension[:output_filter_name]";
571 else if ( oArg == "print-to-file" )
573 eCurrentEvent = CommandLineEvent::BatchPrint;
575 else if ( oArg == "printer-name" )
577 if (eCurrentEvent == CommandLineEvent::BatchPrint)
579 // first argument is the printer name
580 if (supplier.next(&aArg))
581 m_printername = aArg;
582 else if (m_unknown.isEmpty())
583 m_unknown = "--printer-name must be followed by printername";
585 else if (m_unknown.isEmpty())
587 m_unknown = "--printer-name must directly follow --print-to-file";
590 else if ( oArg == "outdir" )
592 if (eCurrentEvent == CommandLineEvent::Conversion ||
593 eCurrentEvent == CommandLineEvent::BatchPrint)
595 if (supplier.next(&aArg))
596 m_conversionout = aArg;
597 else if (m_unknown.isEmpty())
598 m_unknown = "--outdir must be followed by output directory path";
600 else if (m_unknown.isEmpty())
602 m_unknown = "--outdir must directly follow either output filter specification of --convert-to, or --print-to-file or its printer specification";
605 else if ( eCurrentEvent == CommandLineEvent::Conversion
606 && oArg == "convert-images-to" )
608 if (supplier.next(&aArg))
609 m_convertimages = aArg;
610 else if (m_unknown.isEmpty())
611 m_unknown = "--convert-images-to must be followed by an image type";
613 else if ( aArg.startsWith("-") )
615 // because it's impossible to filter these options that
616 // are handled in the soffice shell script with the
617 // primitive tools that /bin/sh offers, ignore them here
618 if (
619 #if defined UNX
620 oArg != "record" &&
621 oArg != "backtrace" &&
622 oArg != "strace" &&
623 oArg != "valgrind" &&
624 // for X Session Management, handled in
625 // vcl/unx/generic/app/sm.cxx:
626 oArg != "session=" &&
627 #endif
628 //ignore additional legacy options that don't do anything anymore
629 oArg != "nocrashreport" &&
630 m_unknown.isEmpty())
632 m_unknown = aArg;
634 oDeprecatedArg.clear();
636 else
638 // handle this argument as a filename
640 // First check if this is an Office URI
641 // This will possibly adjust event for this argument
642 // and put real URI to aArg
643 CommandLineEvent eThisEvent = CheckOfficeURI(aArg, eCurrentEvent);
645 // Now check if this is a Web Query file
646 eThisEvent = CheckWebQuery(aArg, eThisEvent);
648 switch (eThisEvent)
650 case CommandLineEvent::Open:
651 m_openlist.push_back(aArg);
652 m_bDocumentArgs = true;
653 break;
654 case CommandLineEvent::View:
655 m_viewlist.push_back(aArg);
656 m_bDocumentArgs = true;
657 break;
658 case CommandLineEvent::Start:
659 m_startlist.push_back(aArg);
660 m_bDocumentArgs = true;
661 break;
662 case CommandLineEvent::Print:
663 m_printlist.push_back(aArg);
664 m_bDocumentArgs = true;
665 break;
666 case CommandLineEvent::PrintTo:
667 m_printtolist.push_back(aArg);
668 m_bDocumentArgs = true;
669 break;
670 case CommandLineEvent::ForceNew:
671 m_forcenewlist.push_back(aArg);
672 m_bDocumentArgs = true;
673 break;
674 case CommandLineEvent::ForceOpen:
675 m_forceopenlist.push_back(aArg);
676 m_bDocumentArgs = true;
677 break;
678 case CommandLineEvent::Conversion:
679 case CommandLineEvent::BatchPrint:
680 m_conversionlist.push_back(aArg);
681 break;
685 if (!oDeprecatedArg.isEmpty())
687 OString sArg(OUStringToOString(oDeprecatedArg, osl_getThreadTextEncoding()));
688 fprintf(stderr, "Warning: %s is deprecated. Use -%s instead.\n", sArg.getStr(), sArg.getStr());
694 void CommandLineArgs::InitParamValues()
696 m_minimized = false;
697 m_norestore = false;
698 #if HAVE_FEATURE_UI
699 m_invisible = false;
700 m_headless = false;
701 #else
702 m_invisible = true;
703 m_headless = true;
704 #endif
705 m_eventtesting = false;
706 m_quickstart = false;
707 m_noquickstart = false;
708 m_terminateafterinit = false;
709 m_nologo = false;
710 m_nolockcheck = false;
711 m_nodefault = false;
712 m_help = false;
713 m_writer = false;
714 m_calc = false;
715 m_draw = false;
716 m_impress = false;
717 m_global = false;
718 m_math = false;
719 m_web = false;
720 m_base = false;
721 m_helpwriter = false;
722 m_helpcalc = false;
723 m_helpdraw = false;
724 m_helpbasic = false;
725 m_helpmath = false;
726 m_helpimpress = false;
727 m_helpbase = false;
728 m_version = false;
729 m_splashpipe = false;
730 m_bEmpty = true;
731 m_bDocumentArgs = false;
732 m_textcat = false;
733 m_scriptcat = false;
734 m_safemode = false;
737 bool CommandLineArgs::HasModuleParam() const
739 return m_writer || m_calc || m_draw || m_impress || m_global || m_math
740 || m_web || m_base;
743 std::vector< OUString > CommandLineArgs::GetOpenList() const
745 return translateExternalUris(m_openlist);
748 std::vector< OUString > CommandLineArgs::GetViewList() const
750 return translateExternalUris(m_viewlist);
753 std::vector< OUString > CommandLineArgs::GetStartList() const
755 return translateExternalUris(m_startlist);
758 std::vector< OUString > CommandLineArgs::GetForceOpenList() const
760 return translateExternalUris(m_forceopenlist);
763 std::vector< OUString > CommandLineArgs::GetForceNewList() const
765 return translateExternalUris(m_forcenewlist);
768 std::vector< OUString > CommandLineArgs::GetPrintList() const
770 return translateExternalUris(m_printlist);
773 std::vector< OUString > CommandLineArgs::GetPrintToList() const
775 return translateExternalUris(m_printtolist);
778 std::vector< OUString > CommandLineArgs::GetConversionList() const
780 return translateExternalUris(m_conversionlist);
783 OUString CommandLineArgs::GetConversionOut() const
785 return translateExternalUris(m_conversionout);
788 } // namespace desktop
790 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */