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,0,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 QTextStream msg(&helpstr, QIODevice::WriteOnly);
370 int termwidth = GetTermWidth();
371 if (termwidth < off)
372 {
373 if (off > 70)
374 {
375 // developer has configured some absurdly long command line
376 // arguments, but we still need to do something
377 termwidth = off+40;
378 }
379 else
380 {
381 // user is running uselessly narrow console, use a sane console
382 // width instead
383 termwidth = k_defaultWidth;
384 }
385 }
386
387 if (m_help.isEmpty() && !force)
388 // only print if there is a short help to print
389 return helpstr;
390
391 if ((m_group != group) && !force)
392 // only print if looping over the correct group
393 return helpstr;
394
395 if (!m_parents.isEmpty() && !force)
396 {
397 // only print if an independent option, not subject
398 // to a parent option
399 return helpstr;
400 }
401
402 if (!m_deprecated.isEmpty())
403 // option is marked as deprecated, do not show
404 return helpstr;
405
406 if (!m_removed.isEmpty())
407 // option is marked as removed, do not show
408 return helpstr;
409
410 QString pad;
411 pad.fill(' ', off);
412
413 // print the first line with the available keywords
414 QStringList hlist = m_help.split('\n');
415 wrapList(hlist, termwidth-off);
416 msg << " ";
417 if (!m_parents.isEmpty())
418 msg << " ";
419 msg << GetKeywordString().leftJustified(off, ' ')
420 << hlist.takeFirst() << Qt::endl;
421
422 // print remaining lines with necessary padding
423 for (const auto & line : std::as_const(hlist))
424 msg << pad << line << Qt::endl;
425
426 // loop through any child arguments to print underneath
427 for (auto * arg : std::as_const(m_children))
428 msg << arg->GetHelpString(off, group, true);
429
430 msg.flush();
431 return helpstr;
432}
433
441QString CommandLineArg::GetLongHelpString(QString keyword) const
442{
443 QString helpstr;
444 QTextStream msg(&helpstr, QIODevice::WriteOnly);
445 int termwidth = GetTermWidth();
446
447 // help called for an argument that is not me, this should not happen
448 if (!m_keywords.contains(keyword))
449 return helpstr;
450
451 // argument has been marked as removed, so warn user of such
452 if (!m_removed.isEmpty())
453 {
454 PrintRemovedWarning(keyword);
455 // argument has been marked as deprecated, so warn user of such
456 }
457 else if (!m_deprecated.isEmpty())
458 {
459 PrintDeprecatedWarning(keyword);
460 }
461
462 msg << "Option: " << keyword << Qt::endl << Qt::endl;
463
464 bool first = true;
465
466 // print all related keywords, padding for multiples
467 for (const auto & word : std::as_const(m_keywords))
468 {
469 if (word != keyword)
470 {
471 if (first)
472 {
473 msg << "Aliases: " << word << Qt::endl;
474 first = false;
475 }
476 else
477 {
478 msg << " " << word << Qt::endl;
479 }
480 }
481 }
482
483 // print type and default for the stored value
484 msg << "Type: " << QMetaType(m_type).name() << Qt::endl;
485 if (m_default.canConvert<QString>())
486 msg << "Default: " << m_default.toString() << Qt::endl;
487
488 QStringList help;
489 if (m_longhelp.isEmpty())
490 help = m_help.split("\n");
491 else
492 help = m_longhelp.split("\n");
493 wrapList(help, termwidth-13);
494
495 // print description, wrapping and padding as necessary
496 msg << "Description: " << help.takeFirst() << Qt::endl;
497 for (const auto & line : std::as_const(help))
498 msg << " " << line << Qt::endl;
499
500 QList<CommandLineArg*>::const_iterator i2;
501
502 // loop through the four relation types and print
503 if (!m_parents.isEmpty())
504 {
505 msg << Qt::endl << "Can be used in combination with:" << Qt::endl;
506 for (auto * parent : std::as_const(m_parents))
507 msg << " " << parent->GetPreferredKeyword()
508 .toLocal8Bit().constData();
509 msg << Qt::endl;
510 }
511
512 if (!m_children.isEmpty())
513 {
514 msg << Qt::endl << "Allows the use of:" << Qt::endl;
515 for (i2 = m_children.constBegin(); i2 != m_children.constEnd(); ++i2)
516 msg << " " << (*i2)->GetPreferredKeyword()
517 .toLocal8Bit().constData();
518 msg << Qt::endl;
519 }
520
521 if (!m_requires.isEmpty())
522 {
523 msg << Qt::endl << "Requires the use of:" << Qt::endl;
524 for (i2 = m_requires.constBegin(); i2 != m_requires.constEnd(); ++i2)
525 msg << " " << (*i2)->GetPreferredKeyword()
526 .toLocal8Bit().constData();
527 msg << Qt::endl;
528 }
529
530 if (!m_blocks.isEmpty())
531 {
532 msg << Qt::endl << "Prevents the use of:" << Qt::endl;
533 for (i2 = m_blocks.constBegin(); i2 != m_blocks.constEnd(); ++i2)
534 msg << " " << (*i2)->GetPreferredKeyword()
535 .toLocal8Bit().constData();
536 msg << Qt::endl;
537 }
538
539 msg.flush();
540 return helpstr;
541}
542
549bool CommandLineArg::Set(const QString& opt)
550{
551 m_usedKeyword = opt;
552
553 switch (m_type)
554 {
555 case QMetaType::Bool:
556 m_stored = QVariant(!m_default.toBool());
557 break;
558
559 case QMetaType::Int:
560 if (m_stored.isNull())
561 m_stored = QVariant(1);
562 else
563 m_stored = QVariant(m_stored.toInt() + 1);
564 break;
565
566 case QMetaType::QString:
568 break;
569
570 default:
571 std::cerr << "Command line option did not receive value:" << std::endl
572 << " " << opt.toLocal8Bit().constData() << std::endl;
573 return false;
574 }
575
576 m_given = true;
577 return true;
578}
579
582bool CommandLineArg::Set(const QString& opt, const QByteArray& val)
583{
584 QVariantList vlist;
585 QList<QByteArray> blist;
586 QVariantMap vmap;
587 m_usedKeyword = opt;
588
589 switch (m_type)
590 {
591 case QMetaType::Bool:
592 std::cerr << "Boolean type options do not accept values:" << std::endl
593 << " " << opt.toLocal8Bit().constData() << std::endl;
594 return false;
595
596 case QMetaType::QString:
597 m_stored = QVariant(val);
598 break;
599
600 case QMetaType::Int:
601 m_stored = QVariant(val.toInt());
602 break;
603
604 case QMetaType::UInt:
605 m_stored = QVariant(val.toUInt());
606 break;
607
608 case QMetaType::LongLong:
609 m_stored = QVariant(val.toLongLong());
610 break;
611
612 case QMetaType::Double:
613 m_stored = QVariant(val.toDouble());
614 break;
615
616 case QMetaType::QDateTime:
617 m_stored = QVariant(MythDate::fromString(QString(val)));
618 break;
619
620 case QMetaType::QStringList:
621 if (!m_stored.isNull())
622 vlist = m_stored.toList();
623 vlist << val;
624 m_stored = QVariant(vlist);
625 break;
626
627 case QMetaType::QVariantMap:
628 if (!val.contains('='))
629 {
630 std::cerr << "Command line option did not get expected "
631 << "key/value pair" << std::endl;
632 return false;
633 }
634
635 blist = val.split('=');
636
637 if (!m_stored.isNull())
638 vmap = m_stored.toMap();
639 vmap[QString(strip_quotes(blist[0]))] = QVariant(strip_quotes(blist[1]));
640 m_stored = QVariant(vmap);
641 break;
642
643 case QMetaType::QSize:
644 if (!val.contains('x'))
645 {
646 std::cerr << "Command line option did not get expected "
647 << "XxY pair" << std::endl;
648 return false;
649 }
650
651 blist = val.split('x');
652 m_stored = QVariant(QSize(blist[0].toInt(), blist[1].toInt()));
653 break;
654
655 default:
656 m_stored = QVariant(val);
657 }
658
659 m_given = true;
660 return true;
661}
662
666{
667 m_children << new CommandLineArg(opt);
668 return this;
669}
670
674{
675 for (const auto& opt : std::as_const(opts))
676 m_children << new CommandLineArg(opt);
677 return this;
678}
679
683{
684 m_parents << new CommandLineArg(opt);
685 return this;
686}
687
691{
692 for (const auto& opt : std::as_const(opts))
693 m_parents << new CommandLineArg(opt);
694 return this;
695}
696
700{
701 m_parents << new CommandLineArg(opt);
702 return this;
703}
704
708{
709 for (const auto& opt : std::as_const(opts))
710 m_parents << new CommandLineArg(opt);
711 return this;
712}
713
717{
718 m_children << new CommandLineArg(opt);
719 return this;
720}
721
725{
726 for (const auto& opt : std::as_const(opts))
727 m_children << new CommandLineArg(opt);
728 return this;
729}
730
734{
735 m_children << new CommandLineArg(opt);
736 m_requires << new CommandLineArg(opt);
737 return this;
738}
739
743{
744 for (const auto& opt : std::as_const(opts))
745 {
746 m_children << new CommandLineArg(opt);
747 m_requires << new CommandLineArg(opt);
748 }
749 return this;
750}
751
755{
756 m_parents << new CommandLineArg(opt);
757 m_requiredby << new CommandLineArg(opt);
758 return this;
759}
760
764{
765 for (const auto& opt : std::as_const(opts))
766 {
767 m_parents << new CommandLineArg(opt);
768 m_requiredby << new CommandLineArg(opt);
769 }
770 return this;
771}
772
776{
777 m_requires << new CommandLineArg(opt);
778 return this;
779}
780
784{
785 for (const auto& opt : std::as_const(opts))
786 m_requires << new CommandLineArg(opt);
787 return this;
788}
789
793{
794 m_blocks << new CommandLineArg(opt);
795 return this;
796}
797
801{
802 for (const auto& opt : std::as_const(opts))
803 m_blocks << new CommandLineArg(opt);
804 return this;
805}
806
810{
811 if (depstr.isEmpty())
812 depstr = "and will be removed in a future version.";
813 m_deprecated = depstr;
814 return this;
815}
816
819CommandLineArg* CommandLineArg::SetRemoved(QString remstr, QString remver)
820{
821 if (remstr.isEmpty())
822 remstr = "and is no longer available in this version.";
823 m_removed = remstr;
824 m_removedversion = std::move(remver);
825 return this;
826}
827
834{
835 bool replaced = false;
836 other->IncrRef();
837
838 for (int i = 0; i < m_children.size(); i++)
839 {
840 if (m_children[i]->m_name == other->m_name)
841 {
842 m_children[i]->DecrRef();
843 m_children.replace(i, other);
844 replaced = true;
845 break;
846 }
847 }
848
849 if (!replaced)
850 m_children << other;
851
852 if (forward)
853 other->SetChildOf(this, false);
854}
855
862{
863 bool replaced = false;
864 other->IncrRef();
865
866 for (int i = 0; i < m_parents.size(); i++)
867 {
868 if (m_parents[i]->m_name == other->m_name)
869 {
870 m_parents[i]->DecrRef();
871 m_parents.replace(i, other);
872 replaced = true;
873 break;
874 }
875 }
876
877 if (!replaced)
878 m_parents << other;
879
880 if (forward)
881 other->SetParentOf(this, false);
882}
883
889void CommandLineArg::SetRequires(CommandLineArg *other, bool /*forward*/)
890{
891 bool replaced = false;
892 other->IncrRef();
893
894 for (int i = 0; i < m_requires.size(); i++)
895 {
896 if (m_requires[i]->m_name == other->m_name)
897 {
898 m_requires[i]->DecrRef();
899 m_requires.replace(i, other);
900 replaced = true;
901 break;
902 }
903 }
904
905 if (!replaced)
906 m_requires << other;
907
908// requirements need not be reciprocal
909// if (forward)
910// other->SetRequires(this, false);
911}
912
919{
920 bool replaced = false;
921 other->IncrRef();
922
923 for (int i = 0; i < m_blocks.size(); i++)
924 {
925 if (m_blocks[i]->m_name == other->m_name)
926 {
927 m_blocks[i]->DecrRef();
928 m_blocks.replace(i, other);
929 replaced = true;
930 break;
931 }
932 }
933
934 if (!replaced)
935 m_blocks << other;
936
937 if (forward)
938 other->SetBlocks(this, false);
939}
940
943void CommandLineArg::AllowOneOf(const QList<CommandLineArg*>& args)
944{
945 // TODO: blocks do not get set properly if multiple dummy arguments
946 // are provided. since this method will not have access to the
947 // argument list, this issue will have to be resolved later in
948 // ReconcileLinks().
949
950 // loop through all but the last entry
951 for (auto i1 = args.cbegin(); i1 != args.cend()-1; ++i1)
952 {
953 // loop through the next to the last entry
954 // and block use with the current
955 for (auto i2 = i1+1; i2 != args.cend(); ++i2)
956 {
957 (*i1)->SetBlocks(*i2);
958 }
959
960 if ((*i1)->m_type == QMetaType::UnknownType)
961 (*i1)->DecrRef();
962 }
963}
964
972{
973 if (!QCoreApplication::instance())
974 // QApplication not available, no sense doing anything yet
975 return;
976
977 if (m_converted)
978 // already run, abort
979 return;
980
981 if (!m_given)
982 {
983 // nothing to work on, abort
984 m_converted = true;
985 return;
986 }
987
988#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
989 auto storedType = static_cast<QMetaType::Type>(m_stored.type());
990#else
991 auto storedType = m_stored.typeId();
992#endif
993 if (m_type == QMetaType::QString)
994 {
995 if (storedType == QMetaType::QByteArray)
996 {
997 m_stored = QString::fromLocal8Bit(m_stored.toByteArray());
998 }
999 // else
1000 // not sure why this isnt a bytearray, but ignore it and
1001 // set it as converted
1002 }
1003 else if (m_type == QMetaType::QStringList)
1004 {
1005 if (storedType == QMetaType::QVariantList)
1006 {
1007 QVariantList vlist = m_stored.toList();
1008 QStringList slist;
1009 for (const auto& item : std::as_const(vlist))
1010 slist << QString::fromLocal8Bit(item.toByteArray());
1011 m_stored = QVariant(slist);
1012 }
1013 }
1014 else if (m_type == QMetaType::QVariantMap)
1015 {
1016 QVariantMap vmap = m_stored.toMap();
1017 // NOLINTNEXTLINE(modernize-loop-convert)
1018 for (auto iter = vmap.begin(); iter != vmap.end(); ++iter)
1019 (*iter) = QString::fromLocal8Bit(iter->toByteArray());
1020 }
1021 else
1022 {
1023 return;
1024 }
1025
1026 m_converted = true;
1027}
1028
1029
1036{
1037 QStringList::const_iterator it;
1038 QString preferred;
1039 int len = 0;
1040
1041 for (it = m_keywords.constBegin(); it != m_keywords.constEnd(); ++it)
1042 {
1043 int len2 = (*it).size();
1044 if (len2 > len)
1045 {
1046 preferred = *it;
1047 len = len2;
1048 }
1049 }
1050
1051 return preferred;
1052}
1053
1058{
1059 if (!m_given)
1060 return true; // not in use, no need for checks
1061
1062 QList<CommandLineArg*>::const_iterator i;
1063
1064 bool passes = false;
1065 for (i = m_parents.constBegin(); i != m_parents.constEnd(); ++i)
1066 {
1067 // one of these must have been defined
1068 if ((*i)->m_given)
1069 {
1070 passes = true;
1071 break;
1072 }
1073 }
1074 if (!passes && !m_parents.isEmpty())
1075 {
1076 std::cerr << "ERROR: " << m_usedKeyword.toLocal8Bit().constData()
1077 << " requires at least one of the following arguments" << std::endl;
1078 for (i = m_parents.constBegin(); i != m_parents.constEnd(); ++i)
1079 std::cerr << " "
1080 << (*i)->GetPreferredKeyword().toLocal8Bit().constData();
1081 std::cerr << std::endl << std::endl;
1082 return false;
1083 }
1084
1085 // we dont care about children
1086
1087 for (i = m_requires.constBegin(); i != m_requires.constEnd(); ++i)
1088 {
1089 // all of these must have been defined
1090 if (!(*i)->m_given)
1091 {
1092 std::cerr << "ERROR: " << m_usedKeyword.toLocal8Bit().constData()
1093 << " requires all of the following be defined as well"
1094 << std::endl;
1095 for (i = m_requires.constBegin(); i != m_requires.constEnd(); ++i)
1096 {
1097 std::cerr << " "
1098 << (*i)->GetPreferredKeyword().toLocal8Bit()
1099 .constData();
1100 }
1101 std::cerr << std::endl << std::endl;
1102 return false;
1103 }
1104 }
1105
1106 for (i = m_blocks.constBegin(); i != m_blocks.constEnd(); ++i)
1107 {
1108 // none of these can be defined
1109 if ((*i)->m_given)
1110 {
1111 std::cerr << "ERROR: " << m_usedKeyword.toLocal8Bit().constData()
1112 << " requires that none of the following be defined" << std::endl;
1113 for (i = m_blocks.constBegin(); i != m_blocks.constEnd(); ++i)
1114 {
1115 std::cerr << " "
1116 << (*i)->GetPreferredKeyword().toLocal8Bit()
1117 .constData();
1118 }
1119 std::cerr << std::endl << std::endl;
1120 return false;
1121 }
1122 }
1123
1124 return true;
1125}
1126
1130{
1131 // clear out interdependent pointers in preparation for deletion
1132 while (!m_parents.isEmpty())
1133 m_parents.takeFirst()->DecrRef();
1134
1135 while (!m_children.isEmpty())
1136 m_children.takeFirst()->DecrRef();
1137
1138 while (!m_blocks.isEmpty())
1139 m_blocks.takeFirst()->DecrRef();
1140
1141 while (!m_requires.isEmpty())
1142 m_requires.takeFirst()->DecrRef();
1143
1144 while (!m_requiredby.isEmpty())
1145 m_requiredby.takeFirst()->DecrRef();
1146}
1147
1151{
1152 if (!m_given)
1153 return;
1154
1155 std::cerr << " " << m_name.leftJustified(30).toLocal8Bit().constData();
1156
1157 QSize tmpsize;
1158 QMap<QString, QVariant> tmpmap;
1159 QMap<QString, QVariant>::const_iterator it;
1160 QVariantList vlist;
1161 bool first = true;
1162
1163 switch (m_type)
1164 {
1165 case QMetaType::Bool:
1166 std::cerr << (m_stored.toBool() ? "True" : "False") << std::endl;
1167 break;
1168
1169 case QMetaType::Int:
1170 std::cerr << m_stored.toInt() << std::endl;
1171 break;
1172
1173 case QMetaType::UInt:
1174 std::cerr << m_stored.toUInt() << std::endl;
1175 break;
1176
1177 case QMetaType::LongLong:
1178 std::cerr << m_stored.toLongLong() << std::endl;
1179 break;
1180
1181 case QMetaType::Double:
1182 std::cerr << m_stored.toDouble() << std::endl;
1183 break;
1184
1185 case QMetaType::QSize:
1186 tmpsize = m_stored.toSize();
1187 std::cerr << "x=" << tmpsize.width()
1188 << " y=" << tmpsize.height()
1189 << std::endl;
1190 break;
1191
1192 case QMetaType::QString:
1193 std::cerr << '"' << m_stored.toByteArray().constData()
1194 << '"' << std::endl;
1195 break;
1196
1197 case QMetaType::QStringList:
1198 vlist = m_stored.toList();
1199 std::cerr << '"' << vlist.takeFirst().toByteArray().constData() << '"';
1200 for (const auto& str : std::as_const(vlist))
1201 {
1202 std::cerr << ", \""
1203 << str.constData()
1204 << '"';
1205 }
1206 std::cerr << std::endl;
1207 break;
1208
1209 case QMetaType::QVariantMap:
1210 tmpmap = m_stored.toMap();
1211 for (it = tmpmap.cbegin(); it != tmpmap.cend(); ++it)
1212 {
1213 if (first)
1214 first = false;
1215 else
1216 std::cerr << QString("").leftJustified(32)
1217 .toLocal8Bit().constData();
1218
1219 std::cerr << it.key().toLocal8Bit().constData()
1220 << '='
1221 << it->toByteArray().constData()
1222 << std::endl;
1223 }
1224
1225 break;
1226
1227 case QMetaType::QDateTime:
1228 std::cerr << m_stored.toDateTime().toString(Qt::ISODate)
1229 .toLocal8Bit().constData()
1230 << std::endl;
1231 break;
1232
1233 default:
1234 std::cerr << std::endl;
1235 }
1236}
1237
1240void CommandLineArg::PrintRemovedWarning(QString &keyword) const
1241{
1242 QString warn = QString("%1 has been removed").arg(keyword);
1243 if (!m_removedversion.isEmpty())
1244 warn += QString(" as of MythTV %1").arg(m_removedversion);
1245
1246 std::cerr << QString("****************************************************\n"
1247 " WARNING: %1\n"
1248 " %2\n"
1249 "****************************************************\n\n")
1250 .arg(warn, m_removed)
1251 .toLocal8Bit().constData();
1252}
1253
1256void CommandLineArg::PrintDeprecatedWarning(QString &keyword) const
1257{
1258 std::cerr << QString("****************************************************\n"
1259 " WARNING: %1 has been deprecated\n"
1260 " %2\n"
1261 "****************************************************\n\n")
1262 .arg(keyword, m_deprecated)
1263 .toLocal8Bit().constData();
1264}
1265
1280 : m_appname(std::move(appname))
1281{
1282 if (qEnvironmentVariableIsSet("VERBOSE_PARSER"))
1283 {
1284 std::cerr << "MythCommandLineParser is now operating verbosely." << std::endl;
1285 m_verbose = true;
1286 }
1287
1289}
1290
1292{
1293 QString pidfile = toString("pidfile");
1294 if (!pidfile.isEmpty())
1295 {
1296 QFile::remove(pidfile);
1297 }
1298
1299 QMap<QString, CommandLineArg*>::iterator i;
1300
1301 i = m_namedArgs.begin();
1302 while (i != m_namedArgs.end())
1303 {
1304 (*i)->CleanupLinks();
1305 (*i)->DecrRef();
1306 i = m_namedArgs.erase(i);
1307 }
1308
1309 i = m_optionedArgs.begin();
1310 while (i != m_optionedArgs.end())
1311 {
1312 (*i)->DecrRef();
1313 i = m_optionedArgs.erase(i);
1314 }
1315}
1316
1351 const QString& name, QMetaType::Type type, QVariant def,
1352 QString help, QString longhelp)
1353{
1354 CommandLineArg *arg = nullptr;
1355
1356 if (m_namedArgs.contains(name))
1357 arg = m_namedArgs[name];
1358 else
1359 {
1360 arg = new CommandLineArg(name, type, std::move(def), std::move(help), std::move(longhelp));
1361 m_namedArgs.insert(name, arg);
1362 }
1363
1364 for (const auto & str : std::as_const(arglist))
1365 {
1366 if (!m_optionedArgs.contains(str))
1367 {
1368 arg->AddKeyword(str);
1369 if (m_verbose)
1370 {
1371 std::cerr << "Adding " << str.toLocal8Bit().constData()
1372 << " as taking type '"
1373#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1374 << QVariant::typeToName(static_cast<int>(type))
1375#else
1376 << QMetaType(type).name()
1377#endif
1378 << "'" << std::endl;
1379 }
1380 arg->IncrRef();
1381 m_optionedArgs.insert(str, arg);
1382 }
1383 }
1384
1385 return arg;
1386}
1387
1391{
1392 std::cout << "Please attach all output as a file in bug reports." << std::endl;
1393 std::cout << "MythTV Version : " << GetMythSourceVersion() << std::endl;
1394 std::cout << "MythTV Branch : " << GetMythSourcePath() << std::endl;
1395 std::cout << "Network Protocol : " << MYTH_PROTO_VERSION << std::endl;
1396 std::cout << "Library API : " << MYTH_BINARY_VERSION << std::endl;
1397 std::cout << "QT Version : " << QT_VERSION_STR << std::endl;
1398#ifdef MYTH_BUILD_CONFIG
1399 std::cout << "Options compiled in:" <<std::endl;
1400 std::cout << MYTH_BUILD_CONFIG << std::endl;
1401#endif
1402}
1403
1407{
1408 QString help = GetHelpString();
1409 std::cerr << help.toLocal8Bit().constData();
1410}
1411
1418{
1419 QString helpstr;
1420 QTextStream msg(&helpstr, QIODevice::WriteOnly);
1421
1422 QString versionStr = QString("%1 version: %2 [%3] www.mythtv.org")
1424 msg << versionStr << Qt::endl;
1425
1426 if (toString("showhelp").isEmpty())
1427 {
1428 // build generic help text
1429
1430 QString descr = GetHelpHeader();
1431 if (descr.size() > 0)
1432 msg << Qt::endl << descr << Qt::endl << Qt::endl;
1433
1434 // loop through registered arguments to populate list of groups
1435 QStringList groups("");
1436 int maxlen = 0;
1437 for (auto * cmdarg : std::as_const(m_namedArgs))
1438 {
1439 maxlen = std::max(cmdarg->GetKeywordLength(), maxlen);
1440 if (!groups.contains(cmdarg->m_group))
1441 groups << cmdarg->m_group;
1442 }
1443
1444 // loop through list of groups and print help string for each
1445 // arguments will filter themselves if they are not in the group
1446 maxlen += 4;
1447 for (const auto & group : std::as_const(groups))
1448 {
1449 if (group.isEmpty())
1450 msg << "Misc. Options:" << Qt::endl << Qt::endl;
1451 else
1452 msg << group.toLocal8Bit().constData() << " Options:" << Qt::endl << Qt::endl;
1453
1454 for (auto * cmdarg : std::as_const(m_namedArgs))
1455 msg << cmdarg->GetHelpString(maxlen, group);
1456 msg << Qt::endl;
1457 }
1458 }
1459 else
1460 {
1461 // build help for a specific argument
1462 QString optstr = "-" + toString("showhelp");
1463 if (!m_optionedArgs.contains(optstr))
1464 {
1465 optstr = "-" + optstr;
1466 if (!m_optionedArgs.contains(optstr))
1467 return QString("Could not find option matching '%1'\n")
1468 .arg(toString("showhelp"));
1469 }
1470
1471 if (m_optionedArgs[optstr] != nullptr)
1472 msg << m_optionedArgs[optstr]->GetLongHelpString(optstr);
1473 }
1474
1475 msg.flush();
1476 return helpstr;
1477}
1478
1482 int &argpos, QString &opt, QByteArray &val)
1483{
1484 opt.clear();
1485 val.clear();
1486
1487 if (argpos >= argc)
1488 // this shouldnt happen, return and exit
1489 return Result::kEnd;
1490
1491 QByteArray tmp(argv[argpos]);
1492 if (tmp.isEmpty())
1493 // string is empty, return and loop
1494 return Result::kEmpty;
1495
1497 {
1498 // pass through has been activated
1499 val = tmp;
1500 return Result::kArg;
1501 }
1502
1503 if (tmp.startsWith('-') && tmp.size() > 1)
1504 {
1505 if (tmp == "--")
1506 {
1507 // all options beyond this will be passed as a single string
1508 m_passthroughActive = true;
1509 return Result::kPassthrough;
1510 }
1511
1512 if (tmp.contains('='))
1513 {
1514 // option contains '=', split
1515 QList<QByteArray> blist = tmp.split('=');
1516
1517 if (blist.size() != 2)
1518 {
1519 // more than one '=' in option, this is not handled
1520 opt = QString(tmp);
1521 return Result::kInvalid;
1522 }
1523
1524 opt = QString(strip_quotes(blist[0]));
1525 val = strip_quotes(blist[1]);
1526 return Result::kCombOptVal;
1527 }
1528
1529 opt = QString(tmp);
1530
1531 if (argpos+1 >= argc)
1532 // end of input, option only
1533 return Result::kOptOnly;
1534
1535 tmp = QByteArray(argv[++argpos]);
1536 if (tmp.isEmpty())
1537 // empty string, option only
1538 return Result::kOptOnly;
1539
1540 if (tmp.startsWith("-") && tmp.size() > 1)
1541 {
1542 // no value found for option, backtrack
1543 argpos--;
1544 return Result::kOptOnly;
1545 }
1546
1547 val = tmp;
1548 return Result::kOptVal;
1549 }
1550
1551 // input is not an option string, return as arg
1552 val = tmp;
1553 return Result::kArg;
1554}
1555
1562bool MythCommandLineParser::Parse(int argc, const char * const * argv)
1563{
1564 Result res = Result::kEnd;
1565 QString opt;
1566 QByteArray val;
1567 CommandLineArg *argdef = nullptr;
1568
1569 // reconnect interdependencies between command line options
1570 if (!ReconcileLinks())
1571 return false;
1572
1573 // loop through command line arguments until all are spent
1574 for (int argpos = 1; argpos < argc; ++argpos)
1575 {
1576
1577 // pull next option
1578 res = getOpt(argc, argv, argpos, opt, val);
1579
1580 if (m_verbose)
1581 {
1582 std::cerr << "res: " << NamedOptType(res) << std::endl
1583 << "opt: " << opt.toLocal8Bit().constData() << std::endl
1584 << "val: " << val.constData() << std::endl << std::endl;
1585 }
1586
1587 // '--' found on command line, enable passthrough mode
1588 if (res == Result::kPassthrough && !m_namedArgs.contains("_passthrough"))
1589 {
1590 std::cerr << "Received '--' but passthrough has not been enabled" << std::endl;
1591 SetValue("showhelp", "");
1592 return false;
1593 }
1594
1595 // end of options found, terminate loop
1596 if (res == Result::kEnd)
1597 break;
1598
1599 // GetOpt pulled an empty option, this shouldnt happen by ignore
1600 // it and continue
1601 if (res == Result::kEmpty)
1602 continue;
1603
1604 // more than one equal found in key/value pair, fault out
1605 if (res == Result::kInvalid)
1606 {
1607 std::cerr << "Invalid option received:" << std::endl << " "
1608 << opt.toLocal8Bit().constData();
1609 SetValue("showhelp", "");
1610 return false;
1611 }
1612
1613 // passthrough is active, so add the data to the stringlist
1615 {
1616 m_namedArgs["_passthrough"]->Set("", val);
1617 continue;
1618 }
1619
1620 // argument with no preceeding '-' encountered, add to stringlist
1621 if (res == Result::kArg)
1622 {
1623 if (!m_namedArgs.contains("_args"))
1624 {
1625 std::cerr << "Received '"
1626 << val.constData()
1627 << "' but unassociated arguments have not been enabled"
1628 << std::endl;
1629 SetValue("showhelp", "");
1630 return false;
1631 }
1632
1633 m_namedArgs["_args"]->Set("", val);
1634 continue;
1635 }
1636
1637 // this line should not be passed once arguments have started collecting
1638 if (toBool("_args"))
1639 {
1640 std::cerr << "Command line arguments received out of sequence"
1641 << std::endl;
1642 SetValue("showhelp", "");
1643 return false;
1644 }
1645
1646#ifdef Q_OS_DARWIN
1647 if (opt.startsWith("-psn_"))
1648 {
1649 std::cerr << "Ignoring Process Serial Number from command line"
1650 << std::endl;
1651 continue;
1652 }
1653#endif
1654
1655 if (!m_optionedArgs.contains(opt))
1656 {
1657 // argument is unhandled, check if parser allows arbitrary input
1658 if (m_namedArgs.contains("_extra"))
1659 {
1660 // arbitrary allowed, specify general collection pool
1661 argdef = m_namedArgs["_extra"];
1662 QByteArray tmp = opt.toLocal8Bit();
1663 tmp += '=';
1664 tmp += val;
1665 val = tmp;
1666 res = Result::kOptVal;
1667 }
1668 else
1669 {
1670 // arbitrary not allowed, fault out
1671 std::cerr << "Unhandled option given on command line:" << std::endl
1672 << " " << opt.toLocal8Bit().constData() << std::endl;
1673 SetValue("showhelp", "");
1674 return false;
1675 }
1676 }
1677 else
1678 {
1679 argdef = m_optionedArgs[opt];
1680 }
1681
1682 // argument has been marked as removed, warn user and fail
1683 if (!argdef->m_removed.isEmpty())
1684 {
1685 argdef->PrintRemovedWarning(opt);
1686 SetValue("showhelp", "");
1687 return false;
1688 }
1689
1690 // argument has been marked as deprecated, warn user
1691 if (!argdef->m_deprecated.isEmpty())
1692 argdef->PrintDeprecatedWarning(opt);
1693
1694 if (m_verbose)
1695 std::cerr << "name: " << argdef->GetName().toLocal8Bit().constData()
1696 << std::endl;
1697
1698 // argument is keyword only, no value
1699 if (res == Result::kOptOnly)
1700 {
1701 if (!argdef->Set(opt))
1702 {
1703 SetValue("showhelp", "");
1704 return false;
1705 }
1706 }
1707 // argument has keyword and value
1708 else if ((res == Result::kOptVal) || (res == Result::kCombOptVal))
1709 {
1710 if (!argdef->Set(opt, val))
1711 {
1712 // if option and value were combined with a '=', abort directly
1713 // otherwise, attempt processing them independenly
1714 if ((res == Result::kCombOptVal) || !argdef->Set(opt))
1715 {
1716 SetValue("showhelp", "");
1717 return false;
1718 }
1719 // drop back an iteration so the unused value will get
1720 // processed a second time as a keyword-less argument
1721 --argpos;
1722 }
1723 }
1724 else
1725 {
1726 SetValue("showhelp", "");
1727 return false; // this should not occur
1728 }
1729
1730 if (m_verbose)
1731 std::cerr << "value: " << argdef->m_stored.toString().toLocal8Bit().constData()
1732 << std::endl;
1733 }
1734
1735 if (m_verbose)
1736 {
1737 std::cerr << "Processed option list:" << std::endl;
1738 for (auto * cmdarg : std::as_const(m_namedArgs))
1739 cmdarg->PrintVerbose();
1740
1741 if (m_namedArgs.contains("_args"))
1742 {
1743 std::cerr << std::endl << "Extra argument list:" << std::endl;
1744 QStringList slist = toStringList("_args");
1745 for (const auto& lopt : std::as_const(slist))
1746 std::cerr << " " << (lopt).toLocal8Bit().constData() << std::endl;
1747 }
1748
1749 if (m_namedArgs.contains("_passthrough"))
1750 {
1751 std::cerr << std::endl << "Passthrough string:" << std::endl;
1752 std::cerr << " " << GetPassthrough().toLocal8Bit().constData() << std::endl;
1753 }
1754
1755 std::cerr << std::endl;
1756 }
1757
1758 // make sure all interdependencies are fulfilled
1759 for (auto * cmdarg : std::as_const(m_namedArgs))
1760 {
1761 if (!cmdarg->TestLinks())
1762 {
1763 QString keyword = cmdarg->m_usedKeyword;
1764 if (keyword.startsWith('-'))
1765 {
1766 if (keyword.startsWith("--"))
1767 keyword.remove(0,2);
1768 else
1769 keyword.remove(0,1);
1770 }
1771
1772 SetValue("showhelp", keyword);
1773 return false;
1774 }
1775 }
1776
1777 return true;
1778}
1779
1780CommandLineArg* MythCommandLineParser::add(const QString& arg, const QString& name, bool def,
1781 QString help, QString longhelp)
1782{
1783 return add(QStringList(arg), name, QMetaType::Bool, QVariant(def), std::move(help), std::move(longhelp));
1784}
1785
1786CommandLineArg* MythCommandLineParser::add(const QString& arg, const QString& name, int def,
1787 QString help, QString longhelp)
1788{
1789 return add(QStringList(arg), name, QMetaType::Int, QVariant(def), std::move(help), std::move(longhelp));
1790}
1791
1792CommandLineArg* MythCommandLineParser::add(const QString& arg, const QString& name, uint def,
1793 QString help, QString longhelp)
1794{
1795 return add(QStringList(arg), name, QMetaType::UInt, QVariant(def), std::move(help), std::move(longhelp));
1796}
1797
1798CommandLineArg* MythCommandLineParser::add(const QString& arg, const QString& name, long long def,
1799 QString help, QString longhelp)
1800{
1801 return add(QStringList(arg), name, QMetaType::LongLong, QVariant(def), std::move(help), std::move(longhelp));
1802}
1803
1804CommandLineArg* MythCommandLineParser::add(const QString& arg, const QString& name, double def,
1805 QString help, QString longhelp)
1806{
1807 return add(QStringList(arg), name, QMetaType::Double, QVariant(def), std::move(help), std::move(longhelp));
1808}
1809
1810CommandLineArg* MythCommandLineParser::add(const QString& arg, const QString& name, const char *def,
1811 QString help, QString longhelp)
1812{
1813 return add(QStringList(arg), name, QMetaType::QString, QVariant(def), std::move(help), std::move(longhelp));
1814}
1815
1816CommandLineArg* MythCommandLineParser::add(const QString& arg, const QString& name, const QString& def,
1817 QString help, QString longhelp)
1818{
1819 return add(QStringList(arg), name, QMetaType::QString, QVariant(def), std::move(help), std::move(longhelp));
1820}
1821
1822CommandLineArg* MythCommandLineParser::add(const QString& arg, const QString& name, QSize def,
1823 QString help, QString longhelp)
1824{
1825 return add(QStringList(arg), name, QMetaType::QSize, QVariant(def), std::move(help), std::move(longhelp));
1826}
1827
1828CommandLineArg* MythCommandLineParser::add(const QString& arg, const QString& name, const QDateTime& def,
1829 QString help, QString longhelp)
1830{
1831 return add(QStringList(arg), name, QMetaType::QDateTime, QVariant(def), std::move(help), std::move(longhelp));
1832}
1833
1834CommandLineArg* MythCommandLineParser::add(const QString& arg, const QString& name, QMetaType::Type type,
1835 QString help, QString longhelp)
1836{
1837 return add(QStringList(arg), name, type,
1838#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1839 QVariant(static_cast<QVariant::Type>(type)),
1840#else
1841 QVariant(QMetaType(type)),
1842#endif
1843 std::move(help), std::move(longhelp));
1844}
1845
1846CommandLineArg* MythCommandLineParser::add(const QString& arg, const QString& name,
1847 QMetaType::Type type,
1848 QVariant def, QString help, QString longhelp)
1849{
1850 return add(QStringList(arg), name, type, std::move(def), std::move(help), std::move(longhelp));
1851}
1852
1853CommandLineArg* MythCommandLineParser::add(QStringList arglist, const QString& name, bool def,
1854 QString help, QString longhelp)
1855{
1856 return add(std::move(arglist), name, QMetaType::Bool, QVariant(def), std::move(help), std::move(longhelp));
1857}
1858
1859CommandLineArg* MythCommandLineParser::add(QStringList arglist, const QString& name, int def,
1860 QString help, QString longhelp)
1861{
1862 return add(std::move(arglist), name, QMetaType::Int, QVariant(def), std::move(help), std::move(longhelp));
1863}
1864
1865CommandLineArg* MythCommandLineParser::add(QStringList arglist, const QString& name, uint def,
1866 QString help, QString longhelp)
1867{
1868 return add(std::move(arglist), name, QMetaType::UInt, QVariant(def), std::move(help), std::move(longhelp));
1869}
1870
1871CommandLineArg* MythCommandLineParser::add(QStringList arglist, const QString& name, long long def,
1872 QString help, QString longhelp)
1873{
1874 return add(std::move(arglist), name, QMetaType::LongLong, QVariant(def), std::move(help), std::move(longhelp));
1875}
1876
1877CommandLineArg* MythCommandLineParser::add(QStringList arglist, const QString& name, double def,
1878 QString help, QString longhelp)
1879{
1880 return add(std::move(arglist), name, QMetaType::Double, QVariant(def), std::move(help), std::move(longhelp));
1881}
1882
1883CommandLineArg* MythCommandLineParser::add(QStringList arglist, const QString& name, const char *def,
1884 QString help, QString longhelp)
1885{
1886 return add(std::move(arglist), name, QMetaType::QString, QVariant(def), std::move(help), std::move(longhelp));
1887}
1888
1889CommandLineArg* MythCommandLineParser::add(QStringList arglist, const QString& name, const QString& def,
1890 QString help, QString longhelp)
1891{
1892 return add(std::move(arglist), name, QMetaType::QString, QVariant(def), std::move(help), std::move(longhelp));
1893}
1894
1895CommandLineArg* MythCommandLineParser::add(QStringList arglist, const QString& name, QSize def,
1896 QString help, QString longhelp)
1897{
1898 return add(std::move(arglist), name, QMetaType::QSize, QVariant(def), std::move(help), std::move(longhelp));
1899}
1900
1901CommandLineArg* MythCommandLineParser::add(QStringList arglist, const QString& name, const QDateTime& def,
1902 QString help, QString longhelp)
1903{
1904 return add(std::move(arglist), name, QMetaType::QDateTime, QVariant(def), std::move(help), std::move(longhelp));
1905}
1906
1907CommandLineArg* MythCommandLineParser::add(QStringList arglist, const QString& name,
1908 QMetaType::Type type,
1909 QString help, QString longhelp)
1910{
1911 return add(std::move(arglist), name, type,
1912#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1913 QVariant(static_cast<QVariant::Type>(type)),
1914#else
1915 QVariant(QMetaType(type)),
1916#endif
1917 std::move(help), std::move(longhelp));
1918}
1919
1924{
1925 if (m_verbose)
1926 std::cerr << "Reconciling links for option interdependencies." << std::endl;
1927
1928 QMap<QString,CommandLineArg*>::iterator args_it;
1929 for (args_it = m_namedArgs.begin(); args_it != m_namedArgs.end(); ++args_it)
1930 {
1931 QList<CommandLineArg*> links = (*args_it)->m_parents;
1932 QList<CommandLineArg*>::iterator links_it;
1933 for (links_it = links.begin(); links_it != links.end(); ++links_it)
1934 {
1935 if ((*links_it)->m_type != QMetaType::UnknownType)
1936 continue; // already handled
1937
1938 if (!m_namedArgs.contains((*links_it)->m_name))
1939 {
1940 // not found
1941 std::cerr << "ERROR: could not reconcile linked argument." << std::endl
1942 << " '" << (*args_it)->m_name.toLocal8Bit().constData()
1943 << "' could not find '"
1944 << (*links_it)->m_name.toLocal8Bit().constData()
1945 << "'." << std::endl
1946 << " Please resolve dependency and recompile." << std::endl;
1947 return false;
1948 }
1949
1950 // replace linked argument
1951 if (m_verbose)
1952 {
1953 std::cerr << QString(" Setting %1 as child of %2")
1954 .arg((*args_it)->m_name, (*links_it)->m_name)
1955 .toLocal8Bit().constData()
1956 << std::endl;
1957 }
1958 (*args_it)->SetChildOf(m_namedArgs[(*links_it)->m_name]);
1959 }
1960
1961 links = (*args_it)->m_children;
1962 for (links_it = links.begin(); links_it != links.end(); ++links_it)
1963 {
1964 if ((*links_it)->m_type != QMetaType::UnknownType)
1965 continue; // already handled
1966
1967 if (!m_namedArgs.contains((*links_it)->m_name))
1968 {
1969 // not found
1970 std::cerr << "ERROR: could not reconcile linked argument." << std::endl
1971 << " '" << (*args_it)->m_name.toLocal8Bit().constData()
1972 << "' could not find '"
1973 << (*links_it)->m_name.toLocal8Bit().constData()
1974 << "'." << std::endl
1975 << " Please resolve dependency and recompile." << std::endl;
1976 return false;
1977 }
1978
1979 // replace linked argument
1980 if (m_verbose)
1981 {
1982 std::cerr << QString(" Setting %1 as parent of %2")
1983 .arg((*args_it)->m_name, (*links_it)->m_name)
1984 .toLocal8Bit().constData()
1985 << std::endl;
1986 }
1987 (*args_it)->SetParentOf(m_namedArgs[(*links_it)->m_name]);
1988 }
1989
1990 links = (*args_it)->m_requires;
1991 for (links_it = links.begin(); links_it != links.end(); ++links_it)
1992 {
1993 if ((*links_it)->m_type != QMetaType::UnknownType)
1994 continue; // already handled
1995
1996 if (!m_namedArgs.contains((*links_it)->m_name))
1997 {
1998 // not found
1999 std::cerr << "ERROR: could not reconcile linked argument." << std::endl
2000 << " '" << (*args_it)->m_name.toLocal8Bit().constData()
2001 << "' could not find '"
2002 << (*links_it)->m_name.toLocal8Bit().constData()
2003 << "'." << std::endl
2004 << " Please resolve dependency and recompile." << std::endl;
2005 return false;
2006 }
2007
2008 // replace linked argument
2009 if (m_verbose)
2010 {
2011 std::cerr << QString(" Setting %1 as requiring %2")
2012 .arg((*args_it)->m_name, (*links_it)->m_name)
2013 .toLocal8Bit().constData()
2014 << std::endl;
2015 }
2016 (*args_it)->SetRequires(m_namedArgs[(*links_it)->m_name]);
2017 }
2018
2019 QList<CommandLineArg*>::iterator req_it =
2020 (*args_it)->m_requiredby.begin();
2021 while (req_it != (*args_it)->m_requiredby.end())
2022 {
2023 if ((*req_it)->m_type == QMetaType::UnknownType)
2024 {
2025 // if its not an invalid, it shouldnt be here anyway
2026 if (m_namedArgs.contains((*req_it)->m_name))
2027 {
2028 m_namedArgs[(*req_it)->m_name]->SetRequires(*args_it);
2029 if (m_verbose)
2030 {
2031 std::cerr << QString(" Setting %1 as blocking %2")
2032 .arg((*args_it)->m_name,
2033 (*req_it)->m_name)
2034 .toLocal8Bit().constData()
2035 << std::endl;
2036 }
2037 }
2038 }
2039
2040 (*req_it)->DecrRef();
2041 req_it = (*args_it)->m_requiredby.erase(req_it);
2042 }
2043
2044 QList<CommandLineArg*>::iterator block_it =
2045 (*args_it)->m_blocks.begin();
2046 while (block_it != (*args_it)->m_blocks.end())
2047 {
2048 if ((*block_it)->m_type != QMetaType::UnknownType)
2049 {
2050 ++block_it;
2051 continue; // already handled
2052 }
2053
2054 if (!m_namedArgs.contains((*block_it)->m_name))
2055 {
2056 (*block_it)->DecrRef();
2057 block_it = (*args_it)->m_blocks.erase(block_it);
2058 continue; // if it doesnt exist, it cant block this command
2059 }
2060
2061 // replace linked argument
2062 if (m_verbose)
2063 {
2064 std::cerr << QString(" Setting %1 as blocking %2")
2065 .arg((*args_it)->m_name, (*block_it)->m_name)
2066 .toLocal8Bit().constData()
2067 << std::endl;
2068 }
2069 (*args_it)->SetBlocks(m_namedArgs[(*block_it)->m_name]);
2070 ++block_it;
2071 }
2072 }
2073
2074 return true;
2075}
2076
2080QVariant MythCommandLineParser::operator[](const QString &name)
2081{
2082 QVariant var("");
2083 if (!m_namedArgs.contains(name))
2084 return var;
2085
2086 CommandLineArg *arg = m_namedArgs[name];
2087
2088 if (arg->m_given)
2089 var = arg->m_stored;
2090 else
2091 var = arg->m_default;
2092
2093 return var;
2094}
2095
2099QStringList MythCommandLineParser::GetArgs(void) const
2100{
2101 return toStringList("_args");
2102}
2103
2107QMap<QString,QString> MythCommandLineParser::GetExtra(void) const
2108{
2109 return toMap("_extra");
2110}
2111
2115{
2116 return toStringList("_passthrough").join(" ");
2117}
2118
2127{
2128 QMap<QString,QString> smap = toMap("overridesettings");
2129
2131 {
2132 if (toBool("overridesettingsfile"))
2133 {
2134 QString filename = toString("overridesettingsfile");
2135 if (!filename.isEmpty())
2136 {
2137 QFile f(filename);
2138 if (f.open(QIODevice::ReadOnly))
2139 {
2140 QTextStream in(&f);
2141 while (!in.atEnd()) {
2142 QString line = in.readLine().trimmed();
2143 QStringList tokens = line.split("=",
2144 Qt::SkipEmptyParts);
2145 if (tokens.size() == 2)
2146 {
2147 static const QRegularExpression kQuoteStartRE { "^[\"']" };
2148 static const QRegularExpression kQuoteEndRE { "[\"']$" };
2149 tokens[0].remove(kQuoteStartRE);
2150 tokens[0].remove(kQuoteEndRE);
2151 tokens[1].remove(kQuoteStartRE);
2152 tokens[1].remove(kQuoteEndRE);
2153 if (!tokens[0].isEmpty())
2154 smap[tokens[0]] = tokens[1];
2155 }
2156 }
2157 }
2158 else
2159 {
2160 QByteArray tmp = filename.toLatin1();
2161 std::cerr << "Failed to open the override settings file: '"
2162 << tmp.constData() << "'" << std::endl;
2163 }
2164 }
2165 }
2166
2167 if (toBool("windowed"))
2168 smap["RunFrontendInWindow"] = "1";
2169 else if (toBool("notwindowed"))
2170 smap["RunFrontendInWindow"] = "0";
2171
2172 if (toBool("mousecursor"))
2173 smap["HideMouseCursor"] = "0";
2174 else if (toBool("nomousecursor"))
2175 smap["HideMouseCursor"] = "1";
2176
2177 m_overridesImported = true;
2178
2179 if (!smap.isEmpty())
2180 {
2181 QVariantMap vmap;
2182 for (auto it = smap.cbegin(); it != smap.cend(); ++it)
2183 vmap[it.key()] = QVariant(it.value());
2184
2185 m_namedArgs["overridesettings"]->Set(QVariant(vmap));
2186 }
2187 }
2188
2189 if (m_verbose)
2190 {
2191 std::cerr << "Option Overrides:" << std::endl;
2192 QMap<QString, QString>::const_iterator it;
2193 for (it = smap.constBegin(); it != smap.constEnd(); ++it)
2194 std::cerr << QString(" %1 - %2").arg(it.key(), 30).arg(*it)
2195 .toLocal8Bit().constData() << std::endl;
2196 }
2197
2198 return smap;
2199}
2200
2207bool MythCommandLineParser::toBool(const QString& key) const
2208{
2209 if (!m_namedArgs.contains(key))
2210 return false;
2211
2212 CommandLineArg *arg = m_namedArgs[key];
2213 if (arg == nullptr)
2214 return false;
2215
2216 if (arg->m_type == QMetaType::Bool)
2217 {
2218 if (arg->m_given)
2219 return arg->m_stored.toBool();
2220 return arg->m_default.toBool();
2221 }
2222
2223 return arg->m_given;
2224}
2225
2229int MythCommandLineParser::toInt(const QString& key) const
2230{
2231 int val = 0;
2232 if (!m_namedArgs.contains(key))
2233 return val;
2234
2235 CommandLineArg *arg = m_namedArgs[key];
2236 if (arg == nullptr)
2237 return val;
2238
2239 if (arg->m_given)
2240 {
2241 if (arg->m_stored.canConvert<int>())
2242 val = arg->m_stored.toInt();
2243 }
2244 else
2245 {
2246 if (arg->m_default.canConvert<int>())
2247 val = arg->m_default.toInt();
2248 }
2249
2250 return val;
2251}
2252
2256uint MythCommandLineParser::toUInt(const QString& key) const
2257{
2258 uint val = 0;
2259 if (!m_namedArgs.contains(key))
2260 return val;
2261
2262 CommandLineArg *arg = m_namedArgs[key];
2263 if (arg == nullptr)
2264 return val;
2265
2266 if (arg->m_given)
2267 {
2268 if (arg->m_stored.canConvert<uint>())
2269 val = arg->m_stored.toUInt();
2270 }
2271 else
2272 {
2273 if (arg->m_default.canConvert<uint>())
2274 val = arg->m_default.toUInt();
2275 }
2276
2277 return val;
2278}
2279
2283long long MythCommandLineParser::toLongLong(const QString& key) const
2284{
2285 long long val = 0;
2286 if (!m_namedArgs.contains(key))
2287 return val;
2288
2289 CommandLineArg *arg = m_namedArgs[key];
2290 if (arg == nullptr)
2291 return val;
2292
2293 if (arg->m_given)
2294 {
2295 if (arg->m_stored.canConvert<long long>())
2296 val = arg->m_stored.toLongLong();
2297 }
2298 else
2299 {
2300 if (arg->m_default.canConvert<long long>())
2301 val = arg->m_default.toLongLong();
2302 }
2303
2304 return val;
2305}
2306
2310double MythCommandLineParser::toDouble(const QString& key) const
2311{
2312 double val = 0.0;
2313 if (!m_namedArgs.contains(key))
2314 return val;
2315
2316 CommandLineArg *arg = m_namedArgs[key];
2317 if (arg == nullptr)
2318 return val;
2319
2320 if (arg->m_given)
2321 {
2322 if (arg->m_stored.canConvert<double>())
2323 val = arg->m_stored.toDouble();
2324 }
2325 else
2326 {
2327 if (arg->m_default.canConvert<double>())
2328 val = arg->m_default.toDouble();
2329 }
2330
2331 return val;
2332}
2333
2337QSize MythCommandLineParser::toSize(const QString& key) const
2338{
2339 QSize val(0,0);
2340 if (!m_namedArgs.contains(key))
2341 return val;
2342
2343 CommandLineArg *arg = m_namedArgs[key];
2344 if (arg == nullptr)
2345 return val;
2346
2347 if (arg->m_given)
2348 {
2349 if (arg->m_stored.canConvert<QSize>())
2350 val = arg->m_stored.toSize();
2351 }
2352 else
2353 {
2354 if (arg->m_default.canConvert<QSize>())
2355 val = arg->m_default.toSize();
2356 }
2357
2358 return val;
2359}
2360
2364QString MythCommandLineParser::toString(const QString& key) const
2365{
2366 QString val("");
2367 if (!m_namedArgs.contains(key))
2368 return val;
2369
2370 CommandLineArg *arg = m_namedArgs[key];
2371 if (arg == nullptr)
2372 return val;
2373
2374 if (arg->m_given)
2375 {
2376 if (!arg->m_converted)
2377 arg->Convert();
2378
2379 if (arg->m_stored.canConvert<QString>())
2380 val = arg->m_stored.toString();
2381 }
2382 else
2383 {
2384 if (arg->m_default.canConvert<QString>())
2385 val = arg->m_default.toString();
2386 }
2387
2388 return val;
2389}
2390
2395QStringList MythCommandLineParser::toStringList(const QString& key, const QString& sep) const
2396{
2397 QVariant varval;
2398 QStringList val;
2399 if (!m_namedArgs.contains(key))
2400 return val;
2401
2402 CommandLineArg *arg = m_namedArgs[key];
2403 if (arg == nullptr)
2404 return val;
2405
2406 if (arg->m_given)
2407 {
2408 if (!arg->m_converted)
2409 arg->Convert();
2410
2411 varval = arg->m_stored;
2412 }
2413 else
2414 {
2415 varval = arg->m_default;
2416 }
2417
2418 if (arg->m_type == QMetaType::QString && !sep.isEmpty())
2419 val = varval.toString().split(sep);
2420 else if (varval.canConvert<QStringList>())
2421 val = varval.toStringList();
2422
2423 return val;
2424}
2425
2429QMap<QString,QString> MythCommandLineParser::toMap(const QString& key) const
2430{
2431 QMap<QString, QString> val;
2432 QMap<QString, QVariant> tmp;
2433 if (!m_namedArgs.contains(key))
2434 return val;
2435
2436 CommandLineArg *arg = m_namedArgs[key];
2437 if (arg == nullptr)
2438 return val;
2439
2440 if (arg->m_given)
2441 {
2442 if (!arg->m_converted)
2443 arg->Convert();
2444
2445 if (arg->m_stored.canConvert<QMap<QString, QVariant>>())
2446 tmp = arg->m_stored.toMap();
2447 }
2448 else
2449 {
2450 if (arg->m_default.canConvert<QMap<QString, QVariant>>())
2451 tmp = arg->m_default.toMap();
2452 }
2453
2454 for (auto i = tmp.cbegin(); i != tmp.cend(); ++i)
2455 val[i.key()] = i.value().toString();
2456
2457 return val;
2458}
2459
2463QDateTime MythCommandLineParser::toDateTime(const QString& key) const
2464{
2465 QDateTime val;
2466 if (!m_namedArgs.contains(key))
2467 return val;
2468
2469 CommandLineArg *arg = m_namedArgs[key];
2470 if (arg == nullptr)
2471 return val;
2472
2473 if (arg->m_given)
2474 {
2475 if (arg->m_stored.canConvert<QDateTime>())
2476 val = arg->m_stored.toDateTime();
2477 }
2478 else
2479 {
2480 if (arg->m_default.canConvert<QDateTime>())
2481 val = arg->m_default.toDateTime();
2482 }
2483
2484 return val;
2485}
2486
2491{
2492 if (m_namedArgs.contains("_args"))
2493 {
2494 if (!allow)
2495 m_namedArgs.remove("_args");
2496 }
2497 else if (!allow)
2498 {
2499 return;
2500 }
2501
2502 auto *arg = new CommandLineArg("_args", QMetaType::QStringList, QStringList());
2503 m_namedArgs["_args"] = arg;
2504}
2505
2510{
2511 if (m_namedArgs.contains("_extra"))
2512 {
2513 if (!allow)
2514 m_namedArgs.remove("_extra");
2515 }
2516 else if (!allow)
2517 {
2518 return;
2519 }
2520
2521 QMap<QString,QVariant> vmap;
2522 auto *arg = new CommandLineArg("_extra", QMetaType::QVariantMap, vmap);
2523
2524 m_namedArgs["_extra"] = arg;
2525}
2526
2531{
2532 if (m_namedArgs.contains("_passthrough"))
2533 {
2534 if (!allow)
2535 m_namedArgs.remove("_passthrough");
2536 }
2537 else if (!allow)
2538 {
2539 return;
2540 }
2541
2542 auto *arg = new CommandLineArg("_passthrough",
2543 QMetaType::QStringList, QStringList());
2544 m_namedArgs["_passthrough"] = arg;
2545}
2546
2550{
2551 add(QStringList{"-h", "--help", "--usage"},
2552 "showhelp", "", "Display this help printout, or give detailed "
2553 "information of selected option.",
2554 "Displays a list of all commands available for use with "
2555 "this application. If another option is provided as an "
2556 "argument, it will provide detailed information on that "
2557 "option.");
2558}
2559
2563{
2564 add("--version", "showversion", false, "Display version information.",
2565 "Display informtion about build, including:\n"
2566 " version, branch, protocol, library API, Qt "
2567 "and compiled options.");
2568}
2569
2573{
2574 add(QStringList{"-nw", "--no-windowed"},
2575 "notwindowed", false,
2576 "Prevent application from running in a window.", "")
2577 ->SetBlocks("windowed")
2578 ->SetGroup("User Interface");
2579
2580 add(QStringList{"-w", "--windowed"}, "windowed",
2581 false, "Force application to run in a window.", "")
2582 ->SetGroup("User Interface");
2583}
2584
2588{
2589 add("--mouse-cursor", "mousecursor", false,
2590 "Force visibility of the mouse cursor.", "")
2591 ->SetBlocks("nomousecursor")
2592 ->SetGroup("User Interface");
2593
2594 add("--no-mouse-cursor", "nomousecursor", false,
2595 "Force the mouse cursor to be hidden.", "")
2596 ->SetGroup("User Interface");
2597}
2598
2602{
2603 add(QStringList{"-d", "--daemon"}, "daemon", false,
2604 "Fork application into background after startup.",
2605 "Fork application into background, detatching from "
2606 "the local terminal.\nOften used with: "
2607 " --logpath --pidfile --user");
2608}
2609
2614{
2615 add(QStringList{"-O", "--override-setting"},
2616 "overridesettings", QMetaType::QVariantMap,
2617 "Override a single setting defined by a key=value pair.",
2618 "Override a single setting from the database using "
2619 "options defined as one or more key=value pairs\n"
2620 "Multiple can be defined by multiple uses of the "
2621 "-O option.");
2622 add("--override-settings-file", "overridesettingsfile", "",
2623 "Define a file of key=value pairs to be "
2624 "loaded for setting overrides.", "");
2625}
2626
2630{
2631 add("--chanid", "chanid", 0U,
2632 "Specify chanid of recording to operate on.", "")
2633 ->SetRequires("starttime");
2634
2635 add("--starttime", "starttime", QDateTime(),
2636 "Specify start time of recording to operate on.", "")
2637 ->SetRequires("chanid");
2638}
2639
2643{
2644 add(QStringList{"-geometry", "--geometry"}, "geometry",
2645 "", "Specify window size and position (WxH[+X+Y])", "")
2646 ->SetGroup("User Interface");
2647}
2648
2652{
2653 add("--noupnp", "noupnp", false, "Disable use of UPnP.", "");
2654}
2655
2659{
2660 add("--dvbv3", "dvbv3", false, "Use legacy DVBv3 API.", "");
2661}
2662
2667 const QString &defaultVerbosity, LogLevel_t defaultLogLevel)
2668{
2669 defaultLogLevel =
2670 ((defaultLogLevel >= LOG_UNKNOWN) || (defaultLogLevel <= LOG_ANY)) ?
2671 LOG_INFO : defaultLogLevel;
2672
2673 QString logLevelStr = logLevelGetName(defaultLogLevel);
2674
2675 add(QStringList{"-v", "--verbose"}, "verbose",
2676 defaultVerbosity,
2677 "Specify log filtering. Use '-v help' for level info.", "")
2678 ->SetGroup("Logging");
2679 add("-V", "verboseint", 0LL, "",
2680 "This option is intended for internal use only.\n"
2681 "This option takes an unsigned value corresponding "
2682 "to the bitwise log verbosity operator.")
2683 ->SetGroup("Logging");
2684 add("--logpath", "logpath", "",
2685 "Writes logging messages to a file in the directory logpath with "
2686 "filenames in the format: applicationName.date.pid.log.\n"
2687 "This is typically used in combination with --daemon, and if used "
2688 "in combination with --pidfile, this can be used with log "
2689 "rotators, using the HUP call to inform MythTV to reload the "
2690 "file", "")
2691 ->SetGroup("Logging");
2692 add(QStringList{"-q", "--quiet"}, "quiet", 0,
2693 "Don't log to the console (-q). Don't log anywhere (-q -q)", "")
2694 ->SetGroup("Logging");
2695 add("--loglong", "loglong", 0,
2696 "Use long log format for the console, i.e. show file, line number, etc. in the console log.", "")
2697 ->SetGroup("Logging");
2698 add("--loglevel", "loglevel", logLevelStr,
2699 QString(
2700 "Set the logging level. All log messages at lower levels will be "
2701 "discarded.\n"
2702 "In descending order: emerg, alert, crit, err, warning, notice, "
2703 "info, debug, trace\ndefaults to ") + logLevelStr, "")
2704 ->SetGroup("Logging");
2705 add("--syslog", "syslog", "none",
2706 "Set the syslog logging facility.\nSet to \"none\" to disable, "
2707 "defaults to none.", "")
2708 ->SetGroup("Logging");
2709#if CONFIG_SYSTEMD_JOURNAL
2710 add("--systemd-journal", "systemd-journal", "false",
2711 "Use systemd-journal instead of syslog.", "")
2712 ->SetBlocks(QStringList()
2713 << "syslog"
2714 )
2715 ->SetGroup("Logging");
2716#endif
2717 add("--nodblog", "nodblog", false, "", "")
2718 ->SetGroup("Logging")
2719 ->SetRemoved("Database logging has been removed.", "34");
2720 add("--enable-dblog", "enabledblog", false, "", "")
2721 ->SetGroup("Logging")
2722 ->SetRemoved("Database logging has been removed.", "34");
2723
2724 add(QStringList{"-l", "--logfile"},
2725 "logfile", "", "", "")
2726 ->SetGroup("Logging")
2727 ->SetRemoved("This option has been removed as part of "
2728 "rewrite of the logging interface. Please update your init "
2729 "scripts to use --syslog to interface with your system's "
2730 "existing system logging daemon, or --logpath to specify a "
2731 "dirctory for MythTV to write its logs to.", "0.25");
2732}
2733
2737{
2738 add(QStringList{"-p", "--pidfile"}, "pidfile", "",
2739 "Write PID of application to filename.",
2740 "Write the PID of the currently running process as a single "
2741 "line to this file. Used for init scripts to know what "
2742 "process to terminate, and with log rotators "
2743 "to send a HUP signal to process to have it re-open files.");
2744}
2745
2749{
2750 add(QStringList{"-j", "--jobid"}, "jobid", 0, "",
2751 "Intended for internal use only, specify the JobID to match "
2752 "up with in the database for additional information and the "
2753 "ability to update runtime status in the database.");
2754}
2755
2759{
2760 add("--infile", "infile", "", "Input file URI", "");
2761 if (addOutFile)
2762 add("--outfile", "outfile", "", "Output file URI", "");
2763}
2764
2768{
2769#if CONFIG_X11
2770 add(QStringList{"-display", "--display"}, "display", "",
2771 "Qt (QPA) X11 connection name when using xcb (X11) platform plugin", "")
2772 ->SetGroup("Qt");
2773#endif
2774}
2775
2779{
2780 add(QStringList{"-platform", "--platform"}, "platform", "", "Qt (QPA) platform argument",
2781 "Qt platform argument that is passed through to Qt")
2782 ->SetGroup("Qt");
2783}
2784
2788{
2789 QString logfile = toString("logpath");
2790 pid_t pid = getpid();
2791
2792 if (logfile.isEmpty())
2793 return logfile;
2794
2795 QFileInfo finfo(logfile);
2796 if (!finfo.isDir())
2797 {
2798 LOG(VB_GENERAL, LOG_ERR,
2799 QString("%1 is not a directory, disabling logfiles")
2800 .arg(logfile));
2801 return {};
2802 }
2803
2804 QString logdir = finfo.filePath();
2805 logfile = QCoreApplication::applicationName() + "." +
2807 QString(".%1").arg(pid) + ".log";
2808
2809 SetValue("logdir", logdir);
2810 SetValue("logfile", logfile);
2811 SetValue("filepath", QFileInfo(QDir(logdir), logfile).filePath());
2812
2813 return toString("filepath");
2814}
2815
2819{
2820 QString setting = toString("syslog").toLower();
2821 if (setting == "none")
2822 return -2;
2823
2824 return syslogGetFacility(setting);
2825}
2826
2830{
2831 QString setting = toString("loglevel");
2832 if (setting.isEmpty())
2833 return LOG_INFO;
2834
2835 LogLevel_t level = logLevelGet(setting);
2836 if (level == LOG_UNKNOWN)
2837 std::cerr << "Unknown log level: " << setting.toLocal8Bit().constData()
2838 << std::endl;
2839
2840 return level;
2841}
2842
2847bool MythCommandLineParser::SetValue(const QString &key, const QVariant& value)
2848{
2849 CommandLineArg *arg = nullptr;
2850
2851 if (!m_namedArgs.contains(key))
2852 {
2853 const QVariant& val(value);
2854#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
2855 auto type = static_cast<QMetaType::Type>(val.type());
2856#else
2857 auto type = static_cast<QMetaType::Type>(val.typeId());
2858#endif
2859 arg = new CommandLineArg(key, type, val);
2860 m_namedArgs.insert(key, arg);
2861 }
2862 else
2863 {
2864 arg = m_namedArgs[key];
2865#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
2866 auto type = static_cast<QMetaType::Type>(value.type());
2867#else
2868 auto type = value.typeId();
2869#endif
2870 if (arg->m_type != type)
2871 return false;
2872 }
2873
2874 arg->Set(value);
2875 return true;
2876}
2877
2881{
2882 // Setup the defaults
2883 verboseString = "";
2884 verboseMask = 0;
2885 verboseArgParse(mask);
2886
2887 if (toBool("verbose"))
2888 {
2889 int err = verboseArgParse(toString("verbose"));
2890 if (err != 0)
2891 return err;
2892 }
2893 else if (toBool("verboseint"))
2894 {
2895 verboseMask = static_cast<uint64_t>(toLongLong("verboseint"));
2896 }
2897
2898 verboseMask |= VB_STDIO|VB_FLUSH;
2899
2900 int quiet = toInt("quiet");
2901 if (std::max(quiet, static_cast<int>(progress)) > 1)
2902 {
2903 verboseMask = VB_NONE|VB_FLUSH;
2904 verboseArgParse("none");
2905 }
2906
2907 bool loglong = toBool("loglong");
2908
2909 int facility = GetSyslogFacility();
2910#if CONFIG_SYSTEMD_JOURNAL
2911 bool journal = toBool("systemd-journal");
2912 if (journal)
2913 {
2914 if (facility >= 0)
2916 facility = SYSTEMD_JOURNAL_FACILITY;
2917 }
2918#endif
2919 LogLevel_t level = GetLogLevel();
2920 if (level == LOG_UNKNOWN)
2922
2923 LOG(VB_GENERAL, LOG_CRIT,
2924 QString("%1 version: %2 [%3] www.mythtv.org")
2925 .arg(QCoreApplication::applicationName(),
2927 LOG(VB_GENERAL, LOG_CRIT, QString("Qt version: compile: %1, runtime: %2")
2928 .arg(QT_VERSION_STR, qVersion()));
2929 LOG(VB_GENERAL, LOG_INFO, QString("%1 (%2)")
2930 .arg(QSysInfo::prettyProductName(), QSysInfo::currentCpuArchitecture()));
2931 LOG(VB_GENERAL, LOG_NOTICE,
2932 QString("Enabled verbose msgs: %1").arg(verboseString));
2933
2934 QString logfile = GetLogFilePath();
2935 bool propagate = !logfile.isEmpty();
2936
2937 if (toBool("daemon"))
2938 quiet = std::max(quiet, 1);
2939
2940 logStart(logfile, progress, quiet, facility, level, propagate, loglong);
2941 qInstallMessageHandler([](QtMsgType /*unused*/, const QMessageLogContext& /*unused*/, const QString &Msg)
2942 { LOG(VB_GENERAL, LOG_INFO, "Qt: " + Msg); });
2943
2944 return GENERIC_EXIT_OK;
2945}
2946
2952{
2953 if (m_verbose)
2954 std::cerr << "Applying settings override" << std::endl;
2955
2956 QMap<QString, QString> override = GetSettingsOverride();
2957 if (!override.empty())
2958 {
2959 QMap<QString, QString>::iterator it;
2960 for (it = override.begin(); it != override.end(); ++it)
2961 {
2962 LOG(VB_GENERAL, LOG_NOTICE,
2963 QString("Setting '%1' being forced to '%2'")
2964 .arg(it.key(), *it));
2966 }
2967 }
2968}
2969
2970static bool openPidfile(std::ofstream &pidfs, const QString &pidfile)
2971{
2972 if (!pidfile.isEmpty())
2973 {
2974 pidfs.open(pidfile.toLatin1().constData());
2975 if (!pidfs)
2976 {
2977 std::cerr << "Could not open pid file: " << ENO_STR << std::endl;
2978 return false;
2979 }
2980 }
2981 return true;
2982}
2983
2986static bool setUser(const QString &username)
2987{
2988 if (username.isEmpty())
2989 return true;
2990
2991#ifdef Q_OS_WINDOWS
2992 std::cerr << "--user option is not supported on Windows" << std::endl;
2993 return false;
2994#else // ! Q_OS_WINDOWS
2995#ifdef Q_OS_LINUX
2996 // Check the current dumpability of core dumps, which will be disabled
2997 // by setuid, so we can re-enable, if appropriate
2998 int dumpability = prctl(PR_GET_DUMPABLE);
2999#endif
3000 struct passwd *user_info = getpwnam(username.toLocal8Bit().constData());
3001 const uid_t user_id = geteuid();
3002
3003 if (user_id && (!user_info || user_id != user_info->pw_uid))
3004 {
3005 std::cerr << "You must be running as root to use the --user switch." << std::endl;
3006 return false;
3007 }
3008 if (user_info && user_id == user_info->pw_uid)
3009 {
3010 LOG(VB_GENERAL, LOG_WARNING,
3011 QString("Already running as '%1'").arg(username));
3012 }
3013 else if (!user_id && user_info)
3014 {
3015 if (setenv("HOME", user_info->pw_dir,1) == -1)
3016 {
3017 std::cerr << "Error setting home directory." << std::endl;
3018 return false;
3019 }
3020 if (setgid(user_info->pw_gid) == -1)
3021 {
3022 std::cerr << "Error setting effective group." << std::endl;
3023 return false;
3024 }
3025 if (initgroups(user_info->pw_name, user_info->pw_gid) == -1)
3026 {
3027 std::cerr << "Error setting groups." << std::endl;
3028 return false;
3029 }
3030 if (setuid(user_info->pw_uid) == -1)
3031 {
3032 std::cerr << "Error setting effective user." << std::endl;
3033 return false;
3034 }
3035#ifdef Q_OS_LINUX
3036 if (dumpability && (prctl(PR_SET_DUMPABLE, dumpability) == -1))
3037 {
3038 LOG(VB_GENERAL, LOG_WARNING, "Unable to re-enable core file "
3039 "creation. Run without the --user argument to use "
3040 "shell-specified limits.");
3041 }
3042#endif
3043 }
3044 else
3045 {
3046 std::cerr << QString("Invalid user '%1' specified with --user")
3047 .arg(username).toLocal8Bit().constData() << std::endl;
3048 return false;
3049 }
3050 return true;
3051#endif // ! Q_OS_WINDOWS
3052}
3053
3054
3058{
3059 std::ofstream pidfs;
3060 if (!openPidfile(pidfs, toString("pidfile")))
3062
3063 if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
3064 LOG(VB_GENERAL, LOG_WARNING, "Unable to ignore SIGPIPE");
3065
3066#ifdef Q_OS_DARWIN
3067 if (toBool("daemon"))
3068 {
3069 std::cerr << "Daemonizing is unavailable in OSX" << std::endl;
3070 LOG(VB_GENERAL, LOG_WARNING, "Unable to daemonize");
3071 }
3072#else
3073 if (toBool("daemon") && (daemon(0, 1) < 0))
3074 {
3075 std::cerr << "Failed to daemonize: " << ENO_STR << std::endl;
3077 }
3078#endif
3079
3080 QString username = toString("username");
3081 if (!username.isEmpty() && !setUser(username))
3083
3084 if (pidfs)
3085 {
3086 pidfs << getpid() << std::endl;
3087 pidfs.close();
3088 }
3089
3090 return GENERIC_EXIT_OK;
3091}
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:131
#define daemon(x, y)
Definition: compat.h:137
#define SIGPIPE
Definition: compat.h:91
#define setuid(x)
Definition: compat.h:132
unsigned int uint
Definition: compat.h:68
#define setenv(x, y, z)
Definition: compat.h:70
@ 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
static uint32_t * tmp
Definition: goom_core.cpp:28
int verboseArgParse(const QString &arg)
Parse the –verbose commandline argument and set the verbose level.
Definition: logging.cpp:919
uint64_t verboseMask
Definition: logging.cpp:100
QString verboseString
Definition: logging.cpp:101
QString logLevelGetName(LogLevel_t level)
Map a log level enumerated value back to the name.
Definition: logging.cpp:791
LogLevel_t logLevelGet(const QString &level)
Map a log level name back to the enumerated value.
Definition: logging.cpp:769
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:655
int syslogGetFacility(const QString &facility)
Map a syslog facility name back to the enumerated value.
Definition: logging.cpp:744
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
STL namespace.