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