MythTV  master
mythcommandlineparser.cpp
Go to the documentation of this file.
1 /* -*- Mode: c++ -*-
2 *
3 * Class CommandLineArg
4 * Class MythCommandLineParser
5 *
6 * Copyright (C) Raymond Wagner 2011
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
21 */
22 
23 // C++ headers
24 #include <algorithm>
25 #include <csignal>
26 #include <cstdio>
27 #include <cstdlib>
28 #include <fstream>
29 #include <iostream>
30 #include <unistd.h>
31 using namespace std;
32 
33 // System headers
34 #include <sys/types.h>
35 #ifndef _WIN32
36 #include <sys/ioctl.h>
37 #include <pwd.h>
38 #include <grp.h>
39 #if defined(__linux__) || defined(__LINUX__)
40 #include <sys/prctl.h>
41 #endif
42 #endif
43 
44 // Qt headers
45 #include <QCoreApplication>
46 #include <QDateTime>
47 #include <QDir>
48 #include <QFile>
49 #include <QFileInfo>
50 #include <QSize>
51 #include <QString>
52 #include <QTextStream>
53 #include <QVariant>
54 #include <QVariantList>
55 #include <QVariantMap>
56 #include <utility>
57 
58 #include "mythcommandlineparser.h"
59 #include "mythcorecontext.h"
60 #include "exitcodes.h"
61 #include "mythconfig.h"
62 #include "mythlogging.h"
63 #include "mythversion.h"
64 #include "logging.h"
65 #include "mythmiscutil.h"
66 #include "mythdate.h"
67 
68 #define TERMWIDTH 79
69 
70 const int kEnd = 0,
71  kEmpty = 1,
72  kOptOnly = 2,
73  kOptVal = 3,
75  kArg = 5,
77  kInvalid = 7;
78 
79 const char* NamedOptType(int type);
80 bool openPidfile(ofstream &pidfs, const QString &pidfile);
81 bool setUser(const QString &username);
82 int GetTermWidth(void);
83 
87 int GetTermWidth(void)
88 {
89 #if defined(_WIN32) || defined(Q_OS_ANDROID)
90  return TERMWIDTH;
91 #else
92  struct winsize ws {};
93 
94  if (ioctl(0, TIOCGWINSZ, &ws) != 0)
95  return TERMWIDTH;
96 
97  return (int)ws.ws_col;
98 #endif
99 }
100 
104 const char* NamedOptType(int type)
105 {
106  switch (type)
107  {
108  case kEnd:
109  return "kEnd";
110 
111  case kEmpty:
112  return "kEmpty";
113 
114  case kOptOnly:
115  return "kOptOnly";
116 
117  case kOptVal:
118  return "kOptVal";
119 
120  case kCombOptVal:
121  return "kCombOptVal";
122 
123  case kArg:
124  return "kArg";
125 
126  case kPassthrough:
127  return "kPassthrough";
128 
129  case kInvalid:
130  return "kInvalid";
131 
132  default:
133  return "kUnknown";
134  }
135 }
136 
171 CommandLineArg::CommandLineArg(const QString& name, QVariant::Type type,
172  QVariant def, QString help, QString longhelp) :
173  ReferenceCounter(QString("CommandLineArg:%1").arg(name)),
174  m_name(name), m_type(type), m_default(std::move(def)),
175  m_help(std::move(help)), m_longhelp(std::move(longhelp))
176 {
177  if ((m_type != QVariant::String) && (m_type != QVariant::StringList) &&
178  (m_type != QVariant::Map))
179  m_converted = true;
180 }
181 
188 CommandLineArg::CommandLineArg(const QString& name, QVariant::Type type, QVariant def)
189  : ReferenceCounter(QString("CommandLineArg:%1").arg(name)),
190  m_name(name), m_type(type), m_default(std::move(def))
191 {
192  if ((m_type != QVariant::String) && (m_type != QVariant::StringList) &&
193  (m_type != QVariant::Map))
194  m_converted = true;
195 }
196 
204 CommandLineArg::CommandLineArg(const QString& name) :
205  ReferenceCounter(QString("CommandLineArg:%1").arg(name)),
206  m_name(name)
207 {
208 }
209 
214 {
215  // this may cause problems if the terminal is too narrow, or if too
216  // many keywords for the same argument are used
217  return m_keywords.join(" OR ");
218 }
219 
224 {
225  int len = GetKeywordString().length();
226 
227  QList<CommandLineArg*>::const_iterator i1;
228  for (i1 = m_parents.begin(); i1 != m_parents.end(); ++i1)
229  len = max(len, (*i1)->GetKeywordLength()+2);
230 
231  return len;
232 }
233 
249 QString CommandLineArg::GetHelpString(int off, const QString& group, bool force) const
250 {
251  QString helpstr;
252  QTextStream msg(&helpstr, QIODevice::WriteOnly);
253  int termwidth = GetTermWidth();
254  if (termwidth < off)
255  {
256  if (off > 70)
257  {
258  // developer has configured some absurdly long command line
259  // arguments, but we still need to do something
260  termwidth = off+40;
261  }
262  else
263  {
264  // user is running uselessly narrow console, use a sane console
265  // width instead
266  termwidth = 79;
267  }
268  }
269 
270  if (m_help.isEmpty() && !force)
271  // only print if there is a short help to print
272  return helpstr;
273 
274  if ((m_group != group) && !force)
275  // only print if looping over the correct group
276  return helpstr;
277 
278  if (!m_parents.isEmpty() && !force)
279  {
280  // only print if an independent option, not subject
281  // to a parent option
282  return helpstr;
283  }
284 
285  if (!m_deprecated.isEmpty())
286  // option is marked as deprecated, do not show
287  return helpstr;
288 
289  if (!m_removed.isEmpty())
290  // option is marked as removed, do not show
291  return helpstr;
292 
293  QString pad;
294  pad.fill(' ', off);
295 
296  // print the first line with the available keywords
297  QStringList hlist = m_help.split('\n');
298  wrapList(hlist, termwidth-off);
299  if (!m_parents.isEmpty())
300  msg << " ";
301  msg << GetKeywordString().leftJustified(off, ' ')
302  << hlist[0] << endl;
303 
304  // print remaining lines with necessary padding
305  QStringList::const_iterator i1;
306  for (i1 = hlist.begin() + 1; i1 != hlist.end(); ++i1)
307  msg << pad << *i1 << endl;
308 
309  // loop through any child arguments to print underneath
310  QList<CommandLineArg*>::const_iterator i2;
311  for (i2 = m_children.begin(); i2 != m_children.end(); ++i2)
312  msg << (*i2)->GetHelpString(off, group, true);
313 
314  msg.flush();
315  return helpstr;
316 }
317 
325 QString CommandLineArg::GetLongHelpString(QString keyword) const
326 {
327  QString helpstr;
328  QTextStream msg(&helpstr, QIODevice::WriteOnly);
329  int termwidth = GetTermWidth();
330 
331  // help called for an argument that is not me, this should not happen
332  if (!m_keywords.contains(keyword))
333  return helpstr;
334 
335  // argument has been marked as removed, so warn user of such
336  if (!m_removed.isEmpty())
337  {
338  PrintRemovedWarning(keyword);
339  // argument has been marked as deprecated, so warn user of such
340  }
341  else if (!m_deprecated.isEmpty())
342  {
343  PrintDeprecatedWarning(keyword);
344  }
345 
346  msg << "Option: " << keyword << endl << endl;
347 
348  bool first = true;
349 
350  // print all related keywords, padding for multiples
351  QStringList::const_iterator i1;
352  for (i1 = m_keywords.begin(); i1 != m_keywords.end(); ++i1)
353  {
354  if (*i1 != keyword)
355  {
356  if (first)
357  {
358  msg << "Aliases: " << *i1 << endl;
359  first = false;
360  }
361  else
362  msg << " " << *i1 << endl;
363  }
364  }
365 
366  // print type and default for the stored value
367  msg << "Type: " << QVariant::typeToName(m_type) << endl;
368  if (m_default.canConvert(QVariant::String))
369  msg << "Default: " << m_default.toString() << endl;
370 
371  QStringList help;
372  if (m_longhelp.isEmpty())
373  help = m_help.split("\n");
374  else
375  help = m_longhelp.split("\n");
376  wrapList(help, termwidth-13);
377 
378  // print description, wrapping and padding as necessary
379  msg << "Description: " << help[0] << endl;
380  for (i1 = help.begin() + 1; i1 != help.end(); ++i1)
381  msg << " " << *i1 << endl;
382 
383  QList<CommandLineArg*>::const_iterator i2;
384 
385  // loop through the four relation types and print
386  if (!m_parents.isEmpty())
387  {
388  msg << endl << "Can be used in combination with:" << endl;
389  for (i2 = m_parents.constBegin(); i2 != m_parents.constEnd(); ++i2)
390  msg << " " << (*i2)->GetPreferredKeyword()
391  .toLocal8Bit().constData();
392  msg << endl;
393  }
394 
395  if (!m_children.isEmpty())
396  {
397  msg << endl << "Allows the use of:" << endl;
398  for (i2 = m_children.constBegin(); i2 != m_children.constEnd(); ++i2)
399  msg << " " << (*i2)->GetPreferredKeyword()
400  .toLocal8Bit().constData();
401  msg << endl;
402  }
403 
404  if (!m_requires.isEmpty())
405  {
406  msg << endl << "Requires the use of:" << endl;
407  for (i2 = m_requires.constBegin(); i2 != m_requires.constEnd(); ++i2)
408  msg << " " << (*i2)->GetPreferredKeyword()
409  .toLocal8Bit().constData();
410  msg << endl;
411  }
412 
413  if (!m_blocks.isEmpty())
414  {
415  msg << endl << "Prevents the use of:" << endl;
416  for (i2 = m_blocks.constBegin(); i2 != m_blocks.constEnd(); ++i2)
417  msg << " " << (*i2)->GetPreferredKeyword()
418  .toLocal8Bit().constData();
419  msg << endl;
420  }
421 
422  msg.flush();
423  return helpstr;
424 }
425 
432 bool CommandLineArg::Set(const QString& opt)
433 {
434  m_usedKeyword = opt;
435 
436  switch (m_type)
437  {
438  case QVariant::Bool:
439  m_stored = QVariant(!m_default.toBool());
440  break;
441 
442  case QVariant::Int:
443  if (m_stored.isNull())
444  m_stored = QVariant(1);
445  else
446  m_stored = QVariant(m_stored.toInt() + 1);
447  break;
448 
449  case QVariant::String:
451  break;
452 
453  default:
454  cerr << "Command line option did not receive value:" << endl
455  << " " << opt.toLocal8Bit().constData() << endl;
456  return false;
457  }
458 
459  m_given = true;
460  return true;
461 }
462 
465 bool CommandLineArg::Set(const QString& opt, const QByteArray& val)
466 {
467  QVariantList vlist;
468  QList<QByteArray> blist;
469  QVariantMap vmap;
470  m_usedKeyword = opt;
471 
472  switch (m_type)
473  {
474  case QVariant::Bool:
475  cerr << "Boolean type options do not accept values:" << endl
476  << " " << opt.toLocal8Bit().constData() << endl;
477  return false;
478 
479  case QVariant::String:
480  m_stored = QVariant(val);
481  break;
482 
483  case QVariant::Int:
484  m_stored = QVariant(val.toInt());
485  break;
486 
487  case QVariant::UInt:
488  m_stored = QVariant(val.toUInt());
489  break;
490 
491  case QVariant::LongLong:
492  m_stored = QVariant(val.toLongLong());
493  break;
494 
495  case QVariant::Double:
496  m_stored = QVariant(val.toDouble());
497  break;
498 
499  case QVariant::DateTime:
500  m_stored = QVariant(MythDate::fromString(QString(val)));
501  break;
502 
503  case QVariant::StringList:
504  if (!m_stored.isNull())
505  vlist = m_stored.toList();
506  vlist << val;
507  m_stored = QVariant(vlist);
508  break;
509 
510  case QVariant::Map:
511  if (!val.contains('='))
512  {
513  cerr << "Command line option did not get expected "
514  << "key/value pair" << endl;
515  return false;
516  }
517 
518  blist = val.split('=');
519 
520  if (!m_stored.isNull())
521  vmap = m_stored.toMap();
522  vmap[QString(blist[0])] = QVariant(blist[1]);
523  m_stored = QVariant(vmap);
524  break;
525 
526  case QVariant::Size:
527  if (!val.contains('x'))
528  {
529  cerr << "Command line option did not get expected "
530  << "XxY pair" << endl;
531  return false;
532  }
533 
534  blist = val.split('x');
535  m_stored = QVariant(QSize(blist[0].toInt(), blist[1].toInt()));
536  break;
537 
538  default:
539  m_stored = QVariant(val);
540  }
541 
542  m_given = true;
543  return true;
544 }
545 
549 {
550  m_children << new CommandLineArg(opt);
551  return this;
552 }
553 
557 {
558  foreach (const auto opt, opts)
559  m_children << new CommandLineArg(opt);
560  return this;
561 }
562 
566 {
567  m_parents << new CommandLineArg(opt);
568  return this;
569 }
570 
574 {
575  foreach (const auto opt, opts)
576  m_parents << new CommandLineArg(opt);
577  return this;
578 }
579 
583 {
584  m_parents << new CommandLineArg(opt);
585  return this;
586 }
587 
591 {
592  foreach (const auto opt, opts)
593  m_parents << new CommandLineArg(opt);
594  return this;
595 }
596 
600 {
601  m_children << new CommandLineArg(opt);
602  return this;
603 }
604 
608 {
609  foreach (const auto opt, opts)
610  m_children << new CommandLineArg(opt);
611  return this;
612 }
613 
617 {
618  m_children << new CommandLineArg(opt);
619  m_requires << new CommandLineArg(opt);
620  return this;
621 }
622 
626 {
627  foreach (const auto opt, opts)
628  {
629  m_children << new CommandLineArg(opt);
630  m_requires << new CommandLineArg(opt);
631  }
632  return this;
633 }
634 
638 {
639  m_parents << new CommandLineArg(opt);
640  m_requiredby << new CommandLineArg(opt);
641  return this;
642 }
643 
647 {
648  foreach (const auto opt, opts)
649  {
650  m_parents << new CommandLineArg(opt);
651  m_requiredby << new CommandLineArg(opt);
652  }
653  return this;
654 }
655 
659 {
660  m_requires << new CommandLineArg(opt);
661  return this;
662 }
663 
667 {
668  foreach (const auto opt, opts)
669  m_requires << new CommandLineArg(opt);
670  return this;
671 }
672 
676 {
677  m_blocks << new CommandLineArg(opt);
678  return this;
679 }
680 
684 {
685  foreach (const auto opt, opts)
686  m_blocks << new CommandLineArg(opt);
687  return this;
688 }
689 
693 {
694  if (depstr.isEmpty())
695  depstr = "and will be removed in a future version.";
696  m_deprecated = depstr;
697  return this;
698 }
699 
702 CommandLineArg* CommandLineArg::SetRemoved(QString remstr, QString remver)
703 {
704  if (remstr.isEmpty())
705  remstr = "and is no longer available in this version.";
706  m_removed = remstr;
707  m_removedversion = std::move(remver);
708  return this;
709 }
710 
716 void CommandLineArg::SetParentOf(CommandLineArg *other, bool forward)
717 {
718  bool replaced = false;
719  other->IncrRef();
720 
721  for (int i = 0; i < m_children.size(); i++)
722  {
723  if (m_children[i]->m_name == other->m_name)
724  {
725  m_children[i]->DecrRef();
726  m_children.replace(i, other);
727  replaced = true;
728  break;
729  }
730  }
731 
732  if (!replaced)
733  m_children << other;
734 
735  if (forward)
736  other->SetChildOf(this, false);
737 }
738 
744 void CommandLineArg::SetChildOf(CommandLineArg *other, bool forward)
745 {
746  bool replaced = false;
747  other->IncrRef();
748 
749  for (int i = 0; i < m_parents.size(); i++)
750  {
751  if (m_parents[i]->m_name == other->m_name)
752  {
753  m_parents[i]->DecrRef();
754  m_parents.replace(i, other);
755  replaced = true;
756  break;
757  }
758  }
759 
760  if (!replaced)
761  m_parents << other;
762 
763  if (forward)
764  other->SetParentOf(this, false);
765 }
766 
772 void CommandLineArg::SetRequires(CommandLineArg *other, bool /*forward*/)
773 {
774  bool replaced = false;
775  other->IncrRef();
776 
777  for (int i = 0; i < m_requires.size(); i++)
778  {
779  if (m_requires[i]->m_name == other->m_name)
780  {
781  m_requires[i]->DecrRef();
782  m_requires.replace(i, other);
783  replaced = true;
784  break;
785  }
786  }
787 
788  if (!replaced)
789  m_requires << other;
790 
791 // requirements need not be reciprocal
792 // if (forward)
793 // other->SetRequires(this, false);
794 }
795 
801 void CommandLineArg::SetBlocks(CommandLineArg *other, bool forward)
802 {
803  bool replaced = false;
804  other->IncrRef();
805 
806  for (int i = 0; i < m_blocks.size(); i++)
807  {
808  if (m_blocks[i]->m_name == other->m_name)
809  {
810  m_blocks[i]->DecrRef();
811  m_blocks.replace(i, other);
812  replaced = true;
813  break;
814  }
815  }
816 
817  if (!replaced)
818  m_blocks << other;
819 
820  if (forward)
821  other->SetBlocks(this, false);
822 }
823 
826 void CommandLineArg::AllowOneOf(const QList<CommandLineArg*>& args)
827 {
828  // TODO: blocks do not get set properly if multiple dummy arguments
829  // are provided. since this method will not have access to the
830  // argument list, this issue will have to be resolved later in
831  // ReconcileLinks().
832 
833  // loop through all but the last entry
834  for (auto i1 = args.cbegin(); i1 != args.cend()-1; ++i1)
835  {
836  // loop through the next to the last entry
837  // and block use with the current
838  for (auto i2 = i1+1; i2 != args.cend(); ++i2)
839  {
840  (*i1)->SetBlocks(*i2);
841  }
842 
843  if ((*i1)->m_type == QVariant::Invalid)
844  (*i1)->DecrRef();
845  }
846 }
847 
855 {
856  if (!QCoreApplication::instance())
857  // QApplication not available, no sense doing anything yet
858  return;
859 
860  if (m_converted)
861  // already run, abort
862  return;
863 
864  if (!m_given)
865  {
866  // nothing to work on, abort
867  m_converted = true;
868  return;
869  }
870 
871  if (m_type == QVariant::String)
872  {
873  if (m_stored.type() == QVariant::ByteArray)
874  {
875  m_stored = QString::fromLocal8Bit(m_stored.toByteArray());
876  }
877  // else
878  // not sure why this isnt a bytearray, but ignore it and
879  // set it as converted
880  }
881  else if (m_type == QVariant::StringList)
882  {
883  if (m_stored.type() == QVariant::List)
884  {
885  QVariantList vlist = m_stored.toList();
886  QVariantList::const_iterator iter = vlist.begin();
887  QStringList slist;
888  for (; iter != vlist.end(); ++iter)
889  slist << QString::fromLocal8Bit(iter->toByteArray());
890  m_stored = QVariant(slist);
891  }
892  }
893  else if (m_type == QVariant::Map)
894  {
895  QVariantMap vmap = m_stored.toMap();
896  // NOLINTNEXTLINE(modernize-loop-convert)
897  for (auto iter = vmap.begin(); iter != vmap.end(); ++iter)
898  (*iter) = QString::fromLocal8Bit(iter->toByteArray());
899  }
900  else
901  return;
902 
903  m_converted = true;
904 }
905 
906 
913 {
914  QStringList::const_iterator it;
915  QString preferred;
916  int len = 0;
917 
918  for (it = m_keywords.constBegin(); it != m_keywords.constEnd(); ++it)
919  {
920  int len2 = (*it).size();
921  if (len2 > len)
922  {
923  preferred = *it;
924  len = len2;
925  }
926  }
927 
928  return preferred;
929 }
930 
935 {
936  if (!m_given)
937  return true; // not in use, no need for checks
938 
939  QList<CommandLineArg*>::const_iterator i;
940 
941  bool passes = false;
942  for (i = m_parents.constBegin(); i != m_parents.constEnd(); ++i)
943  {
944  // one of these must have been defined
945  if ((*i)->m_given)
946  {
947  passes = true;
948  break;
949  }
950  }
951  if (!passes && !m_parents.isEmpty())
952  {
953  cerr << "ERROR: " << m_usedKeyword.toLocal8Bit().constData()
954  << " requires at least one of the following arguments" << endl;
955  for (i = m_parents.constBegin(); i != m_parents.constEnd(); ++i)
956  cerr << " "
957  << (*i)->GetPreferredKeyword().toLocal8Bit().constData();
958  cerr << endl << endl;
959  return false;
960  }
961 
962  // we dont care about children
963 
964  for (i = m_requires.constBegin(); i != m_requires.constEnd(); ++i)
965  {
966  // all of these must have been defined
967  if (!(*i)->m_given)
968  {
969  cerr << "ERROR: " << m_usedKeyword.toLocal8Bit().constData()
970  << " requires all of the following be defined as well"
971  << endl;
972  for (i = m_requires.constBegin(); i != m_requires.constEnd(); ++i)
973  {
974  cerr << " "
975  << (*i)->GetPreferredKeyword().toLocal8Bit()
976  .constData();
977  }
978  cerr << endl << endl;
979  return false;
980  }
981  }
982 
983  for (i = m_blocks.constBegin(); i != m_blocks.constEnd(); ++i)
984  {
985  // none of these can be defined
986  if ((*i)->m_given)
987  {
988  cerr << "ERROR: " << m_usedKeyword.toLocal8Bit().constData()
989  << " requires that none of the following be defined" << endl;
990  for (i = m_blocks.constBegin(); i != m_blocks.constEnd(); ++i)
991  {
992  cerr << " "
993  << (*i)->GetPreferredKeyword().toLocal8Bit()
994  .constData();
995  }
996  cerr << endl << endl;
997  return false;
998  }
999  }
1000 
1001  return true;
1002 }
1003 
1007 {
1008  // clear out interdependent pointers in preparation for deletion
1009  while (!m_parents.isEmpty())
1010  m_parents.takeFirst()->DecrRef();
1011 
1012  while (!m_children.isEmpty())
1013  m_children.takeFirst()->DecrRef();
1014 
1015  while (!m_blocks.isEmpty())
1016  m_blocks.takeFirst()->DecrRef();
1017 
1018  while (!m_requires.isEmpty())
1019  m_requires.takeFirst()->DecrRef();
1020 
1021  while (!m_requiredby.isEmpty())
1022  m_requiredby.takeFirst()->DecrRef();
1023 }
1024 
1028 {
1029  if (!m_given)
1030  return;
1031 
1032  cerr << " " << m_name.leftJustified(30).toLocal8Bit().constData();
1033 
1034  QSize tmpsize;
1035  QMap<QString, QVariant> tmpmap;
1036  QMap<QString, QVariant>::const_iterator it;
1037  QVariantList vlist;
1038  QVariantList::const_iterator it2;
1039  bool first = true;
1040 
1041  switch (m_type)
1042  {
1043  case QVariant::Bool:
1044  cerr << (m_stored.toBool() ? "True" : "False") << endl;
1045  break;
1046 
1047  case QVariant::Int:
1048  cerr << m_stored.toInt() << endl;
1049  break;
1050 
1051  case QVariant::UInt:
1052  cerr << m_stored.toUInt() << endl;
1053  break;
1054 
1055  case QVariant::LongLong:
1056  cerr << m_stored.toLongLong() << endl;
1057  break;
1058 
1059  case QVariant::Double:
1060  cerr << m_stored.toDouble() << endl;
1061  break;
1062 
1063  case QVariant::Size:
1064  tmpsize = m_stored.toSize();
1065  cerr << "x=" << tmpsize.width()
1066  << " y=" << tmpsize.height()
1067  << endl;
1068  break;
1069 
1070  case QVariant::String:
1071  cerr << '"' << m_stored.toByteArray().constData()
1072  << '"' << endl;
1073  break;
1074 
1075  case QVariant::StringList:
1076  vlist = m_stored.toList();
1077  it2 = vlist.begin();
1078  cerr << '"' << it2->toByteArray().constData() << '"';
1079  ++it2;
1080  for (; it2 != vlist.end(); ++it2)
1081  {
1082  cerr << ", \""
1083  << it2->constData()
1084  << '"';
1085  }
1086  cerr << endl;
1087  break;
1088 
1089  case QVariant::Map:
1090  tmpmap = m_stored.toMap();
1091  for (it = tmpmap.begin(); it != tmpmap.end(); ++it)
1092  {
1093  if (first)
1094  first = false;
1095  else
1096  cerr << QString("").leftJustified(32)
1097  .toLocal8Bit().constData();
1098 
1099  cerr << it.key().toLocal8Bit().constData()
1100  << '='
1101  << it->toByteArray().constData()
1102  << endl;
1103  }
1104 
1105  break;
1106 
1107  case QVariant::DateTime:
1108  cerr << m_stored.toDateTime().toString(Qt::ISODate)
1109  .toLocal8Bit().constData()
1110  << endl;
1111  break;
1112 
1113  default:
1114  cerr << endl;
1115  }
1116 }
1117 
1120 void CommandLineArg::PrintRemovedWarning(QString &keyword) const
1121 {
1122  QString warn = QString("%1 has been removed").arg(keyword);
1123  if (!m_removedversion.isEmpty())
1124  warn += QString(" as of MythTV %1").arg(m_removedversion);
1125 
1126  cerr << QString("****************************************************\n"
1127  " WARNING: %1\n"
1128  " %2\n"
1129  "****************************************************\n\n")
1130  .arg(warn).arg(m_removed)
1131  .toLocal8Bit().constData();
1132 }
1133 
1136 void CommandLineArg::PrintDeprecatedWarning(QString &keyword) const
1137 {
1138  cerr << QString("****************************************************\n"
1139  " WARNING: %1 has been deprecated\n"
1140  " %2\n"
1141  "****************************************************\n\n")
1142  .arg(keyword).arg(m_deprecated)
1143  .toLocal8Bit().constData();
1144 }
1145 
1160  m_appname(std::move(appname))
1161 {
1162  char *verbose = getenv("VERBOSE_PARSER");
1163  if (verbose != nullptr)
1164  {
1165  cerr << "MythCommandLineParser is now operating verbosely." << endl;
1166  m_verbose = true;
1167  }
1168 
1170 }
1171 
1173 {
1174  QMap<QString, CommandLineArg*>::iterator i;
1175 
1176  i = m_namedArgs.begin();
1177  while (i != m_namedArgs.end())
1178  {
1179  (*i)->CleanupLinks();
1180  (*i)->DecrRef();
1181  i = m_namedArgs.erase(i);
1182  }
1183 
1184  i = m_optionedArgs.begin();
1185  while (i != m_optionedArgs.end())
1186  {
1187  (*i)->DecrRef();
1188  i = m_optionedArgs.erase(i);
1189  }
1190 }
1191 
1226  const QString& name, QVariant::Type type, QVariant def,
1227  QString help, QString longhelp)
1228 {
1229  CommandLineArg *arg = nullptr;
1230 
1231  if (m_namedArgs.contains(name))
1232  arg = m_namedArgs[name];
1233  else
1234  {
1235  arg = new CommandLineArg(name, type, std::move(def), std::move(help), std::move(longhelp));
1236  m_namedArgs.insert(name, arg);
1237  }
1238 
1239  QStringList::const_iterator i;
1240  for (i = arglist.begin(); i != arglist.end(); ++i)
1241  {
1242  if (!m_optionedArgs.contains(*i))
1243  {
1244  arg->AddKeyword(*i);
1245  if (m_verbose)
1246  {
1247  cerr << "Adding " << (*i).toLocal8Bit().constData()
1248  << " as taking type '" << QVariant::typeToName(type)
1249  << "'" << endl;
1250  }
1251  arg->IncrRef();
1252  m_optionedArgs.insert(*i, arg);
1253  }
1254  }
1255 
1256  return arg;
1257 }
1258 
1262 {
1263  cout << "Please attach all output as a file in bug reports." << endl;
1264  cout << "MythTV Version : " << MYTH_SOURCE_VERSION << endl;
1265  cout << "MythTV Branch : " << MYTH_SOURCE_PATH << endl;
1266  cout << "Network Protocol : " << MYTH_PROTO_VERSION << endl;
1267  cout << "Library API : " << MYTH_BINARY_VERSION << endl;
1268  cout << "QT Version : " << QT_VERSION_STR << endl;
1269 #ifdef MYTH_BUILD_CONFIG
1270  cout << "Options compiled in:" <<endl;
1271  cout << MYTH_BUILD_CONFIG << endl;
1272 #endif
1273 }
1274 
1278 {
1279  QString help = GetHelpString();
1280  cerr << help.toLocal8Bit().constData();
1281 }
1282 
1289 {
1290  QString helpstr;
1291  QTextStream msg(&helpstr, QIODevice::WriteOnly);
1292 
1293  QString versionStr = QString("%1 version: %2 [%3] www.mythtv.org")
1294  .arg(m_appname).arg(MYTH_SOURCE_PATH).arg(MYTH_SOURCE_VERSION);
1295  msg << versionStr << endl;
1296 
1297  if (toString("showhelp").isEmpty())
1298  {
1299  // build generic help text
1300 
1301  QString descr = GetHelpHeader();
1302  if (descr.size() > 0)
1303  msg << endl << descr << endl << endl;
1304 
1305  // loop through registered arguments to populate list of groups
1306  QStringList groups("");
1307  int maxlen = 0;
1308  QMap<QString, CommandLineArg*>::const_iterator i1;
1309  for (i1 = m_namedArgs.begin(); i1 != m_namedArgs.end(); ++i1)
1310  {
1311  maxlen = max((*i1)->GetKeywordLength(), maxlen);
1312  if (!groups.contains((*i1)->m_group))
1313  groups << (*i1)->m_group;
1314  }
1315 
1316  // loop through list of groups and print help string for each
1317  // arguments will filter themselves if they are not in the group
1318  maxlen += 4;
1319  QStringList::const_iterator i2;
1320  for (i2 = groups.begin(); i2 != groups.end(); ++i2)
1321  {
1322  if ((*i2).isEmpty())
1323  msg << "Misc. Options:" << endl;
1324  else
1325  msg << (*i2).toLocal8Bit().constData() << " Options:" << endl;
1326 
1327  for (i1 = m_namedArgs.begin(); i1 != m_namedArgs.end(); ++i1)
1328  msg << (*i1)->GetHelpString(maxlen, *i2);
1329  msg << endl;
1330  }
1331  }
1332  else
1333  {
1334  // build help for a specific argument
1335  QString optstr = "-" + toString("showhelp");
1336  if (!m_optionedArgs.contains(optstr))
1337  {
1338  optstr = "-" + optstr;
1339  if (!m_optionedArgs.contains(optstr))
1340  return QString("Could not find option matching '%1'\n")
1341  .arg(toString("showhelp"));
1342  }
1343 
1344  msg << m_optionedArgs[optstr]->GetLongHelpString(optstr);
1345  }
1346 
1347  msg.flush();
1348  return helpstr;
1349 }
1350 
1353 int MythCommandLineParser::getOpt(int argc, const char * const * argv,
1354  int &argpos, QString &opt, QByteArray &val)
1355 {
1356  opt.clear();
1357  val.clear();
1358 
1359  if (argpos >= argc)
1360  // this shouldnt happen, return and exit
1361  return kEnd;
1362 
1363  QByteArray tmp(argv[argpos]);
1364  if (tmp.isEmpty())
1365  // string is empty, return and loop
1366  return kEmpty;
1367 
1368  if (m_passthroughActive)
1369  {
1370  // pass through has been activated
1371  val = tmp;
1372  return kArg;
1373  }
1374 
1375  if (tmp.startsWith('-') && tmp.size() > 1)
1376  {
1377  if (tmp == "--")
1378  {
1379  // all options beyond this will be passed as a single string
1380  m_passthroughActive = true;
1381  return kPassthrough;
1382  }
1383 
1384  if (tmp.contains('='))
1385  {
1386  // option contains '=', split
1387  QList<QByteArray> blist = tmp.split('=');
1388 
1389  if (blist.size() != 2)
1390  {
1391  // more than one '=' in option, this is not handled
1392  opt = QString(tmp);
1393  return kInvalid;
1394  }
1395 
1396  opt = QString(blist[0]);
1397  val = blist[1];
1398  return kCombOptVal;
1399  }
1400 
1401  opt = QString(tmp);
1402 
1403  if (argpos+1 >= argc)
1404  // end of input, option only
1405  return kOptOnly;
1406 
1407  tmp = QByteArray(argv[++argpos]);
1408  if (tmp.isEmpty())
1409  // empty string, option only
1410  return kOptOnly;
1411 
1412  if (tmp.startsWith("-") && tmp.size() > 1)
1413  {
1414  // no value found for option, backtrack
1415  argpos--;
1416  return kOptOnly;
1417  }
1418 
1419  val = tmp;
1420  return kOptVal;
1421  }
1422 
1423  // input is not an option string, return as arg
1424  val = tmp;
1425  return kArg;
1426 }
1427 
1434 bool MythCommandLineParser::Parse(int argc, const char * const * argv)
1435 {
1436  int res = kEnd;
1437  QString opt;
1438  QByteArray val;
1439  CommandLineArg *argdef = nullptr;
1440 
1441  // reconnect interdependencies between command line options
1442  if (!ReconcileLinks())
1443  return false;
1444 
1445  // loop through command line arguments until all are spent
1446  for (int argpos = 1; argpos < argc; ++argpos)
1447  {
1448 
1449  // pull next option
1450  res = getOpt(argc, argv, argpos, opt, val);
1451 
1452  if (m_verbose)
1453  {
1454  cerr << "res: " << NamedOptType(res) << endl
1455  << "opt: " << opt.toLocal8Bit().constData() << endl
1456  << "val: " << val.constData() << endl << endl;
1457  }
1458 
1459  // '--' found on command line, enable passthrough mode
1460  if (res == kPassthrough && !m_namedArgs.contains("_passthrough"))
1461  {
1462  cerr << "Received '--' but passthrough has not been enabled" << endl;
1463  SetValue("showhelp", "");
1464  return false;
1465  }
1466 
1467  // end of options found, terminate loop
1468  if (res == kEnd)
1469  break;
1470 
1471  // GetOpt pulled an empty option, this shouldnt happen by ignore
1472  // it and continue
1473  if (res == kEmpty)
1474  continue;
1475 
1476  // more than one equal found in key/value pair, fault out
1477  if (res == kInvalid)
1478  {
1479  cerr << "Invalid option received:" << endl << " "
1480  << opt.toLocal8Bit().constData();
1481  SetValue("showhelp", "");
1482  return false;
1483  }
1484 
1485  // passthrough is active, so add the data to the stringlist
1486  if (m_passthroughActive)
1487  {
1488  m_namedArgs["_passthrough"]->Set("", val);
1489  continue;
1490  }
1491 
1492  // argument with no preceeding '-' encountered, add to stringlist
1493  if (res == kArg)
1494  {
1495  if (!m_namedArgs.contains("_args"))
1496  {
1497  cerr << "Received '"
1498  << val.constData()
1499  << "' but unassociated arguments have not been enabled"
1500  << endl;
1501  SetValue("showhelp", "");
1502  return false;
1503  }
1504 
1505  m_namedArgs["_args"]->Set("", val);
1506  continue;
1507  }
1508 
1509  // this line should not be passed once arguments have started collecting
1510  if (toBool("_args"))
1511  {
1512  cerr << "Command line arguments received out of sequence"
1513  << endl;
1514  SetValue("showhelp", "");
1515  return false;
1516  }
1517 
1518 #ifdef Q_OS_MAC
1519  if (opt.startsWith("-psn_"))
1520  {
1521  cerr << "Ignoring Process Serial Number from command line"
1522  << endl;
1523  continue;
1524  }
1525 #endif
1526 
1527  if (!m_optionedArgs.contains(opt))
1528  {
1529  // argument is unhandled, check if parser allows arbitrary input
1530  if (m_namedArgs.contains("_extra"))
1531  {
1532  // arbitrary allowed, specify general collection pool
1533  argdef = m_namedArgs["_extra"];
1534  QByteArray tmp = opt.toLocal8Bit();
1535  tmp += '=';
1536  tmp += val;
1537  val = tmp;
1538  res = kOptVal;
1539  }
1540  else
1541  {
1542  // arbitrary not allowed, fault out
1543  cerr << "Unhandled option given on command line:" << endl
1544  << " " << opt.toLocal8Bit().constData() << endl;
1545  SetValue("showhelp", "");
1546  return false;
1547  }
1548  }
1549  else
1550  argdef = m_optionedArgs[opt];
1551 
1552  // argument has been marked as removed, warn user and fail
1553  if (!argdef->m_removed.isEmpty())
1554  {
1555  argdef->PrintRemovedWarning(opt);
1556  SetValue("showhelp", "");
1557  return false;
1558  }
1559 
1560  // argument has been marked as deprecated, warn user
1561  if (!argdef->m_deprecated.isEmpty())
1562  argdef->PrintDeprecatedWarning(opt);
1563 
1564  if (m_verbose)
1565  cerr << "name: " << argdef->GetName().toLocal8Bit().constData()
1566  << endl;
1567 
1568  // argument is keyword only, no value
1569  if (res == kOptOnly)
1570  {
1571  if (!argdef->Set(opt))
1572  {
1573  SetValue("showhelp", "");
1574  return false;
1575  }
1576  }
1577  // argument has keyword and value
1578  else if ((res == kOptVal) || (res == kCombOptVal))
1579  {
1580  if (!argdef->Set(opt, val))
1581  {
1582  // if option and value were combined with a '=', abort directly
1583  // otherwise, attempt processing them independenly
1584  if ((res == kCombOptVal) || !argdef->Set(opt))
1585  {
1586  SetValue("showhelp", "");
1587  return false;
1588  }
1589  // drop back an iteration so the unused value will get
1590  // processed a second time as a keyword-less argument
1591  --argpos;
1592  }
1593  }
1594  else
1595  {
1596  SetValue("showhelp", "");
1597  return false; // this should not occur
1598  }
1599 
1600  if (m_verbose)
1601  cerr << "value: " << argdef->m_stored.toString().toLocal8Bit().constData()
1602  << endl;
1603  }
1604 
1605  QMap<QString, CommandLineArg*>::const_iterator it;
1606 
1607  if (m_verbose)
1608  {
1609  cerr << "Processed option list:" << endl;
1610  for (it = m_namedArgs.begin(); it != m_namedArgs.end(); ++it)
1611  (*it)->PrintVerbose();
1612 
1613  if (m_namedArgs.contains("_args"))
1614  {
1615  cerr << endl << "Extra argument list:" << endl;
1616  QStringList slist = toStringList("_args");
1617  foreach (auto lopt, slist)
1618  cerr << " " << (lopt).toLocal8Bit().constData() << endl;
1619  }
1620 
1621  if (m_namedArgs.contains("_passthrough"))
1622  {
1623  cerr << endl << "Passthrough string:" << endl;
1624  cerr << " " << GetPassthrough().toLocal8Bit().constData() << endl;
1625  }
1626 
1627  cerr << endl;
1628  }
1629 
1630  // make sure all interdependencies are fulfilled
1631  for (it = m_namedArgs.begin(); it != m_namedArgs.end(); ++it)
1632  {
1633  if (!(*it)->TestLinks())
1634  {
1635  QString keyword = (*it)->m_usedKeyword;
1636  if (keyword.startsWith('-'))
1637  {
1638  if (keyword.startsWith("--"))
1639  keyword.remove(0,2);
1640  else
1641  keyword.remove(0,1);
1642  }
1643 
1644  SetValue("showhelp", keyword);
1645  return false;
1646  }
1647  }
1648 
1649  return true;
1650 }
1651 
1656 {
1657  if (m_verbose)
1658  cerr << "Reconciling links for option interdependencies." << endl;
1659 
1660  QMap<QString,CommandLineArg*>::iterator args_it;
1661  for (args_it = m_namedArgs.begin(); args_it != m_namedArgs.end(); ++args_it)
1662  {
1663  QList<CommandLineArg*> links = (*args_it)->m_parents;
1664  QList<CommandLineArg*>::iterator links_it;
1665  for (links_it = links.begin(); links_it != links.end(); ++links_it)
1666  {
1667  if ((*links_it)->m_type != QVariant::Invalid)
1668  continue; // already handled
1669 
1670  if (!m_namedArgs.contains((*links_it)->m_name))
1671  {
1672  // not found
1673  cerr << "ERROR: could not reconcile linked argument." << endl
1674  << " '" << (*args_it)->m_name.toLocal8Bit().constData()
1675  << "' could not find '"
1676  << (*links_it)->m_name.toLocal8Bit().constData()
1677  << "'." << endl
1678  << " Please resolve dependency and recompile." << endl;
1679  return false;
1680  }
1681 
1682  // replace linked argument
1683  if (m_verbose)
1684  {
1685  cerr << QString(" Setting %1 as child of %2")
1686  .arg((*args_it)->m_name).arg((*links_it)->m_name)
1687  .toLocal8Bit().constData()
1688  << endl;
1689  }
1690  (*args_it)->SetChildOf(m_namedArgs[(*links_it)->m_name]);
1691  }
1692 
1693  links = (*args_it)->m_children;
1694  for (links_it = links.begin(); links_it != links.end(); ++links_it)
1695  {
1696  if ((*links_it)->m_type != QVariant::Invalid)
1697  continue; // already handled
1698 
1699  if (!m_namedArgs.contains((*links_it)->m_name))
1700  {
1701  // not found
1702  cerr << "ERROR: could not reconcile linked argument." << endl
1703  << " '" << (*args_it)->m_name.toLocal8Bit().constData()
1704  << "' could not find '"
1705  << (*links_it)->m_name.toLocal8Bit().constData()
1706  << "'." << endl
1707  << " Please resolve dependency and recompile." << endl;
1708  return false;
1709  }
1710 
1711  // replace linked argument
1712  if (m_verbose)
1713  {
1714  cerr << QString(" Setting %1 as parent of %2")
1715  .arg((*args_it)->m_name).arg((*links_it)->m_name)
1716  .toLocal8Bit().constData()
1717  << endl;
1718  }
1719  (*args_it)->SetParentOf(m_namedArgs[(*links_it)->m_name]);
1720  }
1721 
1722  links = (*args_it)->m_requires;
1723  for (links_it = links.begin(); links_it != links.end(); ++links_it)
1724  {
1725  if ((*links_it)->m_type != QVariant::Invalid)
1726  continue; // already handled
1727 
1728  if (!m_namedArgs.contains((*links_it)->m_name))
1729  {
1730  // not found
1731  cerr << "ERROR: could not reconcile linked argument." << endl
1732  << " '" << (*args_it)->m_name.toLocal8Bit().constData()
1733  << "' could not find '"
1734  << (*links_it)->m_name.toLocal8Bit().constData()
1735  << "'." << endl
1736  << " Please resolve dependency and recompile." << endl;
1737  return false;
1738  }
1739 
1740  // replace linked argument
1741  if (m_verbose)
1742  {
1743  cerr << QString(" Setting %1 as requiring %2")
1744  .arg((*args_it)->m_name).arg((*links_it)->m_name)
1745  .toLocal8Bit().constData()
1746  << endl;
1747  }
1748  (*args_it)->SetRequires(m_namedArgs[(*links_it)->m_name]);
1749  }
1750 
1751  QList<CommandLineArg*>::iterator req_it =
1752  (*args_it)->m_requiredby.begin();
1753  while (req_it != (*args_it)->m_requiredby.end())
1754  {
1755  if ((*req_it)->m_type == QVariant::Invalid)
1756  {
1757  // if its not an invalid, it shouldnt be here anyway
1758  if (m_namedArgs.contains((*req_it)->m_name))
1759  {
1760  m_namedArgs[(*req_it)->m_name]->SetRequires(*args_it);
1761  if (m_verbose)
1762  {
1763  cerr << QString(" Setting %1 as blocking %2")
1764  .arg((*args_it)->m_name)
1765  .arg((*req_it)->m_name)
1766  .toLocal8Bit().constData()
1767  << endl;
1768  }
1769  }
1770  }
1771 
1772  (*req_it)->DecrRef();
1773  req_it = (*args_it)->m_requiredby.erase(req_it);
1774  }
1775 
1776  QList<CommandLineArg*>::iterator block_it =
1777  (*args_it)->m_blocks.begin();
1778  while (block_it != (*args_it)->m_blocks.end())
1779  {
1780  if ((*block_it)->m_type != QVariant::Invalid)
1781  {
1782  ++block_it;
1783  continue; // already handled
1784  }
1785 
1786  if (!m_namedArgs.contains((*block_it)->m_name))
1787  {
1788  (*block_it)->DecrRef();
1789  block_it = (*args_it)->m_blocks.erase(block_it);
1790  continue; // if it doesnt exist, it cant block this command
1791  }
1792 
1793  // replace linked argument
1794  if (m_verbose)
1795  {
1796  cerr << QString(" Setting %1 as blocking %2")
1797  .arg((*args_it)->m_name).arg((*block_it)->m_name)
1798  .toLocal8Bit().constData()
1799  << endl;
1800  }
1801  (*args_it)->SetBlocks(m_namedArgs[(*block_it)->m_name]);
1802  ++block_it;
1803  }
1804  }
1805 
1806  return true;
1807 }
1808 
1812 QVariant MythCommandLineParser::operator[](const QString &name)
1813 {
1814  QVariant var("");
1815  if (!m_namedArgs.contains(name))
1816  return var;
1817 
1818  CommandLineArg *arg = m_namedArgs[name];
1819 
1820  if (arg->m_given)
1821  var = arg->m_stored;
1822  else
1823  var = arg->m_default;
1824 
1825  return var;
1826 }
1827 
1831 QStringList MythCommandLineParser::GetArgs(void) const
1832 {
1833  return toStringList("_args");
1834 }
1835 
1839 QMap<QString,QString> MythCommandLineParser::GetExtra(void) const
1840 {
1841  return toMap("_extra");
1842 }
1843 
1847 {
1848  return toStringList("_passthrough").join(" ");
1849 }
1850 
1859 {
1860  QMap<QString,QString> smap = toMap("overridesettings");
1861 
1862  if (!m_overridesImported)
1863  {
1864  if (toBool("overridesettingsfile"))
1865  {
1866  QString filename = toString("overridesettingsfile");
1867  if (!filename.isEmpty())
1868  {
1869  QFile f(filename);
1870  if (f.open(QIODevice::ReadOnly))
1871  {
1872  char buf[1024];
1873  int64_t len = f.readLine(buf, sizeof(buf) - 1);
1874  while (len != -1)
1875  {
1876  if (len >= 1 && buf[len-1]=='\n')
1877  buf[len-1] = 0;
1878  QString line(buf);
1879  QStringList tokens = line.split("=",
1880  QString::SkipEmptyParts);
1881  if (tokens.size() == 2)
1882  {
1883  tokens[0].replace(QRegExp("^[\"']"), "");
1884  tokens[0].replace(QRegExp("[\"']$"), "");
1885  tokens[1].replace(QRegExp("^[\"']"), "");
1886  tokens[1].replace(QRegExp("[\"']$"), "");
1887  if (!tokens[0].isEmpty())
1888  smap[tokens[0]] = tokens[1];
1889  }
1890  len = f.readLine(buf, sizeof(buf) - 1);
1891  }
1892  }
1893  else
1894  {
1895  QByteArray tmp = filename.toLatin1();
1896  cerr << "Failed to open the override settings file: '"
1897  << tmp.constData() << "'" << endl;
1898  }
1899  }
1900  }
1901 
1902  if (toBool("windowed"))
1903  smap["RunFrontendInWindow"] = "1";
1904  else if (toBool("notwindowed"))
1905  smap["RunFrontendInWindow"] = "0";
1906 
1907  if (toBool("mousecursor"))
1908  smap["HideMouseCursor"] = "0";
1909  else if (toBool("nomousecursor"))
1910  smap["HideMouseCursor"] = "1";
1911 
1912  m_overridesImported = true;
1913 
1914  if (!smap.isEmpty())
1915  {
1916  QVariantMap vmap;
1917  QMap<QString, QString>::const_iterator it;
1918  for (it = smap.begin(); it != smap.end(); ++it)
1919  vmap[it.key()] = QVariant(it.value());
1920 
1921  m_namedArgs["overridesettings"]->Set(QVariant(vmap));
1922  }
1923  }
1924 
1925  if (m_verbose)
1926  {
1927  cerr << "Option Overrides:" << endl;
1928  QMap<QString, QString>::const_iterator it;
1929  for (it = smap.constBegin(); it != smap.constEnd(); ++it)
1930  cerr << QString(" %1 - %2").arg(it.key(), 30).arg(*it)
1931  .toLocal8Bit().constData() << endl;
1932  }
1933 
1934  return smap;
1935 }
1936 
1943 bool MythCommandLineParser::toBool(const QString& key) const
1944 {
1945  if (!m_namedArgs.contains(key))
1946  return false;
1947 
1948  CommandLineArg *arg = m_namedArgs[key];
1949 
1950  if (arg->m_type == QVariant::Bool)
1951  {
1952  if (arg->m_given)
1953  return arg->m_stored.toBool();
1954  return arg->m_default.toBool();
1955  }
1956 
1957  return arg->m_given;
1958 }
1959 
1963 int MythCommandLineParser::toInt(const QString& key) const
1964 {
1965  int val = 0;
1966  if (!m_namedArgs.contains(key))
1967  return val;
1968 
1969  CommandLineArg *arg = m_namedArgs[key];
1970 
1971  if (arg->m_given)
1972  {
1973  if (arg->m_stored.canConvert(QVariant::Int))
1974  val = arg->m_stored.toInt();
1975  }
1976  else
1977  {
1978  if (arg->m_default.canConvert(QVariant::Int))
1979  val = arg->m_default.toInt();
1980  }
1981 
1982  return val;
1983 }
1984 
1988 uint MythCommandLineParser::toUInt(const QString& key) const
1989 {
1990  uint val = 0;
1991  if (!m_namedArgs.contains(key))
1992  return val;
1993 
1994  CommandLineArg *arg = m_namedArgs[key];
1995 
1996  if (arg->m_given)
1997  {
1998  if (arg->m_stored.canConvert(QVariant::UInt))
1999  val = arg->m_stored.toUInt();
2000  }
2001  else
2002  {
2003  if (arg->m_default.canConvert(QVariant::UInt))
2004  val = arg->m_default.toUInt();
2005  }
2006 
2007  return val;
2008 }
2009 
2013 long long MythCommandLineParser::toLongLong(const QString& key) const
2014 {
2015  long long val = 0;
2016  if (!m_namedArgs.contains(key))
2017  return val;
2018 
2019  CommandLineArg *arg = m_namedArgs[key];
2020 
2021  if (arg->m_given)
2022  {
2023  if (arg->m_stored.canConvert(QVariant::LongLong))
2024  val = arg->m_stored.toLongLong();
2025  }
2026  else
2027  {
2028  if (arg->m_default.canConvert(QVariant::LongLong))
2029  val = arg->m_default.toLongLong();
2030  }
2031 
2032  return val;
2033 }
2034 
2038 double MythCommandLineParser::toDouble(const QString& key) const
2039 {
2040  double val = 0.0;
2041  if (!m_namedArgs.contains(key))
2042  return val;
2043 
2044  CommandLineArg *arg = m_namedArgs[key];
2045 
2046  if (arg->m_given)
2047  {
2048  if (arg->m_stored.canConvert(QVariant::Double))
2049  val = arg->m_stored.toDouble();
2050  }
2051  else
2052  {
2053  if (arg->m_default.canConvert(QVariant::Double))
2054  val = arg->m_default.toDouble();
2055  }
2056 
2057  return val;
2058 }
2059 
2063 QSize MythCommandLineParser::toSize(const QString& key) const
2064 {
2065  QSize val(0,0);
2066  if (!m_namedArgs.contains(key))
2067  return val;
2068 
2069  CommandLineArg *arg = m_namedArgs[key];
2070 
2071  if (arg->m_given)
2072  {
2073  if (arg->m_stored.canConvert(QVariant::Size))
2074  val = arg->m_stored.toSize();
2075  }
2076  else
2077  {
2078  if (arg->m_default.canConvert(QVariant::Size))
2079  val = arg->m_default.toSize();
2080  }
2081 
2082  return val;
2083 }
2084 
2088 QString MythCommandLineParser::toString(const QString& key) const
2089 {
2090  QString val("");
2091  if (!m_namedArgs.contains(key))
2092  return val;
2093 
2094  CommandLineArg *arg = m_namedArgs[key];
2095 
2096  if (arg->m_given)
2097  {
2098  if (!arg->m_converted)
2099  arg->Convert();
2100 
2101  if (arg->m_stored.canConvert(QVariant::String))
2102  val = arg->m_stored.toString();
2103  }
2104  else
2105  {
2106  if (arg->m_default.canConvert(QVariant::String))
2107  val = arg->m_default.toString();
2108  }
2109 
2110  return val;
2111 }
2112 
2117 QStringList MythCommandLineParser::toStringList(const QString& key, const QString& sep) const
2118 {
2119  QVariant varval;
2120  QStringList val;
2121  if (!m_namedArgs.contains(key))
2122  return val;
2123 
2124  CommandLineArg *arg = m_namedArgs[key];
2125 
2126  if (arg->m_given)
2127  {
2128  if (!arg->m_converted)
2129  arg->Convert();
2130 
2131  varval = arg->m_stored;
2132  }
2133  else
2134  varval = arg->m_default;
2135 
2136  if (arg->m_type == QVariant::String && !sep.isEmpty())
2137  val = varval.toString().split(sep);
2138  else if (varval.canConvert(QVariant::StringList))
2139  val = varval.toStringList();
2140 
2141  return val;
2142 }
2143 
2147 QMap<QString,QString> MythCommandLineParser::toMap(const QString& key) const
2148 {
2149  QMap<QString, QString> val;
2150  QMap<QString, QVariant> tmp;
2151  if (!m_namedArgs.contains(key))
2152  return val;
2153 
2154  CommandLineArg *arg = m_namedArgs[key];
2155 
2156  if (arg->m_given)
2157  {
2158  if (!arg->m_converted)
2159  arg->Convert();
2160 
2161  if (arg->m_stored.canConvert(QVariant::Map))
2162  tmp = arg->m_stored.toMap();
2163  }
2164  else
2165  {
2166  if (arg->m_default.canConvert(QVariant::Map))
2167  tmp = arg->m_default.toMap();
2168  }
2169 
2170  QMap<QString, QVariant>::const_iterator i;
2171  for (i = tmp.begin(); i != tmp.end(); ++i)
2172  val[i.key()] = i.value().toString();
2173 
2174  return val;
2175 }
2176 
2180 QDateTime MythCommandLineParser::toDateTime(const QString& key) const
2181 {
2182  QDateTime val;
2183  if (!m_namedArgs.contains(key))
2184  return val;
2185 
2186  CommandLineArg *arg = m_namedArgs[key];
2187 
2188  if (arg->m_given)
2189  {
2190  if (arg->m_stored.canConvert(QVariant::DateTime))
2191  val = arg->m_stored.toDateTime();
2192  }
2193  else
2194  {
2195  if (arg->m_default.canConvert(QVariant::DateTime))
2196  val = arg->m_default.toDateTime();
2197  }
2198 
2199  return val;
2200 }
2201 
2206 {
2207  if (m_namedArgs.contains("_args"))
2208  {
2209  if (!allow)
2210  m_namedArgs.remove("_args");
2211  }
2212  else if (!allow)
2213  return;
2214 
2215  auto *arg = new CommandLineArg("_args", QVariant::StringList, QStringList());
2216  m_namedArgs["_args"] = arg;
2217 }
2218 
2223 {
2224  if (m_namedArgs.contains("_extra"))
2225  {
2226  if (!allow)
2227  m_namedArgs.remove("_extra");
2228  }
2229  else if (!allow)
2230  return;
2231 
2232  QMap<QString,QVariant> vmap;
2233  auto *arg = new CommandLineArg("_extra", QVariant::Map, vmap);
2234 
2235  m_namedArgs["_extra"] = arg;
2236 }
2237 
2242 {
2243  if (m_namedArgs.contains("_passthrough"))
2244  {
2245  if (!allow)
2246  m_namedArgs.remove("_passthrough");
2247  }
2248  else if (!allow)
2249  return;
2250 
2251  auto *arg = new CommandLineArg("_passthrough",
2252  QVariant::StringList, QStringList());
2253  m_namedArgs["_passthrough"] = arg;
2254 }
2255 
2259 {
2260  add(QStringList{"-h", "--help", "--usage"},
2261  "showhelp", "", "Display this help printout, or give detailed "
2262  "information of selected option.",
2263  "Displays a list of all commands available for use with "
2264  "this application. If another option is provided as an "
2265  "argument, it will provide detailed information on that "
2266  "option.");
2267 }
2268 
2272 {
2273  add("--version", "showversion", false, "Display version information.",
2274  "Display informtion about build, including:\n"
2275  " version, branch, protocol, library API, Qt "
2276  "and compiled options.");
2277 }
2278 
2282 {
2283  add(QStringList{"-nw", "--no-windowed"},
2284  "notwindowed", false,
2285  "Prevent application from running in a window.", "")
2286  ->SetBlocks("windowed")
2287  ->SetGroup("User Interface");
2288 
2289  add(QStringList{"-w", "--windowed"}, "windowed",
2290  false, "Force application to run in a window.", "")
2291  ->SetGroup("User Interface");
2292 }
2293 
2297 {
2298  add("--mouse-cursor", "mousecursor", false,
2299  "Force visibility of the mouse cursor.", "")
2300  ->SetBlocks("nomousecursor")
2301  ->SetGroup("User Interface");
2302 
2303  add("--no-mouse-cursor", "nomousecursor", false,
2304  "Force the mouse cursor to be hidden.", "")
2305  ->SetGroup("User Interface");
2306 }
2307 
2311 {
2312  add(QStringList{"-d", "--daemon"}, "daemon", false,
2313  "Fork application into background after startup.",
2314  "Fork application into background, detatching from "
2315  "the local terminal.\nOften used with: "
2316  " --logpath --pidfile --user");
2317 }
2318 
2323 {
2324  add(QStringList{"-O", "--override-setting"},
2325  "overridesettings", QVariant::Map,
2326  "Override a single setting defined by a key=value pair.",
2327  "Override a single setting from the database using "
2328  "options defined as one or more key=value pairs\n"
2329  "Multiple can be defined by multiple uses of the "
2330  "-O option.");
2331  add("--override-settings-file", "overridesettingsfile", "",
2332  "Define a file of key=value pairs to be "
2333  "loaded for setting overrides.", "");
2334 }
2335 
2339 {
2340  add("--chanid", "chanid", 0U,
2341  "Specify chanid of recording to operate on.", "")
2342  ->SetRequires("starttime");
2343 
2344  add("--starttime", "starttime", QDateTime(),
2345  "Specify start time of recording to operate on.", "")
2346  ->SetRequires("chanid");
2347 }
2348 
2352 {
2353  add(QStringList{"-geometry", "--geometry"}, "geometry",
2354  "", "Specify window size and position (WxH[+X+Y])", "")
2355  ->SetGroup("User Interface");
2356 }
2357 
2361 {
2362 #ifdef USING_X11
2363  add("-display", "display", "", "Specify X server to use.", "")
2364  ->SetGroup("User Interface");
2365 #endif
2366 }
2367 
2371 {
2372  add("--noupnp", "noupnp", false, "Disable use of UPnP.", "");
2373 }
2374 
2379  const QString &defaultVerbosity, LogLevel_t defaultLogLevel)
2380 {
2381  defaultLogLevel =
2382  ((defaultLogLevel >= LOG_UNKNOWN) || (defaultLogLevel <= LOG_ANY)) ?
2383  LOG_INFO : defaultLogLevel;
2384 
2385  QString logLevelStr = logLevelGetName(defaultLogLevel);
2386 
2387  add(QStringList{"-v", "--verbose"}, "verbose",
2388  defaultVerbosity,
2389  "Specify log filtering. Use '-v help' for level info.", "")
2390  ->SetGroup("Logging");
2391  add("-V", "verboseint", 0LL, "",
2392  "This option is intended for internal use only.\n"
2393  "This option takes an unsigned value corresponding "
2394  "to the bitwise log verbosity operator.")
2395  ->SetGroup("Logging");
2396  add("--logpath", "logpath", "",
2397  "Writes logging messages to a file in the directory logpath with "
2398  "filenames in the format: applicationName.date.pid.log.\n"
2399  "This is typically used in combination with --daemon, and if used "
2400  "in combination with --pidfile, this can be used with log "
2401  "rotators, using the HUP call to inform MythTV to reload the "
2402  "file", "")
2403  ->SetGroup("Logging");
2404  add(QStringList{"-q", "--quiet"}, "quiet", 0,
2405  "Don't log to the console (-q). Don't log anywhere (-q -q)", "")
2406  ->SetGroup("Logging");
2407  add("--loglevel", "loglevel", logLevelStr,
2408  QString(
2409  "Set the logging level. All log messages at lower levels will be "
2410  "discarded.\n"
2411  "In descending order: emerg, alert, crit, err, warning, notice, "
2412  "info, debug\ndefaults to ") + logLevelStr, "")
2413  ->SetGroup("Logging");
2414  add("--syslog", "syslog", "none",
2415  "Set the syslog logging facility.\nSet to \"none\" to disable, "
2416  "defaults to none.", "")
2417  ->SetGroup("Logging");
2418 #if CONFIG_SYSTEMD_JOURNAL
2419  add("--systemd-journal", "systemd-journal", "false",
2420  "Use systemd-journal instead of syslog.", "")
2421  ->SetBlocks(QStringList()
2422  << "syslog"
2423  )
2424  ->SetGroup("Logging");
2425 #endif
2426  add("--nodblog", "nodblog", false, "Disable database logging.", "")
2427  ->SetGroup("Logging")
2428  ->SetDeprecated("this is now the default, see --enable-dblog");
2429  add("--enable-dblog", "enabledblog", false, "Enable logging to database.", "")
2430  ->SetGroup("Logging");
2431 
2432  add(QStringList{"-l", "--logfile"},
2433  "logfile", "", "", "")
2434  ->SetGroup("Logging")
2435  ->SetRemoved("This option has been removed as part of "
2436  "rewrite of the logging interface. Please update your init "
2437  "scripts to use --syslog to interface with your system's "
2438  "existing system logging daemon, or --logpath to specify a "
2439  "dirctory for MythTV to write its logs to.", "0.25");
2440 }
2441 
2445 {
2446  add(QStringList{"-p", "--pidfile"}, "pidfile", "",
2447  "Write PID of application to filename.",
2448  "Write the PID of the currently running process as a single "
2449  "line to this file. Used for init scripts to know what "
2450  "process to terminate, and with log rotators "
2451  "to send a HUP signal to process to have it re-open files.");
2452 }
2453 
2457 {
2458  add(QStringList{"-j", "--jobid"}, "jobid", 0, "",
2459  "Intended for internal use only, specify the JobID to match "
2460  "up with in the database for additional information and the "
2461  "ability to update runtime status in the database.");
2462 }
2463 
2467 {
2468  add("--infile", "infile", "", "Input file URI", "");
2469  if (addOutFile)
2470  add("--outfile", "outfile", "", "Output file URI", "");
2471 }
2472 
2476 {
2477  QString logfile = toString("logpath");
2478  pid_t pid = getpid();
2479 
2480  if (logfile.isEmpty())
2481  return logfile;
2482 
2483  QString logdir;
2484  QString filepath;
2485 
2486  QFileInfo finfo(logfile);
2487  if (!finfo.isDir())
2488  {
2489  LOG(VB_GENERAL, LOG_ERR,
2490  QString("%1 is not a directory, disabling logfiles")
2491  .arg(logfile));
2492  return QString();
2493  }
2494 
2495  logdir = finfo.filePath();
2496  logfile = QCoreApplication::applicationName() + "." +
2498  QString(".%1").arg(pid) + ".log";
2499 
2500  SetValue("logdir", logdir);
2501  SetValue("logfile", logfile);
2502  SetValue("filepath", QFileInfo(QDir(logdir), logfile).filePath());
2503 
2504  return toString("filepath");
2505 }
2506 
2510 {
2511  QString setting = toString("syslog").toLower();
2512  if (setting == "none")
2513  return -2;
2514 
2515  return syslogGetFacility(setting);
2516 }
2517 
2521 {
2522  QString setting = toString("loglevel");
2523  if (setting.isEmpty())
2524  return LOG_INFO;
2525 
2526  LogLevel_t level = logLevelGet(setting);
2527  if (level == LOG_UNKNOWN)
2528  cerr << "Unknown log level: " << setting.toLocal8Bit().constData() <<
2529  endl;
2530 
2531  return level;
2532 }
2533 
2538 bool MythCommandLineParser::SetValue(const QString &key, const QVariant& value)
2539 {
2540  CommandLineArg *arg = nullptr;
2541 
2542  if (!m_namedArgs.contains(key))
2543  {
2544  const QVariant& val(value);
2545  arg = new CommandLineArg(key, val.type(), val);
2546  m_namedArgs.insert(key, arg);
2547  }
2548  else
2549  {
2550  arg = m_namedArgs[key];
2551  if (arg->m_type != value.type())
2552  return false;
2553  }
2554 
2555  arg->Set(value);
2556  return true;
2557 }
2558 
2561 int MythCommandLineParser::ConfigureLogging(const QString& mask, unsigned int progress)
2562 {
2563  int err = 0;
2564 
2565  // Setup the defaults
2566  verboseString = "";
2567  verboseMask = 0;
2568  verboseArgParse(mask);
2569 
2570  if (toBool("verbose"))
2571  {
2572  if ((err = verboseArgParse(toString("verbose"))))
2573  return err;
2574  }
2575  else if (toBool("verboseint"))
2576  verboseMask = toLongLong("verboseint");
2577 
2578  verboseMask |= VB_STDIO|VB_FLUSH;
2579 
2580  int quiet = toUInt("quiet");
2581  if (max(quiet, (int)progress) > 1)
2582  {
2583  verboseMask = VB_NONE|VB_FLUSH;
2584  verboseArgParse("none");
2585  }
2586 
2587  int facility = GetSyslogFacility();
2588 #if CONFIG_SYSTEMD_JOURNAL
2589  bool journal = toBool("systemd-journal");
2590  if (journal)
2591  {
2592  if (facility >= 0)
2594  facility = SYSTEMD_JOURNAL_FACILITY;
2595  }
2596 #endif
2597  bool dblog = toBool("enabledblog");
2598  LogLevel_t level = GetLogLevel();
2599  if (level == LOG_UNKNOWN)
2601 
2602  LOG(VB_GENERAL, LOG_CRIT,
2603  QString("%1 version: %2 [%3] www.mythtv.org")
2604  .arg(QCoreApplication::applicationName())
2605  .arg(MYTH_SOURCE_PATH).arg(MYTH_SOURCE_VERSION));
2606  LOG(VB_GENERAL, LOG_CRIT, QString("Qt version: compile: %1, runtime: %2")
2607  .arg(QT_VERSION_STR).arg(qVersion()));
2608  LOG(VB_GENERAL, LOG_INFO, QString("%1 (%2)")
2609  .arg(QSysInfo::prettyProductName()).arg(QSysInfo::currentCpuArchitecture()));
2610  LOG(VB_GENERAL, LOG_NOTICE,
2611  QString("Enabled verbose msgs: %1").arg(verboseString));
2612 
2613  QString logfile = GetLogFilePath();
2614  bool propagate = !logfile.isEmpty();
2615 
2616  if (toBool("daemon"))
2617  quiet = max(quiet, 1);
2618 
2619  logStart(logfile, progress, quiet, facility, level, dblog, propagate);
2620 
2621  return GENERIC_EXIT_OK;
2622 }
2623 
2629 {
2630  if (m_verbose)
2631  cerr << "Applying settings override" << endl;
2632 
2633  QMap<QString, QString> override = GetSettingsOverride();
2634  if (!override.empty())
2635  {
2636  QMap<QString, QString>::iterator it;
2637  for (it = override.begin(); it != override.end(); ++it)
2638  {
2639  LOG(VB_GENERAL, LOG_NOTICE,
2640  QString("Setting '%1' being forced to '%2'")
2641  .arg(it.key()).arg(*it));
2642  gCoreContext->OverrideSettingForSession(it.key(), *it);
2643  }
2644  }
2645 }
2646 
2647 bool openPidfile(ofstream &pidfs, const QString &pidfile)
2648 {
2649  if (!pidfile.isEmpty())
2650  {
2651  pidfs.open(pidfile.toLatin1().constData());
2652  if (!pidfs)
2653  {
2654  cerr << "Could not open pid file: " << ENO_STR << endl;
2655  return false;
2656  }
2657  }
2658  return true;
2659 }
2660 
2663 bool setUser(const QString &username)
2664 {
2665  if (username.isEmpty())
2666  return true;
2667 
2668 #ifdef _WIN32
2669  cerr << "--user option is not supported on Windows" << endl;
2670  return false;
2671 #else // ! _WIN32
2672 #if defined(__linux__) || defined(__LINUX__)
2673  // Check the current dumpability of core dumps, which will be disabled
2674  // by setuid, so we can re-enable, if appropriate
2675  int dumpability = prctl(PR_GET_DUMPABLE);
2676 #endif
2677  struct passwd *user_info = getpwnam(username.toLocal8Bit().constData());
2678  const uid_t user_id = geteuid();
2679 
2680  if (user_id && (!user_info || user_id != user_info->pw_uid))
2681  {
2682  cerr << "You must be running as root to use the --user switch." << endl;
2683  return false;
2684  }
2685  if (user_info && user_id == user_info->pw_uid)
2686  {
2687  LOG(VB_GENERAL, LOG_WARNING,
2688  QString("Already running as '%1'").arg(username));
2689  }
2690  else if (!user_id && user_info)
2691  {
2692  if (setenv("HOME", user_info->pw_dir,1) == -1)
2693  {
2694  cerr << "Error setting home directory." << endl;
2695  return false;
2696  }
2697  if (setgid(user_info->pw_gid) == -1)
2698  {
2699  cerr << "Error setting effective group." << endl;
2700  return false;
2701  }
2702  if (initgroups(user_info->pw_name, user_info->pw_gid) == -1)
2703  {
2704  cerr << "Error setting groups." << endl;
2705  return false;
2706  }
2707  if (setuid(user_info->pw_uid) == -1)
2708  {
2709  cerr << "Error setting effective user." << endl;
2710  return false;
2711  }
2712 #if defined(__linux__) || defined(__LINUX__)
2713  if (dumpability && (prctl(PR_SET_DUMPABLE, dumpability) == -1))
2714  {
2715  LOG(VB_GENERAL, LOG_WARNING, "Unable to re-enable core file "
2716  "creation. Run without the --user argument to use "
2717  "shell-specified limits.");
2718  }
2719 #endif
2720  }
2721  else
2722  {
2723  cerr << QString("Invalid user '%1' specified with --user")
2724  .arg(username).toLocal8Bit().constData() << endl;
2725  return false;
2726  }
2727  return true;
2728 #endif // ! _WIN32
2729 }
2730 
2731 
2735 {
2736  ofstream pidfs;
2737  if (!openPidfile(pidfs, toString("pidfile")))
2739 
2740  if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
2741  LOG(VB_GENERAL, LOG_WARNING, "Unable to ignore SIGPIPE");
2742 
2743 #if CONFIG_DARWIN
2744  if (toBool("daemon"))
2745  {
2746  cerr << "Daemonizing is unavailable in OSX" << endl;
2747  LOG(VB_GENERAL, LOG_WARNING, "Unable to daemonize");
2748  }
2749 #else
2750  if (toBool("daemon") && (daemon(0, 1) < 0))
2751  {
2752  cerr << "Failed to daemonize: " << ENO_STR << endl;
2754  }
2755 #endif
2756 
2757  QString username = toString("username");
2758  if (!username.isEmpty() && !setUser(username))
2760 
2761  if (pidfs)
2762  {
2763  pidfs << getpid() << endl;
2764  pidfs.close();
2765  }
2766 
2767  return GENERIC_EXIT_OK;
2768 }
QDateTime toDateTime(const QString &key) const
Returns stored QVariant as a QDateTime, falling to default if not provided.
QString logfile
#define ENO_STR
Definition: mythlogging.h:100
int Daemonize(void)
Fork application into background, and detatch from terminal.
QString logLevelGetName(LogLevel_t level)
Map a log level enumerated value back to the name.
Definition: logging.cpp:852
virtual void LoadArguments(void)
void allowArgs(bool allow=true)
Specify that parser should allow and collect values provided independent of any keyword.
virtual QString GetHelpHeader(void) const
void addWindowed(void)
Canned argument definition for –windowed and -no-windowed.
QList< CommandLineArg * > m_parents
const int kPassthrough
QList< CommandLineArg * > m_requires
#define GENERIC_EXIT_OK
Exited with no error.
Definition: exitcodes.h:10
#define GENERIC_EXIT_DAEMONIZING_ERROR
Error daemonizing or execl.
Definition: exitcodes.h:28
long long toLongLong(const QString &key) const
Returns stored QVariant as a long integer, falling to default if not provided.
int GetSyslogFacility(void)
Helper utility for logging interface to return syslog facility.
static void AllowOneOf(const QList< CommandLineArg * > &args)
Mark a list of arguments as mutually exclusive.
QString pidfile
#define GENERIC_EXIT_PERMISSIONS_ERROR
File permissions error.
Definition: exitcodes.h:19
bool toBool(const QString &key) const
Returns stored QVariant as a boolean.
QString GetLongHelpString(QString keyword) const
Return string containing extended help text.
void addPIDFile(void)
Canned argument definition for –pidfile.
QMap< QString, QString > GetExtra(void) const
Return map of additional key/value pairs provided on the command line independent of any registered a...
const int kInvalid
static void PrintVersion(void)
Print application version information.
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
Definition: mythdate.cpp:30
QMap< QString, CommandLineArg * > m_optionedArgs
General purpose reference counter.
QVariant::Type m_type
void PrintHelp(void) const
Print command line option help.
LogLevel_t GetLogLevel(void)
Helper utility for logging interface to filtering level.
void addMouse(void)
Canned argument definition for –mouse-cursor and –no-mouse-cursor.
QSize toSize(const QString &key) const
Returns stored QVariant as a QSize value, falling to default if not provided.
void allowExtras(bool allow=true)
Specify that parser should allow and collect additional key/value pairs not explicitly defined for pr...
int syslogGetFacility(const QString &facility)
Map a syslog facility name back to the enumerated value.
Definition: logging.cpp:802
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
uint64_t verboseMask
Definition: logging.cpp:107
CommandLineArg * add(const QString &arg, const QString &name, bool def, QString help, QString longhelp)
void addVersion(void)
Canned argument definition for –version.
Definition for a single command line option.
void addLogging(const QString &defaultVerbosity="general", LogLevel_t defaultLogLevel=LOG_INFO)
Canned argument definition for all logging options, including –verbose, –logpath, –quiet,...
void addHelp(void)
Canned argument definition for –help.
bool SetValue(const QString &key, const QVariant &value)
Set a new stored value for an existing argument definition, or spawn a new definition store value in.
void addDaemon(void)
Canned argument definition for –daemon.
static guint32 * tmp
Definition: goom_core.c:35
int GetKeywordLength(void) const
Return length of full keyword string for use in determining indent of help text.
void addUPnP(void)
Canned argument definition for –noupnp.
CommandLineArg * SetRequiredChild(const QString &opt)
Set argument as parent of given child and mark as required.
void CleanupLinks(void)
Clear out references to other arguments in preparation for deletion.
bool setUser(const QString &username)
Drop permissions to the specified user.
void addGeometry(void)
Canned argument definition for –geometry.
bool openPidfile(ofstream &pidfs, const QString &pidfile)
void addInFile(bool addOutFile=false)
Canned argument definition for –infile and –outfile.
void ApplySettingsOverride(void)
Apply all overrides to the global context.
CommandLineArg * SetParentOf(const QString &opt)
Set argument as parent of given child.
QMap< QString, CommandLineArg * > m_namedArgs
QStringList GetArgs(void) const
Return list of additional values provided on the command line independent of any keyword.
virtual int IncrRef(void)
Increments reference count.
void AddKeyword(const QString &keyword)
CommandLineArg * SetChildOf(const QString &opt)
Set argument as child of given parent.
void PrintDeprecatedWarning(QString &keyword) const
Internal use.
QString GetHelpString(int off, const QString &group="", bool force=false) const
Return string containing help text with desired offset.
void OverrideSettingForSession(const QString &key, const QString &value)
QString GetPassthrough(void) const
Return any text supplied on the command line after a bare '–'.
def user_info(user, format="xml")
Definition: vimeo_api.py:515
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
QString toString(const QString &key) const
Returns stored QVariant as a QString, falling to default if not provided.
#define MYTH_PROTO_VERSION
Increment this whenever the MythTV network protocol changes.
Definition: mythversion.h:48
#define geteuid()
Definition: compat.h:261
const int kOptVal
QString GetName(void) const
#define daemon(x, y)
Definition: compat.h:321
unsigned int uint
Definition: compat.h:140
double toDouble(const QString &key) const
Returns stored QVariant as double floating point value, falling to default if not provided.
const int kArg
QList< CommandLineArg * > m_requiredby
CommandLineArg * SetParent(const QString &opt)
Set argument as child of given parent.
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
Definition: mythdate.cpp:101
const int kCombOptVal
const char * m_name
Definition: ParseText.cpp:328
#define TERMWIDTH
void addJob(void)
Canned argument definition for –jobid.
CommandLineArg * SetRemoved(QString remstr="", QString remver="")
Set option as removed.
bool ReconcileLinks(void)
Replace dummy arguments used to define interdependency with pointers to their real counterparts.
QMap< QString, QString > toMap(const QString &key) const
Returns stored QVariant as a QMap, falling to default if not provided.
QStringList toStringList(const QString &key, const QString &sep="") const
Returns stored QVariant as a QStringList, falling to default if not provided.
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
QString GetHelpString(void) const
Generate command line option help text.
CommandLineArg * SetGroup(const QString &group)
bool TestLinks(void) const
Test all related arguments to make sure specified requirements are fulfilled.
QList< CommandLineArg * > m_blocks
const int kOptOnly
void wrapList(QStringList &list, int width)
void PrintRemovedWarning(QString &keyword) const
Internal use.
QMap< QString, QString > GetSettingsOverride(void)
Return map of key/value pairs provided to override database options.
const int kEmpty
virtual bool Parse(int argc, const char *const *argv)
Loop through argv and populate arguments with values.
int GetTermWidth(void)
returns terminal width, or 79 on error
void PrintVerbose(void) const
Internal use.
const int kEnd
QString GetPreferredKeyword(void) const
Return the longest keyword for the argument.
#define MYTH_BINARY_VERSION
Update this whenever the plug-in ABI changes.
Definition: mythversion.h:16
void addDisplay(void)
Canned argument definition for -display.
CommandLineArg(const QString &name, QVariant::Type type, QVariant def, QString help, QString longhelp)
Default constructor for CommandLineArg class.
#define setenv(x, y, z)
Definition: compat.h:156
int verboseArgParse(const QString &arg)
Parse the –verbose commandline argument and set the verbose level.
Definition: logging.cpp:979
MythCommandLineParser(QString appname)
Default constructor for MythCommandLineArg class.
Default UTC.
Definition: mythdate.h:14
#define SIGPIPE
Definition: compat.h:218
QList< CommandLineArg * > m_children
#define GENERIC_EXIT_INVALID_CMDLINE
Command line parse error.
Definition: exitcodes.h:15
Default UTC, "yyyyMMddhhmmss".
Definition: mythdate.h:15
#define setuid(x)
Definition: compat.h:262
LogLevel_t logLevelGet(const QString &level)
Map a log level name back to the enumerated value.
Definition: logging.cpp:830
const char * NamedOptType(int type)
QString GetLogFilePath(void)
Helper utility for logging interface to pull path from –logpath.
QVariant operator[](const QString &name)
Returned stored QVariant for given argument, or default value if not used.
uint toUInt(const QString &key) const
Returns stored QVariant as an unsigned integer, falling to default if not provided.
void addSettingsOverride(void)
Canned argument definition for –override-setting and –override-settings-file.
CommandLineArg * SetRequires(const QString &opt)
Set argument as requiring given option.
CommandLineArg * SetRequiredChildOf(const QString &opt)
Set argument as child required by given parent.
bool Set(const QString &opt)
Set option as provided on command line with no value.
CommandLineArg * SetDeprecated(QString depstr="")
Set option as deprecated.
void allowPassthrough(bool allow=true)
Specify that parser should allow a bare '–', and collect all subsequent text as a QString.
void addRecording(void)
Canned argument definition for –chanid and –starttime.
QString GetKeywordString(void) const
Return string containing all possible keyword triggers for this argument.
int toInt(const QString &key) const
Returns stored QVariant as an integer, falling to default if not provided.
int ConfigureLogging(const QString &mask="general", unsigned int progress=0)
Read in logging options and initialize the logging interface.
void logStart(const QString &logfile, int progress, int quiet, int facility, LogLevel_t level, bool dblog, bool propagate)
Entry point to start logging for the application.
Definition: logging.cpp:717
int getOpt(int argc, const char *const *argv, int &argpos, QString &opt, QByteArray &val)
Internal use.
CommandLineArg * SetChild(const QString &opt)
Set argument as parent of given child.
CommandLineArg * SetBlocks(const QString &opt)
Set argument as incompatible with given option.
void Convert(void)
Convert stored string value from QByteArray to QString.
QString verboseString
Definition: logging.cpp:108