MythTV  0.26-pre
mythcommandlineparser.cpp
Go to the documentation of this file.
00001 /* -*- Mode: c++ -*-
00002 *
00003 * Class CommandLineArg
00004 * Class MythCommandLineParser
00005 *
00006 * Copyright (C) Raymond Wagner 2011
00007 *
00008 * This program is free software; you can redistribute it and/or modify
00009 * it under the terms of the GNU General Public License as published by
00010 * the Free Software Foundation; either version 2 of the License, or
00011 * (at your option) any later version.
00012 *
00013 * This program is distributed in the hope that it will be useful,
00014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00016 * GNU General Public License for more details.
00017 *
00018 * You should have received a copy of the GNU General Public License
00019 * along with this program; if not, write to the Free Software
00020 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
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     // this may cause problems if the terminal is too narrow, or if too
00191     // many keywords for the same argument are used
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         // only print if there is a short help to print
00232         return helpstr;
00233 
00234     if ((m_group != group) && !force)
00235         // only print if looping over the correct group
00236         return helpstr;
00237 
00238     if (!m_parents.isEmpty() && !force)
00239         // only print if an independent option, not subject
00240         // to a parent option
00241         return helpstr;
00242 
00243     if (!m_deprecated.isEmpty())
00244         // option is marked as deprecated, do not show
00245         return helpstr;
00246 
00247     if (!m_removed.isEmpty())
00248         // option is marked as removed, do not show
00249         return helpstr;
00250 
00251     QString pad;
00252     pad.fill(' ', off);
00253 
00254     // print the first line with the available keywords
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     // print remaining lines with necessary padding
00263     QStringList::const_iterator i1;
00264     for (i1 = hlist.begin() + 1; i1 != hlist.end(); ++i1)
00265         msg << pad << *i1 << endl;
00266 
00267     // loop through any child arguments to print underneath
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     // help called for an argument that is not me, this should not happen
00290     if (!m_keywords.contains(keyword))
00291         return helpstr;
00292 
00293     // argument has been marked as removed, so warn user of such
00294     if (!m_removed.isEmpty())
00295         PrintRemovedWarning(keyword);
00296     // argument has been marked as deprecated, so warn user of such
00297     else if (!m_deprecated.isEmpty())
00298         PrintDeprecatedWarning(keyword);
00299 
00300     msg << "Option:      " << keyword << endl << endl;
00301 
00302     bool first = true;
00303 
00304     // print all related keywords, padding for multiples
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     // print type and default for the stored value
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     // print description, wrapping and padding as necessary
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     // loop through the four relation types and print
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 //  requirements need not be reciprocal
00757 //    if (forward)
00758 //        other->SetRequires(this, false);
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     // TODO: blocks do not get set properly if multiple dummy arguments
00795     //       are provided. since this method will not have access to the
00796     //       argument list, this issue will have to be resolved later in
00797     //       ReconcileLinks().
00798     QList<CommandLineArg*>::const_iterator i1,i2;
00799 
00800     // loop through all but the last entry
00801     for (i1 = args.begin(); i1 != args.end()-1; ++i1)
00802     {
00803         // loop through the next to the last entry
00804         // and block use with the current
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         // QApplication not available, no sense doing anything yet
00825         return;
00826 
00827     if (m_converted)
00828         // already run, abort
00829         return;
00830 
00831     if (!m_given)
00832     {
00833         // nothing to work on, abort
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         // else
00845         //      not sure why this isnt a bytearray, but ignore it and
00846         //      set it as converted
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; // not in use, no need for checks
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         // one of these must have been defined
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     // we dont care about children
00930 
00931     for (i = m_requires.constBegin(); i != m_requires.constEnd(); ++i)
00932     {
00933         // all of these must have been defined
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         // none of these can be defined
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     // clear out interdependent pointers in preparation for deletion
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         // build generic help text
01258 
01259         QString descr = GetHelpHeader();
01260         if (descr.size() > 0)
01261             msg << endl << descr << endl << endl;
01262 
01263         // loop through registered arguments to populate list of groups
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         // loop through list of groups and print help string for each
01275         // arguments will filter themselves if they are not in the group
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         // build help for a specific argument
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         // this shouldnt happen, return and exit
01319         return kEnd;
01320 
01321     QByteArray tmp(argv[argpos]);
01322     if (tmp.isEmpty())
01323         // string is empty, return and loop
01324         return kEmpty;
01325 
01326     if (m_passthroughActive)
01327     {
01328         // pass through has been activated
01329         val = tmp;
01330         return kArg;
01331     }
01332 
01333     if (tmp.startsWith('-') && tmp.size() > 1)
01334     {
01335         if (tmp == "--")
01336         {
01337             // all options beyond this will be passed as a single string
01338             m_passthroughActive = true;
01339             return kPassthrough;
01340         }
01341 
01342         if (tmp.contains('='))
01343         {
01344             // option contains '=', split
01345             QList<QByteArray> blist = tmp.split('=');
01346 
01347             if (blist.size() != 2)
01348             {
01349                 // more than one '=' in option, this is not handled
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             // end of input, option only
01363             return kOptOnly;
01364 
01365         tmp = QByteArray(argv[++argpos]);
01366         if (tmp.isEmpty())
01367             // empty string, option only
01368             return kOptOnly;
01369 
01370         if (tmp.startsWith("-") && tmp.size() > 1)
01371         {
01372             // no value found for option, backtrack
01373             argpos--;
01374             return kOptOnly;
01375         }
01376 
01377         val = tmp;
01378         return kOptVal;
01379     }
01380     else
01381     {
01382         // input is not an option string, return as arg
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     // reconnect interdependencies between command line options
01403     if (!ReconcileLinks())
01404         return false;
01405 
01406     // loop through command line arguments until all are spent
01407     for (int argpos = 1; argpos < argc; ++argpos)
01408     {
01409 
01410         // pull next option
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         // '--' found on command line, enable passthrough mode
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         // end of options found, terminate loop
01427         if (res == kEnd)
01428             break;
01429 
01430         // GetOpt pulled an empty option, this shouldnt happen by ignore
01431         // it and continue
01432         else if (res == kEmpty)
01433             continue;
01434 
01435         // more than one equal found in key/value pair, fault out
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         // passthrough is active, so add the data to the stringlist
01445         else if (m_passthroughActive)
01446         {
01447             m_namedArgs["_passthrough"]->Set("", val);
01448             continue;
01449         }
01450 
01451         // argument with no preceeding '-' encountered, add to stringlist
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         // this line should not be passed once arguments have started collecting
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             // argument is unhandled, check if parser allows arbitrary input
01489             if (m_namedArgs.contains("_extra"))
01490             {
01491                 // arbitrary allowed, specify general collection pool
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                 // arbitrary not allowed, fault out
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         // argument has been marked as removed, warn user and fail
01512         if (!argdef->m_removed.isEmpty())
01513         {
01514             argdef->PrintRemovedWarning(opt);
01515             SetValue("showhelp", "");
01516             return false;
01517         }
01518 
01519         // argument has been marked as deprecated, warn user
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         // argument is keyword only, no value
01528         if (res == kOptOnly)
01529         {
01530             if (!argdef->Set(opt))
01531             {
01532                 SetValue("showhelp", "");
01533                 return false;
01534             }
01535         }
01536         // argument has keyword and value
01537         else if (res == kOptVal)
01538         {
01539             if (!argdef->Set(opt, val))
01540             {
01541                 // try testing keyword with no value
01542                 if (!argdef->Set(opt))
01543                 {
01544                     SetValue("showhelp", "");
01545                     return false;
01546                 }
01547                 // drop back an iteration so the unused value will get
01548                 // processed a second time as a keyword-less argument
01549                 --argpos;
01550             }
01551         }
01552         else
01553         {
01554             SetValue("showhelp", "");
01555             return false; // this should not occur
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     // make sure all interdependencies are fulfilled
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; // already handled
01630 
01631             if (!m_namedArgs.contains((*i2)->m_name))
01632             {
01633                 // not found
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             // replace linked argument
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; // already handled
01656 
01657             if (!m_namedArgs.contains((*i2)->m_name))
01658             {
01659                 // not found
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             // replace linked argument
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; // already handled
01682 
01683             if (!m_namedArgs.contains((*i2)->m_name))
01684             {
01685                 // not found
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             // replace linked argument
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                 // if its not an invalid, it shouldnt be here anyway
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; // already handled
01731             }
01732 
01733             if (!m_namedArgs.contains((*i2)->m_name))
01734             {
01735                 (*i2)->DownRef();
01736                 i2 = (*i1)->m_blocks.erase(i2);
01737                 continue; // if it doesnt exist, it cant block this command
01738             }
01739 
01740             // replace linked argument
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     // Setup the defaults
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     // Check the current dumpability of core dumps, which will be disabled
02601     // by setuid, so we can re-enable, if appropriate
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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends