MythTV master
mythcommandlineparser.cpp
Go to the documentation of this file.
1/* -*- Mode: c++ -*-
2*
3* Class CommandLineArg
4* Class MythCommandLineParser
5*
6* Copyright (C) Raymond Wagner 2011
7*
8* This program is free software; you can redistribute it and/or modify
9* it under the terms of the GNU General Public License as published by
10* the Free Software Foundation; either version 2 of the License, or
11* (at your option) any later version.
12*
13* This program is distributed in the hope that it will be useful,
14* but WITHOUT ANY WARRANTY; without even the implied warranty of
15* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16* GNU General Public License for more details.
17*
18* You should have received a copy of the GNU General Public License
19* along with this program; if not, write to the Free Software
20* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
21*/
22
23#include <QtGlobal>
24#if QT_VERSION >= QT_VERSION_CHECK(6,5,0)
25#include <QtEnvironmentVariables>
26#include <QtSystemDetection>
27#endif
28
29#if defined ANDROID && __ANDROID_API__ < 24
30// ftello and fseeko do not exist in android before api level 24
31#define ftello ftell
32#define fseeko fseek
33#endif
34
35// C++ headers
36#include <algorithm>
37#include <csignal>
38#include <cstdio>
39#include <cstdlib>
40#include <fstream>
41#include <iostream>
42#include <unistd.h>
43
44// System headers
45#include <sys/types.h>
46#ifndef Q_OS_WINDOWS
47# include <sys/ioctl.h>
48# include <pwd.h>
49# include <grp.h>
50# ifdef Q_OS_LINUX
51# include <sys/prctl.h>
52# endif // linux
53#endif // not Q_OS_WINDOWS
54
55// Qt headers
56#include <QCoreApplication>
57#include <QDateTime>
58#include <QDir>
59#include <QFile>
60#include <QFileInfo>
61#include <QRegularExpression>
62#include <QSize>
63#include <QString>
64#include <QTextStream>
65#include <QVariant>
66#include <QVariantList>
67#include <QVariantMap>
68#include <utility>
69
70// MythTV headers
72#include "mythcorecontext.h"
73#include "exitcodes.h"
74#include "mythconfig.h"
75#include "mythlogging.h"
76#include "mythversion.h"
77#include "logging.h"
78#include "mythmiscutil.h"
79#include "mythdate.h"
80
81static constexpr int k_defaultWidth = 79;
82
86static int GetTermWidth(void)
87{
88#if defined(Q_OS_WINDOWS) || defined(Q_OS_ANDROID)
89 return k_defaultWidth;
90#else
91 struct winsize ws {};
92
93 if (ioctl(0, TIOCGWINSZ, &ws) != 0)
94 return k_defaultWidth;
95
96 return static_cast<int>(ws.ws_col);
97#endif
98}
99
100static QByteArray strip_quotes(const QByteArray& array)
101{
102 return ((array.startsWith('"') && array.endsWith('"') ) ||
103 (array.startsWith('\'') && array.endsWith('\''))
104 ) ? array.mid(1, array.size() - 2) : array;
105}
106
107static void wrapList(QStringList &list, int width)
108{
109 // Set a minimum width of 5 to prevent a crash; if this is triggered,
110 // something has gone seriously wrong and the result won't really be usable
111 width = std::max(width, 5);
112
113 for (int i = 0; i < list.size(); i++)
114 {
115 QString string = list.at(i);
116
117 if( string.size() <= width )
118 continue;
119
120 QString left = string.left(width);
121 bool inserted = false;
122
123 while( !inserted && !left.endsWith(" " ))
124 {
125 if( string.mid(left.size(), 1) == " " )
126 {
127 list.replace(i, left);
128 list.insert(i+1, string.mid(left.size()).trimmed());
129 inserted = true;
130 }
131 else
132 {
133 left.chop(1);
134 if( !left.contains(" ") )
135 {
136 // Line is too long, just hyphenate it
137 list.replace(i, left + "-");
138 list.insert(i+1, string.mid(left.size()));
139 inserted = true;
140 }
141 }
142 }
143
144 if( !inserted )
145 {
146 left.chop(1);
147 list.replace(i, left);
148 list.insert(i+1, string.mid(left.size()).trimmed());
149 }
150 }
151}
152
157QStringList MythCommandLineParser::MythSplitCommandString(const QString &line)
158{
159 QStringList fields;
163 enum states : std::uint8_t {
164 START,
165 INTEXT,
166 INSQUOTE,
167 INDQUOTE,
168 ESCTEXT,
169 ESCSQUOTE,
170 ESCDQUOTE,
171 };
172 states state = START;
173 int tokenStart = -1;
174
175 for (int i = 0; i < line.size(); i++)
176 {
177 const QChar c = line.at(i);
178
179 switch (state) {
180 case START:
181 tokenStart = i;
182 if (c.isSpace()) break;
183 if (c == '\'') state = INSQUOTE;
184 else if (c == '\"') state = INDQUOTE;
185 else if (c == '\\') state = ESCTEXT;
186 else state = INTEXT;
187 break;
188 case INTEXT:
189 if (c.isSpace()) {
190 fields += line.mid(tokenStart, i - tokenStart);
191 state = START;
192 break;
193 }
194 else if (c == '\'') {
195 state = INSQUOTE;
196 } else if (c == '\"') {
197 state = INDQUOTE;
198 } else if (c == '\\') {
199 state = ESCTEXT;
200 }
201 break;
202 case INSQUOTE:
203 if (c == '\'') state = INTEXT;
204 else if (c == '\\') state = ESCSQUOTE;
205 break;
206 case INDQUOTE:
207 if (c == '\"') state = INTEXT;
208 else if (c == '\\') state = ESCDQUOTE;
209 break;
210 case ESCTEXT: state = INTEXT; break;
211 case ESCSQUOTE: state = INSQUOTE; break;
212 case ESCDQUOTE: state = INDQUOTE; break;
213 }
214 }
215
216 if (state != START)
217 fields += line.mid(tokenStart);
218 return fields;
219}
220
225{
226 switch (type)
227 {
228 case Result::kEnd:
229 return "kEnd";
230
231 case Result::kEmpty:
232 return "kEmpty";
233
234 case Result::kOptOnly:
235 return "kOptOnly";
236
237 case Result::kOptVal:
238 return "kOptVal";
239
241 return "kCombOptVal";
242
243 case Result::kArg:
244 return "kArg";
245
247 return "kPassthrough";
248
249 case Result::kInvalid:
250 return "kInvalid";
251 }
252 return "kUnknown";
253}
254
289CommandLineArg::CommandLineArg(const QString& name, QMetaType::Type type,
290 QVariant def, QString help, QString longhelp) :
291 ReferenceCounter(QString("CommandLineArg:%1").arg(name)),
292 m_name(name), m_type(type), m_default(std::move(def)),
293 m_help(std::move(help)), m_longhelp(std::move(longhelp))
294{
295 if ((m_type != QMetaType::QString) && (m_type != QMetaType::QStringList) &&
296 (m_type != QMetaType::QVariantMap))
297 m_converted = true;
298}
299
306CommandLineArg::CommandLineArg(const QString& name, QMetaType::Type type, QVariant def)
307 : ReferenceCounter(QString("CommandLineArg:%1").arg(name)),
308 m_name(name), m_type(type), m_default(std::move(def))
309{
310 if ((m_type != QMetaType::QString) && (m_type != QMetaType::QStringList) &&
311 (m_type != QMetaType::QVariantMap))
312 m_converted = true;
313}
314
322CommandLineArg::CommandLineArg(const QString& name) :
323 ReferenceCounter(QString("CommandLineArg:%1").arg(name)),
324 m_name(name)
325{
326}
327
332{
333 // this may cause problems if the terminal is too narrow, or if too
334 // many keywords for the same argument are used
335 return m_keywords.join(", ");
336}
337
342{
343 int len = GetKeywordString().length();
344
345 QList<CommandLineArg*>::const_iterator i1;
346 for (i1 = m_parents.begin(); i1 != m_parents.end(); ++i1)
347 len = std::max(len, (*i1)->GetKeywordLength()+2);
348
349 return len;
350}
351
367QString CommandLineArg::GetHelpString(int off, const QString& group, bool force) const
368{
369 QString helpstr;
370#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
371 QTextStream msg(&helpstr, QIODevice::WriteOnly);
372#else
373 QTextStream msg(&helpstr, QIODeviceBase::WriteOnly);
374#endif
375 int termwidth = GetTermWidth();
376 if (termwidth < off)
377 {
378 if (off > 70)
379 {
380 // developer has configured some absurdly long command line
381 // arguments, but we still need to do something
382 termwidth = off+40;
383 }
384 else
385 {
386 // user is running uselessly narrow console, use a sane console
387 // width instead
388 termwidth = k_defaultWidth;
389 }
390 }
391
392 if (m_help.isEmpty() && !force)
393 // only print if there is a short help to print
394 return helpstr;
395
396 if ((m_group != group) && !force)
397 // only print if looping over the correct group
398 return helpstr;
399
400 if (!m_parents.isEmpty() && !force)
401 {
402 // only print if an independent option, not subject
403 // to a parent option
404 return helpstr;
405 }
406
407 if (!m_deprecated.isEmpty())
408 // option is marked as deprecated, do not show
409 return helpstr;
410
411 if (!m_removed.isEmpty())
412 // option is marked as removed, do not show
413 return helpstr;
414
415 QString pad;
416 pad.fill(' ', off);
417
418 // print the first line with the available keywords
419 QStringList hlist = m_help.split('\n');
420 wrapList(hlist, termwidth-off);
421 msg << " ";
422 if (!m_parents.isEmpty())
423 msg << " ";
424 msg << GetKeywordString().leftJustified(off, ' ')
425 << hlist.takeFirst() << Qt::endl;
426
427 // print remaining lines with necessary padding
428 for (const auto & line : std::as_const(hlist))
429 msg << pad << line << Qt::endl;
430
431 // loop through any child arguments to print underneath
432 for (auto * arg : std::as_const(m_children))
433 msg << arg->GetHelpString(off, group, true);
434
435 msg.flush();
436 return helpstr;
437}
438
446QString CommandLineArg::GetLongHelpString(QString keyword) const
447{
448 QString helpstr;
449#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
450 QTextStream msg(&helpstr, QIODevice::WriteOnly);
451#else
452 QTextStream msg(&helpstr, QIODeviceBase::WriteOnly);
453#endif
454 int termwidth = GetTermWidth();
455
456 // help called for an argument that is not me, this should not happen
457 if (!m_keywords.contains(keyword))
458 return helpstr;
459
460 // argument has been marked as removed, so warn user of such
461 if (!m_removed.isEmpty())
462 {
463 PrintRemovedWarning(keyword);
464 // argument has been marked as deprecated, so warn user of such
465 }
466 else if (!m_deprecated.isEmpty())
467 {
468 PrintDeprecatedWarning(keyword);
469 }
470
471 msg << "Option: " << keyword << Qt::endl << Qt::endl;
472
473 bool first = true;
474
475 // print all related keywords, padding for multiples
476 for (const auto & word : std::as_const(m_keywords))
477 {
478 if (word != keyword)
479 {
480 if (first)
481 {
482 msg << "Aliases: " << word << Qt::endl;
483 first = false;
484 }
485 else
486 {
487 msg << " " << word << Qt::endl;
488 }
489 }
490 }
491
492 // print type and default for the stored value
493 msg << "Type: " << QMetaType(m_type).name() << Qt::endl;
494 if (m_default.canConvert<QString>())
495 msg << "Default: " << m_default.toString() << Qt::endl;
496
497 QStringList help;
498 if (m_longhelp.isEmpty())
499 help = m_help.split("\n");
500 else
501 help = m_longhelp.split("\n");
502 wrapList(help, termwidth-13);
503
504 // print description, wrapping and padding as necessary
505 msg << "Description: " << help.takeFirst() << Qt::endl;
506 for (const auto & line : std::as_const(help))
507 msg << " " << line << Qt::endl;
508
509 QList<CommandLineArg*>::const_iterator i2;
510
511 // loop through the four relation types and print
512 if (!m_parents.isEmpty())
513 {
514 msg << Qt::endl << "Can be used in combination with:" << Qt::endl;
515 for (auto * parent : std::as_const(m_parents))
516 msg << " " << parent->GetPreferredKeyword()
517 .toLocal8Bit().constData();
518 msg << Qt::endl;
519 }
520
521 if (!m_children.isEmpty())
522 {
523 msg << Qt::endl << "Allows the use of:" << Qt::endl;
524 for (i2 = m_children.constBegin(); i2 != m_children.constEnd(); ++i2)
525 msg << " " << (*i2)->GetPreferredKeyword()
526 .toLocal8Bit().constData();
527 msg << Qt::endl;
528 }
529
530 if (!m_requires.isEmpty())
531 {
532 msg << Qt::endl << "Requires the use of:" << Qt::endl;
533 for (i2 = m_requires.constBegin(); i2 != m_requires.constEnd(); ++i2)
534 msg << " " << (*i2)->GetPreferredKeyword()
535 .toLocal8Bit().constData();
536 msg << Qt::endl;
537 }
538
539 if (!m_blocks.isEmpty())
540 {
541 msg << Qt::endl << "Prevents the use of:" << Qt::endl;
542 for (i2 = m_blocks.constBegin(); i2 != m_blocks.constEnd(); ++i2)
543 msg << " " << (*i2)->GetPreferredKeyword()
544 .toLocal8Bit().constData();
545 msg << Qt::endl;
546 }
547
548 msg.flush();
549 return helpstr;
550}
551
558bool CommandLineArg::Set(const QString& opt)
559{
560 m_usedKeyword = opt;
561
562 switch (m_type)
563 {
564 case QMetaType::Bool:
565 m_stored = QVariant(!m_default.toBool());
566 break;
567
568 case QMetaType::Int:
569 if (m_stored.isNull())
570 m_stored = QVariant(1);
571 else
572 m_stored = QVariant(m_stored.toInt() + 1);
573 break;
574
575 case QMetaType::QString:
577 break;
578
579 default:
580 std::cerr << "Command line option did not receive value:\n"
581 << " " << opt.toLocal8Bit().constData() << '\n';
582 return false;
583 }
584
585 m_given = true;
586 return true;
587}
588
591bool CommandLineArg::Set(const QString& opt, const QByteArray& val)
592{
593 QVariantList vlist;
594 QList<QByteArray> blist;
595 QVariantMap vmap;
596 m_usedKeyword = opt;
597
598 switch (m_type)
599 {
600 case QMetaType::Bool:
601 std::cerr << "Boolean type options do not accept values:\n"
602 << " " << opt.toLocal8Bit().constData() << '\n';
603 return false;
604
605 case QMetaType::QString:
606 m_stored = QVariant(val);
607 break;
608
609 case QMetaType::Int:
610 m_stored = QVariant(val.toInt());
611 break;
612
613 case QMetaType::UInt:
614 m_stored = QVariant(val.toUInt());
615 break;
616
617 case QMetaType::LongLong:
618 m_stored = QVariant(val.toLongLong());
619 break;
620
621 case QMetaType::Double:
622 m_stored = QVariant(val.toDouble());
623 break;
624
625 case QMetaType::QDateTime:
626 m_stored = QVariant(MythDate::fromString(QString(val)));
627 break;
628
629 case QMetaType::QStringList:
630 if (!m_stored.isNull())
631 vlist = m_stored.toList();
632 vlist << val;
633 m_stored = QVariant(vlist);
634 break;
635
636 case QMetaType::QVariantMap:
637 if (!val.contains('='))
638 {
639 std::cerr << "Command line option did not get expected "
640 << "key/value pair\n";
641 return false;
642 }
643
644 blist = val.split('=');
645
646 if (!m_stored.isNull())
647 vmap = m_stored.toMap();
648 vmap[QString(strip_quotes(blist[0]))] = QVariant(strip_quotes(blist[1]));
649 m_stored = QVariant(vmap);
650 break;
651
652 case QMetaType::QSize:
653 if (!val.contains('x'))
654 {
655 std::cerr << "Command line option did not get expected "
656 << "XxY pair\n";
657 return false;
658 }
659
660 blist = val.split('x');
661 m_stored = QVariant(QSize(blist[0].toInt(), blist[1].toInt()));
662 break;
663
664 default:
665 m_stored = QVariant(val);
666 }
667
668 m_given = true;
669 return true;
670}
671
675{
676 m_children << new CommandLineArg(opt);
677 return this;
678}
679
683{
684 for (const auto& opt : std::as_const(opts))
685 m_children << new CommandLineArg(opt);
686 return this;
687}
688
692{
693 m_parents << new CommandLineArg(opt);
694 return this;
695}
696
700{
701 for (const auto& opt : std::as_const(opts))
702 m_parents << new CommandLineArg(opt);
703 return this;
704}
705
709{
710 m_parents << new CommandLineArg(opt);
711 return this;
712}
713
717{
718 for (const auto& opt : std::as_const(opts))
719 m_parents << new CommandLineArg(opt);
720 return this;
721}
722
726{
727 m_children << new CommandLineArg(opt);
728 return this;
729}
730
734{
735 for (const auto& opt : std::as_const(opts))
736 m_children << new CommandLineArg(opt);
737 return this;
738}
739
743{
744 m_children << new CommandLineArg(opt);
745 m_requires << new CommandLineArg(opt);
746 return this;
747}
748
752{
753 for (const auto& opt : std::as_const(opts))
754 {
755 m_children << new CommandLineArg(opt);
756 m_requires << new CommandLineArg(opt);
757 }
758 return this;
759}
760
764{
765 m_parents << new CommandLineArg(opt);
766 m_requiredby << new CommandLineArg(opt);
767 return this;
768}
769
773{
774 for (const auto& opt : std::as_const(opts))
775 {
776 m_parents << new CommandLineArg(opt);
777 m_requiredby << new CommandLineArg(opt);
778 }
779 return this;
780}
781
785{
786 m_requires << new CommandLineArg(opt);
787 return this;
788}
789
793{
794 for (const auto& opt : std::as_const(opts))
795 m_requires << new CommandLineArg(opt);
796 return this;
797}
798
802{
803 m_blocks << new CommandLineArg(opt);
804 return this;
805}
806
810{
811 for (const auto& opt : std::as_const(opts))
812 m_blocks << new CommandLineArg(opt);
813 return this;
814}
815
819{
820 if (depstr.isEmpty())
821 depstr = "and will be removed in a future version.";
822 m_deprecated = depstr;
823 return this;
824}
825
828CommandLineArg* CommandLineArg::SetRemoved(QString remstr, QString remver)
829{
830 if (remstr.isEmpty())
831 remstr = "and is no longer available in this version.";
832 m_removed = remstr;
833 m_removedversion = std::move(remver);
834 return this;
835}
836
843{
844 bool replaced = false;
845 other->IncrRef();
846
847 for (int i = 0; i < m_children.size(); i++)
848 {
849 if (m_children[i]->m_name == other->m_name)
850 {
851 m_children[i]->DecrRef();
852 m_children.replace(i, other);
853 replaced = true;
854 break;
855 }
856 }
857
858 if (!replaced)
859 m_children << other;
860
861 if (forward)
862 other->SetChildOf(this, false);
863}
864
871{
872 bool replaced = false;
873 other->IncrRef();
874
875 for (int i = 0; i < m_parents.size(); i++)
876 {
877 if (m_parents[i]->m_name == other->m_name)
878 {
879 m_parents[i]->DecrRef();
880 m_parents.replace(i, other);
881 replaced = true;
882 break;
883 }
884 }
885
886 if (!replaced)
887 m_parents << other;
888
889 if (forward)
890 other->SetParentOf(this, false);
891}
892
898void CommandLineArg::SetRequires(CommandLineArg *other, bool /*forward*/)
899{
900 bool replaced = false;
901 other->IncrRef();
902
903 for (int i = 0; i < m_requires.size(); i++)
904 {
905 if (m_requires[i]->m_name == other->m_name)
906 {
907 m_requires[i]->DecrRef();
908 m_requires.replace(i, other);
909 replaced = true;
910 break;
911 }
912 }
913
914 if (!replaced)
915 m_requires << other;
916
917// requirements need not be reciprocal
918// if (forward)
919// other->SetRequires(this, false);
920}
921
928{
929 bool replaced = false;
930 other->IncrRef();
931
932 for (int i = 0; i < m_blocks.size(); i++)
933 {
934 if (m_blocks[i]->m_name == other->m_name)
935 {
936 m_blocks[i]->DecrRef();
937 m_blocks.replace(i, other);
938 replaced = true;
939 break;
940 }
941 }
942
943 if (!replaced)
944 m_blocks << other;
945
946 if (forward)
947 other->SetBlocks(this, false);
948}
949
952void CommandLineArg::AllowOneOf(const QList<CommandLineArg*>& args)
953{
954 // TODO: blocks do not get set properly if multiple dummy arguments
955 // are provided. since this method will not have access to the
956 // argument list, this issue will have to be resolved later in
957 // ReconcileLinks().
958
959 // loop through all but the last entry
960 for (auto i1 = args.cbegin(); i1 != args.cend()-1; ++i1)
961 {
962 // loop through the next to the last entry
963 // and block use with the current
964 for (auto i2 = i1+1; i2 != args.cend(); ++i2)
965 {
966 (*i1)->SetBlocks(*i2);
967 }
968
969 if ((*i1)->m_type == QMetaType::UnknownType)
970 (*i1)->DecrRef();
971 }
972}
973
981{
982 if (!QCoreApplication::instance())
983 // QApplication not available, no sense doing anything yet
984 return;
985
986 if (m_converted)
987 // already run, abort
988 return;
989
990 if (!m_given)
991 {
992 // nothing to work on, abort
993 m_converted = true;
994 return;
995 }
996
997#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
998 auto storedType = static_cast<QMetaType::Type>(m_stored.type());
999#else
1000 auto storedType = m_stored.typeId();
1001#endif
1002 if (m_type == QMetaType::QString)
1003 {
1004 if (storedType == QMetaType::QByteArray)
1005 {
1006 m_stored = QString::fromLocal8Bit(m_stored.toByteArray());
1007 }
1008 // else
1009 // not sure why this isnt a bytearray, but ignore it and
1010 // set it as converted
1011 }
1012 else if (m_type == QMetaType::QStringList)
1013 {
1014 if (storedType == QMetaType::QVariantList)
1015 {
1016 QVariantList vlist = m_stored.toList();
1017 QStringList slist;
1018 slist.reserve(vlist.size());
1019 for (const auto& item : std::as_const(vlist))
1020 slist << QString::fromLocal8Bit(item.toByteArray());
1021 m_stored = QVariant(slist);
1022 }
1023 }
1024 else if (m_type == QMetaType::QVariantMap)
1025 {
1026 QVariantMap vmap = m_stored.toMap();
1027 // NOLINTNEXTLINE(modernize-loop-convert)
1028 for (auto iter = vmap.begin(); iter != vmap.end(); ++iter)
1029 (*iter) = QString::fromLocal8Bit(iter->toByteArray());
1030 }
1031 else
1032 {
1033 return;
1034 }
1035
1036 m_converted = true;
1037}
1038
1039
1046{
1047 QStringList::const_iterator it;
1048 QString preferred;
1049 int len = 0;
1050
1051 for (it = m_keywords.constBegin(); it != m_keywords.constEnd(); ++it)
1052 {
1053 int len2 = (*it).size();
1054 if (len2 > len)
1055 {
1056 preferred = *it;
1057 len = len2;
1058 }
1059 }
1060
1061 return preferred;
1062}
1063
1068{
1069 if (!m_given)
1070 return true; // not in use, no need for checks
1071
1072 QList<CommandLineArg*>::const_iterator i;
1073
1074 bool passes = false;
1075 for (i = m_parents.constBegin(); i != m_parents.constEnd(); ++i)
1076 {
1077 // one of these must have been defined
1078 if ((*i)->m_given)
1079 {
1080 passes = true;
1081 break;
1082 }
1083 }
1084 if (!passes && !m_parents.isEmpty())
1085 {
1086 std::cerr << "ERROR: " << m_usedKeyword.toLocal8Bit().constData()
1087 << " requires at least one of the following arguments\n";
1088 for (i = m_parents.constBegin(); i != m_parents.constEnd(); ++i)
1089 std::cerr << " "
1090 << (*i)->GetPreferredKeyword().toLocal8Bit().constData();
1091 std::cerr << "\n\n";
1092 return false;
1093 }
1094
1095 // we dont care about children
1096
1097 for (i = m_requires.constBegin(); i != m_requires.constEnd(); ++i)
1098 {
1099 // all of these must have been defined
1100 if (!(*i)->m_given)
1101 {
1102 std::cerr << "ERROR: " << m_usedKeyword.toLocal8Bit().constData()
1103 << " requires all of the following be defined as well\n";
1104 for (i = m_requires.constBegin(); i != m_requires.constEnd(); ++i)
1105 {
1106 std::cerr << " "
1107 << (*i)->GetPreferredKeyword().toLocal8Bit()
1108 .constData();
1109 }
1110 std::cerr << "\n\n";
1111 return false;
1112 }
1113 }
1114
1115 for (i = m_blocks.constBegin(); i != m_blocks.constEnd(); ++i)
1116 {
1117 // none of these can be defined
1118 if ((*i)->m_given)
1119 {
1120 std::cerr << "ERROR: " << m_usedKeyword.toLocal8Bit().constData()
1121 << " requires that none of the following be defined\n";
1122 for (i = m_blocks.constBegin(); i != m_blocks.constEnd(); ++i)
1123 {
1124 std::cerr << " "
1125 << (*i)->GetPreferredKeyword().toLocal8Bit()
1126 .constData();
1127 }
1128 std::cerr << "\n\n";
1129 return false;
1130 }
1131 }
1132
1133 return true;
1134}
1135
1139{
1140 // clear out interdependent pointers in preparation for deletion
1141 while (!m_parents.isEmpty())
1142 m_parents.takeFirst()->DecrRef();
1143
1144 while (!m_children.isEmpty())
1145 m_children.takeFirst()->DecrRef();
1146
1147 while (!m_blocks.isEmpty())
1148 m_blocks.takeFirst()->DecrRef();
1149
1150 while (!m_requires.isEmpty())
1151 m_requires.takeFirst()->DecrRef();
1152
1153 while (!m_requiredby.isEmpty())
1154 m_requiredby.takeFirst()->DecrRef();
1155}
1156
1160{
1161 if (!m_given)
1162 return;
1163
1164 std::cerr << " " << m_name.leftJustified(30).toLocal8Bit().constData();
1165
1166 QSize tmpsize;
1167 QMap<QString, QVariant> tmpmap;
1168 QMap<QString, QVariant>::const_iterator it;
1169 QVariantList vlist;
1170 bool first = true;
1171
1172 switch (m_type)
1173 {
1174 case QMetaType::Bool:
1175 std::cerr << (m_stored.toBool() ? "True" : "False") << '\n';
1176 break;
1177
1178 case QMetaType::Int:
1179 std::cerr << m_stored.toInt() << '\n';
1180 break;
1181
1182 case QMetaType::UInt:
1183 std::cerr << m_stored.toUInt() << '\n';
1184 break;
1185
1186 case QMetaType::LongLong:
1187 std::cerr << m_stored.toLongLong() << '\n';
1188 break;
1189
1190 case QMetaType::Double:
1191 std::cerr << m_stored.toDouble() << '\n';
1192 break;
1193
1194 case QMetaType::QSize:
1195 tmpsize = m_stored.toSize();
1196 std::cerr << "x=" << tmpsize.width()
1197 << " y=" << tmpsize.height()
1198 << '\n';
1199 break;
1200
1201 case QMetaType::QString:
1202 std::cerr << '"' << m_stored.toByteArray().constData()
1203 << '"' << '\n';
1204 break;
1205
1206 case QMetaType::QStringList:
1207 vlist = m_stored.toList();
1208 std::cerr << '"' << vlist.takeFirst().toByteArray().constData() << '"';
1209 for (const auto& str : std::as_const(vlist))
1210 {
1211 std::cerr << ", \""
1212 << str.constData()
1213 << '"';
1214 }
1215 std::cerr << '\n';
1216 break;
1217
1218 case QMetaType::QVariantMap:
1219 tmpmap = m_stored.toMap();
1220 for (it = tmpmap.cbegin(); it != tmpmap.cend(); ++it)
1221 {
1222 if (first)
1223 first = false;
1224 else
1225 std::cerr << QString("").leftJustified(32)
1226 .toLocal8Bit().constData();
1227
1228 std::cerr << it.key().toLocal8Bit().constData()
1229 << '='
1230 << it->toByteArray().constData()
1231 << '\n';
1232 }
1233
1234 break;
1235
1236 case QMetaType::QDateTime:
1237 std::cerr << m_stored.toDateTime().toString(Qt::ISODate)
1238 .toLocal8Bit().constData()
1239 << '\n';
1240 break;
1241
1242 default:
1243 std::cerr << '\n';
1244 }
1245}
1246
1249void CommandLineArg::PrintRemovedWarning(QString &keyword) const
1250{
1251 QString warn = QString("%1 has been removed").arg(keyword);
1252 if (!m_removedversion.isEmpty())
1253 warn += QString(" as of MythTV %1").arg(m_removedversion);
1254
1255 std::cerr << QString("****************************************************\n"
1256 " WARNING: %1\n"
1257 " %2\n"
1258 "****************************************************\n\n")
1259 .arg(warn, m_removed)
1260 .toLocal8Bit().constData();
1261}
1262
1265void CommandLineArg::PrintDeprecatedWarning(QString &keyword) const
1266{
1267 std::cerr << QString("****************************************************\n"
1268 " WARNING: %1 has been deprecated\n"
1269 " %2\n"
1270 "****************************************************\n\n")
1271 .arg(keyword, m_deprecated)
1272 .toLocal8Bit().constData();
1273}
1274
1289 : m_appname(std::move(appname))
1290{
1291 if (qEnvironmentVariableIsSet("VERBOSE_PARSER"))
1292 {
1293 std::cerr << "MythCommandLineParser is now operating verbosely.\n";
1294 m_verbose = true;
1295 }
1296
1298}
1299
1301{
1302 QString pidfile = toString("pidfile");
1303 if (!pidfile.isEmpty())
1304 {
1305 QFile::remove(pidfile);
1306 }
1307
1308 QMap<QString, CommandLineArg*>::iterator i;
1309
1310 i = m_namedArgs.begin();
1311 while (i != m_namedArgs.end())
1312 {
1313 (*i)->CleanupLinks();
1314 (*i)->DecrRef();
1315 i = m_namedArgs.erase(i);
1316 }
1317
1318 i = m_optionedArgs.begin();
1319 while (i != m_optionedArgs.end())
1320 {
1321 (*i)->DecrRef();
1322 i = m_optionedArgs.erase(i);
1323 }
1324}
1325
1360 const QString& name, QMetaType::Type type, QVariant def,
1361 QString help, QString longhelp)
1362{
1363 CommandLineArg *arg = nullptr;
1364
1365 if (m_namedArgs.contains(name))
1366 {
1367 arg = m_namedArgs[name];
1368 }
1369 else
1370 {
1371 arg = new CommandLineArg(name, type, std::move(def), std::move(help), std::move(longhelp));
1372 m_namedArgs.insert(name, arg);
1373 }
1374
1375 for (const auto & str : std::as_const(arglist))
1376 {
1377 if (!m_optionedArgs.contains(str))
1378 {
1379 arg->AddKeyword(str);
1380 if (m_verbose)
1381 {
1382 std::cerr << "Adding " << str.toLocal8Bit().constData()
1383 << " as taking type '"
1384#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1385 << QVariant::typeToName(static_cast<int>(type))
1386#else
1387 << QMetaType(type).name()
1388#endif
1389 << "'\n";
1390 }
1391 arg->IncrRef();
1392 m_optionedArgs.insert(str, arg);
1393 }
1394 }
1395
1396 return arg;
1397}
1398
1402{
1403 std::cout << "Please attach all output as a file in bug reports.\n";
1404 std::cout << "MythTV Version : " << GetMythSourceVersion() << '\n';
1405 std::cout << "MythTV Branch : " << GetMythSourcePath() << '\n';
1406 std::cout << "Network Protocol : " << MYTH_PROTO_VERSION << '\n';
1407 std::cout << "Library API : " << MYTH_BINARY_VERSION << '\n';
1408 std::cout << "QT Version : " << QT_VERSION_STR << '\n';
1409#ifdef MYTH_BUILD_CONFIG
1410 std::cout << "Options compiled in:\n";
1411 std::cout << MYTH_BUILD_CONFIG << '\n';
1412#endif
1413}
1414
1418{
1419 QString help = GetHelpString();
1420 std::cerr << help.toLocal8Bit().constData();
1421}
1422
1429{
1430 QString helpstr;
1431#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1432 QTextStream msg(&helpstr, QIODevice::WriteOnly);
1433#else
1434 QTextStream msg(&helpstr, QIODeviceBase::WriteOnly);
1435#endif
1436
1437 QString versionStr = QString("%1 version: %2 [%3] www.mythtv.org")
1439 msg << versionStr << Qt::endl;
1440
1441 if (toString("showhelp").isEmpty())
1442 {
1443 // build generic help text
1444
1445 QString descr = GetHelpHeader();
1446 if (descr.size() > 0)
1447 msg << Qt::endl << descr << Qt::endl << Qt::endl;
1448
1449 // loop through registered arguments to populate list of groups
1450 QStringList groups("");
1451 int maxlen = 0;
1452 for (auto * cmdarg : std::as_const(m_namedArgs))
1453 {
1454 maxlen = std::max(cmdarg->GetKeywordLength(), maxlen);
1455 if (!groups.contains(cmdarg->m_group))
1456 groups << cmdarg->m_group;
1457 }
1458
1459 // loop through list of groups and print help string for each
1460 // arguments will filter themselves if they are not in the group
1461 maxlen += 4;
1462 for (const auto & group : std::as_const(groups))
1463 {
1464 if (group.isEmpty())
1465 msg << "Misc. Options:" << Qt::endl << Qt::endl;
1466 else
1467 msg << group.toLocal8Bit().constData() << " Options:" << Qt::endl << Qt::endl;
1468
1469 for (auto * cmdarg : std::as_const(m_namedArgs))
1470 msg << cmdarg->GetHelpString(maxlen, group);
1471 msg << Qt::endl;
1472 }
1473 }
1474 else
1475 {
1476 // build help for a specific argument
1477 QString optstr = "-" + toString("showhelp");
1478 if (!m_optionedArgs.contains(optstr))
1479 {
1480 optstr = "-" + optstr;
1481 if (!m_optionedArgs.contains(optstr))
1482 return QString("Could not find option matching '%1'\n")
1483 .arg(toString("showhelp"));
1484 }
1485
1486 if (m_optionedArgs[optstr] != nullptr)
1487 msg << m_optionedArgs[optstr]->GetLongHelpString(optstr);
1488 }
1489
1490 msg.flush();
1491 return helpstr;
1492}
1493
1497 int &argpos, QString &opt, QByteArray &val)
1498{
1499 opt.clear();
1500 val.clear();
1501
1502 if (argpos >= argc)
1503 // this shouldnt happen, return and exit
1504 return Result::kEnd;
1505
1506 QByteArray tmp(argv[argpos]);
1507 if (tmp.isEmpty())
1508 // string is empty, return and loop
1509 return Result::kEmpty;
1510
1512 {
1513 // pass through has been activated
1514 val = tmp;
1515 return Result::kArg;
1516 }
1517
1518 if (tmp.startsWith('-') && tmp.size() > 1)
1519 {
1520 if (tmp == "--")
1521 {
1522 // all options beyond this will be passed as a single string
1523 m_passthroughActive = true;
1524 return Result::kPassthrough;
1525 }
1526
1527 if (tmp.contains('='))
1528 {
1529 // option contains '=', split
1530 QList<QByteArray> blist = tmp.split('=');
1531
1532 if (blist.size() != 2)
1533 {
1534 // more than one '=' in option, this is not handled
1535 opt = QString(tmp);
1536 return Result::kInvalid;
1537 }
1538
1539 opt = QString(strip_quotes(blist[0]));
1540 val = strip_quotes(blist[1]);
1541 return Result::kCombOptVal;
1542 }
1543
1544 opt = QString(tmp);
1545
1546 if (argpos+1 >= argc)
1547 // end of input, option only
1548 return Result::kOptOnly;
1549
1550 tmp = QByteArray(argv[++argpos]);
1551 if (tmp.isEmpty())
1552 // empty string, option only
1553 return Result::kOptOnly;
1554
1555 if (tmp.startsWith("-") && tmp.size() > 1)
1556 {
1557 // no value found for option, backtrack
1558 argpos--;
1559 return Result::kOptOnly;
1560 }
1561
1562 val = tmp;
1563 return Result::kOptVal;
1564 }
1565
1566 // input is not an option string, return as arg
1567 val = tmp;
1568 return Result::kArg;
1569}
1570
1577bool MythCommandLineParser::Parse(int argc, const char * const * argv)
1578{
1579 Result res = Result::kEnd;
1580 QString opt;
1581 QByteArray val;
1582 CommandLineArg *argdef = nullptr;
1583
1584 // reconnect interdependencies between command line options
1585 if (!ReconcileLinks())
1586 return false;
1587
1588 // loop through command line arguments until all are spent
1589 for (int argpos = 1; argpos < argc; ++argpos)
1590 {
1591
1592 // pull next option
1593 res = getOpt(argc, argv, argpos, opt, val);
1594
1595 if (m_verbose)
1596 {
1597 std::cerr << "res: " << NamedOptType(res) << '\n'
1598 << "opt: " << opt.toLocal8Bit().constData() << '\n'
1599 << "val: " << val.constData() << "\n\n";
1600 }
1601
1602 // '--' found on command line, enable passthrough mode
1603 if (res == Result::kPassthrough && !m_namedArgs.contains("_passthrough"))
1604 {
1605 std::cerr << "Received '--' but passthrough has not been enabled\n";
1606 SetValue("showhelp", "");
1607 return false;
1608 }
1609
1610 // end of options found, terminate loop
1611 if (res == Result::kEnd)
1612 break;
1613
1614 // GetOpt pulled an empty option, this shouldnt happen by ignore
1615 // it and continue
1616 if (res == Result::kEmpty)
1617 continue;
1618
1619 // more than one equal found in key/value pair, fault out
1620 if (res == Result::kInvalid)
1621 {
1622 std::cerr << "Invalid option received:\n "
1623 << opt.toLocal8Bit().constData();
1624 SetValue("showhelp", "");
1625 return false;
1626 }
1627
1628 // passthrough is active, so add the data to the stringlist
1630 {
1631 m_namedArgs["_passthrough"]->Set("", val);
1632 continue;
1633 }
1634
1635 // argument with no preceeding '-' encountered, add to stringlist
1636 if (res == Result::kArg)
1637 {
1638 if (!m_namedArgs.contains("_args"))
1639 {
1640 std::cerr << "Received '"
1641 << val.constData()
1642 << "' but unassociated arguments have not been enabled\n";
1643 SetValue("showhelp", "");
1644 return false;
1645 }
1646
1647 m_namedArgs["_args"]->Set("", val);
1648 continue;
1649 }
1650
1651 // this line should not be passed once arguments have started collecting
1652 if (toBool("_args"))
1653 {
1654 std::cerr << "Command line arguments received out of sequence\n";
1655 SetValue("showhelp", "");
1656 return false;
1657 }
1658
1659#ifdef Q_OS_DARWIN
1660 if (opt.startsWith("-psn_"))
1661 {
1662 std::cerr << "Ignoring Process Serial Number from command line\n";
1663 continue;
1664 }
1665#endif
1666
1667 if (!m_optionedArgs.contains(opt))
1668 {
1669 // argument is unhandled, check if parser allows arbitrary input
1670 if (m_namedArgs.contains("_extra"))
1671 {
1672 // arbitrary allowed, specify general collection pool
1673 argdef = m_namedArgs["_extra"];
1674 QByteArray tmp = opt.toLocal8Bit();
1675 tmp += '=';
1676 tmp += val;
1677 val = tmp;
1678 res = Result::kOptVal;
1679 }
1680 else
1681 {
1682 // arbitrary not allowed, fault out
1683 std::cerr << "Unhandled option given on command line:\n"
1684 << " " << opt.toLocal8Bit().constData() << '\n';
1685 SetValue("showhelp", "");
1686 return false;
1687 }
1688 }
1689 else
1690 {
1691 argdef = m_optionedArgs[opt];
1692 }
1693
1694 // argument has been marked as removed, warn user and fail
1695 if (!argdef->m_removed.isEmpty())
1696 {
1697 argdef->PrintRemovedWarning(opt);
1698 SetValue("showhelp", "");
1699 return false;
1700 }
1701
1702 // argument has been marked as deprecated, warn user
1703 if (!argdef->m_deprecated.isEmpty())
1704 argdef->PrintDeprecatedWarning(opt);
1705
1706 if (m_verbose)
1707 std::cerr << "name: " << argdef->GetName().toLocal8Bit().constData()
1708 << '\n';
1709
1710 // argument is keyword only, no value
1711 if (res == Result::kOptOnly)
1712 {
1713 if (!argdef->Set(opt))
1714 {
1715 SetValue("showhelp", "");
1716 return false;
1717 }
1718 }
1719 // argument has keyword and value
1720 else if ((res == Result::kOptVal) || (res == Result::kCombOptVal))
1721 {
1722 if (!argdef->Set(opt, val))
1723 {
1724 // if option and value were combined with a '=', abort directly
1725 // otherwise, attempt processing them independenly
1726 if ((res == Result::kCombOptVal) || !argdef->Set(opt))
1727 {
1728 SetValue("showhelp", "");
1729 return false;
1730 }
1731 // drop back an iteration so the unused value will get
1732 // processed a second time as a keyword-less argument
1733 --argpos;
1734 }
1735 }
1736 else
1737 {
1738 SetValue("showhelp", "");
1739 return false; // this should not occur
1740 }
1741
1742 if (m_verbose)
1743 std::cerr << "value: " << argdef->m_stored.toString().toLocal8Bit().constData()
1744 << '\n';
1745 }
1746
1747 if (m_verbose)
1748 {
1749 std::cerr << "Processed option list:\n";
1750 for (auto * cmdarg : std::as_const(m_namedArgs))
1751 cmdarg->PrintVerbose();
1752
1753 if (m_namedArgs.contains("_args"))
1754 {
1755 std::cerr << "\nExtra argument list:\n";
1756 QStringList slist = toStringList("_args");
1757 for (const auto& lopt : std::as_const(slist))
1758 std::cerr << " " << lopt.toLocal8Bit().constData() << '\n';
1759 }
1760
1761 if (m_namedArgs.contains("_passthrough"))
1762 {
1763 std::cerr << "\nPassthrough string:\n";
1764 std::cerr << " " << GetPassthrough().toLocal8Bit().constData() << '\n';
1765 }
1766
1767 std::cerr << '\n';
1768 }
1769
1770 // make sure all interdependencies are fulfilled
1771 for (auto * cmdarg : std::as_const(m_namedArgs))
1772 {
1773 if (!cmdarg->TestLinks())
1774 {
1775 QString keyword = cmdarg->m_usedKeyword;
1776 if (keyword.startsWith('-'))
1777 {
1778 if (keyword.startsWith("--"))
1779 keyword.remove(0,2);
1780 else
1781 keyword.remove(0,1);
1782 }
1783
1784 SetValue("showhelp", keyword);
1785 return false;
1786 }
1787 }
1788
1789 return true;
1790}
1791
1792CommandLineArg* MythCommandLineParser::add(const QString& arg, const QString& name, bool def,
1793 QString help, QString longhelp)
1794{
1795 return add(QStringList(arg), name, QMetaType::Bool, QVariant(def), std::move(help), std::move(longhelp));
1796}
1797
1798CommandLineArg* MythCommandLineParser::add(const QString& arg, const QString& name, int def,
1799 QString help, QString longhelp)
1800{
1801 return add(QStringList(arg), name, QMetaType::Int, QVariant(def), std::move(help), std::move(longhelp));
1802}
1803
1804CommandLineArg* MythCommandLineParser::add(const QString& arg, const QString& name, uint def,
1805 QString help, QString longhelp)
1806{
1807 return add(QStringList(arg), name, QMetaType::UInt, QVariant(def), std::move(help), std::move(longhelp));
1808}
1809
1810CommandLineArg* MythCommandLineParser::add(const QString& arg, const QString& name, long long def,
1811 QString help, QString longhelp)
1812{
1813 return add(QStringList(arg), name, QMetaType::LongLong, QVariant(def), std::move(help), std::move(longhelp));
1814}
1815
1816CommandLineArg* MythCommandLineParser::add(const QString& arg, const QString& name, double def,
1817 QString help, QString longhelp)
1818{
1819 return add(QStringList(arg), name, QMetaType::Double, QVariant(def), std::move(help), std::move(longhelp));
1820}
1821
1822CommandLineArg* MythCommandLineParser::add(const QString& arg, const QString& name, const char *def,
1823 QString help, QString longhelp)
1824{
1825 return add(QStringList(arg), name, QMetaType::QString, QVariant(def), std::move(help), std::move(longhelp));
1826}
1827
1828CommandLineArg* MythCommandLineParser::add(const QString& arg, const QString& name, const QString& def,
1829 QString help, QString longhelp)
1830{
1831 return add(QStringList(arg), name, QMetaType::QString, QVariant(def), std::move(help), std::move(longhelp));
1832}
1833
1834CommandLineArg* MythCommandLineParser::add(const QString& arg, const QString& name, QSize def,
1835 QString help, QString longhelp)
1836{
1837 return add(QStringList(arg), name, QMetaType::QSize, QVariant(def), std::move(help), std::move(longhelp));
1838}
1839
1840CommandLineArg* MythCommandLineParser::add(const QString& arg, const QString& name, const QDateTime& def,
1841 QString help, QString longhelp)
1842{
1843 return add(QStringList(arg), name, QMetaType::QDateTime, QVariant(def), std::move(help), std::move(longhelp));
1844}
1845
1846CommandLineArg* MythCommandLineParser::add(const QString& arg, const QString& name, QMetaType::Type type,
1847 QString help, QString longhelp)
1848{
1849 return add(QStringList(arg), name, type,
1850#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1851 QVariant(static_cast<QVariant::Type>(type)),
1852#else
1853 QVariant(QMetaType(type)),
1854#endif
1855 std::move(help), std::move(longhelp));
1856}
1857
1858CommandLineArg* MythCommandLineParser::add(const QString& arg, const QString& name,
1859 QMetaType::Type type,
1860 QVariant def, QString help, QString longhelp)
1861{
1862 return add(QStringList(arg), name, type, std::move(def), std::move(help), std::move(longhelp));
1863}
1864
1865CommandLineArg* MythCommandLineParser::add(QStringList arglist, const QString& name, bool def,
1866 QString help, QString longhelp)
1867{
1868 return add(std::move(arglist), name, QMetaType::Bool, QVariant(def), std::move(help), std::move(longhelp));
1869}
1870
1871CommandLineArg* MythCommandLineParser::add(QStringList arglist, const QString& name, int def,
1872 QString help, QString longhelp)
1873{
1874 return add(std::move(arglist), name, QMetaType::Int, QVariant(def), std::move(help), std::move(longhelp));
1875}
1876
1877CommandLineArg* MythCommandLineParser::add(QStringList arglist, const QString& name, uint def,
1878 QString help, QString longhelp)
1879{
1880 return add(std::move(arglist), name, QMetaType::UInt, QVariant(def), std::move(help), std::move(longhelp));
1881}
1882
1883CommandLineArg* MythCommandLineParser::add(QStringList arglist, const QString& name, long long def,
1884 QString help, QString longhelp)
1885{
1886 return add(std::move(arglist), name, QMetaType::LongLong, QVariant(def), std::move(help), std::move(longhelp));
1887}
1888
1889CommandLineArg* MythCommandLineParser::add(QStringList arglist, const QString& name, double def,
1890 QString help, QString longhelp)
1891{
1892 return add(std::move(arglist), name, QMetaType::Double, QVariant(def), std::move(help), std::move(longhelp));
1893}
1894
1895CommandLineArg* MythCommandLineParser::add(QStringList arglist, const QString& name, const char *def,
1896 QString help, QString longhelp)
1897{
1898 return add(std::move(arglist), name, QMetaType::QString, QVariant(def), std::move(help), std::move(longhelp));
1899}
1900
1901CommandLineArg* MythCommandLineParser::add(QStringList arglist, const QString& name, const QString& def,
1902 QString help, QString longhelp)
1903{
1904 return add(std::move(arglist), name, QMetaType::QString, QVariant(def), std::move(help), std::move(longhelp));
1905}
1906
1907CommandLineArg* MythCommandLineParser::add(QStringList arglist, const QString& name, QSize def,
1908 QString help, QString longhelp)
1909{
1910 return add(std::move(arglist), name, QMetaType::QSize, QVariant(def), std::move(help), std::move(longhelp));
1911}
1912
1913CommandLineArg* MythCommandLineParser::add(QStringList arglist, const QString& name, const QDateTime& def,
1914 QString help, QString longhelp)
1915{
1916 return add(std::move(arglist), name, QMetaType::QDateTime, QVariant(def), std::move(help), std::move(longhelp));
1917}
1918
1919CommandLineArg* MythCommandLineParser::add(QStringList arglist, const QString& name,
1920 QMetaType::Type type,
1921 QString help, QString longhelp)
1922{
1923 return add(std::move(arglist), name, type,
1924#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1925 QVariant(static_cast<QVariant::Type>(type)),
1926#else
1927 QVariant(QMetaType(type)),
1928#endif
1929 std::move(help), std::move(longhelp));
1930}
1931
1936{
1937 if (m_verbose)
1938 std::cerr << "Reconciling links for option interdependencies.\n";
1939
1940 QMap<QString,CommandLineArg*>::iterator args_it;
1941 for (args_it = m_namedArgs.begin(); args_it != m_namedArgs.end(); ++args_it)
1942 {
1943 QList<CommandLineArg*> links = (*args_it)->m_parents;
1944 QList<CommandLineArg*>::iterator links_it;
1945 for (links_it = links.begin(); links_it != links.end(); ++links_it)
1946 {
1947 if ((*links_it)->m_type != QMetaType::UnknownType)
1948 continue; // already handled
1949
1950 if (!m_namedArgs.contains((*links_it)->m_name))
1951 {
1952 // not found
1953 std::cerr << "ERROR: could not reconcile linked argument.\n"
1954 << " '" << (*args_it)->m_name.toLocal8Bit().constData()
1955 << "' could not find '"
1956 << (*links_it)->m_name.toLocal8Bit().constData()
1957 << "'.\n"
1958 << " Please resolve dependency and recompile.\n";
1959 return false;
1960 }
1961
1962 // replace linked argument
1963 if (m_verbose)
1964 {
1965 std::cerr << QString(" Setting %1 as child of %2")
1966 .arg((*args_it)->m_name, (*links_it)->m_name)
1967 .toLocal8Bit().constData()
1968 << '\n';
1969 }
1970 (*args_it)->SetChildOf(m_namedArgs[(*links_it)->m_name]);
1971 }
1972
1973 links = (*args_it)->m_children;
1974 for (links_it = links.begin(); links_it != links.end(); ++links_it)
1975 {
1976 if ((*links_it)->m_type != QMetaType::UnknownType)
1977 continue; // already handled
1978
1979 if (!m_namedArgs.contains((*links_it)->m_name))
1980 {
1981 // not found
1982 std::cerr << "ERROR: could not reconcile linked argument.\n"
1983 << " '" << (*args_it)->m_name.toLocal8Bit().constData()
1984 << "' could not find '"
1985 << (*links_it)->m_name.toLocal8Bit().constData()
1986 << "'.\n"
1987 << " Please resolve dependency and recompile.\n";
1988 return false;
1989 }
1990
1991 // replace linked argument
1992 if (m_verbose)
1993 {
1994 std::cerr << QString(" Setting %1 as parent of %2")
1995 .arg((*args_it)->m_name, (*links_it)->m_name)
1996 .toLocal8Bit().constData()
1997 << '\n';
1998 }
1999 (*args_it)->SetParentOf(m_namedArgs[(*links_it)->m_name]);
2000 }
2001
2002 links = (*args_it)->m_requires;
2003 for (links_it = links.begin(); links_it != links.end(); ++links_it)
2004 {
2005 if ((*links_it)->m_type != QMetaType::UnknownType)
2006 continue; // already handled
2007
2008 if (!m_namedArgs.contains((*links_it)->m_name))
2009 {
2010 // not found
2011 std::cerr << "ERROR: could not reconcile linked argument.\n"
2012 << " '" << (*args_it)->m_name.toLocal8Bit().constData()
2013 << "' could not find '"
2014 << (*links_it)->m_name.toLocal8Bit().constData()
2015 << "'.\n"
2016 << " Please resolve dependency and recompile.\n";
2017 return false;
2018 }
2019
2020 // replace linked argument
2021 if (m_verbose)
2022 {
2023 std::cerr << QString(" Setting %1 as requiring %2")
2024 .arg((*args_it)->m_name, (*links_it)->m_name)
2025 .toLocal8Bit().constData()
2026 << '\n';
2027 }
2028 (*args_it)->SetRequires(m_namedArgs[(*links_it)->m_name]);
2029 }
2030
2031 QList<CommandLineArg*>::iterator req_it =
2032 (*args_it)->m_requiredby.begin();
2033 while (req_it != (*args_it)->m_requiredby.end())
2034 {
2035 if ((*req_it)->m_type == QMetaType::UnknownType)
2036 {
2037 // if its not an invalid, it shouldnt be here anyway
2038 if (m_namedArgs.contains((*req_it)->m_name))
2039 {
2040 m_namedArgs[(*req_it)->m_name]->SetRequires(*args_it);
2041 if (m_verbose)
2042 {
2043 std::cerr << QString(" Setting %1 as blocking %2")
2044 .arg((*args_it)->m_name,
2045 (*req_it)->m_name)
2046 .toLocal8Bit().constData()
2047 << '\n';
2048 }
2049 }
2050 }
2051
2052 (*req_it)->DecrRef();
2053 req_it = (*args_it)->m_requiredby.erase(req_it);
2054 }
2055
2056 QList<CommandLineArg*>::iterator block_it =
2057 (*args_it)->m_blocks.begin();
2058 while (block_it != (*args_it)->m_blocks.end())
2059 {
2060 if ((*block_it)->m_type != QMetaType::UnknownType)
2061 {
2062 ++block_it;
2063 continue; // already handled
2064 }
2065
2066 if (!m_namedArgs.contains((*block_it)->m_name))
2067 {
2068 (*block_it)->DecrRef();
2069 block_it = (*args_it)->m_blocks.erase(block_it);
2070 continue; // if it doesnt exist, it cant block this command
2071 }
2072
2073 // replace linked argument
2074 if (m_verbose)
2075 {
2076 std::cerr << QString(" Setting %1 as blocking %2")
2077 .arg((*args_it)->m_name, (*block_it)->m_name)
2078 .toLocal8Bit().constData()
2079 << '\n';
2080 }
2081 (*args_it)->SetBlocks(m_namedArgs[(*block_it)->m_name]);
2082 ++block_it;
2083 }
2084 }
2085
2086 return true;
2087}
2088
2092QVariant MythCommandLineParser::operator[](const QString &name)
2093{
2094 QVariant var("");
2095 if (!m_namedArgs.contains(name))
2096 return var;
2097
2098 CommandLineArg *arg = m_namedArgs[name];
2099
2100 if (arg->m_given)
2101 var = arg->m_stored;
2102 else
2103 var = arg->m_default;
2104
2105 return var;
2106}
2107
2111QStringList MythCommandLineParser::GetArgs(void) const
2112{
2113 return toStringList("_args");
2114}
2115
2119QMap<QString,QString> MythCommandLineParser::GetExtra(void) const
2120{
2121 return toMap("_extra");
2122}
2123
2127{
2128 return toStringList("_passthrough").join(" ");
2129}
2130
2139{
2140 QMap<QString,QString> smap = toMap("overridesettings");
2141
2143 {
2144 if (toBool("overridesettingsfile"))
2145 {
2146 QString filename = toString("overridesettingsfile");
2147 if (!filename.isEmpty())
2148 {
2149 QFile f(filename);
2150 if (f.open(QIODevice::ReadOnly))
2151 {
2152 QTextStream in(&f);
2153 while (!in.atEnd()) {
2154 QString line = in.readLine().trimmed();
2155 QStringList tokens = line.split("=",
2156 Qt::SkipEmptyParts);
2157 if (tokens.size() == 2)
2158 {
2159 static const QRegularExpression kQuoteStartRE { "^[\"']" };
2160 static const QRegularExpression kQuoteEndRE { "[\"']$" };
2161 tokens[0].remove(kQuoteStartRE);
2162 tokens[0].remove(kQuoteEndRE);
2163 tokens[1].remove(kQuoteStartRE);
2164 tokens[1].remove(kQuoteEndRE);
2165 if (!tokens[0].isEmpty())
2166 smap[tokens[0]] = tokens[1];
2167 }
2168 }
2169 }
2170 else
2171 {
2172 QByteArray tmp = filename.toLatin1();
2173 std::cerr << "Failed to open the override settings file: '"
2174 << tmp.constData() << "'\n";
2175 }
2176 }
2177 }
2178
2179 if (toBool("windowed"))
2180 smap["RunFrontendInWindow"] = "1";
2181 else if (toBool("notwindowed"))
2182 smap["RunFrontendInWindow"] = "0";
2183
2184 if (toBool("mousecursor"))
2185 smap["HideMouseCursor"] = "0";
2186 else if (toBool("nomousecursor"))
2187 smap["HideMouseCursor"] = "1";
2188
2189 m_overridesImported = true;
2190
2191 if (!smap.isEmpty())
2192 {
2193 QVariantMap vmap;
2194 for (auto it = smap.cbegin(); it != smap.cend(); ++it)
2195 vmap[it.key()] = QVariant(it.value());
2196
2197 m_namedArgs["overridesettings"]->Set(QVariant(vmap));
2198 }
2199 }
2200
2201 if (m_verbose)
2202 {
2203 std::cerr << "Option Overrides:\n";
2204 QMap<QString, QString>::const_iterator it;
2205 for (it = smap.constBegin(); it != smap.constEnd(); ++it)
2206 std::cerr << QString(" %1 - %2").arg(it.key(), 30).arg(*it)
2207 .toLocal8Bit().constData() << '\n';
2208 }
2209
2210 return smap;
2211}
2212
2219bool MythCommandLineParser::toBool(const QString& key) const
2220{
2221 if (!m_namedArgs.contains(key))
2222 return false;
2223
2224 CommandLineArg *arg = m_namedArgs[key];
2225 if (arg == nullptr)
2226 return false;
2227
2228 if (arg->m_type == QMetaType::Bool)
2229 {
2230 if (arg->m_given)
2231 return arg->m_stored.toBool();
2232 return arg->m_default.toBool();
2233 }
2234
2235 return arg->m_given;
2236}
2237
2241int MythCommandLineParser::toInt(const QString& key) const
2242{
2243 int val = 0;
2244 if (!m_namedArgs.contains(key))
2245 return val;
2246
2247 CommandLineArg *arg = m_namedArgs[key];
2248 if (arg == nullptr)
2249 return val;
2250
2251 if (arg->m_given)
2252 {
2253 if (arg->m_stored.canConvert<int>())
2254 val = arg->m_stored.toInt();
2255 }
2256 else
2257 {
2258 if (arg->m_default.canConvert<int>())
2259 val = arg->m_default.toInt();
2260 }
2261
2262 return val;
2263}
2264
2268uint MythCommandLineParser::toUInt(const QString& key) const
2269{
2270 uint val = 0;
2271 if (!m_namedArgs.contains(key))
2272 return val;
2273
2274 CommandLineArg *arg = m_namedArgs[key];
2275 if (arg == nullptr)
2276 return val;
2277
2278 if (arg->m_given)
2279 {
2280 if (arg->m_stored.canConvert<uint>())
2281 val = arg->m_stored.toUInt();
2282 }
2283 else
2284 {
2285 if (arg->m_default.canConvert<uint>())
2286 val = arg->m_default.toUInt();
2287 }
2288
2289 return val;
2290}
2291
2295long long MythCommandLineParser::toLongLong(const QString& key) const
2296{
2297 long long val = 0;
2298 if (!m_namedArgs.contains(key))
2299 return val;
2300
2301 CommandLineArg *arg = m_namedArgs[key];
2302 if (arg == nullptr)
2303 return val;
2304
2305 if (arg->m_given)
2306 {
2307 if (arg->m_stored.canConvert<long long>())
2308 val = arg->m_stored.toLongLong();
2309 }
2310 else
2311 {
2312 if (arg->m_default.canConvert<long long>())
2313 val = arg->m_default.toLongLong();
2314 }
2315
2316 return val;
2317}
2318
2322double MythCommandLineParser::toDouble(const QString& key) const
2323{
2324 double val = 0.0;
2325 if (!m_namedArgs.contains(key))
2326 return val;
2327
2328 CommandLineArg *arg = m_namedArgs[key];
2329 if (arg == nullptr)
2330 return val;
2331
2332 if (arg->m_given)
2333 {
2334 if (arg->m_stored.canConvert<double>())
2335 val = arg->m_stored.toDouble();
2336 }
2337 else
2338 {
2339 if (arg->m_default.canConvert<double>())
2340 val = arg->m_default.toDouble();
2341 }
2342
2343 return val;
2344}
2345
2349QSize MythCommandLineParser::toSize(const QString& key) const
2350{
2351 QSize val(0,0);
2352 if (!m_namedArgs.contains(key))
2353 return val;
2354
2355 CommandLineArg *arg = m_namedArgs[key];
2356 if (arg == nullptr)
2357 return val;
2358
2359 if (arg->m_given)
2360 {
2361 if (arg->m_stored.canConvert<QSize>())
2362 val = arg->m_stored.toSize();
2363 }
2364 else
2365 {
2366 if (arg->m_default.canConvert<QSize>())
2367 val = arg->m_default.toSize();
2368 }
2369
2370 return val;
2371}
2372
2376QString MythCommandLineParser::toString(const QString& key) const
2377{
2378 QString val("");
2379 if (!m_namedArgs.contains(key))
2380 return val;
2381
2382 CommandLineArg *arg = m_namedArgs[key];
2383 if (arg == nullptr)
2384 return val;
2385
2386 if (arg->m_given)
2387 {
2388 if (!arg->m_converted)
2389 arg->Convert();
2390
2391 if (arg->m_stored.canConvert<QString>())
2392 val = arg->m_stored.toString();
2393 }
2394 else
2395 {
2396 if (arg->m_default.canConvert<QString>())
2397 val = arg->m_default.toString();
2398 }
2399
2400 return val;
2401}
2402
2407QStringList MythCommandLineParser::toStringList(const QString& key, const QString& sep) const
2408{
2409 QVariant varval;
2410 QStringList val;
2411 if (!m_namedArgs.contains(key))
2412 return val;
2413
2414 CommandLineArg *arg = m_namedArgs[key];
2415 if (arg == nullptr)
2416 return val;
2417
2418 if (arg->m_given)
2419 {
2420 if (!arg->m_converted)
2421 arg->Convert();
2422
2423 varval = arg->m_stored;
2424 }
2425 else
2426 {
2427 varval = arg->m_default;
2428 }
2429
2430 if (arg->m_type == QMetaType::QString && !sep.isEmpty())
2431 val = varval.toString().split(sep);
2432 else if (varval.canConvert<QStringList>())
2433 val = varval.toStringList();
2434
2435 return val;
2436}
2437
2441QMap<QString,QString> MythCommandLineParser::toMap(const QString& key) const
2442{
2443 QMap<QString, QString> val;
2444 QMap<QString, QVariant> tmp;
2445 if (!m_namedArgs.contains(key))
2446 return val;
2447
2448 CommandLineArg *arg = m_namedArgs[key];
2449 if (arg == nullptr)
2450 return val;
2451
2452 if (arg->m_given)
2453 {
2454 if (!arg->m_converted)
2455 arg->Convert();
2456
2457 if (arg->m_stored.canConvert<QMap<QString, QVariant>>())
2458 tmp = arg->m_stored.toMap();
2459 }
2460 else
2461 {
2462 if (arg->m_default.canConvert<QMap<QString, QVariant>>())
2463 tmp = arg->m_default.toMap();
2464 }
2465
2466 for (auto i = tmp.cbegin(); i != tmp.cend(); ++i)
2467 val[i.key()] = i.value().toString();
2468
2469 return val;
2470}
2471
2475QDateTime MythCommandLineParser::toDateTime(const QString& key) const
2476{
2477 QDateTime val;
2478 if (!m_namedArgs.contains(key))
2479 return val;
2480
2481 CommandLineArg *arg = m_namedArgs[key];
2482 if (arg == nullptr)
2483 return val;
2484
2485 if (arg->m_given)
2486 {
2487 if (arg->m_stored.canConvert<QDateTime>())
2488 val = arg->m_stored.toDateTime();
2489 }
2490 else
2491 {
2492 if (arg->m_default.canConvert<QDateTime>())
2493 val = arg->m_default.toDateTime();
2494 }
2495
2496 return val;
2497}
2498
2503{
2504 if (m_namedArgs.contains("_args"))
2505 {
2506 if (!allow)
2507 m_namedArgs.remove("_args");
2508 }
2509 else if (!allow)
2510 {
2511 return;
2512 }
2513
2514 auto *arg = new CommandLineArg("_args", QMetaType::QStringList, QStringList());
2515 m_namedArgs["_args"] = arg;
2516}
2517
2522{
2523 if (m_namedArgs.contains("_extra"))
2524 {
2525 if (!allow)
2526 m_namedArgs.remove("_extra");
2527 }
2528 else if (!allow)
2529 {
2530 return;
2531 }
2532
2533 QMap<QString,QVariant> vmap;
2534 auto *arg = new CommandLineArg("_extra", QMetaType::QVariantMap, vmap);
2535
2536 m_namedArgs["_extra"] = arg;
2537}
2538
2543{
2544 if (m_namedArgs.contains("_passthrough"))
2545 {
2546 if (!allow)
2547 m_namedArgs.remove("_passthrough");
2548 }
2549 else if (!allow)
2550 {
2551 return;
2552 }
2553
2554 auto *arg = new CommandLineArg("_passthrough",
2555 QMetaType::QStringList, QStringList());
2556 m_namedArgs["_passthrough"] = arg;
2557}
2558
2562{
2563 add(QStringList{"-h", "--help", "--usage"},
2564 "showhelp", "", "Display this help printout, or give detailed "
2565 "information of selected option.",
2566 "Displays a list of all commands available for use with "
2567 "this application. If another option is provided as an "
2568 "argument, it will provide detailed information on that "
2569 "option.");
2570}
2571
2575{
2576 add("--version", "showversion", false, "Display version information.",
2577 "Display informtion about build, including:\n"
2578 " version, branch, protocol, library API, Qt "
2579 "and compiled options.");
2580}
2581
2585{
2586 add(QStringList{"-nw", "--no-windowed"},
2587 "notwindowed", false,
2588 "Prevent application from running in a window.", "")
2589 ->SetBlocks("windowed")
2590 ->SetGroup("User Interface");
2591
2592 add(QStringList{"-w", "--windowed"}, "windowed",
2593 false, "Force application to run in a window.", "")
2594 ->SetGroup("User Interface");
2595}
2596
2600{
2601 add("--mouse-cursor", "mousecursor", false,
2602 "Force visibility of the mouse cursor.", "")
2603 ->SetBlocks("nomousecursor")
2604 ->SetGroup("User Interface");
2605
2606 add("--no-mouse-cursor", "nomousecursor", false,
2607 "Force the mouse cursor to be hidden.", "")
2608 ->SetGroup("User Interface");
2609}
2610
2614{
2615 add(QStringList{"-d", "--daemon"}, "daemon", false,
2616 "Fork application into background after startup.",
2617 "Fork application into background, detatching from "
2618 "the local terminal.\nOften used with: "
2619 " --logpath --pidfile --user");
2620}
2621
2626{
2627 add(QStringList{"-O", "--override-setting"},
2628 "overridesettings", QMetaType::QVariantMap,
2629 "Override a single setting defined by a key=value pair.",
2630 "Override a single setting from the database using "
2631 "options defined as one or more key=value pairs\n"
2632 "Multiple can be defined by multiple uses of the "
2633 "-O option.");
2634 add("--override-settings-file", "overridesettingsfile", "",
2635 "Define a file of key=value pairs to be "
2636 "loaded for setting overrides.", "");
2637}
2638
2642{
2643 add("--chanid", "chanid", 0U,
2644 "Specify chanid of recording to operate on.", "")
2645 ->SetRequires("starttime");
2646
2647 add("--starttime", "starttime", QDateTime(),
2648 "Specify start time of recording to operate on.", "")
2649 ->SetRequires("chanid");
2650}
2651
2655{
2656 add(QStringList{"-geometry", "--geometry"}, "geometry",
2657 "", "Specify window size and position (WxH[+X+Y])", "")
2658 ->SetGroup("User Interface");
2659}
2660
2664{
2665 add("--noupnp", "noupnp", false, "Disable use of UPnP.", "");
2666}
2667
2671{
2672 add("--dvbv3", "dvbv3", false, "Use legacy DVBv3 API.", "");
2673}
2674
2679 const QString &defaultVerbosity, LogLevel_t defaultLogLevel)
2680{
2681 defaultLogLevel =
2682 ((defaultLogLevel >= LOG_UNKNOWN) || (defaultLogLevel <= LOG_ANY)) ?
2683 LOG_INFO : defaultLogLevel;
2684
2685 QString logLevelStr = logLevelGetName(defaultLogLevel);
2686
2687 add(QStringList{"-v", "--verbose"}, "verbose",
2688 defaultVerbosity,
2689 "Specify log filtering. Use '-v help' for level info.", "")
2690 ->SetGroup("Logging");
2691 add("-V", "verboseint", 0LL, "",
2692 "This option is intended for internal use only.\n"
2693 "This option takes an unsigned value corresponding "
2694 "to the bitwise log verbosity operator.")
2695 ->SetGroup("Logging");
2696 add("--logpath", "logpath", "",
2697 "Writes logging messages to a file in the directory logpath with "
2698 "filenames in the format: applicationName.date.pid.log.\n"
2699 "This is typically used in combination with --daemon, and if used "
2700 "in combination with --pidfile, this can be used with log "
2701 "rotators, using the HUP call to inform MythTV to reload the "
2702 "file", "")
2703 ->SetGroup("Logging");
2704 add(QStringList{"-q", "--quiet"}, "quiet", 0,
2705 "Don't log to the console (-q). Don't log anywhere (-q -q)", "")
2706 ->SetGroup("Logging");
2707 add("--loglong", "loglong", 0,
2708 "Use long log format for the console, i.e. show file, line number, etc. in the console log.", "")
2709 ->SetGroup("Logging");
2710 add("--loglevel", "loglevel", logLevelStr,
2711 QString(
2712 "Set the logging level. All log messages at lower levels will be "
2713 "discarded.\n"
2714 "In descending order: emerg, alert, crit, err, warning, notice, "
2715 "info, debug, trace\ndefaults to ") + logLevelStr, "")
2716 ->SetGroup("Logging");
2717 add("--syslog", "syslog", "none",
2718 "Set the syslog logging facility.\nSet to \"none\" to disable, "
2719 "defaults to none.", "")
2720 ->SetGroup("Logging");
2721#if CONFIG_SYSTEMD_JOURNAL
2722 add("--systemd-journal", "systemd-journal", "false",
2723 "Use systemd-journal instead of syslog.", "")
2724 ->SetBlocks(QStringList()
2725 << "syslog"
2726 )
2727 ->SetGroup("Logging");
2728#endif
2729 add("--nodblog", "nodblog", false, "", "")
2730 ->SetGroup("Logging")
2731 ->SetRemoved("Database logging has been removed.", "34");
2732 add("--enable-dblog", "enabledblog", false, "", "")
2733 ->SetGroup("Logging")
2734 ->SetRemoved("Database logging has been removed.", "34");
2735
2736 add(QStringList{"-l", "--logfile"},
2737 "logfile", "", "", "")
2738 ->SetGroup("Logging")
2739 ->SetRemoved("This option has been removed as part of "
2740 "rewrite of the logging interface. Please update your init "
2741 "scripts to use --syslog to interface with your system's "
2742 "existing system logging daemon, or --logpath to specify a "
2743 "dirctory for MythTV to write its logs to.", "0.25");
2744}
2745
2749{
2750 add(QStringList{"-p", "--pidfile"}, "pidfile", "",
2751 "Write PID of application to filename.",
2752 "Write the PID of the currently running process as a single "
2753 "line to this file. Used for init scripts to know what "
2754 "process to terminate, and with log rotators "
2755 "to send a HUP signal to process to have it re-open files.");
2756}
2757
2761{
2762 add(QStringList{"-j", "--jobid"}, "jobid", 0, "",
2763 "Intended for internal use only, specify the JobID to match "
2764 "up with in the database for additional information and the "
2765 "ability to update runtime status in the database.");
2766}
2767
2771{
2772 add("--infile", "infile", "", "Input file URI", "");
2773 if (addOutFile)
2774 add("--outfile", "outfile", "", "Output file URI", "");
2775}
2776
2780{
2781#if CONFIG_X11
2782 add(QStringList{"-display", "--display"}, "display", "",
2783 "Qt (QPA) X11 connection name when using xcb (X11) platform plugin", "")
2784 ->SetGroup("Qt");
2785#endif
2786}
2787
2791{
2792 add(QStringList{"-platform", "--platform"}, "platform", "", "Qt (QPA) platform argument",
2793 "Qt platform argument that is passed through to Qt")
2794 ->SetGroup("Qt");
2795}
2796
2800{
2801 QString logfile = toString("logpath");
2802 pid_t pid = getpid();
2803
2804 if (logfile.isEmpty())
2805 return logfile;
2806
2807 QFileInfo finfo(logfile);
2808 if (!finfo.isDir())
2809 {
2810 LOG(VB_GENERAL, LOG_ERR,
2811 QString("%1 is not a directory, disabling logfiles")
2812 .arg(logfile));
2813 return {};
2814 }
2815
2816 QString logdir = finfo.filePath();
2817 logfile = QCoreApplication::applicationName() + "." +
2819 QString(".%1").arg(pid) + ".log";
2820
2821 SetValue("logdir", logdir);
2822 SetValue("logfile", logfile);
2823 SetValue("filepath", QFileInfo(QDir(logdir), logfile).filePath());
2824
2825 return toString("filepath");
2826}
2827
2831{
2832 QString setting = toString("syslog").toLower();
2833 if (setting == "none")
2834 return -2;
2835
2836 return syslogGetFacility(setting);
2837}
2838
2842{
2843 QString setting = toString("loglevel");
2844 if (setting.isEmpty())
2845 return LOG_INFO;
2846
2847 LogLevel_t level = logLevelGet(setting);
2848 if (level == LOG_UNKNOWN)
2849 std::cerr << "Unknown log level: " << setting.toLocal8Bit().constData()
2850 << '\n';
2851
2852 return level;
2853}
2854
2859bool MythCommandLineParser::SetValue(const QString &key, const QVariant& value)
2860{
2861 CommandLineArg *arg = nullptr;
2862
2863 if (!m_namedArgs.contains(key))
2864 {
2865 const QVariant& val(value);
2866#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
2867 auto type = static_cast<QMetaType::Type>(val.type());
2868#else
2869 auto type = static_cast<QMetaType::Type>(val.typeId());
2870#endif
2871 arg = new CommandLineArg(key, type, val);
2872 m_namedArgs.insert(key, arg);
2873 }
2874 else
2875 {
2876 arg = m_namedArgs[key];
2877#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
2878 auto type = static_cast<QMetaType::Type>(value.type());
2879#else
2880 auto type = value.typeId();
2881#endif
2882 if (arg->m_type != type)
2883 return false;
2884 }
2885
2886 arg->Set(value);
2887 return true;
2888}
2889
2893{
2894 // Setup the defaults
2895 verboseString = "";
2896 verboseMask = 0;
2897 verboseArgParse(mask);
2898
2899 if (toBool("verbose"))
2900 {
2901 int err = verboseArgParse(toString("verbose"));
2902 if (err != 0)
2903 return err;
2904 }
2905 else if (toBool("verboseint"))
2906 {
2907 verboseMask = static_cast<uint64_t>(toLongLong("verboseint"));
2908 }
2909
2910 verboseMask |= VB_STDIO|VB_FLUSH;
2911
2912 int quiet = toInt("quiet");
2913 if (std::max(quiet, static_cast<int>(progress)) > 1)
2914 {
2915 verboseMask = VB_NONE|VB_FLUSH;
2916 verboseArgParse("none");
2917 }
2918
2919 bool loglong = toBool("loglong");
2920
2921 int facility = GetSyslogFacility();
2922#if CONFIG_SYSTEMD_JOURNAL
2923 bool journal = toBool("systemd-journal");
2924 if (journal)
2925 {
2926 if (facility >= 0)
2928 facility = SYSTEMD_JOURNAL_FACILITY;
2929 }
2930#endif
2931 LogLevel_t level = GetLogLevel();
2932 if (level == LOG_UNKNOWN)
2934
2935 LOG(VB_GENERAL, LOG_CRIT,
2936 QString("%1 version: %2 [%3] www.mythtv.org")
2937 .arg(QCoreApplication::applicationName(),
2939 LOG(VB_GENERAL, LOG_CRIT, QString("Qt version: compile: %1, runtime: %2")
2940 .arg(QT_VERSION_STR, qVersion()));
2941 LOG(VB_GENERAL, LOG_INFO, QString("%1 (%2)")
2942 .arg(QSysInfo::prettyProductName(), QSysInfo::currentCpuArchitecture()));
2943 LOG(VB_GENERAL, LOG_NOTICE,
2944 QString("Enabled verbose msgs: %1").arg(verboseString));
2945
2946 QString logfile = GetLogFilePath();
2947 bool propagate = !logfile.isEmpty();
2948
2949 if (toBool("daemon"))
2950 quiet = std::max(quiet, 1);
2951
2952 logStart(logfile, progress, quiet, facility, level, propagate, loglong);
2953 qInstallMessageHandler([](QtMsgType /*unused*/, const QMessageLogContext& /*unused*/, const QString &Msg)
2954 { LOG(VB_GENERAL, LOG_INFO, "Qt: " + Msg); });
2955
2956 return GENERIC_EXIT_OK;
2957}
2958
2964{
2965 if (m_verbose)
2966 std::cerr << "Applying settings override\n";
2967
2968 QMap<QString, QString> override = GetSettingsOverride();
2969 if (!override.empty())
2970 {
2971 QMap<QString, QString>::iterator it;
2972 for (it = override.begin(); it != override.end(); ++it)
2973 {
2974 LOG(VB_GENERAL, LOG_NOTICE,
2975 QString("Setting '%1' being forced to '%2'")
2976 .arg(it.key(), *it));
2978 }
2979 }
2980}
2981
2982static bool openPidfile(std::ofstream &pidfs, const QString &pidfile)
2983{
2984 if (!pidfile.isEmpty())
2985 {
2986 pidfs.open(pidfile.toLatin1().constData());
2987 if (!pidfs)
2988 {
2989 std::cerr << "Could not open pid file: " << ENO_STR << '\n';
2990 return false;
2991 }
2992 }
2993 return true;
2994}
2995
2998static bool setUser(const QString &username)
2999{
3000 if (username.isEmpty())
3001 return true;
3002
3003#ifdef Q_OS_WINDOWS
3004 std::cerr << "--user option is not supported on Windows" << std::endl;
3005 return false;
3006#else // ! Q_OS_WINDOWS
3007#ifdef Q_OS_LINUX
3008 // Check the current dumpability of core dumps, which will be disabled
3009 // by setuid, so we can re-enable, if appropriate
3010 int dumpability = prctl(PR_GET_DUMPABLE);
3011#endif
3012 struct passwd *user_info = getpwnam(username.toLocal8Bit().constData());
3013 const uid_t user_id = geteuid();
3014
3015 if (user_id && (!user_info || user_id != user_info->pw_uid))
3016 {
3017 std::cerr << "You must be running as root to use the --user switch.\n";
3018 return false;
3019 }
3020 if (user_info && user_id == user_info->pw_uid)
3021 {
3022 LOG(VB_GENERAL, LOG_WARNING,
3023 QString("Already running as '%1'").arg(username));
3024 }
3025 else if (!user_id && user_info)
3026 {
3027 if (!qputenv("HOME", user_info->pw_dir))
3028 {
3029 std::cerr << "Error setting home directory.\n";
3030 return false;
3031 }
3032 if (setgid(user_info->pw_gid) == -1)
3033 {
3034 std::cerr << "Error setting effective group.\n";
3035 return false;
3036 }
3037 if (initgroups(user_info->pw_name, user_info->pw_gid) == -1)
3038 {
3039 std::cerr << "Error setting groups.\n";
3040 return false;
3041 }
3042 if (setuid(user_info->pw_uid) == -1)
3043 {
3044 std::cerr << "Error setting effective user.\n";
3045 return false;
3046 }
3047#ifdef Q_OS_LINUX
3048 if (dumpability && (prctl(PR_SET_DUMPABLE, dumpability) == -1))
3049 {
3050 LOG(VB_GENERAL, LOG_WARNING, "Unable to re-enable core file "
3051 "creation. Run without the --user argument to use "
3052 "shell-specified limits.");
3053 }
3054#endif
3055 }
3056 else
3057 {
3058 std::cerr << QString("Invalid user '%1' specified with --user")
3059 .arg(username).toLocal8Bit().constData() << '\n';
3060 return false;
3061 }
3062 return true;
3063#endif // ! Q_OS_WINDOWS
3064}
3065
3066
3070{
3071 std::ofstream pidfs;
3072 if (!openPidfile(pidfs, toString("pidfile")))
3074
3075 if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
3076 LOG(VB_GENERAL, LOG_WARNING, "Unable to ignore SIGPIPE");
3077
3078#ifdef Q_OS_DARWIN
3079 if (toBool("daemon"))
3080 {
3081 std::cerr << "Daemonizing is unavailable in OSX\n";
3082 LOG(VB_GENERAL, LOG_WARNING, "Unable to daemonize");
3083 }
3084#else
3085 if (toBool("daemon") && (daemon(0, 1) < 0))
3086 {
3087 std::cerr << "Failed to daemonize: " << ENO_STR << '\n';
3089 }
3090#endif
3091
3092 QString username = toString("username");
3093 if (!username.isEmpty() && !setUser(username))
3095
3096 if (pidfs)
3097 {
3098 pidfs << getpid() << '\n';
3099 pidfs.close();
3100 }
3101
3102 return GENERIC_EXIT_OK;
3103}
Definition for a single command line option.
CommandLineArg * SetRequires(const QString &opt)
Set argument as requiring given option.
static void AllowOneOf(const QList< CommandLineArg * > &args)
Mark a list of arguments as mutually exclusive.
QList< CommandLineArg * > m_requiredby
CommandLineArg * SetParent(const QString &opt)
Set argument as child of given parent.
int GetKeywordLength(void) const
Return length of full keyword string for use in determining indent of help text.
CommandLineArg * SetRequiredChild(const QString &opt)
Set argument as parent of given child and mark as required.
CommandLineArg * SetChildOf(const QString &opt)
Set argument as child of given parent.
CommandLineArg(const QString &name, QMetaType::Type type, QVariant def, QString help, QString longhelp)
Default constructor for CommandLineArg class.
void PrintVerbose(void) const
Internal use.
QString GetKeywordString(void) const
Return string containing all possible keyword triggers for this argument.
CommandLineArg * SetRemoved(QString remstr="", QString remver="")
Set option as removed.
bool TestLinks(void) const
Test all related arguments to make sure specified requirements are fulfilled.
CommandLineArg * SetDeprecated(QString depstr="")
Set option as deprecated.
void PrintDeprecatedWarning(QString &keyword) const
Internal use.
bool Set(const QString &opt)
Set option as provided on command line with no value.
CommandLineArg * SetChild(const QString &opt)
Set argument as parent of given child.
CommandLineArg * SetGroup(const QString &group)
CommandLineArg * SetParentOf(const QString &opt)
Set argument as parent of given child.
CommandLineArg * SetBlocks(const QString &opt)
Set argument as incompatible with given option.
void CleanupLinks(void)
Clear out references to other arguments in preparation for deletion.
QList< CommandLineArg * > m_children
QList< CommandLineArg * > m_blocks
QList< CommandLineArg * > m_requires
QMetaType::Type m_type
void PrintRemovedWarning(QString &keyword) const
Internal use.
QString GetName(void) const
void AddKeyword(const QString &keyword)
QString GetPreferredKeyword(void) const
Return the longest keyword for the argument.
QString GetLongHelpString(QString keyword) const
Return string containing extended help text.
QString GetHelpString(int off, const QString &group="", bool force=false) const
Return string containing help text with desired offset.
void Convert(void)
Convert stored string value from QByteArray to QString.
QList< CommandLineArg * > m_parents
CommandLineArg * SetRequiredChildOf(const QString &opt)
Set argument as child required by given parent.
static const char * NamedOptType(Result type)
void addVersion(void)
Canned argument definition for –version.
QMap< QString, CommandLineArg * > m_optionedArgs
void addPlatform(void)
Pass through the platform argument to Qt for GUI applications.
bool toBool(const QString &key) const
Returns stored QVariant as a boolean.
int toInt(const QString &key) const
Returns stored QVariant as an integer, falling to default if not provided.
static QStringList MythSplitCommandString(const QString &line)
Parse a string into separate tokens.
Result getOpt(int argc, const char *const *argv, int &argpos, QString &opt, QByteArray &val)
Internal use.
MythCommandLineParser(QString appname)
Default constructor for MythCommandLineArg class.
virtual bool Parse(int argc, const char *const *argv)
Loop through argv and populate arguments with values.
double toDouble(const QString &key) const
Returns stored QVariant as double floating point value, falling to default if not provided.
int GetSyslogFacility(void) const
Helper utility for logging interface to return syslog facility.
void ApplySettingsOverride(void)
Apply all overrides to the global context.
QSize toSize(const QString &key) const
Returns stored QVariant as a QSize value, falling to default if not provided.
void addWindowed(void)
Canned argument definition for –windowed and -no-windowed.
void addPIDFile(void)
Canned argument definition for –pidfile.
void addSettingsOverride(void)
Canned argument definition for –override-setting and –override-settings-file.
int Daemonize(void) const
Fork application into background, and detatch from terminal.
void addDisplay(void)
Canned argument definition for -display.
void addRecording(void)
Canned argument definition for –chanid and –starttime.
int ConfigureLogging(const QString &mask="general", bool progress=false)
Read in logging options and initialize the logging interface.
void addLogging(const QString &defaultVerbosity="general", LogLevel_t defaultLogLevel=LOG_INFO)
Canned argument definition for all logging options, including –verbose, –logpath, –quiet,...
QMap< QString, QString > GetExtra(void) const
Return map of additional key/value pairs provided on the command line independent of any registered a...
void addDVBv3(void)
Canned argument definition for –dvbv3.
QString toString(const QString &key) const
Returns stored QVariant as a QString, falling to default if not provided.
QString GetPassthrough(void) const
Return any text supplied on the command line after a bare '–'.
long long toLongLong(const QString &key) const
Returns stored QVariant as a long integer, falling to default if not provided.
QMap< QString, CommandLineArg * > m_namedArgs
QMap< QString, QString > GetSettingsOverride(void)
Return map of key/value pairs provided to override database options.
static void PrintVersion(void)
Print application version information.
QString GetLogFilePath(void)
Helper utility for logging interface to pull path from –logpath.
CommandLineArg * add(const QString &arg, const QString &name, bool def, QString help, QString longhelp)
bool SetValue(const QString &key, const QVariant &value)
Set a new stored value for an existing argument definition, or spawn a new definition store value in.
void addMouse(void)
Canned argument definition for –mouse-cursor and –no-mouse-cursor.
LogLevel_t GetLogLevel(void) const
Helper utility for logging interface to filtering level.
void addHelp(void)
Canned argument definition for –help.
QVariant operator[](const QString &name)
Returned stored QVariant for given argument, or default value if not used.
QString GetHelpString(void) const
Generate command line option help text.
QDateTime toDateTime(const QString &key) const
Returns stored QVariant as a QDateTime, falling to default if not provided.
QMap< QString, QString > toMap(const QString &key) const
Returns stored QVariant as a QMap, falling to default if not provided.
void addGeometry(void)
Canned argument definition for –geometry.
void allowPassthrough(bool allow=true)
Specify that parser should allow a bare '–', and collect all subsequent text as a QString.
void addUPnP(void)
Canned argument definition for –noupnp.
void addDaemon(void)
Canned argument definition for –daemon.
void addInFile(bool addOutFile=false)
Canned argument definition for –infile and –outfile.
virtual QString GetHelpHeader(void) const
QStringList toStringList(const QString &key, const QString &sep="") const
Returns stored QVariant as a QStringList, falling to default if not provided.
virtual void LoadArguments(void)
void allowArgs(bool allow=true)
Specify that parser should allow and collect values provided independent of any keyword.
void allowExtras(bool allow=true)
Specify that parser should allow and collect additional key/value pairs not explicitly defined for pr...
uint toUInt(const QString &key) const
Returns stored QVariant as an unsigned integer, falling to default if not provided.
QStringList GetArgs(void) const
Return list of additional values provided on the command line independent of any keyword.
void PrintHelp(void) const
Print command line option help.
bool ReconcileLinks(void)
Replace dummy arguments used to define interdependency with pointers to their real counterparts.
void addJob(void)
Canned argument definition for –jobid.
void OverrideSettingForSession(const QString &key, const QString &value)
General purpose reference counter.
virtual int IncrRef(void)
Increments reference count.
#define geteuid()
Definition: compat.h:120
#define daemon(x, y)
Definition: compat.h:126
#define SIGPIPE
Definition: compat.h:80
#define setuid(x)
Definition: compat.h:121
unsigned int uint
Definition: compat.h:60
@ GENERIC_EXIT_PERMISSIONS_ERROR
File permissions error.
Definition: exitcodes.h:22
@ GENERIC_EXIT_OK
Exited with no error.
Definition: exitcodes.h:13
@ GENERIC_EXIT_DAEMONIZING_ERROR
Error daemonizing or execl.
Definition: exitcodes.h:31
@ GENERIC_EXIT_INVALID_CMDLINE
Command line parse error.
Definition: exitcodes.h:18
static constexpr uint8_t START
int verboseArgParse(const QString &arg)
Parse the –verbose commandline argument and set the verbose level.
Definition: logging.cpp:915
uint64_t verboseMask
Definition: logging.cpp:102
QString verboseString
Definition: logging.cpp:103
QString logLevelGetName(LogLevel_t level)
Map a log level enumerated value back to the name.
Definition: logging.cpp:787
LogLevel_t logLevelGet(const QString &level)
Map a log level name back to the enumerated value.
Definition: logging.cpp:765
void logStart(const QString &logfile, bool progress, int quiet, int facility, LogLevel_t level, bool propagate, bool loglong, bool testHarness)
Entry point to start logging for the application.
Definition: logging.cpp:651
int syslogGetFacility(const QString &facility)
Map a syslog facility name back to the enumerated value.
Definition: logging.cpp:740
static int GetTermWidth(void)
returns terminal width, or 79 on error
static constexpr int k_defaultWidth
static QByteArray strip_quotes(const QByteArray &array)
static bool setUser(const QString &username)
Drop permissions to the specified user.
static bool openPidfile(std::ofstream &pidfs, const QString &pidfile)
static void wrapList(QStringList &list, int width)
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
#define ENO_STR
Definition: mythlogging.h:75
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
const char * GetMythSourceVersion()
Definition: mythversion.cpp:7
const char * GetMythSourcePath()
Definition: mythversion.cpp:12
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
Definition: mythdate.cpp:93
@ kFilename
Default UTC, "yyyyMMddhhmmss".
Definition: mythdate.h:19
@ ISODate
Default UTC.
Definition: mythdate.h:18
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
Definition: mythdate.cpp:39
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:15
def user_info(user, format="xml")
Definition: vimeo_api.py:517