00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include <iostream>
00024 #include <fstream>
00025 #include <stdio.h>
00026 #include <stdlib.h>
00027 #include <algorithm>
00028 #include <sys/types.h>
00029 #include <unistd.h>
00030
00031 #ifndef _WIN32
00032 #include <sys/ioctl.h>
00033 #include <pwd.h>
00034 #include <grp.h>
00035 #if defined(__linux__) || defined(__LINUX__)
00036 #include <sys/prctl.h>
00037 #endif
00038 #endif
00039
00040 using namespace std;
00041
00042 #include <QDir>
00043 #include <QFile>
00044 #include <QFileInfo>
00045 #include <QSize>
00046 #include <QVariant>
00047 #include <QVariantList>
00048 #include <QVariantMap>
00049 #include <QString>
00050 #include <QCoreApplication>
00051 #include <QTextStream>
00052 #include <QDateTime>
00053
00054 #include "mythcommandlineparser.h"
00055 #include "mythcorecontext.h"
00056 #include "exitcodes.h"
00057 #include "mythconfig.h"
00058 #include "mythlogging.h"
00059 #include "mythversion.h"
00060 #include "logging.h"
00061 #include "mythmiscutil.h"
00062
00063 #define TERMWIDTH 79
00064
00065 const int kEnd = 0,
00066 kEmpty = 1,
00067 kOptOnly = 2,
00068 kOptVal = 3,
00069 kArg = 4,
00070 kPassthrough = 5,
00071 kInvalid = 6;
00072
00073 const char* NamedOptType(int type);
00074 bool openPidfile(ofstream &pidfs, const QString &pidfile);
00075 bool setUser(const QString &username);
00076 int GetTermWidth(void);
00077
00081 int GetTermWidth(void)
00082 {
00083 #ifdef _WIN32
00084 return TERMWIDTH;
00085 #else
00086 struct winsize ws;
00087
00088 if (ioctl(0, TIOCGWINSZ, &ws) != 0)
00089 return TERMWIDTH;
00090
00091 return (int)ws.ws_col;
00092 #endif
00093 }
00094
00098 const char* NamedOptType(int type)
00099 {
00100 switch (type)
00101 {
00102 case kEnd:
00103 return "kEnd";
00104
00105 case kEmpty:
00106 return "kEmpty";
00107
00108 case kOptOnly:
00109 return "kOptOnly";
00110
00111 case kOptVal:
00112 return "kOptVal";
00113
00114 case kArg:
00115 return "kArg";
00116
00117 case kPassthrough:
00118 return "kPassthrough";
00119
00120 case kInvalid:
00121 return "kInvalid";
00122
00123 default:
00124 return "kUnknown";
00125 }
00126 }
00127
00144 CommandLineArg::CommandLineArg(QString name, QVariant::Type type,
00145 QVariant def, QString help, QString longhelp) :
00146 ReferenceCounter(), m_given(false), m_converted(false), m_name(name),
00147 m_group(""), m_deprecated(""), m_removed(""), m_removedversion(""),
00148 m_type(type), m_default(def), m_help(help), m_longhelp(longhelp)
00149 {
00150 if ((m_type != QVariant::String) && (m_type != QVariant::StringList) &&
00151 (m_type != QVariant::Map))
00152 m_converted = true;
00153 }
00154
00161 CommandLineArg::CommandLineArg(QString name, QVariant::Type type, QVariant def)
00162 : ReferenceCounter(), m_given(false), m_converted(false), m_name(name),
00163 m_group(""), m_deprecated(""), m_removed(""), m_removedversion(""),
00164 m_type(type), m_default(def)
00165 {
00166 if ((m_type != QVariant::String) && (m_type != QVariant::StringList) &&
00167 (m_type != QVariant::Map))
00168 m_converted = true;
00169 }
00170
00178 CommandLineArg::CommandLineArg(QString name) :
00179 ReferenceCounter(), m_given(false), m_converted(false), m_name(name),
00180 m_deprecated(""), m_removed(""), m_removedversion(""),
00181 m_type(QVariant::Invalid)
00182 {
00183 }
00184
00188 QString CommandLineArg::GetKeywordString(void) const
00189 {
00190
00191
00192 return m_keywords.join(" OR ");
00193 }
00194
00198 int CommandLineArg::GetKeywordLength(void) const
00199 {
00200 int len = GetKeywordString().length();
00201
00202 QList<CommandLineArg*>::const_iterator i1;
00203 for (i1 = m_parents.begin(); i1 != m_parents.end(); ++i1)
00204 len = max(len, (*i1)->GetKeywordLength()+2);
00205
00206 return len;
00207 }
00208
00224 QString CommandLineArg::GetHelpString(int off, QString group, bool force) const
00225 {
00226 QString helpstr;
00227 QTextStream msg(&helpstr, QIODevice::WriteOnly);
00228 int termwidth = GetTermWidth();
00229
00230 if (m_help.isEmpty() && !force)
00231
00232 return helpstr;
00233
00234 if ((m_group != group) && !force)
00235
00236 return helpstr;
00237
00238 if (!m_parents.isEmpty() && !force)
00239
00240
00241 return helpstr;
00242
00243 if (!m_deprecated.isEmpty())
00244
00245 return helpstr;
00246
00247 if (!m_removed.isEmpty())
00248
00249 return helpstr;
00250
00251 QString pad;
00252 pad.fill(' ', off);
00253
00254
00255 QStringList hlist = m_help.split('\n');
00256 wrapList(hlist, termwidth-off);
00257 if (!m_parents.isEmpty())
00258 msg << " ";
00259 msg << GetKeywordString().leftJustified(off, ' ')
00260 << hlist[0] << endl;
00261
00262
00263 QStringList::const_iterator i1;
00264 for (i1 = hlist.begin() + 1; i1 != hlist.end(); ++i1)
00265 msg << pad << *i1 << endl;
00266
00267
00268 QList<CommandLineArg*>::const_iterator i2;
00269 for (i2 = m_children.begin(); i2 != m_children.end(); ++i2)
00270 msg << (*i2)->GetHelpString(off, group, true);
00271
00272 msg.flush();
00273 return helpstr;
00274 }
00275
00283 QString CommandLineArg::GetLongHelpString(QString keyword) const
00284 {
00285 QString helpstr;
00286 QTextStream msg(&helpstr, QIODevice::WriteOnly);
00287 int termwidth = GetTermWidth();
00288
00289
00290 if (!m_keywords.contains(keyword))
00291 return helpstr;
00292
00293
00294 if (!m_removed.isEmpty())
00295 PrintRemovedWarning(keyword);
00296
00297 else if (!m_deprecated.isEmpty())
00298 PrintDeprecatedWarning(keyword);
00299
00300 msg << "Option: " << keyword << endl << endl;
00301
00302 bool first = true;
00303
00304
00305 QStringList::const_iterator i1;
00306 for (i1 = m_keywords.begin(); i1 != m_keywords.end(); ++i1)
00307 {
00308 if (*i1 != keyword)
00309 {
00310 if (first)
00311 {
00312 msg << "Aliases: " << *i1 << endl;
00313 first = false;
00314 }
00315 else
00316 msg << " " << *i1 << endl;
00317 }
00318 }
00319
00320
00321 msg << "Type: " << QVariant::typeToName(m_type) << endl;
00322 if (m_default.canConvert(QVariant::String))
00323 msg << "Default: " << m_default.toString() << endl;
00324
00325 QStringList help;
00326 if (m_longhelp.isEmpty())
00327 help = m_help.split("\n");
00328 else
00329 help = m_longhelp.split("\n");
00330 wrapList(help, termwidth-13);
00331
00332
00333 msg << "Description: " << help[0] << endl;
00334 for (i1 = help.begin() + 1; i1 != help.end(); ++i1)
00335 msg << " " << *i1 << endl;
00336
00337 QList<CommandLineArg*>::const_iterator i2;
00338
00339
00340 if (!m_parents.isEmpty())
00341 {
00342 msg << endl << "Can be used in combination with:" << endl;
00343 for (i2 = m_parents.constBegin(); i2 != m_parents.constEnd(); ++i2)
00344 msg << " " << (*i2)->GetPreferredKeyword()
00345 .toLocal8Bit().constData();
00346 msg << endl;
00347 }
00348
00349 if (!m_children.isEmpty())
00350 {
00351 msg << endl << "Allows the use of:" << endl;
00352 for (i2 = m_children.constBegin(); i2 != m_children.constEnd(); ++i2)
00353 msg << " " << (*i2)->GetPreferredKeyword()
00354 .toLocal8Bit().constData();
00355 msg << endl;
00356 }
00357
00358 if (!m_requires.isEmpty())
00359 {
00360 msg << endl << "Requires the use of:" << endl;
00361 for (i2 = m_requires.constBegin(); i2 != m_requires.constEnd(); ++i2)
00362 msg << " " << (*i2)->GetPreferredKeyword()
00363 .toLocal8Bit().constData();
00364 msg << endl;
00365 }
00366
00367 if (!m_blocks.isEmpty())
00368 {
00369 msg << endl << "Prevents the use of:" << endl;
00370 for (i2 = m_blocks.constBegin(); i2 != m_blocks.constEnd(); ++i2)
00371 msg << " " << (*i2)->GetPreferredKeyword()
00372 .toLocal8Bit().constData();
00373 msg << endl;
00374 }
00375
00376 msg.flush();
00377 return helpstr;
00378 }
00379
00386 bool CommandLineArg::Set(QString opt)
00387 {
00388 m_usedKeyword = opt;
00389
00390 switch (m_type)
00391 {
00392 case QVariant::Bool:
00393 m_stored = QVariant(!m_default.toBool());
00394 break;
00395
00396 case QVariant::Int:
00397 if (m_stored.isNull())
00398 m_stored = QVariant(1);
00399 else
00400 m_stored = QVariant(m_stored.toInt() + 1);
00401 break;
00402
00403 case QVariant::String:
00404 m_stored = m_default;
00405 break;
00406
00407 default:
00408 cerr << "Command line option did not receive value:" << endl
00409 << " " << opt.toLocal8Bit().constData() << endl;
00410 return false;
00411 }
00412
00413 m_given = true;
00414 return true;
00415 }
00416
00419 bool CommandLineArg::Set(QString opt, QByteArray val)
00420 {
00421 QVariantList vlist;
00422 QList<QByteArray> blist;
00423 QVariantMap vmap;
00424 m_usedKeyword = opt;
00425
00426 switch (m_type)
00427 {
00428 case QVariant::Bool:
00429 cerr << "Boolean type options do not accept values:" << endl
00430 << " " << opt.toLocal8Bit().constData() << endl;
00431 return false;
00432
00433 case QVariant::String:
00434 m_stored = QVariant(val);
00435 break;
00436
00437 case QVariant::Int:
00438 m_stored = QVariant(val.toInt());
00439 break;
00440
00441 case QVariant::UInt:
00442 m_stored = QVariant(val.toUInt());
00443 break;
00444
00445 case QVariant::LongLong:
00446 m_stored = QVariant(val.toLongLong());
00447 break;
00448
00449 case QVariant::Double:
00450 m_stored = QVariant(val.toDouble());
00451 break;
00452
00453 case QVariant::DateTime:
00454 m_stored = QVariant(myth_dt_from_string(QString(val)));
00455 break;
00456
00457 case QVariant::StringList:
00458 if (!m_stored.isNull())
00459 vlist = m_stored.toList();
00460 vlist << val;
00461 m_stored = QVariant(vlist);
00462 break;
00463
00464 case QVariant::Map:
00465 if (!val.contains('='))
00466 {
00467 cerr << "Command line option did not get expected "
00468 << "key/value pair" << endl;
00469 return false;
00470 }
00471
00472 blist = val.split('=');
00473
00474 if (!m_stored.isNull())
00475 vmap = m_stored.toMap();
00476 vmap[QString(blist[0])] = QVariant(blist[1]);
00477 m_stored = QVariant(vmap);
00478 break;
00479
00480 case QVariant::Size:
00481 if (!val.contains('x'))
00482 {
00483 cerr << "Command line option did not get expected "
00484 << "XxY pair" << endl;
00485 return false;
00486 }
00487
00488 blist = val.split('x');
00489 m_stored = QVariant(QSize(blist[0].toInt(), blist[1].toInt()));
00490 break;
00491
00492 default:
00493 m_stored = QVariant(val);
00494 }
00495
00496 m_given = true;
00497 return true;
00498 }
00499
00502 CommandLineArg* CommandLineArg::SetParentOf(QString opt)
00503 {
00504 m_children << new CommandLineArg(opt);
00505 return this;
00506 }
00507
00510 CommandLineArg* CommandLineArg::SetParentOf(QStringList opts)
00511 {
00512 QStringList::const_iterator i = opts.begin();
00513 for (; i != opts.end(); ++i)
00514 m_children << new CommandLineArg(*i);
00515 return this;
00516 }
00517
00520 CommandLineArg* CommandLineArg::SetParent(QString opt)
00521 {
00522 m_parents << new CommandLineArg(opt);
00523 return this;
00524 }
00525
00528 CommandLineArg* CommandLineArg::SetParent(QStringList opts)
00529 {
00530 QStringList::const_iterator i = opts.begin();
00531 for (; i != opts.end(); ++i)
00532 m_parents << new CommandLineArg(*i);
00533 return this;
00534 }
00535
00538 CommandLineArg* CommandLineArg::SetChildOf(QString opt)
00539 {
00540 m_parents << new CommandLineArg(opt);
00541 return this;
00542 }
00543
00546 CommandLineArg* CommandLineArg::SetChildOf(QStringList opts)
00547 {
00548 QStringList::const_iterator i = opts.begin();
00549 for (; i != opts.end(); ++i)
00550 m_parents << new CommandLineArg(*i);
00551 return this;
00552 }
00553
00556 CommandLineArg* CommandLineArg::SetChild(QString opt)
00557 {
00558 m_children << new CommandLineArg(opt);
00559 return this;
00560 }
00561
00564 CommandLineArg* CommandLineArg::SetChild(QStringList opts)
00565 {
00566 QStringList::const_iterator i = opts.begin();
00567 for (; i != opts.end(); ++i)
00568 m_children << new CommandLineArg(*i);
00569 return this;
00570 }
00571
00574 CommandLineArg* CommandLineArg::SetRequiredChild(QString opt)
00575 {
00576 m_children << new CommandLineArg(opt);
00577 m_requires << new CommandLineArg(opt);
00578 return this;
00579 }
00580
00583 CommandLineArg* CommandLineArg::SetRequiredChild(QStringList opts)
00584 {
00585 QStringList::const_iterator i = opts.begin();
00586 for (; i != opts.end(); ++i)
00587 {
00588 m_children << new CommandLineArg(*i);
00589 m_requires << new CommandLineArg(*i);
00590 }
00591 return this;
00592 }
00593
00596 CommandLineArg* CommandLineArg::SetRequiredChildOf(QString opt)
00597 {
00598 m_parents << new CommandLineArg(opt);
00599 m_requiredby << new CommandLineArg(opt);
00600 return this;
00601 }
00602
00605 CommandLineArg* CommandLineArg::SetRequiredChildOf(QStringList opts)
00606 {
00607 QStringList::const_iterator i = opts.begin();
00608 for (; i != opts.end(); ++i)
00609 {
00610 m_parents << new CommandLineArg(*i);
00611 m_requiredby << new CommandLineArg(*i);
00612 }
00613 return this;
00614 }
00615
00618 CommandLineArg* CommandLineArg::SetRequires(QString opt)
00619 {
00620 m_requires << new CommandLineArg(opt);
00621 return this;
00622 }
00623
00626 CommandLineArg* CommandLineArg::SetRequires(QStringList opts)
00627 {
00628 QStringList::const_iterator i = opts.begin();
00629 for (; i != opts.end(); ++i)
00630 m_requires << new CommandLineArg(*i);
00631 return this;
00632 }
00633
00636 CommandLineArg* CommandLineArg::SetBlocks(QString opt)
00637 {
00638 m_blocks << new CommandLineArg(opt);
00639 return this;
00640 }
00641
00644 CommandLineArg* CommandLineArg::SetBlocks(QStringList opts)
00645 {
00646 QStringList::const_iterator i = opts.begin();
00647 for (; i != opts.end(); ++i)
00648 m_blocks << new CommandLineArg(*i);
00649 return this;
00650 }
00651
00654 CommandLineArg* CommandLineArg::SetDeprecated(QString depstr)
00655 {
00656 if (depstr.isEmpty())
00657 depstr = "and will be removed in a future version.";
00658 m_deprecated = depstr;
00659 return this;
00660 }
00661
00664 CommandLineArg* CommandLineArg::SetRemoved(QString remstr, QString remver)
00665 {
00666 if (remstr.isEmpty())
00667 remstr = "and is no longer available in this version.";
00668 m_removed = remstr;
00669 m_removedversion = remver;
00670 return this;
00671 }
00672
00678 void CommandLineArg::SetParentOf(CommandLineArg *other, bool forward)
00679 {
00680 int i;
00681 bool replaced = false;
00682 other->UpRef();
00683
00684 for (i = 0; i < m_children.size(); i++)
00685 {
00686 if (m_children[i]->m_name == other->m_name)
00687 {
00688 m_children[i]->DownRef();
00689 m_children.replace(i, other);
00690 replaced = true;
00691 break;
00692 }
00693 }
00694
00695 if (!replaced)
00696 m_children << other;
00697
00698 if (forward)
00699 other->SetChildOf(this, false);
00700 }
00701
00707 void CommandLineArg::SetChildOf(CommandLineArg *other, bool forward)
00708 {
00709 int i;
00710 bool replaced = false;
00711 other->UpRef();
00712
00713 for (i = 0; i < m_parents.size(); i++)
00714 {
00715 if (m_parents[i]->m_name == other->m_name)
00716 {
00717 m_parents[i]->DownRef();
00718 m_parents.replace(i, other);
00719 replaced = true;
00720 break;
00721 }
00722 }
00723
00724 if (!replaced)
00725 m_parents << other;
00726
00727 if (forward)
00728 other->SetParentOf(this, false);
00729 }
00730
00736 void CommandLineArg::SetRequires(CommandLineArg *other, bool forward)
00737 {
00738 int i;
00739 bool replaced = false;
00740 other->UpRef();
00741
00742 for (i = 0; i < m_requires.size(); i++)
00743 {
00744 if (m_requires[i]->m_name == other->m_name)
00745 {
00746 m_requires[i]->DownRef();
00747 m_requires.replace(i, other);
00748 replaced = true;
00749 break;
00750 }
00751 }
00752
00753 if (!replaced)
00754 m_requires << other;
00755
00756
00757
00758
00759 }
00760
00766 void CommandLineArg::SetBlocks(CommandLineArg *other, bool forward)
00767 {
00768 int i;
00769 bool replaced = false;
00770 other->UpRef();
00771
00772 for (i = 0; i < m_blocks.size(); i++)
00773 {
00774 if (m_blocks[i]->m_name == other->m_name)
00775 {
00776 m_blocks[i]->DownRef();
00777 m_blocks.replace(i, other);
00778 replaced = true;
00779 break;
00780 }
00781 }
00782
00783 if (!replaced)
00784 m_blocks << other;
00785
00786 if (forward)
00787 other->SetBlocks(this, false);
00788 }
00789
00792 void CommandLineArg::AllowOneOf(QList<CommandLineArg*> args)
00793 {
00794
00795
00796
00797
00798 QList<CommandLineArg*>::const_iterator i1,i2;
00799
00800
00801 for (i1 = args.begin(); i1 != args.end()-1; ++i1)
00802 {
00803
00804
00805 for (i2 = i1+1; i2 != args.end(); ++i2)
00806 {
00807 (*i1)->SetBlocks(*i2);
00808 }
00809
00810 if ((*i1)->m_type == QVariant::Invalid)
00811 (*i1)->DownRef();
00812 }
00813 }
00814
00821 void CommandLineArg::Convert(void)
00822 {
00823 if (!QCoreApplication::instance())
00824
00825 return;
00826
00827 if (m_converted)
00828
00829 return;
00830
00831 if (!m_given)
00832 {
00833
00834 m_converted = true;
00835 return;
00836 }
00837
00838 if (m_type == QVariant::String)
00839 {
00840 if (m_stored.type() == QVariant::ByteArray)
00841 {
00842 m_stored = QString::fromLocal8Bit(m_stored.toByteArray());
00843 }
00844
00845
00846
00847 }
00848 else if (m_type == QVariant::StringList)
00849 {
00850 if (m_stored.type() == QVariant::List)
00851 {
00852 QVariantList vlist = m_stored.toList();
00853 QVariantList::const_iterator iter = vlist.begin();
00854 QStringList slist;
00855 for (; iter != vlist.end(); ++iter)
00856 slist << QString::fromLocal8Bit(iter->toByteArray());
00857 m_stored = QVariant(slist);
00858 }
00859 }
00860 else if (m_type == QVariant::Map)
00861 {
00862 QVariantMap vmap = m_stored.toMap();
00863 QVariantMap::iterator iter = vmap.begin();
00864 for (; iter != vmap.end(); ++iter)
00865 (*iter) = QString::fromLocal8Bit(iter->toByteArray());
00866 }
00867 else
00868 return;
00869
00870 m_converted = true;
00871 }
00872
00873
00879 QString CommandLineArg::GetPreferredKeyword(void) const
00880 {
00881 QStringList::const_iterator it;
00882 QString preferred;
00883 int len = 0, len2;
00884
00885 for (it = m_keywords.constBegin(); it != m_keywords.constEnd(); ++it)
00886 {
00887 len2 = (*it).size();
00888 if (len2 > len)
00889 {
00890 preferred = *it;
00891 len = len2;
00892 }
00893 }
00894
00895 return preferred;
00896 }
00897
00901 bool CommandLineArg::TestLinks(void) const
00902 {
00903 if (!m_given)
00904 return true;
00905
00906 QList<CommandLineArg*>::const_iterator i;
00907
00908 bool passes = false;
00909 for (i = m_parents.constBegin(); i != m_parents.constEnd(); ++i)
00910 {
00911
00912 if ((*i)->m_given)
00913 {
00914 passes = true;
00915 break;
00916 }
00917 }
00918 if (!passes && !m_parents.isEmpty())
00919 {
00920 cerr << "ERROR: " << m_usedKeyword.toLocal8Bit().constData()
00921 << " requires at least one of the following arguments" << endl;
00922 for (i = m_parents.constBegin(); i != m_parents.constEnd(); ++i)
00923 cerr << " "
00924 << (*i)->GetPreferredKeyword().toLocal8Bit().constData();
00925 cerr << endl << endl;
00926 return false;
00927 }
00928
00929
00930
00931 for (i = m_requires.constBegin(); i != m_requires.constEnd(); ++i)
00932 {
00933
00934 if (!(*i)->m_given)
00935 {
00936 cerr << "ERROR: " << m_usedKeyword.toLocal8Bit().constData()
00937 << " requires all of the following be defined as well"
00938 << endl;
00939 for (i = m_requires.constBegin(); i != m_requires.constEnd(); ++i)
00940 cerr << " "
00941 << (*i)->GetPreferredKeyword().toLocal8Bit()
00942 .constData();
00943 cerr << endl << endl;
00944 return false;
00945 }
00946 }
00947
00948 for (i = m_blocks.constBegin(); i != m_blocks.constEnd(); ++i)
00949 {
00950
00951 if ((*i)->m_given)
00952 {
00953 cerr << "ERROR: " << m_usedKeyword.toLocal8Bit().constData()
00954 << " requires that none of the following be defined" << endl;
00955 for (i = m_blocks.constBegin(); i != m_blocks.constEnd(); ++i)
00956 cerr << " "
00957 << (*i)->GetPreferredKeyword().toLocal8Bit()
00958 .constData();
00959 cerr << endl << endl;
00960 return false;
00961 }
00962 }
00963
00964 return true;
00965 }
00966
00969 void CommandLineArg::CleanupLinks(void)
00970 {
00971
00972 while (!m_parents.isEmpty())
00973 m_parents.takeFirst()->DownRef();
00974
00975 while (!m_children.isEmpty())
00976 m_children.takeFirst()->DownRef();
00977
00978 while (!m_blocks.isEmpty())
00979 m_blocks.takeFirst()->DownRef();
00980
00981 while (!m_requires.isEmpty())
00982 m_requires.takeFirst()->DownRef();
00983
00984 while (!m_requiredby.isEmpty())
00985 m_requiredby.takeFirst()->DownRef();
00986 }
00987
00990 void CommandLineArg::PrintVerbose(void) const
00991 {
00992 if (!m_given)
00993 return;
00994
00995 cerr << " " << m_name.leftJustified(30).toLocal8Bit().constData();
00996
00997 QSize tmpsize;
00998 QMap<QString, QVariant> tmpmap;
00999 QMap<QString, QVariant>::const_iterator it;
01000 QVariantList vlist;
01001 QVariantList::const_iterator it2;
01002 bool first;
01003
01004 switch (m_type)
01005 {
01006 case QVariant::Bool:
01007 cerr << (m_stored.toBool() ? "True" : "False") << endl;
01008 break;
01009
01010 case QVariant::Int:
01011 cerr << m_stored.toInt() << endl;
01012 break;
01013
01014 case QVariant::UInt:
01015 cerr << m_stored.toUInt() << endl;
01016 break;
01017
01018 case QVariant::LongLong:
01019 cerr << m_stored.toLongLong() << endl;
01020 break;
01021
01022 case QVariant::Double:
01023 cerr << m_stored.toDouble() << endl;
01024 break;
01025
01026 case QVariant::Size:
01027 tmpsize = m_stored.toSize();
01028 cerr << "x=" << tmpsize.width()
01029 << " y=" << tmpsize.height()
01030 << endl;
01031 break;
01032
01033 case QVariant::String:
01034 cerr << '"' << m_stored.toByteArray().constData()
01035 << '"' << endl;
01036 break;
01037
01038 case QVariant::StringList:
01039 vlist = m_stored.toList();
01040 it2 = vlist.begin();
01041 cerr << '"' << it2->toByteArray().constData() << '"';
01042 ++it2;
01043 for (; it2 != vlist.end(); ++it2)
01044 cerr << ", \""
01045 << it2->constData()
01046 << '"';
01047 cerr << endl;
01048 break;
01049
01050 case QVariant::Map:
01051 tmpmap = m_stored.toMap();
01052 first = true;
01053
01054 for (it = tmpmap.begin(); it != tmpmap.end(); ++it)
01055 {
01056 if (first)
01057 first = false;
01058 else
01059 cerr << QString("").leftJustified(32)
01060 .toLocal8Bit().constData();
01061
01062 cerr << it.key().toLocal8Bit().constData()
01063 << '='
01064 << it->toByteArray().constData()
01065 << endl;
01066 }
01067
01068 break;
01069
01070 case QVariant::DateTime:
01071 cerr << m_stored.toDateTime().toString(Qt::ISODate)
01072 .toLocal8Bit().constData()
01073 << endl;
01074 break;
01075
01076 default:
01077 cerr << endl;
01078 }
01079 }
01080
01083 void CommandLineArg::PrintRemovedWarning(QString &keyword) const
01084 {
01085 QString warn = QString("%1 has been removed").arg(keyword);
01086 if (!m_removedversion.isEmpty())
01087 warn += QString(" as of MythTV %1").arg(m_removedversion);
01088
01089 cerr << QString("****************************************************\n"
01090 " WARNING: %1\n"
01091 " %2\n"
01092 "****************************************************\n\n")
01093 .arg(warn).arg(m_removed)
01094 .toLocal8Bit().constData();
01095 }
01096
01099 void CommandLineArg::PrintDeprecatedWarning(QString &keyword) const
01100 {
01101 cerr << QString("****************************************************\n"
01102 " WARNING: %1 has been deprecated\n"
01103 " %2\n"
01104 "****************************************************\n\n")
01105 .arg(keyword).arg(m_deprecated)
01106 .toLocal8Bit().constData();
01107 }
01108
01121 MythCommandLineParser::MythCommandLineParser(QString appname) :
01122 m_appname(appname), m_passthroughActive(false),
01123 m_overridesImported(false), m_verbose(false)
01124 {
01125 char *verbose = getenv("VERBOSE_PARSER");
01126 if (verbose != NULL)
01127 {
01128 cerr << "MythCommandLineParser is now operating verbosely." << endl;
01129 m_verbose = true;
01130 }
01131
01132 LoadArguments();
01133 }
01134
01135 MythCommandLineParser::~MythCommandLineParser()
01136 {
01137 QMap<QString, CommandLineArg*>::iterator i;
01138
01139 i = m_namedArgs.begin();
01140 while (i != m_namedArgs.end())
01141 {
01142 (*i)->CleanupLinks();
01143 (*i)->DownRef();
01144 i = m_namedArgs.erase(i);
01145 }
01146
01147 i = m_optionedArgs.begin();
01148 while (i != m_optionedArgs.end())
01149 {
01150 (*i)->DownRef();
01151 i = m_optionedArgs.erase(i);
01152 }
01153 }
01154
01185 CommandLineArg* MythCommandLineParser::add(QStringList arglist,
01186 QString name, QVariant::Type type, QVariant def,
01187 QString help, QString longhelp)
01188 {
01189 CommandLineArg *arg;
01190
01191 if (m_namedArgs.contains(name))
01192 arg = m_namedArgs[name];
01193 else
01194 {
01195 arg = new CommandLineArg(name, type, def, help, longhelp);
01196 m_namedArgs.insert(name, arg);
01197 }
01198
01199 QStringList::const_iterator i;
01200 for (i = arglist.begin(); i != arglist.end(); ++i)
01201 {
01202 if (!m_optionedArgs.contains(*i))
01203 {
01204 arg->AddKeyword(*i);
01205 if (m_verbose)
01206 cerr << "Adding " << (*i).toLocal8Bit().constData()
01207 << " as taking type '" << QVariant::typeToName(type)
01208 << "'" << endl;
01209 arg->UpRef();
01210 m_optionedArgs.insert(*i, arg);
01211 }
01212 }
01213
01214 return arg;
01215 }
01216
01219 void MythCommandLineParser::PrintVersion(void) const
01220 {
01221 cout << "Please attach all output as a file in bug reports." << endl;
01222 cout << "MythTV Version : " << MYTH_SOURCE_VERSION << endl;
01223 cout << "MythTV Branch : " << MYTH_SOURCE_PATH << endl;
01224 cout << "Network Protocol : " << MYTH_PROTO_VERSION << endl;
01225 cout << "Library API : " << MYTH_BINARY_VERSION << endl;
01226 cout << "QT Version : " << QT_VERSION_STR << endl;
01227 #ifdef MYTH_BUILD_CONFIG
01228 cout << "Options compiled in:" <<endl;
01229 cout << MYTH_BUILD_CONFIG << endl;
01230 #endif
01231 }
01232
01235 void MythCommandLineParser::PrintHelp(void) const
01236 {
01237 QString help = GetHelpString();
01238 cerr << help.toLocal8Bit().constData();
01239 }
01240
01246 QString MythCommandLineParser::GetHelpString(void) const
01247 {
01248 QString helpstr;
01249 QTextStream msg(&helpstr, QIODevice::WriteOnly);
01250
01251 QString versionStr = QString("%1 version: %2 [%3] www.mythtv.org")
01252 .arg(m_appname).arg(MYTH_SOURCE_PATH).arg(MYTH_SOURCE_VERSION);
01253 msg << versionStr << endl;
01254
01255 if (toString("showhelp").isEmpty())
01256 {
01257
01258
01259 QString descr = GetHelpHeader();
01260 if (descr.size() > 0)
01261 msg << endl << descr << endl << endl;
01262
01263
01264 QStringList groups("");
01265 int maxlen = 0;
01266 QMap<QString, CommandLineArg*>::const_iterator i1;
01267 for (i1 = m_namedArgs.begin(); i1 != m_namedArgs.end(); ++i1)
01268 {
01269 maxlen = max((*i1)->GetKeywordLength(), maxlen);
01270 if (!groups.contains((*i1)->m_group))
01271 groups << (*i1)->m_group;
01272 }
01273
01274
01275
01276 maxlen += 4;
01277 QStringList::const_iterator i2;
01278 for (i2 = groups.begin(); i2 != groups.end(); ++i2)
01279 {
01280 if ((*i2).isEmpty())
01281 msg << "Misc. Options:" << endl;
01282 else
01283 msg << (*i2).toLocal8Bit().constData() << " Options:" << endl;
01284
01285 for (i1 = m_namedArgs.begin(); i1 != m_namedArgs.end(); ++i1)
01286 msg << (*i1)->GetHelpString(maxlen, *i2);
01287 msg << endl;
01288 }
01289 }
01290 else
01291 {
01292
01293 QString optstr = "-" + toString("showhelp");
01294 if (!m_optionedArgs.contains(optstr))
01295 {
01296 optstr = "-" + optstr;
01297 if (!m_optionedArgs.contains(optstr))
01298 return QString("Could not find option matching '%1'\n")
01299 .arg(toString("showhelp"));
01300 }
01301
01302 msg << m_optionedArgs[optstr]->GetLongHelpString(optstr);
01303 }
01304
01305 msg.flush();
01306 return helpstr;
01307 }
01308
01311 int MythCommandLineParser::getOpt(int argc, const char * const * argv,
01312 int &argpos, QString &opt, QByteArray &val)
01313 {
01314 opt.clear();
01315 val.clear();
01316
01317 if (argpos >= argc)
01318
01319 return kEnd;
01320
01321 QByteArray tmp(argv[argpos]);
01322 if (tmp.isEmpty())
01323
01324 return kEmpty;
01325
01326 if (m_passthroughActive)
01327 {
01328
01329 val = tmp;
01330 return kArg;
01331 }
01332
01333 if (tmp.startsWith('-') && tmp.size() > 1)
01334 {
01335 if (tmp == "--")
01336 {
01337
01338 m_passthroughActive = true;
01339 return kPassthrough;
01340 }
01341
01342 if (tmp.contains('='))
01343 {
01344
01345 QList<QByteArray> blist = tmp.split('=');
01346
01347 if (blist.size() != 2)
01348 {
01349
01350 opt = QString(tmp);
01351 return kInvalid;
01352 }
01353
01354 opt = QString(blist[0]);
01355 val = blist[1];
01356 return kOptVal;
01357 }
01358
01359 opt = QString(tmp);
01360
01361 if (argpos+1 >= argc)
01362
01363 return kOptOnly;
01364
01365 tmp = QByteArray(argv[++argpos]);
01366 if (tmp.isEmpty())
01367
01368 return kOptOnly;
01369
01370 if (tmp.startsWith("-") && tmp.size() > 1)
01371 {
01372
01373 argpos--;
01374 return kOptOnly;
01375 }
01376
01377 val = tmp;
01378 return kOptVal;
01379 }
01380 else
01381 {
01382
01383 val = tmp;
01384 return kArg;
01385 }
01386
01387 }
01388
01395 bool MythCommandLineParser::Parse(int argc, const char * const * argv)
01396 {
01397 int res;
01398 QString opt;
01399 QByteArray val;
01400 CommandLineArg *argdef;
01401
01402
01403 if (!ReconcileLinks())
01404 return false;
01405
01406
01407 for (int argpos = 1; argpos < argc; ++argpos)
01408 {
01409
01410
01411 res = getOpt(argc, argv, argpos, opt, val);
01412
01413 if (m_verbose)
01414 cerr << "res: " << NamedOptType(res) << endl
01415 << "opt: " << opt.toLocal8Bit().constData() << endl
01416 << "val: " << val.constData() << endl << endl;
01417
01418
01419 if (res == kPassthrough && !m_namedArgs.contains("_passthrough"))
01420 {
01421 cerr << "Received '--' but passthrough has not been enabled" << endl;
01422 SetValue("showhelp", "");
01423 return false;
01424 }
01425
01426
01427 if (res == kEnd)
01428 break;
01429
01430
01431
01432 else if (res == kEmpty)
01433 continue;
01434
01435
01436 else if (res == kInvalid)
01437 {
01438 cerr << "Invalid option received:" << endl << " "
01439 << opt.toLocal8Bit().constData();
01440 SetValue("showhelp", "");
01441 return false;
01442 }
01443
01444
01445 else if (m_passthroughActive)
01446 {
01447 m_namedArgs["_passthrough"]->Set("", val);
01448 continue;
01449 }
01450
01451
01452 else if (res == kArg)
01453 {
01454 if (!m_namedArgs.contains("_args"))
01455 {
01456 cerr << "Received '"
01457 << val.constData()
01458 << "' but unassociated arguments have not been enabled"
01459 << endl;
01460 SetValue("showhelp", "");
01461 return false;
01462 }
01463
01464 m_namedArgs["_args"]->Set("", val);
01465 continue;
01466 }
01467
01468
01469 if (toBool("_args"))
01470 {
01471 cerr << "Command line arguments received out of sequence"
01472 << endl;
01473 SetValue("showhelp", "");
01474 return false;
01475 }
01476
01477 #ifdef Q_WS_MACX
01478 if (opt.startsWith("-psn_"))
01479 {
01480 cerr << "Ignoring Process Serial Number from command line"
01481 << endl;
01482 continue;
01483 }
01484 #endif
01485
01486 if (!m_optionedArgs.contains(opt))
01487 {
01488
01489 if (m_namedArgs.contains("_extra"))
01490 {
01491
01492 argdef = m_namedArgs["_extra"];
01493 QByteArray tmp = opt.toLocal8Bit();
01494 tmp += '=';
01495 tmp += val;
01496 val = tmp;
01497 res = kOptVal;
01498 }
01499 else
01500 {
01501
01502 cerr << "Unhandled option given on command line:" << endl
01503 << " " << opt.toLocal8Bit().constData() << endl;
01504 SetValue("showhelp", "");
01505 return false;
01506 }
01507 }
01508 else
01509 argdef = m_optionedArgs[opt];
01510
01511
01512 if (!argdef->m_removed.isEmpty())
01513 {
01514 argdef->PrintRemovedWarning(opt);
01515 SetValue("showhelp", "");
01516 return false;
01517 }
01518
01519
01520 if (!argdef->m_deprecated.isEmpty())
01521 argdef->PrintDeprecatedWarning(opt);
01522
01523 if (m_verbose)
01524 cerr << "name: " << argdef->GetName().toLocal8Bit().constData()
01525 << endl;
01526
01527
01528 if (res == kOptOnly)
01529 {
01530 if (!argdef->Set(opt))
01531 {
01532 SetValue("showhelp", "");
01533 return false;
01534 }
01535 }
01536
01537 else if (res == kOptVal)
01538 {
01539 if (!argdef->Set(opt, val))
01540 {
01541
01542 if (!argdef->Set(opt))
01543 {
01544 SetValue("showhelp", "");
01545 return false;
01546 }
01547
01548
01549 --argpos;
01550 }
01551 }
01552 else
01553 {
01554 SetValue("showhelp", "");
01555 return false;
01556 }
01557
01558 if (m_verbose)
01559 cerr << "value: " << argdef->m_stored.toString().toLocal8Bit().constData()
01560 << endl;
01561 }
01562
01563 QMap<QString, CommandLineArg*>::const_iterator it;
01564
01565 if (m_verbose)
01566 {
01567 cerr << "Processed option list:" << endl;
01568 for (it = m_namedArgs.begin(); it != m_namedArgs.end(); ++it)
01569 (*it)->PrintVerbose();
01570
01571 if (m_namedArgs.contains("_args"))
01572 {
01573 cerr << endl << "Extra argument list:" << endl;
01574 QStringList slist = toStringList("_args");
01575 QStringList::const_iterator it2 = slist.begin();
01576 for (; it2 != slist.end(); ++it2)
01577 cerr << " " << (*it2).toLocal8Bit().constData() << endl;
01578 }
01579
01580 if (m_namedArgs.contains("_passthrough"))
01581 {
01582 cerr << endl << "Passthrough string:" << endl;
01583 cerr << " " << GetPassthrough().toLocal8Bit().constData() << endl;
01584 }
01585
01586 cerr << endl;
01587 }
01588
01589
01590 for (it = m_namedArgs.begin(); it != m_namedArgs.end(); ++it)
01591 {
01592 if (!(*it)->TestLinks())
01593 {
01594 QString keyword = (*it)->m_usedKeyword;
01595 if (keyword.startsWith('-'))
01596 {
01597 if (keyword.startsWith("--"))
01598 keyword.remove(0,2);
01599 else
01600 keyword.remove(0,1);
01601 }
01602
01603 SetValue("showhelp", keyword);
01604 return false;
01605 }
01606 }
01607
01608 return true;
01609 }
01610
01614 bool MythCommandLineParser::ReconcileLinks(void)
01615 {
01616 QList<CommandLineArg*> links;
01617 QMap<QString,CommandLineArg*>::iterator i1;
01618 QList<CommandLineArg*>::iterator i2;
01619
01620 if (m_verbose)
01621 cerr << "Reconciling links for option interdependencies." << endl;
01622
01623 for (i1 = m_namedArgs.begin(); i1 != m_namedArgs.end(); ++i1)
01624 {
01625 links = (*i1)->m_parents;
01626 for (i2 = links.begin(); i2 != links.end(); ++i2)
01627 {
01628 if ((*i2)->m_type != QVariant::Invalid)
01629 continue;
01630
01631 if (!m_namedArgs.contains((*i2)->m_name))
01632 {
01633
01634 cerr << "ERROR: could not reconcile linked argument." << endl
01635 << " '" << (*i1)->m_name.toLocal8Bit().constData()
01636 << "' could not find '"
01637 << (*i2)->m_name.toLocal8Bit().constData() << "'." << endl
01638 << " Please resolve dependency and recompile." << endl;
01639 return false;
01640 }
01641
01642
01643 if (m_verbose)
01644 cerr << QString(" Setting %1 as child of %2")
01645 .arg((*i1)->m_name).arg((*i2)->m_name)
01646 .toLocal8Bit().constData()
01647 << endl;
01648 (*i1)->SetChildOf(m_namedArgs[(*i2)->m_name]);
01649 }
01650
01651 links = (*i1)->m_children;
01652 for (i2 = links.begin(); i2 != links.end(); ++i2)
01653 {
01654 if ((*i2)->m_type != QVariant::Invalid)
01655 continue;
01656
01657 if (!m_namedArgs.contains((*i2)->m_name))
01658 {
01659
01660 cerr << "ERROR: could not reconcile linked argument." << endl
01661 << " '" << (*i1)->m_name.toLocal8Bit().constData()
01662 << "' could not find '"
01663 << (*i2)->m_name.toLocal8Bit().constData() << "'." << endl
01664 << " Please resolve dependency and recompile." << endl;
01665 return false;
01666 }
01667
01668
01669 if (m_verbose)
01670 cerr << QString(" Setting %1 as parent of %2")
01671 .arg((*i1)->m_name).arg((*i2)->m_name)
01672 .toLocal8Bit().constData()
01673 << endl;
01674 (*i1)->SetParentOf(m_namedArgs[(*i2)->m_name]);
01675 }
01676
01677 links = (*i1)->m_requires;
01678 for (i2 = links.begin(); i2 != links.end(); ++i2)
01679 {
01680 if ((*i2)->m_type != QVariant::Invalid)
01681 continue;
01682
01683 if (!m_namedArgs.contains((*i2)->m_name))
01684 {
01685
01686 cerr << "ERROR: could not reconcile linked argument." << endl
01687 << " '" << (*i1)->m_name.toLocal8Bit().constData()
01688 << "' could not find '"
01689 << (*i2)->m_name.toLocal8Bit().constData() << "'." << endl
01690 << " Please resolve dependency and recompile." << endl;
01691 return false;
01692 }
01693
01694
01695 if (m_verbose)
01696 cerr << QString(" Setting %1 as requiring %2")
01697 .arg((*i1)->m_name).arg((*i2)->m_name)
01698 .toLocal8Bit().constData()
01699 << endl;
01700 (*i1)->SetRequires(m_namedArgs[(*i2)->m_name]);
01701 }
01702
01703 i2 = (*i1)->m_requiredby.begin();
01704 while (i2 != (*i1)->m_requiredby.end())
01705 {
01706 if ((*i2)->m_type == QVariant::Invalid)
01707 {
01708
01709 if (m_namedArgs.contains((*i2)->m_name))
01710 {
01711 m_namedArgs[(*i2)->m_name]->SetRequires(*i1);
01712 if (m_verbose)
01713 cerr << QString(" Setting %1 as blocking %2")
01714 .arg((*i1)->m_name).arg((*i2)->m_name)
01715 .toLocal8Bit().constData()
01716 << endl;
01717 }
01718 }
01719
01720 (*i2)->DownRef();
01721 i2 = (*i1)->m_requiredby.erase(i2);
01722 }
01723
01724 i2 = (*i1)->m_blocks.begin();
01725 while (i2 != (*i1)->m_blocks.end())
01726 {
01727 if ((*i2)->m_type != QVariant::Invalid)
01728 {
01729 ++i2;
01730 continue;
01731 }
01732
01733 if (!m_namedArgs.contains((*i2)->m_name))
01734 {
01735 (*i2)->DownRef();
01736 i2 = (*i1)->m_blocks.erase(i2);
01737 continue;
01738 }
01739
01740
01741 if (m_verbose)
01742 cerr << QString(" Setting %1 as blocking %2")
01743 .arg((*i1)->m_name).arg((*i2)->m_name)
01744 .toLocal8Bit().constData()
01745 << endl;
01746 (*i1)->SetBlocks(m_namedArgs[(*i2)->m_name]);
01747 ++i2;
01748 }
01749 }
01750
01751 return true;
01752 }
01753
01757 QVariant MythCommandLineParser::operator[](const QString &name)
01758 {
01759 QVariant var("");
01760 if (!m_namedArgs.contains(name))
01761 return var;
01762
01763 CommandLineArg *arg = m_namedArgs[name];
01764
01765 if (arg->m_given)
01766 var = arg->m_stored;
01767 else
01768 var = arg->m_default;
01769
01770 return var;
01771 }
01772
01776 QStringList MythCommandLineParser::GetArgs(void) const
01777 {
01778 return toStringList("_args");
01779 }
01780
01784 QMap<QString,QString> MythCommandLineParser::GetExtra(void) const
01785 {
01786 return toMap("_extra");
01787 }
01788
01791 QString MythCommandLineParser::GetPassthrough(void) const
01792 {
01793 return toStringList("_passthrough").join(" ");
01794 }
01795
01803 QMap<QString,QString> MythCommandLineParser::GetSettingsOverride(void)
01804 {
01805 QMap<QString,QString> smap = toMap("overridesettings");
01806
01807 if (!m_overridesImported)
01808 {
01809 if (toBool("overridesettingsfile"))
01810 {
01811 QString filename = toString("overridesettingsfile");
01812 if (!filename.isEmpty())
01813 {
01814 QFile f(filename);
01815 if (f.open(QIODevice::ReadOnly))
01816 {
01817 char buf[1024];
01818 int64_t len = f.readLine(buf, sizeof(buf) - 1);
01819 while (len != -1)
01820 {
01821 if (len >= 1 && buf[len-1]=='\n')
01822 buf[len-1] = 0;
01823 QString line(buf);
01824 QStringList tokens = line.split("=",
01825 QString::SkipEmptyParts);
01826 if (tokens.size() == 2)
01827 {
01828 tokens[0].replace(QRegExp("^[\"']"), "");
01829 tokens[0].replace(QRegExp("[\"']$"), "");
01830 tokens[1].replace(QRegExp("^[\"']"), "");
01831 tokens[1].replace(QRegExp("[\"']$"), "");
01832 if (!tokens[0].isEmpty())
01833 smap[tokens[0]] = tokens[1];
01834 }
01835 len = f.readLine(buf, sizeof(buf) - 1);
01836 }
01837 }
01838 else
01839 {
01840 QByteArray tmp = filename.toAscii();
01841 cerr << "Failed to open the override settings file: '"
01842 << tmp.constData() << "'" << endl;
01843 }
01844 }
01845 }
01846
01847 if (toBool("windowed"))
01848 smap["RunFrontendInWindow"] = "1";
01849 else if (toBool("notwindowed"))
01850 smap["RunFrontendInWindow"] = "0";
01851
01852 if (toBool("mousecursor"))
01853 smap["HideMouseCursor"] = "0";
01854 else if (toBool("nomousecursor"))
01855 smap["HideMouseCursor"] = "1";
01856
01857 m_overridesImported = true;
01858
01859 if (!smap.isEmpty())
01860 {
01861 QVariantMap vmap;
01862 QMap<QString, QString>::const_iterator it;
01863 for (it = smap.begin(); it != smap.end(); ++it)
01864 vmap[it.key()] = QVariant(it.value());
01865
01866 m_namedArgs["overridesettings"]->Set(QVariant(vmap));
01867 }
01868 }
01869
01870 if (m_verbose)
01871 {
01872 cerr << "Option Overrides:" << endl;
01873 QMap<QString, QString>::const_iterator it;
01874 for (it = smap.constBegin(); it != smap.constEnd(); ++it)
01875 cerr << QString(" %1 - %2").arg(it.key(), 30).arg(*it)
01876 .toLocal8Bit().constData() << endl;
01877 }
01878
01879 return smap;
01880 }
01881
01888 bool MythCommandLineParser::toBool(QString key) const
01889 {
01890 if (!m_namedArgs.contains(key))
01891 return false;
01892
01893 CommandLineArg *arg = m_namedArgs[key];
01894
01895 if (arg->m_type == QVariant::Bool)
01896 {
01897 if (arg->m_given)
01898 return arg->m_stored.toBool();
01899 return arg->m_default.toBool();
01900 }
01901
01902 if (arg->m_given)
01903 return true;
01904
01905 return false;
01906 }
01907
01911 int MythCommandLineParser::toInt(QString key) const
01912 {
01913 int val = 0;
01914 if (!m_namedArgs.contains(key))
01915 return val;
01916
01917 CommandLineArg *arg = m_namedArgs[key];
01918
01919 if (arg->m_given)
01920 {
01921 if (arg->m_stored.canConvert(QVariant::Int))
01922 val = arg->m_stored.toInt();
01923 }
01924 else
01925 {
01926 if (arg->m_default.canConvert(QVariant::Int))
01927 val = arg->m_default.toInt();
01928 }
01929
01930 return val;
01931 }
01932
01936 uint MythCommandLineParser::toUInt(QString key) const
01937 {
01938 uint val = 0;
01939 if (!m_namedArgs.contains(key))
01940 return val;
01941
01942 CommandLineArg *arg = m_namedArgs[key];
01943
01944 if (arg->m_given)
01945 {
01946 if (arg->m_stored.canConvert(QVariant::UInt))
01947 val = arg->m_stored.toUInt();
01948 }
01949 else
01950 {
01951 if (arg->m_default.canConvert(QVariant::UInt))
01952 val = arg->m_default.toUInt();
01953 }
01954
01955 return val;
01956 }
01957
01961 long long MythCommandLineParser::toLongLong(QString key) const
01962 {
01963 long long val = 0;
01964 if (!m_namedArgs.contains(key))
01965 return val;
01966
01967 CommandLineArg *arg = m_namedArgs[key];
01968
01969 if (arg->m_given)
01970 {
01971 if (arg->m_stored.canConvert(QVariant::LongLong))
01972 val = arg->m_stored.toLongLong();
01973 }
01974 else
01975 {
01976 if (arg->m_default.canConvert(QVariant::LongLong))
01977 val = arg->m_default.toLongLong();
01978 }
01979
01980 return val;
01981 }
01982
01986 double MythCommandLineParser::toDouble(QString key) const
01987 {
01988 double val = 0.0;
01989 if (!m_namedArgs.contains(key))
01990 return val;
01991
01992 CommandLineArg *arg = m_namedArgs[key];
01993
01994 if (arg->m_given)
01995 {
01996 if (arg->m_stored.canConvert(QVariant::Double))
01997 val = arg->m_stored.toDouble();
01998 }
01999 else
02000 {
02001 if (arg->m_default.canConvert(QVariant::Double))
02002 val = arg->m_default.toDouble();
02003 }
02004
02005 return val;
02006 }
02007
02011 QSize MythCommandLineParser::toSize(QString key) const
02012 {
02013 QSize val(0,0);
02014 if (!m_namedArgs.contains(key))
02015 return val;
02016
02017 CommandLineArg *arg = m_namedArgs[key];
02018
02019 if (arg->m_given)
02020 {
02021 if (arg->m_stored.canConvert(QVariant::Size))
02022 val = arg->m_stored.toSize();
02023 }
02024 else
02025 {
02026 if (arg->m_default.canConvert(QVariant::Size))
02027 val = arg->m_default.toSize();
02028 }
02029
02030 return val;
02031 }
02032
02036 QString MythCommandLineParser::toString(QString key) const
02037 {
02038 QString val("");
02039 if (!m_namedArgs.contains(key))
02040 return val;
02041
02042 CommandLineArg *arg = m_namedArgs[key];
02043
02044 if (arg->m_given)
02045 {
02046 if (!arg->m_converted)
02047 arg->Convert();
02048
02049 if (arg->m_stored.canConvert(QVariant::String))
02050 val = arg->m_stored.toString();
02051 }
02052 else
02053 {
02054 if (arg->m_default.canConvert(QVariant::String))
02055 val = arg->m_default.toString();
02056 }
02057
02058 return val;
02059 }
02060
02065 QStringList MythCommandLineParser::toStringList(QString key, QString sep) const
02066 {
02067 QVariant varval;
02068 QStringList val;
02069 if (!m_namedArgs.contains(key))
02070 return val;
02071
02072 CommandLineArg *arg = m_namedArgs[key];
02073
02074 if (arg->m_given)
02075 {
02076 if (!arg->m_converted)
02077 arg->Convert();
02078
02079 varval = arg->m_stored;
02080 }
02081 else
02082 varval = arg->m_default;
02083
02084 if (arg->m_type == QVariant::String && !sep.isEmpty())
02085 val = varval.toString().split(sep);
02086 else if (varval.canConvert(QVariant::StringList))
02087 val = varval.toStringList();
02088
02089 return val;
02090 }
02091
02095 QMap<QString,QString> MythCommandLineParser::toMap(QString key) const
02096 {
02097 QMap<QString, QString> val;
02098 QMap<QString, QVariant> tmp;
02099 if (!m_namedArgs.contains(key))
02100 return val;
02101
02102 CommandLineArg *arg = m_namedArgs[key];
02103
02104 if (arg->m_given)
02105 {
02106 if (!arg->m_converted)
02107 arg->Convert();
02108
02109 if (arg->m_stored.canConvert(QVariant::Map))
02110 tmp = arg->m_stored.toMap();
02111 }
02112 else
02113 {
02114 if (arg->m_default.canConvert(QVariant::Map))
02115 tmp = arg->m_default.toMap();
02116 }
02117
02118 QMap<QString, QVariant>::const_iterator i;
02119 for (i = tmp.begin(); i != tmp.end(); ++i)
02120 val[i.key()] = i.value().toString();
02121
02122 return val;
02123 }
02124
02128 QDateTime MythCommandLineParser::toDateTime(QString key) const
02129 {
02130 QDateTime val;
02131 if (!m_namedArgs.contains(key))
02132 return val;
02133
02134 CommandLineArg *arg = m_namedArgs[key];
02135
02136 if (arg->m_given)
02137 {
02138 if (arg->m_stored.canConvert(QVariant::DateTime))
02139 val = arg->m_stored.toDateTime();
02140 }
02141 else
02142 {
02143 if (arg->m_default.canConvert(QVariant::DateTime))
02144 val = arg->m_default.toDateTime();
02145 }
02146
02147 return val;
02148 }
02149
02153 void MythCommandLineParser::allowArgs(bool allow)
02154 {
02155 if (m_namedArgs.contains("_args"))
02156 {
02157 if (!allow)
02158 m_namedArgs.remove("_args");
02159 }
02160 else if (!allow)
02161 return;
02162
02163 CommandLineArg *arg = new CommandLineArg("_args", QVariant::StringList,
02164 QStringList());
02165 m_namedArgs["_args"] = arg;
02166 }
02167
02171 void MythCommandLineParser::allowExtras(bool allow)
02172 {
02173 if (m_namedArgs.contains("_extra"))
02174 {
02175 if (!allow)
02176 m_namedArgs.remove("_extra");
02177 }
02178 else if (!allow)
02179 return;
02180
02181 QMap<QString,QVariant> vmap;
02182 CommandLineArg *arg = new CommandLineArg("_extra", QVariant::Map, vmap);
02183
02184 m_namedArgs["_extra"] = arg;
02185 }
02186
02190 void MythCommandLineParser::allowPassthrough(bool allow)
02191 {
02192 if (m_namedArgs.contains("_passthrough"))
02193 {
02194 if (!allow)
02195 m_namedArgs.remove("_passthrough");
02196 }
02197 else if (!allow)
02198 return;
02199
02200 CommandLineArg *arg = new CommandLineArg("_passthrough",
02201 QVariant::StringList, QStringList());
02202 m_namedArgs["_passthrough"] = arg;
02203 }
02204
02207 void MythCommandLineParser::addHelp(void)
02208 {
02209 add(QStringList( QStringList() << "-h" << "--help" << "--usage" ),
02210 "showhelp", "", "Display this help printout, or give detailed "
02211 "information of selected option.",
02212 "Displays a list of all commands available for use with "
02213 "this application. If another option is provided as an "
02214 "argument, it will provide detailed information on that "
02215 "option.");
02216 }
02217
02220 void MythCommandLineParser::addVersion(void)
02221 {
02222 add("--version", "showversion", false, "Display version information.",
02223 "Display informtion about build, including:\n"
02224 " version, branch, protocol, library API, Qt "
02225 "and compiled options.");
02226 }
02227
02230 void MythCommandLineParser::addWindowed(void)
02231 {
02232 add(QStringList( QStringList() << "-nw" << "--no-windowed" ),
02233 "notwindowed", false,
02234 "Prevent application from running in a window.", "")
02235 ->SetBlocks("windowed")
02236 ->SetGroup("User Interface");
02237
02238 add(QStringList( QStringList() << "-w" << "--windowed" ), "windowed",
02239 false, "Force application to run in a window.", "")
02240 ->SetGroup("User Interface");
02241 }
02242
02245 void MythCommandLineParser::addMouse(void)
02246 {
02247 add("--mouse-cursor", "mousecursor", false,
02248 "Force visibility of the mouse cursor.", "")
02249 ->SetBlocks("nomousecursor")
02250 ->SetGroup("User Interface");
02251
02252 add("--no-mouse-cursor", "nomousecursor", false,
02253 "Force the mouse cursor to be hidden.", "")
02254 ->SetGroup("User Interface");
02255 }
02256
02259 void MythCommandLineParser::addDaemon(void)
02260 {
02261 add(QStringList( QStringList() << "-d" << "--daemon" ), "daemon", false,
02262 "Fork application into background after startup.",
02263 "Fork application into background, detatching from "
02264 "the local terminal.\nOften used with: "
02265 " --logpath --pidfile --user");
02266 }
02267
02271 void MythCommandLineParser::addSettingsOverride(void)
02272 {
02273 add(QStringList( QStringList() << "-O" << "--override-setting" ),
02274 "overridesettings", QVariant::Map,
02275 "Override a single setting defined by a key=value pair.",
02276 "Override a single setting from the database using "
02277 "options defined as one or more key=value pairs\n"
02278 "Multiple can be defined by multiple uses of the "
02279 "-O option.");
02280 add("--override-settings-file", "overridesettingsfile", "",
02281 "Define a file of key=value pairs to be "
02282 "loaded for setting overrides.", "");
02283 }
02284
02287 void MythCommandLineParser::addRecording(void)
02288 {
02289 add("--chanid", "chanid", 0U,
02290 "Specify chanid of recording to operate on.", "")
02291 ->SetRequires("starttime");
02292
02293 add("--starttime", "starttime", QDateTime(),
02294 "Specify start time of recording to operate on.", "");
02295 }
02296
02299 void MythCommandLineParser::addGeometry(void)
02300 {
02301 add(QStringList( QStringList() << "-geometry" << "--geometry" ), "geometry",
02302 "", "Specify window size and position (WxH[+X+Y])", "")
02303 ->SetGroup("User Interface");
02304 }
02305
02308 void MythCommandLineParser::addDisplay(void)
02309 {
02310 #ifdef USING_X11
02311 add("-display", "display", "", "Specify X server to use.", "")
02312 ->SetGroup("User Interface");
02313 #endif
02314 }
02315
02318 void MythCommandLineParser::addUPnP(void)
02319 {
02320 add("--noupnp", "noupnp", false, "Disable use of UPnP.", "");
02321 }
02322
02327 void MythCommandLineParser::addLogging(
02328 const QString &defaultVerbosity, LogLevel_t defaultLogLevel)
02329 {
02330 defaultLogLevel =
02331 ((defaultLogLevel >= LOG_UNKNOWN) || (defaultLogLevel <= LOG_ANY)) ?
02332 LOG_INFO : defaultLogLevel;
02333
02334 QString logLevelStr = logLevelGetName(defaultLogLevel);
02335
02336 add(QStringList( QStringList() << "-v" << "--verbose" ), "verbose",
02337 defaultVerbosity,
02338 "Specify log filtering. Use '-v help' for level info.", "")
02339 ->SetGroup("Logging");
02340 add("-V", "verboseint", 0U, "",
02341 "This option is intended for internal use only.\n"
02342 "This option takes an unsigned value corresponding "
02343 "to the bitwise log verbosity operator.")
02344 ->SetGroup("Logging");
02345 add("--logpath", "logpath", "",
02346 "Writes logging messages to a file in the directory logpath with "
02347 "filenames in the format: applicationName.date.pid.log.\n"
02348 "This is typically used in combination with --daemon, and if used "
02349 "in combination with --pidfile, this can be used with log "
02350 "rotators, using the HUP call to inform MythTV to reload the "
02351 "file", "")
02352 ->SetGroup("Logging");
02353 add(QStringList( QStringList() << "-q" << "--quiet"), "quiet", 0,
02354 "Don't log to the console (-q). Don't log anywhere (-q -q)", "")
02355 ->SetGroup("Logging");
02356 add("--loglevel", "loglevel", logLevelStr,
02357 QString(
02358 "Set the logging level. All log messages at lower levels will be "
02359 "discarded.\n"
02360 "In descending order: emerg, alert, crit, err, warning, notice, "
02361 "info, debug\ndefaults to ") + logLevelStr, "")
02362 ->SetGroup("Logging");
02363 add("--syslog", "syslog", "none",
02364 "Set the syslog logging facility.\nSet to \"none\" to disable, "
02365 "defaults to none.", "")
02366 ->SetGroup("Logging");
02367 add("--nodblog", "nodblog", false, "Disable database logging.", "")
02368 ->SetGroup("Logging");
02369
02370 add(QStringList( QStringList() << "-l" << "--logfile" ),
02371 "logfile", "", "", "")
02372 ->SetGroup("Logging")
02373 ->SetRemoved("This option has been removed as part of "
02374 "rewrite of the logging interface. Please update your init "
02375 "scripts to use --syslog to interface with your system's "
02376 "existing system logging daemon, or --logpath to specify a "
02377 "dirctory for MythTV to write its logs to.", "0.25");
02378 }
02379
02382 void MythCommandLineParser::addPIDFile(void)
02383 {
02384 add(QStringList( QStringList() << "-p" << "--pidfile" ), "pidfile", "",
02385 "Write PID of application to filename.",
02386 "Write the PID of the currently running process as a single "
02387 "line to this file. Used for init scripts to know what "
02388 "process to terminate, and with log rotators "
02389 "to send a HUP signal to process to have it re-open files.");
02390 }
02391
02394 void MythCommandLineParser::addJob(void)
02395 {
02396 add(QStringList( QStringList() << "-j" << "--jobid" ), "jobid", 0, "",
02397 "Intended for internal use only, specify the JobID to match "
02398 "up with in the database for additional information and the "
02399 "ability to update runtime status in the database.");
02400 }
02401
02404 void MythCommandLineParser::addInFile(bool addOutFile)
02405 {
02406 add("--infile", "infile", "", "Input file URI", "");
02407 if (addOutFile)
02408 add("--outfile", "outfile", "", "Output file URI", "");
02409 }
02410
02413 QString MythCommandLineParser::GetLogFilePath(void)
02414 {
02415 QString logfile = toString("logpath");
02416 pid_t pid = getpid();
02417
02418 if (logfile.isEmpty())
02419 return logfile;
02420
02421 QString logdir;
02422 QString filepath;
02423
02424 QFileInfo finfo(logfile);
02425 if (!finfo.isDir())
02426 {
02427 LOG(VB_GENERAL, LOG_ERR,
02428 QString("%1 is not a directory, disabling logfiles")
02429 .arg(logfile));
02430 return QString();
02431 }
02432
02433 logdir = finfo.filePath();
02434 logfile = QCoreApplication::applicationName() + "." +
02435 QDateTime::currentDateTime().toString("yyyyMMddhhmmss") +
02436 QString(".%1").arg(pid) + ".log";
02437
02438 SetValue("logdir", logdir);
02439 SetValue("logfile", logfile);
02440 SetValue("filepath", QFileInfo(QDir(logdir), logfile).filePath());
02441
02442 return toString("filepath");
02443 }
02444
02447 int MythCommandLineParser::GetSyslogFacility(void)
02448 {
02449 QString setting = toString("syslog").toLower();
02450 if (setting == "none")
02451 return -2;
02452
02453 return syslogGetFacility(setting);
02454 }
02455
02458 LogLevel_t MythCommandLineParser::GetLogLevel(void)
02459 {
02460 QString setting = toString("loglevel");
02461 if (setting.isEmpty())
02462 return LOG_INFO;
02463
02464 LogLevel_t level = logLevelGet(setting);
02465 if (level == LOG_UNKNOWN)
02466 cerr << "Unknown log level: " << setting.toLocal8Bit().constData() <<
02467 endl;
02468
02469 return level;
02470 }
02471
02476 bool MythCommandLineParser::SetValue(const QString &key, QVariant value)
02477 {
02478 CommandLineArg *arg;
02479
02480 if (!m_namedArgs.contains(key))
02481 {
02482 QVariant val(value);
02483 arg = new CommandLineArg(key, val.type(), val);
02484 m_namedArgs.insert(key, arg);
02485 }
02486 else
02487 {
02488 arg = m_namedArgs[key];
02489 if (arg->m_type != value.type())
02490 return false;
02491 }
02492
02493 arg->Set(value);
02494 return true;
02495 }
02496
02499 int MythCommandLineParser::ConfigureLogging(QString mask, unsigned int progress)
02500 {
02501 int err = 0;
02502
02503
02504 verboseString = "";
02505 verboseMask = 0;
02506 verboseArgParse(mask);
02507
02508 if (toBool("verbose"))
02509 {
02510 if ((err = verboseArgParse(toString("verbose"))))
02511 return err;
02512 }
02513 else if (toBool("verboseint"))
02514 verboseMask = toUInt("verboseint");
02515
02516 verboseMask |= VB_STDIO|VB_FLUSH;
02517
02518 int quiet = toUInt("quiet");
02519 if (max(quiet, (int)progress) > 1)
02520 {
02521 verboseMask = VB_NONE|VB_FLUSH;
02522 verboseArgParse("none");
02523 }
02524
02525 int facility = GetSyslogFacility();
02526 bool dblog = !toBool("nodblog");
02527 LogLevel_t level = GetLogLevel();
02528 if (level == LOG_UNKNOWN)
02529 return GENERIC_EXIT_INVALID_CMDLINE;
02530
02531 LOG(VB_GENERAL, LOG_CRIT,
02532 QString("%1 version: %2 [%3] www.mythtv.org")
02533 .arg(QCoreApplication::applicationName())
02534 .arg(MYTH_SOURCE_PATH).arg(MYTH_SOURCE_VERSION));
02535 LOG(VB_GENERAL, LOG_CRIT, QString("Qt version: compile: %1, runtime: %2")
02536 .arg(QT_VERSION_STR).arg(qVersion()));
02537 LOG(VB_GENERAL, LOG_NOTICE,
02538 QString("Enabled verbose msgs: %1").arg(verboseString));
02539
02540 QString logfile = GetLogFilePath();
02541 bool propagate = !logfile.isEmpty();
02542
02543 if (toBool("daemon"))
02544 quiet = max(quiet, 1);
02545
02546 logStart(logfile, progress, quiet, facility, level, dblog, propagate);
02547
02548 return GENERIC_EXIT_OK;
02549 }
02550
02555 void MythCommandLineParser::ApplySettingsOverride(void)
02556 {
02557 if (m_verbose)
02558 cerr << "Applying settings override" << endl;
02559
02560 QMap<QString, QString> override = GetSettingsOverride();
02561 if (override.size())
02562 {
02563 QMap<QString, QString>::iterator it;
02564 for (it = override.begin(); it != override.end(); ++it)
02565 {
02566 LOG(VB_GENERAL, LOG_NOTICE,
02567 QString("Setting '%1' being forced to '%2'")
02568 .arg(it.key()).arg(*it));
02569 gCoreContext->OverrideSettingForSession(it.key(), *it);
02570 }
02571 }
02572 }
02573
02574 bool openPidfile(ofstream &pidfs, const QString &pidfile)
02575 {
02576 if (!pidfile.isEmpty())
02577 {
02578 pidfs.open(pidfile.toAscii().constData());
02579 if (!pidfs)
02580 {
02581 cerr << "Could not open pid file: " << ENO_STR << endl;
02582 return false;
02583 }
02584 }
02585 return true;
02586 }
02587
02590 bool setUser(const QString &username)
02591 {
02592 if (username.isEmpty())
02593 return true;
02594
02595 #ifdef _WIN32
02596 cerr << "--user option is not supported on Windows" << endl;
02597 return false;
02598 #else // ! _WIN32
02599 #if defined(__linux__) || defined(__LINUX__)
02600
02601
02602 int dumpability = prctl(PR_GET_DUMPABLE);
02603 #endif
02604 struct passwd *user_info = getpwnam(username.toLocal8Bit().constData());
02605 const uid_t user_id = geteuid();
02606
02607 if (user_id && (!user_info || user_id != user_info->pw_uid))
02608 {
02609 cerr << "You must be running as root to use the --user switch." << endl;
02610 return false;
02611 }
02612 else if (user_info && user_id == user_info->pw_uid)
02613 {
02614 LOG(VB_GENERAL, LOG_WARNING,
02615 QString("Already running as '%1'").arg(username));
02616 }
02617 else if (!user_id && user_info)
02618 {
02619 if (setenv("HOME", user_info->pw_dir,1) == -1)
02620 {
02621 cerr << "Error setting home directory." << endl;
02622 return false;
02623 }
02624 if (setgid(user_info->pw_gid) == -1)
02625 {
02626 cerr << "Error setting effective group." << endl;
02627 return false;
02628 }
02629 if (initgroups(user_info->pw_name, user_info->pw_gid) == -1)
02630 {
02631 cerr << "Error setting groups." << endl;
02632 return false;
02633 }
02634 if (setuid(user_info->pw_uid) == -1)
02635 {
02636 cerr << "Error setting effective user." << endl;
02637 return false;
02638 }
02639 #if defined(__linux__) || defined(__LINUX__)
02640 if (dumpability && (prctl(PR_SET_DUMPABLE, dumpability) == -1))
02641 LOG(VB_GENERAL, LOG_WARNING, "Unable to re-enable core file "
02642 "creation. Run without the --user argument to use "
02643 "shell-specified limits.");
02644 #endif
02645 }
02646 else
02647 {
02648 cerr << QString("Invalid user '%1' specified with --user")
02649 .arg(username).toLocal8Bit().constData() << endl;
02650 return false;
02651 }
02652 return true;
02653 #endif // ! _WIN32
02654 }
02655
02656
02659 int MythCommandLineParser::Daemonize(void)
02660 {
02661 ofstream pidfs;
02662 if (!openPidfile(pidfs, toString("pidfile")))
02663 return GENERIC_EXIT_PERMISSIONS_ERROR;
02664
02665 if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
02666 LOG(VB_GENERAL, LOG_WARNING, "Unable to ignore SIGPIPE");
02667
02668 if (toBool("daemon") && (daemon(0, 1) < 0))
02669 {
02670 cerr << "Failed to daemonize: " << ENO_STR << endl;
02671 return GENERIC_EXIT_DAEMONIZING_ERROR;
02672 }
02673
02674 QString username = toString("username");
02675 if (!username.isEmpty() && !setUser(username))
02676 return GENERIC_EXIT_PERMISSIONS_ERROR;
02677
02678 if (pidfs)
02679 {
02680 pidfs << getpid() << endl;
02681 pidfs.close();
02682 }
02683
02684 return GENERIC_EXIT_OK;
02685 }