24#if QT_VERSION >= QT_VERSION_CHECK(6,5,0)
25#include <QtEnvironmentVariables>
26#include <QtSystemDetection>
29#if defined ANDROID && __ANDROID_API__ < 24
47# include <sys/ioctl.h>
51# include <sys/prctl.h>
56#include <QCoreApplication>
61#include <QRegularExpression>
66#include <QVariantList>
74#include "mythconfig.h"
76#include "mythversion.h"
88#if defined(Q_OS_WINDOWS) || defined(Q_OS_ANDROID)
93 if (ioctl(0, TIOCGWINSZ, &ws) != 0)
96 return static_cast<int>(ws.ws_col);
102 return ((array.startsWith(
'"') && array.endsWith(
'"') ) ||
103 (array.startsWith(
'\'') && array.endsWith(
'\''))
104 ) ? array.mid(1, array.size() - 2) : array;
111 width = std::max(width, 5);
113 for (
int i = 0; i < list.size(); i++)
115 QString
string = list.at(i);
117 if(
string.size() <= width )
120 QString left =
string.left(width);
121 bool inserted =
false;
123 while( !inserted && !left.endsWith(
" " ))
125 if(
string.mid(left.size(), 1) ==
" " )
127 list.replace(i, left);
128 list.insert(i+1,
string.mid(left.size()).trimmed());
134 if( !left.contains(
" ") )
137 list.replace(i, left +
"-");
138 list.insert(i+1,
string.mid(left.size()));
147 list.replace(i, left);
148 list.insert(i+1,
string.mid(left.size()).trimmed());
163 enum states : std::uint8_t {
172 states state =
START;
175 for (
int i = 0; i < line.size(); i++)
177 const QChar c = line.at(i);
182 if (c.isSpace())
break;
183 if (c ==
'\'') state = INSQUOTE;
184 else if (c ==
'\"') state = INDQUOTE;
185 else if (c ==
'\\') state = ESCTEXT;
190 fields += line.mid(tokenStart, i - tokenStart);
194 else if (c ==
'\'') {
196 }
else if (c ==
'\"') {
198 }
else if (c ==
'\\') {
203 if (c ==
'\'') state = INTEXT;
204 else if (c ==
'\\') state = ESCSQUOTE;
207 if (c ==
'\"') state = INTEXT;
208 else if (c ==
'\\') state = ESCDQUOTE;
210 case ESCTEXT: state = INTEXT;
break;
211 case ESCSQUOTE: state = INSQUOTE;
break;
212 case ESCDQUOTE: state = INDQUOTE;
break;
217 fields += line.mid(tokenStart);
241 return "kCombOptVal";
247 return "kPassthrough";
290 QVariant def, QString
help, QString longhelp) :
292 m_name(name), m_type(
type), m_default(std::move(def)),
293 m_help(std::move(
help)), m_longhelp(std::move(longhelp))
295 if ((
m_type != QMetaType::QString) && (
m_type != QMetaType::QStringList) &&
296 (
m_type != QMetaType::QVariantMap))
308 m_name(name), m_type(
type), m_default(std::move(def))
310 if ((
m_type != QMetaType::QString) && (
m_type != QMetaType::QStringList) &&
311 (
m_type != QMetaType::QVariantMap))
345 QList<CommandLineArg*>::const_iterator i1;
347 len = std::max(len, (*i1)->GetKeywordLength()+2);
370#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
371 QTextStream msg(&helpstr, QIODevice::WriteOnly);
373 QTextStream msg(&helpstr, QIODeviceBase::WriteOnly);
419 QStringList hlist =
m_help.split(
'\n');
425 << hlist.takeFirst() << Qt::endl;
428 for (
const auto & line : std::as_const(hlist))
429 msg << pad << line << Qt::endl;
433 msg << arg->GetHelpString(off, group,
true);
449#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
450 QTextStream msg(&helpstr, QIODevice::WriteOnly);
452 QTextStream msg(&helpstr, QIODeviceBase::WriteOnly);
471 msg <<
"Option: " << keyword << Qt::endl << Qt::endl;
476 for (
const auto & word : std::as_const(
m_keywords))
482 msg <<
"Aliases: " << word << Qt::endl;
487 msg <<
" " << word << Qt::endl;
493 msg <<
"Type: " << QMetaType(
m_type).name() << Qt::endl;
495 msg <<
"Default: " <<
m_default.toString() << Qt::endl;
505 msg <<
"Description: " <<
help.takeFirst() << Qt::endl;
506 for (
const auto & line : std::as_const(
help))
507 msg <<
" " << line << Qt::endl;
509 QList<CommandLineArg*>::const_iterator i2;
514 msg << Qt::endl <<
"Can be used in combination with:" << Qt::endl;
515 for (
auto * parent : std::as_const(
m_parents))
516 msg <<
" " << parent->GetPreferredKeyword()
517 .toLocal8Bit().constData();
523 msg << Qt::endl <<
"Allows the use of:" << Qt::endl;
525 msg <<
" " << (*i2)->GetPreferredKeyword()
526 .toLocal8Bit().constData();
532 msg << Qt::endl <<
"Requires the use of:" << Qt::endl;
534 msg <<
" " << (*i2)->GetPreferredKeyword()
535 .toLocal8Bit().constData();
541 msg << Qt::endl <<
"Prevents the use of:" << Qt::endl;
543 msg <<
" " << (*i2)->GetPreferredKeyword()
544 .toLocal8Bit().constData();
564 case QMetaType::Bool:
575 case QMetaType::QString:
580 std::cerr <<
"Command line option did not receive value:\n"
581 <<
" " << opt.toLocal8Bit().constData() <<
'\n';
594 QList<QByteArray> blist;
600 case QMetaType::Bool:
601 std::cerr <<
"Boolean type options do not accept values:\n"
602 <<
" " << opt.toLocal8Bit().constData() <<
'\n';
605 case QMetaType::QString:
613 case QMetaType::UInt:
617 case QMetaType::LongLong:
618 m_stored = QVariant(val.toLongLong());
621 case QMetaType::Double:
622 m_stored = QVariant(val.toDouble());
625 case QMetaType::QDateTime:
629 case QMetaType::QStringList:
636 case QMetaType::QVariantMap:
637 if (!val.contains(
'='))
639 std::cerr <<
"Command line option did not get expected "
640 <<
"key/value pair\n";
644 blist = val.split(
'=');
652 case QMetaType::QSize:
653 if (!val.contains(
'x'))
655 std::cerr <<
"Command line option did not get expected "
660 blist = val.split(
'x');
661 m_stored = QVariant(QSize(blist[0].toInt(), blist[1].toInt()));
684 for (
const auto& opt : std::as_const(
opts))
701 for (
const auto& opt : std::as_const(
opts))
718 for (
const auto& opt : std::as_const(
opts))
735 for (
const auto& opt : std::as_const(
opts))
753 for (
const auto& opt : std::as_const(
opts))
774 for (
const auto& opt : std::as_const(
opts))
794 for (
const auto& opt : std::as_const(
opts))
811 for (
const auto& opt : std::as_const(
opts))
820 if (depstr.isEmpty())
821 depstr =
"and will be removed in a future version.";
830 if (remstr.isEmpty())
831 remstr =
"and is no longer available in this version.";
844 bool replaced =
false;
872 bool replaced =
false;
875 for (
int i = 0; i <
m_parents.size(); i++)
900 bool replaced =
false;
929 bool replaced =
false;
932 for (
int i = 0; i <
m_blocks.size(); i++)
960 for (
auto i1 =
args.cbegin(); i1 !=
args.cend()-1; ++i1)
964 for (
auto i2 = i1+1; i2 !=
args.cend(); ++i2)
966 (*i1)->SetBlocks(*i2);
969 if ((*i1)->m_type == QMetaType::UnknownType)
982 if (!QCoreApplication::instance())
997#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
998 auto storedType =
static_cast<QMetaType::Type
>(
m_stored.type());
1000 auto storedType =
m_stored.typeId();
1002 if (
m_type == QMetaType::QString)
1004 if (storedType == QMetaType::QByteArray)
1012 else if (
m_type == QMetaType::QStringList)
1014 if (storedType == QMetaType::QVariantList)
1016 QVariantList vlist =
m_stored.toList();
1018 slist.reserve(vlist.size());
1019 for (
const auto& item : std::as_const(vlist))
1020 slist << QString::fromLocal8Bit(item.toByteArray());
1024 else if (
m_type == QMetaType::QVariantMap)
1026 QVariantMap vmap =
m_stored.toMap();
1028 for (
auto iter = vmap.begin(); iter != vmap.end(); ++iter)
1029 (*iter) = QString::fromLocal8Bit(iter->toByteArray());
1047 QStringList::const_iterator it;
1053 int len2 = (*it).size();
1072 QList<CommandLineArg*>::const_iterator i;
1074 bool passes =
false;
1086 std::cerr <<
"ERROR: " <<
m_usedKeyword.toLocal8Bit().constData()
1087 <<
" requires at least one of the following arguments\n";
1090 << (*i)->GetPreferredKeyword().toLocal8Bit().constData();
1091 std::cerr <<
"\n\n";
1102 std::cerr <<
"ERROR: " <<
m_usedKeyword.toLocal8Bit().constData()
1103 <<
" requires all of the following be defined as well\n";
1107 << (*i)->GetPreferredKeyword().toLocal8Bit()
1110 std::cerr <<
"\n\n";
1120 std::cerr <<
"ERROR: " <<
m_usedKeyword.toLocal8Bit().constData()
1121 <<
" requires that none of the following be defined\n";
1125 << (*i)->GetPreferredKeyword().toLocal8Bit()
1128 std::cerr <<
"\n\n";
1164 std::cerr <<
" " <<
m_name.leftJustified(30).toLocal8Bit().constData();
1167 QMap<QString, QVariant> tmpmap;
1168 QMap<QString, QVariant>::const_iterator it;
1174 case QMetaType::Bool:
1175 std::cerr << (
m_stored.toBool() ?
"True" :
"False") <<
'\n';
1178 case QMetaType::Int:
1179 std::cerr <<
m_stored.toInt() <<
'\n';
1182 case QMetaType::UInt:
1183 std::cerr <<
m_stored.toUInt() <<
'\n';
1186 case QMetaType::LongLong:
1187 std::cerr <<
m_stored.toLongLong() <<
'\n';
1190 case QMetaType::Double:
1191 std::cerr <<
m_stored.toDouble() <<
'\n';
1194 case QMetaType::QSize:
1196 std::cerr <<
"x=" << tmpsize.width()
1197 <<
" y=" << tmpsize.height()
1201 case QMetaType::QString:
1202 std::cerr <<
'"' <<
m_stored.toByteArray().constData()
1206 case QMetaType::QStringList:
1208 std::cerr <<
'"' << vlist.takeFirst().toByteArray().constData() <<
'"';
1209 for (
const auto& str : std::as_const(vlist))
1218 case QMetaType::QVariantMap:
1220 for (it = tmpmap.cbegin(); it != tmpmap.cend(); ++it)
1225 std::cerr << QString(
"").leftJustified(32)
1226 .toLocal8Bit().constData();
1228 std::cerr << it.key().toLocal8Bit().constData()
1230 << it->toByteArray().constData()
1236 case QMetaType::QDateTime:
1238 .toLocal8Bit().constData()
1251 QString warn = QString(
"%1 has been removed").arg(keyword);
1255 std::cerr << QString(
"****************************************************\n"
1258 "****************************************************\n\n")
1260 .toLocal8Bit().constData();
1267 std::cerr << QString(
"****************************************************\n"
1268 " WARNING: %1 has been deprecated\n"
1270 "****************************************************\n\n")
1272 .toLocal8Bit().constData();
1289 : m_appname(std::move(appname))
1291 if (qEnvironmentVariableIsSet(
"VERBOSE_PARSER"))
1293 std::cerr <<
"MythCommandLineParser is now operating verbosely.\n";
1302 QString pidfile =
toString(
"pidfile");
1303 if (!pidfile.isEmpty())
1305 QFile::remove(pidfile);
1308 QMap<QString, CommandLineArg*>::iterator i;
1313 (*i)->CleanupLinks();
1360 const QString& name, QMetaType::Type
type, QVariant def,
1361 QString
help, QString longhelp)
1375 for (
const auto & str : std::as_const(arglist))
1382 std::cerr <<
"Adding " << str.toLocal8Bit().constData()
1383 <<
" as taking type '"
1384#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1385 << QVariant::typeToName(
static_cast<int>(
type))
1387 << QMetaType(
type).name()
1403 std::cout <<
"Please attach all output as a file in bug reports.\n";
1406 std::cout <<
"Network Protocol : " << MYTH_PROTO_VERSION <<
'\n';
1407 std::cout <<
"Library API : " << MYTH_BINARY_VERSION <<
'\n';
1408 std::cout <<
"QT Version : " << QT_VERSION_STR <<
'\n';
1409#ifdef MYTH_BUILD_CONFIG
1410 std::cout <<
"Options compiled in:\n";
1411 std::cout << MYTH_BUILD_CONFIG <<
'\n';
1420 std::cerr <<
help.toLocal8Bit().constData();
1431#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1432 QTextStream msg(&helpstr, QIODevice::WriteOnly);
1434 QTextStream msg(&helpstr, QIODeviceBase::WriteOnly);
1437 QString versionStr = QString(
"%1 version: %2 [%3] www.mythtv.org")
1439 msg << versionStr << Qt::endl;
1441 if (
toString(
"showhelp").isEmpty())
1446 if (descr.size() > 0)
1447 msg << Qt::endl << descr << Qt::endl << Qt::endl;
1450 QStringList groups(
"");
1454 maxlen = std::max(cmdarg->GetKeywordLength(), maxlen);
1455 if (!groups.contains(cmdarg->m_group))
1456 groups << cmdarg->m_group;
1462 for (
const auto & group : std::as_const(groups))
1464 if (group.isEmpty())
1465 msg <<
"Misc. Options:" << Qt::endl << Qt::endl;
1467 msg << group.toLocal8Bit().constData() <<
" Options:" << Qt::endl << Qt::endl;
1470 msg << cmdarg->GetHelpString(maxlen, group);
1477 QString optstr =
"-" +
toString(
"showhelp");
1480 optstr =
"-" + optstr;
1482 return QString(
"Could not find option matching '%1'\n")
1497 int &argpos, QString &opt, QByteArray &val)
1506 QByteArray tmp(argv[argpos]);
1518 if (tmp.startsWith(
'-') && tmp.size() > 1)
1527 if (tmp.contains(
'='))
1530 QList<QByteArray> blist = tmp.split(
'=');
1532 if (blist.size() != 2)
1546 if (argpos+1 >= argc)
1550 tmp = QByteArray(argv[++argpos]);
1555 if (tmp.startsWith(
"-") && tmp.size() > 1)
1589 for (
int argpos = 1; argpos < argc; ++argpos)
1593 res =
getOpt(argc, argv, argpos, opt, val);
1598 <<
"opt: " << opt.toLocal8Bit().constData() <<
'\n'
1599 <<
"val: " << val.constData() <<
"\n\n";
1605 std::cerr <<
"Received '--' but passthrough has not been enabled\n";
1622 std::cerr <<
"Invalid option received:\n "
1623 << opt.toLocal8Bit().constData();
1640 std::cerr <<
"Received '"
1642 <<
"' but unassociated arguments have not been enabled\n";
1654 std::cerr <<
"Command line arguments received out of sequence\n";
1660 if (opt.startsWith(
"-psn_"))
1662 std::cerr <<
"Ignoring Process Serial Number from command line\n";
1674 QByteArray tmp = opt.toLocal8Bit();
1683 std::cerr <<
"Unhandled option given on command line:\n"
1684 <<
" " << opt.toLocal8Bit().constData() <<
'\n';
1707 std::cerr <<
"name: " << argdef->
GetName().toLocal8Bit().constData()
1713 if (!argdef->
Set(opt))
1722 if (!argdef->
Set(opt, val))
1743 std::cerr <<
"value: " << argdef->
m_stored.toString().toLocal8Bit().constData()
1749 std::cerr <<
"Processed option list:\n";
1751 cmdarg->PrintVerbose();
1755 std::cerr <<
"\nExtra argument list:\n";
1757 for (
const auto& lopt : std::as_const(slist))
1758 std::cerr <<
" " << lopt.toLocal8Bit().constData() <<
'\n';
1763 std::cerr <<
"\nPassthrough string:\n";
1764 std::cerr <<
" " <<
GetPassthrough().toLocal8Bit().constData() <<
'\n';
1773 if (!cmdarg->TestLinks())
1775 QString keyword = cmdarg->m_usedKeyword;
1776 if (keyword.startsWith(
'-'))
1778 if (keyword.startsWith(
"--"))
1779 keyword.remove(0,2);
1781 keyword.remove(0,1);
1793 QString
help, QString longhelp)
1795 return add(QStringList(arg), name, QMetaType::Bool, QVariant(def), std::move(
help), std::move(longhelp));
1799 QString
help, QString longhelp)
1801 return add(QStringList(arg), name, QMetaType::Int, QVariant(def), std::move(
help), std::move(longhelp));
1805 QString
help, QString longhelp)
1807 return add(QStringList(arg), name, QMetaType::UInt, QVariant(def), std::move(
help), std::move(longhelp));
1811 QString
help, QString longhelp)
1813 return add(QStringList(arg), name, QMetaType::LongLong, QVariant(def), std::move(
help), std::move(longhelp));
1817 QString
help, QString longhelp)
1819 return add(QStringList(arg), name, QMetaType::Double, QVariant(def), std::move(
help), std::move(longhelp));
1823 QString
help, QString longhelp)
1825 return add(QStringList(arg), name, QMetaType::QString, QVariant(def), std::move(
help), std::move(longhelp));
1829 QString
help, QString longhelp)
1831 return add(QStringList(arg), name, QMetaType::QString, QVariant(def), std::move(
help), std::move(longhelp));
1835 QString
help, QString longhelp)
1837 return add(QStringList(arg), name, QMetaType::QSize, QVariant(def), std::move(
help), std::move(longhelp));
1841 QString
help, QString longhelp)
1843 return add(QStringList(arg), name, QMetaType::QDateTime, QVariant(def), std::move(
help), std::move(longhelp));
1847 QString
help, QString longhelp)
1849 return add(QStringList(arg), name,
type,
1850#
if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1851 QVariant(
static_cast<QVariant::Type
>(
type)),
1853 QVariant(QMetaType(
type)),
1855 std::move(
help), std::move(longhelp));
1859 QMetaType::Type
type,
1860 QVariant def, QString
help, QString longhelp)
1862 return add(QStringList(arg), name,
type, std::move(def), std::move(
help), std::move(longhelp));
1866 QString
help, QString longhelp)
1868 return add(std::move(arglist), name, QMetaType::Bool, QVariant(def), std::move(
help), std::move(longhelp));
1872 QString
help, QString longhelp)
1874 return add(std::move(arglist), name, QMetaType::Int, QVariant(def), std::move(
help), std::move(longhelp));
1878 QString
help, QString longhelp)
1880 return add(std::move(arglist), name, QMetaType::UInt, QVariant(def), std::move(
help), std::move(longhelp));
1884 QString
help, QString longhelp)
1886 return add(std::move(arglist), name, QMetaType::LongLong, QVariant(def), std::move(
help), std::move(longhelp));
1890 QString
help, QString longhelp)
1892 return add(std::move(arglist), name, QMetaType::Double, QVariant(def), std::move(
help), std::move(longhelp));
1896 QString
help, QString longhelp)
1898 return add(std::move(arglist), name, QMetaType::QString, QVariant(def), std::move(
help), std::move(longhelp));
1902 QString
help, QString longhelp)
1904 return add(std::move(arglist), name, QMetaType::QString, QVariant(def), std::move(
help), std::move(longhelp));
1908 QString
help, QString longhelp)
1910 return add(std::move(arglist), name, QMetaType::QSize, QVariant(def), std::move(
help), std::move(longhelp));
1914 QString
help, QString longhelp)
1916 return add(std::move(arglist), name, QMetaType::QDateTime, QVariant(def), std::move(
help), std::move(longhelp));
1920 QMetaType::Type
type,
1921 QString
help, QString longhelp)
1923 return add(std::move(arglist), name,
type,
1924#
if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1925 QVariant(
static_cast<QVariant::Type
>(
type)),
1927 QVariant(QMetaType(
type)),
1929 std::move(
help), std::move(longhelp));
1938 std::cerr <<
"Reconciling links for option interdependencies.\n";
1940 QMap<QString,CommandLineArg*>::iterator args_it;
1943 QList<CommandLineArg*> links = (*args_it)->m_parents;
1944 QList<CommandLineArg*>::iterator links_it;
1945 for (links_it = links.begin(); links_it != links.end(); ++links_it)
1947 if ((*links_it)->m_type != QMetaType::UnknownType)
1953 std::cerr <<
"ERROR: could not reconcile linked argument.\n"
1954 <<
" '" << (*args_it)->m_name.toLocal8Bit().constData()
1955 <<
"' could not find '"
1956 << (*links_it)->m_name.toLocal8Bit().constData()
1958 <<
" Please resolve dependency and recompile.\n";
1965 std::cerr << QString(
" Setting %1 as child of %2")
1966 .arg((*args_it)->m_name, (*links_it)->m_name)
1967 .toLocal8Bit().constData()
1970 (*args_it)->SetChildOf(
m_namedArgs[(*links_it)->m_name]);
1973 links = (*args_it)->m_children;
1974 for (links_it = links.begin(); links_it != links.end(); ++links_it)
1976 if ((*links_it)->m_type != QMetaType::UnknownType)
1982 std::cerr <<
"ERROR: could not reconcile linked argument.\n"
1983 <<
" '" << (*args_it)->m_name.toLocal8Bit().constData()
1984 <<
"' could not find '"
1985 << (*links_it)->m_name.toLocal8Bit().constData()
1987 <<
" Please resolve dependency and recompile.\n";
1994 std::cerr << QString(
" Setting %1 as parent of %2")
1995 .arg((*args_it)->m_name, (*links_it)->m_name)
1996 .toLocal8Bit().constData()
1999 (*args_it)->SetParentOf(
m_namedArgs[(*links_it)->m_name]);
2002 links = (*args_it)->m_requires;
2003 for (links_it = links.begin(); links_it != links.end(); ++links_it)
2005 if ((*links_it)->m_type != QMetaType::UnknownType)
2011 std::cerr <<
"ERROR: could not reconcile linked argument.\n"
2012 <<
" '" << (*args_it)->m_name.toLocal8Bit().constData()
2013 <<
"' could not find '"
2014 << (*links_it)->m_name.toLocal8Bit().constData()
2016 <<
" Please resolve dependency and recompile.\n";
2023 std::cerr << QString(
" Setting %1 as requiring %2")
2024 .arg((*args_it)->m_name, (*links_it)->m_name)
2025 .toLocal8Bit().constData()
2028 (*args_it)->SetRequires(
m_namedArgs[(*links_it)->m_name]);
2031 QList<CommandLineArg*>::iterator req_it =
2032 (*args_it)->m_requiredby.begin();
2033 while (req_it != (*args_it)->m_requiredby.end())
2035 if ((*req_it)->m_type == QMetaType::UnknownType)
2040 m_namedArgs[(*req_it)->m_name]->SetRequires(*args_it);
2043 std::cerr << QString(
" Setting %1 as blocking %2")
2044 .arg((*args_it)->m_name,
2046 .toLocal8Bit().constData()
2052 (*req_it)->DecrRef();
2053 req_it = (*args_it)->m_requiredby.erase(req_it);
2056 QList<CommandLineArg*>::iterator block_it =
2057 (*args_it)->m_blocks.begin();
2058 while (block_it != (*args_it)->m_blocks.end())
2060 if ((*block_it)->m_type != QMetaType::UnknownType)
2068 (*block_it)->DecrRef();
2069 block_it = (*args_it)->m_blocks.erase(block_it);
2076 std::cerr << QString(
" Setting %1 as blocking %2")
2077 .arg((*args_it)->m_name, (*block_it)->m_name)
2078 .toLocal8Bit().constData()
2081 (*args_it)->SetBlocks(
m_namedArgs[(*block_it)->m_name]);
2121 return toMap(
"_extra");
2140 QMap<QString,QString> smap =
toMap(
"overridesettings");
2144 if (
toBool(
"overridesettingsfile"))
2150 if (f.open(QIODevice::ReadOnly))
2153 while (!in.atEnd()) {
2154 QString line = in.readLine().trimmed();
2155 QStringList tokens = line.split(
"=",
2156 Qt::SkipEmptyParts);
2157 if (tokens.size() == 2)
2159 static const QRegularExpression kQuoteStartRE {
"^[\"']" };
2160 static const QRegularExpression kQuoteEndRE {
"[\"']$" };
2161 tokens[0].remove(kQuoteStartRE);
2162 tokens[0].remove(kQuoteEndRE);
2163 tokens[1].remove(kQuoteStartRE);
2164 tokens[1].remove(kQuoteEndRE);
2165 if (!tokens[0].isEmpty())
2166 smap[tokens[0]] = tokens[1];
2172 QByteArray tmp =
filename.toLatin1();
2173 std::cerr <<
"Failed to open the override settings file: '"
2174 << tmp.constData() <<
"'\n";
2180 smap[
"RunFrontendInWindow"] =
"1";
2181 else if (
toBool(
"notwindowed"))
2182 smap[
"RunFrontendInWindow"] =
"0";
2184 if (
toBool(
"mousecursor"))
2185 smap[
"HideMouseCursor"] =
"0";
2186 else if (
toBool(
"nomousecursor"))
2187 smap[
"HideMouseCursor"] =
"1";
2191 if (!smap.isEmpty())
2194 for (
auto it = smap.cbegin(); it != smap.cend(); ++it)
2195 vmap[it.key()] = QVariant(it.value());
2197 m_namedArgs[
"overridesettings"]->Set(QVariant(vmap));
2203 std::cerr <<
"Option Overrides:\n";
2204 QMap<QString, QString>::const_iterator it;
2205 for (it = smap.constBegin(); it != smap.constEnd(); ++it)
2206 std::cerr << QString(
" %1 - %2").arg(it.key(), 30).arg(*it)
2207 .toLocal8Bit().constData() <<
'\n';
2228 if (arg->
m_type == QMetaType::Bool)
2253 if (arg->
m_stored.canConvert<
int>())
2307 if (arg->
m_stored.canConvert<
long long>())
2312 if (arg->
m_default.canConvert<
long long>())
2334 if (arg->
m_stored.canConvert<
double>())
2339 if (arg->
m_default.canConvert<
double>())
2361 if (arg->
m_stored.canConvert<QSize>())
2391 if (arg->
m_stored.canConvert<QString>())
2396 if (arg->
m_default.canConvert<QString>())
2430 if (arg->
m_type == QMetaType::QString && !sep.isEmpty())
2431 val = varval.toString().split(sep);
2432 else if (varval.canConvert<QStringList>())
2433 val = varval.toStringList();
2443 QMap<QString, QString> val;
2444 QMap<QString, QVariant> tmp;
2457 if (arg->
m_stored.canConvert<QMap<QString, QVariant>>())
2462 if (arg->
m_default.canConvert<QMap<QString, QVariant>>())
2466 for (
auto i = tmp.cbegin(); i != tmp.cend(); ++i)
2467 val[i.key()] = i.value().toString();
2487 if (arg->
m_stored.canConvert<QDateTime>())
2492 if (arg->
m_default.canConvert<QDateTime>())
2514 auto *arg =
new CommandLineArg(
"_args", QMetaType::QStringList, QStringList());
2533 QMap<QString,QVariant> vmap;
2534 auto *arg =
new CommandLineArg(
"_extra", QMetaType::QVariantMap, vmap);
2555 QMetaType::QStringList, QStringList());
2563 add(QStringList{
"-h",
"--help",
"--usage"},
2564 "showhelp",
"",
"Display this help printout, or give detailed "
2565 "information of selected option.",
2566 "Displays a list of all commands available for use with "
2567 "this application. If another option is provided as an "
2568 "argument, it will provide detailed information on that "
2576 add(
"--version",
"showversion",
false,
"Display version information.",
2577 "Display informtion about build, including:\n"
2578 " version, branch, protocol, library API, Qt "
2579 "and compiled options.");
2586 add(QStringList{
"-nw",
"--no-windowed"},
2587 "notwindowed",
false,
2588 "Prevent application from running in a window.",
"")
2589 ->SetBlocks(
"windowed")
2592 add(QStringList{
"-w",
"--windowed"},
"windowed",
2593 false,
"Force application to run in a window.",
"")
2594 ->SetGroup(
"User Interface");
2601 add(
"--mouse-cursor",
"mousecursor",
false,
2602 "Force visibility of the mouse cursor.",
"")
2606 add(
"--no-mouse-cursor",
"nomousecursor",
false,
2607 "Force the mouse cursor to be hidden.",
"")
2615 add(QStringList{
"-d",
"--daemon"},
"daemon",
false,
2616 "Fork application into background after startup.",
2617 "Fork application into background, detatching from "
2618 "the local terminal.\nOften used with: "
2619 " --logpath --pidfile --user");
2627 add(QStringList{
"-O",
"--override-setting"},
2628 "overridesettings", QMetaType::QVariantMap,
2629 "Override a single setting defined by a key=value pair.",
2630 "Override a single setting from the database using "
2631 "options defined as one or more key=value pairs\n"
2632 "Multiple can be defined by multiple uses of the "
2634 add(
"--override-settings-file",
"overridesettingsfile",
"",
2635 "Define a file of key=value pairs to be "
2636 "loaded for setting overrides.",
"");
2643 add(
"--chanid",
"chanid", 0U,
2644 "Specify chanid of recording to operate on.",
"")
2647 add(
"--starttime",
"starttime", QDateTime(),
2648 "Specify start time of recording to operate on.",
"")
2656 add(QStringList{
"-geometry",
"--geometry"},
"geometry",
2657 "",
"Specify window size and position (WxH[+X+Y])",
"")
2658 ->SetGroup(
"User Interface");
2665 add(
"--noupnp",
"noupnp",
false,
"Disable use of UPnP.",
"");
2672 add(
"--dvbv3",
"dvbv3",
false,
"Use legacy DVBv3 API.",
"");
2679 const QString &defaultVerbosity, LogLevel_t defaultLogLevel)
2682 ((defaultLogLevel >= LOG_UNKNOWN) || (defaultLogLevel <= LOG_ANY)) ?
2683 LOG_INFO : defaultLogLevel;
2687 add(QStringList{
"-v",
"--verbose"},
"verbose",
2689 "Specify log filtering. Use '-v help' for level info.",
"")
2690 ->SetGroup(
"Logging");
2691 add(
"-V",
"verboseint", 0LL,
"",
2692 "This option is intended for internal use only.\n"
2693 "This option takes an unsigned value corresponding "
2694 "to the bitwise log verbosity operator.")
2696 add(
"--logpath",
"logpath",
"",
2697 "Writes logging messages to a file in the directory logpath with "
2698 "filenames in the format: applicationName.date.pid.log.\n"
2699 "This is typically used in combination with --daemon, and if used "
2700 "in combination with --pidfile, this can be used with log "
2701 "rotators, using the HUP call to inform MythTV to reload the "
2704 add(QStringList{
"-q",
"--quiet"},
"quiet", 0,
2705 "Don't log to the console (-q). Don't log anywhere (-q -q)",
"")
2706 ->SetGroup(
"Logging");
2707 add(
"--loglong",
"loglong", 0,
2708 "Use long log format for the console, i.e. show file, line number, etc. in the console log.",
"")
2710 add(
"--loglevel",
"loglevel", logLevelStr,
2712 "Set the logging level. All log messages at lower levels will be "
2714 "In descending order: emerg, alert, crit, err, warning, notice, "
2715 "info, debug, trace\ndefaults to ") + logLevelStr,
"")
2717 add(
"--syslog",
"syslog",
"none",
2718 "Set the syslog logging facility.\nSet to \"none\" to disable, "
2719 "defaults to none.",
"")
2721#if CONFIG_SYSTEMD_JOURNAL
2722 add(
"--systemd-journal",
"systemd-journal",
"false",
2723 "Use systemd-journal instead of syslog.",
"")
2729 add(
"--nodblog",
"nodblog",
false,
"",
"")
2731 ->
SetRemoved(
"Database logging has been removed.",
"34");
2732 add(
"--enable-dblog",
"enabledblog",
false,
"",
"")
2734 ->
SetRemoved(
"Database logging has been removed.",
"34");
2736 add(QStringList{
"-l",
"--logfile"},
2737 "logfile",
"",
"",
"")
2738 ->SetGroup(
"Logging")
2739 ->
SetRemoved(
"This option has been removed as part of "
2740 "rewrite of the logging interface. Please update your init "
2741 "scripts to use --syslog to interface with your system's "
2742 "existing system logging daemon, or --logpath to specify a "
2743 "dirctory for MythTV to write its logs to.",
"0.25");
2750 add(QStringList{
"-p",
"--pidfile"},
"pidfile",
"",
2751 "Write PID of application to filename.",
2752 "Write the PID of the currently running process as a single "
2753 "line to this file. Used for init scripts to know what "
2754 "process to terminate, and with log rotators "
2755 "to send a HUP signal to process to have it re-open files.");
2762 add(QStringList{
"-j",
"--jobid"},
"jobid", 0,
"",
2763 "Intended for internal use only, specify the JobID to match "
2764 "up with in the database for additional information and the "
2765 "ability to update runtime status in the database.");
2772 add(
"--infile",
"infile",
"",
"Input file URI",
"");
2774 add(
"--outfile",
"outfile",
"",
"Output file URI",
"");
2782 add(QStringList{
"-display",
"--display"},
"display",
"",
2783 "Qt (QPA) X11 connection name when using xcb (X11) platform plugin",
"")
2792 add(QStringList{
"-platform",
"--platform"},
"platform",
"",
"Qt (QPA) platform argument",
2793 "Qt platform argument that is passed through to Qt")
2801 QString logfile =
toString(
"logpath");
2802 pid_t pid = getpid();
2804 if (logfile.isEmpty())
2807 QFileInfo finfo(logfile);
2810 LOG(VB_GENERAL, LOG_ERR,
2811 QString(
"%1 is not a directory, disabling logfiles")
2816 QString logdir = finfo.filePath();
2817 logfile = QCoreApplication::applicationName() +
"." +
2819 QString(
".%1").arg(pid) +
".log";
2823 SetValue(
"filepath", QFileInfo(QDir(logdir), logfile).filePath());
2832 QString setting =
toString(
"syslog").toLower();
2833 if (setting ==
"none")
2843 QString setting =
toString(
"loglevel");
2844 if (setting.isEmpty())
2848 if (level == LOG_UNKNOWN)
2849 std::cerr <<
"Unknown log level: " << setting.toLocal8Bit().constData()
2865 const QVariant& val(value);
2866#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
2867 auto type =
static_cast<QMetaType::Type
>(val.type());
2869 auto type =
static_cast<QMetaType::Type
>(val.typeId());
2877#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
2878 auto type =
static_cast<QMetaType::Type
>(value.type());
2880 auto type = value.typeId();
2905 else if (
toBool(
"verboseint"))
2912 int quiet =
toInt(
"quiet");
2913 if (std::max(quiet,
static_cast<int>(
progress)) > 1)
2919 bool loglong =
toBool(
"loglong");
2922#if CONFIG_SYSTEMD_JOURNAL
2923 bool journal =
toBool(
"systemd-journal");
2928 facility = SYSTEMD_JOURNAL_FACILITY;
2932 if (level == LOG_UNKNOWN)
2935 LOG(VB_GENERAL, LOG_CRIT,
2936 QString(
"%1 version: %2 [%3] www.mythtv.org")
2937 .arg(QCoreApplication::applicationName(),
2939 LOG(VB_GENERAL, LOG_CRIT, QString(
"Qt version: compile: %1, runtime: %2")
2940 .arg(QT_VERSION_STR, qVersion()));
2941 LOG(VB_GENERAL, LOG_INFO, QString(
"%1 (%2)")
2942 .arg(QSysInfo::prettyProductName(), QSysInfo::currentCpuArchitecture()));
2943 LOG(VB_GENERAL, LOG_NOTICE,
2947 bool propagate = !logfile.isEmpty();
2950 quiet = std::max(quiet, 1);
2953 qInstallMessageHandler([](QtMsgType ,
const QMessageLogContext& ,
const QString &Msg)
2954 {
LOG(VB_GENERAL, LOG_INFO,
"Qt: " + Msg); });
2966 std::cerr <<
"Applying settings override\n";
2969 if (!
override.empty())
2971 QMap<QString, QString>::iterator it;
2972 for (it =
override.begin(); it !=
override.end(); ++it)
2974 LOG(VB_GENERAL, LOG_NOTICE,
2975 QString(
"Setting '%1' being forced to '%2'")
2976 .arg(it.key(), *it));
2984 if (!pidfile.isEmpty())
2986 pidfs.open(pidfile.toLatin1().constData());
2989 std::cerr <<
"Could not open pid file: " <<
ENO_STR <<
'\n';
3000 if (username.isEmpty())
3004 std::cerr <<
"--user option is not supported on Windows" << std::endl;
3010 int dumpability = prctl(PR_GET_DUMPABLE);
3012 struct passwd *
user_info = getpwnam(username.toLocal8Bit().constData());
3013 const uid_t user_id =
geteuid();
3017 std::cerr <<
"You must be running as root to use the --user switch.\n";
3022 LOG(VB_GENERAL, LOG_WARNING,
3023 QString(
"Already running as '%1'").arg(username));
3027 if (!qputenv(
"HOME",
user_info->pw_dir))
3029 std::cerr <<
"Error setting home directory.\n";
3034 std::cerr <<
"Error setting effective group.\n";
3039 std::cerr <<
"Error setting groups.\n";
3044 std::cerr <<
"Error setting effective user.\n";
3048 if (dumpability && (prctl(PR_SET_DUMPABLE, dumpability) == -1))
3050 LOG(VB_GENERAL, LOG_WARNING,
"Unable to re-enable core file "
3051 "creation. Run without the --user argument to use "
3052 "shell-specified limits.");
3058 std::cerr << QString(
"Invalid user '%1' specified with --user")
3059 .arg(username).toLocal8Bit().constData() <<
'\n';
3071 std::ofstream pidfs;
3075 if (signal(
SIGPIPE, SIG_IGN) == SIG_ERR)
3076 LOG(VB_GENERAL, LOG_WARNING,
"Unable to ignore SIGPIPE");
3081 std::cerr <<
"Daemonizing is unavailable in OSX\n";
3082 LOG(VB_GENERAL, LOG_WARNING,
"Unable to daemonize");
3087 std::cerr <<
"Failed to daemonize: " <<
ENO_STR <<
'\n';
3092 QString username =
toString(
"username");
3093 if (!username.isEmpty() && !
setUser(username))
3098 pidfs << getpid() <<
'\n';
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
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
virtual ~MythCommandLineParser()
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.
@ GENERIC_EXIT_PERMISSIONS_ERROR
File permissions error.
@ GENERIC_EXIT_OK
Exited with no error.
@ GENERIC_EXIT_DAEMONIZING_ERROR
Error daemonizing or execl.
@ GENERIC_EXIT_INVALID_CMDLINE
Command line parse error.
static constexpr uint8_t START
int verboseArgParse(const QString &arg)
Parse the –verbose commandline argument and set the verbose level.
QString logLevelGetName(LogLevel_t level)
Map a log level enumerated value back to the name.
LogLevel_t logLevelGet(const QString &level)
Map a log level name back to the enumerated value.
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.
int syslogGetFacility(const QString &facility)
Map a syslog facility name back to the enumerated value.
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 LOG(_MASK_, _LEVEL_, _QSTRING_)
const char * GetMythSourceVersion()
const char * GetMythSourcePath()
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
@ kFilename
Default UTC, "yyyyMMddhhmmss".
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
def user_info(user, format="xml")