1 | /*************************************************************************** |
2 | * Copyright (C) 2005-2014 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 | |
30 | CliParser::CliParser() : AbstractCliParser() |
31 | { |
32 | } |
33 | |
34 | |
35 | void CliParser::addArgument(const QString &longName_, const CliParserArg &arg) |
36 | { |
37 | QString longName = longName_; |
38 | longName.remove(QRegExp("\\s*<.*>\\s*" )); // KCmdLineArgs takes args of the form "arg <defval>" |
39 | if (argsMap.contains(longName)) qWarning() << "Warning: Multiple definition of argument" << longName; |
40 | if (arg.shortName != 0 && !lnameOfShortArg(arg.shortName).isNull()) |
41 | qWarning().nospace() << "Warning: Redefining shortName '" << arg.shortName << "' for " << longName << " previously defined for " << lnameOfShortArg(arg.shortName); |
42 | argsMap.insert(longName, arg); |
43 | } |
44 | |
45 | |
46 | bool CliParser::addLongArg(const CliParserArg::CliArgType type, const QString &name, const QString &value) |
47 | { |
48 | if (argsMap.contains(name)) { |
49 | if (type == CliParserArg::CliArgOption && argsMap.value(name).type == type) { |
50 | argsMap[name].value = escapedValue(value); |
51 | return true; |
52 | } |
53 | else if (type == CliParserArg::CliArgSwitch && argsMap.value(name).type == type) { |
54 | argsMap[name].boolValue = true; |
55 | return true; |
56 | } |
57 | } |
58 | qWarning() << "Warning: Unrecognized argument:" << name; |
59 | return false; |
60 | } |
61 | |
62 | |
63 | bool CliParser::addShortArg(const CliParserArg::CliArgType type, const char shortName, const QString &value) |
64 | { |
65 | QString longName = lnameOfShortArg(shortName); |
66 | if (!longName.isNull()) { |
67 | if (type == CliParserArg::CliArgOption && argsMap.value(longName).type == type) { |
68 | argsMap[longName].value = escapedValue(value); |
69 | return true; |
70 | } |
71 | else if (type == CliParserArg::CliArgSwitch) { |
72 | if (argsMap.value(longName).type == type) { |
73 | argsMap[longName].boolValue = true; |
74 | return true; |
75 | } |
76 | // arg is an option but detected as a switch -> argument is missing |
77 | else { |
78 | qWarning().nospace() << "Warning: '" << shortName << "' is an option which needs an argument" ; |
79 | return false; |
80 | } |
81 | } |
82 | } |
83 | qWarning().nospace() << "Warning: Unrecognized argument: '" << shortName << "'" ; |
84 | return false; |
85 | } |
86 | |
87 | |
88 | QString CliParser::escapedValue(const QString &value) |
89 | { |
90 | QString escapedValue = value; |
91 | if (escapedValue.startsWith("~" )) |
92 | escapedValue.replace(0, 1, QDir::homePath()); |
93 | |
94 | return escapedValue; |
95 | } |
96 | |
97 | |
98 | bool CliParser::init(const QStringList &args) |
99 | { |
100 | argsRaw = args; |
101 | QStringList::const_iterator currentArg; |
102 | for (currentArg = argsRaw.constBegin(); currentArg != argsRaw.constEnd(); ++currentArg) { |
103 | if (currentArg->startsWith("--" )) { |
104 | // long |
105 | QString name; |
106 | if (currentArg->contains("=" )) { |
107 | // option |
108 | QStringList tmp = currentArg->mid(2).split("=" ); |
109 | name = tmp.at(0); |
110 | QString value; |
111 | // this is needed to allow --option="" |
112 | if (tmp.at(1).isNull()) value = QString("" ); |
113 | else value = tmp.at(1); |
114 | if (!addLongArg(CliParserArg::CliArgOption, name, value)) return false; |
115 | } |
116 | else { |
117 | // switch |
118 | name = currentArg->mid(2); |
119 | if (!addLongArg(CliParserArg::CliArgSwitch, name)) return false; |
120 | } |
121 | } |
122 | else if (currentArg->startsWith("-" )) { |
123 | // short |
124 | char name; |
125 | QStringList::const_iterator nextArg = currentArg+1; |
126 | // if next arg is a short/long option/switch the current arg is one too |
127 | if (nextArg == argsRaw.constEnd() || nextArg->startsWith("-" )) { |
128 | // switch |
129 | for (int i = 0; i < currentArg->mid(1).toLatin1().size(); i++) { |
130 | name = currentArg->mid(1).toLatin1().at(i); |
131 | if (!addShortArg(CliParserArg::CliArgSwitch, name)) return false; |
132 | } |
133 | } |
134 | // if next arg is is no option/switch it's an argument to a shortoption |
135 | else { |
136 | // option |
137 | // short options are not freely mixable with other shortargs |
138 | if (currentArg->mid(1).toLatin1().size() > 1) { |
139 | qWarning() << "Warning: Shortoptions may not be combined with other shortoptions or switches" ; |
140 | return false; |
141 | } |
142 | QString value; |
143 | bool skipNext = false; |
144 | if (nextArg != argsRaw.constEnd()) { |
145 | value = nextArg->toLocal8Bit(); |
146 | skipNext = true; |
147 | } |
148 | else value = currentArg->toLocal8Bit(); |
149 | name = currentArg->mid(1).toLatin1().at(0); |
150 | // we took one argument as argument to an option so skip it next time |
151 | if (skipNext) currentArg++; |
152 | if (!addShortArg(CliParserArg::CliArgOption, name, value)) return false; |
153 | } |
154 | } |
155 | else { |
156 | // we don't support plain arguments without -/-- |
157 | if (currentArg->toLatin1() != argsRaw.at(0)) return false; |
158 | } |
159 | } |
160 | return true; |
161 | } |
162 | |
163 | |
164 | void CliParser::usage() |
165 | { |
166 | std::cout << "Usage: " << qPrintable(QFileInfo(argsRaw.at(0)).completeBaseName()) << " [arguments]" << std::endl; |
167 | |
168 | // get size of longName field |
169 | QStringList keys = argsMap.keys(); |
170 | uint lnameFieldSize = 0; |
171 | foreach(QString key, keys) { |
172 | uint size = 0; |
173 | if (argsMap.value(key).type == CliParserArg::CliArgOption) |
174 | size += key.size()*2; |
175 | else |
176 | size += key.size(); |
177 | // this is for " --...=[....] " |
178 | size += 8; |
179 | if (size > lnameFieldSize) lnameFieldSize = size; |
180 | } |
181 | |
182 | QMap<QString, CliParserArg>::const_iterator arg; |
183 | for (arg = argsMap.constBegin(); arg != argsMap.constEnd(); ++arg) { |
184 | QString output; |
185 | QString lnameField; |
186 | |
187 | if (arg.value().shortName) { |
188 | output.append(" -" ).append(arg.value().shortName).append("," ); |
189 | } |
190 | else output.append(" " ); |
191 | lnameField.append(" --" ).append(arg.key()); |
192 | if (arg.value().type == CliParserArg::CliArgOption) { |
193 | lnameField.append("=[" ).append(arg.key().toUpper()).append("]" ); |
194 | } |
195 | output.append(lnameField.leftJustified(lnameFieldSize)); |
196 | if (!arg.value().help.isEmpty()) { |
197 | output.append(arg.value().help); |
198 | } |
199 | if (arg.value().type == CliParserArg::CliArgOption && !arg.value().def.isNull()) { |
200 | output.append(". Default is: " ).append(arg.value().def); |
201 | } |
202 | std::cout << qPrintable(output) << std::endl; |
203 | } |
204 | } |
205 | |
206 | |
207 | QString CliParser::value(const QString &longName) |
208 | { |
209 | if (argsMap.contains(longName) && argsMap.value(longName).type == CliParserArg::CliArgOption) { |
210 | if (!argsMap.value(longName).value.isNull()) |
211 | return argsMap.value(longName).value; |
212 | else |
213 | return argsMap.value(longName).def; |
214 | } |
215 | else { |
216 | qWarning() << "Warning: Requested value of not defined argument" << longName << "or argument is a switch" ; |
217 | return QString(); |
218 | } |
219 | } |
220 | |
221 | |
222 | bool CliParser::isSet(const QString &longName) |
223 | { |
224 | if (argsMap.contains(longName)) { |
225 | if (argsMap.value(longName).type == CliParserArg::CliArgOption) return !argsMap.value(longName).value.isNull(); |
226 | else return argsMap.value(longName).boolValue; |
227 | } |
228 | else { |
229 | qWarning() << "Warning: Requested isSet of not defined argument" << longName; |
230 | return false; |
231 | } |
232 | } |
233 | |
234 | |
235 | QString CliParser::lnameOfShortArg(const char arg) |
236 | { |
237 | QMap<QString, CliParserArg>::const_iterator cur; |
238 | for (cur = argsMap.constBegin(); cur != argsMap.constEnd(); ++cur) { |
239 | if (cur.value().shortName == arg) return cur.key(); |
240 | } |
241 | return QString(); |
242 | } |
243 | |