Quassel IRC  Pre-Release
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
cliparser.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * Copyright (C) 2005-2015 by the Quassel Project *
3  * devel@quassel-irc.org *
4  * *
5  * This program is free software; you can redistribute it and/or modify *
6  * it under the terms of the GNU General Public License as published by *
7  * the Free Software Foundation; either version 2 of the License, or *
8  * (at your option) version 3. *
9  * *
10  * This program is distributed in the hope that it will be useful, *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13  * GNU General Public License for more details. *
14  * *
15  * You should have received a copy of the GNU General Public License *
16  * along with this program; if not, write to the *
17  * Free Software Foundation, Inc., *
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
19  ***************************************************************************/
20 
21 #include "cliparser.h"
22 
23 #include <QDir>
24 #include <QDebug>
25 #include <QString>
26 #include <QFileInfo>
27 
28 #include <iostream>
29 
31 {
32 }
33 
34 
35 void CliParser::addArgument(const QString &longName, const CliParserArg &arg)
36 {
37  if (argsMap.contains(longName)) qWarning() << "Warning: Multiple definition of argument" << longName;
38  if (arg.shortName != 0 && !lnameOfShortArg(arg.shortName).isNull())
39  qWarning().nospace() << "Warning: Redefining shortName '" << arg.shortName << "' for " << longName << " previously defined for " << lnameOfShortArg(arg.shortName);
40  argsMap.insert(longName, arg);
41 }
42 
43 
44 bool CliParser::addLongArg(const CliParserArg::CliArgType type, const QString &name, const QString &value)
45 {
46  if (argsMap.contains(name)) {
47  if (type == CliParserArg::CliArgOption && argsMap.value(name).type == type) {
48  argsMap[name].value = escapedValue(value);
49  return true;
50  }
51  else if (type == CliParserArg::CliArgSwitch && argsMap.value(name).type == type) {
52  argsMap[name].boolValue = true;
53  return true;
54  }
55  }
56  qWarning() << "Warning: Unrecognized argument:" << name;
57  return false;
58 }
59 
60 
61 bool CliParser::addShortArg(const CliParserArg::CliArgType type, const char shortName, const QString &value)
62 {
63  QString longName = lnameOfShortArg(shortName);
64  if (!longName.isNull()) {
65  if (type == CliParserArg::CliArgOption && argsMap.value(longName).type == type) {
66  argsMap[longName].value = escapedValue(value);
67  return true;
68  }
69  else if (type == CliParserArg::CliArgSwitch) {
70  if (argsMap.value(longName).type == type) {
71  argsMap[longName].boolValue = true;
72  return true;
73  }
74  // arg is an option but detected as a switch -> argument is missing
75  else {
76  qWarning().nospace() << "Warning: '" << shortName << "' is an option which needs an argument";
77  return false;
78  }
79  }
80  }
81  qWarning().nospace() << "Warning: Unrecognized argument: '" << shortName << "'";
82  return false;
83 }
84 
85 
86 QString CliParser::escapedValue(const QString &value)
87 {
88  QString escapedValue = value;
89  if (escapedValue.startsWith("~"))
90  escapedValue.replace(0, 1, QDir::homePath());
91 
92  return escapedValue;
93 }
94 
95 
96 bool CliParser::init(const QStringList &args)
97 {
98  argsRaw = args;
99  QStringList::const_iterator currentArg;
100  for (currentArg = argsRaw.constBegin(); currentArg != argsRaw.constEnd(); ++currentArg) {
101  if (currentArg->startsWith("--")) {
102  // long
103  QString name;
104  if (currentArg->contains("=")) {
105  // option
106  QStringList tmp = currentArg->mid(2).split("=");
107  name = tmp.at(0);
108  QString value;
109  // this is needed to allow --option=""
110  if (tmp.at(1).isNull()) value = QString("");
111  else value = tmp.at(1);
112  if (!addLongArg(CliParserArg::CliArgOption, name, value)) return false;
113  }
114  else {
115  // switch
116  name = currentArg->mid(2);
117  if (!addLongArg(CliParserArg::CliArgSwitch, name)) return false;
118  }
119  }
120  else if (currentArg->startsWith("-")) {
121  // short
122  char name;
123  QStringList::const_iterator nextArg = currentArg+1;
124  // if next arg is a short/long option/switch the current arg is one too
125  if (nextArg == argsRaw.constEnd() || nextArg->startsWith("-")) {
126  // switch
127  for (int i = 0; i < currentArg->mid(1).toLatin1().size(); i++) {
128  name = currentArg->mid(1).toLatin1().at(i);
129  if (!addShortArg(CliParserArg::CliArgSwitch, name)) return false;
130  }
131  }
132  // if next arg is is no option/switch it's an argument to a shortoption
133  else {
134  // option
135  // short options are not freely mixable with other shortargs
136  if (currentArg->mid(1).toLatin1().size() > 1) {
137  qWarning() << "Warning: Shortoptions may not be combined with other shortoptions or switches";
138  return false;
139  }
140  QString value;
141  bool skipNext = false;
142  if (nextArg != argsRaw.constEnd()) {
143  value = nextArg->toLocal8Bit();
144  skipNext = true;
145  }
146  else value = currentArg->toLocal8Bit();
147  name = currentArg->mid(1).toLatin1().at(0);
148  // we took one argument as argument to an option so skip it next time
149  if (skipNext) currentArg++;
150  if (!addShortArg(CliParserArg::CliArgOption, name, value)) return false;
151  }
152  }
153  else {
154  // we don't support plain arguments without -/--
155  if (currentArg->toLatin1() != argsRaw.at(0)) return false;
156  }
157  }
158  return true;
159 }
160 
161 
163 {
164  std::cout << "Usage: " << qPrintable(QFileInfo(argsRaw.at(0)).completeBaseName()) << " [arguments]" << std::endl;
165 
166  // get size of longName field
167  QStringList keys = argsMap.keys();
168  uint lnameFieldSize = 0;
169  foreach(QString key, keys) {
170  uint size = 0;
171  if (argsMap.value(key).type == CliParserArg::CliArgOption)
172  size += key.size()*2;
173  else
174  size += key.size();
175  // this is for " --...=[....] "
176  size += 8;
177  if (size > lnameFieldSize) lnameFieldSize = size;
178  }
179 
180  QMap<QString, CliParserArg>::const_iterator arg;
181  for (arg = argsMap.constBegin(); arg != argsMap.constEnd(); ++arg) {
182  QString output;
183  QString lnameField;
184 
185  if (arg.value().shortName) {
186  output.append(" -").append(arg.value().shortName).append(",");
187  }
188  else output.append(" ");
189  lnameField.append(" --").append(arg.key());
190  if (arg.value().type == CliParserArg::CliArgOption && !arg.value().valueName.isEmpty()) {
191  lnameField.append("=<").append(arg.value().valueName).append(">");
192  }
193  output.append(lnameField.leftJustified(lnameFieldSize));
194  if (!arg.value().help.isEmpty()) {
195  output.append(arg.value().help);
196  }
197  if (arg.value().type == CliParserArg::CliArgOption && !arg.value().def.isNull()) {
198  output.append(". Default is: ").append(arg.value().def);
199  }
200  std::cout << qPrintable(output) << std::endl;
201  }
202 }
203 
204 
205 QString CliParser::value(const QString &longName)
206 {
207  if (argsMap.contains(longName) && argsMap.value(longName).type == CliParserArg::CliArgOption) {
208  if (!argsMap.value(longName).value.isNull())
209  return argsMap.value(longName).value;
210  else
211  return argsMap.value(longName).def;
212  }
213  else {
214  qWarning() << "Warning: Requested value of not defined argument" << longName << "or argument is a switch";
215  return QString();
216  }
217 }
218 
219 
220 bool CliParser::isSet(const QString &longName)
221 {
222  if (argsMap.contains(longName)) {
223  if (argsMap.value(longName).type == CliParserArg::CliArgOption) return !argsMap.value(longName).value.isNull();
224  else return argsMap.value(longName).boolValue;
225  }
226  else {
227  qWarning() << "Warning: Requested isSet of not defined argument" << longName;
228  return false;
229  }
230 }
231 
232 
233 QString CliParser::lnameOfShortArg(const char arg)
234 {
235  QMap<QString, CliParserArg>::const_iterator cur;
236  for (cur = argsMap.constBegin(); cur != argsMap.constEnd(); ++cur) {
237  if (cur.value().shortName == arg) return cur.key();
238  }
239  return QString();
240 }