Torc  0.1
torccommandline.cpp
Go to the documentation of this file.
1 /* Class TorcCommandLine
2 *
3 * This file is part of the Torc project.
4 *
5 * Copyright (C) Mark Kendall 2013-18
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
20 * USA.
21 */
22 
23 // Qt
24 #include <QMutex>
25 #include <QStringList>
26 #include <QCoreApplication>
27 
28 // Torc
29 #include "torcexitcodes.h"
30 #include "torclogging.h"
31 #include "torccommandline.h"
32 
33 #include <iostream>
34 
39 {
40  public:
41  TorcEnvironmentVariable(const QString &Var, const QString &Description)
43  m_variable(Var),
44  m_description(Description)
45  {
46  gEnvironmentVariable = this;
47  }
48 
51  QString m_variable;
52  QString m_description;
53 
54  private:
55  Q_DISABLE_COPY(TorcEnvironmentVariable)
56 };
57 
59 
69  : m_value(QVariant()),
70  m_helpText(),
71  m_exitImmediately(true),
72  m_flags(TorcCommandLine::None)
73 {
74 }
75 
76 TorcArgument::TorcArgument(const QVariant &Default, const QString &HelpText, TorcCommandLine::Options Flags, bool Exit)
77  : m_value(Default),
78  m_helpText(HelpText),
79  m_exitImmediately(Exit),
80  m_flags(Flags)
81 {
82 }
83 
95  : m_options(),
96  m_aliases(),
97  m_help(),
98  m_maxLength(0)
99 {
100  // always enable version, help and logging
101  TorcCommandLine::Options options = Flags | TorcCommandLine::Version | TorcCommandLine::Help | TorcCommandLine::LogLevel |
104 
105  if (options.testFlag(TorcCommandLine::Help))
106  AddPriv(QStringLiteral("h,help"), QVariant(), QStringLiteral("Display full usage information."), TorcCommandLine::Help, true);
107  if (options.testFlag(TorcCommandLine::LogLevel))
108  AddPriv(QStringLiteral("l,log"), QStringList(QStringLiteral("general")), QStringLiteral("Set the logging level."), TorcCommandLine::None);
109  if (options.testFlag(TorcCommandLine::LogType))
110  AddPriv(QStringLiteral("v,verbose,verbosity"), QStringLiteral("info"), QStringLiteral("Set the logging type."), TorcCommandLine::None);
111  if (options.testFlag(TorcCommandLine::Version))
112  AddPriv(QStringLiteral("version"), QVariant(), QStringLiteral("Display version information."), TorcCommandLine::Version, true);
113  if (options.testFlag(TorcCommandLine::Database))
114  AddPriv(QStringLiteral("db,database"), QStringLiteral(""), QStringLiteral("Use a custom database location. If the file does not exist, it will be created."));
115  if (options.testFlag(TorcCommandLine::LogFile))
116  AddPriv(QStringLiteral("logfile"), QStringLiteral(""), QStringLiteral("Override the logfile location."));
117  if (options.testFlag(TorcCommandLine::XSDTest))
118  AddPriv(QStringLiteral("xsdtest"), QStringLiteral(""), QStringLiteral("Run validation of test configuration XML files found in the given directory."), TorcCommandLine::XSDTest);
119  if (options.testFlag(TorcCommandLine::ConfDir))
120  AddPriv(QStringLiteral("c,config"), QStringLiteral(""), QStringLiteral("Override the configuration directory for XML config file, database etc."));
121  if (options.testFlag(TorcCommandLine::ShareDir))
122  AddPriv(QStringLiteral("s,share"), QStringLiteral(""), QStringLiteral("Overrride the shared directory for HTML files etc"));
123  if (options.testFlag(TorcCommandLine::TransDir))
124  AddPriv(QStringLiteral("t,trans"), QStringLiteral(""), QStringLiteral("Override the translations directory"));
125 }
126 
134 void TorcCommandLine::Add(const QString &Keys, const QVariant &Default, const QString &HelpText, bool ExitImmediately/*=false*/)
135 {
136  AddPriv(Keys, Default, HelpText, TorcCommandLine::None, ExitImmediately);
137 }
138 
139 void TorcCommandLine::AddPriv(const QString &Keys, const QVariant &Default, const QString &HelpText, TorcCommandLine::Options Flags /*= TorcCommandLine::None*/, bool ExitImmediately /*=false*/)
140 {
141  QStringList keys = Keys.split(',');
142 
143  QStringList valid;
144 
145  bool first = true;
146  QString master;
147  foreach (const QString &key, keys)
148  {
149  if (key.contains('='))
150  {
151  std::cout << QStringLiteral("Invalid option '%1'").arg(key).toLocal8Bit().constData() << std::endl;
152  }
153  else if (first && !m_options.contains(key))
154  {
155  master = key;
156  m_options.insert(key, TorcArgument(Default, HelpText, Flags, ExitImmediately));
157  valid.append("-" + key);
158  first = false;
159  }
160  else if (!first && !m_aliases.contains(key) && !m_options.contains(key))
161  {
162  m_aliases.insert(key, master);
163  valid.append("-" + key);
164  }
165  else
166  {
167  std::cout << QStringLiteral("Command line option '%1' already in use - ignoring").arg(key).toLocal8Bit().constData() << std::endl;
168  }
169  }
170 
171  if (!valid.isEmpty())
172  {
173  QString options = valid.join(QStringLiteral(" OR "));
174  m_help.insert(options, HelpText);
175  if (options.size() > (int)m_maxLength)
176  m_maxLength = options.size() + 2;
177  }
178 }
179 
186 int TorcCommandLine::Evaluate(int argc, const char * const *argv, bool &Exit)
187 {
188  QString error;
189  bool parserror = false;
190  int result = TORC_EXIT_OK;
191  bool printhelp = false;
192  bool printversion = false;
193 
194  // loop through the command line arguments
195  for (int i = 1; i < argc; ++i)
196  {
197  QString key = QString::fromLocal8Bit(argv[i]);
198 
199  // remove trailing '-'s
200  while (key.startsWith('-'))
201  key = key.mid(1);
202 
203  QString value(QStringLiteral(""));
204 
205  bool simpleformat = key.contains('=');
206  if (simpleformat)
207  {
208  // option is --key=value format
209  QStringList keyval = key.split('=');
210  key = keyval.at(0).trimmed();
211  value = keyval.at(1).trimmed();
212  }
213 
214  // do we recognise this option
215  if (!m_options.contains(key))
216  {
217  // is it an alias?
218  if (!m_aliases.contains(key))
219  {
220  parserror = true;
221  error = QStringLiteral("Unknown command line option '%1'").arg(key);
222  break;
223  }
224  else
225  {
226  key = m_aliases.value(key);
227  }
228  }
229 
230  TorcArgument argument = m_options.value(key);
231 
232  // --key value format
233  if (!simpleformat && argument.m_value.isValid())
234  {
235  if (i >= argc - 1)
236  {
237  parserror = true;
238  error = QStringLiteral("Insufficient arguments - option '%1' requires a value").arg(key);
239  break;
240  }
241 
242  value = QString::fromLocal8Bit(argv[++i]).trimmed();
243 
244  if (value.startsWith('='))
245  {
246  parserror = true;
247  error = QStringLiteral("Option '%1' expects a value").arg(key);
248  break;
249  }
250  }
251 
252  if (!argument.m_value.isValid())
253  {
254  if (!value.isEmpty())
255  {
256  // unexpected value
257  parserror = true;
258  error = QStringLiteral("Option '%1' does not expect a value ('%2')").arg(key, value);
259  break;
260  }
261  else
262  {
263  // mark option as detected
264  argument.m_value = QVariant((bool)true);
265  }
266  }
267  else if (argument.m_value.isValid() && value.isEmpty())
268  {
269  parserror = true;
270  error = QStringLiteral("Option '%1' expects a value").arg(key).toLocal8Bit();
271  break;
272  }
273 
274  Exit |= argument.m_exitImmediately;
275  printhelp |= (bool)(argument.m_flags & TorcCommandLine::Help);
276  printversion |= (bool)(argument.m_flags & TorcCommandLine::Version);
277 
278  // update the value for the option
279  if (!value.isEmpty())
280  {
281  switch ((QMetaType::Type)argument.m_value.type())
282  {
283  case QMetaType::QStringList:
284  argument.m_value = QVariant(QStringList(value));
285  break;
286  default:
287  argument.m_value = QVariant(value);
288  }
289  // replace option with updated argument
290  m_options.insert(key, argument);
291  }
292  }
293 
294  if (parserror)
295  {
296  result = TORC_EXIT_INVALID_CMDLINE;
297  Exit = true;
298  printhelp = true;
299  std::cout << error.toLocal8Bit().constData() << std::endl << std::endl;
300  }
301 
302  if (printhelp)
303  {
304  std::cout << "Command line options:" << std::endl << std::endl;
305 
306  QHash<QString,QString>::const_iterator it = m_help.constBegin();
307  for ( ; it != m_help.constEnd(); ++it)
308  {
309  QByteArray option(it.key().toLocal8Bit().constData());
310  QByteArray padding(m_maxLength - option.size(), 32);
311  std::cout << option.constData() << padding.constData() << it.value().toLocal8Bit().constData() << std::endl;
312  }
313 
314  std::cout << std::endl << "All options may be preceeded by '-' or '--'" << std::endl;
315 
316  {
318  if (variable)
319  {
320  std::cout << std::endl << "The following environment variables may be useful:" << std::endl;
321 
322  while (variable)
323  {
324  QByteArray var(variable->m_variable.toLocal8Bit().constData());
325  QByteArray padding(m_maxLength - var.size(), 32);
326  std::cout << var.constData() << padding.constData() << variable->m_description.toLocal8Bit().constData() << std::endl;
327  variable = variable->nextEnvironmentVariable;
328  }
329  }
330  }
331  }
332 
333  if (printversion)
334  {
335  std::cout << "QT Version : " << QT_VERSION_STR << std::endl;
336  }
337 
338  return result;
339 }
340 
346 QVariant TorcCommandLine::GetValue(const QString &Key)
347 {
348  if (m_options.contains(Key))
349  return m_options.value(Key).m_value;
350 
351  if (m_aliases.contains(Key))
352  return m_options.value(m_aliases.value(Key)).m_value;
353 
354  return QVariant();
355 }
356 
358 bool TorcCommandLine::RegisterEnvironmentVariable(const QString &Var, const QString &Description)
359 {
360  return (bool)new TorcEnvironmentVariable(Var, Description);
361 }
QString m_helpText
#define TORC_EXIT_INVALID_CMDLINE
Definition: torcexitcodes.h:7
Torc command line handler.
bool m_exitImmediately
QVariant m_value
VERBOSE_PREAMBLE true
TorcCommandLine(TorcCommandLine::Options Flags)
void Add(const QString &Keys, const QVariant &Default, const QString &HelpText, bool ExitImmediately)
Implement custom command line options.
static bool RegisterEnvironmentVariable(const QString &Var, const QString &Description)
Register an environment variable for display via the help option.
A simple private linked list to manage registered environment variables.
TorcEnvironmentVariable * nextEnvironmentVariable
TorcCommandLine::Options m_flags
Simple wrapper around a command line argument.
static TorcEnvironmentVariable * gEnvironmentVariable
#define TORC_EXIT_OK
Definition: torcexitcodes.h:4
int Evaluate(int argc, const char *const *argv, bool &Exit)
Evaluate the command line options.
QVariant GetValue(const QString &Key)
Return the value associated with Key or an invalid QVariant if the option is not present.
TorcEnvironmentVariable(const QString &Var, const QString &Description)