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