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